Natzar Partner API Reference - v1.0.0
    Preparing search index...

    Getting started

    This walkthrough takes you from an empty tenant to a completed async consult: provision a patient and a physician, open a consult, exchange messages, and resolve it. Every request shown is real — the routes, bodies, and responses match the typed contract ( Endpoints) exactly.

    • Your API key (pp_live_… in production, pp_test_… elsewhere) and your assigned API host. Keys are server-side secrets — see the Authentication guide.
    • The examples use two shell variables:
    export NATZAR_API="https://<your-assigned-api-host>"
    export NATZAR_KEY="pp_test_..."

    All routes live under /v1 and every request carries Authorization: Bearer $NATZAR_KEY.

    POST /v1/patients is an idempotent upsert keyed on your externalId — run it as many times as you like; repeats update the mutable fields, never duplicate. phone must be E.164 and is the patient's identity key (immutable after creation).

    curl -s -X POST "$NATZAR_API/v1/patients" \
    -H "Authorization: Bearer $NATZAR_KEY" \
    -H "Content-Type: application/json" \
    -d '{
    "externalId": "pt-1001",
    "phone": "+15551234567",
    "email": "jane.doe@example.com",
    "givenName": "Jane",
    "familyName": "Doe",
    "birthdate": "1990-04-17",
    "sex": "female",
    "lang": "en_US"
    }'

    Response (200):

    {
    "patient": {
    "id": "aB3xK9mQpZ4r",
    "externalId": "pt-1001",
    "phone": "+15551234567",
    "email": "jane.doe@example.com",
    "givenName": "Jane",
    "familyName": "Doe",
    "birthdate": "1990-04-17",
    "sex": "female",
    "lang": "en_US",
    "messageChannel": "partner",
    "createdAt": "2026-08-13T09:30:00.000Z",
    "updatedAt": "2026-08-13T09:30:00.000Z"
    },
    "created": true
    }

    Note messageChannel: "partner": API-provisioned patients are never contacted over WhatsApp/SMS — every message reaches them through your integration (webhooks, the messages endpoint, or the embed widgets).

    You can address this patient by our id or your externalId interchangeably wherever a consult is created.

    POST /v1/physicians creates the physician profile plus a backing portal account keyed by email. An identical repeat (same externalId + same email) is idempotent — 200, names refreshed — so retries are safe; repeating an externalId with a different email yields 409 external_id_conflict, and a new externalId with an already-registered email yields 409 email_in_use. By default no email is sent; pass sendPortalInvite: true (or call POST /v1/physicians/{id}/portal-invite later) if the physician should sign in to the Natzar portal.

    curl -s -X POST "$NATZAR_API/v1/physicians" \
    -H "Authorization: Bearer $NATZAR_KEY" \
    -H "Content-Type: application/json" \
    -d '{
    "externalId": "dr-42",
    "email": "s.chen@partner-clinic.example",
    "givenName": "Sarah",
    "familyName": "Chen",
    "sendPortalInvite": false
    }'

    The response's physician.id is the value you will pass as physicianId on reply/claim/takeover/resolve calls. New physicians default to asyncAvailable: true — they participate in automatic assignment immediately; toggle via POST /v1/physicians/{id}/availability.

    consent declares who collects the patient's consent:

    • "collected" — you already collected it in your own UX; the consult is queued/assigned immediately.
    • "embed" — the <natzar-async> widget collects it; the create response carries a sessionToken (see the Embed guide).
    curl -s -X POST "$NATZAR_API/v1/async-consults" \
    -H "Authorization: Bearer $NATZAR_KEY" \
    -H "Content-Type: application/json" \
    -d '{
    "externalPatientId": "pt-1001",
    "context": "34yo female, 3 days of sore throat and low-grade fever, no strep exposure known.",
    "consent": "collected"
    }'

    Response (200, physician capacity available):

    {
    "consult": {
    "id": "kQ7wN2pT9xLm",
    "patientId": "aB3xK9mQpZ4r",
    "externalPatientId": "pt-1001",
    "status": "active",
    "context": "34yo female, 3 days of sore throat and low-grade fever, no strep exposure known.",
    "assignedPhysicianId": "e5f0c8aa-…",
    "assignedPhysicianExternalId": "dr-42",
    "assignedPhysicianName": "Dr. Sarah Chen",
    "rateable": false,
    "origin": "partner",
    "createdAt": "2026-08-13T09:31:00.000Z",
    "sentAt": "2026-08-13T09:31:00.000Z",
    "lastActivityAt": "2026-08-13T09:31:00.000Z"
    }
    }

    If no physician had capacity, status is "queued" — assignment happens automatically the moment one becomes available (you'll get an async_consult.assigned webhook). One open thread per patient is an invariant: creating a second consult while one is open yields 409 has_open_thread with the existing consult's id in error.details.

    4. Post a patient message

    Your patient UI posts patient-authored messages. The response is a 202 — the message is validated and enqueued into the per-patient FIFO pipeline, and will appear on the transcript in order (see Errors & pagination for the full 202 semantics).

    curl -s -X POST "$NATZAR_API/v1/async-consults/kQ7wN2pT9xLm/messages" \
    -H "Authorization: Bearer $NATZAR_KEY" \
    -H "Content-Type: application/json" \
    -d '{"text": "The fever is up to 38.5 tonight, should I be worried?"}'
    {"accepted": true, "messageId": "mA1bC2dE3fG4"}
    

    messageId is the id the message will carry on the transcript and in the async_consult.message webhook.

    5. Post the physician's reply

    If your clinicians work in your own UI (headless integration), post their replies with the assigned physician's id. physicianId must be the consult's current assignee — otherwise 409 not_assigned (assignment can move via SLA reassignment; re-read the consult).

    curl -s -X POST "$NATZAR_API/v1/async-consults/kQ7wN2pT9xLm/replies" \
    -H "Authorization: Bearer $NATZAR_KEY" \
    -H "Content-Type: application/json" \
    -d '{
    "physicianId": "e5f0c8aa-…",
    "text": "38.5 with a sore throat is usually viral. Alternate acetaminophen and fluids tonight; if it passes 39.5 or you develop trouble swallowing, message me right away."
    }'

    Also 202 with a messageId. (Physicians who use the Natzar portal instead reply there — you don't call this endpoint for them; their replies still show up in the transcript and webhooks identically.)

    curl -s "$NATZAR_API/v1/async-consults/kQ7wN2pT9xLm/messages?limit=50" \
    -H "Authorization: Bearer $NATZAR_KEY"

    Returns a page of messages, oldest first, including system lifecycle notices (assignment, warnings, close notices) — the transcript is complete. Lifecycle-generated messages carry a classification like "async:assigned" so a headless UI can render semantic chips instead of the localized body text. Attachment urls in the response are short-lived presigned links — fetch promptly, never persist the URL.

    The assigned physician marks the consult resolved; the patient gets a bounded window to reply (a reply reopens the thread to active, silence closes it as accepted — you'll see async_consult.closed with closedReason: "accept_timeout").

    curl -s -X POST "$NATZAR_API/v1/async-consults/kQ7wN2pT9xLm/resolve" \
    -H "Authorization: Bearer $NATZAR_KEY" \
    -H "Content-Type: application/json" \
    -d '{"physicianId": "e5f0c8aa-…", "note": "Supportive care; message again if symptoms worsen."}'

    The response's consult.status is "resolve_requested".

    • Webhooks — don't poll; receive every lifecycle transition and message as a signed POST: Webhooks.
    • Embedded patient UI — skip building a chat/video UI and drop in <natzar-async> / <natzar-telehealth>: Embed.
    • Telehealth — POST /v1/telehealth-consults works just like step 3 (minus consent); the patient joins via the embed, the physician takes the call in the Natzar portal, and a recording + transcript arrive via telehealth.recording_ready.
    • Attachments — presign slots with POST /v1/attachments/upload-urls, PUT the bytes, then reference the returned stagingKey in a message or reply.
    • Error handling and paging — Errors & pagination.

    Everything above books nothing — the patient joins a queue and sees whoever is free. To offer an appointment at a time they choose, with a named physician, see Scheduled consultations.