Errors and status handling
Use this page to decide what to retry, what to trust, and what counts as a terminal answer.Typical error shape
Most Hilt route failures come back as one of these shapes:detail.code, thencode, thenerrordetail.message, thendetail, thenmessage- the request id header, usually
X-Hilt-Request-IdorX-Request-Id
HiltApiError.code, statusCode or status_code, requestId or request_id, retryable, and docsUrl or docs_url.
Public error-code catalog
Use the error code as the durable branch in your integration. Human-readable messages can become more specific over time.payment_failed
The payment did not complete successfully. Example:subscription_expired
The paid-through period has ended. Example:invalid_authorization
The API key, bearer token, or permission scope is missing or invalid for the route. Example:X-Hilt-Key, and that the key has the required scope.
webhook_signature_failed
The webhook payload could not be verified against the endpoint signing secret. Example:X-Hilt-Signature as t=<unix_timestamp>,v1=<hex_hmac_sha256>, reject stale timestamps, and use the signing secret returned when the webhook endpoint was created or rotated.
rate_limited
The client is sending too many requests. Example:Retry-After when present, and avoid retry loops that create new payment sessions before reading current Hilt state.
setup_not_ready
The product, rail, webhook, billing, or live configuration needs attention before the requested live action can proceed. Example:GET /v1/access/setup/readiness, complete the returned customer-facing actions, and retry the live request only after readiness is clear.
entitlement_missing
Hilt does not have an active entitlement for the customer and product. Example:402 Payment Required from the protected resource, create a Hilt payment session, and re-check entitlement after payment.
subscription_cancelled
The native subscription authorization has been cancelled. Example:subscription_requires_reapproval
The subscription needs buyer reapproval before future automatic renewals can continue. Example:request_timeout
The SDK did not receive a response before its local timeout. Example:Idempotency-Key and read current Hilt state before creating a replacement object.
Idempotency errors
Hilt Pay API write routes require anIdempotency-Key header so retries do not create duplicate live objects.
idempotency_key_required
The write request did not include a usable idempotency key. Example:idempotency_key_too_long
The idempotency key is longer than Hilt accepts. Example:session-cust-123-pro-api-001.
idempotency_key_invalid
The idempotency key contains whitespace, control characters, or non-visible ASCII. Example:invalid_idempotency_key
Some SDK and integration layers use this broader client-side code when an idempotency key fails local validation before a request is sent. Example:idempotency_in_progress
Another request with the same idempotency key and request body is still processing. Example:idempotency_conflict
The same idempotency key was already used with a different request body. Example:idempotency_race
Hilt could not yet read the idempotency record that another concurrent request just created. Example:Common HTTP meanings
Payment states that matter most
Expiry handling
Expiry usually appears one of two ways:410during the active buyer sessionFAILEDwith an expiry-style failure reason when Hilt later finalises the payment record
Payment behavior that is normal
Treat these as normal:- a payment taking time to settle
- repeated polling before a terminal state appears
- the buyer seeing wallet success before your backend sees final Hilt status
- asking the buyer to sign again while the first payment is still pending
- building business logic from screenshots instead of Hilt state
Edge cases worth handling deliberately
Duplicate payment attempts
If your checkout flow already has a livepayment_id, keep using it until that payment reaches a terminal state.
Do not start a second payment just because:
- the buyer refreshed the page
- the wallet popup closed late
- confirmation is still catching up
Duplicate confirm calls
If your own app callsPOST /v1/pay/confirm more than once for the same transaction, read the current payment state before trying again.
The right recovery pattern is:
- read
GET /v1/payments/{payment_id} - if the payment is already
CONFIRMED, continue - if the payment is still transitional, wait and poll
- if the payment is terminal and failed, start a clean new checkout
Expired sessions
If a checkout session has expired:- treat it as closed
- create a fresh session
- do not ask the buyer to sign the old one again
Identity handoff expiry
If a signed handoff link is no longer valid, create a fresh handoff token and send the buyer back through a new checkout URL. Do not patch around this by manually guessing the buyer identity on the backend.Good polling pattern
Use a simple progression:- poll quickly just after payment starts
- slow down once the payment is clearly in
PENDING_CONFIRMATION - stop polling once the payment is
CONFIRMEDorFAILED
payment_id. Do not create a second payment just because confirmation is still catching up.
Safe retry guidance
Safe retries usually mean:- retrying reads
- retrying lookups
- retrying your own app-side automation after reading current Hilt state
- starting a second payment before the first one is clearly failed or expired
- mutating access state without checking the current payment, membership, or receipt trail
Timeout and backoff guidance
Use calmer retry behavior as a payment ages:
This gives buyers a responsive experience early without turning your backend into a busy loop.
Good retry candidates
GET /v1/productsGET /v1/products/{product_id}GET /v1/payments/{payment_id}GET /v1/membershipsGET /v1/memberships/lookupGET /v1/receiptsPOST /v1/memberships/{membership_id}/retry-delivery
Routes that deserve more care
POST /v1/productsPATCH /v1/products/{product_id}POST /v1/pay/confirmPOST /v1/support/tickets
Practical recovery sequence
When something looks wrong:- read
GET /v1/payments/{payment_id} - inspect the related membership if access is involved
- inspect the related receipt if proof is involved
- open or append a support ticket if a human conversation is needed
A strong correlation habit
Even without a separate idempotency header, you can keep your integration safe by treatingpayment_id as the canonical correlation key for:
- retries
- queue jobs
- buyer progress tracking
- post-payment automation
A good developer rule
Use Hilt states for:- payment truth
- access truth
- receipt truth

