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:detail- then
message - then
error
Common HTTP meanings
| Status | Meaning | Typical action |
|---|---|---|
400 | The request shape is valid enough to parse, but the action itself is not allowed | Fix the request |
401 | Session or API key is not valid | Re-authenticate or replace the key |
403 | Caller is authenticated but not allowed to do that action | Check workspace ownership or access level |
404 | Object does not exist or does not belong to that workspace | Re-check the identifier and workspace context |
408 | The chain has not confirmed quickly enough for the current confirm attempt | Keep the same payment_id and retry later |
409 | The object state conflicts with the requested action | Read current state before retrying |
410 | The active session is no longer usable | Start a fresh buyer session |
422 | Payload shape is valid JSON, but not valid for this operation | Fix the request body or query parameters |
429 | You are sending too many requests | Back off and retry more slowly |
503 | A required runtime dependency is unavailable | Retry later and surface a temporary error |
Payment states that matter most
| Status | Meaning | What your app should do |
|---|---|---|
PENDING_SIGNATURE | Buyer session exists but the transaction is not signed yet | Wait for buyer action |
PENDING_CONFIRMATION | The transaction is signed or broadcast but not yet final in Hilt | Poll until a terminal state appears |
CONFIRMED | Payment is final in Hilt | Continue into memberships, receipts, and post-payment logic |
FAILED | Payment did not complete successfully | Stop and show a recovery path |
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:| Situation | Good behavior |
|---|---|
| Fresh checkout just started | retry reads quickly |
Payment has moved to PENDING_CONFIRMATION | slow down |
| Payment is older than expected | keep the same payment_id, log the case, and continue with wider intervals |
| Payment is terminal | stop polling |
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

