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

# Transactions

> The core money movement operation in OMS: quote, then execute.

A transaction moves money through OMS. You specify a source instrument and a destination instrument on the quote; OMS infers the direction from those two sides and reports it as `sourceToDestination`. There is no settable transaction "type".

Standard sends and payouts follow a two-step flow: create a quote to lock pricing, then create a transaction to execute it. Cash-in and auto-created transactions (virtual accounts, deposit addresses) skip the quote step.

## Direction (`sourceToDestination`)

The `sourceToDestination` field is a composite of the source and destination instrument categories, inferred from each side:

| Value                 | Source                              | Destination                             |
| --------------------- | ----------------------------------- | --------------------------------------- |
| `cryptoToCrypto`      | OMS wallet                          | OMS wallet or external on-chain address |
| `cryptoToFiatAccount` | OMS wallet                          | Registered bank external account        |
| `cryptoToCash`        | OMS wallet                          | Cash pickup (early access)              |
| `fiatAccountToCrypto` | Bank account, via a virtual account | OMS wallet                              |
| `cashToCrypto`        | Cash, via a cash-in                 | OMS wallet                              |

## Two-step flow

```
POST /quotes   →  quote in "open" status (rate locked)
                    │
POST /transactions  →  transaction in "processing"
                    │
              completed | failed
```

The quote locks the exchange rate and fee breakdown for a short validity window. To execute it, call `POST /transactions` with a body of `{ "quoteId": "qt_..." }`. If the quote expires before you create the transaction, create a new quote.

## Quote object

```json theme={null}
{
  "id": "qt_...",
  "object": "quote",
  "status": "open",
  "customerId": "cst_...",
  "sourceToDestination": "cryptoToCrypto",
  "source": {
    "type": "walletCrypto",
    "category": "crypto",
    "id": "wlt_...",
    "asset": "usdc",
    "network": "polygon",
    "displayName": "0xabc…def"
  },
  "destination": {
    "type": "walletExternal",
    "category": "crypto",
    "id": "ext_wlt_...",
    "asset": "usdc",
    "network": "polygon",
    "displayName": "0x123…456"
  },
  "pricing": {
    "source": {
      "asset": "usdc",
      "amountGross": "100.00",
      "amountNet": "99.42",
      "feesDeducted": { "total": "0.58", "developer": "0.20", "oms": "0.35", "gas": "0.03" }
    },
    "destination": {
      "asset": "usdc",
      "amountGross": "99.42",
      "amountNet": "99.42"
    },
    "pair": "usdc/usdc",
    "exchangeRate": "1.0",
    "effectiveRate": "0.9942",
    "fixedAmountSide": "source"
  },
  "expiresAt": "2025-01-15T10:35:00Z"
}
```

Set `pricing.fixedAmountSide` to `"source"` when the user specifies how much to send, or `"destination"` when the user specifies how much to receive.

The `source` and `destination` are typed instruments discriminated by `type`: an OMS wallet (`walletCrypto`), an externally-custodied wallet (`walletExternal`), a bank account (`bankUs`, `bankIban`, `bankCanada`), a debit card (`card`), or cash pickup (`cash`). Each instrument carries the reference fields (`id`, `asset`, `network`) directly, alongside `type`, `category`, and `displayName`. The one exception is an unregistered external party, where `id` is `null` and a small `details` object carries either `blockchainAddress` (crypto) or `accountNumberLast4` and `bankName` (fiat).

## Party, precursor, and payout origin

Transaction and quote responses carry three flat reference objects that identify who is on each side, what created the transaction, and which of your resources funded the payout:

```json theme={null}
"source": {
  "type": "walletCrypto",
  "category": "crypto",
  "id": "wlt_...",
  "asset": "usdc",
  "network": "polygon",
  "party": { "relationship": "customer", "id": "cst_...", "name": "Jane Doe" }
},
"precursor": { "type": "depositAddress", "id": "da_..." },
"payoutOrigin": { "type": "virtualAccount", "id": "va_..." }
```

| Field          | Shape                        | Notes                                                                                                                                                                                                           |
| -------------- | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `party`        | `{ relationship, id, name }` | `relationship` is one of `customer`, `otherCustomer`, `externalRegistered`, `externalUnregistered`. Sits on the side it describes.                                                                              |
| `precursor`    | `{ type, id }`               | `type` is one of `depositAddress`, `virtualAccount`, `cashIn`, `quote`, or `manual`. `id` is `null` for the `manual` arm; the field itself is `null` for out-of-band arrivals with no OMS-originating resource. |
| `payoutOrigin` | `{ type, id }`               | `type` is `wallet` for a crypto-source send or `virtualAccount` for a fiat-account payout. `id` is `null` before execution and on cash off-ramp pickups.                                                        |

Dereference the referenced resource by `id` to retrieve richer detail. Bank account numbers, routing numbers, and BICs are not returned on `payoutOrigin`; read them from the funding [virtual account](/payments/virtual-accounts) or [external account](/payments/external-accounts) directly. Cash payouts render the `pickupCode` at the destination top level (a sibling of `payoutOrigin`), not inside the instrument.

## Instrument display name

Every source and destination instrument carries a `displayName`: an opaque, ready-to-render label for the value being moved. The field is always present and is the value you should show in receipts, tables, and confirmation UIs; do not compose your own from other instrument fields.

| Instrument                                 | `displayName`                                                                                 |
| ------------------------------------------ | --------------------------------------------------------------------------------------------- |
| `bankUs`, `bankIban`, `bankCanada`, `card` | The stored last four, mask-prefixed (`••••1234`)                                              |
| `walletOms`, `walletExternal`              | The blockchain address, falling back to the wallet ID when the address is not yet provisioned |
| `walletFiat`                               | The OMS wallet ID (`wlt_fiat_…`)                                                              |
| `cash`                                     | The retail location name                                                                      |

The value is derived server-side from the stored data on each instrument. Format is deliberately unversioned: OMS may refine the rendering without changing the schema, so treat it as an opaque string.

## Bank memo

Bank source and destination instruments carry an optional `memo` field on transaction responses. When populated, it echoes the wire, ACH, or SWIFT memo that actually travelled on the rail:

```json theme={null}
"source":      { "type": "bankUs",     "memo": "Invoice 20482" },
"destination": { "type": "bankIban",   "memo": "FFC Jane Doe 12345" }
```

OMS echoes only memos that a sender supplied. Any default text OMS may compose on behalf of a rail (for example, a `SELL PROCEEDS` line) never appears in the response. The field is present on `GET /transactions/{transactionId}`, on every entry of `GET /transactions`, and on `transaction.statusChanged` webhook payloads. Non-bank arms do not carry a memo.

## Rail tracking identifiers

Transactions and grouped transactions carry a top-level `tracking[]` array that lists rail identifiers for each leg that moved on a settlement rail:

```json theme={null}
"tracking": [
  {
    "scheme": "onchainTxHash",
    "value": "0xabc...def",
    "network": "polygon",
    "leg": "source",
    "assignedBy": "network",
    "url": "https://polygonscan.com/tx/0xabc...def"
  }
]
```

| Field        | Meaning                                                                                       |
| ------------ | --------------------------------------------------------------------------------------------- |
| `scheme`     | Identifier scheme, such as `onchainTxHash` for a blockchain transaction hash.                 |
| `value`      | The identifier itself.                                                                        |
| `network`    | The network the identifier is meaningful on (for example, a chain name).                      |
| `leg`        | Which leg of the transaction the identifier belongs to: `source`, `destination`, or `return`. |
| `assignedBy` | Who produced the identifier: `network` for a chain-generated hash, or an OMS-side assignor.   |
| `url`        | An optional explorer or lookup URL for the identifier, when one exists.                       |

The array is present on `GET /transactions/{transactionId}`, on every entry of `GET /transactions`, and on `transaction.statusChanged` webhook payloads. A single transaction may have multiple entries (for example, a same-chain crypto move plus a return leg). Prefer `tracking[]` over parsing rail-specific fields on the instrument arms.

## Customer summary on a transaction

Transactions carry a `customer` summary that resolves the transaction's customer in one hop:

```json theme={null}
"customer": {
  "id": "cst_...",
  "name": "Jane Doe"
}
```

The field is populated on `GET /transactions/{transactionId}`, in `POST /transactions` responses, and on every entry of `GET /transactions`. Names on a list response resolve from the same request context, so listing a page of transactions from the same customer does not multiply the underlying customer read. The same `customer` shape appears on virtual accounts and deposit addresses; external accounts carry the same field plus a bare `ownerDisplayName` for the owning customer or counterparty.

## Transaction statuses

| Status           | Meaning                                                                                                                          |
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `processing`     | Executing. Funds are being pulled or converted.                                                                                  |
| `awaitingAction` | Non-terminal. Blocked on developer, upstream, or compliance action; see the `hold` object. Returns to `processing` once cleared. |
| `completed`      | Funds delivered to the destination.                                                                                              |
| `failed`         | Terminal failure. The `error` object describes the cause.                                                                        |

OMS fires `transaction.statusChanged` on every status change, so your handler branches on the transaction's `status` field rather than parsing an event-name string. Subscribe with the Webhooks endpoints (`POST /webhooks` with a body of `{ url, subscriptions }`; pass `["*"]` for all partner-visible events) or in the OMS Dashboard. See the [transaction lifecycle](/payments/core-concepts/transaction-lifecycle) for the delivery model.

## Developer fees

Developer fees are configurable per integration and are never shown to the end user. Set them on your OMS account or pass them in the quote request. They appear in the `feesDeducted.developer` field on both the source and destination sides of the `pricing` object.

## Key operations

| Operation            | Endpoint                            |
| -------------------- | ----------------------------------- |
| Create a quote       | `POST /quotes`                      |
| Get a quote          | `GET /quotes/{quoteId}`             |
| Create a transaction | `POST /transactions`                |
| Get a transaction    | `GET /transactions/{transactionId}` |
| List transactions    | `GET /transactions`                 |

`GET /transactions` returns results newest-first and filters by `status`, `sourceToDestination`, `customerId`, the originating instrument (`walletId`, `virtualAccountId`, `depositAddressId`, `cashInId`), and inclusive `createdAfter`/`createdBefore` date bounds. Pagination uses `limit`, `startingAfter`, and `endingBefore`.

<Note>
  All `POST` endpoints accept an `Idempotency-Key` header. Use a stable key tied to your internal order ID to safely retry on network failure without risk of double-execution.
</Note>

## Related

* [Fiat to crypto guide](/payments/guides/fiat-to-crypto): cash-in and virtual-account funding
* [Send from a wallet guide](/payments/guides/crypto-to-fiat): crypto sends and bank payouts
* [Bank transfers guide](/api-reference/guide-bank-transfers): ACH and wire transfer details
