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

# API Design Principles

> How ReconLayer's API is structured: API-first design, route conventions, idempotency, business-level uniqueness, and versioning.

ReconLayer is built **API-first**. The dashboard is a consumer of this API, not the platform boundary — anything you can do in the dashboard, you can do programmatically, and the dashboard itself calls the same `/v1/*` routes documented here.

## Route structure mirrors the domain

The API's route groups map directly onto ReconLayer's core domain model:

| Prefix                                                                    | Purpose                                                                                         |
| ------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| `/v1/payment-intents`                                                     | Expected payments — the canonical record of what *should* happen for a transfer.                |
| `/v1/reconciliation-cases`                                                | Reconciliation truth and workflow — the matched (or unmatched) outcome for each payment intent. |
| `/v1/evidence/*`                                                          | Ingest provider and on-chain evidence that the matcher reconciles against payment intents.      |
| `/v1/integrations`, `/v1/bridge-integrations`, `/v1/onchain-integrations` | Connections to banking, stablecoin, and on-chain data sources.                                  |
| `/v1/outbound-webhook-endpoints`, `/v1/outbound-webhook-events`           | Client callback subscriptions and delivery tracking — ReconLayer notifying *you*.               |
| `/webhooks/*`                                                             | Provider callbacks — providers notifying *ReconLayer*.                                          |
| `/v1/import-profiles`, `/v1/import-batches`                               | Bulk file-based ingestion of payment intents and evidence.                                      |
| `/v1/api-keys`                                                            | Headless credential management (signed-in users only).                                          |

<Note>
  `/webhooks/*` (inbound, from providers) and `/v1/outbound-webhook-*` (outbound, to your systems) are deliberately separate namespaces. See [Authentication](/api-reference/authentication) for how each is secured.
</Note>

## Base URL and versioning

```
https://api.reconlayer.com
```

All resource routes are versioned under `/v1`. The machine-readable OpenAPI contract is served at `/openapi.json` and `/openapi.yaml`, and an interactive Scalar API explorer is available at `/docs` — both are public and require no credential.

## Authentication model

Every `/v1/*` route requires a Bearer credential — either an **API key** (headless clients, organization and scopes are bound to the key) or a **Clerk session token** (dashboard users, organization resolved from the session or `x-organization-id`). See [Authentication](/api-reference/authentication) for the full model, including the local-only dev bypass.

## Idempotency on writes

Network failures happen. If a `POST` to ReconLayer times out or your client crashes mid-request, you need a safe way to retry without creating a duplicate `PaymentIntent`, evidence record, or reconciliation side effect.

Client write endpoints require an `Idempotency-Key` header:

1. **Generate the key before the first attempt** — a UUID or other unique, stable string scoped to that logical operation.
2. **Reuse the exact same key on every retry** of that same logical request.
3. ReconLayer stores the request's hash and result keyed by `(organizationId, operation, idempotencyKey)`. A retry with the same key and the same body **replays the original response** — no new record is created, and the response carries `Idempotency-Replayed: true`.
4. Reusing the key with a **different** request body returns `409 idempotency_key_conflict` instead of executing anything.

See [Idempotency](/api-reference/idempotency) for the full mechanics, including what happens to a request that is still in flight.

## Business-level uniqueness

The `Idempotency-Key` header prevents *the same request* from being executed twice. ReconLayer separately enforces **business-level uniqueness** to prevent *different* requests from creating duplicate domain records.

For payment intents, this is the `externalReference` field — your stable, internal identifier for the payment. ReconLayer deduplicates on `(organizationId, externalReference)`:

* **Reuse** — if you `POST /v1/payment-intents` with a new `Idempotency-Key` but an `externalReference` that already exists *and* the canonical fields (amounts, currencies, beneficiary, etc.) match, ReconLayer returns the **existing** payment intent with `"outcome": "reused"` and a `200` status — no duplicate is created.
* **Conflict** — if the `externalReference` already exists but the new request's canonical fields **don't match** the stored record, ReconLayer rejects the request with `409 payment_intent_conflict` and a `mismatches` array describing exactly which fields disagree.
* **Created** — if the `externalReference` is new, ReconLayer creates the payment intent and its reconciliation case, returning `"outcome": "created"` and a `201` status.

```json 201 Created theme={null}
{
  "caseId": "case_01J...",
  "paymentIntentId": "pi_01J...",
  "externalReference": "invoice-9182",
  "status": "reconciling",
  "verdict": null,
  "outcome": "created"
}
```

```json 200 Reused — already existed, fields match theme={null}
{
  "caseId": "case_01J...",
  "paymentIntentId": "pi_01J...",
  "externalReference": "invoice-9182",
  "status": "reconciling",
  "verdict": null,
  "outcome": "reused"
}
```

## Pagination

List endpoints (`GET /v1/payment-intents`, `GET /v1/reconciliation-cases`, `GET /v1/api-keys`, etc.) take `limit` and `offset` query parameters and return a consistent envelope:

```json theme={null}
{ "items": [ /* ... */ ], "total": 128, "limit": 25, "offset": 0 }
```

See [Pagination](/api-reference/pagination) for defaults, maximums, and traversal patterns.

## Errors

All errors share an envelope with at minimum an `error` machine-readable code, usually with a `message`. Validation failures additionally include an `issues` array pinpointing the offending field. See [Errors](/api-reference/errors) for the full catalog of codes and HTTP statuses.

## Rate limits

Requests are rate-limited per organization (or per IP for unauthenticated/public traffic), with standard `RateLimit-*` response headers and a `429` response when exceeded. See [Rate limits](/api-reference/rate-limits).

## Next steps

* [Authentication](/api-reference/authentication) — credential types and organization resolution.
* [Idempotency](/api-reference/idempotency) — the `Idempotency-Key` contract in depth.
* [Quickstart](/getting-started/quickstart) — create an API key and run through a full payment-intent → evidence → reconciliation flow.
