Documentation

API documentation

Two endpoints verify a prescription; two more handle the patient's permission. Everything else is bookkeeping. Call from your backend only.

v1

Downloads

Ready-made files for your developers. Import the Postman collection, set its apiKey variable to a key from the API keys page, and every call below is one click.

Overview

The ScanSureX Prescription Verification API tells an online pharmacy whether a prescription a customer uploaded is genuine: issued through ScanSureX (the doctor portal or Instant e-Prescription), signed by a verified doctor, unaltered, still valid and not cancelled. It returns the prescription's content when the patient's permission is established.

Base URLhttps://api.scansurex.com/api/v1/online-pharmacy
FormatJSON over HTTPS; uploads as multipart/form-data
AuthBearer API key (sandbox or production)
Pricing₹2.00 per completed verification, from prepaid credits

Verification is not dispensing authority. pharmacist_review_required is always true and dispensing_authorised always false: your registered pharmacist still reviews the prescription before anything is supplied.

Integration in four steps

  1. Create a sandbox key and download the sample prescriptions.
  2. Post a sample to /prescriptions/verify/upload; branch on verification_status.
  3. For photos and typed references, add the two consent calls so the patient can release details.
  4. Get approved, add credits, swap in a production key.

Authentication

Every request carries your key as a bearer token. Sandbox keys start with ssx_test_, production keys with ssx_live_; the prefix alone decides the environment, and a sandbox key can never see a real prescription or vice versa.

Authorization: Bearer ssx_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

A missing, revoked, expired or wrong-environment key gets one and the same 401 API_KEY_INVALID, so a response never confirms that a guessed handle exists. Production keys stop working the moment a partner is suspended.

Sandbox

The sandbox is the same service with a separate issuer (“Dr Sandbox Example”) and a fixed set of signed prescriptions. Nothing in it is a real patient and nothing in it is charged. Sample files for every outcome are available to sandbox keys:

curl -H "Authorization: Bearer ssx_test_…" \
  -o valid-email.pdf https://api.scansurex.com/api/v1/online-pharmacy/sandbox/samples/valid-email

# kinds: valid-email · valid-portal · valid-photo · expired · cancelled · tampered

Or skip curl entirely: the Sandbox page downloads samples and runs a verification from the browser, showing the exact JSON.

API keys

Keys are created on the API keys page and shown in full exactly once; we keep a one-way hash. Give each deployment its own key so one can be rotated without touching the others, and set an expiry for anything temporary.

  • Sandbox keys are available from the moment you register.
  • Production keys require an approved application and production access enabled by ScanSureX.
  • Revocation is immediate and permanent. Create the replacement first.

GET /api-keys lists your keys (prefixes only) for reconciliation.

Verify an uploaded file

POST /prescriptions/verify/upload — multipart field file; PDF, JPEG or PNG up to 10 MB. The service finds the ScanSureX QR, re-checks the doctor's Ed25519 signature against the stored original, and for PDFs compares the text layer with the signed content line by line.

curl -X POST https://api.scansurex.com/api/v1/online-pharmacy/prescriptions/verify/upload \
  -H "Authorization: Bearer ssx_test_…" \
  -H "Idempotency-Key: order-8812-rx-1" \
  -F "[email protected]"

Response (201)

{
  "verification_id": "VER-2026-7F3A91C2",
  "environment": "sandbox",
  "verification_status": "DOCUMENT_VERIFIED",
  "reason_code": "CONTENT_MATCHES_SIGNED",
  "prescription_id": "SSX-RX-HGWYKH",
  "prescription_status": "ISSUED",
  "signature_valid": true,
  "document_integrity_verified": true,
  "doctor_registration_status": "VERIFIED_AT_ISSUANCE",
  "consent_required": false,
  "consent_method": "QR_TOKEN",
  "billable": true,
  "charge_amount": "2.00",
  "currency": "INR",
  "pharmacist_review_required": true,
  "dispensing_authorised": false,
  "prescription": {
    "prescription_ref": "SSX-RX-HGWYKH",
    "issued_at": "2026-11-04T08:12:00.000Z",
    "expires_at": "2027-02-02T08:12:00.000Z",
    "status": "ISSUED",
    "diagnosis": "…",
    "doctor": { "name": "…", "registration_no": "…", "council": "…", "speciality": "…", "registry_attestation": "ABDM_HPR" },
    "patient": { "name": "…", "date_of_birth": null, "gender": null, "allergies": [] },
    "items": [{ "name": "…", "strength": "500 mg", "dosage": "1 tablet", "frequency": "Twice daily", "duration_days": 5, "quantity": 10, "schedule": "OTC" }],
    "signature": { "type": "PLATFORM_ED25519", "document_hash": "…", "verified": true }
  },
  "verified_at": "2026-11-04T09:00:12.000Z"
}

The prescription object is present only when permission is established — a ScanSureX token in the file, or a confirmed consent code. Until then every other field is still returned, so you know the document is genuine before you ask the patient for anything.

Verify by reference

POST /prescriptions/verify/reference — when your software already decoded the QR (…/rx#TOKEN or SCANSUREX-VERIFY|ref|TOKEN) or the customer typed the reference printed on the prescription. A token proves permission; a bare reference proves only authenticity.

curl -X POST https://api.scansurex.com/api/v1/online-pharmacy/prescriptions/verify/reference \
  -H "Authorization: Bearer ssx_test_…" -H "Content-Type: application/json" \
  -d '{"reference":"SSX-RX-HGWYKH","token":"<secret from the QR, optional>"}'

Results

verification_statusMeaningBillableWhat to do
DOCUMENT_VERIFIEDA ScanSureX PDF whose content matches the signed original.YesProceed to pharmacist review with the returned details.
REFERENCE_VERIFIEDThe reference is a genuine, valid prescription; the file itself (a photo, a typed number) is not proven.YesRequest consent if details are needed; have the pharmacist compare the photo.
REJECTEDSignature invalid, cancelled, expired, altered, or no ScanSureX code in a PDF.YesDo not dispense against it. Show reason_code to the customer.
REVIEW_REQUIREDCould not decide automatically — typically no readable QR on a photo.NoAsk for a clearer photo or the reference number.
VERIFICATION_UNAVAILABLEOur failure. Not a statement about the prescription.NoRetry with the same Idempotency-Key.

Common reason codes

CONTENT_MATCHES_SIGNED · TOKEN_VALID · REFERENCE_VALID · SIGNATURE_INVALID · DOCUMENT_ALTERED · PRESCRIPTION_EXPIRED · PRESCRIPTION_CANCELLED · PRESCRIPTION_FROZEN · NO_SCANSUREX_CODE · QR_UNREADABLE · UNKNOWN_REFERENCE

A ScanSureX QR token inside the uploaded file (the emailed PDF or a portal download) is the patient handing you their prescription, and details are returned at once. A photo of the paper or a typed reference proves authenticity only. To receive the details, ask the patient for a one-time code:

POST https://api.scansurex.com/api/v1/online-pharmacy/verifications/VER-2026-7F3A91C2/consent/request
→ 200 { "consent_required": true, "consent_id": "ck…", "channel": "email", "sent_to": "r***@gmail.com", "expires_at": "…" }

POST https://api.scansurex.com/api/v1/online-pharmacy/verifications/VER-2026-7F3A91C2/consent/confirm
     { "code": "482913" }
→ 200 the full Verification with "prescription" populated and "consent_method": "OTP"
  • 422 — ScanSureX holds no contact for this patient; ask them for the emailed PDF instead.
  • 403 wrong code · 410 no live code, request a new one · 429 too many wrong codes.
  • Consent calls are not charged; only the verification that produced the verification_id was.

Fetch a verification

GET /verifications/{id} returns the stored result exactly as it was given, including details once consent is confirmed. GET /verifications?limit=50 lists recent ones; GET /usage?from&to totals charges for a period — the same numbers as the Usage page.

Idempotency

Send an Idempotency-Key header (≤128 chars) unique to each logical request — your order id plus a suffix works well. A retry with the same key and payload returns the stored result with X-Idempotent-Replay: true and status 200, and is never charged twice. The same key with a different payload is refused with 409.

Errors

{ "error": "API_KEY_INVALID", "message": "…" }
HTTPerrorWhen
400FILE_REQUIREDNo file part in the multipart body.
401API_KEY_INVALIDMissing, unknown, revoked, expired or wrong-environment key.
402INSUFFICIENT_CREDITSProduction: balance below the price. No verification ran; nothing was charged. Response carries wallet.balance.
403SANDBOX_ONLYSamples requested with a production key.
404VERIFICATION_NOT_FOUNDNot a verification of this partner.
409IDEMPOTENCY_CONFLICTKey reused with a different payload.
413FILE_TOO_LARGEOver 10 MB.
415UNSUPPORTED_MEDIA_TYPENot a PDF, JPEG or PNG (checked by content, not by name).
429RATE_LIMITEDPer-key limit exceeded; honour Retry-After.
422VALIDATION_ERRORMalformed JSON body; details names the field.

Rate limits

Each key is limited per minute (service default; raised on request). A 429 carries Retry-After. Verifications take 1–4 s for PDFs and up to ~8 s for photos; set client timeouts of at least 30 s and retry 5xx with the same Idempotency-Key.

Pricing & billing

Usage-based, prepaid, no subscription. Your price is ₹2.00 per completed verification. Each production call reserves the price before classification and then charges it (DOCUMENT_VERIFIED, REFERENCE_VERIFIED, REJECTED) or releases it (REVIEW_REQUIRED, VERIFICATION_UNAVAILABLE). Sandbox calls and idempotent replays are never charged.

  • When the balance cannot cover one verification the API answers 402 INSUFFICIENT_CREDITS and nothing runs — top up on the Wallet page.
  • Top-ups include 18% GST; credits are the net amount and a GST invoice is issued for each payment.
  • billable and charge_amount on every response say what that call cost; Verifications exports the same as CSV.

Security

  • Keys never reach a browser. Call from your server; proxy through it if your checkout runs client-side.
  • Store keys in a secrets manager. Rotate by creating a new key, deploying it, then revoking the old one.
  • Prescription details are personal health data: keep them only as long as the order needs them and only for fulfilling it. Consent is the patient's, not yours to reuse.
  • Every verification is logged with your key id, the result and the charge; the same record is what we invoice against.
  • All traffic is TLS. Signatures are Ed25519 over the document hash; we re-verify on every call rather than trusting a stored flag.

Troubleshooting

401 with a key that works in the sandboxYou are using an ssx_test_ key against a real prescription, or production is not yet enabled for your account.
REVIEW_REQUIRED on every photoThe QR is too small or blurred. Ask for the emailed PDF, or have the customer type the SSX-RX reference and use /verify/reference.
REJECTED · DOCUMENT_ALTERED on a PDF you believe is genuineThe PDF was re-saved or edited (even “print to PDF” rewrites the text layer). Ask for the original attachment.
402 in productionCredits are exhausted. Top up; the Wallet page shows the low-balance threshold you can set an alert on.
Two charges for one orderSend an Idempotency-Key. Retries without one are separate verifications.
No consent email arrives422 means we have no contact on file. Otherwise codes expire in 10 minutes — request a new one.