Errors
Every error response has the shape:
{ "error": { "code": "validation_error", "message": "The request body is invalid.", "details": { "fields": { "patient.phone": "must be a 10-digit US number" } } } }
| Code | HTTP status | Meaning |
|---|---|---|
invalid_api_key | 401 | Missing, malformed, unknown, or revoked key. |
wrong_environment | 401 | A live key used against the sandbox, or a test key against production. |
partner_suspended | 403 | The partner account is suspended. |
validation_error | 400 | Request body failed schema or field-level validation (details.fields), or a bad query param on GET /orders / GET /locations. |
unavailable_in_state | 400 | Patient address is in a restricted state (details.state). |
unknown_item | 400 | One or more items[].id are not in the catalog (details.ids). |
minor_not_eligible | 400 | An item is not available to a patient under 18 (details.ids). |
unknown_location | 400 | preferredLocationId does not match a Quest location (details.id). |
idempotency_key_required | 400 | Missing, >100-character, or non-[A-Za-z0-9_.-] Idempotency-Key header on POST /orders. |
no_payment_method | 402 | No payment method on file for the partner. |
payment_failed | 402 | Stripe declined the charge (details: { declineCode }, which may itself be null), or the PaymentIntent ended in a terminal non-succeeded status — requires_payment_method or canceled (details: { status }, no declineCode in that case). |
idempotency_in_progress | 409 | Another request with this key is still processing — also returned if you retry within 5 minutes of a 500 internal_error on the same key. |
idempotency_conflict | 409 | This key was already used with a different request body. |
payment_in_flight | 409 | The previous attempt's payment outcome is unknown — retry with the same key and body, within the 24-hour idempotency window. |
cannot_cancel | 409 | The order can no longer be cancelled (details.reason — see §5). |
not_found | 404 | No such order (also returned for another partner's order id, to avoid leaking existence). |
not_ready | 409 | Requisition or results have not been issued yet. |
internal_error | 500 | Something went wrong after a charge may have occurred — retry POST /orders with the same Idempotency-Key within 24 hours (the key is left in_progress, so an immediate retry returns 409 idempotency_in_progress first — that's expected); for DELETE /orders/{id}, contact support with the order id. |
payment_provider_error | 502 | The payment provider failed or did not respond (Stripe outage, or another non-network Stripe error) — retry with the same Idempotency-Key and body, within 24 hours. |
payment_pending | 502 | The PaymentIntent is still live (details: { status } — processing, requires_action, requires_confirmation or requires_capture); the outcome is not yet known — retry with the same Idempotency-Key and body, within 24 hours. |
For anything not covered above, or an order stuck in an unexpected state,
contact support@justlabs.health with the order id (and, for webhook
issues, the X-JustLabs-Delivery id of the affected delivery).