API Reference
POST a URL, get a human QA report. Integrate TestMyVibes into your CI/CD — tests run async, results come back fast.
https://testmyvibes.com/v1
Authentication
Every request needs a Bearer token. Grab one from Dashboard → API Keys.
Authorization: Bearer tmv_live_abc123...
Rate limit: 60 req/min per key. Generate keys from your dashboard.
Errors
Every error response from /v1, /api, and /hooks uses a single JSON envelope. Branch on code, log requestId, and surface hint to your users.
{
"error": "Insufficient credits",
"code": "INSUFFICIENT_CREDITS",
"hint": "Top up credits in Dashboard → Billing, then retry.",
"requestId": "k9c3xQ8nY7Pa"
}
| Field | Type | Notes |
|---|---|---|
error | string | Human-readable summary. May change wording across versions — do not pattern-match it. |
code | string | Stable machine-readable identifier. Branch on this. Values are SCREAMING_SNAKE_CASE and never renamed without a deprecation notice. |
hint | string? | Optional. Actionable next step (e.g. "Top up credits in Dashboard → Billing"). Safe to show to end-users. |
requestId | string | Short server-issued id, also echoed in the x-request-id response header. Include this when contacting support — we look it up in our logs. |
Some responses also include endpoint-specific extras at the top level (e.g. creditsRequired on a 402, details on a Zod validation failure). These are additive — your client can ignore unknown keys.
Standard codes
| Status | Code | Meaning |
|---|---|---|
| 400 | INVALID_REQUEST | Missing, malformed, or schema-invalid fields. details carries the offending paths for Zod failures. |
| 401 | UNAUTHORIZED | Missing, invalid, expired, or revoked API key. |
| 402 | INSUFFICIENT_CREDITS | Account balance too low for this operation. Includes creditsRequired, creditsAvailable, creditsShort. |
| 403 | FORBIDDEN | Key is valid but lacks access to this resource. |
| 403 | ACCOUNT_SUSPENDED | Account is suspended. Contact support. |
| 404 | NOT_FOUND | Job, project, session, or resource doesn't exist for this account. |
| 409 | CONFLICT | Resource is in a state that doesn't allow the requested operation (e.g. session already closed). |
| 409 | WEBHOOK_SECRET_CONFLICT | Project already has a different webhook secret. Rotate via the dashboard or send the existing value. |
| 413 | PAYLOAD_TOO_LARGE | Request body exceeded the 50 MB limit. |
| 429 | RATE_LIMITED | More than 60 requests/min for this API key. Slow down and retry. |
| 500 | INTERNAL_ERROR | Something broke on our end. Retry with backoff; if it persists, share the requestId with support. |
Request correlation
Every request — success or error — gets an x-request-id response header. Save it alongside the response in your logs so you can quote it back to support if you ever need to investigate. You can also send your own x-request-id request header (1–80 chars, A-Z a-z 0-9 . _ -) and we'll honor and echo it back. When you contact us, paste the id into the Request ID field on our support form and we'll find the matching log line in seconds.
curl -i https://testmyvibes.com/v1/credits \
-H "Authorization: Bearer tmv_live_abc123"
HTTP/1.1 200 OK
x-request-id: k9c3xQ8nY7Pa
content-type: application/json
...
Recommended client pattern
const res = await fetch(`${BASE}/v1/replit/test`, { method: 'POST', headers, body });
const requestId = res.headers.get('x-request-id'); // log this on every call
const data = await res.json();
if (!res.ok) {
// Branch on `code`, never on `error` text.
switch (data.code) {
case 'INSUFFICIENT_CREDITS': return showTopupModal(data);
case 'RATE_LIMITED': return retryAfter(60_000);
case 'UNAUTHORIZED': return promptForKey();
default: throw new Error(`${data.code}: ${data.error} (req ${data.requestId || requestId})`);
}
}
Replit Integration
The Replit integration endpoints are designed for one-command testing. Post a URL and optionally describe what to test — the AI auto-generates a targeted test scope, links it to a project, and queues a human checker.
Works with *.replit.dev (dev) and *.replit.app (published) URLs. Projects are auto-created and linked so regression testing works across deploys.
/v1/replit/test
Submit a Replit app for human QA testing
One-shot endpoint: give it a URL, get back a job tracking handle. The AI auto-scopes the test based on the URL, optional feature description, and any previous failures for the same project.
"test the signup flow" or "checkout is broken on mobile". AI tailors the checklist to this.job.created, job.completed). Saved to the auto-created project.webhookUrl and we'll sign every callback to your URL with this exact value — store it as an env var on your side, no dashboard round-trip needed. Re-passing the same value on later calls is a no-op; passing a different value to a project that already has a secret returns 409 WEBHOOK_SECRET_CONFLICT. Omit to have us generate one server-side (you'll then need to copy it from the dashboard).job.created, job.completed, job.ai_report_ready, job.session_ready, job.session_completed, loop_session.ended. Omit to receive every event. An empty array disables delivery for the project. Unknown event names are rejected with 400 INVALID_REQUEST. Only applied when webhookUrl is also set on a brand new project; for existing projects, this updates the saved subscription list."normal" (default) or "urgent" (2x credits, faster pickup).First Impression, General QA, Payment Flow, Custom, AI Review.Example — basic test
curl -X POST https://testmyvibes.com/v1/replit/test \
-H "Authorization: Bearer tmv_live_abc123" \
-H "Content-Type: application/json" \
-d '{"url": "https://my-app.replit.app"}'
Example — focused test
curl -X POST https://testmyvibes.com/v1/replit/test \
-H "Authorization: Bearer tmv_live_abc123" \
-H "Content-Type: application/json" \
-d '{
"url": "https://my-app.replit.app",
"feature": "test the new checkout flow",
"webhookUrl": "https://my-server.com/webhook",
"priority": "urgent"
}'
Response 201
{
"jobId": "j_abc123",
"projectId": "p_xyz789",
"status": "pending",
"estimatedCompletionMinutes": 60,
"creditsDeducted": 2,
"creditsCharged": 2,
"creditsRemaining": 18,
"checklistItemCount": 7,
"jobType": "Payment Flow",
"isReplitApp": true,
"regressionItemsIncluded": 3,
"statusUrl": "https://testmyvibes.com/v1/replit/status/j_abc123",
"reportUrl": "https://testmyvibes.com/v1/jobs/j_abc123/report",
"dashboardReportUrl": "https://testmyvibes.com/projects/p_xyz789/jobs/j_abc123"
}
creditsDeducted and creditsCharged always carry the same value — pick whichever name you prefer. reportUrl is the JSON API endpoint for fetching the report; dashboardReportUrl is the operator-facing page to deep-link humans to.
Errors
// 402 — out of credits
{
"error": "Insufficient credits",
"code": "INSUFFICIENT_CREDITS",
"creditsRequired": 4,
"creditsAvailable": 1,
"creditsShort": 3
}
// 401 — bad/missing API key
{ "error": "Invalid API key", "code": "UNAUTHORIZED" }
Branch on the code field, not error. Code values are stable across versions.
/v1/replit/sessions
Launch a synchronized multi-checker session
Create a session where 2–10 human checkers test the same app in parallel and at the same moment. Useful for multi-role flows (buyer + seller, host + guest, admin + member) and for stress-testing across devices/browsers. Credits = perSlot × slot count, deducted up front. Each slot becomes its own job — poll any slot's jobId via /v1/replit/status/:jobId, or watch the session via the dashboard URL.
/v1/replit/test.roleLabel (e.g. "Buyer") and optional targeting fields: targetDevice, targetOS, targetBrowser, targetScreenSize, targetNetwork. Defaults to "Checker N" if no role label./v1/replit/test. Apply to every slot.true.Example — buyer + seller flow
curl -X POST https://testmyvibes.com/v1/replit/sessions \
-H "Authorization: Bearer tmv_live_abc123" \
-H "Content-Type: application/json" \
-d '{
"url": "https://my-marketplace.replit.app",
"feature": "test the listing + offer + accept flow end-to-end",
"scenarioBrief": "Seller posts a listing. Buyer makes an offer. Seller accepts. Both confirm checkout.",
"slots": [
{ "roleLabel": "Seller", "targetDevice": "desktop" },
{ "roleLabel": "Buyer", "targetDevice": "mobile" }
]
}'
Response 201
{
"sessionId": "ses_abc123",
"projectId": "p_xyz789",
"status": "lobby",
"checkerCount": 2,
"creditsDeducted": 4,
"creditsCharged": 4,
"creditsRemaining": 16,
"checklistItemCount": 7,
"jobType": "General QA",
"isReplitApp": true,
"readyDeadline": "2026-04-19T15:30:00.000Z",
"slots": [
{
"slot": 1, "roleLabel": "Seller", "jobId": "j_seller01",
"targetDevice": "desktop",
"statusUrl": "https://testmyvibes.com/v1/replit/status/j_seller01"
},
{
"slot": 2, "roleLabel": "Buyer", "jobId": "j_buyer02",
"targetDevice": "mobile",
"statusUrl": "https://testmyvibes.com/v1/replit/status/j_buyer02"
}
],
"sessionUrl": "https://testmyvibes.com/dashboard/sessions/ses_abc123"
}
/v1/replit/status/:jobId
Poll job progress with rich status info
Real-time progress with percentage, item-level counts, and the full report inline when complete. Poll this every 30-60 seconds or use webhooks for push-based updates.
Example
curl https://testmyvibes.com/v1/replit/status/j_abc123 \
-H "Authorization: Bearer tmv_live_abc123"
Response — in progress
{
"jobId": "j_abc123",
"status": "in_progress",
"statusMessage": "Testing in progress — 4/7 items checked",
"progressPercent": 58,
"url": "https://my-app.replit.app",
"jobType": "Payment Flow",
"checklist": {
"totalItems": 7,
"checkedItems": 4,
"passedItems": 3,
"failedItems": 1,
"skippedItems": 0
}
}
Response — completed (includes full report)
{
"jobId": "j_abc123",
"status": "completed",
"statusMessage": "Testing complete",
"progressPercent": 100,
"result": {
"overallStatus": "fail",
"passCount": 5,
"failCount": 2,
"skipCount": 0,
"summary": "Checkout flow mostly works but...",
"items": [
{
"id": "ci_1",
"description": "Complete checkout with valid card",
"status": "pass",
"note": "Works correctly"
}
]
}
}
/v1/replit/prepare-next
Prepare regression tests for next deploy
After fixing bugs, call this to prepare a focused regression test. The next deploy hook or test submission will auto-include verification items for the fixed bugs.
Request Body
{
"projectId": "p_abc123",
"fixedBugIds": ["ci_3", "ci_5"],
"fixedDescriptions": ["Login button is unresponsive on mobile"]
}
fixedBugIds references item IDs from the last test's failed items. fixedDescriptions lets you describe fixes in plain text. Provide at least one.
Response
{
"message": "Regression test prepared...",
"regressionItems": 2,
"descriptions": ["Login button is unresponsive on mobile", "Cart total shows wrong currency"],
"projectId": "p_abc123"
}
Jobs
/v1/jobs
Submit a test job. Returns a jobId immediately — tests run async.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
url | string | Yes | URL to test |
title | string | Yes | Job title |
description | string | Yes | What should the checker test? |
jobType | string | Yes | First Impression, General QA, Payment Flow, Custom, AI Review |
checklistItems | array | No | Custom checklist items [{description, expectedOutcome, severity}]. If omitted, AI generates them. |
priority | string | No | "normal" (default) or "urgent" (2x credits, jumps the queue) |
projectId | string | No | Associate with a project |
slaMinutes | number | No | 15, 30, or 60 (default 60) |
credentials | object | No | {username, password, notes} — AES-256-GCM encrypted at rest |
generateChecklist | string | No | "sync" or "async" (default). Sync waits for AI generation before responding. |
curl -X POST https://testmyvibes.com/v1/jobs \
-H "Authorization: Bearer tmv_live_abc123" \
-H "Content-Type: application/json" \
-d '{
"url": "https://myapp.com",
"title": "Test checkout flow",
"description": "Complete a purchase with a test card",
"jobType": "payment_flow",
"priority": "normal"
}'
const res = await fetch('https://testmyvibes.com/v1/jobs', {
method: 'POST',
headers: {
'Authorization': 'Bearer tmv_live_abc123',
'Content-Type': 'application/json'
},
body: JSON.stringify({
url: 'https://myapp.com',
title: 'Test checkout flow',
description: 'Complete a purchase with a test card',
jobType: 'payment_flow',
priority: 'normal'
})
});
const data = await res.json();
import requests
res = requests.post(
"https://testmyvibes.com/v1/jobs",
headers={"Authorization": "Bearer tmv_live_abc123"},
json={
"url": "https://myapp.com",
"title": "Test checkout flow",
"description": "Complete a purchase with a test card",
"jobType": "payment_flow",
"priority": "normal"
}
)
data = res.json()
{
"jobId": "j_abc123",
"status": "pending",
"estimatedCompletionMinutes": 15,
"creditsDeducted": 3,
"checklistItemCount": 8
}
/v1/jobs
List your jobs. Filter by status, project, or date.
Query Parameters
| Param | Type | Default | Description |
|---|---|---|---|
status | string | all | pending, claimed, completed, failed |
projectId | string | — | Filter by project |
limit | number | 20 | 1–100 |
offset | number | 0 | Pagination offset |
since | string | — | ISO 8601 date — only jobs created after this |
curl "https://testmyvibes.com/v1/jobs?status=completed&limit=10" \
-H "Authorization: Bearer tmv_live_abc123"
const res = await fetch('https://testmyvibes.com/v1/jobs?status=completed&limit=10', {
headers: { 'Authorization': 'Bearer tmv_live_abc123' }
});
const data = await res.json();
import requests
res = requests.get(
"https://testmyvibes.com/v1/jobs",
headers={"Authorization": "Bearer tmv_live_abc123"},
params={"status": "completed", "limit": 10}
)
data = res.json()
{
"jobs": [
{
"id": "j_abc123",
"status": "completed",
"title": "Test checkout flow",
"url": "https://myapp.com",
"jobType": "payment_flow",
"createdAt": "2026-03-14T20:30:00Z",
"reportId": "r_def456"
}
],
"total": 42,
"limit": 10,
"offset": 0
}
/v1/jobs/:id
Get a single job's status and metadata.
Path Parameters
| Param | Type | Description |
|---|---|---|
id | string | Job ID (e.g. j_abc123) |
curl https://testmyvibes.com/v1/jobs/j_abc123 \
-H "Authorization: Bearer tmv_live_abc123"
const res = await fetch('https://testmyvibes.com/v1/jobs/j_abc123', {
headers: { 'Authorization': 'Bearer tmv_live_abc123' }
});
const data = await res.json();
import requests
res = requests.get(
"https://testmyvibes.com/v1/jobs/j_abc123",
headers={"Authorization": "Bearer tmv_live_abc123"}
)
data = res.json()
{
"id": "j_abc123",
"status": "completed",
"title": "Test checkout flow",
"url": "https://myapp.com",
"jobType": "payment_flow",
"creditsUsed": 3,
"slaMinutes": 60,
"createdAt": "2026-03-14T20:30:00Z",
"claimedAt": "2026-03-14T20:31:00Z",
"completedAt": "2026-03-14T20:42:00Z",
"reportId": "r_def456"
}
/v1/jobs/:id/report
Get the full QA report. Only available once the job is complete.
curl https://testmyvibes.com/v1/jobs/j_abc123/report \
-H "Authorization: Bearer tmv_live_abc123"
const res = await fetch('https://testmyvibes.com/v1/jobs/j_abc123/report', {
headers: { 'Authorization': 'Bearer tmv_live_abc123' }
});
const data = await res.json();
import requests
res = requests.get(
"https://testmyvibes.com/v1/jobs/j_abc123/report",
headers={"Authorization": "Bearer tmv_live_abc123"}
)
data = res.json()
{
"id": "r_def456",
"overallStatus": "fail",
"passCount": 5,
"failCount": 2,
"skipCount": 0,
"checkerSummary": "Checkout flow has two critical issues...",
"items": [
{
"id": "ci_001",
"description": "Submit form with empty email",
"status": "fail",
"checkerNote": "No validation shown — form submits silently",
"screenshotUrl": "https://storage.example.com/ss_001.png"
},
{
"id": "ci_002",
"description": "Complete purchase with test card",
"status": "pass",
"checkerNote": "Worked perfectly, confirmation page shown"
}
],
"recordingUrl": "https://storage.example.com/rec_001.webm",
"checkerRating": 4.8,
"createdAt": "2026-03-14T20:42:00Z"
}
/v1/jobs/:id/ai-report
Get the AI-generated analysis report for a completed job. Includes health score, bugs, strengths, and recommendations.
curl https://testmyvibes.com/v1/jobs/j_abc123/ai-report \
-H "Authorization: Bearer tmv_live_abc123"
import requests
res = requests.get(
"https://testmyvibes.com/v1/jobs/j_abc123/ai-report",
headers={"Authorization": "Bearer tmv_live_abc123"}
)
data = res.json()
{
"id": "ar_abc123",
"jobId": "j_abc123",
"status": "completed",
"executiveSummary": "The app has 2 critical issues...",
"healthScore": 62,
"bugs": [
{
"title": "Login form submit fails silently",
"severity": "critical",
"description": "Clicking submit with valid credentials shows no feedback",
"reproductionSteps": ["Navigate to /login", "Enter valid credentials", "Click submit"],
"suggestedFix": "Add error handling to the fetch call in login handler"
}
],
"passCount": 4,
"failCount": 2,
"skipCount": 0,
"strengths": ["Clean responsive layout", "Fast page load times"],
"recommendations": ["Add form validation feedback", "Implement loading states"]
}
Returns 202 if the AI report is still being generated. Returns 404 if the job is not yet completed.
/v1/jobs/:id/retest
Re-run the same test. No body needed — we clone the original job.
curl -X POST https://testmyvibes.com/v1/jobs/j_abc123/retest \
-H "Authorization: Bearer tmv_live_abc123"
import requests
res = requests.post(
"https://testmyvibes.com/v1/jobs/j_abc123/retest",
headers={"Authorization": "Bearer tmv_live_abc123"}
)
data = res.json()
{
"jobId": "j_xyz789",
"parentJobId": "j_abc123",
"status": "pending",
"creditsDeducted": 3
}
/v1/jobs/bulk
Fire up to 20 jobs at once. Perfect for post-deploy smoke tests.
curl -X POST https://testmyvibes.com/v1/jobs/bulk \
-H "Authorization: Bearer tmv_live_abc123" \
-H "Content-Type: application/json" \
-d '{
"jobs": [
{ "url": "https://myapp.com/login", "title": "Test login", "description": "Sign in with test creds", "jobType": "auth_flow" },
{ "url": "https://myapp.com/checkout", "title": "Test checkout", "description": "Complete a purchase", "jobType": "payment_flow" }
]
}'
import requests
res = requests.post(
"https://testmyvibes.com/v1/jobs/bulk",
headers={"Authorization": "Bearer tmv_live_abc123"},
json={
"jobs": [
{"url": "https://myapp.com/login", "title": "Test login", "description": "Sign in", "jobType": "auth_flow"},
{"url": "https://myapp.com/checkout", "title": "Test checkout", "description": "Buy something", "jobType": "payment_flow"}
]
}
)
data = res.json()
{
"batchId": "batch_abc123",
"jobIds": ["j_001", "j_002"],
"totalCreditsDeducted": 5,
"estimatedCompletionMinutes": 20
}
/v1/jobs/bulk/:batchId
Check the status of a bulk job batch.
curl https://testmyvibes.com/v1/jobs/bulk/batch_abc123 \
-H "Authorization: Bearer tmv_live_abc123"
{
"batchId": "batch_abc123",
"overallStatus": "in_progress",
"totalJobs": 2,
"completedJobs": 1,
"passCount": 1,
"failCount": 0,
"jobs": [
{ "id": "j_001", "status": "completed" },
{ "id": "j_002", "status": "claimed" }
]
}
Projects
/v1/projects
List all your projects with stats.
curl https://testmyvibes.com/v1/projects \
-H "Authorization: Bearer tmv_live_abc123"
import requests
res = requests.get(
"https://testmyvibes.com/v1/projects",
headers={"Authorization": "Bearer tmv_live_abc123"}
)
data = res.json()
{
"projects": [
{
"id": "p_abc123",
"name": "My SaaS App",
"defaultUrl": "https://myapp.com",
"jobCount": 42,
"passRate": 0.85,
"webhookSecretRotatedAt": "2026-04-12T09:00:00.000Z",
"webhookSecretRotations": [
{ "at": "2026-04-12T09:00:00.000Z", "userId": "u_123" },
{ "at": "2026-03-01T17:30:00.000Z", "userId": "u_123" }
],
"createdAt": "2026-01-15T10:00:00Z"
}
]
}
webhookSecretRotations lists up to the 5 most recent webhook-secret rotations (most recent first). Each entry has an at ISO timestamp and the userId who triggered the rotation (null for legacy entries). The field is null when no webhook is configured.
/v1/projects
Create a new project. Gets you a deploy hook URL for CI integration.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Project name |
description | string | No | What you're building |
defaultUrl | string | No | Default URL to test |
defaultJobType | string | No | Default job type for new tests |
defaultSlaMinutes | number | No | Default SLA: 15, 30, or 60 |
webhookUrl | string | No | HTTPS URL to receive event notifications for this project. Loopback, private, and internal hostnames are rejected with 400 INVALID_REQUEST. When set, a webhookSecret is generated for HMAC signing. |
subscribedWebhookEvents | string[] | No | Array of event names to subscribe to. Allowed: job.created, job.completed, job.ai_report_ready, job.session_ready, job.session_completed, loop_session.ended. Omit to receive every event. An empty array disables delivery for the project. Unknown event names are rejected with 400 INVALID_REQUEST. Only applied when webhookUrl is also set. |
curl -X POST https://testmyvibes.com/v1/projects \
-H "Authorization: Bearer tmv_live_abc123" \
-H "Content-Type: application/json" \
-d '{
"name": "My SaaS App",
"defaultUrl": "https://myapp.com",
"defaultJobType": "General QA"
}'
import requests
res = requests.post(
"https://testmyvibes.com/v1/projects",
headers={"Authorization": "Bearer tmv_live_abc123"},
json={
"name": "My SaaS App",
"defaultUrl": "https://myapp.com",
"defaultJobType": "General QA"
}
)
data = res.json()
{
"projectId": "p_new123",
"name": "My SaaS App",
"deployHookUrl": "https://testmyvibes.com/v1/hooks/deploy/dhk_xxxxxxxx"
}
/v1/projects/:id
Update project defaults and webhook configuration. All fields optional; only provided fields change.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
name | string | No | New project name (must be non-empty if provided). |
description | string | No | Project description. Pass empty string or null to clear. |
defaultUrl | string | No | Default URL for new tests. Pass empty string or null to clear. |
defaultJobType | string | No | Default job type. Pass empty string or null to clear. |
defaultSlaMinutes | number | No | Default SLA: 15, 30, or 60. Pass null to clear. |
webhookUrl | string | No | HTTPS URL for event notifications. Same validation as POST /v1/projects. Rotating to a new URL preserves the existing webhookSecret, or generates one if none was set. Pass an empty string to clear the webhook (also clears subscribedWebhookEvents). |
subscribedWebhookEvents | string[] | No | Replace the saved subscription list. Same validation as POST /v1/projects. Only applied when a webhookUrl is configured (either already on the project or set in this same request). Empty array disables delivery for the project; omit the field to leave the existing list untouched. |
curl -X PATCH https://testmyvibes.com/v1/projects/p_abc123 \
-H "Authorization: Bearer tmv_live_abc123" \
-H "Content-Type: application/json" \
-d '{
"webhookUrl": "https://hooks.example.com/tmv",
"subscribedWebhookEvents": ["job.completed", "job.ai_report_ready"]
}'
{
"projectId": "p_abc123",
"name": "My SaaS App",
"description": null,
"defaultUrl": "https://myapp.com",
"defaultJobType": "General QA",
"defaultSlaMinutes": 60,
"webhookUrl": "https://hooks.example.com/tmv",
"webhookSecretRotatedAt": "2026-04-19T12:00:00.000Z",
"webhookSecretRotations": [
{ "at": "2026-04-19T12:00:00.000Z", "userId": "u_123" },
{ "at": "2026-03-01T17:30:00.000Z", "userId": "u_123" }
],
"subscribedWebhookEvents": ["job.completed", "job.ai_report_ready"],
"updatedAt": "2026-04-19T12:00:00.000Z"
}
Returns 404 NOT_FOUND if the project doesn't exist or belongs to a different API key. The webhookSecret itself is never returned by this endpoint. webhookSecretRotations lists up to the 5 most recent webhook-secret rotations (most recent first); each entry has an at ISO timestamp and the userId who triggered it (null for legacy entries). The field is null when no webhook is configured.
Project History
/v1/projects/:id/history
Get test history, trends, regressions, and prepared regression tests for a project.
curl https://testmyvibes.com/v1/projects/p_abc123/history \
-H "Authorization: Bearer tmv_live_abc123"
{
"projectId": "p_abc123",
"projectName": "My SaaS App",
"totalTests": 12,
"passCount": 9,
"failCount": 3,
"passRate": 75,
"averageHealthScore": 72,
"latestFailures": [
{ "id": "ci_3", "description": "Login form shows error on submit", "severity": "Critical" }
],
"regressions": ["Cart total calculates wrong with discount"],
"trend": [
{ "jobId": "j_abc", "overallStatus": "FAIL", "healthScore": 62, "passCount": 5, "failCount": 2, "testedAt": "..." }
],
"preparedRegressions": [
{ "bugIds": ["ci_3"], "descriptions": ["Login form shows error on submit"], "preparedAt": "..." }
]
}
trend shows the last 10 tests. regressions lists items that passed in the previous test but failed in the latest. preparedRegressions shows bug fixes queued for verification on the next deploy.
Credits
/v1/credits
Check your credit balance, usage, and expiring credits.
curl https://testmyvibes.com/v1/credits \
-H "Authorization: Bearer tmv_live_abc123"
import requests
res = requests.get(
"https://testmyvibes.com/v1/credits",
headers={"Authorization": "Bearer tmv_live_abc123"}
)
data = res.json()
{
"available": 47,
"used": 28,
"total": 75,
"expiringCredits": [
{ "amount": 10, "expiresAt": "2026-06-14T00:00:00Z" }
]
}
Webhooks
Get real-time notifications when stuff happens. Set your webhook URL in Project Settings.
Events
| Event | Fires when |
|---|---|
job.created | A new test job is submitted |
job.claimed | A checker picks up the job |
job.completed | Report and AI analysis ready — includes full AI report with health score, bugs, and fix suggestions |
job.ai_report_ready | AI analysis finished processing — fires separately after the async AI generation completes |
job.failed_sla | Job exceeded SLA without completion — it's been re-queued |
Payload Example
{
"event": "job.completed",
"type": "job.completed",
"timestamp": "2026-03-14T20:42:00Z",
"projectId": "p_xyz789",
"jobId": "j_abc123",
"dashboardReportUrl": "https://testmyvibes.com/projects/p_xyz789/jobs/j_abc123",
"data": {
"projectId": "p_xyz789",
"jobId": "j_abc123",
"dashboardReportUrl": "https://testmyvibes.com/projects/p_xyz789/jobs/j_abc123",
"reportId": "r_def456",
"overallStatus": "fail",
"passCount": 5,
"failCount": 2,
"skipCount": 0,
"summary": "Login flow has critical issues...",
"aiReport": {
"id": "ar_xyz789",
"status": "completed",
"healthScore": 62,
"executiveSummary": "The app has 2 critical issues...",
"bugCount": 2,
"bugs": [{ "title": "Login fails silently", "severity": "critical", "suggestedFix": "Add error handling..." }],
"strengths": ["Clean responsive layout"],
"recommendations": ["Add form validation feedback"]
}
}
}
The event name is available as both event (original) and type (Stripe-style alias). jobId, projectId, and dashboardReportUrl appear at the top level for easy routing and also inside data for permissive parsers. dashboardReportUrl is only present when the event is about a job.
Verifying Signatures
Every webhook payload is signed with HMAC-SHA256 using your project's webhook secret. Check the X-TMV-Signature header.
const crypto = require('crypto');
const expected = crypto
.createHmac('sha256', process.env.TMV_WEBHOOK_SECRET)
.update(rawBody)
.digest('hex');
if (expected === req.headers['x-tmv-signature']) {
// legit — process it
}
Retry Policy
3 attempts with exponential backoff (1s, 10s, 60s) on non-2xx responses. After 3 failures, the webhook is disabled — re-enable it from project settings.
Changelog
Replit Integration API: POST /v1/replit/test one-shot testing endpoint with AI auto-scoping, feature-focused checklists, project auto-linking, regression detection. GET /v1/replit/status/:jobId rich progress polling. POST /v1/replit/prepare-next regression test preparation. GET /v1/projects/:id/history test history with trends, regressions, and health scores. Smart deploy hooks auto-include regression items from previous failures and prepared fixes. job.ai_report_ready webhook event.
Initial API release. 10 endpoints: submit jobs, list jobs, get status, reports, AI report, retest, bulk submit, bulk status, projects, credits. AI-powered report analysis with health scores, bug detection, and actionable fix suggestions. job.completed webhook includes full AI report with bugs, health score, and recommended fixes.