
Batch Upsert Origin Check Runs via the API
Atomically upsert several check runs that share one suite with POST https://api.cursor.com/v1/origin/repos/{ownerSlug}/{repoName}/check-runs:batchUpsert. Official reference: Origin API → Checks / Batch Upsert Check Runs (OpenAPI OriginService_BatchUpsertCheckRuns).
Origin is in Early Beta and subject to change. Authenticate with an installation access token (oit_…) that carries repository:checks:write. The write is attributed to the app that owns the authenticated installation. Availability: Early Beta per the API overview.
The request accepts 1–10 runs under one checkSuite and rejects duplicate (externalId, key) identities. Every run commits or the entire request rolls back. Keep suite and run key values stable and readable (required checks match on app + suite key, optionally run key; name is display-only). Reuse externalId to update an attempt; use a new externalId for a retry so the earlier attempt stays as history. externalUpdatedAt orders updates so stale retries cannot overwrite newer state. Do not send status: rerequested — only Origin sets that; a request carrying it returns InvalidArgument (HTTP 400).
Cost: 5 points against the principal budget (ordinary write).
Path parameters
| Param | Required | Meaning |
|---|---|---|
ownerSlug |
yes | Owning entity's unique slug |
repoName |
yes | Repo name, unique to the owner |
Request body
| Field | Required | Meaning |
|---|---|---|
headSha |
yes | Head commit SHA (40- or 64-char hex) |
checkSuite |
yes | Shared suite input (key, name, externalId; optional detailsUrl) |
checkRuns |
yes | Array of 1–10 CheckRunInput entries with unique (externalId, key) |
Each run needs key, name, status (queued / in_progress / completed), externalUpdatedAt, and externalId. When status is completed, supply conclusion. Optional fields match single-run upsert: startedAt, completedAt, detailsUrl, output, deadlineAt, isRerequestable.
curl --request POST \
--url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/check-runs:batchUpsert' \
--header 'Authorization: Bearer YOUR_ORIGIN_TOKEN' \
--header 'Content-Type: application/json' \
--data '{
"headSha": "9a41f0c3d2b8e7f6a5c4d3e2f1b0a9c8d7e6f5a4",
"checkSuite": {
"key": "ci-8842",
"name": "CI",
"detailsUrl": "https://ci.acme.dev/runs/8842",
"externalId": "build-8842"
},
"checkRuns": [
{
"key": "ci-8842-unit-tests",
"name": "unit-tests",
"status": "completed",
"conclusion": "success",
"externalUpdatedAt": "2026-08-02T14:44:30Z",
"startedAt": "2026-08-02T14:40:00Z",
"completedAt": "2026-08-02T14:44:30Z",
"detailsUrl": "https://ci.acme.dev/runs/8842",
"externalId": "run-8842",
"output": {
"title": "Unit tests",
"summary": "128 tests passed.",
"text": "All suites green."
}
}
]
}'
Response
The body returns the upserted checkSuite and checkRuns in request order, including server-assigned crg_… / cr_… ids, repository references, timestamps, and actors.
Example shape from the OpenAPI:
{
"checkSuite": {
"id": "crg_01k2ja2000e0080000000000h8",
"repository": {
"id": "repo_01k2ja2000e0080000000000q4",
"name": "rocket",
"owner": {
"slug": "acme",
"id": "ns_01k2ja2000e0080000000000p3",
"type": "team"
}
},
"sha": "9a41f0c3d2b8e7f6a5c4d3e2f1b0a9c8d7e6f5a4",
"key": "ci-8842",
"name": "CI",
"detailsUrl": "https://ci.acme.dev/runs/8842",
"createdAt": "2026-08-01T09:30:00Z",
"updatedAt": "2026-08-02T14:45:00Z",
"externalId": "build-8842",
"actor": {
"user": {
"id": "user_01k2ja2000e0080000000000c3",
"email": "jane@acme.dev"
}
}
},
"checkRuns": [
{
"id": "cr_01k2ja2000e0080000000000g7",
"checkSuite": {
"id": "crg_01k2ja2000e0080000000000h8"
},
"sha": "9a41f0c3d2b8e7f6a5c4d3e2f1b0a9c8d7e6f5a4",
"key": "ci-8842-unit-tests",
"name": "unit-tests",
"status": "completed",
"conclusion": "success",
"externalId": "run-8842"
}
]
}
Prefer batch upsert when one CI job finishes several checks together; use single-run upsert when jobs complete on different schedules.
Base URL: https://api.cursor.com/v1/origin.