Skip to main content
This walkthrough takes you end-to-end through ReconLayer’s core loop: tell ReconLayer what payment you expect (a payment intent), submit evidence of what actually happened (provider evidence), and read back the reconciliation outcome (a reconciliation case). All examples use the local development base URL. Replace it with your deployment’s URL in other environments.
http://localhost:3001
1

Get an API key

API keys are created from the dashboard by a signed-in user (or via POST /v1/api-keys with a Clerk session token — this endpoint requires a signed-in dashboard session, not another API key).
curl -X POST http://localhost:3001/v1/api-keys \
  -H "Authorization: Bearer <clerk-session-token>" \
  -H "x-organization-id: org_acme" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Quickstart integration",
    "scopes": ["cases_action", "cases_view"]
  }'
201 Created
{
  "id": "key_01J...",
  "organizationId": "org_acme",
  "name": "Quickstart integration",
  "prefix": "rl_live_AbCd1234",
  "scopes": ["cases_action", "cases_view"],
  "status": "active",
  "plaintextKey": "rl_live_AbCdEfGhIjKlMnOpQrStUvWxYz0123456789abcdefghijk",
  "createdByUserId": "user_01J...",
  "lastUsedAt": null,
  "expiresAt": null,
  "revokedAt": null,
  "metadata": {},
  "createdAt": "2026-06-15T12:00:00.000Z",
  "updatedAt": "2026-06-15T12:00:00.000Z"
}
plaintextKey is returned once. Copy it now — ReconLayer stores only its hash and cannot show it again. If you lose it, revoke the key and create a new one.
Export it for the rest of this walkthrough:
export RECONLAYER_API_KEY="rl_live_AbCdEfGhIjKlMnOpQrStUvWxYz0123456789abcdefghijk"
From here on, every request authenticates with this key alone — the API key resolves your organization, so you don’t need x-organization-id. See Authentication for the full credential model.
2

Create a payment intent

A payment intent tells ReconLayer what you expect to happen for a transfer — the canonical “what should occur” record that evidence is later reconciled against.This is a write endpoint, so it requires an Idempotency-Key header. Generate a fresh UUID before your first attempt and reuse it on any retry — see Idempotency.
curl -X POST http://localhost:3001/v1/payment-intents \
  -H "Authorization: Bearer $RECONLAYER_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "externalReference": "invoice-9182",
    "sourceAmount": "100.00",
    "sourceCurrency": "USD",
    "destinationAmount": "100.00",
    "destinationCurrency": "USDC",
    "beneficiaryAccount": "0xA1b2C3d4E5f6789012345678901234567890aBcD",
    "beneficiaryName": "Acme Vendor LLC",
    "paymentType": "stablecoin",
    "clientCompletedAt": "2026-06-15T12:00:00Z",
    "statusOnClientSide": "completed"
  }'
201 Created
{
  "caseId": "case_01J...",
  "paymentIntentId": "pi_01J...",
  "externalReference": "invoice-9182",
  "status": "reconciling",
  "verdict": null,
  "outcome": "created"
}
Save paymentIntentId and caseId — you’ll use them in the next steps.
outcome is "created" the first time ReconLayer sees this externalReference. If you retry with a new Idempotency-Key but the same externalReference and matching fields, ReconLayer returns the existing payment intent with "outcome": "reused" and a 200 instead of creating a duplicate. If the fields don’t match, you get 409 payment_intent_conflict. See API Design Principles.
Full reference: POST /v1/payment-intents.
3

Submit provider evidence

Once a payment intent exists, submit evidence describing what actually happened on the rail — for example, a confirmed transfer reported by your provider. ReconLayer’s matcher reconciles this evidence against the payment intent’s expected legs.This is also a write endpoint and requires its own Idempotency-Key.
curl -X POST http://localhost:3001/v1/evidence/provider \
  -H "Authorization: Bearer $RECONLAYER_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "paymentIntentId": "pi_01J...",
    "provider": "bridge",
    "integrationKey": "bridge_main",
    "sourceRef": "transfer_01J...",
    "externalReference": "invoice-9182",
    "phase": "destination",
    "status": "confirmed",
    "amount": {
      "amount": "100.00",
      "currency": "USDC"
    },
    "occurredAt": "2026-06-15T12:05:00Z",
    "payload": {
      "raw": "provider-specific payload, stored as-is"
    }
  }'
201 Created
{
  "accepted": true,
  "duplicate": false,
  "flowLegIds": ["leg_01J..."],
  "matchedCaseIds": ["case_01J..."],
  "rawRecordId": "raw_01J...",
  "signatureValid": null,
  "source": "bridge",
  "sourceRef": "transfer_01J..."
}
matchedCaseIds tells you which reconciliation case(s) this evidence was matched against — in this walkthrough, the case created alongside your payment intent in step 2.Full reference: POST /v1/evidence/provider. For on-chain transfer evidence, see POST /v1/evidence/onchain.
4

Fetch the reconciliation case

Read back the reconciliation outcome using the caseId from step 2 (or a matchedCaseId from step 3 — they’re the same case in this walkthrough).
curl http://localhost:3001/v1/reconciliation-cases/case_01J... \
  -H "Authorization: Bearer $RECONLAYER_API_KEY"
200 OK
{
  "id": "case_01J...",
  "organizationId": "org_acme",
  "status": "open",
  "reconciliationStatus": "reconciled",
  "externalReference": "invoice-9182",
  "verdict": "matched",
  "unexplainedDelta": "0.00",
  "slaAgeMinutes": 5,
  "slaRisk": false,
  "requiredLegsTotal": 1,
  "requiredLegsPresent": 1,
  "evidenceCoverage": {
    "expected": "1",
    "provider": "1",
    "chain": "0",
    "bank": "0",
    "file": "0"
  },
  "providerFee": null,
  "networkFee": null,
  "developerFee": null,
  "fxSpread": null,
  "roundingDelta": null,
  "notes": null,
  "reconciledAt": "2026-06-15T12:05:01.000Z",
  "lastRunAt": "2026-06-15T12:05:01.000Z",
  "paymentIntent": { "...": "PaymentIntent summary" },
  "flowLegs": [ { "...": "FlowLegSummary" } ],
  "auditEvents": [ { "...": "AuditEvent" } ]
}
reconciliationStatus: "reconciled" and verdict: "matched" confirm the evidence you submitted in step 3 satisfied the expectation created in step 2.If the amounts didn’t line up, reconciliationStatus would be "unreconciled" and unexplainedDelta would carry the difference — that’s an exception for your team (or automation) to investigate.Full reference: GET /v1/reconciliation-cases/{caseId}. To list and filter cases in bulk, see GET /v1/reconciliation-cases.

What’s next

  • API Design Principles — idempotency, business-level uniqueness, and route conventions in depth.
  • Idempotency — the Idempotency-Key contract used in steps 2 and 3.
  • Errors — what 400/404/409/429 responses look like and how to handle them.
  • Pagination — listing payment intents and reconciliation cases at scale.
  • Rate limits — request limits and backoff guidance.
  • Authentication — the full credential and organization-resolution model.