Hyperstruck
API

Answer API

Ask a question about an account and get an answer written from the documents that concern it, with the evidence, what changed, what is outstanding, and what was not read.

Use the Answer API to ask an agent a question in plain words and get an answer composed from the documents it holds. It reads the documents that concern your question, latest first, and writes from them. It does not search the web, it does not guess, and it says what the documents do not cover.

Documents reach an account through the metadata you send with them, so read Documents API first. A document submitted without a record_context reaches no account, and a question about that account will not find it however well written the question is.

Access control

Needs claims:read and agents:read. The answer carries obligations, which are reachable nowhere else under claims:read alone.

Ask a question

POST /agents/{agent_id}/answer is synchronous with a 60 second deadline. It writes nothing and earns no attribution, so asking the same question twice changes nothing.

curl -X POST \
  -H "Authorization: Bearer <API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{"question": "Where are Meridian Books up to on arrears and pricing, what has changed, and what is outstanding?"}' \
  "https://api.hyperstruck.com/agents/<AGENT_ID>/answer"

Prop

Type

Unknown fields are rejected rather than ignored.

Read the answer in this order

bottom_line first, then answer. Either can be null with a typed reason beside it, and nothing else in the response depends on them: documents, findings and boundary are the evidence, and they are there whether or not an answer was composed.

Prop

Type

agent_id, question, as_of, subject, selection, claims, multi_valued_slots and sources are also returned.

change is about the claim shelf, not about your documents

This is the one field people read wrongly. change reports supersession: a stored value formally replaced by a newer one. It is not "what changed since the last meeting".

Change across documents is written into answer, in its own section. So an answer can correctly describe three quarters of movement in prose while change.entries is empty and change.statements reads never_superseded, because three independent documents each stating their own values supersede nothing.

statements is never empty when entries is, precisely so that silence here cannot be read as "nothing has changed". never_superseded is only ever said when it was measured; when nothing was counted you get change_unmeasured instead, and when the scope holds replaced values that are not on this page you get superseded_not_shown with superseded_count beside it.

If you want to know what changed, read answer.

A real response

Three quarterly account reviews for one account, submitted through the documents door, with three obligations recorded against it. The question asked was the one in the curl above. Long arrays are trimmed and every trim is marked; everything shown is exactly what came back.

{
  "question": "Where are Meridian Books up to on arrears and pricing, what has changed, and what is outstanding?",
  "as_of": "2026-09-16T01:43:19.204118Z",
  "bottom_line": "Arrears rose from current to $62,400; committed payments remain outstanding, pricing rises from $3.95 to confirmed $4.65 from 1 January 2027, express stays $11.00, and schools payment/INV-88344 status are unconfirmed.",
  "answer": "### Arrears\n- Meridian Books is currently $62,400 in arrears across invoices INV-88201 and INV-88344; INV-88201 was not cleared by the committed 31 July deadline. (2026-09-15)\n- Dana committed to pay INV-88201 in full by 30 September 2026 and the remaining arrears by 31 October 2026; both payments remain outstanding. (2026-09-15)\n\n### Pricing\n- From 1 January 2027, Meridian moves to the 2027 rate card, increasing per-title handling from $3.95 to $4.65. (2026-09-15)\n- The express tier will remain $11.00 throughout 2027, subject to the condition Dana accepted and Sam agreed to. (2026-09-15)\n\n### What has changed since earlier documents\n- Arrears changed from $0, with Meridian current and paid within terms for six consecutive quarters, on 10 March 2026, to $41,000 on INV-88201 on 12 June 2026, and then to $62,400 across two invoices on 15 September 2026. (2026-03-10; 2026-06-12; 2026-09-15)\n- The 2026 per-title price was $3.95 on 12 June 2026 and remains the current rate, but the confirmed 2027 price is $4.65; the express tier remains $11.00 for 2027. (2026-06-12; 2026-09-15)\n- Earlier, no pricing change was planned for 2026; a 2027 increase was only being prepared, whereas the increase is now confirmed. (2026-03-10; 2026-06-12; 2026-09-15)\n\n### What is outstanding\n- The arrears payments due by 30 September and 31 October 2026 remain outstanding. (2026-09-15)\n- The documents do not state whether the schools-contract payment has landed or provide the due dates or status of INV-88344. (2026-09-15)",
  "answer_unavailable_reason": null,

  "findings": [
    {
      "document_ref": "crm/meridian-books/review-2026-09-15",
      "kind": "missing_or_blocking",
      "text": "Meridian Books is now 62,400 dollars in arrears across invoices INV-88201 and INV-88344.",
      "claim_ids": ["f7abbe41-f5c9-4293-aa17-b69c18c3f5c1"]
    }
    // ... 13 more findings, each with its document_ref, kind and claim_ids
  ],

  "documents": [
    {
      "document_ref": "crm/meridian-books/review-2026-09-15",
      "title": "Meridian Books quarterly account review, September",
      "read": "main",
      "occurred_at": "2026-09-15T02:00:00Z"
      // ... each document also carries its claims inline
    },
    { "document_ref": "crm/meridian-books/review-2026-06-12", "read": "main", "occurred_at": "2026-06-12T02:00:00Z" },
    { "document_ref": "crm/meridian-books/review-2026-03-10", "read": "main", "occurred_at": "2026-03-10T02:00:00Z" }
  ],

  "boundary": {
    "is_complete": true,
    "budget_chars": 400000,
    "used_chars": 5729,
    "main_read_since": "2026-03-10T02:00:00Z",
    "dropped": [],
    "not_read": []
  },

  "change": {
    "entries": [],
    "statements": ["never_superseded", "staleness_unavailable"],
    "superseded_count": 0
  },

  "obligations": {
    "items": [
      {
        "obligation_id": "1c0d8e8e-40a6-43b3-9567-8e12cc88ad1c",
        "statement": "Clear invoice INV-88201 in full",
        "status": "open",
        "is_terminal": false,
        "entity_id": "79c291bb-d44a-43e7-bd06-3471a3167d25",
        "entity_name": "Meridian Books",
        "due_at": "2026-09-30T14:00:00Z",
        "due_local": "2026-09-30",
        "due_tz": "Australia/Sydney",
        "due_precision": "date",
        "premise_claim_ids": []
      }
      // ... 2 more obligations
    ],
    "is_available": true,
    "unavailable_reason": null,
    "is_truncated": false,
    "is_as_of_pinned": false
  }

  // subject, selection, completeness, claims, multi_valued_slots, silence and sources also returned
}

Note the fourth section of answer. What is outstanding is there because the obligation shelf had rows; with no obligations recorded, the answer has three sections rather than four and obligations.items is empty.

Note also due_at of 2026-09-30T14:00:00Z against due_local of 2026-09-30. Under date precision the instant is the end of the day meant, in the named zone, so reading due_at as a date will state the day after in some zones and the day before in others. Read due_local and due_tz when you are showing a date to a person.

When there is no answer

A 200 with answer: null is a normal, informative outcome, not a failure. The reason is typed:

answer_unavailable_reason is a closed set of six:

answer_unavailable_reasonWhat it means
nothing_readThe read reached no linked document. Check the account links on the agent's documents before blaming the question; a document with no record_context concerns reaches no account.
read_deadlineThe document read did not finish in time, so there was nothing to write from.
findings_unavailableThe composition could not be reached or could not start.
composer_deadlineThe composition started and did not finish inside its stage deadline.
composer_failedThe composition ran and failed.
composer_unavailableNo composition capacity was available to start.

bottom_line can be null while answer is not, and it carries its own closed set of five in bottom_line_unavailable_reason: no_answer (there was no answer to summarise), deadline, failed, unavailable, and too_long (the answer could not be reduced to one sentence inside the bound). Read the two independently rather than assuming one implies the other.

StatusWhy
403Either this key lacks claims:read or agents:read, or the tenant is over its configured spend cap for the billing window. An agent you cannot see, and an agent that does not exist, both answer 403 rather than 404, because the authorization check fails closed and will not confirm whether an id is real.
429Too many claim reads for this key this minute. The answer shares POST /claims/question's limit, because its read is that read. Safe to retry after the window.
501The deployed runtime predates the answer. Retrying will not help.
503Either a claim migration has not been applied, or too many reads or compositions are already running for this server to start another. Retrying is safe.
504The answer did not finish inside its 60 second deadline. Nothing was written, so retrying is safe.

You are only billed for an answer you receive

A call that returns nothing releases its spend hold, so a 503 or a 504 costs you nothing. The composition we ran and lost on the way back is ours to absorb.

Size and latency

The composed part of the response is small, typically under 2 KB. The evidence is not. Each document read carries its claims inline, and a top-level claims array carries the same claims arranged by claim, so the response grows by roughly 13 KB per document read. Three documents produced an 80 KB response.

If you are sizing a timeout, a mobile payload or a log line, size it on the number of documents an account can accumulate rather than on the example above. budget_chars bounds how much document text is read; it does not bound how much evidence is serialised back.

Ten sequential questions over the three documents above answered in a median of 7 seconds, none over 15. Latency grows with the number of documents read, and the 60 second deadline is a ceiling rather than a target.

  • Documents API, how documents get in and how they reach an account.
  • Claims API, to inspect and govern the claims the answer rests on.
  • Claims, for what a claim is.