Skip to content

Errors and rate limits ​

Summary: every error is JSON with a machine-readable error code, sometimes with details beside it. Branch on the HTTP status and error, never on message, which is for people and can change. Retry 429, 503 and 5xx with the same Idempotency-Key; fix and resend anything else.

The error format ​

json
{ "error": "ship_state_not_licensed", "state": "AK" }
  • error is always present and stable.
  • Details depend on the code: sku, state, reason, blockers, issues, retryable and so on. They are listed with each code below and in the API reference.
  • Validation errors carry issues (each with a path and a message). The clinic endpoints also return fields, the list of dotted field paths. A message can quote what you sent (for example the value given for an enum field), so do not log error bodies anywhere you would not log the request.
  • Every response, success or error, has an X-Request-Id header. Log it and quote it when you contact us.

Retrying ​

StatusRetry?
400, 401, 403, 404, 409 (except the in-flight codes), 413, 415, 422, 423No. Fix the request or the account state first.
409 order_in_flight, 409 idempotency_key_in_flightYes, shortly, with the same key. If order_in_flight persists, the key belongs to an earlier failed or cancelled order: read that order first (GET /orders/{partnerOrderId}) and follow the failed-order guidance; never resubmit one whose failure.code is pharmacy_outcome_unknown. Only then resubmit, under a new key.
429 rate_limit_exceededYes, after Retry-After seconds.
429 bank_change_quotaNot soon: one bank account change per clinic per 24 hours. There is no Retry-After.
503Yes, after Retry-After seconds when present, otherwise a few seconds with backoff.
500Yes, with backoff and the same Idempotency-Key, so a request that did go through is not repeated.
Timeout or network errorYes, with the same Idempotency-Key.

Rate limits ​

Limits are per account, counted over a 60-second window that starts with the first request in it. For the per-minute limits below, requests are counted before the signature is checked, so failed attempts count too. The upload limit counts only signed uploads. Attachment downloads are not rate limited.

TrafficLimit
Writes (POST, PUT, PATCH under /api/inbound/{slug})120 per minute
Reads (GET under /api/inbound/{slug}, except attachment downloads)600 per minute
POST /orders/check600 per minute, counted separately, so checks never use up your order budget
POST /uploads100 per hour
Webhook endpoint API (/api/partner/{slug}/webhooks)60 writes and 300 reads per minute

Over a limit you get 429 with a Retry-After header and a body of { "error": "rate_limit_exceeded", "retryAfterSec": 12 }.

General ​

StatusCodeMeaning
400invalid_jsonThe body is not valid JSON.
400invalid_payloadThe body could not be read (for example an unsupported encoding).
400validation_failedThe body failed validation. See issues.
400invalid_queryA query parameter is invalid or unknown. See issues.
404not_foundNo such resource for your account in this environment. Also returned by every ticket endpoint when ticketing is off for your account.
413payload_too_largeThe JSON body is over 1 MB (uploads have their own limits).
429rate_limit_exceededSee rate limits.
500internal_errorSomething failed on our side. Some endpoints add a reference_id, the same value as X-Request-Id. Safe to retry with the same idempotency key.
503database_busyWe are briefly overloaded. Retry after Retry-After seconds.

Signing ​

StatusCodeMeaning
401hmac_signature_missingNo X-Axiom-Signature.
401hmac_timestamp_missingNo X-Axiom-Timestamp.
401hmac_timestamp_invalidThe timestamp is not whole seconds.
401hmac_timestamp_skewMore than 300 seconds from our clock.
401hmac_partner_mismatchX-Axiom-Partner-Slug differs from the URL.
401hmac_partner_unknownNo account has that slug.
401hmac_invalidThe signature does not match an active key.
403production_not_enabledProduction is not enabled for your account yet.
423partner_inactiveYour account is deactivated.
423tenant_suspendedYour account is suspended.

See Authentication and signing.

Idempotency ​

StatusCodeMeaning
400idempotency_key_requiredThis endpoint needs an Idempotency-Key.
400idempotency_key_invalidEmpty, or over 128 characters.
400idempotency_key_mismatchOrder create: header and body keys differ.
409idempotency_key_reusedThe key was used with a different body.
409idempotency_key_in_flightA request with this key is still running. Retry shortly.

See Idempotency.

Orders ​

StatusCodeMeaning
404not_found / order_not_foundNo such order for your account in this environment.
409partner_order_id_duplicateYou already have a live order with this id. Carries partnerOrderId and axiomOrderId.
409order_in_flightThe same order is being created right now (retryable: true), or, if it persists, the key belongs to an earlier failed or cancelled order (see Orders lifecycle before resubmitting).
409order_not_editableOnly a received order can be edited. Carries status.
409order_not_cancellableToo late to cancel. Carries status and sometimes reason (dispatched, child_dispatched, split_child).
409default_clinic_unavailableThe order relied on your account's default clinic, and that setting changed while the order was being placed. Nothing was created; retry. Sending clinic.external_id avoids it.
422prescriber_incompletePrescriber details missing. See blockers.
422order_incompleteOther required details missing (for example patient.sex). See blockers.
422unknown_productSKU not recognised for you. Carries sku and reason.
422product_retiredSKU retired. Carries sku and retiredAt.
422product_not_in_catalogSKU not in your catalog or the clinic's. Carries sku.
422sku_not_pricedNo price set up for you. Carries sku.
422product_not_yet_availableHeld until availableAt (it can still be refused after that, for example as backordered). Carries sku.
422product_backorderedCarries sku and, when known, expectedBackAt.
422ship_state_not_licensedNo licensed pharmacy for the ship-to state.
422product_unavailable_in_stateThe SKU cannot ship to state.
422no_pharmacy_can_fillNo pharmacy available to this order can fill sku for state.
422prescriber_inactiveThe prescriber was removed from the clinic's provider list. Carries message. Being introduced; announced before it is switched on.
422agreement_unsignedThe clinic's agreement is missing. Carries reason. See Clinics and agreements.
422clinic_overdueThe clinic has an invoice past due. Carries overdueSince and graceDays.
422partner_overdueYour account has an invoice past due. Carries overdueSince and graceDays.
422clinic_suspendedThe clinic is suspended.
422clinic_over_credit_capOver the clinic's credit limit. Carries capCents, outstandingCents, orderChargeCents, projectedCents.
422partner_exposure_capOver your account's overall limit.
422billing_account_id_unsupportedbilling.account_id disagrees with clinic.external_id. Carries details.clinicExternalIdPresent.

See Orders lifecycle.

Clinics ​

StatusCodeMeaning
400banking_invalidA bank field failed the storage check, for example a bank or account-holder name that is only spaces. (Malformed routing or account numbers are normally a 400 validation_failed.)
404clinic_not_foundNo such clinic on your account.
409banking_conflictA different bank account is on file. Use PUT /clinics/banking.
409npi_conflictAnother of your clinics has that NPI.
409ach_debit_in_flightA debit for the clinic is in progress. Retry later.
409bank_revisions_offBank account authorization through the API is not on for this clinic yet.
409bank_authorize_refusedCarries reason: not_newest, account_mismatch, already_authorized, rejected or approval_pending.
422banking_requires_attestation / identity_requires_attestationOnly partner-attested accounts can use the banking endpoints and PATCH /clinics/identity (and send banking on enrollment).
422clinic_not_attestedThe clinic has no partner attestation on file.
422axiom_boldsign_requiredYour clinics sign Axiom's agreement directly; they cannot be enrolled through the API.
422signer_identity_locked_boldsignSigner details cannot change through the API on e-signature accounts. Carries fields.
422clinic_not_resolvedThe clinic could not be created or found.
429bank_change_quotaOne bank account change per clinic per 24 hours (where bank account revisions are kept).

Support tickets ​

StatusCodeMeaning
400empty_fileThe upload had no bytes.
400invalid_upload_token / duplicate_upload_tokenAn attachment token is unknown, expired, already used, or repeated. (More than 4 attachments is a 400 validation_failed.)
404ticket_not_found / attachment_not_foundNo such ticket or attachment on your account.
413file_too_large / payload_too_largeOver the size limit. Usually carries maxBytes; a body the server stops reading early may answer with only a message.
415unsupported_file_type / file_content_mismatchFile type not accepted, or the bytes do not match the declared type.
422invalid_status_transitionFor example resolving a resolved ticket. Carries from, to, allowed.
503upload_capacityToo many uploads in progress. Retry after Retry-After seconds.

Webhook endpoints ​

StatusCodeMeaning
400validation_failedOn create: a bad URL, environment or event name. See issues. (A bad URL on PATCH answers 400 with the reason written out in error.)
400no_fields_to_updateA PATCH with nothing to change.
400invalid_idThe endpoint id is not a number.
404not_foundNo such endpoint on your account.
409endpoint_url_already_registeredYou already registered that URL.