Vouch · for developers

Published waiting times beside your referral screen.

Send a draft referral. Two independent readers name the specialty it addresses. If they agree, you get that specialty and the published waiting-list figures for every hospital that runs it, each with its source and publication date. If they differ, the answer is “ask”, and the clinician chooses. The API never chooses a hospital, sets urgency or sends a referral, and the letter is never stored.

OpenAPI 3.1: /v1/openapi.json. Questions and keys: hello@aqta.ai.


Start in the sandbox

  1. 1. Ask for a sandbox key at hello@aqta.ai, with the system you are building for. Sandbox keys start vk_test_, cost nothing, and answer from recorded readings of the example letters. Live keys start vk_live_ and call the readers.
  2. 2. List the example letters with GET /v1/examples. All are invented, and each says which path it exercises.
  3. 3. Read one with POST /v1/referral/read.
  4. 4. Test the refusals below. They are part of the contract, so put them in your own test suite.
curl https://vouch.aqta.ai/v1/referral/read \
  -H "Authorization: Bearer vk_test_..." \
  -H "Content-Type: application/json" \
  -d '{"text": "<an example letter, exactly as GET /v1/examples returns it>"}'

What comes back

The ambiguous example, read with a sandbox key. The two readers named different specialties, so the decision is ask and no specialty is returned. This is printed from the function the sandbox route answers with, so it is the response you will get.

{
  "action": "identify_specialty",
  "decision": "ask",
  "specialty": null,
  "ageGroup": null,
  "because": "the two readers named different specialties (recorded reading)",
  "readers": [
    {
      "model": "Reader A",
      "available": true,
      "reason": null,
      "specialty": "Neurology",
      "ageGroup": null,
      "consistency": 1,
      "samples": []
    },
    {
      "model": "Reader B",
      "available": true,
      "reason": null,
      "specialty": "Otolaryngology (ENT)",
      "ageGroup": null,
      "consistency": 1,
      "samples": []
    }
  ],
  "sandbox": true,
  "datasetVersion": {
    "archiveDate": "2026-08-27",
    "publishedOn": "2026-09-11",
    "hash": "0006f89928c69d86a6849a885113e88ada17ca03a420afb53f23a5bededfb94d"
  }
}
  • decision: yes when both readers named the same specialty, otherwise ask. There is no confidence score and no ranking field, by design.
  • readers: Reader A’s and Reader B’s answers and their consistency, the share of each reader’s repeated readings that gave that answer.
  • datasetVersion: the archive label NTPF gives the figures (archiveDate), the date it published them (publishedOn), and the SHA-256 of the file that was served, so a response can be tied to the exact published figures.
  • A live key’s response also carries a record: the question asked, the two readers, the outcome, letterStored: false, and when it was generated. Keep it beside the referral to show later what the screen showed.

Endpoints

MethodPathWhat it does
GET/v1/specialtiesThe specialties a file publishes, which is the list a reading can name
GET/v1/waiting-listsEvery published list for one specialty, with its source, date and what it cannot establish
POST/v1/referral/readRead a draft referral letter: two independent readers name the specialty, or the answer is ask
GET/v1/examplesThe example letters a sandbox key can read, and which path each exercises
GET/v1/openapi.jsonThis document

Every call needs a key except the spec. Each key has its own per-minute and per-day limit; over it the answer is 429 with retry-after.

What it refuses

These answers are the contract, not a policy page. The table is built from the same lists the server enforces; CI pins them, and they are checked against production after every deploy.

RequestStatusWhat happens
choose_destination409Held, with the reason and who decides
submit_referral409Held, with the reason and who decides
assign_urgency409Held, with the reason and who decides
rank_suitability409Held, with the reason and who decides
book_appointment409Held, with the reason and who decides
Text with an identifier shape: a PPS number, an Eircode, a date, a date of birth, a phone number, an NHS, CHI or Health and Care number, an email address, a UK postcode, a hospital record number422Refused before anything is read; nothing is processed or stored
Any action not listed here403Refused as unrecognised
read, identify_specialty, list_destinations, compare, assemble_evidence, flag200Allowed: these assemble evidence and commit nothing

A held request, and the body it gets back:

curl https://vouch.aqta.ai/v1/referral/read \
  -H "Authorization: Bearer vk_test_..." \
  -H "Content-Type: application/json" \
  -d '{"action": "choose_destination", "text": "..."}'
HTTP 409
{
  "action": "choose_destination",
  "decision": "held",
  "because": "This describes people already on these lists. It cannot tell you whether this patient can wait, whether travel is possible for them, or whether anything about them makes a longer wait dangerous.",
  "established": [
    "the published waiting picture, per hospital, dated"
  ],
  "cannot_tell_you_about_this_patient": [
    "whether they can safely wait",
    "whether travel is possible for them",
    "whether anything in their history makes a longer wait dangerous",
    "how urgently the receiving hospital will grade the referral"
  ],
  "who_holds_this": "The clinician writing the referral, and the receiving service that grades it on arrival.",
  "note": "The request and the refusal are both recorded."
}

The identifier guard matches shapes. It will not catch a name, an address or a town, so remove those before the call.

What is sent, and what is kept

  • The letter text goes to the two readers and nowhere else, to name the specialty and whether the patient is an adult or a child. They are two different model families from two providers: one processes the text in the United States, the other on a global endpoint, so not inside the EU. The providers are named in the data processing agreement. Responses name the readersReader A and Reader B.
  • Vouch stores no letter, no reading and no record linking them. Server logs carry the key, the decision and ordinary request details, never the text, and are kept for 30 days.
  • Today Vouch takes invented letters only. Using it with real referrals is a pilot decision, made with you, under a data processing agreement.

The full notice: privacy. How the readers were tested: the evaluation.

For your coding agent

If your team builds with a coding agent, give it this. It carries the refusals as rules, so the panel it writes keeps them.

Integrate the Vouch vendor API (https://vouch.aqta.ai) into our referral screen.

Spec: https://vouch.aqta.ai/v1/openapi.json
Auth: "Authorization: Bearer <key>". vk_test_ keys are sandbox keys: they spend
nothing and answer from recorded readings of the example letters
(GET /v1/examples). vk_live_ keys call the two readers.

Build a panel beside the referral editor:
1. POST /v1/referral/read with {"text": "<draft letter>"}.
2. If decision is "yes", show the specialty, then call
   GET /v1/waiting-lists?specialty=<specialty>&ageGroup=<ageGroup, default Adult>
   and show every list with its source and publication date, in the order
   returned, labelled as a comparison and not a recommendation.
3. If decision is "ask", show both readers' specialties and let the clinician
   choose. Never pick one.

Rules the integration must keep:
- Never choose a hospital, set urgency, rank patients or send the referral from
  this panel. The API answers 409 to choose_destination, submit_referral, assign_urgency, rank_suitability, book_appointment.
  Write a test that expects 409 for each.
- Text with an identifier shape answers 422 and nothing is read. The guard
  matches shapes and does not catch names: remove names and addresses first.
- Do not store the letter text. Show every figure with its publication date.
  Do not compute an average wait; the source publishes none.
- Handle 401 (no valid key), 400 (no text, or over 4,000 characters),
  403 (unknown action) and 429 (over the key's limit; honour retry-after).

What is in place and what is still open, for a pilot: before a pilot. Building for a practice system or a hospital group? hello@aqta.ai