Start building

API reference · version 2026-08-22

Ambition Health API reference

Find out what you cannot sell before you sign anything: the formulary and rulebook are public. Developer operations use scoped keys, first-party operations use a console session, and pharmacy callbacks require a versioned partner signature. The specification (/v1/openapi.json on each API host, /api/v1/openapi on any deployment) is generated from the same schemas the handlers validate with, so it cannot describe a shape the API does not enforce.

New here? The quickstart goes from a sandbox key to a test patient and a signed webhook; the hand-off guide covers sending visitors from your site to your care site.

Quickstart

Production deployment These calls go to https://app.ambitionhealth.co, the deployment serving this page, on real data. Use a key issued for this deployment.

curl · production deployment
# The formulary needs no credential at all.
curl "https://app.ambitionhealth.co/api/v1/formulary?status=prohibited"

# Ask whether one substance is sellable, and why not.
curl "https://app.ambitionhealth.co/api/v1/formulary/bpc-157"

# Scan copy against the same rulebook that gates publication. The key needs
# the claims.scan scope and resolves only on the deployment that issued it.
AMBITION_API_BASE_URL="https://app.ambitionhealth.co/api/v1"
curl -X POST "$AMBITION_API_BASE_URL/claims/scan" \
  -H "authorization: Bearer $AMBITION_API_KEY" \
  -H "content-type: application/json" \
  -d '{"text":"Same active ingredient as Ozempic."}'

Sandbox keys are created in a workspace's Developer Center. The scan endpoint runs the same engine, against the same rulebook version, that gates publication inside the platform. Wire it into your CI and you find out at pull-request time instead of at launch.

API hosts and sandbox

Two planes, two hosts. The developer sandbox is a separate deployment with its own database, on synthetic data only; live is the production deployment, on real data. A key works only on the plane its prefix names. Each plane's API host at /v1 is its canonical base URL once it serves. The deployment's own origin at /api/v1 stays served as an alias, with the same routes.

PlaneBase URL (canonical once serving)AliasKey prefixData
Sandboxhttps://sandbox.api.ambitionhealth.co/v1 (Not serving yet)https://aws-nonprod.ambitionhealth.co/api/v1ak_sandbox_Synthetic only
Livehttps://api.ambitionhealth.co/v1 (Not serving yet)https://ambitionhealth.co/api/v1 (Not serving yet)ak_live_Real

A base URL marked "Not serving yet" answers nothing. Until the API host serves, call its alias, or the origin of the deployment that issued your key, followed by /api/v1 (this deployment: https://app.ambitionhealth.co/api/v1). The paths are the same: /v1/<path> on an API host is /api/v1/<path> on the alias.

Every answer says which plane served it. Ambition-Environment is sandbox or live, and Ambition-Livemode is false or true. The deployment sets both; nothing in your request changes them. Check them in your client before you treat an answer as real data.

The OpenAPI document is at /v1/openapi.json on each API host and at /api/v1/openapi on any deployment. Its servers list both planes, sandbox first, each canonical host followed by its alias and marked with its plane and whether it serves yet; while the API hosts do not serve, the deployment that served the document comes first. An API host serves the API and nothing else: no pages, no sign-in, and no cookies.

Hosted mode guides

Hosted mode: your site sells; your Ambition-hosted care site (care.<brand>) does care. These guides cover its server-to-server path: the public catalog, enrollments and launch links, signed webhooks, and what your site may and may not hold.

On this deployment: Headless Connect API: Not available in this environment · Self-enrollment on the care host: Not open yet · Webhooks to your receivers: Sandbox only · Live keys: Not available
  • Headless Connect API: Not available in this environment. What runs now: Sandbox-key calls to /api/v1 on the nonproduction deployment with synthetic data: claims scan, formulary, rulebook, the sandbox order preview, delivery reads and replay, and the MCP endpoint, on a deployment whose database has the 20261101090000-range migrations applied (a hard deploy-order dependency the merge gate enforces; ahead of them the rulebook, claims scan, MCP and replay answer 503).
  • Self-enrollment on the care host: Not open yet. What runs now: Waitlist capture with consent and privacy-notice digest on /storefront/start; prescriber-issued referral invitations. Built but not yet verified end to end and open for no brand: self-enrollment on the care host (/storefront/start?p=<handle>: email code, account, consent recorded by notice digest, admission under brand + program + state enrollment authority, intake for that program) and launch-link redemption (/storefront/launch), behind each brand's hosted.self_enrollment capability row on the deployment's own plane. A brand's production row is set live only by ah_set_brand_capability_state (a platform administrator at AAL2 with a reason, recorded in ah_capability_state_events).
  • Webhooks to your receivers: Sandbox only. What runs now: Signing, retries (19 attempts over at least about 74 hours; every 4xx but 410 retried, 410 disables the receiver), dead letters, replay and delivery receipts against the in-process synthetic receiver (.demo.invalid) on synthetic deployments; a self-serve developer connection for brands with no partner integration; and, built but not yet exercised on a deployed sandbox, sandbox delivery of synthetic events to a developer's own https receiver while the developer_webhooks sandbox rail is open; and, written and unit-tested but with its migration not yet applied and its pgTAP suite not yet run, lifecycle event producers with refs-only payloads carrying the enrollment id and externalReference (enrollment.created, subscription.created, subscription.canceled, payment.failed, and enrollment.status_changed produced only under the brand-to-entity BAA and downstream-systems attestation and re-checked at every send).
  • Live keys: Not available. What runs now: Nothing on any deployment: the governed request, approval and issuance path (20261101090100) is written but not applied to a hosted database or run, and no production database has recorded its plane.

Headless mode guides

Headless mode, the Hims/Ro model: your own site and app run the whole patient experience (account, intake, consent, checkout, portal, messaging) and your Node server calls the patient endpoints (tagged Patient (PHI) in the reference) with a phi_server key, once your patient data access is approved. Ambition is the clinical engine behind it.

On this deployment: Headless clinical API (patient data): Not available · Webhooks to your receivers: Sandbox only · Live keys: Not available
  • Headless clinical API (patient data): Not available. What runs now: Nothing for a real patient: the headless patient API is written and has run only against a local synthetic stack; no brand's patient data access is approved in any environment, and the Developer Center's clinical-loop simulator is a script with synthetic ids.
  • Webhooks to your receivers: Sandbox only. What runs now: Signing, retries (19 attempts over at least about 74 hours; every 4xx but 410 retried, 410 disables the receiver), dead letters, replay and delivery receipts against the in-process synthetic receiver (.demo.invalid) on synthetic deployments; a self-serve developer connection for brands with no partner integration; and, built but not yet exercised on a deployed sandbox, sandbox delivery of synthetic events to a developer's own https receiver while the developer_webhooks sandbox rail is open; and, written and unit-tested but with its migration not yet applied and its pgTAP suite not yet run, lifecycle event producers with refs-only payloads carrying the enrollment id and externalReference (enrollment.created, subscription.created, subscription.canceled, payment.failed, and enrollment.status_changed produced only under the brand-to-entity BAA and downstream-systems attestation and re-checked at every send).
  • Live keys: Not available. What runs now: Nothing on any deployment: the governed request, approval and issuance path (20261101090100) is written but not applied to a hosted database or run, and no production database has recorded its plane.

Public — no credential required

GET/api/v1/formularyno auth

List every substance the platform has a position on

The authoritative record of what this platform permits, refuses, and permits with conditions. Every row carries the provision that governs it, and every refusal carries a reason in plain language. This endpoint is public and unauthenticated on purpose: an operator should be able to learn what they cannot sell before they sign anything.

Query parameters

FieldTypeRequiredNotes
status"approved" | "approved_off_label" | "compoundable" | "conditional" | "prohibited"noOnly rows with this status.
categorystringnoOnly rows in this category.
searchstringnoMatches the name and aliases.
limitintegernoRows per page, 1 to 200.
cursorstringnoThe `nextCursor` of the previous page.

Example response · 200

Response
{
  "data": [
    {
      "slug": "example-slug",
      "displayName": "example-display-name",
      "aliases": [
        "example-aliases"
      ],
      "category": "example-category",
      "status": "approved",
      "controlledSchedule": "II",
      "approvedProducts": [
        {
          "brand": "example-brand",
          "application": "example-application",
          "approved": "example-approved"
        }
      ],
      "compounding": {
        "status503a": "example-status503a",
        "status503b": "example-status503b",
        "bulkBasis": "example-bulk-basis",
        "note": "example-note"
      },
      "citation": "example-citation",
      "conditions": [
        "example-conditions"
      ],
      "refusalReason": "example-refusal-reason",
      "reviewBy": "example-review-by"
    }
  ],
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment",
    "formularyVersion": "example-formulary-version",
    "total": 1,
    "nextCursor": "example-next-cursor"
  }
}

Errors

StatusCodeWhen
429rate_limitedToo many requests from this network address. Retry after the interval in `Retry-After`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
422validation_errorQuery parameters were invalid.
GET/api/v1/formulary/{slug}no auth

Read one substance, with its citation and refusal reason

Returns the platform's position on a single substance. Also resolves aliases, so `Viagra` and `sildenafil citrate` both reach the `sildenafil` row.

Example response · 200

Response
{
  "data": {
    "slug": "example-slug",
    "displayName": "example-display-name",
    "aliases": [
      "example-aliases"
    ],
    "category": "example-category",
    "status": "approved",
    "controlledSchedule": "II",
    "approvedProducts": [
      {
        "brand": "example-brand",
        "application": "example-application",
        "approved": "example-approved"
      }
    ],
    "compounding": {
      "status503a": "example-status503a",
      "status503b": "example-status503b",
      "bulkBasis": "example-bulk-basis",
      "note": "example-note"
    },
    "citation": "example-citation",
    "conditions": [
      "example-conditions"
    ],
    "refusalReason": "example-refusal-reason",
    "reviewBy": "example-review-by"
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
429rate_limitedToo many requests from this network address. Retry after the interval in `Retry-After`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
404not_foundNo substance matches that slug or alias.
GET/api/v1/rulebookno auth

Read the active marketing-claim rulebook

Every rule the claim scanner enforces, with the statute or enforcement instance behind it. Published so that a tenant can audit the gate rather than discover it at publish time. Patterns are not published. A rule with `detection: alias_pair` reads two different substances of an alias group joined together (`aliasGroups` lists which substances each group joins).

Example response · 200

Response
{
  "data": {
    "version": "example-version",
    "description": "example-description",
    "sourceCitation": "example-source-citation",
    "activatedAt": "2026-01-01T00:00:00.000Z",
    "rules": [
      {
        "code": "example-code",
        "severity": "block",
        "category": "example-category",
        "message": "example-message",
        "rewrite": "example-rewrite",
        "citation": "example-citation",
        "rationale": "example-rationale",
        "channels": [
          "example-channels"
        ],
        "detection": "pattern",
        "pairGroup": "example-pair-group"
      }
    ],
    "aliasGroups": [
      {
        "name": "example-name",
        "substances": [
          "example-substances"
        ]
      }
    ]
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
429rate_limitedToo many requests from this network address. Retry after the interval in `Retry-After`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
503rulebook_unavailableNo active rulebook is loaded.
GET/api/v1/lifecyclesno auth

List every lifecycle in the product

Every record that moves between named states (a case, an order, a subscription, a site version, a webhook delivery), grouped by product area, with the status values each can hold. Read one in full at `/lifecycles/{id}` to learn what each status means and what can happen next, so a client never has to hard-code a lifecycle from prose.

Query parameters

FieldTypeRequiredNotes
area"care" | "orders" | "money" | "launch" | "builder" | "platform"noOnly this product area.

Example response · 200

Response
{
  "data": {
    "total": 1,
    "areas": [
      {
        "area": "care",
        "title": "example-title",
        "lifecycles": [
          {
            "id": "example_id",
            "title": "example-title",
            "subject": "example-subject",
            "area": "care",
            "states": [
              null
            ]
          }
        ]
      }
    ]
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
429rate_limitedToo many requests from this network address. Retry after the interval in `Retry-After`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
422validation_errorQuery parameters were invalid.
GET/api/v1/lifecycles/{id}no auth

Read one lifecycle: its states in words and every move

Each state with the words a person is shown, one sentence on what it means, who is being waited on, whether it is final, and every move out of it with who can make it. The database decides whether a move happens; this describes what it will accept.

Example response · 200

Response
{
  "data": {
    "id": "example_id",
    "title": "example-title",
    "subject": "example-subject",
    "area": "care",
    "stored_in": "example-stored_in",
    "event_log": "example-event_log",
    "initial": [
      "example-initial"
    ],
    "terminal": [
      "example-terminal"
    ],
    "states": [
      {
        "state": "example-state",
        "label": "example-label",
        "meaning": "example-meaning",
        "terminal": false,
        "waiting_on": "patient",
        "next": [
          {
            "to": "example-to",
            "label": "example-label",
            "by": [
              null
            ],
            "via": "example-via"
          }
        ]
      }
    ]
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
429rate_limitedToo many requests from this network address. Retry after the interval in `Retry-After`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
404not_foundNo lifecycle has that id.
POST/api/v1/oauth/registerno auth

Register an agent platform as an OAuth client

OAuth 2.0 Dynamic Client Registration (RFC 7591) for the MCP endpoint's authorization server. Registers a public client (no secret; PKCE S256 is required at authorization) with one to five exact redirect URIs: HTTPS, or a loopback address for a native client. Registration grants nothing: a client acts for nobody until a signed-in person consents with a fresh second factor. Answers in the RFC 7591 shapes, not the v1 envelope. Discovery: `/.well-known/oauth-authorization-server`.

Request body

FieldTypeRequiredNotes
client_namestringno
redirect_urisarrayyes
grant_typesarrayno
response_typesarrayno
token_endpoint_auth_methodstringno
scopestringno

Example response · 201

Response
{
  "client_id": "example_client_id",
  "client_id_issued_at": 1,
  "client_name": "example-client_name",
  "redirect_uris": [
    "example-redirect_uris"
  ],
  "grant_types": [
    "example-grant_types"
  ],
  "response_types": [
    "example-response_types"
  ],
  "token_endpoint_auth_method": "none"
}

Errors

StatusCodeWhen
429rate_limitedToo many requests from this network address. Retry after the interval in `Retry-After`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400invalid_requestAn OAuth error body (`error`, `error_description`, RFC 6749 5.2): a parameter is missing, repeated or malformed, or the grant is not valid (`invalid_grant`).
401invalid_clientNo registered client_id was sent.
503temporarily_unavailableThe authorization server could not answer. Nothing was granted.
400invalid_redirect_uriA redirect URI is not an exact HTTPS URL or a loopback address.
413payload_too_largeThe request body exceeds 4096 bytes (4 KiB). Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API: the same status and code, with `request_id` `edge` and no `Request-Id` header.
POST/api/v1/oauth/tokenno auth

Exchange an authorization code or a refresh token

The OAuth 2.1 token endpoint (`application/x-www-form-urlencoded`). `authorization_code` needs the code, the exact redirect URI and the PKCE `code_verifier`; `refresh_token` rotates a single-use refresh token (one presented twice ends the connection). Tokens are bound to this server's MCP endpoint (`resource`, RFC 8707). Access tokens last 15 minutes; a connection lasts at most eight hours, like a console session. Answers in the RFC 6749 shapes, never cached.

Request body

FieldTypeRequiredNotes
grant_type"authorization_code" | "refresh_token"yes
client_idstringyes
codestringno
code_verifierstringno
redirect_uristringno
refresh_tokenstringno
resourcestringnoRFC 8707: this server's MCP endpoint, the token's only audience.

Example response · 200

Response
{
  "access_token": "example-access_token",
  "token_type": "Bearer",
  "expires_in": 1,
  "refresh_token": "example-refresh_token",
  "scope": "example-scope"
}

Errors

StatusCodeWhen
429rate_limitedToo many requests from this network address. Retry after the interval in `Retry-After`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400invalid_requestAn OAuth error body (`error`, `error_description`, RFC 6749 5.2): a parameter is missing, repeated or malformed, or the grant is not valid (`invalid_grant`).
401invalid_clientNo registered client_id was sent.
503temporarily_unavailableThe authorization server could not answer. Nothing was granted.
400unsupported_grant_typeOnly authorization_code and refresh_token are supported.
400invalid_targetThe resource is not this server's MCP endpoint.
413payload_too_largeThe request body exceeds 4096 bytes (4 KiB). Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API: the same status and code, with `request_id` `edge` and no `Request-Id` header.
POST/api/v1/oauth/revokeno auth

Revoke an agent connection

OAuth token revocation (RFC 7009, form body): either token of a connection ends the whole connection. An unknown token is answered 200, as the RFC requires.

Request body

FieldTypeRequiredNotes
tokenstringyes
client_idstringyes
token_type_hintstringno

Example response · 200

Response
{}

Errors

StatusCodeWhen
429rate_limitedToo many requests from this network address. Retry after the interval in `Retry-After`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400invalid_requestAn OAuth error body (`error`, `error_description`, RFC 6749 5.2): a parameter is missing, repeated or malformed, or the grant is not valid (`invalid_grant`).
401invalid_clientNo registered client_id was sent.
503temporarily_unavailableThe authorization server could not answer. Nothing was granted.
413payload_too_largeThe request body exceeds 4096 bytes (4 KiB). Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API: the same status and code, with `request_id` `edge` and no `Request-Id` header.
GET/api/v1/statusno auth

Service objectives, measured, and every published incident

Public and unauthenticated. Each objective reports the ratio actually measured over its rolling window rather than the one we would like to claim, and an objective with no observations behind it reports `unmeasured` rather than met. Resolved incidents carry their customer impact and root cause, and the update timeline is append-only.

Example response · 200

Response
{
  "data": {
    "objectives": [
      {
        "component": "example-component",
        "metric": "example-metric",
        "target": 1,
        "windowDays": 1,
        "measured": 1,
        "errorBudgetRemaining": 1,
        "state": "met"
      }
    ],
    "incidents": [
      {
        "reference": "example-reference",
        "title": "example-title",
        "severity": "sev1",
        "component": "example-component",
        "status": "investigating",
        "startedAt": "2026-01-01T00:00:00.000Z",
        "resolvedAt": "2026-01-01T00:00:00.000Z",
        "customerImpact": "example-customer-impact",
        "updates": [
          {
            "status": "example-status",
            "body": "example-body",
            "postedAt": "2026-01-01T00:00:00.000Z"
          }
        ]
      }
    ]
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
429rate_limitedToo many requests from this network address. Retry after the interval in `Retry-After`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
503status_unavailableStatus could not be read.
GET/api/v1/catalog/{brand}/programsno auth

List a brand's programs with their approved offers

Every program the brand offers on this deployment whose approved offer and approved disclosure both resolve, with the opaque program handle to link the care host's Start page with, the offer exactly as the hosted payment flow bills it, and the digest of the disclosure that must accompany it. A program with no approved offer is omitted rather than shown with a default price. No clinician, program key or clinical content is returned. Public and unauthenticated: no credential is read, the answer is the same for every caller, and it is cacheable (`Cache-Control: public, max-age=60`) with `Access-Control-Allow-Origin: *` and no credentials, so a brand's own site may fetch it from the browser. The brand handle is the brand's stable slug. An unknown brand answers 404 after the same response floor as a known one.

Example response · 200

Response
{
  "data": {
    "brand": {
      "handle": "example-handle",
      "name": "example-name"
    },
    "programs": [
      {
        "handle": "example-handle",
        "name": "example-name",
        "offer": {
          "amountCents": 1,
          "currency": "usd",
          "interval": "week",
          "trialDays": 1,
          "display": "example-display",
          "offerVersion": 1,
          "termsHash": "example-terms-hash",
          "effectiveTo": "example-effective-to"
        },
        "disclosure": {
          "version": "example-version",
          "digest": "example-digest"
        }
      }
    ]
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
429rate_limitedToo many requests from this network address. Retry after the interval in `Retry-After`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
404brand_not_foundNo brand catalog is served under that handle on this deployment.
503catalog_unavailableThe catalog could not be read. Nothing was changed; retry.
GET/api/v1/catalog/{brand}/programs/{handle}no auth

Read one program's approved offer by its public handle

The same entry the list returns, for one program handle. A handle that names no program of the brand, or a program with no approved offer, answers 404. Public and unauthenticated: no credential is read, the answer is the same for every caller, and it is cacheable (`Cache-Control: public, max-age=60`) with `Access-Control-Allow-Origin: *` and no credentials, so a brand's own site may fetch it from the browser. The brand handle is the brand's stable slug. An unknown brand answers 404 after the same response floor as a known one.

Example response · 200

Response
{
  "data": {
    "handle": "example-handle",
    "name": "example-name",
    "offer": {
      "amountCents": 1,
      "currency": "usd",
      "interval": "week",
      "trialDays": 1,
      "display": "example-display",
      "offerVersion": 1,
      "termsHash": "example-terms-hash",
      "effectiveTo": "example-effective-to"
    },
    "disclosure": {
      "version": "example-version",
      "digest": "example-digest"
    }
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
429rate_limitedToo many requests from this network address. Retry after the interval in `Retry-After`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
404brand_not_foundNo brand catalog is served under that handle on this deployment.
404program_not_foundNo program with an approved offer has that handle in this brand's catalog.
503catalog_unavailableThe catalog could not be read. Nothing was changed; retry.
GET/api/v1/catalog/{brand}/programs/{handle}/availabilityno auth

Whether a program is offered in a state

`available`, `video_required` or `unavailable`, with a reason code. It reads the program's current state policy, the California exclusion (always `unavailable`, `state_excluded`), and whether any licensed clinician covers the program there. Coverage is reported as presence only: the answer never names, identifies or counts a clinician. An availability answer is not a medical diagnosis, autonomous eligibility, or production clinical approval: it says only whether the program is offered in that state. Whether a patient can be treated is decided by a licensed clinician on the care host, after intake. Public and unauthenticated: no credential is read, the answer is the same for every caller, and it is cacheable (`Cache-Control: public, max-age=60`) with `Access-Control-Allow-Origin: *` and no credentials, so a brand's own site may fetch it from the browser. The brand handle is the brand's stable slug. An unknown brand answers 404 after the same response floor as a known one.

Query parameters

FieldTypeRequiredNotes
state"AL" | "AK" | "AZ" | "AR" | "CA" | "CO" | "CT" | "DE" | "FL" | "GA" | "HI" | "ID" | "IL" | "IN" | "IA" | "KS" | "KY" | "LA" | "ME" | "MD" | "MA" | "MI" | "MN" | "MS" | "MO" | "MT" | "NE" | "NV" | "NH" | "NJ" | "NM" | "NY" | "NC" | "ND" | "OH" | "OK" | "OR" | "PA" | "RI" | "SC" | "SD" | "TN" | "TX" | "UT" | "VT" | "VA" | "WA" | "WV" | "WI" | "WY" | "DC"yesA two-letter USPS state code, or DC.

Example response · 200

Response
{
  "data": {
    "program": "example-program",
    "state": "AL",
    "status": "available",
    "reason": "state_excluded"
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
429rate_limitedToo many requests from this network address. Retry after the interval in `Retry-After`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
422validation_error`state` must be a two-letter USPS state code or DC.
404brand_not_foundNo brand catalog is served under that handle on this deployment.
404program_not_foundNo program with an approved offer has that handle in this brand's catalog.
503catalog_unavailableThe catalog could not be read. Nothing was changed; retry.
GET/api/v1/catalog/{brand}/disclosuresno auth

The disclosures a brand's pages must show, verbatim

Every disclosure the builder would require on a hosted page, for the brand's own site: each program's approved disclosure and cancellation terms, its regulatory standing when its substance stands on a regulatory policy, the emergency notice, and the legal documents to link. Each block has a stable id, a digest, the verbatim text and a placement hint, and the response carries the same blocks as a sanitized HTML fragment. The programs are exactly the catalog's; an `approved_disclosure` digest equals the catalog's `disclosure.digest`. A regulatory block is `not_available` when the policy is withdrawn or the substance is held, and the program's availability is then `regulatory_unavailable`. No program key, clinician or clinical content is returned. Public and unauthenticated: no credential is read, the answer is the same for every caller, and it is cacheable (`Cache-Control: public, max-age=60`) with `Access-Control-Allow-Origin: *` and no credentials. An unknown brand answers 404 after the same response floor as a known one.

Query parameters

FieldTypeRequiredNotes
programstringnoOnly this program's blocks, and the brand-wide ones.
state"AL" | "AK" | "AZ" | "AR" | "CA" | "CO" | "CT" | "DE" | "FL" | "GA" | "HI" | "ID" | "IL" | "IN" | "IA" | "KS" | "KY" | "LA" | "ME" | "MD" | "MA" | "MI" | "MN" | "MS" | "MO" | "MT" | "NE" | "NV" | "NH" | "NJ" | "NM" | "NY" | "NC" | "ND" | "OH" | "OK" | "OR" | "PA" | "RI" | "SC" | "SD" | "TN" | "TX" | "UT" | "VT" | "VA" | "WA" | "WV" | "WI" | "WY" | "DC"noA two-letter USPS state code or DC. A regulatory block then also says whether the program is offered there.

Example response · 200

Response
{
  "data": {
    "brand": {
      "handle": "example-handle",
      "name": "example-name"
    },
    "program": "example-program",
    "state": "AL",
    "digest": "example-digest",
    "blocks": [
      {
        "id": "example_id",
        "kind": "approved_disclosure",
        "program": "example-program",
        "status": "required",
        "reason": "announced",
        "version": "example-version",
        "digest": "example-digest",
        "text": "example-text",
        "links": [
          {
            "kind": "privacy",
            "label": "example-label",
            "url": "example-url",
            "version": "example-version",
            "digest": "example-digest",
            "legalEntityName": "example-legal-entity-name"
          }
        ],
        "placement": "with_price",
        "draft": false
      }
    ],
    "html": "example-html"
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
429rate_limitedToo many requests from this network address. Retry after the interval in `Retry-After`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
422validation_error`program` must be a program handle and `state` a two-letter USPS state code or DC.
404brand_not_foundNo brand catalog is served under that handle on this deployment.
404program_not_foundNo program with an approved offer has that handle in this brand's catalog.
503disclosures_unavailableThe disclosures could not be read and verified. Nothing was changed; retry.
GET/api/v1/catalog/{brand}/care-networkno auth

The brand's care network, for its consumer terms

The legal names of the physician groups that cover the brand (with their clinicians' licensed states and their messaging SLA), the network pharmacies, and the labs. Name them in your consumer terms. Public, PHI-free and cacheable; no clinician, count, address or identifier. California is excluded.

Query parameters

FieldTypeRequiredNotes
state"AL" | "AK" | "AZ" | "AR" | "CA" | "CO" | "CT" | "DE" | "FL" | "GA" | "HI" | "ID" | "IL" | "IN" | "IA" | "KS" | "KY" | "LA" | "ME" | "MD" | "MA" | "MI" | "MN" | "MS" | "MO" | "MT" | "NE" | "NV" | "NH" | "NJ" | "NM" | "NY" | "NC" | "ND" | "OH" | "OK" | "OR" | "PA" | "RI" | "SC" | "SD" | "TN" | "TX" | "UT" | "VT" | "VA" | "WA" | "WV" | "WI" | "WY" | "DC"no

Example response · 200

Response
{
  "data": {
    "brand": {
      "handle": "example-handle",
      "name": "example-name"
    },
    "state": "AL",
    "physicianGroups": [
      {
        "legalName": "example-legal-name",
        "doingBusinessAs": "example-doing-business-as",
        "formationState": "example-formation-state",
        "states": [
          "example-states"
        ],
        "messagingSla": {
          "routineResponseHours": 1,
          "urgentResponseHours": 1
        }
      }
    ],
    "pharmacies": [
      {
        "name": "example-name",
        "states": [
          "example-states"
        ],
        "coldChain": false
      }
    ],
    "labs": [
      {
        "legalName": "example-legal-name",
        "cliaNumber": "example-clia-number"
      }
    ],
    "labsNote": "example-labs-note",
    "californiaExcluded": true
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
429rate_limitedToo many requests from this network address. Retry after the interval in `Retry-After`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
422validation_error`state` must be a two-letter USPS state code or DC.
404brand_not_foundNo brand is served under that handle on this deployment.
503care_network_unavailableThe catalog could not be read. Retry.
POST/api/v1/records-requestsno auth

Ask for a copy of your records (patients)

The Ambition-run records request, for a patient whose brand cannot serve them. If the email is the one Ambition verified for a patient of the brand, a code is sent to it from the physician group. The answer is the same either way.

Request body

FieldTypeRequiredNotes
brandstringyesThe brand the patient used.
emailstringyesThe email Ambition verified for the patient.

Example response · 202

Response
{
  "data": {
    "requestToken": "example-request-token",
    "expiresAt": "2026-01-01T00:00:00.000Z"
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
429rate_limitedToo many requests from this network address. Retry after the interval in `Retry-After`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
422validation_errorThe body was invalid.
503records_request_unavailableThe request could not be taken. Retry.
413payload_too_largeThe request body exceeds 1024 bytes (1 KiB). Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API: the same status and code, with `request_id` `edge` and no `Request-Id` header.
POST/api/v1/records-requests/confirmno auth

Confirm a records request with the emailed code

Opens a right-of-access request with the physician group, answered within 30 days. Five attempts per code.

Request body

FieldTypeRequiredNotes
requestTokenstringyes
codestringyes
detailsstringnoWhat records the patient wants, in their words.

Example response · 200

Response
{
  "data": {
    "status": "opened",
    "reference": "example-reference",
    "dueAt": "2026-01-01T00:00:00.000Z"
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
429rate_limitedToo many requests from this network address. Retry after the interval in `Retry-After`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
422validation_errorThe body was invalid.
503records_request_unavailableThe request could not be confirmed. Retry.
413payload_too_largeThe request body exceeds 4096 bytes (4 KiB). Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API: the same status and code, with `request_id` `edge` and no `Request-Id` header.
GET/api/v1/catalog/{brand}/programs/{handle}/questionnaireno auth

The program's current questionnaire, as FHIR R4

The published intake (or check-in) questionnaire of a program, as a FHIR R4 Questionnaire for your own intake screens: items with linkId, type, text, required, answerOption and enableWhen, plus Ambition extensions for the stop rules (show their patient message when the answers match), the consent scopes the program requires, the identity gate and, with `state`, the modality there. `id` is the version id and `version` its content hash; submit against both. Public and non-PHI: cacheable for 60 seconds with an ETag of the content hash.

Query parameters

FieldTypeRequiredNotes
kind"intake" | "check_in"no`intake` (default) or `check_in`.
state"AL" | "AK" | "AZ" | "AR" | "CA" | "CO" | "CT" | "DE" | "FL" | "GA" | "HI" | "ID" | "IL" | "IN" | "IA" | "KS" | "KY" | "LA" | "ME" | "MD" | "MA" | "MI" | "MN" | "MS" | "MO" | "MT" | "NE" | "NV" | "NH" | "NJ" | "NM" | "NY" | "NC" | "ND" | "OH" | "OK" | "OR" | "PA" | "RI" | "SC" | "SD" | "TN" | "TX" | "UT" | "VT" | "VA" | "WA" | "WV" | "WI" | "WY" | "DC"noWith a state, the `required-modality` extension says async or sync_video there.

Example response · 200

Response
{
  "data": {
    "brand": {
      "handle": "example-handle",
      "name": "example-name"
    },
    "programHandle": "example-program-handle",
    "questionnaire": {
      "resourceType": "Questionnaire",
      "id": "example_id",
      "version": "example-version",
      "status": "active",
      "item": [
        {}
      ]
    }
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
429rate_limitedToo many requests from this network address. Retry after the interval in `Retry-After`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
422validation_errorThe request body or parameters were invalid.
404brand_not_foundNo brand catalog is served under that handle on this deployment.
404program_not_foundNo program has that handle in this brand's catalog.
404questionnaire_not_foundThe program has no published questionnaire of that kind.
503intake_catalog_unavailableThe questionnaire could not be read. Retry.
GET/api/v1/catalog/{brand}/questionnaires/{versionId}no auth

One questionnaire version, as FHIR R4 (immutable)

A published or superseded questionnaire version by id, as a FHIR R4 Questionnaire. A version never changes, so this is the reference for answers you stored. Public and non-PHI.

Example response · 200

Response
{
  "data": {
    "brand": {
      "handle": "example-handle",
      "name": "example-name"
    },
    "programHandle": "example-program-handle",
    "questionnaire": {
      "resourceType": "Questionnaire",
      "id": "example_id",
      "version": "example-version",
      "status": "active",
      "item": [
        {}
      ]
    }
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
429rate_limitedToo many requests from this network address. Retry after the interval in `Retry-After`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
422validation_errorThe request body or parameters were invalid.
404brand_not_foundNo brand catalog is served under that handle on this deployment.
404questionnaire_not_foundNo questionnaire version with that id in this brand.
503intake_catalog_unavailableThe questionnaire could not be read. Retry.
GET/api/v1/catalog/{brand}/consent-documentsno auth

The physician group's consent documents for a program and state

The legal kit a patient accepts before intake, issued by the physician group covering the brand: telehealth consent (your brand named as third-party beneficiary), the Notice of Privacy Practices acknowledgment, the compounded medication and interim peptide policy acknowledgment (programs that sell a formulary substance), the cash-pay and Medicare TrOOP certification, clinician-messaging consent and the AI-use disclosure. Each with its verbatim text and label, their SHA-256 digests, presentation rules and the capture mode in force. Public and non-PHI.

Query parameters

FieldTypeRequiredNotes
programstringyesThe program's opaque public handle (`prg_` and 24 hex characters). Link the care host's Start page with it: `/start?p=<handle>`. It never changes and carries no program or condition word.
state"AL" | "AK" | "AZ" | "AR" | "CA" | "CO" | "CT" | "DE" | "FL" | "GA" | "HI" | "ID" | "IL" | "IN" | "IA" | "KS" | "KY" | "LA" | "ME" | "MD" | "MA" | "MI" | "MN" | "MS" | "MO" | "MT" | "NE" | "NV" | "NH" | "NJ" | "NM" | "NY" | "NC" | "ND" | "OH" | "OK" | "OR" | "PA" | "RI" | "SC" | "SD" | "TN" | "TX" | "UT" | "VT" | "VA" | "WA" | "WV" | "WI" | "WY" | "DC"yesA two-letter USPS state code or DC.
localestringnoA BCP 47 language-region tag, e.g. `en-US`.

Example response · 200

Response
{
  "data": {
    "brand": {
      "handle": "example-handle",
      "name": "example-name"
    },
    "program": "example-program",
    "state": "example-state",
    "locale": "example-locale",
    "classification": "synthetic",
    "documents": [
      {
        "scope": "telehealth_consent",
        "title": "example-title",
        "available": false,
        "documentId": "00000000-0000-4000-8000-00000000e001",
        "version": 1,
        "jurisdiction": "example-jurisdiction",
        "locale": "example-locale",
        "documentTitle": "example-document-title",
        "sha256": "example-sha256",
        "body": "example-body",
        "labelText": "example-label-text",
        "labelSha256": "example-label-sha256",
        "presentation": {
          "separateCheckbox": false,
          "noPrefill": false,
          "mustDisplayFullText": false
        },
        "brandBeneficiary": false,
        "sandboxTemplate": false,
        "legalEntityName": "example-legal-entity-name",
        "effectiveAt": "2026-01-01T00:00:00.000Z",
        "captureMode": "brand_rendered"
      }
    ]
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
429rate_limitedToo many requests from this network address. Retry after the interval in `Retry-After`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
422validation_errorThe request body or parameters were invalid.
404brand_not_foundNo brand catalog is served under that handle on this deployment.
404program_not_foundNo program has that handle in this brand's catalog.
503intake_catalog_unavailableThe documents could not be read. Retry.
GET/api/v1/catalog/{brand}/safety-screenno auth

The versioned emergency screen for safety reports

The emergency copy and the three seriousness questions to show before a patient reports a side effect or a product problem. Echo `version` as `emergencyScreenVersion`. The same for every brand; public and non-PHI.

Example response · 200

Response
{
  "data": {
    "version": "example-version",
    "sha256": "example-sha256",
    "draft": false,
    "emergencyText": "example-emergency-text",
    "questions": [
      {
        "id": "emergency_symptoms",
        "text": "example-text",
        "answers": [
          "example-answers"
        ]
      }
    ],
    "medwatchNotice": "example-medwatch-notice"
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
429rate_limitedToo many requests from this network address. Retry after the interval in `Retry-After`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
422validation_errorThe request body or parameters were invalid.
GET/api/v1/catalog/{brand}/programs/{handle}/regulatoryno auth

A program's regulatory standing: substance, policy, products, state availability, labs

Everything your storefront needs to list or pull a regulated SKU: the substance and the regulatory policy it stands on (status, label to show verbatim, citation, Appendix B routes), the patient disclosure with its version and digest, the active pharmacy products with their standing, availability in every state with a reason, and the program's required labs. Subscribe to `formulary.policy_changed` and `formulary.state_availability_changed` and re-read this when one arrives. Public regulatory reference data: no patient, clinician or price. Cacheable (`Cache-Control: public, max-age=60`) with `Access-Control-Allow-Origin: *`; an unknown brand answers 404 after the same response floor as a known one.

Example response · 200

Response
{
  "data": {
    "brand": {
      "handle": "example-handle",
      "name": "example-name"
    },
    "program": {
      "handle": "example-handle",
      "name": "example-name",
      "substance": {
        "slug": "example-slug",
        "name": "example-name",
        "formularyStatus": "example-formulary-status",
        "singleIngredient": true
      },
      "policy": {
        "key": "example-key",
        "status": "announced",
        "regulatoryStatus": "example-regulatory-status",
        "label": "example-label",
        "citation": "example-citation",
        "sourceUrl": "example-source-url",
        "allowedRoutes": [
          "example-allowed-routes"
        ],
        "intermittentCourse": false
      },
      "disclosure": {
        "version": "example-version",
        "digest": "example-digest",
        "text": "example-text",
        "reportingInstruction": "example-reporting-instruction"
      },
      "products": [
        {
          "productId": "00000000-0000-4000-8000-00000000e001",
          "form": "example-form",
          "route": "example-route",
          "strength": "example-strength",
          "standing": "policy_withdrawn"
        }
      ],
      "regulatoryGated": false,
      "states": [
        {
          "state": "AL",
          "available": false,
          "reason": "policy_not_in_effect"
        }
      ],
      "requiredLabs": [
        {
          "key": "example-key",
          "label": "example-label",
          "kind": "baseline",
          "loincCodes": [
            "example-loinc-codes"
          ],
          "minCodesPresent": 1,
          "maxAgeDays": 1,
          "intervalDays": 1,
          "noticeDays": 1,
          "acceptedSources": [
            "brand_arranged"
          ]
        }
      ]
    }
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
429rate_limitedToo many requests from this network address. Retry after the interval in `Retry-After`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
404brand_not_foundNo brand catalog is served under that handle on this deployment.
404program_not_foundNo program of this brand has that handle on this deployment.
503catalog_unavailableThe catalog could not be read. Nothing was changed; retry.

Authenticated

POST/api/v1/claims/scanclaims.scan

Evaluate marketing copy against the rulebook before you publish it

Runs the same engine, against the same active rulebook, that gates publication inside the platform — so a result here is the result you will get at publish time. Rules that apply only to other channels than `context` are not applied. Each hit carries the matched text, the provision behind the rule, and a compliant rewrite where one exists. With `programKey`, the copy is judged for that program: when it sells a substance on a regulatory policy, the rules for such a program's pages run too (`regulatedContext`); without it they run whenever your brand has such a program, as they would on its pages. Every scan stores a receipt (`receiptId`) with the rulebook version, the channel, the request id and the SHA-256 of the text; the text itself, and the matched fragments, are stored only when `retainText` is true. `requiredDisclosures` are checked by exact text against their current approved version. This checks claims only: the builder's price-book conflict check does not run here, so render prices from the catalog rather than writing them into copy. Do not include patient information in the text.

Request body

FieldTypeRequiredNotes
textstringyesMarketing copy to evaluate.
context"landing_page" | "email" | "sms" | "paid_ad" | "social" | "packaging"noWhere the copy will appear; some rules apply only to some channels.
programKeystringnoThe key of the program this copy sells. When that program sells a substance on a regulatory policy (such as the HHS interim-policy peptides), the rules for copy on such a program's pages run as well, so a claim like "Heal injuries faster" blocks without naming the substance. Without it they run whenever your brand has any such program. A key that is not one of your programs is refused.
urlstringnoThe page the copy appears on. Recorded on the receipt without its query string or fragment, with whether its origin is one of your verified external sites.
retainTextbooleannoKeep the scanned text on the receipt. Off by default: the receipt then holds only the text's SHA-256 and the rules that matched, not the words.
requiredDisclosuresarraynoDisclosures the copy must carry verbatim. Each is reported present or missing.

Example response · 200

Response
{
  "data": {
    "outcome": "pass",
    "blocked": 1,
    "warned": 1,
    "hits": [
      {
        "code": "example-code",
        "severity": "block",
        "category": "example-category",
        "message": "example-message",
        "rewrite": "example-rewrite",
        "citation": "example-citation",
        "match": "example-match"
      }
    ],
    "rulebookVersion": "example-rulebook-version",
    "truncated": false,
    "context": "landing_page",
    "programKey": "example-program-key",
    "regulatedContext": false,
    "receiptId": "example_receiptId",
    "textDigest": "example-text-digest",
    "textRetained": false,
    "url": "example-url",
    "urlOriginVerified": false,
    "requiredDisclosures": [
      {
        "id": "example_id",
        "digest": "example-digest",
        "status": "present",
        "reason": "not_found"
      }
    ]
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
422validation_errorRequest body did not match the contract.
422unknown_program`programKey` is not one of your brand's programs.
503rulebook_unavailableNo active rulebook is loaded.
503claim_context_unavailableYour brand's programs could not be read, so the copy could not be judged in its context. Retry the scan.
503scan_receipt_unavailableThe receipt could not be stored, so no result is returned. Retry the scan.
413payload_too_largeThe request body exceeds 64000 bytes. Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API: the same status and code, with `request_id` `edge` and no `Request-Id` header.
POST/api/v1/mcpclaims.scan

Model Context Protocol endpoint for outside AI agents

A single Streamable HTTP endpoint that lets an agent platform discover and call platform tools with an API key: `search_formulary`, `get_formulary_entry`, `get_claim_rulebook` and `describe_lifecycle` (any valid key) and `scan_claims` (scope `claims.scan`), each key scope re-checked live against its minter's capabilities. A person's own assistant connects with OAuth 2.1 instead (`/oauth/*`, discovery at `/.well-known/oauth-protected-resource`) and is offered the brand's operating tools their role allows, read under their own permissions; its `propose_` tools only create proposals for a person to approve. A call with no credential is answered 401 with the OAuth discovery challenge. Both protocol eras are served without sessions: revision 2026-07-28 (per-request version, `MCP-Protocol-Version`, `Mcp-Method` and `Mcp-Name` headers, `server/discover`) and the earlier `initialize` handshake. The tools list a key sees is limited to its scopes. Every tool is read-only and returns operational, non-patient data; treat all tool output as untrusted data. Browser origins are refused. See `docs/agent-gateway/README.md`.

Request body

FieldTypeRequiredNotes
jsonrpcstringyes
idstringnoAbsent for a notification.
methodstringyesinitialize, ping, server/discover, tools/list or tools/call.
paramsobjectno

Example response · 200

Response
{
  "jsonrpc": "2.0",
  "id": "example_id",
  "result": {},
  "error": {
    "code": 1,
    "message": "example-message",
    "data": null
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400invalid_requestNot one JSON-RPC message, invalid JSON, a header that disagrees with the body (-32020) or an unsupported protocol version (-32022).
403insufficient_scopeThe tool needs a scope the key lacks, or a browser Origin was sent. A `WWW-Authenticate` challenge names the scope.
403phi_key_not_allowedThe key is a `phi_server` key. Patient-data keys are never agent credentials; use a standard key.
404method_not_foundThe JSON-RPC method is not offered (-32601). Legacy (initialize-based) clients receive the same error inside an HTTP 200, because they read a non-2xx POST as a transport failure.
413payload_too_largeThe request body exceeds 8000 bytes. Nothing was processed; send a smaller body. This endpoint answers its own refusal as a JSON-RPC error (`error.code` -32600, `id` null), not the v1 envelope; a message over 8 KiB (8192 bytes) is refused at the edge first, in the v1 envelope with `request_id` `edge`. Send longer copy to `POST /claims/scan`.
GET/api/v1/deliveriesdeliveries.read

Every webhook delivery attempt made on your behalf

One row per attempt, successes included — a log that records only failures cannot answer "did you send it?", which is the question an incident actually asks. Each row carries the response code, the error, whether the payload was signed, when the next attempt is due, and the delivery, event id and receiver it belongs to. Newest first; pass `meta.nextCursor` as `startingAfter` for the next page. The first page counts the attempts matching the filters in `meta.total` (up to 10,000, with `meta.totalCapped` when there are more); later pages answer null.

Query parameters

FieldTypeRequiredNotes
status"delivered" | "retrying" | "dead_letter" | "duplicate" | "reconciled" | "rejected"noOnly attempts with this outcome.
orderIdstringnoOnly attempts for this order.
eventTypestringnoOnly attempts of this event type.
endpointIdstringnoOnly attempts sent to this receiver.
createdAfterstringnoOnly attempts made at or after this time (ISO 8601).
createdBeforestringnoOnly attempts made before this time (ISO 8601).
startingAfterstringnoThe `nextCursor` of the previous page. Pages run newest first; an attempt recorded while you page can land before your cursor, so dedupe on `ambition-event-id` and use GET /events for completeness.
limitintegernoAttempts per page, 1 to 200.

Example response · 200

Response
{
  "data": [
    {
      "id": "example_id",
      "requestId": "example_requestId",
      "eventType": "example-event-type",
      "orderId": "example_orderId",
      "status": "delivered",
      "attempt": 1,
      "responseCode": 1,
      "errorCode": "example-error-code",
      "detail": "example-detail",
      "signatureValid": false,
      "nextAttemptAt": "2026-01-01T00:00:00.000Z",
      "occurredAt": "2026-01-01T00:00:00.000Z",
      "outboxId": "example_outboxId",
      "eventId": "example_eventId",
      "endpointId": "example_endpointId"
    }
  ],
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment",
    "total": 1,
    "totalCapped": false,
    "hasMore": false,
    "nextCursor": "example-next-cursor"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
422validation_errorQuery parameters were invalid, or the cursor was not one this API issued.
GET/api/v1/deliveries/{id}deliveries.read

One webhook delivery with its attempts

A delivery of one event to one of your receivers: its status, every attempt (number, time, response code and class, failure class, next retry), when the next attempt is due, and its redeliveries. Never a payload, a response body or a secret.

Example response · 200

Response
{
  "data": {
    "id": "example_id",
    "eventId": "example_eventId",
    "requestId": "example_requestId",
    "eventType": "example-event-type",
    "orderId": "example_orderId",
    "endpointId": "example_endpointId",
    "status": "pending",
    "attemptCount": 1,
    "maxAttempts": 1,
    "nextAttemptAt": "2026-01-01T00:00:00.000Z",
    "createdAt": "2026-01-01T00:00:00.000Z",
    "deliveredAt": "2026-01-01T00:00:00.000Z",
    "replayOf": "example-replay-of",
    "replayReason": "example-replay-reason",
    "attempts": [
      {
        "attempt": 1,
        "occurredAt": "2026-01-01T00:00:00.000Z",
        "status": "delivered",
        "responseCode": 1,
        "responseClass": "1xx",
        "failureClass": "example-failure-class",
        "signatureValid": false,
        "nextAttemptAt": "2026-01-01T00:00:00.000Z"
      }
    ],
    "redeliveries": [
      {
        "id": "example_id",
        "status": "pending",
        "createdAt": "2026-01-01T00:00:00.000Z"
      }
    ]
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
404not_foundNo such delivery for this key's brand and environment. A sandbox key cannot read a production delivery.
503delivery_log_unavailableThe delivery could not be read. Nothing was changed; retry.
POST/api/v1/deliveries/replaydeliveries.replay

Send a delivered or dead-lettered event again, without opening a support ticket

Queues a fresh attempt at the same event for the receiver it went to: by `outboxId`, or by the `eventId` your receiver saw (with `endpointId` when the event went to several receivers). The event id and payload are preserved, so a receiver that deduplicates on `ambition-event-id` will not act twice. Works on a delivered delivery and on a dead-lettered one; a delivery still being retried, or a receiver that is paused or disabled, is refused. Asking again while the redelivery is queued returns it and costs nothing. Naming a dead-lettered delivery's `outboxId` after it was replayed returns that replay (`created: false`), even once it was delivered: redeliver by `eventId` to send the newest delivery again. A clinical event needs a phi_server key with `clinical_events.read`, a verified clinical receiver and the patient data gate now, and each such request is recorded in the access log with your key as the actor. The reason is recorded against the redelivery; the budget is per API key.

Request body

FieldTypeRequiredNotes
outboxIdstringnoThe delivery to send again (`outboxId` in the delivery history).
eventIdstringnoInstead of `outboxId`: the `ambition-event-id` your receiver saw. The newest delivery of that event to its receiver is sent again.
endpointIdstringnoWith `eventId`, when the event went to more than one of your receivers: which one to send it to again.
reasonstringyesRecorded against the redelivery, and shown when you read the delivery it creates. Never include patient data.

Example response · 200

Response
{
  "data": {
    "outboxId": "example_outboxId",
    "requestId": "example_requestId",
    "status": "example-status",
    "replayOf": "example-replay-of",
    "eventId": "example_eventId",
    "endpointId": "example_endpointId",
    "eventType": "example-event-type",
    "created": false
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
404not_foundNo such delivery or event for this key's brand and environment. A sandbox key cannot address a production delivery.
409not_replayableNothing to send again: a dead letter that was resolved or discarded, a reconciled delivery, a receiver's verification handshake, or a prescription transmission.
409delivery_in_flightThe event is still being retried to this receiver. It arrives on its own schedule; read it with GET /deliveries/{id}.
409receiver_not_activeThe receiver the event went to is paused or disabled (a 410 disables it), or no longer subscribed to the event type. Resume the receiver in the console, or register another one, and catch up from GET /events, whose ids match `ambition-event-id`.
409endpoint_requiredThe event went to more than one of your receivers: name one with `endpointId`.
403phi_key_requiredA clinical event is redelivered only with a phi_server key that holds `clinical_events.read`.
409phi_gate_closedA clinical event: your patient data access is not open now. Nothing was queued.
409phi_suspendedA clinical event: your patient data access is suspended. Nothing was queued.
409entity_baa_missingA clinical event about a case: your BAA with the physician group covering it is not in force now.
409clinical_event_heldA clinical event that is held (or expired) is released by Ambition, not redelivered.
429replay_budget_exhaustedThis API key has queued its redelivery budget for the current window (each key has its own). Repeating a queued redelivery costs nothing.
422validation_errorRequest body did not match the contract.
413payload_too_largeThe request body exceeds 4096 bytes (4 KiB). Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API: the same status and code, with `request_id` `edge` and no `Request-Id` header.
POST/api/v1/pharmacy/callbacks/{partnerId}partner signature

Receive a signed pharmacy status callback

Partner-only machine endpoint. The raw body is authenticated with the pharmacy contract's versioned HMAC secret; timestamp and event headers are mandatory, replay-bounded, and matched to the body before an immutable receipt or order transition is recorded. A cancellation Ambition sent is a request until the pharmacy answers: `canceled` confirms it and `cancel_denied` (with a reason code) refuses it, leaving the order where it is. A shipped status may carry the carrier and tracking number, which the order records. The whole partner contract, including what Ambition sends the pharmacy, is published at GET /api/v1/pharmacy/order-api.

Request body

FieldTypeRequiredNotes
eventIdstringyes
eventType"prescription.status" | "prescription.clarification" | "fulfillment.status"yes
schemaVersionstringyes
orderIdstringyes
partnerReferencestringyes
status"acknowledged" | "clarification_required" | "accepted" | "rejected" | "in_production" | "shipped" | "delivered" | "canceled" | "cancel_denied"yes
occurredAtstringyes
reasonCodestring | nullnoA code: letters, digits and _ . : - only, at most 120 characters. Anything else, up to 2000 characters, is recorded as a generic code (the status still applies), so explanations don't belong here.
questionstring | nullno
carrier"ups" | "fedex" | "usps" | "dhl" | "other" | nullno
trackingNumberstring | nullno

Example response · 202

Response
{
  "ok": false,
  "outcome": "example-outcome"
}

Errors

StatusCodeWhen
400invalid_jsonThe callback body is not valid JSON.
401invalid_callback_signatureThe HMAC or timestamp window is invalid.
404callback_contract_unavailableNo active callback contract matches the partner.
422callback_validation_failedThe body, event identity, schema, time, or order tuple is invalid. A signed callback whose status is outside the canonical vocabulary is answered with code callback_status_unrecognized and kept whole in quarantine for an integration operator; it is never applied.
429rate_limitedThe public API callback budget is exhausted.
503callback_store_unavailableThe durable callback store is unavailable.
413payload_too_largeThe request body exceeds 262144 bytes (256 KiB). Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API, with the same status and body.
POST/api/v1/sandbox/orderssession

Create a deterministic synthetic order preview

Models the order path end to end against synthetic data. Nothing is prescribed, transmitted to a pharmacy, or charged. A console-session route: it needs a same-origin request from a signed-in tenant workforce member and does not accept an API key.

Requires an Idempotency-Key header of 16–255 characters. Replays for 24 hours; reusing a key with a different body returns 409 rather than applying the second request.

Request body

FieldTypeRequiredNotes
programKeystringyesThe program the order belongs to, e.g. `hair-health`.
skustringyesThe product SKU, uppercase.
jurisdictionstringyesTwo-letter US state code.
visitMode"async" | "video"yesHow the clinician visit is held.

Example response · 200

Response
{
  "data": {},
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment",
    "replayed": false
  }
}

Errors

StatusCodeWhen
400idempotency_key_requiredProvide a 16–255 character Idempotency-Key.
403forbiddenTenant integration access is required.
409idempotency_conflictKey reused with different parameters.
409idempotency_in_progressA request with this key is still being processed. Retry after `Retry-After`; nothing was applied twice.
422validation_errorRequest body did not match the contract.
413payload_too_largeThe request body exceeds 8192 bytes (8 KiB). Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API: the same status and code, with `request_id` `edge` and no `Request-Id` header.
POST/api/v1/sandbox/enrollments/{id}/simulateenrollments.write

Simulate a clinician's decision on a sandbox enrollment

Sandbox only. Stands in for the clinician who does not exist on the developer sandbox: moves one of your sandbox enrollments to `approved`, `needs_info`, `not_approved` or `canceled` in a single transition, so your receivers get the same `enrollment.status_changed` webhook (one per subscribed receiver) and `GET /v1/enrollments/{id}` the same status a real decision would produce. Nothing clinical is created: no case, clinician or prescription. Run the full test patient flow first: create the enrollment, open its `launchUrl` on your sandbox care host and verify a test email; then simulate the decision. `canceled` also works before that. Asking for the status the enrollment already holds changes nothing (`meta.changed: false`) and sends no webhook, so a retry is safe. Requires a sandbox secret key (`ak_sandbox_`) with `enrollments.write`; a live key is refused 403, and the database refuses any enrollment that is not synthetic sandbox data. Status is shown exactly as the enrollment read shows it: without your business associate agreement and downstream-systems attestation in force the decision reads `in_progress`, and no `enrollment.status_changed` is sent.

Request body

FieldTypeRequiredNotes
outcome"approved" | "needs_info" | "not_approved" | "canceled"yesThe decision to simulate. `approved`, `needs_info` and `not_approved` need a test patient who has redeemed the enrollment's launch link on your sandbox care host; `canceled` works at any open stage.

Example response · 200

Response
{
  "data": {
    "id": "00000000-0000-4000-8000-00000000e001",
    "externalReference": "example-external-reference",
    "status": "created",
    "binding": "confirmed",
    "createdAt": "2026-01-01T00:00:00.000Z",
    "updatedAt": "2026-01-01T00:00:00.000Z"
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment",
    "outcome": "approved",
    "changed": false
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400invalid_jsonThe body is not valid JSON.
403sandbox_key_requiredThe simulator runs only with a sandbox secret key, on the developer sandbox, against synthetic data.
404enrollment_not_foundNo enrollment of your brand in the sandbox has that id.
409enrollment_not_boundNo test patient has redeemed this enrollment's launch link yet. Only `canceled` can be simulated before that.
409simulation_not_allowedThe enrollment cannot move to that outcome from where it is, for example because it has closed.
422validation_errorThe body does not match the contract.
503enrollment_unavailableThe enrollment record could not be reached. Nothing was changed; retry.
413payload_too_largeThe request body exceeds 1024 bytes (1 KiB). Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API: the same status and code, with `request_id` `edge` and no `Request-Id` header.
GET/api/v1/programscatalog.read

Your brand's catalog, for server callers

The same document as `GET /catalog/{brand}/programs`, for the key's own brand and environment. Use it from a backend that already holds a key; the public endpoint needs none. The answer is not cached.

Example response · 200

Response
{
  "data": {
    "brand": {
      "handle": "example-handle",
      "name": "example-name"
    },
    "programs": [
      {
        "handle": "example-handle",
        "name": "example-name",
        "offer": {
          "amountCents": 1,
          "currency": "usd",
          "interval": "week",
          "trialDays": 1,
          "display": "example-display",
          "offerVersion": 1,
          "termsHash": "example-terms-hash",
          "effectiveTo": "example-effective-to"
        },
        "disclosure": {
          "version": "example-version",
          "digest": "example-digest"
        }
      }
    ]
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
404brand_not_foundThe key's brand has no catalog served on this deployment.
503catalog_unavailableThe catalog could not be read. Nothing was changed; retry.
POST/api/v1/enrollmentsenrollments.write

Start an enrollment and get a launch link to the care host

Creates an enrollment for your own customer in a program and state, and returns a single-use launch link to your brand's care host, where the person proves their email, consents, completes intake and saves a card. Name the program by its catalog handle (or its key) and the state the person will be cared for in; the same authority the care host applies decides whether it is offered there, so California, a state the program is not offered in, or one no licensed clinician covers is refused 422 `program_unavailable_in_state`. In production a program and state the physician network has not yet approved for enrollment is refused 422 `program_awaiting_network_approval` instead: that one is not recorded against the key, so the same request works once the approval is in force. `Idempotency-Key` is required: a retry with the same key and body returns the same enrollment with a freshly minted launch link (`meta.replayed: true`); the earlier link stays valid until it expires. The answer is identical whether or not the email has an account. Brand and environment always come from the key: a sandbox key creates and reads only sandbox enrollments of its own brand, and another brand's enrollment is indistinguishable from one that does not exist.

Requires an Idempotency-Key header of 16–255 characters. Replays for 24 hours; reusing a key with a different body returns 409 rather than applying the second request.

Request body

FieldTypeRequiredNotes
programHandlestringnoThe program's opaque public handle from the catalog (`prg_` and 24 hex characters).
programKeystringnoThe program's key, as an alternative to `programHandle`.
state"AL" | "AK" | "AZ" | "AR" | "CA" | "CO" | "CT" | "DE" | "FL" | "GA" | "HI" | "ID" | "IL" | "IN" | "IA" | "KS" | "KY" | "LA" | "ME" | "MD" | "MA" | "MI" | "MN" | "MS" | "MO" | "MT" | "NE" | "NV" | "NH" | "NJ" | "NM" | "NY" | "NC" | "ND" | "OH" | "OK" | "OR" | "PA" | "RI" | "SC" | "SD" | "TN" | "TX" | "UT" | "VT" | "VA" | "WA" | "WV" | "WI" | "WY" | "DC"yesThe two-letter USPS state (or DC) the person will be cared for in. California is never available.
externalReferencestringyesYour own reference for this enrollment attempt, such as your customer or order id: 1 to 128 letters, digits and `._:-`, starting with a letter or digit. Unique among your brand's open enrollments in the key's environment: it names one attempt, not a customer for life, so once an enrollment is `canceled` or `expired` the same reference can start a new one. For a second enrollment while the first is still open (another program for the same customer), use a different reference. It can never be an email address or free text.
emailstringnoYour customer's email, if you already hold it as your own customer. Only a person who proves this address with a one-time code on the care host can redeem the launch link, so a forwarded link cannot bind someone else to your reference. Stored only as a hash. Ambition does not yet email the launch link: send the person to `launchUrl` yourself. The answer is identical whether or not the address has an account.
returnUrlstringnoWhere the care host's optional "Return to <brand>" link points after card setup: an https URL on one of your brand's verified external sites in this environment, with no port, credentials or fragment. It is checked and recorded with the enrollment; the care host does not show the link yet.

Example response · 201

Response
{
  "data": {
    "id": "00000000-0000-4000-8000-00000000e001",
    "externalReference": "example-external-reference",
    "status": "created",
    "binding": "confirmed",
    "createdAt": "2026-01-01T00:00:00.000Z",
    "updatedAt": "2026-01-01T00:00:00.000Z",
    "launchUrl": "example-launch-url",
    "launchExpiresAt": "2026-01-01T00:00:00.000Z"
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment",
    "replayed": false
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400idempotency_key_requiredProvide a 16 to 255 character Idempotency-Key.
400invalid_jsonThe body is not valid JSON.
422validation_errorThe body does not match the contract.
422program_not_foundNo program of your brand has that handle or key here.
422program_unavailableThe program has no approved offer, or your brand is not live.
422program_self_enrollment_closedSelf-serve enrollment is off for this program (the default while enrollment is invite-only), so no launch link is issued. A program manager turns it on in the console.
422program_unavailable_in_stateThe program is not offered in that state. California never is.
422program_awaiting_network_approvalProduction only: enrollment in this program and state is awaiting network approval. The physician group's medical director has not yet approved production enrollment there, or the approval has ended. Nothing is wrong with the request and nothing was created; ask Ambition where the approval stands. Not stored against the Idempotency-Key: retry with the same key once it is approved.
422environment_not_servedThe key's environment does not serve enrollments.
422return_url_unverified`returnUrl` is not an https URL on one of your verified external sites in this environment.
409external_reference_takenAn open enrollment of yours already uses this externalReference. Find it with `GET /enrollments?externalReference=` (the one that is not `canceled` or `expired`) and get a fresh link with `POST /enrollments/{id}/launch`. The reference is free again once that enrollment is canceled or expires. An enrollment that reads `in_progress` holds its reference for as long as it reads that way.
409care_host_unavailableYour brand has no active care host in this environment.
409enrollment_closedA replay named an enrollment that has since closed. Start a new one with a new key.
409enrollment_link_unavailableA replay named an enrollment whose intake has since started, so it has no new launch link. The person continues by signing in on your care host.
409idempotency_in_progressA request with this Idempotency-Key is still being processed. Retry after `Retry-After`.
409idempotency_conflictThis Idempotency-Key was used with a different body.
503idempotency_unavailableThe idempotency record could not be checked. Nothing was processed.
503launch_link_unavailableThe enrollment was created, but its launch link could not be issued. Retry with the same Idempotency-Key for its launch link.
503enrollment_unavailableThe enrollment record could not be reached. Nothing was changed; retry.
403enrollment_admission_closedThis deployment is not admitting patients yet (a real-data deployment before its real-PHI launch, or without a loaded launch authorization). Answered before the request body is read or an Idempotency-Key is reserved: nothing was created and no launch link was minted. Never answered on the sandbox.
413payload_too_largeThe request body exceeds 4096 bytes (4 KiB). Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API: the same status and code, with `request_id` `edge` and no `Request-Id` header.
POST/api/v1/enrollments/{id}/launchenrollments.write

Get a fresh launch link for an open enrollment

Mints a new single-use launch link to your brand's care host for one of your open enrollments created through the API, so you can send back a person who did not finish after the first link expired (links last 30 minutes, and Ambition does not email them) and after the create's Idempotency-Key window closed. It changes nothing about the enrollment: it does not rebind it, and only the person bound to it, or for an unredeemed enrollment the person who proves the email you supplied, can redeem the link. Earlier links stay valid until they expire. Takes no body and no Idempotency-Key: a retry mints another link to the same enrollment. A canceled or expired enrollment has no link (409 `enrollment_closed`); start a new one with `POST /enrollments`, which may carry the same `externalReference`. Once the person has started intake there is no new link either, whatever the enrollment's state (409 `enrollment_link_unavailable`, one answer for every state `GET` reads as `in_progress`): the person continues by signing in on your care host. Brand and environment always come from the key: a sandbox key creates and reads only sandbox enrollments of its own brand, and another brand's enrollment is indistinguishable from one that does not exist.

Example response · 200

Response
{
  "data": {
    "id": "00000000-0000-4000-8000-00000000e001",
    "launchUrl": "example-launch-url",
    "launchExpiresAt": "2026-01-01T00:00:00.000Z"
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
404enrollment_not_foundNo API enrollment of your brand in this environment has that id.
409enrollment_closedThe enrollment was canceled or has expired, so it has no launch link. A key whose creator may read clinical states also gets this for one that was not approved.
409enrollment_link_unavailableThe person has already started intake, so there is no new launch link; they continue by signing in on your care host. The same answer whatever the enrollment's state.
409care_host_unavailableYour brand has no active care host in this environment.
503enrollment_unavailableThe enrollment record could not be reached. Nothing was changed; retry.
403enrollment_admission_closedThis deployment is not admitting patients yet (a real-data deployment before its real-PHI launch, or without a loaded launch authorization). Answered before the request body is read or an Idempotency-Key is reserved: nothing was created and no launch link was minted. Never answered on the sandbox.
GET/api/v1/enrollmentsenrollments.read

List your enrollments by reference or change time

Your brand's enrollments in the key's environment, oldest change first, filtered by your `externalReference` and by `updatedSince`, with cursor pagination. Poll with `updatedSince` set to the last `updatedAt` you saw. Status is coarse and treated as PHI: the intake and decision states are shown only with your brand's business associate agreement and downstream-systems attestation in force and minimum-necessary patient access held by the key's creator; otherwise they read `in_progress`. No answer carries a patient, a program, an email, an intake answer or a clinical reason. Every read is recorded as an access event. Brand and environment always come from the key: a sandbox key creates and reads only sandbox enrollments of its own brand, and another brand's enrollment is indistinguishable from one that does not exist.

Query parameters

FieldTypeRequiredNotes
externalReferencestringnoOnly enrollments with this reference of yours: at most one that is open, and any earlier attempts with the same reference that have closed.
updatedSincestringnoOnly enrollments whose `updatedAt` is at or after this instant (ISO 8601).
cursorstringnoThe `nextCursor` of the previous page.
limitintegernoPer page, 1 to 100.

Example response · 200

Response
{
  "data": [
    {
      "id": "00000000-0000-4000-8000-00000000e001",
      "externalReference": "example-external-reference",
      "status": "created",
      "binding": "confirmed",
      "createdAt": "2026-01-01T00:00:00.000Z",
      "updatedAt": "2026-01-01T00:00:00.000Z"
    }
  ],
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment",
    "hasMore": false,
    "nextCursor": "example-next-cursor"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
422validation_errorQuery parameters, or the cursor, were invalid.
503enrollment_unavailableThe enrollment record could not be reached. Nothing was changed; retry.
GET/api/v1/enrollments/{id}enrollments.read

Read one enrollment's coarse status

One enrollment of your brand in the key's environment. Status is coarse and treated as PHI: the intake and decision states are shown only with your brand's business associate agreement and downstream-systems attestation in force and minimum-necessary patient access held by the key's creator; otherwise they read `in_progress`. No answer carries a patient, a program, an email, an intake answer or a clinical reason. Every read is recorded as an access event. Brand and environment always come from the key: a sandbox key creates and reads only sandbox enrollments of its own brand, and another brand's enrollment is indistinguishable from one that does not exist.

Example response · 200

Response
{
  "data": {
    "id": "00000000-0000-4000-8000-00000000e001",
    "externalReference": "example-external-reference",
    "status": "created",
    "binding": "confirmed",
    "createdAt": "2026-01-01T00:00:00.000Z",
    "updatedAt": "2026-01-01T00:00:00.000Z"
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
404enrollment_not_foundNo enrollment of your brand in this environment has that id.
503enrollment_unavailableThe enrollment record could not be reached. Nothing was changed; retry.
GET/api/v1/eventsevents.read

Backfill the webhook events of your brand

Events in the order Ambition recorded them, after an opaque cursor: catch a receiver up after downtime or seed a new one. `tier=standard` (default, `events.read`) lists commerce and enrollment events and never names a patient. `tier=clinical` needs `clinical_events.read` on a phi_server key and lists the refs-only `clinical.*` events of your API patients under the delivery rules: your patient data access open and unsuspended and, for a case event, your BAA covering the case's physician group now. A held event appears once it is released. Each event's `id` equals the `ambition-event-id` of its deliveries. Clinical pages are recorded in the access log with your key as the actor.

Query parameters

FieldTypeRequiredNotes
tier"standard" | "clinical"noWhich tier to list. `clinical` needs `clinical_events.read` on a phi_server key.
typestringnoOnly events of this type (it must be on the tier you list).
patientIdstringnoClinical tier only: one of your API patients.
afterstringnoThe `nextCursor` of the previous page.
limitintegernoEvents per page, 1 to 100.

Example response · 200

Response
{
  "data": [
    {
      "id": "example_id",
      "type": "example-type",
      "livemode": false,
      "schemaVersion": "example-schema-version",
      "data": {},
      "recordedAt": "2026-01-01T00:00:00.000Z"
    }
  ],
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment",
    "tier": "standard",
    "hasMore": false,
    "nextCursor": "example-next-cursor"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
403clinical_events_scope_requiredThe clinical tier needs `clinical_events.read` on a phi_server key.
403clinical_events_unavailableYour patient data access is not open (not approved, withdrawn or suspended).
404patient_not_foundNo patient with that id is available to this key.
422validation_errorQuery parameters were invalid (an unknown tier, a type of the other tier, a patient filter on the standard tier, or a malformed cursor).
503events_unavailableEvents could not be read. Nothing was changed; retry.
GET/api/v1/phi-access/status

Whether your patient data access is enabled, item by item

The patient data access checklist for the key's brand on this deployment's plane: the business associate agreement with the physician group, the privacy officer's frontend attestation, platform PHI terms (and merchant-of-record terms when a headless program bills with you as merchant), the commerce-terms and no-research-use-only commitments, the downstream vendor inventory, on a real-data deployment a verified sending domain (a coarse reason, never the domain itself), and the network approval; plus the kill switch, launch controls and the latest PHI page scan, and whether this deployment is admitting patients yet (`admittingPatients`). Any phi_server key may read it; a standard key gets 403 `phi_key_required`. Contains no patient data and no names.

Example response · 200

Response
{
  "data": {
    "environment": "sandbox",
    "enabled": false,
    "phiAccess": "full",
    "admittingPatients": false,
    "suspended": false,
    "completeCount": 1,
    "requiredCount": 1,
    "items": [
      {
        "key": "baa",
        "state": "complete",
        "reason": "example-reason",
        "detail": {}
      }
    ],
    "launchControls": {
      "required": false,
      "missing": [
        "example-missing"
      ]
    },
    "scan": {
      "state": "none",
      "ranAt": "2026-01-01T00:00:00.000Z",
      "findings": 1,
      "cureEndsAt": "2026-01-01T00:00:00.000Z"
    },
    "evaluatedAt": "2026-01-01T00:00:00.000Z"
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
403phi_key_requiredOnly a phi_server key reads the PHI gate status.
503phi_status_unavailableThe gate could not be read. Nothing was changed; retry.
POST/api/v1/patientspatients.write

Create a patient (your account is their identity)

Creates a delegated patient for your brand: an Ambition patient record bound to your externalId, with no Ambition login (a synthetic, non-routable identity). The same person in two brands is two patients. The email and phone are recorded as brand-asserted and never count as verified; demographics, when sent, are brand-asserted until identity verification matches them. 201 with the patient; 409 `patient_exists` with the existing id when the externalId or email is already your patient; 409 `hosted_patient_requires_link` when the email belongs to a patient who signed up on your care host. Needs Idempotency-Key and the patient headers. `Ambition-Patient-Auth: otp` is refused (401 `patient_sign_in_unverified`): a patient being created has no Ambition sign-in session; send the method your own sign-up used. A replay (the same Idempotency-Key and body) answers 201 with `meta.replayed` and only the patient's `id`, `externalId`, `status` and `revision`: no demographics are kept with the key. Read the rest with GET /patients/{patientId}.

Requires an Idempotency-Key header of 16–255 characters. Replays for 24 hours; reusing a key with a different body returns 409 rather than applying the second request.

Request body

FieldTypeRequiredNotes
externalIdstringyesYour own account id for the patient: letters, digits and ._:- only, at most 128 (never an email address).
emailstringyesThe address the patient gave you. Recorded as brand-asserted; verify it with a contact verification.
phonestringnoA US phone number; stored as +1 and ten digits.
legalNamestringno
dateOfBirthstringnoYYYY-MM-DD.
shippingAddressobjectno
residenceState"AL" | "AK" | "AZ" | "AR" | "CA" | "CO" | "CT" | "DE" | "FL" | "GA" | "HI" | "ID" | "IL" | "IN" | "IA" | "KS" | "KY" | "LA" | "ME" | "MD" | "MA" | "MI" | "MN" | "MS" | "MO" | "MT" | "NE" | "NV" | "NH" | "NJ" | "NM" | "NY" | "NC" | "ND" | "OH" | "OK" | "OR" | "PA" | "RI" | "SC" | "SD" | "TN" | "TX" | "UT" | "VT" | "VA" | "WA" | "WV" | "WI" | "WY" | "DC"noWhere the patient lives. Defaults to the shipping address's state.
linkExistingobjectnoNot available yet: a hosted patient links only by proving control of their own account (after launch). Answers 409.

Example response · 201

Response
{
  "data": {
    "id": "00000000-0000-4000-8000-00000000e001",
    "externalId": "example_externalId",
    "status": "active",
    "revision": 1,
    "residenceState": "AL",
    "createdAt": "2026-01-01T00:00:00.000Z",
    "updatedAt": "2026-01-01T00:00:00.000Z",
    "demographics": {
      "legalName": "example-legal-name",
      "dateOfBirth": "example-date-of-birth",
      "phone": "example-phone",
      "shippingAddress": {
        "line1": "example-line1",
        "line2": "example-line2",
        "city": "example-city",
        "state": "AL",
        "postalCode": "example-postal-code",
        "country": "US"
      },
      "source": "brand_asserted",
      "version": 1
    },
    "contactPoints": [
      {
        "kind": "email",
        "value": "example-value",
        "verifiedBy": "brand_asserted",
        "verifiedAt": "2026-01-01T00:00:00.000Z"
      }
    ],
    "contactVerified": false,
    "identityVerified": false
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment",
    "replayed": false
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend `Ambition-Patient-Session` and `Ambition-Patient-Auth`.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key (also: another brand's patient, PHI gate closed, key suspended).
503patient_access_unavailablePatient access is temporarily unavailable. Nothing was processed.
400idempotency_key_requiredProvide a 16–255 character Idempotency-Key.
409idempotency_conflictThe Idempotency-Key was used with a different request.
422validation_errorThe body was invalid.
403phi_access_not_enabledYour brand's PHI gate is closed, suspended, or the key's owner lost the capability.
409patient_existsThat externalId or email is already your patient; `error.resource_id` (and `Location`) names its id.
409hosted_patient_requires_linkThe email belongs to a hosted patient; linking needs the patient's own proof (after launch).
403patient_admission_closedThis deployment is not admitting patients or taking patient data yet (a real-data deployment before its real-PHI launch, or without a loaded launch authorization). Nothing was processed and the Idempotency-Key is not spent. Never answered on the sandbox.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent. A patient being created has no Ambition sign-in session, so otp cannot be claimed here; send the method your own sign-up used. Nothing was created and the Idempotency-Key is not spent.
413payload_too_largeThe request body exceeds 8192 bytes (8 KiB). Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API: the same status and code, with `request_id` `edge` and no `Request-Id` header.
409idempotency_in_progressA request with this Idempotency-Key is still being processed. Retry after `Retry-After`; nothing is applied twice.
503idempotency_unavailableThe idempotency record could not be checked. Nothing was processed; retry after `Retry-After`.
GET/api/v1/patientspatients.read

Find your patient by externalId

Your active patient with that externalId, as a one-element list, or an empty list. Exact match only. With `Ambition-Patient-Auth: otp`, `Ambition-Patient-Session` must be a current Ambition sign-in session reference of the patient found, from this key; otherwise 401 `patient_sign_in_unverified`.

Query parameters

FieldTypeRequiredNotes
externalIdstringyesYour own account id for the patient: letters, digits and ._:- only, at most 128 (never an email address).

Example response · 200

Response
{
  "data": [
    {
      "id": "00000000-0000-4000-8000-00000000e001",
      "externalId": "example_externalId",
      "status": "active",
      "revision": 1
    }
  ],
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend `Ambition-Patient-Session` and `Ambition-Patient-Auth`.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key (also: another brand's patient, PHI gate closed, key suspended).
503patient_access_unavailablePatient access is temporarily unavailable. Nothing was processed.
403phi_access_not_enabledYour brand's PHI gate is closed.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
GET/api/v1/patients/{patientId}patients.read

Read a patient

The patient's record as your brand may see it: reference, demographics, contact points with who verified each, and verification state. Audited.

Example response · 200

Response
{
  "data": {
    "id": "00000000-0000-4000-8000-00000000e001",
    "externalId": "example_externalId",
    "status": "active",
    "revision": 1,
    "residenceState": "AL",
    "createdAt": "2026-01-01T00:00:00.000Z",
    "updatedAt": "2026-01-01T00:00:00.000Z",
    "demographics": {
      "legalName": "example-legal-name",
      "dateOfBirth": "example-date-of-birth",
      "phone": "example-phone",
      "shippingAddress": {
        "line1": "example-line1",
        "line2": "example-line2",
        "city": "example-city",
        "state": "AL",
        "postalCode": "example-postal-code",
        "country": "US"
      },
      "source": "brand_asserted",
      "version": 1
    },
    "contactPoints": [
      {
        "kind": "email",
        "value": "example-value",
        "verifiedBy": "brand_asserted",
        "verifiedAt": "2026-01-01T00:00:00.000Z"
      }
    ],
    "contactVerified": false,
    "identityVerified": false
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend `Ambition-Patient-Session` and `Ambition-Patient-Auth`.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key (also: another brand's patient, PHI gate closed, key suspended).
503patient_access_unavailablePatient access is temporarily unavailable. Nothing was processed.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
PATCH/api/v1/patients/{patientId}patients.write

Update contact, demographics or shipping address

Send `If-Match: <revision>`. A changed email or phone is brand-asserted again and needs a new contact verification. When an Ambition-verified email is replaced, Ambition sends the previous address a neutral notice, asynchronously, with no new address, no link and nothing about care; a brand-asserted email gets none. The legal name and date of birth change only while they are your assertion; once identity verification matched them, or while a prescription is in progress, 409 `identity_fields_locked` (an amendment the care team reviews).

Requires an Idempotency-Key header of 16–255 characters. Replays for 24 hours; reusing a key with a different body returns 409 rather than applying the second request.

Request body

FieldTypeRequiredNotes
emailstringno
phonestringnoA US phone number; stored as +1 and ten digits.
legalNamestringno
dateOfBirthstringnoYYYY-MM-DD.
shippingAddressobjectno

Example response · 200

Response
{
  "data": {
    "id": "00000000-0000-4000-8000-00000000e001",
    "externalId": "example_externalId",
    "status": "active",
    "revision": 1,
    "residenceState": "AL",
    "createdAt": "2026-01-01T00:00:00.000Z",
    "updatedAt": "2026-01-01T00:00:00.000Z",
    "demographics": {
      "legalName": "example-legal-name",
      "dateOfBirth": "example-date-of-birth",
      "phone": "example-phone",
      "shippingAddress": {
        "line1": "example-line1",
        "line2": "example-line2",
        "city": "example-city",
        "state": "AL",
        "postalCode": "example-postal-code",
        "country": "US"
      },
      "source": "brand_asserted",
      "version": 1
    },
    "contactPoints": [
      {
        "kind": "email",
        "value": "example-value",
        "verifiedBy": "brand_asserted",
        "verifiedAt": "2026-01-01T00:00:00.000Z"
      }
    ],
    "contactVerified": false,
    "identityVerified": false
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment",
    "replayed": false
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend `Ambition-Patient-Session` and `Ambition-Patient-Auth`.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key (also: another brand's patient, PHI gate closed, key suspended).
503patient_access_unavailablePatient access is temporarily unavailable. Nothing was processed.
400idempotency_key_requiredProvide a 16–255 character Idempotency-Key.
409idempotency_conflictThe Idempotency-Key was used with a different request.
422validation_errorThe body was invalid.
428if_match_requiredSend the patient's revision as If-Match.
412revision_mismatchThe patient changed since you read it.
409identity_fields_lockedThe legal name and date of birth are verified or a prescription is in progress.
409email_in_useAnother of your patients has that email.
403patient_admission_closedThis deployment is not admitting patients or taking patient data yet (a real-data deployment before its real-PHI launch, or without a loaded launch authorization). Nothing was processed and the Idempotency-Key is not spent. Never answered on the sandbox.
413payload_too_largeThe request body exceeds 8192 bytes (8 KiB). Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API: the same status and code, with `request_id` `edge` and no `Request-Id` header.
409idempotency_in_progressA request with this Idempotency-Key is still being processed. Retry after `Retry-After`; nothing is applied twice.
503idempotency_unavailableThe idempotency record could not be checked. Nothing was processed; retry after `Retry-After`.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
POST/api/v1/patients/{patientId}/deactivatepatients.write

Close the patient's API access

The record stays with the physician group; the patient can still reach it through the Ambition records request. Refused while a prescription is in progress. Later calls for the patient answer 404.

Requires an Idempotency-Key header of 16–255 characters. Replays for 24 hours; reusing a key with a different body returns 409 rather than applying the second request.

Request body

FieldTypeRequiredNotes
reason"brand_request" | "patient_request" | "duplicate" | "other"no

Example response · 200

Response
{
  "data": {
    "id": "00000000-0000-4000-8000-00000000e001",
    "status": "deactivated"
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment",
    "replayed": false
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend `Ambition-Patient-Session` and `Ambition-Patient-Auth`.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key (also: another brand's patient, PHI gate closed, key suspended).
503patient_access_unavailablePatient access is temporarily unavailable. Nothing was processed.
400idempotency_key_requiredProvide a 16–255 character Idempotency-Key.
409idempotency_conflictThe Idempotency-Key was used with a different request.
422validation_errorThe body was invalid.
409patient_has_active_careA prescription is in progress.
413payload_too_largeThe request body exceeds 1024 bytes (1 KiB). Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API: the same status and code, with `request_id` `edge` and no `Request-Id` header.
409idempotency_in_progressA request with this Idempotency-Key is still being processed. Retry after `Retry-After`; nothing is applied twice.
503idempotency_unavailableThe idempotency record could not be checked. Nothing was processed; retry after `Retry-After`.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
POST/api/v1/patients/{patientId}/contact-verificationspatients.write

Send an Ambition contact code

Ambition generates a six-digit code and sends it to the patient's email from your verified domain (or the physician group's sender). You never see the code: relay what the patient types to `/confirm`. Use it as your sign-up code; it proves the patient controls that email, but it is not an Ambition sign-in: afterwards send the method your own sign-in used, not `otp` (`otp` needs an Ambition sign-in link's sessionReference). Required once before the first prescription is transmitted (hold `contact_unverified`). Ten minutes, five attempts, five codes per patient per hour. On the sandbox the email is caught, never delivered: read the code with `GET /sandbox/patients/{patientId}/messages`.

Requires an Idempotency-Key header of 16–255 characters. Replays for 24 hours; reusing a key with a different body returns 409 rather than applying the second request.

Request body

FieldTypeRequiredNotes
channel"email" | "sms"yes`email` at launch; `sms` answers 422 `sms_not_available` until 10DLC registration.

Example response · 201

Response
{
  "data": {
    "verificationId": "00000000-0000-4000-8000-00000000e001",
    "status": "sent",
    "channel": "email",
    "destinationHint": "example-destination-hint",
    "expiresAt": "2026-01-01T00:00:00.000Z",
    "attemptsRemaining": 1
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment",
    "replayed": false
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend `Ambition-Patient-Session` and `Ambition-Patient-Auth`.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key (also: another brand's patient, PHI gate closed, key suspended).
503patient_access_unavailablePatient access is temporarily unavailable. Nothing was processed.
400idempotency_key_requiredProvide a 16–255 character Idempotency-Key.
409idempotency_conflictThe Idempotency-Key was used with a different request.
422validation_errorThe body was invalid.
422sms_not_availableSMS codes wait on 10DLC registration; use email.
429contact_verification_rate_limitedToo many codes for this patient this hour.
503contact_sender_unavailableNo sender is configured; the code was not sent.
403patient_admission_closedThis deployment is not admitting patients or taking patient data yet (a real-data deployment before its real-PHI launch, or without a loaded launch authorization). Nothing was processed and the Idempotency-Key is not spent. Never answered on the sandbox.
413payload_too_largeThe request body exceeds 1024 bytes (1 KiB). Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API: the same status and code, with `request_id` `edge` and no `Request-Id` header.
409idempotency_in_progressA request with this Idempotency-Key is still being processed. Retry after `Retry-After`; nothing is applied twice.
503idempotency_unavailableThe idempotency record could not be checked. Nothing was processed; retry after `Retry-After`.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
POST/api/v1/patients/{patientId}/contact-verifications/{verificationId}/confirmpatients.write

Confirm the code the patient typed

`verified`, or `invalid_code` (with attempts remaining), `expired`, `locked` or `undelivered`. A wrong code counts as an attempt.

Requires an Idempotency-Key header of 16–255 characters. Replays for 24 hours; reusing a key with a different body returns 409 rather than applying the second request.

Request body

FieldTypeRequiredNotes
codestringyesThe six-digit code the patient received and typed into your UI.

Example response · 200

Response
{
  "data": {
    "verificationId": "00000000-0000-4000-8000-00000000e001",
    "status": "sent",
    "channel": "email",
    "destinationHint": "example-destination-hint",
    "expiresAt": "2026-01-01T00:00:00.000Z",
    "attemptsRemaining": 1
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment",
    "replayed": false
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend `Ambition-Patient-Session` and `Ambition-Patient-Auth`.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key (also: another brand's patient, PHI gate closed, key suspended).
503patient_access_unavailablePatient access is temporarily unavailable. Nothing was processed.
400idempotency_key_requiredProvide a 16–255 character Idempotency-Key.
409idempotency_conflictThe Idempotency-Key was used with a different request.
422validation_errorThe body was invalid.
404verification_not_foundNo verification with that id for this patient.
403patient_admission_closedThis deployment is not admitting patients or taking patient data yet (a real-data deployment before its real-PHI launch, or without a loaded launch authorization). Nothing was processed and the Idempotency-Key is not spent. Never answered on the sandbox.
413payload_too_largeThe request body exceeds 1024 bytes (1 KiB). Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API: the same status and code, with `request_id` `edge` and no `Request-Id` header.
409idempotency_in_progressA request with this Idempotency-Key is still being processed. Retry after `Retry-After`; nothing is applied twice.
503idempotency_unavailableThe idempotency record could not be checked. Nothing was processed; retry after `Retry-After`.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
POST/api/v1/patients/{patientId}/identity-verificationsidentity.write

Verify identity (BYO vendor or Ambition-run)

`{mode: byo, vendor, inquiryId}`: you ran the inquiry at your allowlisted vendor with reference_id = the patientId; Ambition fetches the result with the read-only credential you registered and checks the binding, that the inquiry was created after the patient, the template (government photo ID and selfie liveness), that the inquiry was never used for another patient, and that the verified name and date of birth match the record. `{mode: ambition}`: Ambition creates the inquiry and returns the client token for the vendor SDK you embed; poll `/current`. Your assertion of identity never counts. Identity is needed before the case enters the clinician queue.

Requires an Idempotency-Key header of 16–255 characters. Replays for 24 hours; reusing a key with a different body returns 409 rather than applying the second request.

Request body

FieldTypeRequiredNotes
mode"byo" | "ambition"yes`byo`: your vendor's inquiry. `ambition`: Ambition creates the inquiry for you to embed.
vendor"persona" | "vouched" | "incode" | "sandbox_fixture"nobyo only: your allowlisted vendor (`sandbox_fixture` on the sandbox only).
inquiryIdstringnobyo only: the inquiry you created with reference_id = the Ambition patientId.

Example response · 201

Response
{
  "data": {
    "verificationId": "00000000-0000-4000-8000-00000000e001",
    "status": "not_started",
    "method": "byo_vendor",
    "vendor": "example-vendor",
    "synthetic": false,
    "verifiedAt": "2026-01-01T00:00:00.000Z",
    "expiresAt": "2026-01-01T00:00:00.000Z",
    "reasonCode": "example-reason-code",
    "clientToken": "example-client-token",
    "sessionUrl": "example-session-url",
    "sessionExpiresAt": "2026-01-01T00:00:00.000Z"
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend `Ambition-Patient-Session` and `Ambition-Patient-Auth`.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key (also: another brand's patient, PHI gate closed, key suspended).
503patient_access_unavailablePatient access is temporarily unavailable. Nothing was processed.
400idempotency_key_requiredProvide a 16–255 character Idempotency-Key.
409idempotency_conflictThe Idempotency-Key was used with a different request.
422validation_errorThe body was invalid.
409inquiry_already_usedThat inquiry was already submitted (for this or another patient).
409demographics_requiredRecord the legal name and date of birth first.
409identity_review_openAn earlier check for this patient is waiting on a person: the covering physician group reviews it and you learn the outcome from `clinical.identity_verification.completed` or `.failed`. Retry only after that; a new check cannot replace an open review.
422idv_vendor_not_allowedThe vendor is not on the allowlist.
422idv_credential_missingRegister a read-only credential for the vendor first.
503idv_vendor_not_certifiedAmbition has not certified this vendor's adapter yet; the inquiry stays pending.
503idv_vendor_unavailableThe vendor could not be reached; the inquiry stays pending. Poll `/current`.
403patient_admission_closedThis deployment is not admitting patients or taking patient data yet (a real-data deployment before its real-PHI launch, or without a loaded launch authorization). Nothing was processed and the Idempotency-Key is not spent. Never answered on the sandbox.
413payload_too_largeThe request body exceeds 1024 bytes (1 KiB). Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API: the same status and code, with `request_id` `edge` and no `Request-Id` header.
409idempotency_in_progressA request with this Idempotency-Key is still being processed. Retry after `Retry-After`; nothing is applied twice.
503idempotency_unavailableThe idempotency record could not be checked. Nothing was processed; retry after `Retry-After`.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
GET/api/v1/patients/{patientId}/identity-verifications/currentpatients.read

The patient's identity verification state

A pending inquiry is fetched from the vendor at most every 10 seconds, so polling this settles it. Coarse reason codes only; nothing the vendor saw.

Example response · 200

Response
{
  "data": {
    "verificationId": "00000000-0000-4000-8000-00000000e001",
    "status": "not_started",
    "method": "byo_vendor",
    "vendor": "example-vendor",
    "synthetic": false,
    "verifiedAt": "2026-01-01T00:00:00.000Z",
    "expiresAt": "2026-01-01T00:00:00.000Z",
    "reasonCode": "example-reason-code",
    "clientToken": "example-client-token",
    "sessionUrl": "example-session-url",
    "sessionExpiresAt": "2026-01-01T00:00:00.000Z"
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend `Ambition-Patient-Session` and `Ambition-Patient-Auth`.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key (also: another brand's patient, PHI gate closed, key suspended).
503patient_access_unavailablePatient access is temporarily unavailable. Nothing was processed.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
POST/api/v1/patients/{patientId}/eligibilityeligibility.check

Can this patient be admitted to the program in a state

Reason codes only (never a clinician or licence). Also says what stands between the patient and a prescription: consent scopes, identity, an Ambition-verified contact. Headless programs serve async states at launch: a state that needs a video visit answers `modality_unavailable_headless`. California is excluded. While the program's self-serve enrollment is off (the default for the invite-only pilot) every state answers `program_self_enrollment_closed`. Nothing is written but the audit.

Request body

FieldTypeRequiredNotes
programHandlestringyesThe program's opaque public handle (`prg_` and 24 hex characters). Link the care host's Start page with it: `/start?p=<handle>`. It never changes and carries no program or condition word.
state"AL" | "AK" | "AZ" | "AR" | "CA" | "CO" | "CT" | "DE" | "FL" | "GA" | "HI" | "ID" | "IL" | "IN" | "IA" | "KS" | "KY" | "LA" | "ME" | "MD" | "MA" | "MI" | "MN" | "MS" | "MO" | "MT" | "NE" | "NV" | "NH" | "NJ" | "NM" | "NY" | "NC" | "ND" | "OH" | "OK" | "OR" | "PA" | "RI" | "SC" | "SD" | "TN" | "TX" | "UT" | "VT" | "VA" | "WA" | "WV" | "WI" | "WY" | "DC"yes

Example response · 200

Response
{
  "data": {
    "programHandle": "example-program-handle",
    "state": "AL",
    "eligible": false,
    "reasonCode": "example-reason-code",
    "requiredModality": "async",
    "requiredConsentScopes": [
      "example-required-consent-scopes"
    ],
    "consentCaptureMode": "brand_rendered",
    "identityRequired": false,
    "identityVerificationMode": "byo_vendor",
    "contactVerificationRequired": false,
    "paymentRequired": false
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend `Ambition-Patient-Session` and `Ambition-Patient-Auth`.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key (also: another brand's patient, PHI gate closed, key suspended).
503patient_access_unavailablePatient access is temporarily unavailable. Nothing was processed.
404program_not_foundNo program with that handle in your brand.
413payload_too_largeThe request body exceeds 1024 bytes (1 KiB). Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API: the same status and code, with `request_id` `edge` and no `Request-Id` header.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
POST/api/v1/patients/{patientId}/enrollmentsintake.write

Admit the patient to a program

Headless admission: no launch token, no OTP. The enrollment authority rule and eligibility apply; a contact need not be verified yet (that gates the first transmission). The program's self-serve enrollment must be on, as for `POST /enrollments`. One open enrollment per program and state: a repeat returns it (200), even after the switch is turned off.

Requires an Idempotency-Key header of 16–255 characters. Replays for 24 hours; reusing a key with a different body returns 409 rather than applying the second request.

Request body

FieldTypeRequiredNotes
programHandlestringyesThe program's opaque public handle (`prg_` and 24 hex characters). Link the care host's Start page with it: `/start?p=<handle>`. It never changes and carries no program or condition word.
state"AL" | "AK" | "AZ" | "AR" | "CA" | "CO" | "CT" | "DE" | "FL" | "GA" | "HI" | "ID" | "IL" | "IN" | "IA" | "KS" | "KY" | "LA" | "ME" | "MD" | "MA" | "MI" | "MN" | "MS" | "MO" | "MT" | "NE" | "NV" | "NH" | "NJ" | "NM" | "NY" | "NC" | "ND" | "OH" | "OK" | "OR" | "PA" | "RI" | "SC" | "SD" | "TN" | "TX" | "UT" | "VT" | "VA" | "WA" | "WV" | "WI" | "WY" | "DC"yes

Example response · 201

Response
{
  "data": {
    "id": "00000000-0000-4000-8000-00000000e001",
    "programHandle": "example-program-handle",
    "state": "AL",
    "status": "example-status",
    "caseId": "00000000-0000-4000-8000-00000000e001",
    "createdAt": "2026-01-01T00:00:00.000Z",
    "updatedAt": "2026-01-01T00:00:00.000Z"
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend `Ambition-Patient-Session` and `Ambition-Patient-Auth`.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key (also: another brand's patient, PHI gate closed, key suspended).
503patient_access_unavailablePatient access is temporarily unavailable. Nothing was processed.
400idempotency_key_requiredProvide a 16–255 character Idempotency-Key.
409idempotency_conflictThe Idempotency-Key was used with a different request.
422validation_errorThe body was invalid.
422not_eligibleThe patient cannot be admitted; `code` is the eligibility reason.
422program_self_enrollment_closedSelf-serve enrollment is off for this program (the default while enrollment is invite-only), so no new patient is admitted. A program manager turns it on in the console once your physician group approves self-serve enrollment.
403patient_admission_closedThis deployment is not admitting patients or taking patient data yet (a real-data deployment before its real-PHI launch, or without a loaded launch authorization). Nothing was processed and the Idempotency-Key is not spent. Never answered on the sandbox.
413payload_too_largeThe request body exceeds 1024 bytes (1 KiB). Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API: the same status and code, with `request_id` `edge` and no `Request-Id` header.
409idempotency_in_progressA request with this Idempotency-Key is still being processed. Retry after `Retry-After`; nothing is applied twice.
503idempotency_unavailableThe idempotency record could not be checked. Nothing was processed; retry after `Retry-After`.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
GET/api/v1/patients/{patientId}/enrollmentspatients.read

The patient's enrollments

Every enrollment of the patient in your brand, newest first, with coarse status.

Example response · 200

Response
{
  "data": [
    {
      "id": "00000000-0000-4000-8000-00000000e001",
      "programHandle": "example-program-handle",
      "state": "AL",
      "status": "example-status",
      "caseId": "00000000-0000-4000-8000-00000000e001",
      "createdAt": "2026-01-01T00:00:00.000Z",
      "updatedAt": "2026-01-01T00:00:00.000Z"
    }
  ],
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend `Ambition-Patient-Session` and `Ambition-Patient-Auth`.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key (also: another brand's patient, PHI gate closed, key suspended).
503patient_access_unavailablePatient access is temporarily unavailable. Nothing was processed.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
GET/api/v1/patients/{patientId}/documentsdocuments.read

The patient's documents

Consent records (with the exact text accepted), care records, signed prescriptions and lab reports the clinician signed off; every document is offered as html, json and pdf. A case-bound document is listed only where your BAA covers the case. Audited. `clinical.document.available` announces each new one.

Example response · 200

Response
{
  "data": [
    {
      "id": "example_id",
      "kind": "consent_record",
      "title": "example-title",
      "status": "example-status",
      "createdAt": "2026-01-01T00:00:00.000Z",
      "caseId": "00000000-0000-4000-8000-00000000e001",
      "formats": [
        "html"
      ]
    }
  ],
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend `Ambition-Patient-Session` and `Ambition-Patient-Auth`.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key (also: another brand's patient, PHI gate closed, key suspended).
503patient_access_unavailablePatient access is temporarily unavailable. Nothing was processed.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
GET/api/v1/patients/{patientId}/documents/{documentId}/contentdocuments.read

One document's content

`format=json` (default), `html` (escaped, no scripts) or `pdf` (rendered on request from the same minimum-necessary content, headed by your brand's mark; `Content-Disposition: attachment` with a name that is the kind of document only, never the patient's). Each read, in any format, is one audited read; never cached. A case-bound document (care record, prescription, lab report) needs your BAA with the physician group covering that case.

Query parameters

FieldTypeRequiredNotes
format"json" | "html" | "pdf"no`json` (default), `html` (escaped, no scripts) or `pdf` (rendered on request, headed by your brand's mark).

Example response · 200

Response
{
  "data": {},
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend `Ambition-Patient-Session` and `Ambition-Patient-Auth`.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key (also: another brand's patient, PHI gate closed, key suspended).
503patient_access_unavailablePatient access is temporarily unavailable. Nothing was processed.
404document_not_foundNo document with that id for this patient.
403entity_baa_missingA care record, prescription or lab report whose case's physician group your BAA does not cover.
503document_render_unavailableThe PDF could not be rendered. Nothing about the document is in the error; retry or read `html`.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
POST/api/v1/patients/{patientId}/record-exportsdocuments.read

Export the patient's record (right of access)

The patient's copy of their record, logged as a fulfilled right-of-access request. The content is in this answer only; store it. A replay (the same Idempotency-Key) answers 201 with the same export's manifest (every field the first answer had, the same values), `content` null and `meta.replayed` true: the record is never exported twice. Cases (and their prescriptions) whose physician group your BAA does not cover are left out; the patient obtains those through the group's records request.

Requires an Idempotency-Key header of 16–255 characters. Replays for 24 hours; reusing a key with a different body returns 409 rather than applying the second request.

Example response · 201

Response
{
  "data": {
    "id": "00000000-0000-4000-8000-00000000e001",
    "reference": "example-reference",
    "status": "example-status",
    "schema": "ambition.patient-record-export.v1",
    "generatedAt": "2026-01-01T00:00:00.000Z",
    "createdAt": "2026-01-01T00:00:00.000Z",
    "sha256": "example-sha256",
    "sections": {},
    "content": {}
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment",
    "replayed": false
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend `Ambition-Patient-Session` and `Ambition-Patient-Auth`.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key (also: another brand's patient, PHI gate closed, key suspended).
503patient_access_unavailablePatient access is temporarily unavailable. Nothing was processed.
400idempotency_key_requiredProvide a 16–255 character Idempotency-Key.
409idempotency_conflictThe Idempotency-Key was used with a different request.
422validation_errorThe body was invalid.
503patient_service_unavailableThe record could not be exported, or the export read again. Nothing was exported twice; retry.
409idempotency_in_progressA request with this Idempotency-Key is still being processed. Retry after `Retry-After`; nothing is applied twice.
503idempotency_unavailableThe idempotency record could not be checked. Nothing was processed; retry after `Retry-After`.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
GET/api/v1/patients/{patientId}/record-exports/{exportId}documents.read

An export's manifest

The digest and section counts of an earlier export (no content).

Example response · 200

Response
{
  "data": {
    "id": "00000000-0000-4000-8000-00000000e001",
    "reference": "example-reference",
    "status": "example-status",
    "schema": "ambition.patient-record-export.v1",
    "generatedAt": "2026-01-01T00:00:00.000Z",
    "createdAt": "2026-01-01T00:00:00.000Z",
    "sha256": "example-sha256",
    "sections": {},
    "content": {}
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend `Ambition-Patient-Session` and `Ambition-Patient-Auth`.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key (also: another brand's patient, PHI gate closed, key suspended).
503patient_access_unavailablePatient access is temporarily unavailable. Nothing was processed.
404export_not_foundNo export with that id for this patient.
503patient_service_unavailableThe record could not be exported, or the export read again. Nothing was exported twice; retry.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
POST/api/v1/patients/{patientId}/referralsintake.write

Refer the patient to your medical group

Refers your patient to the medical group that covers your brand, for one program and state. On live data this is how a patient you created becomes eligible for intake. Every eligibility rule applies except the program's self-serve switch (a referral the group reviews is not self-serve). The referral waits for the group (`pending_group_review`; `clinical.referral.accepted` or `.declined` follows, `.expired` after 30 days with no decision) unless the group's written policy allows self-enrollment and the program's self-serve switch is on, when it is accepted at once (`acceptance: group_policy`). One open referral per program and state: a repeat returns it (200).

Requires an Idempotency-Key header of 16–255 characters. Replays for 24 hours; reusing a key with a different body returns 409 rather than applying the second request.

Request body

FieldTypeRequiredNotes
programHandlestringyesThe program's opaque public handle from the catalog (`prg_` and 24 hex characters).
state"AL" | "AK" | "AZ" | "AR" | "CA" | "CO" | "CT" | "DE" | "FL" | "GA" | "HI" | "ID" | "IL" | "IN" | "IA" | "KS" | "KY" | "LA" | "ME" | "MD" | "MA" | "MI" | "MN" | "MS" | "MO" | "MT" | "NE" | "NV" | "NH" | "NJ" | "NM" | "NY" | "NC" | "ND" | "OH" | "OK" | "OR" | "PA" | "RI" | "SC" | "SD" | "TN" | "TX" | "UT" | "VT" | "VA" | "WA" | "WV" | "WI" | "WY" | "DC"yesThe two-letter USPS state (or DC) the patient will be cared for in. California is never available.
externalReferencestringnoYour own reference for this referral (an order or request id): letters, digits and `._:-`. Never PHI.

Example response · 201

Response
{
  "data": {
    "id": "00000000-0000-4000-8000-00000000e001",
    "programHandle": "example-program-handle",
    "state": "AL",
    "status": "pending_group_review",
    "acceptance": "group_review",
    "sandboxDecision": false,
    "declineReason": "outside_group_scope",
    "externalReference": "example-external-reference",
    "enrollmentId": "00000000-0000-4000-8000-00000000e001",
    "createdAt": "2026-01-01T00:00:00.000Z",
    "updatedAt": "2026-01-01T00:00:00.000Z",
    "decidedAt": "2026-01-01T00:00:00.000Z",
    "enrolledAt": "2026-01-01T00:00:00.000Z",
    "expiresAt": "2026-01-01T00:00:00.000Z"
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment",
    "replayed": false
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
400invalid_jsonThe body is not valid JSON.
422validation_errorThe body does not match the contract.
404program_not_foundNo program with that handle in your brand.
422program_not_headlessThe program does not run headless or hybrid.
422state_excludedCalifornia is never available.
422modality_unavailable_headlessThe state needs a video visit, which headless care does not serve yet.
422no_clinician_coverageNo clinician of a covering group is licensed in the state.
422enrollment_authorization_requiredLive data only: the medical group's medical director has not authorized enrollment for this program and state. Any other enrollment-authority or availability reason is answered as its own code.
422medical_group_unavailableNo verified medical group serves your brand and this program.
422referrals_not_acceptedThe medical group is not taking referrals from your brand now.
422external_reference_invalidexternalReference is 1 to 128 letters, digits and `._:-`.
409referral_declinedThe medical group declined this patient for this program and state in the last 30 days. A later referral always waits for the group's review.
403patient_admission_closedThis deployment is not admitting patients or taking patient data yet (a real-data deployment before its real-PHI launch, or without a loaded launch authorization). Nothing was processed and the Idempotency-Key is not spent. Never answered on the sandbox.
413payload_too_largeThe request body exceeds 1024 bytes (1 KiB). Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API: the same status and code, with `request_id` `edge` and no `Request-Id` header.
400idempotency_key_requiredProvide a 16–255 character Idempotency-Key.
409idempotency_conflictThis Idempotency-Key was already used with different request parameters. Nothing was applied; use a new key.
409idempotency_in_progressA request with this Idempotency-Key is still being processed. Retry after `Retry-After`; nothing is applied twice.
503idempotency_unavailableThe idempotency record could not be checked. Nothing was processed; retry after `Retry-After`.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
GET/api/v1/patients/{patientId}/referralspatients.read

The patient's referrals

Every referral of the patient in your brand, newest first. Audited.

Example response · 200

Response
{
  "data": [
    {
      "id": "00000000-0000-4000-8000-00000000e001",
      "programHandle": "example-program-handle",
      "state": "AL",
      "status": "pending_group_review",
      "acceptance": "group_review",
      "sandboxDecision": false,
      "declineReason": "outside_group_scope",
      "externalReference": "example-external-reference",
      "enrollmentId": "00000000-0000-4000-8000-00000000e001",
      "createdAt": "2026-01-01T00:00:00.000Z",
      "updatedAt": "2026-01-01T00:00:00.000Z",
      "decidedAt": "2026-01-01T00:00:00.000Z",
      "enrolledAt": "2026-01-01T00:00:00.000Z",
      "expiresAt": "2026-01-01T00:00:00.000Z"
    }
  ],
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
GET/api/v1/patients/{patientId}/referrals/{referralId}patients.read

One referral

One referral of the patient, as it is now (an open referral past its expiry reads `expired`). Audited.

Example response · 200

Response
{
  "data": {
    "id": "00000000-0000-4000-8000-00000000e001",
    "programHandle": "example-program-handle",
    "state": "AL",
    "status": "pending_group_review",
    "acceptance": "group_review",
    "sandboxDecision": false,
    "declineReason": "outside_group_scope",
    "externalReference": "example-external-reference",
    "enrollmentId": "00000000-0000-4000-8000-00000000e001",
    "createdAt": "2026-01-01T00:00:00.000Z",
    "updatedAt": "2026-01-01T00:00:00.000Z",
    "decidedAt": "2026-01-01T00:00:00.000Z",
    "enrolledAt": "2026-01-01T00:00:00.000Z",
    "expiresAt": "2026-01-01T00:00:00.000Z"
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment",
    "replayed": false
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
404referral_not_foundNo referral with that id for this patient.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
POST/api/v1/patients/{patientId}/referrals/{referralId}/acceptintake.write

The patient accepts the referral

Call from the patient's signed-in session in your app, once the group accepted the referral, the patient accepted every consent the program requires (consent presentations and consents) and you showed the program's terms. Ambition checks the consent evidence, the terms digest and the enrollment authority again, then enrolls the patient (`status: enrolled`, `enrollmentId`): intake drafts and submissions for the program and state are accepted from then on. Identity verification is not needed here; a case without it waits in `awaiting_identity`. A repeat returns the enrolled referral.

Requires an Idempotency-Key header of 16–255 characters. Replays for 24 hours; reusing a key with a different body returns 409 rather than applying the second request.

Request body

FieldTypeRequiredNotes
termsDigeststringyesThe `disclosure.digest` of the program terms you showed the patient (GET /catalog/{brand}/programs/{handle}). It records your attestation that the patient was shown these terms; Ambition checks they are the program's current approved terms (if they changed, show the new ones and send their digest). Consent evidence, by contrast, is Ambition's own (presentation nonces and render receipts).

Example response · 200

Response
{
  "data": {
    "id": "00000000-0000-4000-8000-00000000e001",
    "programHandle": "example-program-handle",
    "state": "AL",
    "status": "pending_group_review",
    "acceptance": "group_review",
    "sandboxDecision": false,
    "declineReason": "outside_group_scope",
    "externalReference": "example-external-reference",
    "enrollmentId": "00000000-0000-4000-8000-00000000e001",
    "createdAt": "2026-01-01T00:00:00.000Z",
    "updatedAt": "2026-01-01T00:00:00.000Z",
    "decidedAt": "2026-01-01T00:00:00.000Z",
    "enrolledAt": "2026-01-01T00:00:00.000Z",
    "expiresAt": "2026-01-01T00:00:00.000Z"
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment",
    "replayed": false
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
400invalid_jsonThe body is not valid JSON.
422validation_errorThe body does not match the contract.
404referral_not_foundNo referral with that id for this patient.
409referral_pending_group_reviewThe medical group has not accepted the referral yet, or it was accepted by the group's policy and that policy (or the program's self-serve switch) has changed since, so it went back to the group's review.
422program_not_headlessThe program no longer runs headless or hybrid.
422modality_unavailable_headlessThe state now needs a video visit, which headless care does not serve yet.
422no_clinician_coverageNo clinician of a covering group is licensed in the state any more. Every eligibility and enrollment-authority reason is checked again at acceptance and answered as its own code.
409referral_declinedThe medical group declined the referral.
409referral_withdrawnYou withdrew the referral.
409referral_expiredThe referral expired; make a new one.
422consent_requiredThe patient has not accepted the current consent document of every scope the program requires in the state.
422reconsent_requiredA consent document changed since the patient accepted it.
422hosted_consent_requiredA required consent must be captured on the hosted component.
422terms_acceptance_requiredSend termsDigest.
422terms_digest_invalidtermsDigest is a lowercase hex SHA-256.
409terms_changedThe program terms changed; show the current terms and send their digest.
422program_terms_unavailableThe program has no approved terms.
422enrollment_authorization_cap_reachedThe group's authorization admits no more patients.
422enrollment_authorization_requiredLive data only: enrollment for this program and state is not authorized.
422medical_group_unavailableThe medical group no longer serves your brand and this program.
422referrals_not_acceptedThe medical group is not taking referrals from your brand now.
403patient_admission_closedThis deployment is not admitting patients or taking patient data yet (a real-data deployment before its real-PHI launch, or without a loaded launch authorization). Nothing was processed and the Idempotency-Key is not spent. Never answered on the sandbox.
413payload_too_largeThe request body exceeds 1024 bytes (1 KiB). Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API: the same status and code, with `request_id` `edge` and no `Request-Id` header.
400idempotency_key_requiredProvide a 16–255 character Idempotency-Key.
409idempotency_conflictThis Idempotency-Key was already used with different request parameters. Nothing was applied; use a new key.
409idempotency_in_progressA request with this Idempotency-Key is still being processed. Retry after `Retry-After`; nothing is applied twice.
503idempotency_unavailableThe idempotency record could not be checked. Nothing was processed; retry after `Retry-After`.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
POST/api/v1/patients/{patientId}/referrals/{referralId}/withdrawintake.write

Withdraw an open referral

Closes a referral that is still waiting for the group or for the patient. A referral the patient accepted cannot be withdrawn: cancel the case instead. A repeat returns the withdrawn referral.

Requires an Idempotency-Key header of 16–255 characters. Replays for 24 hours; reusing a key with a different body returns 409 rather than applying the second request.

Example response · 200

Response
{
  "data": {
    "id": "00000000-0000-4000-8000-00000000e001",
    "programHandle": "example-program-handle",
    "state": "AL",
    "status": "pending_group_review",
    "acceptance": "group_review",
    "sandboxDecision": false,
    "declineReason": "outside_group_scope",
    "externalReference": "example-external-reference",
    "enrollmentId": "00000000-0000-4000-8000-00000000e001",
    "createdAt": "2026-01-01T00:00:00.000Z",
    "updatedAt": "2026-01-01T00:00:00.000Z",
    "decidedAt": "2026-01-01T00:00:00.000Z",
    "enrolledAt": "2026-01-01T00:00:00.000Z",
    "expiresAt": "2026-01-01T00:00:00.000Z"
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment",
    "replayed": false
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
404referral_not_foundNo referral with that id for this patient.
409referral_enrolledThe patient already accepted; cancel their case instead.
409referral_declinedThe medical group declined the referral.
409referral_expiredThe referral already expired.
400idempotency_key_requiredProvide a 16–255 character Idempotency-Key.
409idempotency_conflictThis Idempotency-Key was already used with different request parameters. Nothing was applied; use a new key.
409idempotency_in_progressA request with this Idempotency-Key is still being processed. Retry after `Retry-After`; nothing is applied twice.
503idempotency_unavailableThe idempotency record could not be checked. Nothing was processed; retry after `Retry-After`.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
POST/api/v1/sandbox/patients/{patientId}/referrals/{referralId}/simulateintake.write

Sandbox: the medical group accepts or declines a referral

The sandbox medical group decides a referral waiting for review, through the same decision the group's own members make (`clinical.referral.accepted` or `.declined`, livemode false; `sandboxDecision: true` on the referral). Sandbox only: a sandbox phi_server key, a synthetic brand and the developer sandbox deployment (any other answers 403 `sandbox_simulation_only`). 600 decisions per brand per hour. The same decision again answers the referral as it is.

Requires an Idempotency-Key header of 16–255 characters. Replays for 24 hours; reusing a key with a different body returns 409 rather than applying the second request.

Request body

FieldTypeRequiredNotes
decision"accept" | "decline"yesWhat the sandbox medical group decides.
reason"outside_group_scope" | "group_capacity" | "duplicate_referral" | "information_incomplete" | "other"noRequired to decline; not sent to accept.

Example response · 200

Response
{
  "data": {
    "id": "00000000-0000-4000-8000-00000000e001",
    "programHandle": "example-program-handle",
    "state": "AL",
    "status": "pending_group_review",
    "acceptance": "group_review",
    "sandboxDecision": false,
    "declineReason": "outside_group_scope",
    "externalReference": "example-external-reference",
    "enrollmentId": "00000000-0000-4000-8000-00000000e001",
    "createdAt": "2026-01-01T00:00:00.000Z",
    "updatedAt": "2026-01-01T00:00:00.000Z",
    "decidedAt": "2026-01-01T00:00:00.000Z",
    "enrolledAt": "2026-01-01T00:00:00.000Z",
    "expiresAt": "2026-01-01T00:00:00.000Z"
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment",
    "replayed": false
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
400invalid_jsonThe body is not valid JSON.
422validation_errorThe body does not match the contract.
404referral_not_foundNo referral with that id for this patient.
403sandbox_simulation_onlyDeveloper sandbox, sandbox key and synthetic brand only.
409referral_not_pendingThe referral is not waiting for the medical group.
409referral_expiredThe referral expired before a decision.
422referral_decision_invaliddecision is accept or decline; an acceptance takes no reason.
422referral_decline_reason_invalidA decline names one reason code.
422medical_group_unavailableThe medical group no longer serves your brand and this program.
429sandbox_simulation_rate_limitedAt most 600 sandbox decisions per brand per hour.
413payload_too_largeThe request body exceeds 1024 bytes (1 KiB). Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API: the same status and code, with `request_id` `edge` and no `Request-Id` header.
400idempotency_key_requiredProvide a 16–255 character Idempotency-Key.
409idempotency_conflictThis Idempotency-Key was already used with different request parameters. Nothing was applied; use a new key.
409idempotency_in_progressA request with this Idempotency-Key is still being processed. Retry after `Retry-After`; nothing is applied twice.
503idempotency_unavailableThe idempotency record could not be checked. Nothing was processed; retry after `Retry-After`.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
POST/api/v1/sign-in-sessions/revokepatients.write

Sign the patient out of an Ambition sign-in session

Call it when the patient signs out of your site (and whenever you end that session): the sessionReference stops backing `Ambition-Patient-Auth: otp` at once, and a replay of its confirmation answers 409 sign_in_session_not_bound instead of the reference. Revokes the session of this patient, bound by this key; another brand's, key's or patient's session, or an unknown one, is 404 sign_in_session_not_found. Signing out again answers `revoked` again. Ending access is never refused for your brand's state: it works with the PHI gate closed or the brand suspended, needing only a live phi_server key with patients.write. Takes no patient headers. Needs Idempotency-Key.

Requires an Idempotency-Key header of 16–255 characters. Replays for 24 hours; reusing a key with a different body returns 409 rather than applying the second request.

Request body

FieldTypeRequiredNotes
patientIdstringyesThe patient the sign-in session is for.
sessionReferencestringyesThe sessionReference the confirmation answered (`ass_` and 43 characters). Only its digest reaches the database.

Example response · 200

Response
{
  "data": {
    "status": "revoked"
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment",
    "replayed": false
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
429rate_limitedToo many requests, per network address and per API key, or (opening a link) more than 5 links for this patient in 15 minutes, in which case nothing is sent. Retry after the interval in `Retry-After` (the patient's budget frees as their links age).
403browser_origin_refusedSign-in sessions are server-to-server only: call from your backend, never from a browser.
403phi_key_requiredSign-in sessions take a phi_server key.
400invalid_jsonThe body is not valid JSON.
422validation_errorThe body does not match the contract.
503patient_access_unavailablePatient access is temporarily unavailable. Nothing was processed; retry.
404sign_in_session_not_foundNo sign-in session of this patient with that reference was bound by this key (also: another brand's, key's or patient's session). Never answered for a closed PHI gate or a suspension: those do not stop a sign-out.
413payload_too_largeThe request body exceeds 1024 bytes (1 KiB). Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API: the same status and code, with `request_id` `edge` and no `Request-Id` header.
400idempotency_key_requiredProvide a 16–255 character Idempotency-Key.
409idempotency_conflictThis Idempotency-Key was already used with different request parameters. Nothing was applied; use a new key.
409idempotency_in_progressA request with this Idempotency-Key is still being processed. Retry after `Retry-After`; nothing is applied twice.
503idempotency_unavailableThe idempotency record could not be checked. Nothing was processed; retry after `Retry-After`.
POST/api/v1/patients/{patientId}/consent-presentationsconsents.write

Issue presentation nonces for the consent documents you will show

One single-use presentation per document, bound to the patient, the document version, its text and label digests, the program, state and locale, valid 60 minutes. Refused with `hosted_consent_required` for a scope that must be captured on the hosted component, and `document_not_current` for a document that is not the current text.

Request body

FieldTypeRequiredNotes
programHandlestringyesThe program's opaque public handle (`prg_` and 24 hex characters). Link the care host's Start page with it: `/start?p=<handle>`. It never changes and carries no program or condition word.
state"AL" | "AK" | "AZ" | "AR" | "CA" | "CO" | "CT" | "DE" | "FL" | "GA" | "HI" | "ID" | "IL" | "IN" | "IA" | "KS" | "KY" | "LA" | "ME" | "MD" | "MA" | "MI" | "MN" | "MS" | "MO" | "MT" | "NE" | "NV" | "NH" | "NJ" | "NM" | "NY" | "NC" | "ND" | "OH" | "OK" | "OR" | "PA" | "RI" | "SC" | "SD" | "TN" | "TX" | "UT" | "VT" | "VA" | "WA" | "WV" | "WI" | "WY" | "DC"yesThe state the patient is in; the documents are the ones issued for it.
localestringnoA BCP 47 language-region tag, e.g. `en-US`.
documentIdsarrayyesThe `documentId`s you will show, one per scope.

Example response · 201

Response
{
  "data": [
    {
      "presentationId": "00000000-0000-4000-8000-00000000e001",
      "documentId": "00000000-0000-4000-8000-00000000e001",
      "scope": "telehealth_consent",
      "version": 1,
      "sha256": "example-sha256",
      "labelSha256": "example-label-sha256",
      "locale": "example-locale",
      "issuedAt": "2026-01-01T00:00:00.000Z",
      "expiresAt": "2026-01-01T00:00:00.000Z"
    }
  ],
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
503patient_access_unavailablePatient access is temporarily unavailable. Nothing was processed.
503temporarily_unavailableThe request could not be completed. Nothing was changed; retry.
422validation_errorThe request body or parameters were invalid.
404program_not_foundNo program with that handle in this brand.
422document_not_currentThat document is not the current text for this program, state and locale.
422hosted_consent_requiredThat scope must be captured on the hosted consent component.
403patient_admission_closedThis deployment is not admitting patients or taking patient data yet (a real-data deployment before its real-PHI launch, or without a loaded launch authorization). Nothing was processed and the Idempotency-Key is not spent. Never answered on the sandbox.
413payload_too_largeThe request body exceeds 8192 bytes (8 KiB). Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API: the same status and code, with `request_id` `edge` and no `Request-Id` header.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
POST/api/v1/patients/{patientId}/consentsconsents.write

Record a brand-rendered consent with its render receipt

Records the patient's acceptance against its presentation: acceptedAt between issue and expiry, the receipt's text and label digests equal to the document's, the locale the presentation's, and exactly one checkbox event of the scope's own between issue and acceptance. The record is append-only, with actor provenance and how the patient authenticated to you. Emits clinical.consent.recorded.

Requires an Idempotency-Key header of 16–255 characters. Replays for 24 hours; reusing a key with a different body returns 409 rather than applying the second request.

Request body

FieldTypeRequiredNotes
presentationIdstringyes
patientAction"checkbox" | "signature"yes
acceptedAtstringyes
receiptobjectyes
signatureSha256stringnoFor `signature`: SHA-256 of the typed signature (the signature itself is not sent).
deviceSha256stringnoOptional SHA-256 of the patient's user agent and address, as your server saw them.

Example response · 201

Response
{
  "data": {
    "id": "00000000-0000-4000-8000-00000000e001",
    "scope": "example-scope",
    "status": "proposed",
    "documentId": "00000000-0000-4000-8000-00000000e001",
    "documentVersion": "example-document-version",
    "sha256": "example-sha256",
    "acceptedAt": "2026-01-01T00:00:00.000Z",
    "withdrawnAt": "2026-01-01T00:00:00.000Z",
    "withdrawsConsentId": "00000000-0000-4000-8000-00000000e001",
    "withdrawn": false,
    "channel": "example-channel",
    "actorKind": "example-actor-kind",
    "locale": "example-locale",
    "patientAction": "example-patient-action",
    "text": "example-text",
    "labelText": "example-label-text"
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
503patient_access_unavailablePatient access is temporarily unavailable. Nothing was processed.
503temporarily_unavailableThe request could not be completed. Nothing was changed; retry.
422validation_errorThe request body or parameters were invalid.
404presentation_not_foundNo presentation with that id for this patient.
409presentation_usedThat presentation was already used.
422presentation_expiredThe presentation expired; issue a new one.
422rendered_text_mismatchrenderedTextSha256 is not the document's.
422label_mismatchlabelSha256 is not the document's label.
422checkbox_not_separateThe scope needs exactly one checkbox event of its own.
422hosted_consent_requiredThat scope must be captured on the hosted consent component.
403patient_admission_closedThis deployment is not admitting patients or taking patient data yet (a real-data deployment before its real-PHI launch, or without a loaded launch authorization). Nothing was processed and the Idempotency-Key is not spent. Never answered on the sandbox.
413payload_too_largeThe request body exceeds 16384 bytes (16 KiB). Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API: the same status and code, with `request_id` `edge` and no `Request-Id` header.
400idempotency_key_requiredProvide a 16–255 character Idempotency-Key.
409idempotency_conflictThis Idempotency-Key was already used with different request parameters. Nothing was applied; use a new key.
409idempotency_in_progressA request with this Idempotency-Key is still being processed. Retry after `Retry-After`; nothing is applied twice.
503idempotency_unavailableThe idempotency record could not be checked. Nothing was processed; retry after `Retry-After`.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
GET/api/v1/patients/{patientId}/consentspatients.read

The patient's consent records

Every consent record of the patient in your brand, newest first, without evidence internals. Audited.

Example response · 200

Response
{
  "data": [
    {
      "id": "00000000-0000-4000-8000-00000000e001",
      "scope": "example-scope",
      "status": "proposed",
      "documentId": "00000000-0000-4000-8000-00000000e001",
      "documentVersion": "example-document-version",
      "sha256": "example-sha256",
      "acceptedAt": "2026-01-01T00:00:00.000Z",
      "withdrawnAt": "2026-01-01T00:00:00.000Z",
      "withdrawsConsentId": "00000000-0000-4000-8000-00000000e001",
      "withdrawn": false,
      "channel": "example-channel",
      "actorKind": "example-actor-kind",
      "locale": "example-locale",
      "patientAction": "example-patient-action",
      "text": "example-text",
      "labelText": "example-label-text"
    }
  ],
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
503patient_access_unavailablePatient access is temporarily unavailable. Nothing was processed.
503temporarily_unavailableThe request could not be completed. Nothing was changed; retry.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
GET/api/v1/patients/{patientId}/consents/{consentId}patients.read

One consent record, with the text exactly as accepted

The record and the exact text and label the patient accepted. Audited individually.

Example response · 200

Response
{
  "data": {
    "id": "00000000-0000-4000-8000-00000000e001",
    "scope": "example-scope",
    "status": "proposed",
    "documentId": "00000000-0000-4000-8000-00000000e001",
    "documentVersion": "example-document-version",
    "sha256": "example-sha256",
    "acceptedAt": "2026-01-01T00:00:00.000Z",
    "withdrawnAt": "2026-01-01T00:00:00.000Z",
    "withdrawsConsentId": "00000000-0000-4000-8000-00000000e001",
    "withdrawn": false,
    "channel": "example-channel",
    "actorKind": "example-actor-kind",
    "locale": "example-locale",
    "patientAction": "example-patient-action",
    "text": "example-text",
    "labelText": "example-label-text"
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
503patient_access_unavailablePatient access is temporarily unavailable. Nothing was processed.
503temporarily_unavailableThe request could not be completed. Nothing was changed; retry.
404consent_not_foundNo consent with that id for this patient.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
POST/api/v1/patients/{patientId}/consents/{consentId}/withdrawconsents.write

Withdraw a consent

Records the withdrawal as an append-only successor record. The care team is told on every open case, and the program's consent hold answers consent_required until a new acceptance. Emits clinical.consent.withdrawn.

Requires an Idempotency-Key header of 16–255 characters. Replays for 24 hours; reusing a key with a different body returns 409 rather than applying the second request.

Request body

FieldTypeRequiredNotes
reasonstringno

Example response · 200

Response
{
  "data": {
    "id": "00000000-0000-4000-8000-00000000e001",
    "scope": "example-scope",
    "status": "proposed",
    "documentId": "00000000-0000-4000-8000-00000000e001",
    "documentVersion": "example-document-version",
    "sha256": "example-sha256",
    "acceptedAt": "2026-01-01T00:00:00.000Z",
    "withdrawnAt": "2026-01-01T00:00:00.000Z",
    "withdrawsConsentId": "00000000-0000-4000-8000-00000000e001",
    "withdrawn": false,
    "channel": "example-channel",
    "actorKind": "example-actor-kind",
    "locale": "example-locale",
    "patientAction": "example-patient-action",
    "text": "example-text",
    "labelText": "example-label-text"
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
503patient_access_unavailablePatient access is temporarily unavailable. Nothing was processed.
503temporarily_unavailableThe request could not be completed. Nothing was changed; retry.
404consent_not_foundNo consent with that id for this patient.
409consent_already_withdrawnThis consent is already withdrawn.
422consent_not_withdrawableOnly an active legal-kit consent can be withdrawn here.
413payload_too_largeThe request body exceeds 4096 bytes (4 KiB). Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API: the same status and code, with `request_id` `edge` and no `Request-Id` header.
400idempotency_key_requiredProvide a 16–255 character Idempotency-Key.
409idempotency_conflictThis Idempotency-Key was already used with different request parameters. Nothing was applied; use a new key.
409idempotency_in_progressA request with this Idempotency-Key is still being processed. Retry after `Retry-After`; nothing is applied twice.
503idempotency_unavailableThe idempotency record could not be checked. Nothing was processed; retry after `Retry-After`.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
GET/api/v1/patients/{patientId}/intake-drafts/{programHandle}intake.write

The patient's saved intake draft for a program

The saved answers as an in-progress QuestionnaireResponse, with the revision to send back. Audited.

Example response · 200

Response
{
  "data": {
    "draftId": "00000000-0000-4000-8000-00000000e001",
    "programHandle": "example-program-handle",
    "questionnaireVersionId": "00000000-0000-4000-8000-00000000e001",
    "currentQuestionnaireVersionId": "00000000-0000-4000-8000-00000000e001",
    "state": "example-state",
    "questionnaireResponse": {
      "resourceType": "QuestionnaireResponse",
      "questionnaire": "example-questionnaire",
      "status": "in-progress",
      "item": [
        null
      ]
    },
    "currentStep": "example-current-step",
    "revision": "example-revision",
    "expiresAt": "2026-01-01T00:00:00.000Z"
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
503patient_access_unavailablePatient access is temporarily unavailable. Nothing was processed.
503temporarily_unavailableThe request could not be completed. Nothing was changed; retry.
404draft_not_foundNo saved intake draft for this program.
404program_not_foundNo program with that handle in this brand.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
PUT/api/v1/patients/{patientId}/intake-drafts/{programHandle}intake.write

Save the patient's intake draft

Saves the in-progress answers (30-day expiry) against the current questionnaire version, for the program the path names. Send the revision you read as expectedRevision; a stale one answers 409 `conflict` and the saved draft is unchanged. A draft on an older version must be restarted first. On real data the patient needs an accepted referral or enrollment invitation for that program, as submission does, and is refused the same 422 `enrollment_required` without one.

Request body

FieldTypeRequiredNotes
questionnaireVersionIdstringyes
state"AL" | "AK" | "AZ" | "AR" | "CA" | "CO" | "CT" | "DE" | "FL" | "GA" | "HI" | "ID" | "IL" | "IN" | "IA" | "KS" | "KY" | "LA" | "ME" | "MD" | "MA" | "MI" | "MN" | "MS" | "MO" | "MT" | "NE" | "NV" | "NH" | "NJ" | "NM" | "NY" | "NC" | "ND" | "OH" | "OK" | "OR" | "PA" | "RI" | "SC" | "SD" | "TN" | "TX" | "UT" | "VT" | "VA" | "WA" | "WV" | "WI" | "WY" | "DC"yesA two-letter USPS state code or DC.
questionnaireResponseobjectyes
expectedRevisionstring | nullnoThe revision you read; omit for a new draft.
currentStepintegerno

Example response · 200

Response
{
  "data": {
    "draftId": "00000000-0000-4000-8000-00000000e001",
    "revision": "example-revision",
    "savedAt": "2026-01-01T00:00:00.000Z"
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
503patient_access_unavailablePatient access is temporarily unavailable. Nothing was processed.
503temporarily_unavailableThe request could not be completed. Nothing was changed; retry.
422validation_errorThe request body or parameters were invalid.
409conflictThe draft changed or uses older questions; read it again (or restart).
404program_not_foundNo program with that handle in this brand.
422intake_unavailableIntake is not available for this brand yet.
422enrollment_requiredOn real data the patient holds no accepted referral or enrollment invitation for this program (and, at submission, the declared state). Enroll the patient in the program first; drafts and submissions answer the same code.
403not_permittedThe patient's session cannot hold a draft for this program.
403patient_admission_closedThis deployment is not admitting patients or taking patient data yet (a real-data deployment before its real-PHI launch, or without a loaded launch authorization). Nothing was processed and the Idempotency-Key is not spent. Never answered on the sandbox.
413payload_too_largeThe request body exceeds 131072 bytes (128 KiB). Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API: the same status and code, with `request_id` `edge` and no `Request-Id` header.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
POST/api/v1/patients/{patientId}/intake-drafts/{programHandle}/restartintake.write

Restart a draft on the current questionnaire

Keeps the previous answers as read-only history and starts the draft again on the current questionnaire version. Idempotent by Idempotency-Key: a retry with the same key and body answers the first restart again (`wasDuplicate` true, the draft as it is now) and restarts nothing twice. The program binds as for saveIntakeDraft (422 `enrollment_required` on real data without an accepted invitation for it).

Requires an Idempotency-Key header of 16–255 characters. Replays for 24 hours; reusing a key with a different body returns 409 rather than applying the second request.

Request body

FieldTypeRequiredNotes
state"AL" | "AK" | "AZ" | "AR" | "CA" | "CO" | "CT" | "DE" | "FL" | "GA" | "HI" | "ID" | "IL" | "IN" | "IA" | "KS" | "KY" | "LA" | "ME" | "MD" | "MA" | "MI" | "MN" | "MS" | "MO" | "MT" | "NE" | "NV" | "NH" | "NJ" | "NM" | "NY" | "NC" | "ND" | "OH" | "OK" | "OR" | "PA" | "RI" | "SC" | "SD" | "TN" | "TX" | "UT" | "VT" | "VA" | "WA" | "WV" | "WI" | "WY" | "DC"yesA two-letter USPS state code or DC.
expectedRevisionstringyes

Example response · 200

Response
{
  "data": {
    "draftId": "00000000-0000-4000-8000-00000000e001",
    "revision": "example-revision",
    "questionnaireVersionId": "00000000-0000-4000-8000-00000000e001",
    "state": "example-state",
    "wasDuplicate": false
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
503patient_access_unavailablePatient access is temporarily unavailable. Nothing was processed.
503temporarily_unavailableThe request could not be completed. Nothing was changed; retry.
404draft_not_foundNo saved intake draft for this program.
409draft_revision_conflictThe saved draft changed; read it again.
409draft_already_currentThe saved draft already uses the current questions.
409conflictThe draft changed while it was restarted, or the questions changed again; read it again.
422validation_errorThe request body or parameters were invalid.
404program_not_foundNo program with that handle in this brand.
422intake_unavailableIntake is not available for this brand yet.
422enrollment_requiredOn real data the patient holds no accepted referral or enrollment invitation for this program (and, at submission, the declared state). Enroll the patient in the program first; drafts and submissions answer the same code.
403not_permittedThe patient's session cannot hold a draft for this program.
403patient_admission_closedThis deployment is not admitting patients or taking patient data yet (a real-data deployment before its real-PHI launch, or without a loaded launch authorization). Nothing was processed and the Idempotency-Key is not spent. Never answered on the sandbox.
413payload_too_largeThe request body exceeds 4096 bytes (4 KiB). Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API: the same status and code, with `request_id` `edge` and no `Request-Id` header.
400idempotency_key_requiredProvide a 16–255 character Idempotency-Key.
409idempotency_conflictThis Idempotency-Key was already used with different request parameters. Nothing was applied; use a new key.
409idempotency_in_progressA request with this Idempotency-Key is still being processed. Retry after `Retry-After`; nothing is applied twice.
503idempotency_unavailableThe idempotency record could not be checked. Nothing was processed; retry after `Retry-After`.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
POST/api/v1/patients/{patientId}/intake/evaluateintake.write

Evaluate answers: stop rules and an eligibility preview

Runs the questionnaire's stop rules over the answers so far and previews eligibility (coverage, modality, an open review, the consent hold). Only a stop is persisted (no answers); a stop clears the saved draft. Identity does not block submission: an unverified patient's case waits before the clinician queue.

Request body

FieldTypeRequiredNotes
programHandlestringyesThe program's opaque public handle (`prg_` and 24 hex characters). Link the care host's Start page with it: `/start?p=<handle>`. It never changes and carries no program or condition word.
state"AL" | "AK" | "AZ" | "AR" | "CA" | "CO" | "CT" | "DE" | "FL" | "GA" | "HI" | "ID" | "IL" | "IN" | "IA" | "KS" | "KY" | "LA" | "ME" | "MD" | "MA" | "MI" | "MN" | "MS" | "MO" | "MT" | "NE" | "NV" | "NH" | "NJ" | "NM" | "NY" | "NC" | "ND" | "OH" | "OK" | "OR" | "PA" | "RI" | "SC" | "SD" | "TN" | "TX" | "UT" | "VT" | "VA" | "WA" | "WV" | "WI" | "WY" | "DC"yesA two-letter USPS state code or DC.
questionnaireResponseobjectyes

Example response · 200

Response
{
  "data": {
    "outcome": "continue",
    "eligible": false,
    "reasonCode": "example-reason-code",
    "ruleCode": "example-rule-code",
    "patientMessage": "example-patient-message",
    "requiredModality": "async",
    "flags": [
      "example-flags"
    ],
    "consentHold": "example-consent-hold",
    "identityRequiredBeforeReview": false
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
503patient_access_unavailablePatient access is temporarily unavailable. Nothing was processed.
503temporarily_unavailableThe request could not be completed. Nothing was changed; retry.
422validation_errorThe request body or parameters were invalid.
409questionnaire_changedThe questionnaire changed; fetch the current version.
403patient_admission_closedThis deployment is not admitting patients or taking patient data yet (a real-data deployment before its real-PHI launch, or without a loaded launch authorization). Nothing was processed and the Idempotency-Key is not spent. Never answered on the sandbox.
413payload_too_largeThe request body exceeds 131072 bytes (128 KiB). Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API: the same status and code, with `request_id` `edge` and no `Request-Id` header.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
POST/api/v1/patients/{patientId}/intake-submissionsintake.write

Submit the intake

Submits the completed QuestionnaireResponse through Ambition's intake kernel after checking the consent hold (every required scope accepted on its current text), the questionnaire content hash, the declared location and the intake attestation. The case is created and routed to the physician group. When the patient's identity is not yet verified the case waits before the clinician queue (`caseStatus: awaiting_identity`, outstanding action identity_required) and closes after 14 days. Refusals are 422 with one of consent_required, reconsent_required, hosted_consent_required, location_mismatch, stopped, not_eligible, open_review_exists, modality_unavailable_headless, clinician_unavailable, enrollment_required, answers_incomplete or attestation_required. On real data `enrollment_required` means the patient holds no accepted referral or enrollment invitation for the program and state, the same code a draft answers for its program. Emits clinical.intake.submitted (and clinical.intake.identity_required).

Requires an Idempotency-Key header of 16–255 characters. Replays for 24 hours; reusing a key with a different body returns 409 rather than applying the second request.

Request body

FieldTypeRequiredNotes
enrollmentIdstringno
programHandlestringyesThe program's opaque public handle (`prg_` and 24 hex characters). Link the care host's Start page with it: `/start?p=<handle>`. It never changes and carries no program or condition word.
questionnaireResponseobjectyesA FHIR R4 QuestionnaireResponse: one answer per linkId, groups allowed.
contentHashstringyesThe Questionnaire's `version` you rendered.
consentIdsarrayyesThe legal-kit consents this submission relies on.
declaredLocationobjectyes
attestationobjectyesThe intake attestation from the questionnaire's `intake-attestation` extension, shown verbatim and accepted.
shippingAddressobjectno

Example response · 201

Response
{
  "data": {
    "submissionId": "00000000-0000-4000-8000-00000000e001",
    "caseId": "00000000-0000-4000-8000-00000000e001",
    "programHandle": "example-program-handle",
    "questionnaireVersionId": "00000000-0000-4000-8000-00000000e001",
    "contentHash": "example-content-hash",
    "consentIds": [
      "00000000-0000-4000-8000-00000000e001"
    ],
    "declaredLocation": {
      "state": "example-state",
      "attestedAt": "2026-01-01T00:00:00.000Z"
    },
    "status": "in_review",
    "caseStatus": "awaiting_identity",
    "outstandingActions": [
      {
        "type": "identity_required",
        "requestedAt": "2026-01-01T00:00:00.000Z",
        "expiresAt": "2026-01-01T00:00:00.000Z"
      }
    ],
    "submittedAt": "2026-01-01T00:00:00.000Z",
    "wasDuplicate": false
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
503patient_access_unavailablePatient access is temporarily unavailable. Nothing was processed.
503temporarily_unavailableThe request could not be completed. Nothing was changed; retry.
422validation_errorThe request body or parameters were invalid.
409questionnaire_changedThe questionnaire or its content hash changed.
422consent_requiredA required consent has not been accepted on its current text.
422stoppedThe answers stop this pathway.
422not_eligibleThe program is not available to this patient in that state.
422open_review_existsAn open review for this program already exists.
422modality_unavailable_headlessThe state requires a video visit (launch week).
422location_mismatchThe declared location is stale or not the enrollment's state.
422enrollment_requiredOn real data the patient holds no accepted referral or enrollment invitation for this program (and, at submission, the declared state). Enroll the patient in the program first; drafts and submissions answer the same code.
409idempotency_key_reusedThat Idempotency-Key was already used and its first submission cannot be read again; use a new key.
403billing_suspendedBilling mode B: new intake is paused until the brand's platform invoice is settled (Finances, Platform billing). Not retryable.
403patient_admission_closedThis deployment is not admitting patients or taking patient data yet (a real-data deployment before its real-PHI launch, or without a loaded launch authorization). Nothing was processed and the Idempotency-Key is not spent. Never answered on the sandbox.
413payload_too_largeThe request body exceeds 196608 bytes (192 KiB). Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API: the same status and code, with `request_id` `edge` and no `Request-Id` header.
400idempotency_key_requiredProvide a 16–255 character Idempotency-Key.
409idempotency_conflictThis Idempotency-Key was already used with different request parameters. Nothing was applied; use a new key.
409idempotency_in_progressA request with this Idempotency-Key is still being processed. Retry after `Retry-After`; nothing is applied twice.
503idempotency_unavailableThe idempotency record could not be checked. Nothing was processed; retry after `Retry-After`.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
GET/api/v1/patients/{patientId}/intake-submissions/{submissionId}cases.read

One intake submission and its case status

The submission's case, its coarse status and outstanding actions (identity_required while it waits). Never the answers. Needs your BAA with the physician group covering the case. Audited.

Example response · 200

Response
{
  "data": {
    "submissionId": "00000000-0000-4000-8000-00000000e001",
    "caseId": "00000000-0000-4000-8000-00000000e001",
    "programHandle": "example-program-handle",
    "questionnaireVersionId": "00000000-0000-4000-8000-00000000e001",
    "contentHash": "example-content-hash",
    "consentIds": [
      "00000000-0000-4000-8000-00000000e001"
    ],
    "declaredLocation": {
      "state": "example-state",
      "attestedAt": "2026-01-01T00:00:00.000Z"
    },
    "status": "in_review",
    "caseStatus": "awaiting_identity",
    "outstandingActions": [
      {
        "type": "identity_required",
        "requestedAt": "2026-01-01T00:00:00.000Z",
        "expiresAt": "2026-01-01T00:00:00.000Z"
      }
    ],
    "submittedAt": "2026-01-01T00:00:00.000Z",
    "wasDuplicate": false
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
503patient_access_unavailablePatient access is temporarily unavailable. Nothing was processed.
503temporarily_unavailableThe request could not be completed. Nothing was changed; retry.
403entity_baa_missingYour BAA does not cover the physician group responsible for this case.
404submission_not_foundNo submission with that id for this patient.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
POST/api/v1/patients/{patientId}/safety-reportssafety_reports.write

Report a side effect or a product problem

Files the patient's report after you showed the current safety screen (echo its version). A serious answer makes it urgent for the clinical safety team; a report on a compounded or interim-policy product is routed to the prescriber, the dispensing pharmacy and the MedWatch path. Emits clinical.safety_report.received.

Requires an Idempotency-Key header of 16–255 characters. Replays for 24 hours; reusing a key with a different body returns 409 rather than applying the second request.

Request body

FieldTypeRequiredNotes
category"adverse_event" | "product_complaint"yes
descriptionstringyes
onsetAtstringnoThe date the problem started (within five years).
prescriptionIdstringno
orderIdstringno
seriousIndicatorsarrayyesThe seriousness questions the patient answered yes.
unsureIndicatorsarraynoThe seriousness questions the patient answered unsure.
emergencyScreenVersionstringyesThe `version` of the safety screen you showed.
productNamestringno
lotNumberstringno

Example response · 201

Response
{
  "data": {
    "id": "00000000-0000-4000-8000-00000000e001",
    "reference": "example-reference",
    "status": "example-status",
    "seriousness": "example-seriousness",
    "caseId": "00000000-0000-4000-8000-00000000e001",
    "category": "example-category",
    "routedToPhysicianGroup": false,
    "wasDuplicate": false
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
503patient_access_unavailablePatient access is temporarily unavailable. Nothing was processed.
503temporarily_unavailableThe request could not be completed. Nothing was changed; retry.
422validation_errorThe request body or parameters were invalid.
404prescription_not_foundNo prescription with that id for this patient.
404order_not_foundNo order with that id for this patient.
422emergency_screen_outdatedShow the current safety screen and echo its version.
413payload_too_largeThe request body exceeds 16384 bytes (16 KiB). Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API: the same status and code, with `request_id` `edge` and no `Request-Id` header.
400idempotency_key_requiredProvide a 16–255 character Idempotency-Key.
409idempotency_conflictThis Idempotency-Key was already used with different request parameters. Nothing was applied; use a new key.
409idempotency_in_progressA request with this Idempotency-Key is still being processed. Retry after `Retry-After`; nothing is applied twice.
503idempotency_unavailableThe idempotency record could not be checked. Nothing was processed; retry after `Retry-After`.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
GET/api/v1/patients/{patientId}/safety-reportscases.read

The patient's safety reports

Reference, category, status and date of each report. Never the assessment or decision. Audited.

Example response · 200

Response
{
  "data": [
    {
      "id": "00000000-0000-4000-8000-00000000e001",
      "reference": "example-reference",
      "category": "example-category",
      "status": "example-status",
      "submittedAt": "2026-01-01T00:00:00.000Z"
    }
  ],
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
503patient_access_unavailablePatient access is temporarily unavailable. Nothing was processed.
503temporarily_unavailableThe request could not be completed. Nothing was changed; retry.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
POST/api/v1/patients/{patientId}/prescriptions/{prescriptionId}/fillsfills.write

Request this cycle's fill of a prescription

Call once per billing cycle of your subscription, after you charge the patient. Ambition checks the prescription's clinical currency (approval expiry, refills left, check-ins and monitoring labs due), the declared location, regulatory standing and your fulfillment release, then either creates the refill's pharmacy order (`status: requested`; `clinical.fill.requested`, then `clinical.fill.transmitted` when the pharmacy has it) or holds the fill with one coarse `holdReason` (`clinical.fill.held`). Idempotent per `cycleRef`: the same cycleRef answers the same fill, and repeating it re-evaluates a held fill, so retry a hold with the same cycleRef once it clears. A new cycleRef supersedes an earlier held fill. The first dispense of a new prescription is its own order (sent on signing, or on your release under brand_release); call this for each later cycle. To pause or cancel, do not request a fill; to stop therapy, send a renewal request with `requestedChange: stop` or a message to the care team. When the last authorized refill is used, `clinical.prescription.refills_exhausted` tells you to offer a renewal.

Requires an Idempotency-Key header of 16–255 characters. Replays for 24 hours; reusing a key with a different body returns 409 rather than applying the second request.

Request body

FieldTypeRequiredNotes
cycleRefstringyesYour own reference for the billing cycle this fill serves (1-80 letters, digits and `._:-`; never PHI). The same cycleRef always answers the same fill: repeat it to retry a held fill once the hold clears.
declaredLocationobjectyesThe patient's own statement of where they are, captured by your UI for this request.
shippingAddressobjectnoWhere this fill ships, when it differs from the address on the prescription. It must be in the declared state. It is sent to the pharmacy with this fill only.

Example response · 201

Response
{
  "data": {
    "id": "00000000-0000-4000-8000-00000000e001",
    "prescriptionId": "00000000-0000-4000-8000-00000000e001",
    "cycleRef": "example-cycle-ref",
    "status": "held",
    "holdReason": "prescription_expired",
    "fillNumber": 1,
    "orderId": "example_orderId",
    "declaredState": "AL",
    "requestedAt": "2026-01-01T00:00:00.000Z",
    "evaluatedAt": "2026-01-01T00:00:00.000Z",
    "refillsRemaining": 1
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment",
    "replayed": false
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
404prescription_not_foundNo prescription with that id belongs to this patient.
403entity_baa_missingYour BAA does not cover the physician group responsible for this prescription's case.
400idempotency_key_requiredProvide a 16 to 255 character Idempotency-Key.
400invalid_jsonThe body is not valid JSON.
422validation_errorThe body does not match the contract.
409idempotency_in_progressA request with this Idempotency-Key is still being processed. Retry after `Retry-After`.
409idempotency_conflictThis Idempotency-Key was used with a different body.
409prescription_not_activeThe prescription is cancelled, superseded or was never signed.
422location_mismatchThe shipping address is not in the declared state.
503fill_unavailableThe fill could not be decided. Nothing was changed; retry with the same cycleRef.
403patient_admission_closedThis deployment is not admitting patients or taking patient data yet (a real-data deployment before its real-PHI launch, or without a loaded launch authorization). Nothing was processed and the Idempotency-Key is not spent. Never answered on the sandbox.
413payload_too_largeThe request body exceeds 4096 bytes (4 KiB). Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API: the same status and code, with `request_id` `edge` and no `Request-Id` header.
503idempotency_unavailableThe idempotency record could not be checked. Nothing was processed; retry after `Retry-After`.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
GET/api/v1/patients/{patientId}/prescriptions/{prescriptionId}/fillsfulfillment.read

List a prescription's fills

The fills you requested for one of the patient's prescriptions, newest first: status, coarse hold reason, fill number and order id. No address, product or clinical field.

Example response · 200

Response
{
  "data": [
    {
      "id": "00000000-0000-4000-8000-00000000e001",
      "prescriptionId": "00000000-0000-4000-8000-00000000e001",
      "cycleRef": "example-cycle-ref",
      "status": "held",
      "holdReason": "prescription_expired",
      "fillNumber": 1,
      "orderId": "example_orderId",
      "declaredState": "AL",
      "requestedAt": "2026-01-01T00:00:00.000Z",
      "evaluatedAt": "2026-01-01T00:00:00.000Z",
      "transmittedAt": "2026-01-01T00:00:00.000Z",
      "closedAt": "2026-01-01T00:00:00.000Z"
    }
  ],
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
404prescription_not_foundNo prescription with that id belongs to this patient.
403entity_baa_missingYour BAA does not cover the physician group responsible for this prescription's case.
503fill_unavailableThe fills could not be read; retry.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
GET/api/v1/patients/{patientId}/prescriptions/{prescriptionId}/fills/{fillId}fulfillment.read

Read one fill

One fill of one of the patient's prescriptions.

Example response · 200

Response
{
  "data": {
    "id": "00000000-0000-4000-8000-00000000e001",
    "prescriptionId": "00000000-0000-4000-8000-00000000e001",
    "cycleRef": "example-cycle-ref",
    "status": "held",
    "holdReason": "prescription_expired",
    "fillNumber": 1,
    "orderId": "example_orderId",
    "declaredState": "AL",
    "requestedAt": "2026-01-01T00:00:00.000Z",
    "evaluatedAt": "2026-01-01T00:00:00.000Z",
    "transmittedAt": "2026-01-01T00:00:00.000Z",
    "closedAt": "2026-01-01T00:00:00.000Z"
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
404prescription_not_foundNo prescription with that id belongs to this patient.
403entity_baa_missingYour BAA does not cover the physician group responsible for this prescription's case.
404fill_not_foundNo fill with that id on this prescription.
503fill_unavailableThe fill could not be read; retry.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
GET/api/v1/patients/{patientId}/prescriptions/{prescriptionId}/renewal-eligibilitycases.read

Whether a prescription can be renewed now

Whether you may request a renewal now, and if not why; when the approval or the authorized supply runs out and when a renewal opens (the physician group's lead time); refills left; and the program's current check-in questionnaire to render with the renewal request.

Example response · 200

Response
{
  "data": {
    "prescriptionId": "00000000-0000-4000-8000-00000000e001",
    "eligible": false,
    "reasonCode": "prescription_not_active",
    "dueAt": "2026-01-01T00:00:00.000Z",
    "eligibleAt": "2026-01-01T00:00:00.000Z",
    "expiresAt": "2026-01-01T00:00:00.000Z",
    "refillsRemaining": 1,
    "checkInQuestionnaireVersionId": "00000000-0000-4000-8000-00000000e001"
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
404prescription_not_foundNo prescription with that id belongs to this patient.
403entity_baa_missingYour BAA does not cover the physician group responsible for this prescription's case.
503renewal_unavailableEligibility could not be read; retry.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
POST/api/v1/patients/{patientId}/renewal-requestsrenewals.write

Request a renewal for the clinician to review

Opens a renewal-kind case for the prescription's physician group (its prescriber while they may still take it) with the patient's check-in answers and the change they ask for. The change is a request only: the clinician decides, writes any new prescription, and you follow the case through the cases read and clinical.case.* events (`clinical.renewal.requested` now). The answers also complete the therapy's check-in. Refused while any review of the program is open. Needs the patient's current verified identity and consent, the state the therapy was approved in, and an asynchronous modality (headless serves async only until video lands). Consent is checked from the evidence on record; there is no consent field.

Requires an Idempotency-Key header of 16–255 characters. Replays for 24 hours; reusing a key with a different body returns 409 rather than applying the second request.

Request body

FieldTypeRequiredNotes
prescriptionIdstringyesThe prescription's id (from the prescriptions read).
questionnaireResponseobjectyes
requestedChange"continue" | "escalate" | "decrease" | "stop"yesWhat the patient asks for. A request only: the clinician decides the dose and whether to continue.
declaredLocationobjectyesThe patient's own statement of where they are, captured by your UI for this request.

Example response · 201

Response
{
  "data": {
    "id": "00000000-0000-4000-8000-00000000e001",
    "caseId": "00000000-0000-4000-8000-00000000e001",
    "kind": "renewal",
    "prescriptionId": "00000000-0000-4000-8000-00000000e001",
    "requestedChange": "continue",
    "checkInId": "00000000-0000-4000-8000-00000000e001",
    "createdAt": "2026-01-01T00:00:00.000Z"
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment",
    "replayed": false
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
404prescription_not_foundNo prescription with that id belongs to this patient.
403entity_baa_missingYour BAA does not cover the physician group responsible for this prescription's case.
400idempotency_key_requiredProvide a 16 to 255 character Idempotency-Key.
400invalid_jsonThe body is not valid JSON.
422validation_errorThe body does not match the contract.
409idempotency_in_progressA request with this Idempotency-Key is still being processed. Retry after `Retry-After`.
409idempotency_conflictThis Idempotency-Key was used with a different body.
409prescription_not_activeThe prescription is not an active therapy.
409prescription_renewedThis prescription has already been renewed.
409open_review_existsA review of this program is already open for the patient.
409identity_requiredThe patient's identity verification is not current.
409consent_requiredA consent the program requires is missing, out of date, or must be captured hosted.
409clinician_unavailableNo clinician of the prescription's physician group can take it now.
422location_mismatchThe declared location is not the state the therapy was approved in.
422questionnaire_not_foundNot this program's published check-in questionnaire.
422answers_invalidAn answer names no item of the questionnaire, or is not a value.
422answers_incompleteA required item is unanswered.
422modality_unavailable_headlessThis follow-up needs a synchronous visit, which headless care does not serve yet.
503follow_up_unavailableThe request could not be recorded. Nothing was changed; retry.
403patient_admission_closedThis deployment is not admitting patients or taking patient data yet (a real-data deployment before its real-PHI launch, or without a loaded launch authorization). Nothing was processed and the Idempotency-Key is not spent. Never answered on the sandbox.
413payload_too_largeThe request body exceeds 49152 bytes (48 KiB). Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API: the same status and code, with `request_id` `edge` and no `Request-Id` header.
503idempotency_unavailableThe idempotency record could not be checked. Nothing was processed; retry after `Retry-After`.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
GET/api/v1/patients/{patientId}/check-inscases.read

List the patient's check-ins

Due, overdue and submitted check-ins of the patient's therapies under the physician group's monitoring cadence, and the next one each active therapy needs (`id` null until it is scheduled). An overdue check-in holds fills (`checkin_due`). `clinical.checkin.due` fires when one enters its notice period. Only therapies whose case your BAA covers are listed.

Query parameters

FieldTypeRequiredNotes
prescriptionIdstringnoOnly this therapy's check-ins.

Example response · 200

Response
{
  "data": [
    {
      "id": "00000000-0000-4000-8000-00000000e001",
      "prescriptionId": "00000000-0000-4000-8000-00000000e001",
      "status": "upcoming",
      "dueAt": "2026-01-01T00:00:00.000Z",
      "submittedAt": "2026-01-01T00:00:00.000Z",
      "reviewCaseId": "00000000-0000-4000-8000-00000000e001"
    }
  ],
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
404prescription_not_foundNo prescription with that id belongs to this patient.
403entity_baa_missingYour BAA does not cover the physician group responsible for this prescription's case.
422validation_errorQuery parameters were invalid.
503check_in_unavailableCheck-ins could not be read; retry.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
POST/api/v1/patients/{patientId}/check-insrenewals.write

Submit a check-in

Records the patient's answers to the program's check-in questionnaire for one active therapy and opens a check_in-kind case for the clinician. Answers the program's protocol rules flag hold fills (`checkin_under_review`) until the clinician decides; otherwise fills continue. Refused while any review of the program is open (a renewal request also counts as the check-in).

Requires an Idempotency-Key header of 16–255 characters. Replays for 24 hours; reusing a key with a different body returns 409 rather than applying the second request.

Request body

FieldTypeRequiredNotes
prescriptionIdstringyesThe prescription's id (from the prescriptions read).
questionnaireResponseobjectyes
declaredLocationobjectyesThe patient's own statement of where they are, captured by your UI for this request.

Example response · 201

Response
{
  "data": {
    "id": "00000000-0000-4000-8000-00000000e001",
    "prescriptionId": "00000000-0000-4000-8000-00000000e001",
    "caseId": "00000000-0000-4000-8000-00000000e001",
    "status": "submitted",
    "dueAt": "2026-01-01T00:00:00.000Z",
    "submittedAt": "2026-01-01T00:00:00.000Z"
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment",
    "replayed": false
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
404prescription_not_foundNo prescription with that id belongs to this patient.
403entity_baa_missingYour BAA does not cover the physician group responsible for this prescription's case.
400idempotency_key_requiredProvide a 16 to 255 character Idempotency-Key.
400invalid_jsonThe body is not valid JSON.
422validation_errorThe body does not match the contract.
409idempotency_in_progressA request with this Idempotency-Key is still being processed. Retry after `Retry-After`.
409idempotency_conflictThis Idempotency-Key was used with a different body.
409prescription_not_activeThe prescription is not an active therapy.
409prescription_renewedThis prescription has already been renewed.
409open_review_existsA review of this program is already open for the patient.
409identity_requiredThe patient's identity verification is not current.
409consent_requiredA consent the program requires is missing, out of date, or must be captured hosted.
409clinician_unavailableNo clinician of the prescription's physician group can take it now.
422location_mismatchThe declared location is not the state the therapy was approved in.
422questionnaire_not_foundNot this program's published check-in questionnaire.
422answers_invalidAn answer names no item of the questionnaire, or is not a value.
422answers_incompleteA required item is unanswered.
422modality_unavailable_headlessThis follow-up needs a synchronous visit, which headless care does not serve yet.
503follow_up_unavailableThe request could not be recorded. Nothing was changed; retry.
403patient_admission_closedThis deployment is not admitting patients or taking patient data yet (a real-data deployment before its real-PHI launch, or without a loaded launch authorization). Nothing was processed and the Idempotency-Key is not spent. Never answered on the sandbox.
413payload_too_largeThe request body exceeds 49152 bytes (48 KiB). Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API: the same status and code, with `request_id` `edge` and no `Request-Id` header.
503idempotency_unavailableThe idempotency record could not be checked. Nothing was processed; retry after `Retry-After`.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
GET/api/v1/patients/{patientId}/casescases.read

List the patient's cases

The patient's clinical reviews, newest first: coarse stage and who acts next, the assigned clinician's required disclosure (name, credential, licence state; bio only when they opted in), outstanding actions with the clinician's patient-facing prompts, the decision and its patient-facing note, and the prescription and order ids. Only cases whose physician group your BAA covers are listed. Follow changes with clinical.case.* events.

Example response · 200

Response
{
  "data": [
    {
      "id": "00000000-0000-4000-8000-00000000e001",
      "reference": "example-reference",
      "kind": "initial",
      "stage": "awaiting_identity",
      "patientStatus": "with_care_team",
      "programKey": "example-program-key",
      "state": "example-state",
      "requiredModality": "async",
      "clinician": {
        "displayName": "example-display-name",
        "credential": "example-credential",
        "licenseState": "example-license-state",
        "bio": "example-bio",
        "photoUrl": "example-photo-url"
      },
      "outstandingActions": [
        {
          "type": "more_info",
          "prompt": "example-prompt",
          "requestedAt": "2026-01-01T00:00:00.000Z"
        }
      ],
      "decision": {
        "outcome": "approved",
        "patientFacingNote": "example-patient-facing-note",
        "decidedAt": "2026-01-01T00:00:00.000Z",
        "refundExpected": false
      },
      "prescriptionIds": [
        "00000000-0000-4000-8000-00000000e001"
      ],
      "orderIds": [
        "example-order-ids"
      ],
      "createdAt": "2026-01-01T00:00:00.000Z",
      "updatedAt": "2026-01-01T00:00:00.000Z"
    }
  ],
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
403entity_baa_missingYour BAA does not cover the physician group responsible for this case.
503case_unavailableCould not be completed. Nothing was changed; retry.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
GET/api/v1/patients/{patientId}/cases/{caseId}cases.read

Read one case

One of the patient's cases, as in the list. The BAA must cover the case's physician group.

Example response · 200

Response
{
  "data": {
    "id": "00000000-0000-4000-8000-00000000e001",
    "reference": "example-reference",
    "kind": "initial",
    "stage": "awaiting_identity",
    "patientStatus": "with_care_team",
    "programKey": "example-program-key",
    "state": "example-state",
    "requiredModality": "async",
    "clinician": {
      "displayName": "example-display-name",
      "credential": "example-credential",
      "licenseState": "example-license-state",
      "bio": "example-bio",
      "photoUrl": "example-photo-url"
    },
    "outstandingActions": [
      {
        "type": "more_info",
        "prompt": "example-prompt",
        "requestedAt": "2026-01-01T00:00:00.000Z"
      }
    ],
    "decision": {
      "outcome": "approved",
      "patientFacingNote": "example-patient-facing-note",
      "decidedAt": "2026-01-01T00:00:00.000Z",
      "refundExpected": false
    },
    "prescriptionIds": [
      "00000000-0000-4000-8000-00000000e001"
    ],
    "orderIds": [
      "example-order-ids"
    ],
    "createdAt": "2026-01-01T00:00:00.000Z",
    "updatedAt": "2026-01-01T00:00:00.000Z"
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment",
    "replayed": false
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
403entity_baa_missingYour BAA does not cover the physician group responsible for this case.
404case_not_foundNo case with that id belongs to this patient.
503case_unavailableCould not be completed. Nothing was changed; retry.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
POST/api/v1/patients/{patientId}/cases/{caseId}/more-info-responsescases.write

Answer the clinician's request for more information

Posts the patient's answer on the case's care-team thread and returns the case to the clinician's review (`clinical.case.info_received`). Only while the case's stage is `needs_info`. Send the patient's own words; content your AI wrote must carry Ambition-Content-Source: automation and is badged for the clinician.

Requires an Idempotency-Key header of 16–255 characters. Replays for 24 hours; reusing a key with a different body returns 409 rather than applying the second request.

Request body

FieldTypeRequiredNotes
messagestringnoA free-text answer.
answersarraynoStructured answers, one per question.

Example response · 201

Response
{
  "data": {
    "caseId": "00000000-0000-4000-8000-00000000e001",
    "threadId": "00000000-0000-4000-8000-00000000e001",
    "messageId": "00000000-0000-4000-8000-00000000e001",
    "stage": "awaiting_identity"
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment",
    "replayed": false
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
403entity_baa_missingYour BAA does not cover the physician group responsible for this case.
400idempotency_key_requiredProvide a 16 to 255 character Idempotency-Key.
400invalid_jsonThe body is not valid JSON.
422validation_errorThe body does not match the contract.
409idempotency_in_progressA request with this Idempotency-Key is still being processed.
409idempotency_conflictThis Idempotency-Key was used with a different body.
404case_not_foundNo case with that id belongs to this patient.
409case_not_awaiting_infoThe clinician is not waiting for information on this case.
409clinician_not_assignedNo clinician is assigned to the case.
503case_unavailableCould not be completed. Nothing was changed; retry.
403patient_admission_closedThis deployment is not admitting patients or taking patient data yet (a real-data deployment before its real-PHI launch, or without a loaded launch authorization). Nothing was processed and the Idempotency-Key is not spent. Never answered on the sandbox.
413payload_too_largeThe request body exceeds 32768 bytes (32 KiB). Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API: the same status and code, with `request_id` `edge` and no `Request-Id` header.
503idempotency_unavailableThe idempotency record could not be checked. Nothing was processed; retry after `Retry-After`.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
POST/api/v1/patients/{patientId}/cases/{caseId}/cancelcases.write

Cancel a case before the clinician decides

Withdraws the patient's request before any decision (`clinical.case.cancelled`). Refused once a clinician has decided, and while a payment Ambition collected is still in progress. As merchant of record you refund under your own terms. Idempotent: cancelling a cancelled case answers it again.

Requires an Idempotency-Key header of 16–255 characters. Replays for 24 hours; reusing a key with a different body returns 409 rather than applying the second request.

Request body

FieldTypeRequiredNotes
reason"patient_request" | "duplicate" | "no_longer_needed" | "cost" | "other"yesWhy the patient withdraws the request. A code only; never PHI.

Example response · 200

Response
{
  "data": {
    "id": "00000000-0000-4000-8000-00000000e001",
    "reference": "example-reference",
    "kind": "initial",
    "stage": "awaiting_identity",
    "patientStatus": "with_care_team",
    "programKey": "example-program-key",
    "state": "example-state",
    "requiredModality": "async",
    "clinician": {
      "displayName": "example-display-name",
      "credential": "example-credential",
      "licenseState": "example-license-state",
      "bio": "example-bio",
      "photoUrl": "example-photo-url"
    },
    "outstandingActions": [
      {
        "type": "more_info",
        "prompt": "example-prompt",
        "requestedAt": "2026-01-01T00:00:00.000Z"
      }
    ],
    "decision": {
      "outcome": "approved",
      "patientFacingNote": "example-patient-facing-note",
      "decidedAt": "2026-01-01T00:00:00.000Z",
      "refundExpected": false
    },
    "prescriptionIds": [
      "00000000-0000-4000-8000-00000000e001"
    ],
    "orderIds": [
      "example-order-ids"
    ],
    "createdAt": "2026-01-01T00:00:00.000Z",
    "updatedAt": "2026-01-01T00:00:00.000Z"
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment",
    "replayed": false
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
403entity_baa_missingYour BAA does not cover the physician group responsible for this case.
400idempotency_key_requiredProvide a 16 to 255 character Idempotency-Key.
400invalid_jsonThe body is not valid JSON.
422validation_errorThe body does not match the contract.
409idempotency_in_progressA request with this Idempotency-Key is still being processed.
409idempotency_conflictThis Idempotency-Key was used with a different body.
404case_not_foundNo case with that id belongs to this patient.
409case_already_decidedThe clinician has decided; the case cannot be cancelled.
409case_payment_openA payment Ambition collected is in progress for this case.
503case_unavailableCould not be completed. Nothing was changed; retry.
413payload_too_largeThe request body exceeds 1024 bytes (1 KiB). Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API: the same status and code, with `request_id` `edge` and no `Request-Id` header.
503idempotency_unavailableThe idempotency record could not be checked. Nothing was processed; retry after `Retry-After`.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
GET/api/v1/patients/{patientId}/prescriptionsprescriptions.read

List the patient's prescriptions

Written prescriptions, newest first, minimum necessary: product, strength, form, route, directions, quantity, refills authorized and remaining, status and one coarse hold. Never the prescriber's DEA or NPI, the pharmacy, transport attempts or clarification text.

Example response · 200

Response
{
  "data": [
    {
      "id": "00000000-0000-4000-8000-00000000e001",
      "caseId": "00000000-0000-4000-8000-00000000e001",
      "product": "example-product",
      "strength": "example-strength",
      "form": "example-form",
      "route": "example-route",
      "patientDirections": "example-patient-directions",
      "quantity": 1,
      "quantityUnit": "example-quantity-unit",
      "daysSupply": 1,
      "refillsAuthorized": 1,
      "refillsRemaining": 1,
      "status": "active",
      "hold": "none",
      "writtenAt": "2026-01-01T00:00:00.000Z",
      "expiresAt": "2026-01-01T00:00:00.000Z",
      "renewalEligibleAt": "2026-01-01T00:00:00.000Z"
    }
  ],
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
403entity_baa_missingYour BAA does not cover the physician group responsible for this case.
503prescription_unavailableCould not be completed. Nothing was changed; retry.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
GET/api/v1/patients/{patientId}/prescriptions/{prescriptionId}prescriptions.read

Read one prescription

One of the patient's written prescriptions, as in the list.

Example response · 200

Response
{
  "data": {
    "id": "00000000-0000-4000-8000-00000000e001",
    "caseId": "00000000-0000-4000-8000-00000000e001",
    "product": "example-product",
    "strength": "example-strength",
    "form": "example-form",
    "route": "example-route",
    "patientDirections": "example-patient-directions",
    "quantity": 1,
    "quantityUnit": "example-quantity-unit",
    "daysSupply": 1,
    "refillsAuthorized": 1,
    "refillsRemaining": 1,
    "status": "active",
    "hold": "none",
    "writtenAt": "2026-01-01T00:00:00.000Z",
    "expiresAt": "2026-01-01T00:00:00.000Z",
    "renewalEligibleAt": "2026-01-01T00:00:00.000Z"
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
403entity_baa_missingYour BAA does not cover the physician group responsible for this case.
404prescription_not_foundNo written prescription with that id belongs to this patient.
503prescription_unavailableCould not be completed. Nothing was changed; retry.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
GET/api/v1/patients/{patientId}/ordersfulfillment.read

List the patient's pharmacy orders

Pharmacy orders of the patient's prescriptions, newest first: coarse status, exception code, tracking number, shipped and delivered times, items and, when the clinician cancelled the prescription while the pharmacy might hold the order, where that cancellation stands (`cancellation`). No pharmacy ids, partner references, costs, callback bodies or the pharmacy's reason for refusing a cancellation.

Example response · 200

Response
{
  "data": [
    {
      "id": "example_id",
      "prescriptionId": "00000000-0000-4000-8000-00000000e001",
      "caseId": "00000000-0000-4000-8000-00000000e001",
      "status": "created",
      "exception": "example-exception",
      "carrier": "example-carrier",
      "trackingNumber": "example-tracking-number",
      "trackingUrl": "example-tracking-url",
      "shippedAt": "2026-01-01T00:00:00.000Z",
      "deliveredAt": "2026-01-01T00:00:00.000Z",
      "items": [
        {
          "product": "example-product",
          "quantity": 1,
          "quantityUnit": "example-quantity-unit"
        }
      ],
      "cancellation": {
        "state": "requested",
        "requestedAt": "2026-01-01T00:00:00.000Z",
        "answeredAt": "2026-01-01T00:00:00.000Z"
      },
      "createdAt": "2026-01-01T00:00:00.000Z",
      "updatedAt": "2026-01-01T00:00:00.000Z"
    }
  ],
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
403entity_baa_missingYour BAA does not cover the physician group responsible for this case.
503order_unavailableCould not be completed. Nothing was changed; retry.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
GET/api/v1/patients/{patientId}/orders/{orderId}fulfillment.read

Read one pharmacy order

One of the patient's pharmacy orders, as in the list. After `clinical.prescription.cancelled` or `clinical.order.exception`, read `cancellation` here to learn whether the pharmacy still holds the order.

Example response · 200

Response
{
  "data": {
    "id": "example_id",
    "prescriptionId": "00000000-0000-4000-8000-00000000e001",
    "caseId": "00000000-0000-4000-8000-00000000e001",
    "status": "created",
    "exception": "example-exception",
    "carrier": "example-carrier",
    "trackingNumber": "example-tracking-number",
    "trackingUrl": "example-tracking-url",
    "shippedAt": "2026-01-01T00:00:00.000Z",
    "deliveredAt": "2026-01-01T00:00:00.000Z",
    "items": [
      {
        "product": "example-product",
        "quantity": 1,
        "quantityUnit": "example-quantity-unit"
      }
    ],
    "cancellation": {
      "state": "requested",
      "requestedAt": "2026-01-01T00:00:00.000Z",
      "answeredAt": "2026-01-01T00:00:00.000Z"
    },
    "createdAt": "2026-01-01T00:00:00.000Z",
    "updatedAt": "2026-01-01T00:00:00.000Z"
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
403entity_baa_missingYour BAA does not cover the physician group responsible for this case.
404order_not_foundNo order with that id belongs to this patient.
503order_unavailableCould not be completed. Nothing was changed; retry.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
GET/api/v1/patients/{patientId}/threadsmessages.read

List the patient's message threads

The care-team thread of each case your BAA covers, and the patient's support and billing conversations with Ambition care operations, most recent first, with unread care-team messages.

Example response · 200

Response
{
  "data": [
    {
      "id": "00000000-0000-4000-8000-00000000e001",
      "topic": "clinical",
      "caseId": "00000000-0000-4000-8000-00000000e001",
      "subject": "example-subject",
      "status": "open",
      "lastMessageAt": "2026-01-01T00:00:00.000Z",
      "unreadCount": 1,
      "createdAt": "2026-01-01T00:00:00.000Z"
    }
  ],
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
403entity_baa_missingYour BAA does not cover the physician group responsible for this case.
503message_unavailableCould not be completed. Nothing was changed; retry.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
POST/api/v1/patients/{patientId}/threadsmessages.write

Start a conversation

`clinical`: posts the patient's message to the clinician assigned to `caseId` (the case's care-team thread). `support`, `billing`: opens a request with Ambition care operations. `clinical.message.created` carries ids and the sender role only, never the body.

Requires an Idempotency-Key header of 16–255 characters. Replays for 24 hours; reusing a key with a different body returns 409 rather than applying the second request.

Request body

FieldTypeRequiredNotes
topic"clinical" | "support" | "billing"yes
caseIdstringnoRequired for `clinical`: the case whose clinician receives the message.
bodystringyesThe patient's first message (10 to 1500 characters for support and billing).

Example response · 201

Response
{
  "data": {
    "id": "00000000-0000-4000-8000-00000000e001",
    "topic": "clinical",
    "caseId": "00000000-0000-4000-8000-00000000e001",
    "messageId": "00000000-0000-4000-8000-00000000e001"
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment",
    "replayed": false
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
403entity_baa_missingYour BAA does not cover the physician group responsible for this case.
400idempotency_key_requiredProvide a 16 to 255 character Idempotency-Key.
400invalid_jsonThe body is not valid JSON.
422validation_errorThe body does not match the contract.
409idempotency_in_progressA request with this Idempotency-Key is still being processed.
409idempotency_conflictThis Idempotency-Key was used with a different body.
404case_not_foundNo case with that id belongs to this patient.
409clinician_not_assignedNo clinician is assigned to the case yet.
422case_requiredA clinical thread names its case.
503message_unavailableCould not be completed. Nothing was changed; retry.
403patient_admission_closedThis deployment is not admitting patients or taking patient data yet (a real-data deployment before its real-PHI launch, or without a loaded launch authorization). Nothing was processed and the Idempotency-Key is not spent. Never answered on the sandbox.
413payload_too_largeThe request body exceeds 24576 bytes (24 KiB). Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API: the same status and code, with `request_id` `edge` and no `Request-Id` header.
503idempotency_unavailableThe idempotency record could not be checked. Nothing was processed; retry after `Retry-After`.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
GET/api/v1/patients/{patientId}/threads/{threadId}/messagesmessages.read

Read a thread's messages

Messages of one thread, newest first, 50 per page (`limit` up to 100), with `meta.nextCursor` for older ones. Internal care-team notes are never included.

Query parameters

FieldTypeRequiredNotes
cursorstringno`meta.nextCursor` of the previous page.
limitintegernoDefault 50.

Example response · 200

Response
{
  "data": [
    {
      "id": "00000000-0000-4000-8000-00000000e001",
      "threadId": "00000000-0000-4000-8000-00000000e001",
      "senderRole": "patient",
      "contentSource": "patient",
      "body": "example-body",
      "createdAt": "2026-01-01T00:00:00.000Z",
      "readAt": "2026-01-01T00:00:00.000Z"
    }
  ],
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment",
    "nextCursor": "example-next-cursor"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
403entity_baa_missingYour BAA does not cover the physician group responsible for this case.
404thread_not_foundNo thread with that id belongs to this patient.
422validation_errorQuery parameters were invalid.
503message_unavailableCould not be completed. Nothing was changed; retry.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
POST/api/v1/patients/{patientId}/threads/{threadId}/messagesmessages.write

Send the patient's message

Posts the patient's message on an open thread. Attachments arrive with uploads (launch-week). Only the patient's side is ever written through the API; care-team replies are written by licensed clinicians.

Requires an Idempotency-Key header of 16–255 characters. Replays for 24 hours; reusing a key with a different body returns 409 rather than applying the second request.

Request body

FieldTypeRequiredNotes
bodystringyesThe patient's message (at most 1500 characters on support and billing threads).

Example response · 201

Response
{
  "data": {
    "id": "00000000-0000-4000-8000-00000000e001",
    "threadId": "00000000-0000-4000-8000-00000000e001",
    "createdAt": "2026-01-01T00:00:00.000Z"
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment",
    "replayed": false
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
403entity_baa_missingYour BAA does not cover the physician group responsible for this case.
400idempotency_key_requiredProvide a 16 to 255 character Idempotency-Key.
400invalid_jsonThe body is not valid JSON.
422validation_errorThe body does not match the contract.
409idempotency_in_progressA request with this Idempotency-Key is still being processed.
409idempotency_conflictThis Idempotency-Key was used with a different body.
404thread_not_foundNo thread with that id belongs to this patient.
409thread_closedThe thread is closed; start a new one.
409clinician_not_assignedNo clinician is assigned to the case.
503message_unavailableCould not be completed. Nothing was changed; retry.
403patient_admission_closedThis deployment is not admitting patients or taking patient data yet (a real-data deployment before its real-PHI launch, or without a loaded launch authorization). Nothing was processed and the Idempotency-Key is not spent. Never answered on the sandbox.
413payload_too_largeThe request body exceeds 24576 bytes (24 KiB). Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API: the same status and code, with `request_id` `edge` and no `Request-Id` header.
503idempotency_unavailableThe idempotency record could not be checked. Nothing was processed; retry after `Retry-After`.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
POST/api/v1/patients/{patientId}/messages/{messageId}/readmessages.write

Record that the patient read a care-team message

Call when your UI shows the patient a care-team message, so the clinician sees it was read. Support messages carry no read receipt (409 read_receipt_unsupported).

Requires an Idempotency-Key header of 16–255 characters. Replays for 24 hours; reusing a key with a different body returns 409 rather than applying the second request.

Example response · 200

Response
{
  "data": {
    "id": "00000000-0000-4000-8000-00000000e001",
    "readAt": "2026-01-01T00:00:00.000Z"
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment",
    "replayed": false
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
403entity_baa_missingYour BAA does not cover the physician group responsible for this case.
400idempotency_key_requiredProvide a 16 to 255 character Idempotency-Key.
404message_not_foundNo message with that id belongs to this patient.
409read_receipt_unsupportedOnly a care-team message delivered to the patient is marked read.
503message_unavailableCould not be completed. Nothing was changed; retry.
409idempotency_conflictThis Idempotency-Key was already used with different request parameters. Nothing was applied; use a new key.
409idempotency_in_progressA request with this Idempotency-Key is still being processed. Retry after `Retry-After`; nothing is applied twice.
503idempotency_unavailableThe idempotency record could not be checked. Nothing was processed; retry after `Retry-After`.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
GET/api/v1/patients/{patientId}/unread-summarymessages.read

Count unread care-team messages

Care-team messages delivered to the patient and not yet marked read, in total and per thread.

Example response · 200

Response
{
  "data": {
    "unreadCount": 1,
    "threads": [
      {
        "threadId": "00000000-0000-4000-8000-00000000e001",
        "unreadCount": 1,
        "latestAt": "2026-01-01T00:00:00.000Z"
      }
    ]
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
403entity_baa_missingYour BAA does not cover the physician group responsible for this case.
503message_unavailableCould not be completed. Nothing was changed; retry.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
GET/api/v1/patients/{patientId}/lab-requirementslabs.read

What labs the patient's program needs, and the labs gate

The physician group's lab protocol for the program (baseline labs before prescribing, monitoring labs on an interval once therapy starts; for example a 40+ biomarker peptide baseline and a CBC and CMP every 6 months) and where the patient stands on each: met, under review, required or due, with due dates and the result that satisfies it. `gate` is what holds prescribing and fills (null when labs hold nothing). Give `caseId`, or `programId` before the patient has a case (requirements then follow the patient's latest case in the program). Use it to tell the patient which draw to book; `clinical.case.*` and `clinical.monitoring.labs_due` tell you when to look.

Query parameters

FieldTypeRequiredNotes
caseIdstringnoThe case the labs are for (from the cases read).
programHandlestringnoThe program's public handle (`prg_...`, from the catalog, eligibility or enrollments), when there is no case yet (requirements follow the patient's latest case in it).
programIdstringnoDeprecated: the `programId` an earlier answer of this endpoint returned. Use programHandle.

Example response · 200

Response
{
  "data": {
    "caseId": "00000000-0000-4000-8000-00000000e001",
    "programId": "00000000-0000-4000-8000-00000000e001",
    "programHandle": "example-program-handle",
    "gate": "labs_required",
    "requirementSetId": "00000000-0000-4000-8000-00000000e001",
    "requirements": [
      {
        "id": "00000000-0000-4000-8000-00000000e001",
        "key": "example-key",
        "label": "example-label",
        "kind": "baseline",
        "loincCodes": [
          "example-loinc-codes"
        ],
        "minCodesPresent": 1,
        "maxAgeDays": 1,
        "intervalDays": 1,
        "acceptedSources": [
          "brand_arranged"
        ],
        "status": "met",
        "dueAt": "2026-01-01T00:00:00.000Z",
        "satisfiedByResultId": "00000000-0000-4000-8000-00000000e001",
        "lastCollectedAt": "2026-01-01T00:00:00.000Z",
        "pendingResultId": "00000000-0000-4000-8000-00000000e001"
      }
    ]
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
403entity_baa_missingYour BAA does not cover the physician group responsible for this case.
404case_not_foundNo case with that id belongs to this patient.
404program_not_foundNo program with that id in your brand.
422validation_errorGive caseId or programId (valid ids).
422program_mismatchThe case is not in that program.
503labs_unavailableLab requirements could not be read; retry.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
POST/api/v1/patients/{patientId}/lab-resultslabs.write

Submit a lab result

Posts one performed-lab report for a case: structured LOINC observations (value, unit, reference range and the lab's interpretation code), the source (`brand_arranged` or `patient_supplied`), the performing lab's CLIA number and collectedAt. Ambition flags it on receipt (`flag`), assigns it to the case's clinician for review and sign-off and sends `clinical.lab.result_received`; a critical value also alerts Care operations and sends `clinical.lab.critical`. Until the clinician signs off a critical result, prescribing and fills hold (`labs_critical`); `clinical.lab.reviewed` follows the sign-off. Idempotent by Idempotency-Key and, when you send it, by `externalRef`. Values are stored as reported; Ambition does not convert units.

Requires an Idempotency-Key header of 16–255 characters. Replays for 24 hours; reusing a key with a different body returns 409 rather than applying the second request.

Request body

FieldTypeRequiredNotes
caseIdstringyesThe case the labs are for (from the cases read).
source"brand_arranged" | "patient_supplied"yes`brand_arranged`: you ordered the draw (through your lab partner or aggregator). `patient_supplied`: the patient brought a report. A result of a requisition Ambition's clinician signed is reported by that lab, not by you: it reads `ambition_ordered` in the results list.
performingLabobjectyes
collectedAtstringyesWhen the specimen was collected (not in the future; at most 5 years ago).
reportedAtstringnoWhen the lab reported it.
panelCodestringnoThe panel's LOINC code, e.g. `24323-8` (CMP).
panelNamestringno
externalRefstringnoYour own reference for this report (never PHI). The same externalRef answers the same result.
observationsarrayyesOne entry per analyte, each LOINC code at most once.

Example response · 201

Response
{
  "data": {
    "id": "00000000-0000-4000-8000-00000000e001",
    "caseId": "00000000-0000-4000-8000-00000000e001",
    "source": "brand_arranged",
    "flag": "normal",
    "observationCount": 1,
    "abnormalCount": 1,
    "criticalCount": 1,
    "reviewStatus": "pending_review",
    "collectedAt": "2026-01-01T00:00:00.000Z",
    "receivedAt": "2026-01-01T00:00:00.000Z"
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment",
    "replayed": false
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
403entity_baa_missingYour BAA does not cover the physician group responsible for this case.
400idempotency_key_requiredProvide a 16 to 255 character Idempotency-Key.
400invalid_jsonThe body is not valid JSON.
422validation_errorThe body does not match the contract.
409idempotency_in_progressA request with this Idempotency-Key is still being processed. Retry after `Retry-After`.
409idempotency_conflictThis Idempotency-Key was used with a different body.
404case_not_foundNo case with that id belongs to this patient.
409external_ref_conflictThat externalRef already names another patient's or case's report.
422loinc_invalidA code is not a LOINC code with a valid check digit.
422clia_invalidThe CLIA number is not two digits, D and seven digits.
422collected_at_invalidcollectedAt is in the future or more than 5 years ago, or reportedAt precedes it.
422observations_invalidAn observation's value, unit or reference range is invalid, or a code repeats.
422lab_order_not_foundAn `ambition_ordered` result names an Ambition lab order and is reported by the lab, not submitted here.
503labs_unavailableThe result could not be recorded. Nothing was changed; retry with the same Idempotency-Key.
403patient_admission_closedThis deployment is not admitting patients or taking patient data yet (a real-data deployment before its real-PHI launch, or without a loaded launch authorization). Nothing was processed and the Idempotency-Key is not spent. Never answered on the sandbox.
413payload_too_largeThe request body exceeds 65536 bytes (64 KiB). Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API: the same status and code, with `request_id` `edge` and no `Request-Id` header.
503idempotency_unavailableThe idempotency record could not be checked. Nothing was processed; retry after `Retry-After`.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
GET/api/v1/patients/{patientId}/lab-resultslabs.read

List the patient's lab results

The patient's lab results, newest first: source, performing lab, collectedAt, flag and counts, review status and disposition. Read one result for its values. Only cases whose physician group your BAA covers are listed.

Query parameters

FieldTypeRequiredNotes
caseIdstringnoOnly this case's results.

Example response · 200

Response
{
  "data": [
    {
      "id": "00000000-0000-4000-8000-00000000e001",
      "caseId": "00000000-0000-4000-8000-00000000e001",
      "source": "brand_arranged",
      "flag": "normal",
      "observationCount": 1,
      "abnormalCount": 1,
      "criticalCount": 1,
      "reviewStatus": "pending_review",
      "collectedAt": "2026-01-01T00:00:00.000Z",
      "receivedAt": "2026-01-01T00:00:00.000Z",
      "performingLab": {
        "name": "example-name",
        "cliaNumber": "example-clia-number"
      },
      "panelCode": "example-panel-code",
      "panelName": "example-panel-name",
      "reviewedAt": "2026-01-01T00:00:00.000Z",
      "disposition": "no_action"
    }
  ],
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
403entity_baa_missingYour BAA does not cover the physician group responsible for this case.
404case_not_foundNo case with that id belongs to this patient.
422validation_errorQuery parameters were invalid.
503labs_unavailableLab results could not be read; retry.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
GET/api/v1/patients/{patientId}/lab-results/{labResultId}labs.read

Read one lab result

One result with its observations (as submitted, with Ambition's flag for each) and the clinician's sign-off as you may show it: status, time, disposition and the clinician's note to the patient. The care-team note is never served.

Example response · 200

Response
{
  "data": {
    "id": "00000000-0000-4000-8000-00000000e001",
    "caseId": "00000000-0000-4000-8000-00000000e001",
    "source": "brand_arranged",
    "performingLab": {
      "name": "example-name",
      "cliaNumber": "example-clia-number"
    },
    "panelCode": "example-panel-code",
    "panelName": "example-panel-name",
    "collectedAt": "2026-01-01T00:00:00.000Z",
    "reportedAt": "2026-01-01T00:00:00.000Z",
    "receivedAt": "2026-01-01T00:00:00.000Z",
    "externalRef": "example-external-ref",
    "flag": "normal",
    "observationCount": 1,
    "abnormalCount": 1,
    "criticalCount": 1,
    "review": {
      "status": "pending_review",
      "reviewedAt": "2026-01-01T00:00:00.000Z",
      "disposition": "no_action",
      "patientNote": "example-patient-note"
    },
    "observations": [
      {
        "code": "example-code",
        "display": "example-display",
        "value": 1,
        "unit": "example-unit",
        "referenceRange": {
          "low": 1,
          "high": 1,
          "text": "example-text"
        },
        "interpretation": "N",
        "flag": "normal"
      }
    ]
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
403entity_baa_missingYour BAA does not cover the physician group responsible for this case.
404lab_result_not_foundNo lab result with that id belongs to this patient.
503labs_unavailableThe result could not be read; retry.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
GET/api/v1/patients/{patientId}/lab-orderslabs.read

List the patient's lab orders

The lab requisitions a clinician signed for the patient (a panel of the program's lab protocol, or one the clinician ordered), newest first: the lab, how the specimen is collected, fasting hours, the lab's instructions, the status, the times it moved and how long the requisition is valid. Use it to tell the patient how to complete the draw after `clinical.lab.order_signed`, and to follow `clinical.lab.order_updated`. A requisition not yet signed is not listed. No diagnosis code, clinician signature or lab account is served. Only cases whose physician group your BAA covers are listed.

Query parameters

FieldTypeRequiredNotes
caseIdstringnoOnly this case's lab orders.

Example response · 200

Response
{
  "data": [
    {
      "id": "00000000-0000-4000-8000-00000000e001",
      "reference": "example-reference",
      "caseId": "00000000-0000-4000-8000-00000000e001",
      "status": "signed",
      "panelKey": "example-panel-key",
      "panelName": "example-panel-name",
      "lab": {
        "name": "example-name",
        "sandbox": false
      },
      "collectionMode": "walk_in",
      "fastingHours": 1,
      "instructions": [
        "example-instructions"
      ],
      "signedAt": "2026-01-01T00:00:00.000Z",
      "expiresAt": "2026-01-01T00:00:00.000Z",
      "sentAt": "2026-01-01T00:00:00.000Z",
      "acceptedAt": "2026-01-01T00:00:00.000Z",
      "collectedAt": "2026-01-01T00:00:00.000Z",
      "receivedAt": "2026-01-01T00:00:00.000Z",
      "resultedAt": "2026-01-01T00:00:00.000Z",
      "closedAt": "2026-01-01T00:00:00.000Z",
      "closedReason": "example-closed-reason",
      "resultIds": [
        "00000000-0000-4000-8000-00000000e001"
      ]
    }
  ],
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
403entity_baa_missingYour BAA does not cover the physician group responsible for this case.
404case_not_foundNo case with that id belongs to this patient.
422validation_errorQuery parameters were invalid.
503labs_unavailableLab orders could not be read; retry.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
GET/api/v1/patients/{patientId}/lab-orders/{labOrderId}labs.read

Read one lab order

One signed lab requisition of the patient, as in the list: what to tell the patient (the lab, collection, fasting, instructions, validity), where it stands and the results reported for it.

Example response · 200

Response
{
  "data": {
    "id": "00000000-0000-4000-8000-00000000e001",
    "reference": "example-reference",
    "caseId": "00000000-0000-4000-8000-00000000e001",
    "status": "signed",
    "panelKey": "example-panel-key",
    "panelName": "example-panel-name",
    "lab": {
      "name": "example-name",
      "sandbox": false
    },
    "collectionMode": "walk_in",
    "fastingHours": 1,
    "instructions": [
      "example-instructions"
    ],
    "signedAt": "2026-01-01T00:00:00.000Z",
    "expiresAt": "2026-01-01T00:00:00.000Z",
    "sentAt": "2026-01-01T00:00:00.000Z",
    "acceptedAt": "2026-01-01T00:00:00.000Z",
    "collectedAt": "2026-01-01T00:00:00.000Z",
    "receivedAt": "2026-01-01T00:00:00.000Z",
    "resultedAt": "2026-01-01T00:00:00.000Z",
    "closedAt": "2026-01-01T00:00:00.000Z",
    "closedReason": "example-closed-reason",
    "resultIds": [
      "00000000-0000-4000-8000-00000000e001"
    ]
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
403entity_baa_missingYour BAA does not cover the physician group responsible for this case.
404lab_order_not_foundNo signed lab order with that id belongs to this patient.
503labs_unavailableThe lab order could not be read; retry.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
GET/api/v1/patients/{patientId}/notificationsnotifications.ack

The patient's notices: what you still owe them

Notices raised for the patient, newest first. `state=open` (default) lists the ones routed to you and not yet acknowledged: reconcile against it after downtime, so no clinically required notice misses its deadline. A notice about a case whose physician group your BAA no longer covers is left out. Recorded in the access log.

Query parameters

FieldTypeRequiredNotes
state"open" | "all"no`open`: yours to send and not yet acknowledged. `all`: every notice.
limitintegernoNotices per page, newest first, 1 to 100.

Example response · 200

Response
{
  "data": [
    {
      "id": "00000000-0000-4000-8000-00000000e001",
      "templateKey": "urgent_clinician_message",
      "clinicallyRequired": false,
      "urgency": "urgent",
      "route": "brand_webhook",
      "caseId": "00000000-0000-4000-8000-00000000e001",
      "requestedAt": "2026-01-01T00:00:00.000Z",
      "ackDeadline": "example-ack-deadline",
      "acknowledgedAt": "2026-01-01T00:00:00.000Z",
      "channel": "email",
      "ambitionNotice": "not_needed"
    }
  ],
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend `Ambition-Patient-Session` and `Ambition-Patient-Auth`.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
503patient_access_unavailablePatient access is temporarily unavailable. Nothing was processed.
422validation_errorQuery parameters were invalid.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
POST/api/v1/patients/{patientId}/notifications/{notificationId}/deliverednotifications.ack

Acknowledge that you delivered a notice

Records that you told the patient (channel and time). The first acknowledgment stands; repeating it returns the notice as acknowledged. A clinically required notice acknowledged by its deadline cancels Ambition's content-free fallback. Stamped with your key, the patient's session reference and whether your automation sent it.

Requires an Idempotency-Key header of 16–255 characters. Replays for 24 hours; reusing a key with a different body returns 409 rather than applying the second request.

Request body

FieldTypeRequiredNotes
channel"email" | "sms" | "push" | "in_app"yesHow you delivered the notice.
deliveredAtstringyesWhen you delivered it (ISO 8601).

Example response · 200

Response
{
  "data": {
    "id": "00000000-0000-4000-8000-00000000e001",
    "templateKey": "urgent_clinician_message",
    "clinicallyRequired": false,
    "urgency": "urgent",
    "route": "brand_webhook",
    "caseId": "00000000-0000-4000-8000-00000000e001",
    "requestedAt": "2026-01-01T00:00:00.000Z",
    "ackDeadline": "example-ack-deadline",
    "acknowledgedAt": "2026-01-01T00:00:00.000Z",
    "channel": "email",
    "ambitionNotice": "not_needed"
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend `Ambition-Patient-Session` and `Ambition-Patient-Auth`.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
404patient_not_foundNo patient with that id is available to this key.
503patient_access_unavailablePatient access is temporarily unavailable. Nothing was processed.
400idempotency_key_requiredProvide a 16–255 character Idempotency-Key.
409idempotency_conflictThis Idempotency-Key was already used for another notice or body.
409idempotency_in_progressA request with this Idempotency-Key is still being processed. Retry after `Retry-After`.
403entity_baa_missingYour BAA does not cover the physician group responsible for this notice's case.
404notification_not_foundNo notice with that id for this patient.
422validation_errorThe body was invalid, or `deliveredAt` is in the future or before the notice was raised.
413payload_too_largeThe request body exceeds 4096 bytes (4 KiB). Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API: the same status and code, with `request_id` `edge` and no `Request-Id` header.
503idempotency_unavailableThe idempotency record could not be checked. Nothing was processed; retry after `Retry-After`.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
POST/api/v1/patients/{patientId}/prescriptions/{prescriptionId}/release-fulfillmentfulfillment.write

Release a signed prescription to the pharmacy after you captured payment

For a program whose fulfillment release is `brand_release` (you are merchant of record and charge once the prescription is written): Ambition does not transmit a signed prescription until you call this. Call it after you capture the patient's payment. A prescription you do not release within 7 days of signature is never transmitted; the clinician and the patient are told (`clinical.prescription.held`), and refunding is yours. A release never overrides a clinical or regulatory hold: the answer says when one still stands. Idempotent: releasing again answers the current state with `replayed: true`.

Requires an Idempotency-Key header of 16–255 characters. Replays for 24 hours; reusing a key with a different body returns 409 rather than applying the second request.

Request body

FieldTypeRequiredNotes
paymentReferencestringnoYour own reference for the payment you captured (for example your processor's charge id). Ambition keeps only its SHA-256 digest, to reconcile; it is never shown or sent anywhere.

Example response · 200

Response
{
  "data": {
    "prescriptionId": "00000000-0000-4000-8000-00000000e001",
    "release": "released",
    "releasedAt": "2026-01-01T00:00:00.000Z",
    "transmission": "awaiting_order",
    "holdReason": "regulatory",
    "replayed": false
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
403entity_baa_missingYour BAA does not cover the physician group of this prescription's case.
404prescription_not_foundNo prescription with that id for this patient.
409release_not_requiredThe program transmits on signature (`transmission_release` is `auto`).
409prescription_not_releasableThe prescription is not signed, or was cancelled, rejected or replaced.
409release_expiredMore than 7 days passed since signature; it will not be transmitted. Ask the clinician for a new prescription.
503release_unavailableThe release could not be recorded. Nothing changed; retry with the same Idempotency-Key.
403patient_admission_closedThis deployment is not admitting patients or taking patient data yet (a real-data deployment before its real-PHI launch, or without a loaded launch authorization). Nothing was processed and the Idempotency-Key is not spent. Never answered on the sandbox.
413payload_too_largeThe request body exceeds 4096 bytes (4 KiB). Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API: the same status and code, with `request_id` `edge` and no `Request-Id` header.
400idempotency_key_requiredProvide a 16–255 character Idempotency-Key.
409idempotency_conflictThis Idempotency-Key was already used with different request parameters. Nothing was applied; use a new key.
409idempotency_in_progressA request with this Idempotency-Key is still being processed. Retry after `Retry-After`; nothing is applied twice.
503idempotency_unavailableThe idempotency record could not be checked. Nothing was processed; retry after `Retry-After`.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
GET/api/v1/billing/ledgerbilling.read

Your billing ledger: fees, prefund or credit position, standing, and ledger lines

Billing mode B (you are merchant of record): what you owe Ambition at your fixed fees. One professional and one platform line per decided review (approved or declined alike) and one pharmacy and one platform line per fill, newest first with cursor pagination, plus your fee schedule, prefund or credit position and standing (a nonpayment suspension pauses new intake; patients in care keep a 30-day continuity grace). PHI-free by construction: a line is named by its own opaque id and never by a case, order, patient, program or product, so this is a standard key scope (`billing.read`, minted with `finance.read`), not a patient scope, and it is never offered to a marketplace app. Brand and plane come from the key: a sandbox key reads the sandbox plane's test ledger, a live key the live one, and another brand's rows are not addressable. Never cached.

Query parameters

FieldTypeRequiredNotes
basis"review" | "fill" | "adjustment"noOnly lines of this basis.
invoiced"true" | "false"no`true`: only lines already on an invoice; `false`: only uninvoiced lines.
cursorstringnoThe `nextCursor` of the previous page.
limitintegernoPer page, 1 to 100.

Example response · 200

Response
{
  "data": {
    "environment": "example-environment",
    "livemode": false,
    "schedule": {
      "version": 1,
      "currency": "usd",
      "reviewProfessionalCents": 1,
      "reviewPlatformCents": 1,
      "fillPharmacyCents": 1,
      "fillPlatformCents": 1,
      "settlement": "prefund",
      "creditLimitCents": 1,
      "prefundFloorCents": 1,
      "paymentTermsDays": 1,
      "effectiveFrom": "example-effective-from"
    },
    "position": {
      "currency": "usd",
      "prefundedCents": 1,
      "accruedCents": 1,
      "balanceCents": 1,
      "uninvoicedCents": 1,
      "openInvoicedCents": 1,
      "pastDueCents": 1
    },
    "standing": {
      "state": "good",
      "reason": "invoice_past_due",
      "suspendedAt": "2026-01-01T00:00:00.000Z",
      "continuityUntil": "example-continuity-until"
    },
    "lines": [
      {
        "id": "00000000-0000-4000-8000-00000000e001",
        "occurredAt": "2026-01-01T00:00:00.000Z",
        "basis": "review",
        "kind": "professional",
        "amountCents": 1,
        "currency": "usd",
        "invoiceNumber": "example-invoice-number",
        "livemode": false
      }
    ]
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment",
    "hasMore": false,
    "nextCursor": "example-next-cursor"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
422validation_errorQuery parameters, or the cursor, were invalid.
503billing_unavailableThe ledger could not be read. Nothing was changed; retry.
GET/api/v1/billing/invoicesbilling.read

Your invoices from Ambition

The B2B invoices Ambition issued you under billing mode B, newest first with cursor pagination: period, counts, totals by kind (always summing to the total), status (`past_due` once an open invoice passes its due date), due and paid dates. A prefund brand's invoice is a statement settled from its balance. PHI-free by construction: a line is named by its own opaque id and never by a case, order, patient, program or product, so this is a standard key scope (`billing.read`, minted with `finance.read`), not a patient scope, and it is never offered to a marketplace app. Brand and plane come from the key: a sandbox key reads the sandbox plane's test ledger, a live key the live one, and another brand's rows are not addressable. Never cached.

Query parameters

FieldTypeRequiredNotes
status"open" | "past_due" | "paid" | "void" | "uncollectible"noOnly invoices in this state (`past_due`: open past its due date).
cursorstringnoThe `nextCursor` of the previous page.
limitintegernoPer page, 1 to 100.

Example response · 200

Response
{
  "data": [
    {
      "id": "00000000-0000-4000-8000-00000000e001",
      "number": "example-number",
      "periodStart": "example-period-start",
      "periodEnd": "example-period-end",
      "settlement": "prefund",
      "reviewCount": 1,
      "fillCount": 1,
      "professionalCents": 1,
      "pharmacyCents": 1,
      "platformCents": 1,
      "adjustmentCents": 1,
      "totalCents": 1,
      "currency": "usd",
      "status": "open",
      "issuedAt": "2026-01-01T00:00:00.000Z",
      "dueAt": "2026-01-01T00:00:00.000Z",
      "paidAt": "2026-01-01T00:00:00.000Z",
      "livemode": false,
      "stripeLinked": false
    }
  ],
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment",
    "hasMore": false,
    "nextCursor": "example-next-cursor"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
422validation_errorQuery parameters, or the cursor, were invalid.
503billing_unavailableThe ledger could not be read. Nothing was changed; retry.
POST/api/v1/sandbox/patients/{patientId}/cases/{caseId}/simulatecases.write

Sandbox: the test clinician decides a case (approve prescribes)

The sandbox test clinician takes the case's review task and records `approve`, `needs_info` or `decline` with the real decision command, so `clinical.case.approved`, `.needs_info` or `.declined` (with `refundExpected` in mode B) reaches your receiver. On approve it also writes, signs and routes the prescription through the prescription rail (`clinical.prescription.signed`, `clinical.order.created`); transmission then waits on every real hold (release, contact, labs). Real gates hold: a case waiting on labs answers 409 `labs_required`; a case assigned to another clinician 409 `sandbox_clinician_unavailable`. Sandbox only: a sandbox phi_server key, a synthetic brand and the developer sandbox deployment (any other answers 403 `sandbox_simulation_only`). The actor is synthetic and labelled; the commands, gates and events are the real ones, with `livemode: false`. 60 loops (patients) and 600 simulations per brand per hour. Idempotent: the same Idempotency-Key answers the first simulation.

Requires an Idempotency-Key header of 16–255 characters. Replays for 24 hours; reusing a key with a different body returns 409 rather than applying the second request.

Request body

FieldTypeRequiredNotes
action"approve" | "needs_info" | "decline"yesThe sandbox test clinician's decision: `approve`, `needs_info` or `decline`.
reasonstringnoThe patient-facing reason (needs_info and decline); a synthetic default is used when omitted. Never real clinical content.
prescribebooleannoOn approve (default true): also write, sign and route the prescription for the case's first prescribable product.
pharmacyProductIdstringnoOn approve with prescribe: which product of the case's prescription workspace to prescribe.

Example response · 200

Response
{
  "data": {
    "id": "00000000-0000-4000-8000-00000000e001",
    "object": "sandbox_simulation",
    "kind": "case",
    "action": "example-action",
    "objectId": "example_objectId",
    "patientId": "00000000-0000-4000-8000-00000000e001",
    "actor": {
      "kind": "sandbox_test_clinician",
      "label": "example-label"
    },
    "result": {},
    "events": [
      {
        "id": "00000000-0000-4000-8000-00000000e001",
        "type": "example-type",
        "object": "example-object",
        "objectId": "example_objectId",
        "deliveryState": "example-delivery-state",
        "recordedAt": "2026-01-01T00:00:00.000Z"
      }
    ],
    "standardEvents": [
      "example-standard-events"
    ],
    "livemode": false,
    "simulatedAt": "2026-01-01T00:00:00.000Z",
    "replayed": false
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
422validation_failedThe simulation request is invalid.
403sandbox_simulation_onlySandbox simulation runs only on the developer sandbox, for a synthetic brand, with a sandbox key.
404patient_not_foundNo patient with that id is available to this key.
422simulation_action_invalidThat action is not one this object takes.
409idempotency_key_reusedThis Idempotency-Key was used for a different simulation.
429sandbox_simulation_rate_limitedAt most 600 sandbox simulations per brand per hour.
429sandbox_loop_rate_limitedAt most 60 sandbox loops (patients) per brand per hour.
503simulation_unavailableThe simulation could not run. Nothing changed; retry with the same Idempotency-Key.
404case_not_foundNo case with that id for this patient.
409case_not_assignedThe case has no clinician yet; the sandbox network assigns one when the case enters review.
409sandbox_clinician_unavailableThe case is with a clinician who is not the sandbox test clinician.
409sandbox_clinician_not_credentialedThe sandbox test clinician is not licensed in the case's state.
409labs_requiredThe program's lab requirements must be met and signed off before this case is approved.
409case_not_reviewableThe case is not in review.
409decision_refusedThe sandbox test clinician could not record the decision; a real gate refused it.
409prescription_refusedThe sandbox test clinician could not write the prescription; a real gate refused it.
409no_prescribable_productNo product of the sandbox formulary fits this case.
413payload_too_largeThe request body exceeds 2048 bytes (2 KiB). Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API: the same status and code, with `request_id` `edge` and no `Request-Id` header.
400idempotency_key_requiredProvide a 16–255 character Idempotency-Key.
409idempotency_conflictThis Idempotency-Key was already used with different request parameters. Nothing was applied; use a new key.
409idempotency_in_progressA request with this Idempotency-Key is still being processed. Retry after `Retry-After`; nothing is applied twice.
503idempotency_unavailableThe idempotency record could not be checked. Nothing was processed; retry after `Retry-After`.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
POST/api/v1/sandbox/patients/{patientId}/prescriptions/{prescriptionId}/simulatefulfillment.write

Sandbox: the test pharmacy ships or delivers; policy withdrawn; renewal due

`acknowledge`, `accept`, `ship`, `deliver` or `exception`: the sandbox test pharmacy reports the order's status as an authenticated partner callback, earlier steps first (`clinical.order.acknowledged`, `.accepted`, `.in_production`, `.shipped`, `.delivered`, `.exception`). The order must have been transmitted; name a refill's order with `orderId` and it walks its own steps through to delivered. `policy_withdrawn`: for a prescription of a regulated substance, the real regulatory hold on this prescription (`clinical.prescription.held`, hold `policy_withdrawn`) and `formulary.policy_changed` to you only. `renewal_due`: the therapy's check-in falls due now (`clinical.checkin.due`) and the `renewal_due` notice is requested (`clinical.notification.requested`). Sandbox only: a sandbox phi_server key, a synthetic brand and the developer sandbox deployment (any other answers 403 `sandbox_simulation_only`). The actor is synthetic and labelled; the commands, gates and events are the real ones, with `livemode: false`. 60 loops (patients) and 600 simulations per brand per hour. Idempotent: the same Idempotency-Key answers the first simulation.

Requires an Idempotency-Key header of 16–255 characters. Replays for 24 hours; reusing a key with a different body returns 409 rather than applying the second request.

Request body

FieldTypeRequiredNotes
action"acknowledge" | "accept" | "ship" | "deliver" | "exception" | "policy_withdrawn" | "renewal_due"yes`acknowledge`, `accept`, `ship`, `deliver` or `exception`: the sandbox test pharmacy reports the order's status (earlier steps first; the order must have been transmitted; a refill order walks its own steps). `policy_withdrawn`: the regulatory policy of the prescription's regulated substance is withdrawn for this prescription only (held `policy_withdrawn`, `formulary.policy_changed` to you). `renewal_due`: the therapy's check-in falls due now and the renewal notice is requested.
orderIdstringnoThe order to report on; default the prescription's first order. Name a refill's order (`orderId` of the fill) to move it through to delivered.

Example response · 200

Response
{
  "data": {
    "id": "00000000-0000-4000-8000-00000000e001",
    "object": "sandbox_simulation",
    "kind": "case",
    "action": "example-action",
    "objectId": "example_objectId",
    "patientId": "00000000-0000-4000-8000-00000000e001",
    "actor": {
      "kind": "sandbox_test_clinician",
      "label": "example-label"
    },
    "result": {},
    "events": [
      {
        "id": "00000000-0000-4000-8000-00000000e001",
        "type": "example-type",
        "object": "example-object",
        "objectId": "example_objectId",
        "deliveryState": "example-delivery-state",
        "recordedAt": "2026-01-01T00:00:00.000Z"
      }
    ],
    "standardEvents": [
      "example-standard-events"
    ],
    "livemode": false,
    "simulatedAt": "2026-01-01T00:00:00.000Z",
    "replayed": false
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
422validation_failedThe simulation request is invalid.
403sandbox_simulation_onlySandbox simulation runs only on the developer sandbox, for a synthetic brand, with a sandbox key.
404patient_not_foundNo patient with that id is available to this key.
422simulation_action_invalidThat action is not one this object takes.
409idempotency_key_reusedThis Idempotency-Key was used for a different simulation.
429sandbox_simulation_rate_limitedAt most 600 sandbox simulations per brand per hour.
429sandbox_loop_rate_limitedAt most 60 sandbox loops (patients) per brand per hour.
503simulation_unavailableThe simulation could not run. Nothing changed; retry with the same Idempotency-Key.
404prescription_not_foundNo prescription with that id for this patient.
409order_not_foundThe prescription has no pharmacy order yet.
409order_not_transmittedThe order has not been transmitted yet; transmission waits on every hold and runs within a minute.
409order_closedThe order is closed.
409order_past_exceptionThe pharmacy has already accepted this prescription or refill order.
409sandbox_pharmacy_unavailableThe order is routed to a pharmacy that is not the sandbox test pharmacy.
409pharmacy_report_refusedThe pharmacy status could not be applied in this order's state.
409regulated_product_requiredpolicy_withdrawn applies to a prescription of a regulated (policy) substance.
409prescription_not_activeThe prescription is not active.
413payload_too_largeThe request body exceeds 2048 bytes (2 KiB). Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API: the same status and code, with `request_id` `edge` and no `Request-Id` header.
400idempotency_key_requiredProvide a 16–255 character Idempotency-Key.
409idempotency_conflictThis Idempotency-Key was already used with different request parameters. Nothing was applied; use a new key.
409idempotency_in_progressA request with this Idempotency-Key is still being processed. Retry after `Retry-After`; nothing is applied twice.
503idempotency_unavailableThe idempotency record could not be checked. Nothing was processed; retry after `Retry-After`.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
POST/api/v1/sandbox/patients/{patientId}/lab-results/{labResultId}/simulatelabs.write

Sandbox: the test clinician signs a lab result off

The sandbox test clinician of the result's case signs it off with the real sign-off command (`clinical.lab.reviewed`); a critical result records the synthetic contact confirmation. The labs gate then clears or holds exactly as it would. Sandbox only: a sandbox phi_server key, a synthetic brand and the developer sandbox deployment (any other answers 403 `sandbox_simulation_only`). The actor is synthetic and labelled; the commands, gates and events are the real ones, with `livemode: false`. 60 loops (patients) and 600 simulations per brand per hour. Idempotent: the same Idempotency-Key answers the first simulation.

Requires an Idempotency-Key header of 16–255 characters. Replays for 24 hours; reusing a key with a different body returns 409 rather than applying the second request.

Request body

FieldTypeRequiredNotes
action"sign_off"yes`sign_off`: the sandbox test clinician signs the result off.
disposition"no_action" | "follow_up" | "repeat_test"noDefault `no_action` for a normal result, `follow_up` otherwise. A critical result records the synthetic contact confirmation.

Example response · 200

Response
{
  "data": {
    "id": "00000000-0000-4000-8000-00000000e001",
    "object": "sandbox_simulation",
    "kind": "case",
    "action": "example-action",
    "objectId": "example_objectId",
    "patientId": "00000000-0000-4000-8000-00000000e001",
    "actor": {
      "kind": "sandbox_test_clinician",
      "label": "example-label"
    },
    "result": {},
    "events": [
      {
        "id": "00000000-0000-4000-8000-00000000e001",
        "type": "example-type",
        "object": "example-object",
        "objectId": "example_objectId",
        "deliveryState": "example-delivery-state",
        "recordedAt": "2026-01-01T00:00:00.000Z"
      }
    ],
    "standardEvents": [
      "example-standard-events"
    ],
    "livemode": false,
    "simulatedAt": "2026-01-01T00:00:00.000Z",
    "replayed": false
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
422validation_failedThe simulation request is invalid.
403sandbox_simulation_onlySandbox simulation runs only on the developer sandbox, for a synthetic brand, with a sandbox key.
404patient_not_foundNo patient with that id is available to this key.
422simulation_action_invalidThat action is not one this object takes.
409idempotency_key_reusedThis Idempotency-Key was used for a different simulation.
429sandbox_simulation_rate_limitedAt most 600 sandbox simulations per brand per hour.
429sandbox_loop_rate_limitedAt most 60 sandbox loops (patients) per brand per hour.
503simulation_unavailableThe simulation could not run. Nothing changed; retry with the same Idempotency-Key.
404lab_result_not_foundNo lab result with that id for this patient.
409already_reviewedThis result is already signed off.
409case_not_assignedThe case has no clinician yet; the sandbox network assigns one when the case enters review.
409sandbox_clinician_unavailableThe case is with a clinician who is not the sandbox test clinician.
409sign_off_refusedThe sandbox test clinician could not sign the result off.
413payload_too_largeThe request body exceeds 2048 bytes (2 KiB). Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API: the same status and code, with `request_id` `edge` and no `Request-Id` header.
400idempotency_key_requiredProvide a 16–255 character Idempotency-Key.
409idempotency_conflictThis Idempotency-Key was already used with different request parameters. Nothing was applied; use a new key.
409idempotency_in_progressA request with this Idempotency-Key is still being processed. Retry after `Retry-After`; nothing is applied twice.
503idempotency_unavailableThe idempotency record could not be checked. Nothing was processed; retry after `Retry-After`.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
POST/api/v1/sandbox/patients/{patientId}/fills/{fillId}/simulatefills.write

Sandbox: a refill held too soon becomes due

For a fill held only `refill_too_soon`: every other gate is evaluated for real and, as if enough of the last supply had been used, the real refill order is created (`clinical.fill.requested`, then `clinical.fill.transmitted` when the pharmacy worker sends it). Sandbox only: a sandbox phi_server key, a synthetic brand and the developer sandbox deployment (any other answers 403 `sandbox_simulation_only`). The actor is synthetic and labelled; the commands, gates and events are the real ones, with `livemode: false`. 60 loops (patients) and 600 simulations per brand per hour. Idempotent: the same Idempotency-Key answers the first simulation.

Requires an Idempotency-Key header of 16–255 characters. Replays for 24 hours; reusing a key with a different body returns 409 rather than applying the second request.

Request body

FieldTypeRequiredNotes
action"refill_due"yes`refill_due`: a fill held only `refill_too_soon` is decided again as if enough of the supply had been used.

Example response · 200

Response
{
  "data": {
    "id": "00000000-0000-4000-8000-00000000e001",
    "object": "sandbox_simulation",
    "kind": "case",
    "action": "example-action",
    "objectId": "example_objectId",
    "patientId": "00000000-0000-4000-8000-00000000e001",
    "actor": {
      "kind": "sandbox_test_clinician",
      "label": "example-label"
    },
    "result": {},
    "events": [
      {
        "id": "00000000-0000-4000-8000-00000000e001",
        "type": "example-type",
        "object": "example-object",
        "objectId": "example_objectId",
        "deliveryState": "example-delivery-state",
        "recordedAt": "2026-01-01T00:00:00.000Z"
      }
    ],
    "standardEvents": [
      "example-standard-events"
    ],
    "livemode": false,
    "simulatedAt": "2026-01-01T00:00:00.000Z",
    "replayed": false
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
422validation_failedThe simulation request is invalid.
403sandbox_simulation_onlySandbox simulation runs only on the developer sandbox, for a synthetic brand, with a sandbox key.
404patient_not_foundNo patient with that id is available to this key.
422simulation_action_invalidThat action is not one this object takes.
409idempotency_key_reusedThis Idempotency-Key was used for a different simulation.
429sandbox_simulation_rate_limitedAt most 600 sandbox simulations per brand per hour.
429sandbox_loop_rate_limitedAt most 60 sandbox loops (patients) per brand per hour.
503simulation_unavailableThe simulation could not run. Nothing changed; retry with the same Idempotency-Key.
404fill_not_foundNo fill with that id for this patient.
409fill_not_waiting_on_timeOnly a fill held refill_too_soon waits on time.
409fill_heldThe fill is held for another reason now.
409fill_refusedThe refill order could not be created.
413payload_too_largeThe request body exceeds 2048 bytes (2 KiB). Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API: the same status and code, with `request_id` `edge` and no `Request-Id` header.
400idempotency_key_requiredProvide a 16–255 character Idempotency-Key.
409idempotency_conflictThis Idempotency-Key was already used with different request parameters. Nothing was applied; use a new key.
409idempotency_in_progressA request with this Idempotency-Key is still being processed. Retry after `Retry-After`; nothing is applied twice.
503idempotency_unavailableThe idempotency record could not be checked. Nothing was processed; retry after `Retry-After`.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
POST/api/v1/sandbox/patients/{patientId}/notifications/simulatenotifications.ack

Sandbox: a notice goes unacknowledged past its deadline

A clinically required notice routed to you (or one requested now: `clinical.notification.requested`) passes its acknowledgment deadline; Ambition's content-free fallback becomes due and the notice sender sends it from your domain to the patient's verified contact (`clinical.notification.fallback_sent`). Sandbox only: a sandbox phi_server key, a synthetic brand and the developer sandbox deployment (any other answers 403 `sandbox_simulation_only`). The actor is synthetic and labelled; the commands, gates and events are the real ones, with `livemode: false`. 60 loops (patients) and 600 simulations per brand per hour. Idempotent: the same Idempotency-Key answers the first simulation.

Requires an Idempotency-Key header of 16–255 characters. Replays for 24 hours; reusing a key with a different body returns 409 rather than applying the second request.

Request body

FieldTypeRequiredNotes
action"notification_unacked"yes`notification_unacked`: the notice's acknowledgment deadline passes now; Ambition's content-free fallback becomes due.
notificationIdstringnoAn unacknowledged clinically required notice routed to you. Omit it to request a new one first.
templateKey"urgent_clinician_message" | "recall" | "safety_followup" | "rx_cancelled" | "visit_required" | "contact_verification_required"noWith no notificationId: the clinically required template to request (default `urgent_clinician_message`).
caseIdstringnoWith no notificationId: the patient's case the requested notice is about.

Example response · 200

Response
{
  "data": {
    "id": "00000000-0000-4000-8000-00000000e001",
    "object": "sandbox_simulation",
    "kind": "case",
    "action": "example-action",
    "objectId": "example_objectId",
    "patientId": "00000000-0000-4000-8000-00000000e001",
    "actor": {
      "kind": "sandbox_test_clinician",
      "label": "example-label"
    },
    "result": {},
    "events": [
      {
        "id": "00000000-0000-4000-8000-00000000e001",
        "type": "example-type",
        "object": "example-object",
        "objectId": "example_objectId",
        "deliveryState": "example-delivery-state",
        "recordedAt": "2026-01-01T00:00:00.000Z"
      }
    ],
    "standardEvents": [
      "example-standard-events"
    ],
    "livemode": false,
    "simulatedAt": "2026-01-01T00:00:00.000Z",
    "replayed": false
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
422validation_failedThe simulation request is invalid.
403sandbox_simulation_onlySandbox simulation runs only on the developer sandbox, for a synthetic brand, with a sandbox key.
404patient_not_foundNo patient with that id is available to this key.
422simulation_action_invalidThat action is not one this object takes.
409idempotency_key_reusedThis Idempotency-Key was used for a different simulation.
429sandbox_simulation_rate_limitedAt most 600 sandbox simulations per brand per hour.
429sandbox_loop_rate_limitedAt most 60 sandbox loops (patients) per brand per hour.
503simulation_unavailableThe simulation could not run. Nothing changed; retry with the same Idempotency-Key.
404notification_not_foundNo notice with that id for this patient.
404case_not_foundNo case with that id for this patient.
422template_not_clinically_requiredName a clinically required notice template; only those have a fallback.
409notification_acknowledgedThe notice was already acknowledged.
409notification_has_no_fallbackOnly an unacknowledged clinically required notice routed to you has a scheduled fallback.
413payload_too_largeThe request body exceeds 2048 bytes (2 KiB). Nothing was processed; send a smaller body. The edge may refuse it before it reaches the API: the same status and code, with `request_id` `edge` and no `Request-Id` header.
400idempotency_key_requiredProvide a 16–255 character Idempotency-Key.
409idempotency_conflictThis Idempotency-Key was already used with different request parameters. Nothing was applied; use a new key.
409idempotency_in_progressA request with this Idempotency-Key is still being processed. Retry after `Retry-After`; nothing is applied twice.
503idempotency_unavailableThe idempotency record could not be checked. Nothing was processed; retry after `Retry-After`.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
GET/api/v1/sandbox/patients/{patientId}/messagespatients.read

Sandbox: the email Ambition sent your sandbox patient

On the developer sandbox no email is delivered: contact codes (`POST /patients/{patientId}/contact-verifications`) and sign-in links (`POST /sign-in-links`) are caught by the sandbox mail catcher, and this reads them back so your integration can complete the loop. The newest 10 messages Ambition sent this patient, addressed to their email on record, newest first: subject, text (the code or the link is in it), sender and when it was sent. Only your own sandbox patient's mail; the catcher itself is never reachable from outside. Sandbox only: a sandbox phi_server key and a synthetic brand on the developer sandbox deployment (any other answers 403 `sandbox_simulation_only`). 503 `sandbox_mail_catcher_unavailable` when the deployment has no catcher.

Example response · 200

Response
{
  "data": [
    {
      "id": "example_id",
      "object": "sandbox_message",
      "subject": "example-subject",
      "text": "example-text",
      "from": {
        "name": "example-name",
        "address": "example-address"
      },
      "sentAt": "2026-01-01T00:00:00.000Z",
      "livemode": false
    }
  ],
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
400patient_context_requiredSend Ambition-Patient-Session and Ambition-Patient-Auth.
403browser_origin_refusedPatient endpoints are server-to-server only.
403phi_key_requiredPatient endpoints take a phi_server key.
403sandbox_simulation_onlySandbox simulation runs only on the developer sandbox, for a synthetic brand, with a sandbox key.
404patient_not_foundNo patient with that id is available to this key.
503sandbox_mail_catcher_unavailableThe sandbox mail catcher is not configured on this deployment or did not answer. Nothing was read; retry.
401patient_sign_in_unverifiedAmbition-Patient-Auth: otp was sent, but Ambition-Patient-Session is not the sessionReference of a current Ambition sign-in for this patient from this key (POST /sign-in-links/{signInLinkId}/confirm answers it; it lasts 24 hours, until signed out or the patient's email is replaced). otp is only valid with an Ambition sign-in session: sign the patient in again. Nothing was processed.
GET/api/v1/sandbox/certification

Sandbox: your integration certification checklist

The checklist required before live patient data access, computed from your brand's real sandbox activity: clinical receiver verified, signature checked (a mis-signed probe refused), render receipt accepted, consent and intake complete, identity verification, verified contact, labs (when a program requires them), brand release (when a program uses it), fills, renewals, refundExpected handled (merchant-of-record programs), policy_withdrawn handled (regulated programs) and idempotency keys used. Counts and times only; no patient. Any sandbox phi_server key; 403 `sandbox_simulation_only` otherwise.

Example response · 200

Response
{
  "data": {
    "object": "sandbox_certification",
    "environment": "sandbox",
    "livemode": false,
    "certified": false,
    "completeCount": 1,
    "requiredCount": 1,
    "sandboxPatients": 1,
    "items": [
      {
        "key": "receiver_verified",
        "state": "complete",
        "count": 1,
        "lastAt": "2026-01-01T00:00:00.000Z",
        "reason": "example-reason"
      }
    ],
    "evaluatedAt": "2026-01-01T00:00:00.000Z"
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
403phi_key_requiredOnly a phi_server key reads the certification.
403sandbox_simulation_onlySandbox simulation runs only on the developer sandbox, for a synthetic brand, with a sandbox key.
503certification_unavailableThe checklist could not be read. Nothing changed; retry.
POST/api/v1/sandbox/webhook-signature-probes

Sandbox: send your clinical receivers a mis-signed probe

Queues one signature probe to each verified clinical receiver. The probe is your receiver's own verification envelope (`webhook.verification`, a fresh challenge that completes nothing), sent twice: first signed wrongly on purpose, which your receiver must refuse with 401 or 403, then signed correctly, which it must answer with a 2xx. Both together complete `signature_checked`. A 2xx to the mis-signed copy fails it (`signature_not_checked`); any other answer is `probe_inconclusive` (408 and 429 are retried). At most one open probe per receiver and 20 per hour. Any sandbox phi_server key.

Example response · 202

Response
{
  "data": {
    "object": "signature_probe",
    "sent": 1,
    "alreadyOpen": 1,
    "receivers": 1,
    "livemode": false
  },
  "meta": {
    "requestId": "example_requestId",
    "apiVersion": "example-api-version",
    "environment": "example-environment"
  }
}

Errors

StatusCodeWhen
401unauthenticatedMissing or unrecognised credential.
403insufficient_scopeThe credential lacks the required scope.
429rate_limitedToo many requests, per network address and per API key. Retry after the interval in `Retry-After`; every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.
503rate_limit_unavailableRequest protection is temporarily unavailable. Nothing was processed; retry after `Retry-After`.
403phi_key_requiredOnly a phi_server key sends probes.
403sandbox_simulation_onlySandbox simulation runs only on the developer sandbox, for a synthetic brand, with a sandbox key.
409receiver_not_verifiedVerify a clinical receiver first; the probe goes only to verified receivers.
429signature_probe_rate_limitedAt most 20 signature probes per brand per hour.
503certification_unavailableThe probe could not be queued. Nothing changed; retry.

Webhook events

Register receivers per connection and environment in the console. Every delivery is signed (ambition-signature, verified as the webhooks guide shows), retried with backoff, and dead-lettered for an operator when it keeps failing. Deduplicate on ambition-event-id: it is the same on every attempt, at every receiver and in every replay.

Events a receiver can subscribe to
EventSent whenStatus
order.acknowledgedOrder acknowledgedPlanned: nothing sends it yet
order.acceptedOrder acceptedPlanned: nothing sends it yet
order.in_productionOrder in productionPlanned: nothing sends it yet
order.shippedOrder shippedPlanned: nothing sends it yet
order.deliveredOrder deliveredPlanned: nothing sends it yet
order.exceptionOrder exceptionPlanned: nothing sends it yet
payment.capturedA patient payment was capturedHas a producer; sent where webhooks run
payment.failedA patient payment failedHas a producer; sent where webhooks run
subscription.createdA patient subscription was createdHas a producer; sent where webhooks run
subscription.updatedA patient subscription changed statusHas a producer; sent where webhooks run
subscription.canceledA patient subscription was canceledHas a producer; sent where webhooks run
enrollment.createdAn enrollment was createdHas a producer; sent where webhooks run
enrollment.status_changedAn enrollment's coarse status changed. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.patient.createdPatient created. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.patient.updatedPatient updated. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.patient.deactivatedPatient deactivated. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.contact.verifiedContact verified by Ambition. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.identity_verification.completedIdentity verified. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.identity_verification.failedIdentity verification failed. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.identity_verification.review_requiredIdentity verification needs review. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.referral.acceptedThe medical group accepted a referral. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.referral.declinedThe medical group declined a referral. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.referral.expiredA referral expired without its next step. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.document.availableA patient document is available. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.consent.recordedA consent was recorded. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.consent.withdrawnA consent was withdrawn. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.intake.submittedAn intake was submitted. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.intake.identity_requiredA case waits for identity verification. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.intake.queuedA waiting case entered the clinician queue. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.intake.expiredA waiting case closed unverified. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.safety_report.receivedA safety report was received. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.fill.requestedA brand-requested fill was sent toward the pharmacy. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.fill.heldA brand-requested fill is held. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.fill.transmittedA fill reached the pharmacy. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.prescription.refills_exhaustedA prescription has no refills left. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.renewal.requestedA renewal request opened a case. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.checkin.dueA therapy check-in is due. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.monitoring.labs_dueMonitoring labs are due. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.case.createdA case was created. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.case.assignedA clinician was assigned. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.case.needs_infoThe clinician needs more information. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.case.info_receivedThe patient's answer reached the clinician. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.case.labs_requiredThe clinician needs labs. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.case.approvedThe clinician approved. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.case.declinedThe clinician declined. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.case.cancelledThe request was cancelled. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.case.closedThe case was closed by the care team. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.prescription.signedA prescription was signed. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.prescription.heldA prescription is held. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.prescription.releasedA prescription hold was released. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.prescription.cancelledA prescription was cancelled. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.order.createdA pharmacy order was created. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.order.acknowledgedThe pharmacy acknowledged the order. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.order.acceptedThe pharmacy accepted the order. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.order.in_productionThe order is being prepared. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.order.shippedThe order shipped. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.order.deliveredThe order was delivered. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.order.exceptionThe order has a problem. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.message.createdA message was posted. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.lab.result_receivedA lab result was received. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.lab.criticalA lab result has a critical value. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.lab.reviewedA lab result was signed off. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.lab.order_signedA lab order was signed. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.lab.order_updatedA lab order's status changed. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.lab.releasedA lab result was released to the patient. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.notification.requestedA care notice is due to your patient. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
clinical.notification.fallback_sentAmbition sent your patient a content-free fallback notice. Needs the business associate agreement and downstream-systems attestationHas a producer; sent where webhooks run
formulary.policy_changedA regulatory policy your catalog relies on changedHas a producer; sent where webhooks run
formulary.state_availability_changedA program's State availability changedHas a producer; sent where webhooks run

The webhooks guide has the signed string, verifiers in Python, Go, Ruby and Node, the retry schedule and every event’s payload.

A commerce or enrollment event’s data holds only ids (with the enrollment id and your externalReference), the Stripe object id, the amount in cents, the currency, a coarse status and the time it happened. It never carries a patient’s name or contact details, a product, a program or anything clinical. payloadDigest is the SHA-256 of that payload as recorded.

Versioning and deprecation

Additive changes — a new endpoint, a new optional field, a new enum member in a response — ship without a version bump. A breaking change is announced on the changelog, marked deprecated in the specification with a Sunset date at least 180 days out, and only then removed.

Errors always have the same shape: an error object with a stable code, a human message, and optional per-field detail. Branch on code, never on message.