---
title: "Collect an async Answer Engine result by claim ticket"
method: GET
path: "/api/answer/{ticketId}"
---

# Collect an async Answer Engine result by claim ticket

`GET /api/answer/{ticketId}`

## Request

- Base URL: `https://api.signalbureau.ai`
- URL: `https://api.signalbureau.ai/api/answer/{ticketId}`
- Auth: none declared

The other half of the async ask flow. Free and unmetered — the work was paid for once at submit. status values: working (poll again after pollAfter seconds), done (the complete answer rides in this response), failed (reason in error; ask again), lost (no progress past the 5-minute horizon AFTER a re-drive was also tried; ask again). A ticket still working past the published 120-second bound is re-driven once, automatically — the response then carries redriven:true and redrivenAt, the answer still lands under the same ticket, and the lost clock restarts from the re-drive. Tickets are unlisted uuid capability URLs, same model as /s/<id> share links. 30 requests/minute burst per network address (shared egress shares the allowance).

## Path parameters

- `ticketId` string, uuid, required

## Response `200`

Ticket state: {status:"working", pollAfter} or {status:"done", ...full answer} or {status:"failed"|"lost", error}.

- object: Shape varies by `status`; the done/failed/lost shapes are summarized in this response's own description above. Documented here is the `working` shape specifically — the one that changed: a run still in progress now says whether it is on schedule, instead of reading identically to one that just started.
  - `status` 'working' | 'done' | 'failed' | 'lost': The fields below (elapsedSeconds, late, message) ride only when status is "working".
  - `elapsedSeconds` number: Present when status is "working" — seconds since the ticket was created; the same value as `ageSeconds`, under the name a poller reaches for (`ageSeconds` stays present too, for every client already reading it).
  - `late` boolean: Present when status is "working". True once `elapsedSeconds` passes the published 30–120 second range for a typical async answer — the honest middle state between "working" and "lost" (a run is only reported lost past 300 seconds): a run behind schedule now says so instead of reading identically to one that just started.
  - `message` string: Present only when `late` is true — states the elapsed seconds, the published range, whether the run was re-driven, and that it has not been dropped (lost is declared only 300 seconds after the re-drive).
  - `redriven` boolean: Present (and always true) once this ticket has been re-driven. A run still working past the published 120-second bound is re-fired exactly once — a compare-and-set in the ticket store, so polling repeatedly cannot fire it twice and concurrent pollers fire it once between them. The answer lands under the same ticketId; nothing is re-metered.
  - `redrivenAt` string, date-time, nullable: Present with `redriven` — when the re-drive was claimed. Null in the rare case where a concurrent poller claimed it in the same instant and this response did not re-read the row; the next poll carries the timestamp.

## Other responses

- `404`: No ticket with that id.
- `422`: Malformed ticket id.
- `429`: Polling faster than the stated burst limit.
- `503`: Ticket store briefly unreachable; the question was not lost.

---

[API](https://skmtc.dev/sigb/apis/signal-bureau-api.md) · [All operations](https://skmtc.dev/sigb/apis/signal-bureau-api/llms.txt) · [OpenAPI document](https://skmtc.dev/sigb/apis/signal-bureau-api/revisions/290c9c6ca0d0?raw)
