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

# Card Transactions

Send card-purchase events into Findity to auto-generate expenses for cardholders.

This guide covers the partner-side integration for card issuers — banks, fintechs, and Card-as-a-Service platforms — that want to embed Findity's expense management. You'll onboard a customer with a one-time setup call, then stream card transactions so each purchase becomes an expense in the cardholder's app, ready for receipt capture and approval.

## Overview

A card-issuer integration has two phases — a one-time customer onboarding flow and a continuous transaction stream. The layers below sit between your existing card-issuing platform and the Findity expense engine:

```
    ┌───────────────────────────────────────┐
    │       Your card-issuing platform      │
    │   (customer portal + card scheme      │
    │    authorization / settlement feed)   │
    ├───────────────────────────────────────┤
    │         Your Findity adapter          │
    │  (onboarding calls + transaction      │
    │   streaming + invoice reconciliation) │
    ├───────────────────────────────────────┤
    │           Findity APIs                │
    │   Admin API · Connect /cardtxns       │
    ├───────────────────────────────────────┤
    │       Findity Authentication          │
    │       (OAuth 2.0 access token)        │
    └───────────────────────────────────────┘
```

Onboarding runs once per customer (creating the organization, the cardholders, and at least one administrator). After that, every card authorization and every settlement on a connected card flows through a single `POST /cardtransactions` call. Transactions are processed asynchronously by default — the endpoint returns `202 Accepted` immediately and queues the transaction for processing. The closer to real-time you send them, the better the cardholder experience — expenses appear in the app within seconds of purchase.

## Prerequisites

1. Have Findity configure your `cardType` value. Contact your project manager or [Findity support](https://developer.findity.com/discuss) before development begins — `cardType` must match a value the backend recognises, otherwise every transaction returns `400`.
2. Complete [Authentication](/docs/accessing) and have an admin-scoped access token. The same token is used for onboarding calls (Admin API) and transaction calls (Connect API).
3. Decide which `liability` each card type you issue carries: `PRIVATE`, `CORPORATE`, or `DEBET`. This is not a per-transaction choice — it's tied to the card and dictates how Findity treats the resulting expenses. See *Key concepts* below.

## Key concepts

| Concept | Description |
| - | - |
| **`cardType`** | An identifier Findity provisions for your card brand (e.g. `acme-bank-corporate`). Used to route the transaction through the right pipeline. Cannot be invented by the integrator — must be configured by Findity in advance. |
| **`liability`** | Who pays the card's invoice. `PRIVATE` (cardholder pays, employer reimburses), `CORPORATE` (employer pays directly, no reimbursement), or `DEBET` (corporate bank account is charged directly, similar to corporate but for debit-card products). |
| **`eventType`** | The transaction's lifecycle stage from the card scheme: `AUTHORIZATION_ACCEPTED` (initial purchase), `AUTHORIZATION_REJECTED` (declined), `AUTHORIZATION_REVERSAL` (cancelled before settlement), `CAPTURE` (settlement completed), `REFUND` (returned), `PRE_AUTHORIZATION` (preliminary hold, hidden from cardholder until confirmed). |
| **`supplierCardId`** | Your unique identifier for the physical or virtual card. Findity uses it to look up the cardholder on subsequent transactions and to group same-card history. |
| **`transactionId`** | Your unique identifier for the transaction. Required on every call — Findity uses it to deduplicate retries and to match `AUTHORIZATION_ACCEPTED` events to their later `CAPTURE` updates. |
| **`invoiceNumber`** | Set on `CAPTURE` events. Findity groups all `CORPORATE`-liability transactions that share the same `invoiceNumber` into a single expense report, ready for the administrator to review against the card-issuer invoice. |

## API endpoints

Base URL for all examples in this guide:

```
https://stage-expense.findity.com
```

Use the matching production host (`https://expense.findity.com`) only after your integration is verified in stage.

### Create customer organization

```Text theme={"system"}
POST /api/admin/organizations/externalid/{externalId}
```

| Parameter | Type | Description |
| - | - | - |
| `externalId` | string | Your unique identifier for the customer organization |

See the [Admin API — Organizations](/docs/admin-api-organizations) guide for full request and response details.

### Create cardholder or administrator

```Text theme={"system"}
PUT /api/admin/organizations/externalid/{externalId}/users
```

| Parameter | Type | Description |
| - | - | - |
| `externalId` | string | Your unique identifier for the customer organization |

See the [Admin API — Users](/docs/admin-api-users) guide for full request and response details.

### Submit card transaction

```Text theme={"system"}
POST /v1/connect/cardtransactions
```

This is the primary endpoint for this integration — used for all transaction lifecycle events.

### Required headers

| Header | Value | Applies to |
| - | - | - |
| `Content-Type` | `application/json` | all three endpoints |
| `Authorization` | `Bearer {access_token}` | all three endpoints |
| `X-Findity-ApiVersion` | `3` | Admin API calls only (organizations, users). The Connect API does not version this way. |

## Customer onboarding

When a customer signs up to use Findity through your card-issuing platform — typically from a "My Pages" or equivalent surface — your adapter makes three sequential calls to provision the customer in Findity.

```
    Customer activates expense management
    in your card-issuing platform
                       │
                       ▼
    ┌─────────────────────────────────────────────┐
    │  Card issuer → Findity, three sequential    │
    │  Admin API calls:                           │
    ├─────────────────────────────────────────────┤
    │  1.  POST /api/admin/organizations/         │
    │      externalid/{externalId}                │
    │      → Create the customer's organization   │
    ├─────────────────────────────────────────────┤
    │  2.  PUT /api/admin/organizations/          │
    │      externalid/{externalId}/users          │
    │      → Add each cardholder as a user        │
    ├─────────────────────────────────────────────┤
    │  3.  PUT /api/admin/organizations/          │
    │      externalid/{externalId}/users          │
    │      → Add at least one administrator       │
    │        (set `admin: true` on the user)      │
    └─────────────────────────────────────────────┘
```

After these three calls succeed, the customer's organization is provisioned, its cardholders exist as users, and there is at least one administrator who can sign in to the Findity admin web client.

The administrator is who reviews corporate-card invoices, runs reports, and configures expense categories. The administrator does not need to be a cardholder, and a cardholder may also be made an administrator — set `admin: true` on the user object.

See the [Organizations](/docs/admin-api-organizations) and [Users](/docs/admin-api-users) guides for the full request and response shape of these calls.

<Info>
  **The `organizationId` and `personId` values returned by these onboarding calls are what you'll pass into every subsequent `POST /cardtransactions` call. Store them in your adapter, keyed by your own customer and cardholder ids.**
</Info>

## Add card to user

A user becomes a cardholder when a card is linked to their account. An employee can hold multiple cards (for example, a personal-liability card for travel plus a corporate-liability card for office purchases), but most integrations connect one card per cardholder.

The card's `liability` setting dictates how Findity treats the expenses generated from it. Pick the right value when the card is provisioned — changing it later requires migration.

| `liability` | Who pays the card invoice | Expense behaviour | Cardholder can delete expenses |
| - | - | - | - |
| **`PRIVATE`** | Cardholder, employer reimburses | Treated like any out-of-pocket purchase. Cardholder submits a report and is reimbursed via payroll or expense payout. | Yes — cardholders may delete a generated expense if the purchase was private. |
| **`CORPORATE`** | Employer is invoiced directly | Cardholder is not reimbursed. Expenses cannot be submitted individually — they are grouped by `invoiceNumber` after settlement and the cardholder submits the resulting corporate-card report. | No — corporate-card expenses are part of the audit trail and cannot be removed. |
| **`DEBET`** | Corporate bank account is charged | Similar to `CORPORATE` — no reimbursement, the transaction flows straight to bookkeeping. Use for debit-card products tied to an employer account. | No. |

<Warning>
  **The cardholder's user must exist in the organization (created in the onboarding step) before any transaction can be associated with them. A transaction with an unknown `personId` returns `400`.**
</Warning>

## Integration steps

After onboarding, your adapter spends most of its time on a single endpoint — `POST /api/v1/connect/cardtransactions` — with the `eventType` field driving the behaviour. The four steps below cover the full transaction lifecycle. Steps 1 and 3 fire on every successful transaction; steps 2 and 4 are conditional. Reuse the same `transactionId` across all events that belong to one purchase so Findity can stitch them together.

### Step 1 — Send the authorization

When the card scheme approves a purchase, send an `AUTHORIZATION_ACCEPTED` event so Findity creates the draft expense and notifies the cardholder via the mobile app. Generate a fresh `transactionId` here and reuse it on every subsequent event for this purchase.

```Text theme={"system"}
POST /api/v1/connect/cardtransactions
{
  "eventType": "AUTHORIZATION_ACCEPTED",
  "transactionId": "<your-unique-id>",
  ...
}
```

**Optional pre-step — pre-authorization.** For flows where the final amount isn't known yet (e.g. fuel pumps, hotel holds), send `PRE_AUTHORIZATION` first. Findity creates a draft expense that's hidden from the cardholder. Replay the same `transactionId` with `AUTHORIZATION_ACCEPTED` once the final amount is confirmed; the existing draft is updated and the cardholder is notified.

```Text theme={"system"}
POST /api/v1/connect/cardtransactions
{
  "eventType": "PRE_AUTHORIZATION",
  "transactionId": "<your-unique-id>",
  ...
}
```

See [*Generate expenses from card transaction*](#generate-expenses-from-card-transaction) for the full request body, the required fields, and a worked cURL example.

### Step 2 — Reverse or reject (if applicable)

If the card scheme cancels the authorization before settlement, or rejects it outright, replay the same `transactionId` with the matching `eventType`.

```Text theme={"system"}
POST /api/v1/connect/cardtransactions
{
  "eventType": "AUTHORIZATION_REVERSAL",
  "transactionId": "<same-id-as-step-1>",
  "billingAmount": <reduced-amount>,
  ...
}
```

```Text theme={"system"}
POST /api/v1/connect/cardtransactions
{
  "eventType": "AUTHORIZATION_REJECTED",
  "transactionId": "<same-id-as-step-1>",
  ...
}
```

Findity reduces the amount on `AUTHORIZATION_REVERSAL` and removes the draft expense entirely on `AUTHORIZATION_REJECTED`. After either event, the lifecycle is closed for this `transactionId` and no further events (except `REFUND` after a settled `CAPTURE`) are accepted.

### Step 3 — Capture on settlement

When the transaction settles (the funds actually move from the cardholder's account), send a `CAPTURE` event with the same `transactionId`. For `CORPORATE`-liability cards, include an `invoiceNumber` — Findity groups all transactions sharing the same `invoiceNumber` into a single corporate-card report that the cardholder submits as one batch.

```Text theme={"system"}
POST /api/v1/connect/cardtransactions
{
  "eventType": "CAPTURE",
  "transactionId": "<same-id-as-step-1>",
  "invoiceNumber": "INV-2026-05",
  "billingAmount": <final-amount>,
  ...
}
```

`CAPTURE` finalises the expense amount. If the cardholder hasn't yet attached a receipt, they're prompted to do so at this point. See [*Invoice matching*](#invoice-matching) for the full grouping behaviour.

### Step 4 — Process refunds (if applicable)

When a refund clears for a previously captured transaction, send a `REFUND` event using the same `transactionId`. Findity records the negative amount on the original expense and preserves the audit trail — the cardholder can see both the original purchase and the refund in their report.

```Text theme={"system"}
POST /api/v1/connect/cardtransactions
{
  "eventType": "REFUND",
  "transactionId": "<same-id-as-step-1>",
  "billingAmount": <refund-amount>,
  ...
}
```

For partial refunds, send the partial amount; Findity adjusts the net total accordingly. For full refunds, send the full original amount as `billingAmount` — do not negate the value, the negation is implicit from `eventType: REFUND`.

<Tip>
  The whole lifecycle uses a single endpoint with the same body shape — only `eventType` and the relevant amount
  fields change. Build one transport function in your adapter and switch on `eventType` at the boundary.
</Tip>

## Generate expenses from card transaction

For each purchase made on a connected card, your adapter sends a `POST /cardtransactions` request. Findity creates a draft expense for the cardholder, sends them a push notification through the Findity mobile app, and lets them attach a receipt and submit it for approval. The same endpoint is used for the full transaction lifecycle — initial authorization, optional reversal, settlement (`CAPTURE`), and refund — by varying the `eventType` field and reusing the same `transactionId`.

```
    Card issuer                       Findity
    ────────────────                  ─────────────────────────

    Cardholder
    makes a purchase
          │
          │   POST /cardtransactions
          │     eventType:
          │     AUTHORIZATION_ACCEPTED
          ├──────────────────────────────►   Receive transaction
          │                                          │
          │                                          ▼
          │                                  Create draft expense
          │                                  for the cardholder
          │                                          │
          │       Push notification                  │
          ◄──────────────────────────────────────────┤
          │                                          │
          ▼                                          ▼
    Settlement clears                         Cardholder attaches
          │                                   a receipt and submits
          │
          │   POST /cardtransactions
          │     eventType: CAPTURE
          │     invoiceNumber: <inv>
          ├──────────────────────────────►   Update the expense;
          │                                  if liability is
          │                                  CORPORATE, group it
          │                                  with siblings into
          │                                  one report keyed by
          │                                  invoiceNumber
```

### Request body — required fields

| Field | Type | Description |
| - | - | - |
| `cardType` | string | Your card brand, configured by Findity in advance (see *Prerequisites*). |
| `eventType` | string | One of `AUTHORIZATION_ACCEPTED`, `AUTHORIZATION_REJECTED`, `AUTHORIZATION_REVERSAL`, `CAPTURE`, `REFUND`, `PRE_AUTHORIZATION`. |
| `supplierCardId` | string | Your unique identifier for the card. |
| `transactionId` | string | Your unique identifier for the transaction. Reuse the same value across the full lifecycle (authorization → capture). For Mastercard, you may alternatively use `clearingUniqueId`. |
| `transactionDate` | string (ISO 8601) | When the purchase occurred. |
| `billingAmount` | number | Amount billed to the cardholder, in their billing currency. Maximum value: 1,000,000,000. |
| `billingCurrencyCode` | string | ISO 4217 code for `billingAmount` (e.g. `SEK`). |

### Request body — conditionally required fields

These fields are required depending on your card type and how the cardholder is identified:

| Field | Type | Required when | Description |
| - | - | - | - |
| `liability` | string | Required for non-real-time card types (not Mastercard/Visa) | One of `PRIVATE`, `CORPORATE`, `DEBET`. See the table in *Add card to user*. |
| `organizationId` | string | Required when identifying cardholder | The Findity organization id returned by the onboarding `POST /organizations` call. |
| `personId` | string | Required with `organizationId` (option 1) | The Findity person id of the cardholder, returned by the onboarding user create call. |
| `employeeId` | string | Alternative to `personId` (option 2) | The Findity employee id, if you stored this instead of `personId`. |

<Info>
  ****Cardholder identification**: You must provide either `organizationId` + `personId`, or `organizationId` + `employeeId`. If the card is already registered in Findity (via a previous transaction), the system can also look up the cardholder by `supplierCardId` alone.**
</Info>

### Request body — optional fields

| Field | Type | Description |
| - | - | - |
| `merchantAmount` | number | Amount in the merchant's currency, if different from billing currency. Maximum value: 1,000,000,000. |
| `merchantCurrencyCode` | string | ISO 4217 code for `merchantAmount`. |
| `billingExchangeRate` | number | FX rate applied to convert merchant amount to billing amount. Must be positive. Maximum value: 1,000,000,000. |
| `billingVatAmount` | number | VAT portion of `billingAmount`, when known. Maximum value: 1,000,000,000. |
| `merchantMcc` | number | Merchant Category Code (4-digit). |
| `posName` | string | The merchant name as it appears on the receipt. Surfaced on the expense for the cardholder. |
| `description` | string | Free-form description, surfaced alongside `posName` on the expense. |
| `cardIssuer` | string | The issuing institution name (useful when one `cardType` covers multiple sub-brands). If not provided, defaults to `cardType`. |
| `cardNumber` | string | Masked PAN (e.g. `XXXXXXXXXXXX6789`). Last four are usable for cardholder identification; full PAN must not be sent. |
| `country` | string | ISO 3166-1 alpha-2 or alpha-3 country code of the merchant. Numeric country codes are also accepted for Mastercard/Visa. |
| `city` | string | Merchant city. |
| `state` | string | Merchant state or region. Values starting with `--` are ignored. |
| `transactionReference` | string | A second identifier from your card scheme (for example, the original network reference) when `transactionId` alone isn't enough to trace. |
| `transactionOwnerName` | string | Display name of the cardholder. Findity shows this in the admin overview alongside the cardholder's user record. If not provided, defaults to the cardholder's name from their user profile. |
| `invoiceNumber` | string | Set on `CAPTURE` events. Groups all matching transactions into one corporate-card report (see *Invoice matching*). |
| `clearingUniqueId` | string | Alternative to `transactionId` for Mastercard transactions. |
| `vat` | string | Organization VAT number. Used as fallback to identify the organization if `organizationId` is not provided. |
| `employeeNumber` | string | Employee number for reference. |
| `contractID` | string | Contract identifier from your card scheme. If not provided, defaults to `supplierCardId`. |
| `authorizationId` | string | Authorization identifier from the card network. |
| `approvalCode` | string | Approval code from the card network. |
| `isSimulated` | boolean | Set to `true` for test/demo transactions. Default: `false`. |

### Example request

```sh cURL theme={"system"}
curl -X POST "https://stage-expense.findity.com/api/v1/connect/cardtransactions" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -d '{
    "cardType": "acme-bank-corporate",
    "posName": "Café Solde",
    "description": "Espresso, takeaway",
    "billingAmount": 250,
    "billingCurrencyCode": "SEK",
    "merchantAmount": 24,
    "merchantCurrencyCode": "EUR",
    "merchantMcc": 5921,
    "transactionDate": "2026-05-22T12:41:00Z",
    "transactionId": "464411049",
    "eventType": "AUTHORIZATION_ACCEPTED",
    "organizationId": "8a5c9cc090e8ccae0190edd18eca07d3",
    "personId": "8a5c848990e8cbb70190edd6cea907ba",
    "transactionOwnerName": "Bob Curry",
    "supplierCardId": "721c47e6-ba56-4c1f-82a2-1d3dbf8c81f2",
    "liability": "PRIVATE",
    "cardNumber": "XXXXXXXXXXXX6789"
  }'
```

### Example response

By default, transactions are processed asynchronously. A successful call returns `202 Accepted` with the request body echoed back, indicating the transaction has been queued for processing.

**Async response (default):**

```json theme={"system"}
{
  "cardType": "acme-bank-corporate",
  "posName": "Café Solde",
  "description": "Espresso, takeaway",
  "billingAmount": 250,
  "billingCurrencyCode": "SEK",
  "merchantAmount": 24,
  "merchantCurrencyCode": "EUR",
  "merchantMcc": 5921,
  "transactionDate": "2026-05-22T12:41:00Z",
  "transactionId": "464411049",
  "eventType": "AUTHORIZATION_ACCEPTED",
  "organizationId": "8a5c9cc090e8ccae0190edd18eca07d3",
  "personId": "8a5c848990e8cbb70190edd6cea907ba",
  "transactionOwnerName": "Bob Curry",
  "supplierCardId": "721c47e6-ba56-4c1f-82a2-1d3dbf8c81f2",
  "liability": "PRIVATE",
  "cardNumber": "XXXXXXXXXXXX6789"
}
```

**Sync response (with `?async=false`):**

For integration testing, you can add `?async=false` to process the transaction synchronously. This returns `201 Created` with the stored transaction(s), including the Findity-assigned `id` and `expenseRecordId`:

```json theme={"system"}
[
  {
    "id": "9a3b14f0a0d540e191f6c3d2b8ea1190",
    "expenseRecordId": "7f2c4d1a4b3e44e58b21e5c7f6d9aa01",
    "cardType": "acme-bank-corporate",
    "posName": "Café Solde",
    "description": "Espresso, takeaway",
    "billingAmount": 250,
    "billingCurrencyCode": "SEK",
    "merchantAmount": 24,
    "merchantCurrencyCode": "EUR",
    "merchantMcc": 5921,
    "transactionDate": "2026-05-22T12:41:00Z",
    "transactionId": "464411049",
    "eventType": "AUTHORIZATION_ACCEPTED",
    "organizationId": "8a5c9cc090e8ccae0190edd18eca07d3",
    "personId": "8a5c848990e8cbb70190edd6cea907ba",
    "transactionOwnerName": "Bob Curry",
    "supplierCardId": "721c47e6-ba56-4c1f-82a2-1d3dbf8c81f2",
    "liability": "PRIVATE",
    "cardNumber": "XXXXXXXXXXXX6789",
    "lastUpdated": "2026-05-22T12:41:01.482Z"
  }
]
```

<Warning>
  ****Production note**: Do not use `?async=false` in production — it may cause issues with simultaneous requests. Use it only for integration testing.**
</Warning>

<Note>
  Send `AUTHORIZATION_ACCEPTED` as close to real time as your card scheme allows — typically within a few seconds of
  the authorization clearing. The cardholder's push notification is generated when Findity processes the event, so any
  delay translates directly into a worse user experience.
</Note>

## Invoice matching

For `CORPORATE`-liability cards, the cardholder cannot submit individual expenses — purchases are invoiced to the employer monthly (or whatever your billing cycle is) and must be grouped to match the invoice. This is what `invoiceNumber` is for.

The flow:

1. During the month, you send `AUTHORIZATION_ACCEPTED` events for each purchase. Findity creates draft expenses; the cardholder attaches receipts but cannot submit yet.
2. When your card scheme settles a batch and issues an invoice, you replay each affected transaction with `eventType: CAPTURE`, the same `transactionId` as the original authorization, and the new `invoiceNumber`.
3. Findity matches the `CAPTURE` event to the original draft by `transactionId`, updates the expense with the final amount, and groups all expenses sharing the same `invoiceNumber` into one corporate-card report.
4. The cardholder submits the resulting report. The administrator sees an overview of open corporate-card reports and can send reminders to cardholders who haven't yet submitted.

### Example `CAPTURE` request

```sh cURL theme={"system"}
curl -X POST "https://stage-expense.findity.com/api/v1/connect/cardtransactions" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -d '{
    "cardType": "acme-bank-corporate",
    "transactionId": "464411049",
    "eventType": "CAPTURE",
    "invoiceNumber": "INV-2026-05",
    "billingAmount": 250,
    "billingCurrencyCode": "SEK",
    "transactionDate": "2026-05-22T12:41:00Z",
    "organizationId": "8a5c9cc090e8ccae0190edd18eca07d3",
    "personId": "8a5c848990e8cbb70190edd6cea907ba",
    "supplierCardId": "721c47e6-ba56-4c1f-82a2-1d3dbf8c81f2",
    "liability": "CORPORATE"
  }'
```

<Tip>
  You can update the same `transactionId` multiple times — for example, an `AUTHORIZATION_REVERSAL` followed by a
  `CAPTURE` for the corrected amount. Always send the same `transactionId` across the lifecycle so Findity can stitch
  the events together.
</Tip>

## Transaction lifecycle

Each transaction progresses through a lifecycle of events. The `eventType` field indicates the current stage:

```
    PRE_AUTHORIZATION ──► AUTHORIZATION_ACCEPTED ──► CAPTURE
          │                       │                     │
          │                       ▼                     ▼
          │              AUTHORIZATION_REVERSAL      REFUND
          │                       │
          ▼                       ▼
    AUTHORIZATION_REJECTED   (expense removed)
```

### Event type behavior

| eventType | Expense created | Cardholder notified | Can be followed by |
| - | - | - | - |
| `PRE_AUTHORIZATION` | Yes (hidden) | No | `AUTHORIZATION_ACCEPTED`, `AUTHORIZATION_REJECTED` |
| `AUTHORIZATION_ACCEPTED` | Yes (visible) | Yes (push notification) | `AUTHORIZATION_REVERSAL`, `CAPTURE`, `REFUND` |
| `AUTHORIZATION_REJECTED` | No (removed if existed) | No | None |
| `AUTHORIZATION_REVERSAL` | Updated (amount reduced) | No | `CAPTURE` |
| `CAPTURE` | Updated (finalized) | No | `REFUND` |
| `REFUND` | Updated (negative amount) | No | None |

### Capture timeout

If a `CAPTURE` event is not received within a configured timeout period (set per `cardType`), Findity automatically removes the draft expense. This prevents orphaned expenses from purchases that were never settled.

### Airline/travel accumulation

For airline and travel merchants (MCC codes 3005, 3010, 3020, 3030, 3031, 3041, 4511), multiple `CAPTURE` events with the same `transactionId` are accumulated onto the existing expense rather than creating duplicates. This handles the common pattern where airlines send multiple settlement records for a single booking.

## Error handling

Findity returns a JSON envelope on errors:

```json theme={"system"}
{ "result": "error", "code": "<machine-readable-code>", "description": "<human-readable message>" }
```

### Validation errors (400)

| Code | Description |
| - | - |
| `INVALID_JSON_BODY` | The request body could not be parsed as valid JSON. |
| `NO_KNOWN_JSON_PROPERTIES` | The request body contains no recognized fields. |
| `MISSING_PARAMETER` | A required field is missing. The `description` field indicates which parameter. Common missing fields: `cardType`, `supplierCardId`, `transactionId`, `eventType`, `billingAmount`, `billingCurrencyCode`, `transactionDate`, `liability` (for non-real-time cards). |
| `INVALID_PARAMETER` | A field has an invalid value. Common causes: invalid `eventType`, invalid `liability`, invalid currency code, negative `billingExchangeRate`, amount exceeds maximum (1,000,000,000). |
| `MISSING_CARD_LICENSE` | No active license exists for the specified `cardType` in the organization. Contact Findity to activate the card license. |
| `USER_NOT_FOUND` | The `organizationId`, `personId`, or `employeeId` does not exist in Findity, or the employee is not active in the specified organization. Confirm the IDs returned by the onboarding calls are what you're sending. |

### Authentication errors

| Status | Code | Description |
| - | - | - |
| `401` | `UNAUTHORIZED` | Access token is missing, expired, or invalid. Refresh the token and retry. |
| `403` | `FORBIDDEN` | The token does not have permission for the Connect API. Verify the API client is configured for this surface. |

### Other errors

| Status | Code | Description |
| - | - | - |
| `202` | *(no error)* | Returned when the card is not active (`CARD_IS_NOT_ACTIVE`). The transaction is silently dropped. This is not an error — it's expected behavior for deleted cards. |
| `404` | `NOT_FOUND` | The request path is wrong. Confirm you're calling `/api/v1/connect/cardtransactions` and not a misspelled variant. |
| `429` | `RATE_LIMIT_EXCEEDED` | Too many requests in a short window. Apply exponential backoff before retrying. |
| `500` | `INTERNAL_SERVER_ERROR` | Server error. Safe to retry with backoff; if persistent, contact support with the request body, `transactionId`, and the response. |

### Transaction order errors

Findity enforces transaction lifecycle order. You cannot send an `AUTHORIZATION_ACCEPTED`, `AUTHORIZATION_REVERSAL`, or `AUTHORIZATION_REJECTED` event after a `CAPTURE` or `REFUND` has been recorded for the same `transactionId`. Attempting to do so returns a `400` error with the message "Transactions received in the wrong order".

## Next steps

<Cards columns={2}>
  <Card title="Authentication" href="/docs/accessing" icon="fa-key">
    OAuth 2.0 token management — client credentials, refresh, scoped tokens.
  </Card>

  <Card title="Admin API — Getting started" href="/docs/getting-started-with-admin-api" icon="fa-flag-checkered">
    Provision customer organizations, cardholders, and administrators end-to-end.
  </Card>

  <Card title="Admin API — Users" href="/docs/admin-api-users" icon="fa-users">
    Full reference for adding and updating cardholders in the customer's organization.
  </Card>

  <Card title="Card transaction reference" href="/reference/addcardtransaction" icon="fa-code">
    Complete request and response schema for the `POST /cardtransactions` endpoint.
  </Card>
</Cards>
