Start building

Getting started

Quickstart

From a sandbox key to a test patient a simulated clinician approved, with the enrollment.status_changed webhook received.

Open the Developer Center

Never send patient information to a sandbox key.

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
  • 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).

Before you start

This is hosted mode. Everything below runs on the developer sandbox deployment, on synthetic data. Your site sells; your Ambition-hosted care site does care. Your server never sends or receives an intake answer, a clinical reason, a medication or a dose.

  • A sandbox brand with a program that has an approved offer, and a care host on the sandbox deployment.
  • A sandbox key with enrollments.write and enrollments.read, created by someone with minimum-necessary patient access.
  • An https receiver your server controls (a tunnel works) for the last step.
The variables the examples read
VariableValue
AMBITION_API_BASE_URLThis deployment's origin followed by /api/v1 (the sandbox's alias is https://aws-nonprod.ambitionhealth.co/api/v1). The canonical sandbox base URL, https://sandbox.api.ambitionhealth.co/v1, is not serving yet.
AMBITION_SANDBOX_API_KEYA sandbox key (ak_sandbox_…) with enrollments.write and enrollments.read.
AMBITION_BRAND_HANDLEYour brand's public handle (its slug).
  1. Create a sandbox key

    In the Developer Center's Keys tab, create a sandbox key with enrollments.write and enrollments.read. A key can only carry what its creator can already see: both scopes need minimum-necessary patient access, so a developer role without it asks the brand's tenant owner to create the key. The secret is shown once; keep it on your server. A sandbox key works only on the sandbox deployment that issued it.

    On this deployment: Not available in this environment

  2. Read your public catalog

    The catalog needs no key, so your site can fetch it from the browser. Each program has an opaque handle (prg_…), the approved offer exactly as the care host bills it, and the digest of the disclosure your page must show verbatim. Check availability for the person's state before you offer a Start button.

    GET /catalog/{brand}/programs · GET /catalog/{brand}/programs/{handle}/availability · GET /catalog/{brand}/disclosures

    On this deployment: Not available in this environment

    curl "$AMBITION_API_BASE_URL/catalog/$AMBITION_BRAND_HANDLE/programs"
    curl "$AMBITION_API_BASE_URL/catalog/$AMBITION_BRAND_HANDLE/programs/$PROGRAM_HANDLE/availability?state=AZ"
    curl "$AMBITION_API_BASE_URL/catalog/$AMBITION_BRAND_HANDLE/disclosures"
  3. Create an enrollment from your server

    Your backend creates the enrollment for your own customer, with your own reference and an Idempotency-Key. Send no health information: the API refuses any field it does not list. Supply an email you can read: only the person who proves it can redeem the link. A retry with the same key returns the same enrollment with a fresh launch link.

    POST /enrollments

    On this deployment: Not available in this environment

    curl -X POST "$AMBITION_API_BASE_URL/enrollments" \
      -H "authorization: Bearer $AMBITION_SANDBOX_API_KEY" \
      -H "content-type: application/json" \
      -H "idempotency-key: $(uuidgen)" \
      -d '{"programHandle":"'"$PROGRAM_HANDLE"'","state":"AZ","externalReference":"cus_test_001","email":"you+patient1@example.com"}'
  4. Send the person to the launch link

    Redirect the browser to data.launchUrl from the answer, a top-level navigation, never a frame. The link is single use and expires after 30 minutes. It does not sign anyone in: your care host moves the token into a cookie, removes it from the address bar and asks for a one-time email code. Do not log, store or forward it. The program's self-serve enrollment must be on: while it is off (the default for the invite-only pilot) POST /enrollments answers 422 program_self_enrollment_closed and issues no link.

    On this deployment: Not open yet. Only for a program whose self-serve enrollment a program manager has turned on in the console (off by default while enrollment is invite-only).

  5. Complete the test patient on your sandbox care host

    As the test patient, enter the code sent to the email you supplied (use an inbox you can read), create the account, consent, answer the intake and save Stripe's test card. All of it happens on your care host, never on your site. The enrollment then reads account_verified or later.

    On this deployment: Not open yet. Only for a program whose self-serve enrollment a program manager has turned on in the console (off by default while enrollment is invite-only).

  6. Simulate the clinician's approval

    The sandbox has no clinician, so you ask it to stand in for one. A sandbox key only: a live key is refused. Asking again for the status the enrollment already holds changes nothing and sends no webhook.

    POST /sandbox/enrollments/{id}/simulate · GET /enrollments/{id}

    On this deployment: Not available in this environment

    curl -X POST "$AMBITION_API_BASE_URL/sandbox/enrollments/$ENROLLMENT_ID/simulate" \
      -H "authorization: Bearer $AMBITION_SANDBOX_API_KEY" \
      -H "content-type: application/json" \
      -d '{"outcome":"approved"}'
    
    curl "$AMBITION_API_BASE_URL/enrollments/$ENROLLMENT_ID" \
      -H "authorization: Bearer $AMBITION_SANDBOX_API_KEY"
  7. Receive enrollment.status_changed

    Register an https receiver on your developer connection (Integrations) subscribed to enrollment.status_changed, and verify every delivery's signature before you act on it. Coarse status is treated as health information: the event is produced and sent only while your business associate agreement and downstream-systems attestation are in force and the person who registered the receiver holds minimum-necessary patient access. Without them the enrollment reads in_progress and no status event is sent.

    On this deployment: Sandbox only

    // ambition-webhook.mjs is the Node.js verifier on the webhooks page
    // (/docs/api/webhooks#verify-node): standard library only.
    import { verifyDelivery } from "./ambition-webhook.mjs";
    
    export async function POST(request) {
      let event;
      try {
        event = verifyDelivery({
          rawBody: await request.text(),
          eventId: request.headers.get("ambition-event-id"),
          timestamp: request.headers.get("ambition-timestamp"),
          signature: request.headers.get("ambition-signature"),
          secret: process.env.AMBITION_WEBHOOK_SECRET,
        });
      } catch {
        return new Response("invalid signature", { status: 401 });
      }
      // Record ambition-event-id before acting; answer 2xx to a duplicate.
      if (event.type === "enrollment.status_changed") {
        // event.data: enrollmentId, externalReference, status, occurredAt.
      }
      return new Response(null, { status: 204 });
    }