Reference: POST /runs/ingest/junit
Endpoint for sending a JUnit XML file to Qably: query parameters, the 202 response, and how runs are created.
Allows submitting raw JUnit XML reports directly for server-side parsing. Shares identical authentication requirements with POST /runs/ingest.
Request
The request body carries raw XML with Content-Type set to application/xml or text/xml (up to 10 MB). Additional run parameters are passed as query string arguments.
| Query parameter | Required | Notes |
|---|---|---|
externalId | yes | Idempotency key identical to POST /runs/ingest. |
source | no | "api" (default) or "github_actions". |
suiteId / suiteName | no | Same resolution rules as the JSON endpoint. When omitted, suite name defaults to the <testsuite name="..."> attribute. |
name | no | Defaults to the suite name when omitted. |
startedAt / finishedAt / commitSha / commitMessage / commitAuthor | no | Same fields supported by POST /runs/ingest. |
Example request
curl --fail --silent \
--request POST \
"https://api.qably.dev/runs/ingest/junit?externalId=ci-42" \
--header "Authorization: Bearer $QABLY_API_KEY" \
--header "Content-Type: application/xml" \
--data-binary @junit.xmlResponse
The endpoint answers 202 Accepted, unlike POST /runs/ingest, which saves the run before it responds: the report is parsed on arrival, and each run is queued and created in the background, so a run can take a moment to appear. The response body lists accepted (the number of queued runs), runs (the externalId, suiteName and jobId of each one), rejected (the suites that failed validation, with the reason), caseIdentityCollisions and truncatedFields.
One run per suite
When the XML holds several <testsuite name="..."> values and the request has neither suiteId nor suiteName, Qably creates one run per distinct suite name, and each run goes to the suite with that name. Every run gets its own externalId, derived as <externalId>-<slug>-<hash> (the suite name as a slug plus a short hash), so resending the same report updates the same runs. With a single suite name, the run keeps the externalId you sent. To keep the whole report in one run, pass suiteId or suiteName.
Case titles and errors
Case titles are extracted from the <testcase name="..."> attribute, falling back to classname when name is omitted. Together, classname and name form the case identity. Malformed XML or empty bodies return HTTP 400.