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

# Process a transaction

> The request a POS sends at the moment of sale, field by field, and how to read the response.

`POST /api/v1/transaction` is the endpoint a register calls after sales tax is computed and before the drawer opens. It needs a `merchant` or `partner` key with `transaction:create`.

## The request

```json theme={null}
{
  "merchant_id": "SCH-1",
  "location_id": "L1",
  "terminal_id": "REG-2",
  "cashier_id": "EMP-014",
  "subtotal_cents": 1000,
  "tax_total_cents": 73,
  "payment_method": "cash",
  "transaction_type": "retail",
  "transaction_timestamp": "2026-10-25T14:05:00-04:00"
}
```

| Field | Required | Notes |
| - | - | - |
| `merchant_id` | yes | Your merchant identifier (the `external_id` used at registration) |
| `subtotal_cents`, `tax_total_cents` | yes | Integers in cents. Tax is the amount computed on the pre-rounded subtotal. |
| `payment_method` or `tenders` | yes | `cash`, `card`, `mobile`, `ebt`; or a `tenders` array for split tender |
| `location_id` | recommended | The registered location (your `location_id` or the internal UUID). Requires a merchant-scoped key. |
| `zip_code` / `state`, `county`, `city` | if no `location_id` | Legacy resolution paths; with `location_id`, `zip_code` is a cross-check |
| `transaction_type` | recommended | Defaults to `retail`. See [Transaction types and tiers](https://docs.centsless.org/concepts/transaction-types-and-tiers.md). |
| `transaction_timestamp` | recommended | ISO 8601 with the POS clock's own UTC offset. Sets the local date for activation. |
| `terminal_id`, `store_id`, `cashier_id` | optional | Carried into the record for attribution |
| `transaction_location` | mobile only | Coordinates or an address for a sale made away from the registered location |
| `early_adoption`, `rounding_preference`, `local_adoption` | optional | Per-request versions of the merchant settings. See [Early adoption and operator choice](https://docs.centsless.org/guides/early-adoption-and-operator-choice.md). |
| `cash_rounding_mode` + `jurisdiction` | testing only | Explicit override; sealed as `manual` with no statute |

## The response

```json theme={null}
{
  "success": true,
  "transaction": {
    "id": "TXN-2026-10-25-8F360056",
    "timestamp": "2026-10-25T18:05:01.214Z",
    "merchant_id": "SCH-1",
    "location_id": "92018fdd-…",
    "state": "FL",
    "calculation": {
      "subtotal_cents": 1000,
      "tax_total_cents": 73,
      "ledger_total_cents": 1073,
      "cash_total_cents": 1075,
      "rounding_delta_cents": 2,
      "rounding_applied": true,
      "small_total_exempt": false,
      "cash_rounding_mode": "symmetrical_rounding",
      "jurisdiction": "US-FL",
      "jurisdiction_level": "state",
      "jurisdiction_status": "enacted",
      "jurisdiction_legislation": "SB 1074",
      "jurisdiction_obligation": "permits",
      "activation_state": "in_force",
      "jurisdiction_data_source": "database+registered_location"
    },
    "statutory_basis": { "...": "see The audit record" },
    "activation": { "state": "in_force", "local_transaction_date": "2026-10-25", "local_date_source": "sender_offset", "days_until_effective": 0 },
    "pending_rules": [],
    "location_resolution": { "method": "address_geocode", "confidence": "medium", "county_geoid": "12009", "place_geoid": "1271900", "zip_cross_check": null },
    "local_records": [ { "key": "FL:BREVARD:TITUSVILLE", "engine_supported": false } ],
    "compliance": { "compliance_status": "PASS", "fail_count": 0 },
    "fraud_score": 0,
    "risk_level": "NONE",
    "auditHash": "7f3c…",
    "previousHash": "a91e…",
    "chainPosition": 85,
    "hashSchema": 2
  }
}
```

**What the register uses:** `calculation.cash_total_cents` is the amount to collect. `rounding_delta_cents` is what to print on the receipt as the rounding adjustment. `ledger_total_cents` is what the books keep.

**What the evidence uses:** `statutory_basis`, `activation`, `location_resolution` and the hash fields.

**What a dashboard uses:** `pending_rules` tells the merchant what is coming at that location; `local_records` tells them a local instrument exists even though it does not yet apply.

## Where the rule came from

`calculation.jurisdiction_data_source` reads `database+registered_location` when a registered location governed, `database+location_override` when a mobile override did, `database` on the ZIP or name paths, and `hardcoded` only if the database was unreachable (the engine then applies its built-in table and the record says so).

## Idempotency and retries

Transaction ids are generated by the engine. A retried request creates a second record; the POS should hold its own reference and not resend on a `200`. On a network failure with no response, query `GET /api/v1/transactions?merchant_id=…` for the last minute before resending.

## Errors

| Status | Meaning |
| - | - |
| `400 INVALID_REQUEST` | A required field is missing or malformed; `errors` lists each problem |
| `404 LOCATION_NOT_FOUND` | `location_id` is not a registered location for this merchant |
| `409 LOCATION_INACTIVE` | The registered location was deactivated |
| `401`, `403`, `429` | See [Authentication](https://docs.centsless.org/authentication.md) and [Rate limits](https://docs.centsless.org/reference/rate-limits.md) |
| `500 PROCESSING_ERROR` | The engine could not complete the pipeline; nothing was sealed |


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