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

# Build a Lumx reconciliation ledger

> A copy-paste prompt that builds a ledger reconciling Lumx transactions against your own records, with late and failed treated as different states.

Paste this prompt into your coding agent to build the ledger that answers "did this actually settle" without anyone opening a dashboard. The agent models each transaction type's statuses, ingests from both webhooks and the API, and tells a late payment apart from a failed one using the cut-off table.

## Before you start

* A Sandbox API key. See [Authentication](/get-started/authentication).
* Your internal ledger or accounting schema, so the agent can join to it.
* A webhook endpoint registered under **Developers > Webhooks** in the [Dashboard](https://dashboard.lumx.io). See [Webhooks](/developer/webhooks).

## Prompt

```text Prompt theme={null}
You are a senior backend engineer building a reconciliation ledger over Lumx transactions. The job is to answer, for any date, which movements settled, which are still in flight, and which failed — and to tell late apart from broken.

Ground truth. Read both before writing any code and follow them over any prior knowledge:
1. https://docs.lumx.io/llms.txt — the documentation index. Read /additional-information/sla-and-cutoffs and /concepts/transactions before designing the state model.
2. https://lumx-docs-public-prod.s3.us-east-1.amazonaws.com/api-production.yaml — the OpenAPI 3.1 spec. Status enums differ per transaction type; take each one from the spec rather than sharing one enum across types.

1. Model the three types separately. On-ramp, off-ramp and transfer each have their own status progression in the spec, and collapsing them into one enum is what makes a ledger lie. Read GET /transactions/{id} and map each type's statuses, including the terminal failure.
2. Ingest twice, from two directions. Webhooks give you the state change as it happens; GET /transactions with size and cursor gives you the backfill and the truth after an outage. Treat the API read as authoritative and the webhook as a trigger.
3. Key every row on the Lumx transaction id and your own reference. Put your reference in the metadata object on creation so the join exists before you need it — you cannot add it afterwards.
4. Read the receipt, not the request. On success the receipt carries the amounts, the rate, the fees and the chain transaction hash; the request carries what you asked for. A ledger built on request values overstates every conversion.
5. Distinguish late from failed using the per-rail cut-off table. PIX, SPEI and FPS are instant and 24/7; ACH, FEDWIRE and SWIFT have same-day cut-offs in US Eastern time and do not run on weekends or US holidays; SEPA is instant, except payments of €100k or more sent between 4:30 PM and 2:30 AM UK time, which route to SEPA Credit Transfer and settle the same or next business day. An off-ramp submitted after a rail's cut-off is not late until the next business day in that rail's own timezone. The transaction resource does not carry rail for an off-ramp, so join through the destination to learn which rail a payout used.
6. Define settled the way Lumx does: the off-ramp is complete once funds leave the banking partner. The beneficiary bank may still hold them, so your ledger needs a state for "sent, not yet credited" or it will report false failures.
7. Run the backfill sequentially and resumably, persisting the cursor, so a restart does not re-read the whole history.

Constraints:
- Do not state a rate limit for the backfill. None is published and the spec declares no 429 response, so paginate conservatively, handle 429 TOO_MANY_REQUESTS with exponential backoff, and record the missing limit as an open question.
- Never infer a status the spec does not list for that type. Flag an unknown status rather than mapping it to the nearest known one.
- state.error is an untyped object in the spec, and the failed-event examples carry only a generic message with no code. Build for an opaque blob, do not match on a code key that may not exist, and record the missing failure taxonomy as a question.
- onramp.expired carries status EXPIRED, which the on-ramp status enum in the spec does not list. Treat it as terminal, and log any event or status you cannot map rather than coercing it into one you know.

Deliverables:
- A ledger schema with per-type status columns and a late-versus-failed derivation.
- A resumable backfill plus a webhook-triggered incremental update.
- A daily report of unreconciled rows, each with the reason it is unreconciled.
```

## How to use

1. Start putting your own reference into transaction metadata today, even before the ledger exists. Rows created without it are the ones you will reconcile by hand.
2. Give the agent your existing internal schema; a ledger that does not join to your books is a second set of numbers.
3. Read the open question on the rate limit and take it to Lumx before the backfill runs against real volume.

## What the prompt builds

| **Endpoint** | **What the agent uses it for** |
| :- | :- |
| `GET /transactions` | Backfills the ledger with paginated reads |
| `GET /transactions/{id}` | Reads a single transaction's status and receipt |

## Errors to expect

These codes come from the [errors catalog](/developer/errors), for the resources this prompt uses. Match on `code`, not on `message`.

| **Code** | **Status** | **When it happens** |
| :- | :- | :- |
| `TRANSACTION_NOT_FOUND` | 404 | Transaction does not exist. |
| `TOO_MANY_REQUESTS` | 429 | Rate limit exceeded. |

## Related resources

<CardGroup cols={2}>
  <Card title="Transactions" href="/concepts/transactions">
    Transaction types and status progressions.
  </Card>

  <Card title="Payment rail cut-off times" href="/additional-information/sla-and-cutoffs">
    Cut-offs and settlement times per rail.
  </Card>

  <Card title="Audit an existing Lumx integration" href="/prompts/audit-lumx-integration">
    Find what costs money or fails compliance in a live integration.
  </Card>

  <Card title="Migrate from one PSP per country" href="/prompts/migrate-from-one-psp-per-country">
    Collapse one payment provider per country into one integration.
  </Card>
</CardGroup>


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