Quickstart
Everything here runs against the sandbox with synthetic data. The key and secrets below are visibly fake placeholders: replace them with the ones issued to you, and never paste a real key or secret into a ticket, a chat or a log.
export TR_BASE="https://sandbox.trueroute.health"
export TR_API_KEY="tr_sandbox_EXAMPLE_KEY_DO_NOT_USE"
export TR_SIGNING_SECRET="trsec_EXAMPLE_SECRET_DO_NOT_USE"
1. Is the API enabled here?
curl -s "$TR_BASE/health/enterprise"
No credential needed. It reports whether the deployment has the partner API mounted. If it is not mounted, every partner path answers 404.
2. A GET: bearer only
curl -s "$TR_BASE/v1/integrations/catalog" \
-H "Authorization: Bearer $TR_API_KEY"
curl -s "$TR_BASE/v1/modules/availability?clinicId=<YOUR_CLINIC_ID>" \
-H "Authorization: Bearer $TR_API_KEY"
GETs are never signed. catalog:read and modules:read are the scopes.
3. A signed POST, unkeyed form (a quote)
The quote endpoint takes no Idempotency-Key, so it is signed over {t}.{rawBody}. Write the body to a file once and send that file, so the bytes you sign are the bytes you send.
printf '%s' '{"patient":{"externalPatientId":"synthetic-patient-one","shippingState":"TX","dateOfBirth":"1980-01-01"},"provider":{"npi":"<SYNCED_PRESCRIBER_NPI>"},"productId":"<PACKAGE_ID_FROM_CATALOG>","quantity":1,"orderType":"patient_specific","shippingService":"ground"}' > quote.json
T=$(date +%s)
SIG=$(printf '%s.%s' "$T" "$(cat quote.json)" \
| openssl dgst -sha256 -hmac "$TR_SIGNING_SECRET" -hex | sed 's/^.* //')
curl -s "$TR_BASE/v1/integrations/quotes" \
-H "Authorization: Bearer $TR_API_KEY" \
-H "Content-Type: application/json" \
-H "X-TrueRoute-Signature: t=$T,v1=$SIG" \
--data-binary @quote.json
Two details keep the signed bytes and the sent bytes identical: printf '%s' writes the body with no trailing newline, and --data-binary sends the file unchanged (-d strips newlines).
4. A signed POST, keyed form
Endpoints that require an Idempotency-Key (marked in the reference) sign TRv1.{t}.{keyLength}.{key}.{rawBody}:
KEY=$(uuidgen | tr 'A-Z' 'a-z') # one per logical operation; persist it
printf '%s' '<REQUEST_JSON>' > body.json
T=$(date +%s)
SIG=$(printf 'TRv1.%s.%s.%s.%s' "$T" "${#KEY}" "$KEY" "$(cat body.json)" \
| openssl dgst -sha256 -hmac "$TR_SIGNING_SECRET" -hex | sed 's/^.* //')
curl -s "$TR_BASE/v1/integrations/regimen-quotes" \
-H "Authorization: Bearer $TR_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $KEY" \
-H "X-TrueRoute-Signature: t=$T,v1=$SIG" \
--data-binary @body.json
Retrying after a timeout: send the SAME key and the SAME body.json with a fresh t and signature. You get the stored answer (Idempotent-Replay: true), never a second order.
5. When something is refused
| You see | Do |
|---|---|
| 404 on every path | the deployment has not enabled the partner API: check /health/enterprise |
authentication_failed (401) | check the key, the signing secret, keyed vs unkeyed form, and that you signed the exact bytes you sent |
signature_expired (401) | your clock is off by more than the window: sync NTP and re-sign |
environment_mismatch (403) | a sandbox key on production or the reverse |
insufficient_scope (403) | ask TrueRoute for the scope; never reuse another credential |
request_in_flight (409) | the same key is still running: wait, then retry the same key and bytes |
idempotency_key_reuse (422) | you changed the body under an old key: send the original bytes, or a new key for a new operation |
rate_limited (429) | wait Retry-After seconds |
Every code: errors.