Skip to content

Test mode

The sandbox, the simulated clinician and pharmacy, and how to drive a whole consult without one.

A cuvo_sk_test_ key reaches a sandbox. It holds only the data you invented for it, it is scoped to your integration, and every row is deleted 30 days after it is written. Do not send real patient information to test mode.

Test mode is not a mock. The same routes, the same validation, the same problem bodies, the same events, the same signed webhook deliveries. What differs is that behind the seam there is no chart and no pharmacy, so this API gives you the controls to play both.

The sandbox controls

Everything under /v1/test requires the test:manage scope and is refused outright to a live credential with 403 forbidden. There is no way to reach these from production, which is what keeps a simulated approval out of a real chart.

OperationWhat it plays
POST /v1/test/cases/{id}/decisionThe clinician approving, declining, or asking a question
POST /v1/test/cases/{id}/clinician_messageThe clinician replying on the case thread
POST /v1/test/cases/{id}/shipThe pharmacy shipping, with optional tracking
POST /v1/test/cases/{id}/deliverThe parcel arriving
POST /v1/test/cases/{id}/blockThe order stopping, with a reason
POST /v1/test/cases/{id}/failThe order failing for good
POST /v1/test/visits/{id}/startThe visit call beginning
POST /v1/test/visits/{id}/completeThe clinician completing the visit
POST /v1/test/visits/{id}/no_showThe patient never arriving
POST /v1/test/webhooks/triggerAny event type, against a fabricated resource
PATCH /v1/test/autopilotThe timers below
POST /v1/test/resetWiping the sandbox

Deciding a case

An approval carries the plan the clinician settled on, which does not have to match what was requested. A line with dispensable: false is recorded for the record and will never be filled.

curl https://api.cuvo.co/v1/test/cases/case_7d1e4b2a/decision \
  -H "Authorization: Bearer cuvo_sk_test_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: decide-case-ada-2026-09-08" \
  -d '{
    "decision": "approve",
    "plan": [
      {
        "medication_id": "semaglutide-0-25mg",
        "quantity": 4,
        "refills": 0,
        "days_supply": 28,
        "directions": "Inject 0.25 mg subcutaneously once weekly."
      }
    ]
  }'

A decline takes a reason, which is written to the case and is safe to show the patient. A needs_info decision takes a target of patient or integration and the question being asked, and moves the case to waiting_on_patient or waiting_on_integration.

Autopilot

Autopilot is off by default. Turn it on and the sandbox approves a case on its own after approve_after_s seconds, then ships and delivers its order after ship_after_s each. Both are seconds, 0 to 3600, and null turns that timer off. A background tick runs every minute, so a timer fires on the next tick after it comes due.

curl -X PATCH https://api.cuvo.co/v1/test/autopilot \
  -H "Authorization: Bearer cuvo_sk_test_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: autopilot-demo-2026-09-08" \
  -d '{"approve_after_s": 30, "ship_after_s": 60}'

Use it for a demo that runs untouched. Turn it off when you are testing a branch that depends on a case sitting still. Nothing advances a live case on a timer.

Proving a receiver

POST /v1/test/webhooks/trigger emits a real event of a type you choose and schedules real, signed deliveries for it, so you can prove your receiver before a case exists. Name an endpoint to send to one endpoint only.

Starting over

POST /v1/test/reset removes this integration's sandbox rows and tells you how many went.

{ "reset": true, "resources_deleted": 47 }

It does not touch your integration, your credentials or your webhook endpoints.

What test mode cannot tell you

DELETE /v1/files exists in test mode and has no live counterpart, so a live delete answers 501 live_not_available. It is the one operation left that does: a document filed against a chart is part of a clinical record, and removing one is a decision the clinic makes in its own console. The honest answer rather than a permission error, and the reference lists every operation with the modes it serves.

A sandbox export is worth calling out the other way. It assembles a real bundle out of the fixtures you made, at the same /v1/exports/{id}/download link live mode hands out, so the code that collects it is the code you ship. The one difference is that a sandbox document carries no link to its bytes: the sandbox accepts uploads and serves none back.