> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bilt.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors and retries

> Error codes, rejection reasons, and retry rules for account links and order reports

Account links answer with an HTTP error, and order reports answer `202` and report problems later as a reason.

## Account links error format

The [endpoints you host](/api-reference/endpoints-you-host/look-up-a-contact.md) define their own error formats. A business error from the account links API returns this envelope:

```json theme={null}
{
  "error": {
    "code": "ALREADY_LINKED",
    "message": "Member is already linked to a different partner account",
    "requestId": "4bf92f3577b34da6a3ce929d0e0e4736"
  }
}
```

* Branch on `error.code`. Use `message` for logs only, and never show it to a customer.
* Log `requestId`, and quote it when you contact Bilt. It is empty on credential refusals and absent when the request had no trace.
* Bilt does not echo an `x-request-id` you send.
* Parse leniently, and decide retries from the code. `retry` is always `false`.

A `400` that fails validation has a different shape, with `violations` instead of `error.code`. Malformed JSON returns neither shape. A `POST` without `Content-Type: application/json` returns `415`. Check the status code before you parse the body.

## Error codes

These codes match the `ErrorCode` enum in the [spec](/api-reference/overview.md).

"New token" means the request can succeed only after you mint a fresh `id_token`. Never resend the same body, and cap these retries at one.

| Code | HTTP | Retry | Meaning |
| - | - | - | - |
| `UNAUTHORIZED` | 401 | No | Your credential is missing, unknown, or disabled. |
| `INVALID_ID_TOKEN` | 401 | New token | Create a link: the token failed signature, issuer, or subject validation, or Bilt could not verify it against its IdP. |
| `ID_TOKEN_EXPIRED` | 401 | New token | Create a link: the token is past its expiry. |
| `ID_TOKEN_AUDIENCE_MISMATCH` | 401 | No | Create a link: the token was minted for a different OAuth client. |
| `PARTNER_ACCOUNT_NOT_FOUND` | 404 | No | Get a link: no link is keyed to that `partnerUserId`. |
| `LINK_NOT_FOUND` | 404 | No | End a link: no link of yours has that `linkId`. |
| `ALREADY_LINKED` | 409 | No | Create a link: the member is linked to a different account of yours. |
| `IDENTITY_ALREADY_LINKED` | 409 | No | Create a link: the account is linked to a different Bilt member. |
| `ID_TOKEN_REPLAYED` | 409 | New token | Create a link: the token was already used for a different link. |
| `CONCURRENT_UPDATE` | 409 | Yes | Create a link, end a link: two writes raced. |
| `INVALID_STATE_TRANSITION` | 409 | Yes | End a link: the link was mid-transition. Wait a second and resend. |
| `IDP_UNAVAILABLE` | 502 | Yes | Bilt's IdP was unreachable while validating your `id_token` or access token. |
| `PARTNER_MISCONFIGURED` | 500 | No | A Bilt-side configuration problem. Report it. |
| `INTERNAL_ERROR` | 500 | No | An unexpected Bilt-side failure. Report it. |
| `ACCOUNT_LINK_PERSISTENCE_ERROR` | 500 | No | A Bilt database failure. You should not see it. Report it. |
| `INTERNAL_SERVER_ERROR` | 500 | No | An unhandled Bilt failure with a raw diagnostic `message`. You should not see it. Report it. |
| `PARTNER_DISABLED` | 409 | No | Your integration was disabled while the request was in flight. You should not see it. Report it. |

An IdP outage can also show up as `401 INVALID_ID_TOKEN`. Retry once with a fresh token, then alert.

## Account links retry rules

Retry `502`, `409 CONCURRENT_UPDATE`, and timeouts with exponential backoff. Send up to three tries, starting at about one second, and send the identical body each time. Do not retry a `500`.

| Request | Safe to repeat |
| - | - |
| Get a link | Always. |
| End a link | Always. A repeat returns `204`. |
| Create a link | Only as a byte-identical resend. |

For create a link, the same token and `partnerUserId` return `200` while the link is active, and `409 ID_TOKEN_REPLAYED` after it ends. A different `partnerUserId` also returns `ID_TOKEN_REPLAYED`. A failure before Bilt commits the link does not spend the token.

A `404` from get a link means the account is not linked. It never means you should start over while a create request is still in flight.

## Conflicts shown to customers

`ALREADY_LINKED` and `IDENTITY_ALREADY_LINKED` are not retryable. Tell the customer that their Bilt account, or this account, is already linked elsewhere. Both clear as soon as the other link ends.

## Order reports HTTP responses

[Report an order event](/api-reference/order-reports/report-an-order-event.md) and [Report a batch](/api-reference/order-reports/report-a-batch.md) answer before they check the content. They have no error body.

| HTTP | Meaning | What to do |
| - | - | - |
| `202` | Bilt stored the request. It is not checked yet. | Nothing. |
| `400` | A signing header is missing, longer than 1,024 bytes, or `webhook-timestamp` is not an integer. | Fix the headers. Bilt stored nothing. |
| `413` | The body is over 1 MiB. | Split the batch. Bilt stored nothing. |
| `500` | Bilt could not store the request. | Retry with the same `webhook-id` and body, signed again, with backoff. |

## Signature failures

Bilt checks the signature after the `202`. A failed request is not processed, and Bilt does not retry it. See [Sign your requests](/partners/order-reporting/sign-your-requests.md).

| Reason | Meaning |
| - | - |
| `unknown_key` | `X-Bilt-Key-Id` is not a live key for you: never registered, revoked, past its 7-day rotation window, or your integration is not enabled yet. |
| `stale_timestamp` | `webhook-timestamp` is more than 300 seconds from when Bilt received the request. |
| `bad_signature` | No `v1a,` entry in `webhook-signature` is valid for that key. |

## Order report reasons

Every event ends `ACCEPTED`, `HELD`, or `REJECTED`. A held or rejected event has one of these reasons. Held events are checked again by Bilt. Rejected events are final: fix them and send a new request. See [Handle rejections and resends](/partners/order-reporting/handle-rejections-and-resends.md).

| Reason | Result | Who fixes it | Meaning |
| - | - | - | - |
| `VALIDATION_FAILED` | `REJECTED` | You | Schema, format, enum, or an unknown key. Also a field that must not change across the order's events changed, or a later event resent line details, the location, or the totals. |
| `IDEMPOTENCY_KEY_REUSED` | `REJECTED` | You | Same `webhook-id`, different body. |
| `SUM_MISMATCH` | `REJECTED` | You | The amounts do not add up. See [Amounts](/partners/order-reporting/overview.md#amounts). |
| `ORDER_CONFLICT` | `REJECTED` | You | The `orderId` or a `returnId` was already accepted with another body. |
| `PAYMENT_CONFLICT` | `REJECTED` | You | A `refundId` was reused with another body, or the tender type or card differs from the stored one. |
| `ORDER_NOT_OPEN` | `REJECTED` | You | The order is canceled and cannot take this event. |
| `PAYMENT_NOT_OPEN` | `REJECTED` | You | The tender cannot take this status, such as a void after a capture. |
| `CAPTURE_EXCEEDS_AUTHORIZATION` | `REJECTED` | You | Captures on a card add up to more than its authorized amount. |
| `CAPTURE_EXCEEDS_ORDER` | `REJECTED` | You | Captures over all tenders add up to more than `netAmounts.total`. |
| `REFUND_EXCEEDS_CAPTURE` | `REJECTED` | You | Refunds on a tender add up to more than its captures. |
| `RETURN_EXCEEDS_PURCHASE` | `REJECTED` | You | More of a line came back than was bought. |
| `STATUS_MISMATCH` | `REJECTED` | You | `order.status` or a payment status is not allowed here. |
| `CURRENCY_MISMATCH` | `REJECTED` | You | The currency differs from the order's earlier events. |
| `TIMESTAMP_ORDER` | `REJECTED` | You | `createdAt` is after `closedAt`, or the event is dated before its purchase. |
| `PARTNER_DISABLED` | `REJECTED` | Bilt | Your integration is disabled. Contact Bilt. |
| `CONSUMER_NOT_LINKED` | `HELD` | Nobody | The customer has not linked yet. Checked again once they link. |
| `ORIGINAL_ORDER_NOT_FOUND` | `HELD` | Nobody | The event arrived before its `PURCHASE`. Checked again once it lands. |
| `AUTHORIZATION_NOT_FOUND` | `HELD` | Nobody | A card capture or void arrived before its `AUTHORIZED`. Checked again once it lands. |
| `PAYMENT_NOT_FOUND` | `HELD` | Nobody | A refund arrived before its capture. Checked again once it lands. |

Bilt also rejects with `VALIDATION_FAILED` an event dated more than 90 days ago or more than one hour in the future.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.