# Transaction

URL: /business/nouns/transaction · Id: `noun:transaction`

> A Transaction is money moving on a payment rail, such as a charge, a payout, or a transfer, reconciled to Entries.

A Transaction is money moving on a payment rail, such as a charge, a payout, or a transfer, reconciled to [Entries](/business/nouns/entry).

## Properties
| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `kind` | `string` | Yes | One of `charge`, `refund`, `payout`, or `transfer`. |
| `amount` | `money` | Yes | The amount moved. |
| `receipt` | [File](/business/nouns/file) | No | The receipt matched to a card Transaction. |
| `settledAt` | `datetime` | No | When the money became available in a cash Account. |

## Object
```json
{
  "$id": "https://api.sb/tutorloop.com/transactions/transaction_1",
  "$type": "Transaction",
  "kind": "charge",
  "amount": {
    "amount": 60,
    "currency": "USD"
  },
  "receipt": "file_1",
  "settledAt": "2026-10-22T12:00:00Z"
}
```

## Relationships
- <a id="rel-reconciles"></a>A Transaction **reconciles** many [Entries](/business/nouns/entry): the ledger [Entries](/business/nouns/entry) the Transaction settles, matched during reconciliation. `reconciles`, inverse [`reconciledBy`](/business/nouns/entry#rel-reconciled-by).

## Verbs
| Verb | Description | Subject | Event | Class |
| --- | --- | --- | --- | --- |
| [Transaction.attach](/business/nouns/transaction/attach) | Attach matches a receipt File to a card Transaction, or flags the Transaction when no receipt arrives in time. | [Finance Lead](/agents/finance/finance-lead) | [`Transaction.attached`](/business/events/Transaction.attached) | Two-way: `try`, `send`, `do` |
| [Transaction.categorize](/business/nouns/transaction/categorize) | Categorize codes a Transaction to an account in the chart by the Startup’s Policy and flags a low-confidence code for review. | [Finance Lead](/agents/finance/finance-lead) | [`Transaction.categorized`](/business/events/Transaction.categorized) | Two-way: `try`, `send`, `do` |
| [Transaction.reconcile](/business/nouns/transaction/reconcile) | Reconcile matches a Transaction on a payment rail to the ledger Entries it settles. | [Finance Lead](/agents/finance/finance-lead) | [`Transaction.reconciled`](/business/events/Transaction.reconciled) | Two-way: `try`, `send`, `do` |
| <a id="get"></a>[Transaction.get](/business/nouns/transaction/get) | Returns one Transaction by id. Inherited from [Noun](/business/nouns/noun). | None. | None. | Read: `try`, `do` |
| <a id="list"></a>[Transaction.list](/business/nouns/transaction/list) | Returns Transactions, newest first, one page at a time. Inherited from [Noun](/business/nouns/noun). | None. | None. | Read: `try`, `do` |

A Transaction is kept forever and never edited: it has no `update` or `delete`.

## Events
Events no Verb emits. Each Verb’s Event is in the Verbs table above; every Event is delivered in the same shape.

### Transaction.failed
The rail refused a charge or payout Transaction after it was sent. Raised by the rail.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | `string` | Yes | `Transaction.failed`. |
| `at` | `datetime` | Yes | When it happened. |
| `seq` | `integer` | No | Its position in the namespace, one more than the Event before; any seq is a cursor into the history. |
| `data` | `json` | Yes | The Thing as the Action left it. |
| `data.transaction` | [Transaction](/business/nouns/transaction) | Yes | The Transaction as the change left it. |
| `data.transaction.kind` | `string` | Yes | One of `charge`, `refund`, `payout`, or `transfer`. |
| `data.transaction.amount` | `money` | Yes | The amount moved. |
| `data.transaction.receipt` | [File](/business/nouns/file) | No | The receipt matched to a card Transaction. |
| `data.transaction.settledAt` | `datetime` | No | When the money became available in a cash Account. |
| `data.reason` | `text` | Yes | Why the rail refused it, in the rail’s words. |

```json
{
  "$id": "https://api.sb/tutorloop.com/events/event_1",
  "$type": "Event",
  "name": "Transaction.failed",
  "at": "2026-10-22T12:00:00Z",
  "data": {
    "transaction": {
      "$id": "https://api.sb/tutorloop.com/transactions/transaction_1",
      "$type": "Transaction",
      "kind": "charge",
      "amount": {
        "amount": 60,
        "currency": "USD"
      },
      "receipt": "file_1",
      "settledAt": "2026-10-22T12:00:00Z"
    },
    "reason": "The bank declined the transfer."
  },
  "seq": 1
}
```

### Transaction.settled
The money in a Transaction became available in a cash Account. Raised by the rail.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | `string` | Yes | `Transaction.settled`. |
| `at` | `datetime` | Yes | When it happened. |
| `seq` | `integer` | No | Its position in the namespace, one more than the Event before; any seq is a cursor into the history. |
| `data` | `json` | Yes | The Thing as the Action left it. |
| `data.transaction` | [Transaction](/business/nouns/transaction) | Yes | The Transaction as the change left it. |
| `data.transaction.kind` | `string` | Yes | One of `charge`, `refund`, `payout`, or `transfer`. |
| `data.transaction.amount` | `money` | Yes | The amount moved. |
| `data.transaction.receipt` | [File](/business/nouns/file) | No | The receipt matched to a card Transaction. |
| `data.transaction.settledAt` | `datetime` | No | When the money became available in a cash Account. |
| `data.account` | [Account](/business/nouns/account) | Yes | The cash Account the money is in. |
| `data.availableAt` | `datetime` | Yes | When the money became available. |

```json
{
  "$id": "https://api.sb/tutorloop.com/events/event_1",
  "$type": "Event",
  "name": "Transaction.settled",
  "at": "2026-10-22T12:00:00Z",
  "data": {
    "transaction": {
      "$id": "https://api.sb/tutorloop.com/transactions/transaction_1",
      "$type": "Transaction",
      "kind": "charge",
      "amount": {
        "amount": 60,
        "currency": "USD"
      },
      "receipt": "file_1",
      "settledAt": "2026-10-22T12:00:00Z"
    },
    "account": {
      "$id": "https://api.sb/tutorloop.com/accounts/account_1",
      "$type": "Account",
      "class": "cash"
    },
    "availableAt": "2026-10-22T12:00:00Z"
  },
  "seq": 1
}
```

## Permissions
Which Access level may call each Verb. One-way Verbs wait their delay window.

| Operation | `owner` | `admin` | `member` | `viewer` |
| --- | --- | --- | --- | --- |
| `Transaction.get` | Yes | Yes | Yes | Yes |
| `Transaction.list` | Yes | Yes | Yes | Yes |
| `Transaction.attach` | Yes | Yes | Yes | No |
| `Transaction.categorize` | Yes | Yes | Yes | No |
| `Transaction.reconcile` | Yes | Yes | Yes | No |

## Stripe
**PaymentIntent** (`payment_intent`), for a Transaction whose `kind` is `charge`. The source of truth is Stripe: db.sb mirrors it, written only from Stripe’s webhooks.

| Property | Stripe field | Source of truth |
| --- | --- | --- |
| `amount` | `amount` | Stripe |

**Payout** (`payout`), for a Transaction whose `kind` is `payout`. The source of truth is Stripe: db.sb mirrors it, written only from Stripe’s webhooks.

| Property | Stripe field | Source of truth |
| --- | --- | --- |
| `amount` | `amount` | Stripe |

**Refund** (`refund`), for a Transaction whose `kind` is `refund`. The source of truth is Stripe: db.sb mirrors it, written only from Stripe’s webhooks.

| Property | Stripe field | Source of truth |
| --- | --- | --- |
| `amount` | `amount` | Stripe |

## Call it

**Ask**

```text
Reconcile the latest transaction.
```

**API**

```bash
curl -X POST https://api.sb/kilnbakery.com/transactions/transaction_1/reconcile \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"
```

```json
{
  "api": "https://api.sb/kilnbakery.com",
  "transaction": {
    "$id": "https://api.sb/kilnbakery.com/transactions/transaction_1",
    "$type": "Transaction",
    "kind": "charge",
    "amount": {
      "amount": 9,
      "currency": "USD"
    }
  },
  "action": {
    "$id": "https://api.sb/kilnbakery.com/actions/action_1",
    "operation": "Transaction.reconcile",
    "status": "done"
  },
  "links": {
    "self": "https://api.sb/kilnbakery.com/actions/action_1"
  }
}
```

**SDK**

```ts
import { $ } from 'startup-builder'

const transaction = await $.Transaction.reconcile({ transaction: 'transaction_1' })
```

**CLI**

```bash
npx startup-builder do Transaction.reconcile --json '{"transaction":"transaction_1"}'
```

**MCP**

```json
{
  "name": "do",
  "arguments": {
    "code": "return await $.Transaction.reconcile({ transaction: 'transaction_1' })"
  }
}
```

## Example
A $49 Stripe charge is a Transaction. The monthly close reconciles it to two Entries.