Async mutations

Every mutation — creating a submission, attaching a file — runs as an asynchronous job. You can either wait a few seconds for the result inline, or accept a job handle and poll for it.

Why mutations are asynchronous

A mutation is accepted, queued, and processed by a background worker. This keeps the API responsive and lets processing retry safely on transient failures. Two request headers let you control the experience: Idempotency-Key (safe retries) and Prefer: wait (wait inline vs. poll).

Idempotency-Key (optional)

Send an Idempotency-Key header — any unique string you generate, such as a UUID — to make a mutation safe to retry. If a request is interrupted (a dropped connection, a timeout) you can resend it with the same key and be certain you won't create a duplicate.

Idempotency-Key: 7c9e6679-7425-40de-944b-e07fc1f90ae7

Keys are remembered for 24 hours. The header is optional — omit it and each request creates a new job — but it's strongly recommended for any mutation you might retry.

Wait inline, or poll

By default a mutation returns immediately with a job handle. Add a Prefer: wait=N header (RFC 7240) to ask the API to hold the connection for up to N seconds waiting for the job to finish.

Worked example: create a submission

Submit a new-business intake and wait up to 15 seconds for the result:

curl -X POST https://api.uat.trailblazertech.com/v1/submissions \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 7c9e6679-7425-40de-944b-e07fc1f90ae7" \
  -H "Prefer: wait=15" \
  -d '{
    "externalRef": "APP-2026-000123",
    "submissionType": "application",
    "effectiveDate": "2026-09-01",
    "productId": "019986b1-d856-781c-b685-79bb74f2007f",
    "applicant": { "legalName": "Acme Widgets LLC" }
  }'

If it settles in time — 200

{
  "submissionId": "01999f7a-2c31-7b90-8a11-6d0e4f2b9c77",
  "submissionName": "Acme Widgets LLC",
  "status": "submitted"
}

If it's still running — 202

{
  "id": "job_01999f7a2c317b90",
  "status": "accepted",
  "statusUrl": "https://api.uat.trailblazertech.com/v1/jobs/job_01999f7a2c317b90"
}

id is the job handle; statusUrl is the exact URL to poll next.

Polling a job

Fetch the job by id (or just call its statusUrl):

curl https://api.uat.trailblazertech.com/v1/jobs/job_01999f7a2c317b90 \
  -H "Authorization: Bearer $ACCESS_TOKEN"

A job moves through these statuses:

StatusMeaning
acceptedQueued, not started yet.
runningBeing processed.
succeededFinished successfully; result is present.
failedFinished with an error; error is present.
processingSee the note below — returned while you poll a not-yet-settled job.

A settled (succeeded) job

{
  "id": "job_01999f7a2c317b90",
  "status": "succeeded",
  "result": {
    "submissionId": "01999f7a-2c31-7b90-8a11-6d0e4f2b9c77",
    "submissionName": "Acme Widgets LLC",
    "status": "submitted"
  },
  "createdAt": "2026-09-01T14:03:11.000Z",
  "updatedAt": "2026-09-01T14:03:17.000Z"
}

A succeeded job carries result and no error; a failed job carries error (code, message, retryable — the same error envelope) and no result.

The processing status. While a job is still in flight, GET /v1/jobs/{id} responds with HTTP 200, a Retry-After: 5 header, and a body of { "id": "...", "status": "processing", "retryAfter": 5 }. Treat processing as "not settled yet — poll again after retryAfter seconds." (The OpenAPI reference lists only accepted/running/succeeded/failed on the job schema; processing is the live polling response and is documented here.)

You can wait while polling too

GET /v1/jobs/{id} also honors Prefer: wait=N: it holds the connection up to N seconds and returns the settled result if the job finishes in that window, otherwise the processing response. This lets you long-poll instead of tight-looping.

Recommended pattern

  1. Generate an Idempotency-Key and send the mutation with Prefer: wait=15.
  2. On 200, you're done — use the result.
  3. On 202, poll statusUrl (optionally with Prefer: wait), honoring Retry-After, until the status is succeeded or failed.
  4. If the connection drops at any point, resend the original request with the same Idempotency-Key — you'll get the same job back, never a duplicate.