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

> ## Agent Instructions
> Reconify's API reference is read-only for customer data. Do not invent endpoints or authentication behavior beyond the OpenAPI contract.
> The public OpenAPI document contains only the documented external /v2 contract.

# Troubleshooting

> Start from a visible symptom and choose a safe action.

Every entry below starts from something you can see: a status code, an item result, a
stuck event, an empty read. Each one ends in an action that neither duplicates evidence
nor exposes customer data.

## Before you retry

The four values worth preserving on any failure are the HTTP status, the stable error
`code`, the item `index`, and the `X-Request-ID`. Together they identify what happened
without carrying a single financial field, and they answer the question a retry depends
on: did Reconify store the event?

<Danger>
  API keys, full event bodies, provider responses, and unredacted financial data never
  belong in logs, tickets, or AI tools.
</Danger>

## Authentication fails

Two statuses cover every credential failure. The status names the problem, and for `403`
the method you called picks the fix.

| Symptom                    | Meaning                                                                                | Action                                                                        |
| -------------------------- | -------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| `401 unauthorized`         | The bearer key is missing, malformed, expired, revoked, or unknown.                    | Replace the key, and confirm the header reads `Authorization: Bearer rk_...`. |
| `403 forbidden` on a read  | The key lacks `read` scope or organization access.                                     | Use a `read` or `admin` key for `GET`.                                        |
| `403 forbidden` on a write | The key lacks `write` scope, or monitoring access is unavailable for the organization. | Use a `write` or `admin` key and confirm organization access.                 |

A key test needs a protected route, because `GET /v2` and `GET /v2/health` answer without
validating credentials:

```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl --fail-with-body -sS \
  "https://api.reconifyhq.com/v2/events?limit=1" \
  -H "Authorization: Bearer $RECONIFY_READ_KEY"
```

## A batch contains a rejected item

`POST /v2/events` validates each item independently, so a `202` can carry both stored and
rejected results. A batch where every item fails returns `422`. The recovery runs in five
steps:

1. The result carries a zero-based `index` that locates the item you sent.
2. Your handler branches on `code`, not on `message`.
3. The `field` names the input to correct.
4. Only the corrected item goes back out.
5. Its original `id` gets reused only when the corrected payload is the same event
   identity.

The [error catalog](/reference/api/errors) lists every public code.

## A retry returns `duplicate` or `idempotency_conflict`

These two results look similar and mean opposite things.

| Result                                           | Meaning                                                        | Action                                                                      |
| ------------------------------------------------ | -------------------------------------------------------------- | --------------------------------------------------------------------------- |
| `status: duplicate`                              | The organization already stored the same event ID and content. | Treat it as successful delivery, and create no new ID.                      |
| `status: rejected`, `code: idempotency_conflict` | The event ID is already bound to different content.            | Stop retrying, and compare the source record against your identity mapping. |

An event sent without an `id` receives a generated one, which means Reconify cannot
recognize its replay. Automatic retries of those events create a second event.

## An event stays in `received` or `published`

Processing is asynchronous, so a status read a second after ingestion often shows an early
state. The four states run in order:

* `received` means Reconify holds the durable receipt.
* `published` means the event entered asynchronous processing.
* `processed` means evaluation finished.
* `failed` means evaluation could not complete.

Resending an `accepted` event pushes nothing along and creates a second delivery attempt
for the same evidence. A state that has not advanced past its flow's deadline justifies a
look at the [status page](https://status.reconifyhq.com) and a support packet.

## An accepted event produced no finding

`accepted` means stored, not evaluated, and not failed. Five conditions decide whether a
finding is even expected:

1. The events share the same `flow` and `reference`.
2. The `entity_id` values follow that flow's participant rules.
3. The relevant deadline has passed. `payment_to_wallet` allows five minutes, and
   `payment_to_order` allows 24 hours for fulfillment.
4. The event status reads `processed`, rather than `received` or `published`.
5. The expected evidence is genuinely missing, late, mismatched, failed, or conflicting.

A read of the event by ID confirms conditions 1, 2, and 4 in one request, because the
response carries `reference`, `entity_type`, and `status`.

## Reads come back empty

The read key and the write key have to belong to the same organization. Reconify resolves
the organization from the key, and no query parameter or body field selects another
tenant, so a key from a second organization returns an empty list rather than an error.

For cursor reads, the `after` parameter takes the exact `next_cursor` value from the
previous response. Cursors are opaque, so parsing, editing, or constructing one produces
nothing usable.

## Related activity is missing or wrong

Related activity expands from an exact, case-sensitive `correlation_id` match.

| Symptom                       | Likely cause                                            | Action                                                                   |
| ----------------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------ |
| No related results            | Only one service sends the field, or the values differ. | Confirm every service sends the same value for the transaction.          |
| Unrelated transactions appear | One value is reused across transactions.                | Move to an identifier that is unique per transaction.                    |
| Operations did not merge      | Expected behavior.                                      | Correlation links evidence, and `reference` still defines the operation. |

See [Trace a flow across services](/guides/trace-across-services).

## Provider tracking is degraded

A provider timeout, authentication failure, rate limit, or unmapped status is a provider
issue, and Reconify converts none of them into a synthetic `payment.failed` event. The fix
runs through the integration:

1. The saved integration in the dashboard shows its latest health reason and environment.
2. Expired or incorrect credentials get replaced.
3. The optional test, run with a safe provider-side identifier, confirms the fix.
4. The status mapping gets checked against the provider value that went unmapped.

Resending the original user event under a new ID creates a second piece of user evidence
and fixes nothing, because user evidence and provider evidence stay separate by design.

## The API returns `503`

`503 database_unavailable` means durable ingestion or a required public-data dependency is
unavailable. The response deserves a retry, under three conditions:

* Exponential backoff with jitter separates the attempts.
* The event IDs and payloads stay identical.
* Concurrency stays bounded, so recovery does not meet a retry surge.

The public contract promises no `429` and no `Retry-After` header. The status page is
worth a check before escalating.

## Prepare a support packet

A packet carries the minimum evidence needed to correlate the failure:

```text theme={"theme":{"light":"github-light","dark":"github-dark"}}
Timestamp (UTC): 2026-08-19T14:05:00Z
Route and method: POST /v2/events
HTTP status: 503
Stable code: database_unavailable
X-Request-ID: <request ID>
SDK and version: @reconifyhq/sdk 1.0.0
Caller environment: development (your label, not a Reconify sandbox)
Redacted response: {"code":"database_unavailable","message":"..."}
```

API keys, authorization headers, event bodies, amounts, references, customer identifiers,
provider payloads, and member data all come out first. The packet goes to
[support@reconifyhq.com](mailto:support@reconifyhq.com).

Next: [Errors](/reference/api/errors) for exact code handling, or
[Go live](/guides/production-checklist) for the operational runbooks.
