M00N ReportIngest API reference

Ingest API v2 reference

Generated from the same OpenAPI document the ingest service serves at /api/ingest/v2/openapi.json (version 2.0.0). Authentication: X-API-Key header on every request (the liveness probes below are the exception).

GET /healthz

Liveness probe - Health check served directly by the ingest service: self-hosted deployments, local development, direct ingest URLs, and the conformance mock. The official Playwright JS reporter currently probes this path before streaming, with a 2 second timeout, and disables itself for the run if it is unreachable. NOTE: on the m00nreport.com cloud apex this path is currently handled by the frontend edge - only specific path prefixes (such as /api/*) reach the backend there, and /healthz is not among them - use /api/ingest/health when probing through that edge instead. No authentication. Any server implementing this contract should serve BOTH health paths.

{
  "ok": true,
  "service": "ingest"
}

Responses: 200 (Service is alive)

GET /api/ingest/health

Liveness probe (cloud-routable) - Same liveness semantics as /healthz, but routable through edges/proxies where only specific path prefixes (such as /api/*) reach the backend - including the m00nreport.com cloud, where /healthz is not among the reachable prefixes. Prefer this path when probing through such an edge. No authentication.

{
  "ok": true,
  "service": "ingest"
}

Responses: 200 (Service is alive)

POST /api/ingest/v2/run/start

Start a run - Creates a run. The project is derived from the API key. Returns the server-generated runId used by all subsequent calls.

FieldTypeRequiredDescription
launchstringyesLaunch (run) title shown in the UI.
tagsarraynoLabels shown on the launch, usable as dashboard filters.
totalnumbernoExpected total number of tests, if known upfront.
startedAtstringnoISO 8601 timestamp.
attributesobjectnoOptional key-value metadata shown on the run (e.g. environment, branch). Accepted but not schema-enforced at runtime.
metadataobjectnoOptional structured metadata (CI context etc). Accepted but not schema-enforced at runtime.
{
  "launch": "Nightly regression",
  "tags": [
    "smoke"
  ],
  "total": 42,
  "startedAt": "2026-07-17T21:00:00.000Z"
}

Responses: 200 (Success), 401 (Missing or invalid API key), 403 (Run belongs to another organization, or writes are blocked by subscription status), 500 (Unexpected server error)

POST /api/ingest/v2/test/start

Register a test attempt (fire-and-forget) - Registers a client-generated testId so steps can stream before the test finishes. Safe to send without awaiting the response.

FieldTypeRequiredDescription
runIdstringyesRun UUID returned by run/start.
testIdstringyesClient-generated UUID identifying this test attempt group.
titlePatharrayyesPath from file/module to test title, e.g. ['auth.spec.ts', 'Login', 'valid credentials'] - the last element is the display title, earlier elements group the test into suites, and a file-like first element becomes the FILE badge (a trailing [chromium]/[firefox]/[webkit] segment renders a browser badge). Reused unchanged across retries. See the guide's "Naming tests: titlePath" section for full semantics and per-ecosystem examples.
filePathstringnoOptional source file path, shown alongside the test. Independent of titlePath; not required for the FILE badge, which is derived from titlePath[0].
retrynumbernoZero-based retry attempt index.
startedAtstringnoISO 8601 timestamp.
{
  "runId": "3f0f1e6a-7b1c-4b6e-9a52-1c2d3e4f5a6b",
  "testId": "9a1b2c3d-4e5f-4a6b-8c7d-0e1f2a3b4c5d",
  "titlePath": [
    "auth.spec.ts",
    "Login",
    "valid credentials"
  ],
  "filePath": "tests/auth.spec.ts",
  "retry": 0,
  "startedAt": "2026-07-17T21:00:01.000Z"
}

Responses: 200 (Success), 401 (Missing or invalid API key), 403 (Run belongs to another organization, or writes are blocked by subscription status), 409 (Run is no longer active (stopped or interrupted)), 500 (Unexpected server error)

POST /api/ingest/v2/test/complete

Complete a test attempt

FieldTypeRequiredDescription
testIdstringyesSame client-generated UUID sent to test/start for this attempt.
runIdstringyesRun UUID returned by run/start.
titlePatharraynoPath from file/module to test title, e.g. ['auth.spec.ts', 'Login', 'valid credentials'] - the last element is the display title, earlier elements group the test into suites, and a file-like first element becomes the FILE badge (a trailing [chromium]/[firefox]/[webkit] segment renders a browser badge). Reused unchanged across retries. See the guide's "Naming tests: titlePath" section for full semantics and per-ecosystem examples.
filePathstringnoOptional source file path, shown alongside the test. Independent of titlePath; not required for the FILE badge, which is derived from titlePath[0].
annotationsobjectnoFree-form key-value metadata for this attempt (e.g. a caseId annotation for test case linking).
retrynumbernoZero-based retry attempt index; must match the value sent to test/start.
startedAtstringnoISO 8601 timestamp.
endedAtstringnoISO 8601 timestamp.
statuspassed | failed | skipped | timedOut | timedout | interruptedyesFinal outcome of this attempt.
durationnumbernoMilliseconds. Computed from startedAt/endedAt if omitted.
errorobjectnoFailure details (message, stack, etc). Required in practice when status is failed/timedOut.
stepsarray of EmbeddedStepnoSteps not already sent via steps/stream, in EmbeddedStep shape.
attachmentsarray of EmbeddedAttachmentnoInline base64 attachments in EmbeddedAttachment shape; empty when files were uploaded separately via attachment/upload, attachment/stream, or presign+confirm.
{
  "runId": "3f0f1e6a-7b1c-4b6e-9a52-1c2d3e4f5a6b",
  "testId": "9a1b2c3d-4e5f-4a6b-8c7d-0e1f2a3b4c5d",
  "titlePath": [
    "auth.spec.ts",
    "Login",
    "valid credentials"
  ],
  "status": "passed",
  "retry": 0,
  "duration": 4210,
  "startedAt": "2026-07-17T21:00:01.000Z",
  "endedAt": "2026-07-17T21:00:05.210Z",
  "steps": [],
  "attachments": []
}

Responses: 200 (Success), 401 (Missing or invalid API key), 403 (Run belongs to another organization, or writes are blocked by subscription status), 409 (Run is no longer active (stopped or interrupted)), 500 (Unexpected server error)

POST /api/ingest/v2/steps/stream

Stream live step events (batched) - Batch of step events, possibly spanning multiple tests and runs. Every unique runId in the batch is ownership-checked.

FieldTypeRequiredDescription
itemsarray of StepItemyesBatch of StepItem events, up to 1000 per call. May span multiple tests and runs.
{
  "items": [
    {
      "runId": "3f0f1e6a-7b1c-4b6e-9a52-1c2d3e4f5a6b",
      "testId": "9a1b2c3d-4e5f-4a6b-8c7d-0e1f2a3b4c5d",
      "title": "page.click(#login)",
      "action": "end",
      "status": "passed",
      "duration": 120,
      "nestingLevel": 0,
      "stepIndex": 3
    }
  ]
}

Responses: 200 (Success), 401 (Missing or invalid API key), 403 (Run belongs to another organization, or writes are blocked by subscription status), 409 (Run is no longer active (stopped or interrupted)), 500 (Unexpected server error)

POST /api/ingest/v2/run/end

End a run

FieldTypeRequiredDescription
runIdstringyesRun UUID returned by run/start.
statuspassed | failed | timedOut | timedout | interrupted | finished | completednoFinal outcome of the run as a whole.
endedAtstringnoISO 8601 timestamp.
{
  "runId": "3f0f1e6a-7b1c-4b6e-9a52-1c2d3e4f5a6b",
  "status": "finished",
  "endedAt": "2026-07-17T21:05:00.000Z"
}

Responses: 200 (Success), 401 (Missing or invalid API key), 403 (Run belongs to another organization, or writes are blocked by subscription status), 500 (Unexpected server error)

POST /api/ingest/v2/attachment/upload

Upload attachments (buffered multipart) - For small files. Multipart form with runId, testId fields and up to 10 file parts.

multipart/form-data request: fields runId, testId plus up to 10 file parts (200 MB each).
FieldTypeRequiredDescription
runIdstringyesRun UUID returned by run/start.
testIdstringyesClient-generated test UUID.
filestringyesOne or more file parts. Max 10 files, 200MB each.

Responses: 200 (Success), 400 (runId and testId are required, or no files provided), 401 (Missing or invalid API key), 403 (Run belongs to another organization, or writes are blocked by subscription status), 409 (Run is no longer active (stopped or interrupted)), 413 (Per-run attachment byte quota exceeded), 500 (Unexpected server error)

POST /api/ingest/v2/attachment/stream

Upload large attachments (streaming multipart) - For large files (videos, traces). Streams to storage without buffering. Send runId and testId fields BEFORE file parts.

multipart/form-data request: fields runId, testId plus up to 10 file parts (200 MB each).
FieldTypeRequiredDescription
runIdstringyesRun UUID returned by run/start.
testIdstringyesClient-generated test UUID.
filestringyesOne or more file parts. Max 10 files, 200MB each.

Responses: 200 (Success), 400 (runId and testId are required, or no files uploaded), 401 (Missing or invalid API key), 403 (Run belongs to another organization, or writes are blocked by subscription status), 409 (Run is no longer active (stopped or interrupted)), 413 (Per-run attachment byte quota exceeded), 500 (Unexpected server error)

POST /api/ingest/v2/attachment/presign

Get a presigned direct-upload URL (or check capability) - With {"check": true}: returns whether direct upload is supported. Otherwise returns a presigned PUT URL valid for 10 minutes; upload the file there, then call attachment/confirm.

FieldTypeRequiredDescription
checkbooleannoIf true, only report capability.
runIdstringnoRun UUID returned by run/start.
testIdstringnoClient-generated test UUID.
filenamestringnoName of the file to be uploaded.
contentTypestringnoMIME type of the file to be uploaded.
sizenumbernoFile size in bytes; reserved against the run quota at presign time.
{
  "runId": "3f0f1e6a-7b1c-4b6e-9a52-1c2d3e4f5a6b",
  "testId": "9a1b2c3d-4e5f-4a6b-8c7d-0e1f2a3b4c5d",
  "filename": "trace.zip",
  "contentType": "application/zip",
  "size": 1048576
}

Responses: 200 (Success), 400 (Missing required fields: runId, testId, filename, contentType), 401 (Missing or invalid API key), 403 (Run belongs to another organization, or writes are blocked by subscription status), 409 (Run is no longer active (stopped or interrupted)), 413 (Per-run attachment byte quota exceeded), 500 (Unexpected server error)

POST /api/ingest/v2/attachment/confirm

Confirm a direct upload - Records the attachment after a successful presigned PUT. Quota was already reserved at presign time.

FieldTypeRequiredDescription
runIdstringyesRun UUID returned by run/start.
testIdstringyesClient-generated test UUID.
attachmentIdstringyesThe attachmentId returned by attachment/presign.
filenamestringyesName of the uploaded file.
contentTypestringnoMIME type of the uploaded file.
sizenumbernoFile size in bytes.
{
  "success": true,
  "attachmentId": "7c8d9e0f-1a2b-4c3d-8e5f-6a7b8c9d0e1f",
  "objectName": "autotests-attachments/7c8d9e0f-1a2b-4c3d-8e5f-6a7b8c9d0e1f"
}

Responses: 200 (Success), 400 (Missing required fields: runId, testId, attachmentId, filename), 401 (Missing or invalid API key), 403 (Run belongs to another organization, or writes are blocked by subscription status), 409 (Run is no longer active (stopped or interrupted)), 500 (Unexpected server error)

Component schemas

Named schemas referenced above by name (e.g. an array of EmbeddedStep field in a table) are defined here.

ErrorEnvelope

FieldTypeRequiredDescription
errorstringnoShort error category, e.g. Unauthorized, Forbidden, Conflict.
messagestringnoHuman-readable detail; not machine-parseable, use code instead.
codeAPI_KEY_REQUIRED | INVALID_API_KEY_FORMAT | INVALID_API_KEY | RUN_ACCESS_DENIED | RUN_NOT_ACTIVE | RUN_ATTACHMENT_LIMIT_EXCEEDEDnoMachine-readable error code.

RunQuota

Per-run attachment byte quota, by pricing tier.

FieldTypeRequiredDescription
usedBytesnumbernoBytes already consumed by this run.
limitBytesnumbernoTotal bytes allowed for this run.
remainingBytesnumbernolimitBytes minus usedBytes.

AttachmentResult

One stored attachment, as returned by attachment/upload and attachment/stream.

FieldTypeRequiredDescription
idstringnoAttachment UUID.
namestringnoOriginal file name.
contentTypestringnoMIME type, as provided by the upload.
sizenumbernoFile size in bytes.

StepItem

One step event. Items without runId, testId and title are silently skipped by the server.

FieldTypeRequiredDescription
runIdstringyesRun UUID this step belongs to; ownership-checked per batch.
testIdstringyesClient-generated test UUID.
titlestringyesStep label shown as the row title.
actionstringno'begin' while running, 'end' when finished. Only 'end' (or a final status) is persisted.
statusstringno'running' while in progress, then 'passed' or 'failed'.
categorystringnoOptional grouping tag, e.g. 'pw:api' for framework-native steps.
durationnumbernoMilliseconds.
errorobjectnoFailure details for this step, if any.
nestingLevelnumbernoZero-based depth for nested steps.
stepIndexnumbernoStable per-test index; used to dedupe against test/complete steps.

EmbeddedStep

One step embedded in test/complete's steps array (distinct from steps/stream's StepItem: no runId/testId per item, since the parent call already carries both for the whole array). No field is required - the server accepts partial items and falls back to array position for ordering when index/stepIndex is omitted.

FieldTypeRequiredDescription
titlestringnoStep label shown as the row title.
categorystringnoOptional grouping tag, e.g. 'pw:api' for framework-native steps.
statusstringno'passed', 'failed', 'skipped' or similar.
durationnumbernoMilliseconds.
errorobjectnoFailure details for this step, if any.
nestingLevelnumbernoZero-based depth for nested steps.
indexnumbernoZero-based step order within this test attempt. This is the field name the official reporter sends; falls back to array position if omitted.
stepIndexnumbernoAccepted as an alias for 'index' if 'index' is not present.

EmbeddedAttachment

One inline base64-encoded attachment embedded in test/complete's attachments array (distinct from AttachmentResult, the shape the upload endpoints return). Only sent when the reporter uses binaryAttachments:false; with the default binaryAttachments:true, files are uploaded separately via attachment/upload or attachment/stream and this array is empty. Items without data or name are silently skipped by the server.

FieldTypeRequiredDescription
idstringnoClient-generated attachment UUID. Generated server-side if omitted.
namestringyesFile name shown on the attachment.
contentTypestringnoDefaults to 'application/octet-stream' if omitted.
datastringyesBase64-encoded file content.
compressedbooleannoIf true, data is gzip-compressed before base64 encoding and is decompressed server-side.
sizenumbernoInformational only: the server recomputes size from the decoded buffer and ignores this value.