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.
https://api.scansurex.com/api/v1/online-pharmacyVerification 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
- Create a sandbox key and download the sample prescriptions.
- Post a sample to
/prescriptions/verify/upload; branch onverification_status. - For photos and typed references, add the two consent calls so the patient can release details.
- 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_status | Meaning | Billable | What to do |
|---|---|---|---|
| DOCUMENT_VERIFIED | A ScanSureX PDF whose content matches the signed original. | Yes | Proceed to pharmacist review with the returned details. |
| REFERENCE_VERIFIED | The reference is a genuine, valid prescription; the file itself (a photo, a typed number) is not proven. | Yes | Request consent if details are needed; have the pharmacist compare the photo. |
| REJECTED | Signature invalid, cancelled, expired, altered, or no ScanSureX code in a PDF. | Yes | Do not dispense against it. Show reason_code to the customer. |
| REVIEW_REQUIRED | Could not decide automatically — typically no readable QR on a photo. | No | Ask for a clearer photo or the reference number. |
| VERIFICATION_UNAVAILABLE | Our failure. Not a statement about the prescription. | No | Retry 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
Patient consent
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.403wrong code ·410no live code, request a new one ·429too many wrong codes.- Consent calls are not charged; only the verification that produced the
verification_idwas.
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": "…" }| HTTP | error | When |
|---|---|---|
| 400 | FILE_REQUIRED | No file part in the multipart body. |
| 401 | API_KEY_INVALID | Missing, unknown, revoked, expired or wrong-environment key. |
| 402 | INSUFFICIENT_CREDITS | Production: balance below the price. No verification ran; nothing was charged. Response carries wallet.balance. |
| 403 | SANDBOX_ONLY | Samples requested with a production key. |
| 404 | VERIFICATION_NOT_FOUND | Not a verification of this partner. |
| 409 | IDEMPOTENCY_CONFLICT | Key reused with a different payload. |
| 413 | FILE_TOO_LARGE | Over 10 MB. |
| 415 | UNSUPPORTED_MEDIA_TYPE | Not a PDF, JPEG or PNG (checked by content, not by name). |
| 429 | RATE_LIMITED | Per-key limit exceeded; honour Retry-After. |
| 422 | VALIDATION_ERROR | Malformed 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_CREDITSand 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.
billableandcharge_amounton 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.