Veritura veritura Docs
veritura.co Open workspace

Getting started

Errors & rate limits

Every refusal is a plain sentence that says what happened and what fixes it. Errors are JSON with a single envelope; machine-readable codes appear where a program should branch.


The error envelope#

{
  "error": "More than one lot matches that identity. Provide lot_id.",
  "code": "ambiguous_lot",          // present when a program should branch on it
  "candidateLotIds": ["LOT-…", "LOT-…"]  // extra fields when they help fix the request
}

Status codes#

StatusWhenExample (verbatim)
400The request is invalid as sent.order_refs rejected: an entry looks like patient information. Use opaque references (order or PO numbers), never patient information. (phi_in_order_refs)
401Missing or unrecognised API key.A valid API key is required: Authorization: Bearer vk_live_... (or vk_test_...).
404The referenced record does not exist for this account: an unknown lot_id or batch_id.,
409The request is coherent but conflicts with the record.More than one lot matches that identity. Provide lot_id. (ambiguous_lot)
429Rate or concurrency limit reached.Rate limit exceeded.

Rate limits#

Each API key has its own allowance on each endpoint, per minute, wherever it calls from. Beyond it the answer is 429, "code": "rate_limited", with Retry-After, retryAfterSeconds in the body, and X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset headers. A second, per-address ceiling of the same size also applies on each endpoint.

EndpointPaid plan, per key per minuteSupported API automation (Operations), per key per minute
POST /v1/check6001,200
POST /v1/passports6001,200
POST /v1/seal-batches6001,200
GET /v1/seal-batches/<id>/labels6001,200
POST /v1/cleargate6001,200
POST /v1/cleargate-batch6001,200
POST /v1/eligibility6001,200
GET /v1/network/source6001,200
GET /v1/network/directory6001,200
POST /v1/network/route6001,200
GET /v1/pharmacies, /v1/suppliers, /v1/lookups, /v1/watches, /v1/exposure300 per workspace free, 600 on a paid plan, 1,200 with supported API automation; counted apart for test and live keys

Release refusal codes#

Passport issuance enforces the release rules on every path. These are the codes POST /v1/passports can answer, with 409 unless stated; each refusal's sentence names its fix. Before a purchased lot issues anything: it is cleared, carries no hold or recall, a certificate has been read against it, and a named person has reviewed it.

CodeMeaningThe fix
ambiguous_lotMore than one lot matches the identity you sent.Send lot_id (the refusal lists candidateLotIds).
ambiguous_legacy_passportAn older record for this order matches more than one lot.Send lot_id.
phi_in_order_reforder_id reads like patient information.Use an opaque order or Rx number, never a name or a date of birth.
phi_in_customer_refcustomer_ref reads like patient information.Use an opaque reference.
as_purchased_requiredThe lot is a purchased lot, not a preparation.Send as_purchased: true (asPurchased) when it is dispensed as purchased, or record the preparation made from it and issue for that.
copy_determination_requiredThe substance is essentially a copy of a marketed drug under your substance screen.Confirm the prescriber's determination for the order is in your copy file (copy_determined: true).
lot_pending_reviewThe lot is not cleared.Clear the lot's documents, or record a person's review.
cleargate_holdThe lot carries a ClearGate hold.Close the reason the decision names; the next decision lifts the hold.
release_policy_errorThe release policy could not be evaluated.Retry; the release is held, never waved through.
lot_recalledRequirement not met: no recall on the lot.The refusal names what to do on the lot record.
lot_on_holdRequirement not met: no hold by a person.The refusal names what to do on the lot record.
no_503a_routeRequirement not met: a 503A route for the substance.The refusal names what to do on the lot record.
ingredient_heldRequirement not met: no ingredient lot held.The refusal names what to do on the lot record.
lot_not_clearedRequirement not met: the lot is cleared.The refusal names what to do on the lot record.
ingredient_unreviewedRequirement not met: every ingredient lot reviewed by a person.The refusal names what to do on the lot record.
no_certificateRequirement not met: a certificate read against the lot.The refusal names what to do on the lot record.
person_review_requiredRequirement not met: a person reviewed the lot.The refusal names what to do on the lot record.
specification_unresolvedA selected product specification has failed, missing, stale or unassessed evidence.As the refusal states.
material_chain_unresolvedThe recorded material chain has unresolved source or lot-mapping evidence.As the refusal states.
no_lotThis passport names no canonical lot, so there is nothing to release evidence about.As the refusal states.
supplier_critical_gapThe supplier file is missing evidence its own persona schema calls critical.As the refusal states.
supplier_unqualifiedNo supplier qualification file exists for the source named on this passport.As the refusal states.
supplier_not_linkedThe supplier file exists but does not link to this lot: no one has signed a qualification decision on it and it is not a reviewed packet, or the supplier is signed "Not qualified".Sign a qualification decision on the supplier's file.
dispenser_identity_requiredThis passport names the dispensing pharmacy itself as the source.As the refusal states.
lot_document_aliasThe same document is on record for this lot under more than one supplier name, so this passport cannot state which source it came from.As the refusal states.
demo_lot_sandbox_onlyThis lot is demonstration data and cannot back a production passport.As the refusal states.
evidence_staleThe evidence snapshot this decision was made from is no longer current.As the refusal states.
sandbox_decisionThis release was authorised by a sandbox decision, which cannot release production evidence.As the refusal states.
human_review_requiredThis workspace requires a person to release, and no human release is recorded.As the refusal states.
cleargate_requiredThis workspace requires a ClearGate Allow decision on the lot before a passport can be issued.As the refusal states.
no_production_capabilityThis workspace is not activated for production evidence.As the refusal states.

phi_in_order_ref and phi_in_customer_ref answer 400; release_policy_error answers 500. A seal batch refuses an entry that reads like patient information with 400 phi_in_order_refs, and a test key with 403 test_mode_unsupported.

Signing refusals#

A signature in the workspace (a lot review, a hold, the 503A basis, a by-hand release, a Seal assignment, a quality unit decision) is made by a signed-in person, never by an API key. The routes that write one answer 428 when the person must confirm who they are first; nothing is recorded, and the same request succeeds once they have.

CodeMeaningThe fix
password_step_up_requiredThe first signature in a session, from a seat with a password and no authenticator, is made with the password as well as the name (21 CFR 11.200(a)(1)(i)).POST /api/auth/password/step-up with { "password": ... }, then send the request again. The rest of the session signs without asking. A wrong password answers 403.
mfa_step_up_requiredThe seat has an authenticator, and its second factor was not confirmed in the last minutes.POST /api/auth/mfa/step-up with { "code": ... } (an authenticator or recovery code), then send the request again.

Each signature records how it was made: reauthenticated, and reauthMethod password or authenticator.

Retries and idempotency#

Veritura reads documents. It does not test material. Documentation status only: never a quality, safety, or FDA-approval determination.