> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.thepennyinc.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.thepennyinc.com/_mcp/server.

# Transactions & Ledger

A financial activity in Penny can be represented in several ways depending on whether you need its current state, lifecycle history, or balance impact. There's no separate object for "funding" or "transfer": every movement of money, from a card purchase to an account-to-account transfer, is a Transaction, distinguished by its `type`, `channel`, and `counterparty`.

The diagram below follows a transfer, which produces a Transaction, Transaction Event, and Ledger Entry chain on each of its two accounts. Other activity, such as a card purchase, has a single chain on one account.

## Transactions

A **Transaction** represents the current financial and business state of an activity.

**What it's for:** it's the single record to read when you need to know what an activity represents right now (a card purchase, a transfer, funds received from a linked account) without reconstructing it from lower-level events or postings.

**How it behaves:**

* `type` distinguishes `payment` (a card purchase, or funds received from a linked account) from `transfer` (a movement between two of your Capital Accounts).
* `direction` is `inbound` or `outbound` relative to the account named on the transaction. A transfer between two of a Business Entity's own accounts produces **two** transactions, one `outbound` on the source and one `inbound` on the destination, not one transaction shared between them.
* `channel` records how it happened: `card` (with the card, cardholder, and network processing details), `internal` (a transfer between your Capital Accounts), or `bank` (funds received from a linked account, identified by `linked_account_id`).
* `counterparty` records who's on the other side: `card` (a merchant and acquirer), `transfer` (the account on the other side, by `account_id`), or `business` (another business, for transactions that cross business boundaries).
* Transactions can be associated with contextual resources including Capital Accounts, Control Accounts, Cards, Cardholders, merchants, and Spend Controls. Not every transaction type uses every resource.

See [Transactions](/banking/transactions) for the full field reference.

### Example: a transfer between two accounts

A transfer isn't one record shared between both accounts. Each side gets its own Transaction, its own Transaction Events, and its own Ledger Entries, tied to the account it affected:

Account A sees an `outbound` Transaction, whose event posts a debit Ledger Entry; Account B sees an `inbound` Transaction, whose event posts a credit Ledger Entry. `POST /accounts/capital/transfer` returns both, as `source_transaction` and `destination_transaction`. Reading either account's history on its own gives you the complete picture of what happened to its balance; you don't need the other side's records to reconcile one account.

## Transaction Events

**Transaction Events** represent changes to a transaction over time, advancing it through its lifecycle. Each one carries an `identifier`, which is one of:

| Identifier                     | Applies to                                             | Meaning                                                                                                             |
| ------------------------------ | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------- |
| `transaction.created`          | A new transaction                                      | Initializes the transaction.                                                                                        |
| `transaction.request.approved` | A new or existing transaction                          | Penny's own authorization decision approves the request: an initial hold, before final approval.                    |
| `transaction.request.declined` | A new or existing transaction                          | Penny's own authorization decision declines the request.                                                            |
| `transaction.approved`         | A new or existing, not-yet-approved transaction        | Final approval, decided by the card network or issuer, or applied directly when there's no separate request phase.  |
| `transaction.declined`         | A new or existing transaction                          | Final decline.                                                                                                      |
| `transaction.adjustment`       | An approved transaction                                | Changes the pending amount without changing lifecycle state, for example a tip or pre-authorization adjustment.     |
| `transaction.cancelled`        | An approved transaction                                | Releases the pending hold before anything settles.                                                                  |
| `transaction.expired`          | An approved transaction                                | The authorization hold timed out before settling.                                                                   |
| `transaction.settled`          | A new or existing, not declined/deprecated transaction | Funds moved. Can post more than once for a partial settlement, and can apply directly with no prior approval event. |
| `transaction.deprecated`       | An existing transaction                                | Administratively supersedes the transaction, zeroing all its amounts.                                               |

Penny's own authorization decision (`transaction.request.approved`/`transaction.request.declined`) and the final approval decided by the card network or issuer (`transaction.approved`/`transaction.declined`) are separate phases. A transaction can go through both, or go straight to final approval when there's no separate request phase. Either approval event puts the transaction in `approved` status.

**What it's for:** processing lifecycle changes or building event-driven integrations, where you need to react as a transaction moves from one state to the next rather than only its current snapshot.

**How it behaves:** the transaction's `stage` increases by one each time an event advances the transaction through its lifecycle. Use `stage` to detect out-of-order webhook delivery: if an event arrives with a `stage` lower than one you've already processed, it's safe to ignore. `GET /transactions/{transaction_id}/events` lists a transaction's events, and `GET /transactions/{transaction_id}/history` returns the full transaction snapshot at each stage.

Do not treat a transaction's current state and its event history as interchangeable: one represents the current projection, while the other describes changes over time.

### How events combine into a status

The transaction's overall `status` isn't one of the event identifiers above. It's derived from the combination of events applied so far, checked in this priority order:

```
deprecated
   ↓ (else)
declined
   ↓ (else)
settled → reversed (settled, but a matching movement in the opposite
          direction later brought the net effect back to zero)
        → partially_settled (settled amount doesn't yet match pending)
        → settled (fully matched)
   ↓ (else)
approved → expired (an expired event was applied)
         → cancelled (a cancelled event was applied)
         → approved (still pending)
   ↓ (else)
created
```

For example, `cancelled` and `expired` are **statuses** that result from applying a `transaction.cancelled` or `transaction.expired` event to an already-`approved` transaction.

## Ledger Entries

**Ledger Entries** represent the financial postings that affect balances: the resulting record once a Transaction Event posts, not the event itself.

**What it's for:** reconciliation and balance analysis, where you need the raw, append-only record of exactly what moved and when, rather than inferring financial postings from the current Transaction object.

**How it behaves:** each Transaction Event posts exactly one Ledger Entry, against the one account its Transaction belongs to. Most activity, such as a card purchase, has a single Transaction on a single account. A transfer is the exception: it produces two independent Transactions, one per account, so their separate Transaction Events each post their own Ledger Entry. The debit on the source account and the credit on the destination account are paired only in that they trace back to the same transfer; one event never splits into two entries. Ledger Entries are read per account (`GET /accounts/capital/{account_id}/ledger`, and the equivalent under `/accounts/control/{account_id}`) or across your business with `GET /ledger/`.

> **Info**
>
> Transaction state and ledger impact are related but serve different purposes. Reconciliation should use the financial records intended for that purpose.

## Resource context

A Transaction may reference additional resources that explain the activity, including the account, card, cardholder, merchant, or authorization controls involved. `GET /transactions/` accepts at most one scope filter per request (`account_id`, `card_id`, `cardholder_id`, `control_account_id`, or `program_id`), and `control_account_id` can't be combined with `stage_time` filters. You can also read transactions scoped directly under an account, card, or cardholder, or find them by your own reference with `GET /transactions/by-client-reference`.

These relationships provide context; they do not mean every transaction type uses every resource.

## Related concepts

* [Transactions](/banking/transactions)
* [Reading Transactions & Ledger Entries](/banking/reading-transactions)
* [Banking & Accounts](/get-started/platform-concepts/banking-accounts)
* [Issuing & Cards](/get-started/platform-concepts/issuing-cards)
* [Webhooks & Events](/get-started/platform-concepts/webhooks-events)