---
title: "Create a new transaction"
method: POST
path: "/v3/{ledgerName}/transactions"
tags: ["Transactions"]
---

# Create a new transaction

`POST /v3/{ledgerName}/transactions`

## Request

- Base URL: `http://localhost:9000`
- URL: `http://localhost:9000/v3/{ledgerName}/transactions`
- Auth: one of:
  - HTTP bearer
  - none

Creates a new transaction in the specified ledger with the specified postings. Requires `ledger:write` scope.

## Path parameters

- `ledgerName` string, required

## Headers

- `Formance-Bigint-As-String` string
- `Idempotency-Key` string

## Request body

- CreateTransactionRequest: A transaction must declare exactly one content source: explicit `postings`, an inline `script`, or a `scriptReference`. The `postings` array cannot be combined with `script` or `scriptReference`. Idempotency is controlled exclusively via the `Idempotency-Key` HTTP header — there is no body-level key.
  - `accountMetadata` object: Metadata for accounts involved in the transaction (supports typed values). Account addresses must be nonempty and contain only letters, digits, colons, underscores and hyphens, without empty segments. Malformed addresses are rejected before submission; competing invalid addresses select the lexicographically smallest address. Each account's map is bounded independently. Cluster policy defaults allow 128 entries per entity, 256 bytes per key, 16384 bytes per value, 65536 bytes per entity, and 1048576 bytes per request. Effective ceilings are configurable through the replicated cluster policy.
  - `timestamp` string, date-time: Transaction timestamp (ISO 8601 format). If not provided, current time is used.
  - `metadata` object: Transaction metadata (supports typed values). Cluster policy defaults allow 128 entries per entity, 256 bytes per key, 16384 bytes per value, 65536 bytes per entity, and 1048576 bytes per request. Effective ceilings are configurable through the replicated cluster policy.
  - `reference` string: Transaction reference
  - `postings` PostingRequest[]: List of postings for the transaction. Mutually exclusive with `script` and `scriptReference`.
    - `source` string, required: Source account address
    - `destination` string, required: Destination account address
    - `amount` union, required: Unsigned amount from 0 through 2^256-1. Requests accept JSON integer tokens or canonical decimal strings independently of the response header; the server enforces the bound for both forms. Responses use numbers by default and strings with Formance-Bigint-As-String enabled, as do all response Uint256 values regardless of business meaning.
      - integer
      - string
    - `asset` string, required: Asset identifier
    - `color` string: Optional segregation key for "color of money". The empty string (or omitted field) means the uncolored bucket. Two postings on the same (account, asset) but different colors operate on strictly isolated balances. Color is constrained to `^[A-Z]*$`, capped at 32 characters, and is immutable once carried by funds.
  - `script` Script
    - `plain` string, required: The numscript source code
    - `vars` object: Variables to pass to the script
  - `scriptReference` ScriptReference
    - `name` string, required: Name of the numscript in the ledger's library
    - `version` string, required: Version selector: the literal `latest` or an exact full semver (`1.2.3`). Partial selectors such as `1` or `1.2` are read-only and rejected here.
    - `vars` object: Variables to pass to the referenced script
  - `force` boolean: Skip the balance check, so the transaction commits even when a source account has insufficient funds.

## Response `201`

Transaction created successfully

- CreateTransactionResponse
  - `data` object, required
    - `transaction` TransactionResponse
      - `postings` PostingResponse[], required
        - `source` string, required
        - `destination` string, required
        - `amount` union, required: Unsigned amount from 0 through 2^256-1. Requests accept JSON integer tokens or canonical decimal strings independently of the response header; the server enforces the bound for both forms. Responses use numbers by default and strings with Formance-Bigint-As-String enabled, as do all response Uint256 values regardless of business meaning.
          - integer
          - string
        - `asset` string, required
        - `color` string, required: Segregation key for "color of money". The empty string means the uncolored bucket. Always present on responses (never omitted); two postings on the same (account, asset) but different colors operate on strictly isolated balances.
      - `metadata` object, required
      - `timestamp` string, date-time: Transaction timestamp (ISO 8601 format)
      - `reference` string
      - `id` integer, required: Transaction ID (assigned by the system)
      - `reverted` boolean, required: Whether this transaction has been reverted
      - `insertedAt` string, date-time: When the transaction was inserted into the ledger
      - `updatedAt` string, date-time: Last update timestamp
      - `revertedAt` string, date-time, nullable: When the transaction was reverted (null if not reverted)
      - `revertedByTransactionId` integer: ID of the transaction that reverted this one (absent when not reverted)
      - `revertsTransactionId` integer: ID of the original transaction this transaction reverts (absent when this is not a revert)
      - `postCommitVolumes` PostCommitVolumes: Post-commit volumes, keyed by account address. Each account maps to a flat array of per-(asset, color) volume tuples. The wire is a plain `{"addr": [{asset, color, input, output}]}` map — the proto `volumesByAccount` / `volumes` wrappers are flattened away by `PostCommitVolumes.MarshalJSON`. This replaces the pre-color `{"addr": {"asset": Volumes}}` map shape, which can no longer key a bucket uniquely once a color dimension exists.
    - `accountMetadata` object: Metadata for accounts involved in the transaction

## Other responses

- `400`: Bad request
- `401`: Authentication required (missing or invalid JWT token)
- `403`: Insufficient permissions (missing required scope)
- `404`: Not found
- `405`: Method not allowed
- `409`: Conflict (concurrent modification, or reference already claimed by another transaction). Callers that need continue-on-failure semantics should use the bulk endpoint with a per-entry `skippableReasons` list.
- `429`: Resource exhausted. Write admission can reject with `WRITES_BLOCKED_DISK_FULL` while disk pressure blocks writes; `SEQUENCE_EXHAUSTED` means an authoritative identifier allocator cannot allocate another identifier and is permanent for that allocator. Inspect `errorCode` before deciding whether to retry. No retry interval is supplied, and retrying cannot resolve sequence exhaustion.
- `500`: Internal server error
- `503`: Service unavailable — the server cannot serve the request right now but an immediate retry can succeed. The `errorCode` field discriminates the cause: `NO_LEADER` (no elected Raft leader), `CACHE_HORIZON_EXCEEDED` (admission snapshot too old, retry against a fresh one), `UNAVAILABLE` (an internal forwarded stream was torn down mid-transfer), or the reason of any domain error of the unavailable kind (e.g. `INDEX_BUILDING`).

## Changes

- **2026-11-03** `f5e817eca038`: 1 info
  - api operation id `createTransaction` removed and replaced with `v3CreateTransaction`
- **2026-10-15** `98fb67e06597`: 2 warning
  - removed the optional property `data/previousAccountMetadata` from the response with the `201` status
  - removed the optional property `data/warnings` from the response with the `201` status
- **2026-10-09** `0ed18e459f51`: 3 breaking, 4 info
  - response property `data/transaction/postCommitVolumes/additionalProperties/items/input` list-of-types was widened by adding types `integer` to media type `application/json` of response `201`
  - response property `data/transaction/postCommitVolumes/additionalProperties/items/output` list-of-types was widened by adding types `integer` to media type `application/json` of response `201`
  - response property `data/transaction/postings/items/amount` list-of-types was widened by adding types `string` to media type `application/json` of response `201`
  - added the new optional `header` request parameter `Formance-Bigint-As-String`
  - …3 more
- **2026-10-08** `3da23d327316`: 3 info
  - added the media type `text/plain` for the response with the status `401`
  - added the media type `text/plain` for the response with the status `403`
  - added the non-success response with the status `429`
- …earlier changes not shown

[Operation history](https://skmtc.dev/formancehq/apis/ledger-api/changes/v3/:ledgerName/transactions/post.md)

---

[API](https://skmtc.dev/formancehq/apis/ledger-api.md) - [All operations](https://skmtc.dev/formancehq/apis/ledger-api/llms.txt) - [OpenAPI document](https://skmtc.dev/formancehq/apis/ledger-api/revisions/f5e817eca038?raw)
