Appearance
Security and compliance
Summary: this API carries prescriptions and money, so every request is signed and time-bound, each key is tied to one environment, every read is scoped to your own account, secrets and bank details are stored encrypted, and webhooks are signed and sent only to public addresses. This page lists what we do and what your side needs to do.
Compliance at a glance
- SOC 2: Axiom is not SOC 2 audited. Our controls are built to the SOC 2 criteria.
- HIPAA: we sign a business associate agreement (BAA) with partners that handle patient data. Contact Axiom to set one up.
- Encryption: data is encrypted in transit and at rest.
What we do
Requests are signed and cannot be replayed elsewhere
- Every request carries an HMAC-SHA256 signature over the HTTP method, the full path including the query string, a timestamp and the exact body, so a signature made for one endpoint does not validate on another. (An older signature format, without the method and path, is accepted only for accounts Axiom is migrating, and only on a few endpoints. New integrations always use the format on this site.)
- A timestamp more than 5 minutes from our clock is refused, which limits how long a captured request stays usable.
- Signatures are compared in constant time.
- The answers that describe account state (inactive, suspended, production not enabled) are only given to a correctly signed request.
Keys are per environment and under your control
- Each key belongs to sandbox or production. The key that signs a request decides its environment, so a sandbox key can never create a real order, change a real clinic or its bank account, or touch a production webhook endpoint.
- A production key does nothing until Axiom enables production for your account.
- You create and revoke keys yourself in the partner portal, which requires a partner admin login and a recent sign-in. A revoked key stops working at once.
- A key's secret is shown once, at creation. We store it encrypted (AES-256-GCM) and cannot display it again. Webhook endpoint secrets are handled the same way.
You only ever see your own data
- Every endpoint is scoped to the account that signed the request. An order, invoice, ticket or clinic belonging to anyone else answers exactly as one that does not exist (
404), so ids cannot be probed across accounts. - Orders, tickets, invoices, the billing feed and notices are also scoped to the signing key's environment. Clinics and webhook endpoints are not: they belong to the account, and either of your keys can manage them.
- Order events in API responses and webhooks are built from an allow-list of fields, not from our internal records.
Sensitive fields are protected
- Clinic bank account and routing numbers and tax ids (EIN) are stored encrypted and are never returned by the API. A clinic validation error reports the field and the reason; a value that fails a format check (a routing number, an account number, a tax id) is not repeated back.
- In production, unexpected errors return a generic
internal_errorand a request id, never internal detail.
Changes are recorded
- Successful changes made through the API (clinic enrollment and updates, order creation, edits and cancellations, new tickets, webhook endpoint changes) are written to an audit log. (A clinic created by an order that is then refused is not recorded there separately.) The audit log is append-only: the database refuses updates and deletes to it.
Webhooks are signed and go only to public endpoints
- Each delivery attempt is signed with HMAC-SHA256 using the secret of the active endpoint registered for its URL, with a fresh timestamp on every attempt (see Webhooks for deliveries queued before an endpoint changed).
- Production endpoints must use HTTPS. A URL naming a local, private, link-local or carrier-grade NAT address is refused when you register it, and at every delivery we resolve the hostname, refuse a private address, and connect only to the address we checked.
- We do not follow redirects.
- Events go to the endpoints registered for the environment they happened in. (An account that still has a single webhook URL set up by Axiom, and no endpoint registered for an environment, receives that environment's order, ticket and invoice events at the single URL; and deliveries already queued for a URL keep going to it if you re-register that URL in the other environment, so use a different URL per environment. See Webhooks.)
Files are checked
- Ticket attachments are limited to a list of document, image and video types, size-limited, and refused when the file's signature bytes do not fit the declared type.
- Downloads are served as attachments with
X-Content-Type-Options: nosniffandCache-Control: private, no-store.
What your integration is responsible for
| Area | What to do |
|---|---|
| Secrets | Keep signing and webhook secrets in a secret store. Never put them in client-side code, URLs, logs or tickets. Rotate a key when someone with access leaves, and revoke any key you think was exposed. |
| Webhooks | Verify every delivery's signature on the raw body and reject stale timestamps before acting on it. See Webhooks. |
| Transport | Call the API only over HTTPS, and serve your production webhook endpoint over HTTPS with a valid certificate. |
| Minimum necessary | Send only the patient and prescriber details an order needs. |
| Identifiers | Never build partner_order_id, Idempotency-Key, clinic.external_id, metadata values, ticket subjects or file names out of patient names, dates of birth or other patient details. These values appear in logs and support tools on both sides. Use opaque ids. |
| Access | Limit partner portal admin accounts to the people who need them. |
| Clocks | Keep your servers on NTP so signatures stay inside the 5-minute window. |
Reporting a security issue
If you believe you have found a vulnerability or a leaked credential, contact Axiom straight away through your account contact or the contact form on axiomscript.com, and revoke any affected key in the partner portal.

