Pocket Money - Relay API (0.6.0)

Download OpenAPI specification:

The Relay API lets Pocket Customers manage Accounts and recipients, move money, submit Batches of transfers, and reconcile Account-relative balances and activity.

  • A Customer (cus_…) is the person or business represented by the API key.
  • An Account (acc_…) holds a Customer's funds and has its own ID. A Customer can own multiple Accounts in the same currency.
  • A Recipient is another Pocket Customer saved for future transfers.
  • A Transfer is an outbound movement from an owned Account, either to another owned Account or to another Pocket Customer.
  • A Deposit is the inbound view of a movement into an owned Account.
  • A Batch groups one or more Customer-to-Customer transfers for submission and reconciliation.

Pocket publishes the Relay API as an OpenAPI specification rather than client SDKs.

API surface

Resource Purpose
Accounts Create, list, retrieve and label the Customer's funds containers.
Balances Read current funds by Account.
Recipients Manage a business Customer's payout roster.
Transactions Read inbound and outbound activity together.
Transfers Create and reconcile same-Customer Account transfers and Customer-to-Customer P2P.
Deposits Reconcile money received by the Customer.
Batch transfers Submit and reconcile asynchronous Customer-to-Customer payouts.

Specification 0.6.0 uses the existing /v1 paths. This is the Sandbox release candidate; it does not establish what is deployed to production.

Environments

Environment API base URL Documentation
Sandbox https://api.relay.sandbox.pocketmoney.global Sandbox docs
Production https://api.relay.pocketmoney.global Production docs

Use a key issued in the environment you are calling. Do not use a production key or production funds for Sandbox tests. Paths and JSON examples below are relative to the selected API base URL. Replace example IDs with resources in that environment. Use a new idempotency key for each new operation, and reuse that key when retrying the same operation.

For example, read Sandbox Accounts with an environment-specific bearer key:

curl 'https://api.relay.sandbox.pocketmoney.global/v1/accounts' \
  -H "Authorization: Bearer $RELAY_SANDBOX_API_KEY"

Supported scope and availability

The release contains 23 public operations across the seven resource families above. New Accounts, internal Transfers and Batches use USD. An Account's owner, currency, default designation and lifecycle status cannot be changed through these endpoints. Account updates change the label, reference and metadata.

Operations require appropriate permissions and eligible Customers and Accounts. Payments also require sufficient funds. Customer-to-Customer payments may have a limit on the total sent in the previous 30 days. This includes pending and settled payments. The limit can vary by Customer. Batch acceptance confirms submission; check each item's status to confirm payment.

Authentication and ownership

Send the API key as a bearer token:

Authorization: Bearer eak_v1_...

Every request runs as the Customer attached to that key. A source account_id must belong to it. Direct retrieval of another Customer's resource returns 404 as if it did not exist. List-filter error behaviour is described on each operation. Recipient, Account-create and Batch operations require an active, unblocked business Customer. Pending, suspended, closed, blocked or non-business Customers cannot use those business-only operations.

The key must also contain every scope named by the operation. The V1 scopes are accounts:read, accounts:write, balances:read, recipients:read, recipients:write, transactions:read, transfers:read, transfers:write, deposits:read, batches:read and batches:write. Broad read grants every read scope; broad write grants every read and write scope. Each granular write scope also grants its matching read scope (for example, accounts:write includes accounts:read, but not balances:read). Batch creation needs both batches:write and transfers:write; listing Batch Transfers needs both batches:read and transfers:read.

Keys authenticate API requests, not Relay browser sessions. Current key validity, ownership, scopes and applicable business eligibility are checked on retries too: an idempotency key is not an authorization bypass.

Transfers, deposits and transactions

An accepted internal transfer creates one txn_… ID. The source Account sees a Transfer and the receiving Account sees a Deposit. Addressing another Customer credits its immutable default Account. Addressing another Account owned by the same Customer creates an Account transfer with one debit and one credit view.

Accepted outbound money moves from available to reserved. At settlement it leaves the source balance and is credited to the destination balance. A failed or voided movement remains visible and the reservation is released.

Amounts

Amounts are decimal strings in major currency units. New payments must be positive USD amounts with at most two decimal places and no more than 9999999999999999.99 per payment. JSON numbers, commas, whitespace, scientific notation and negative values are rejected. Monetary amounts returned in balance and activity views follow the response schemas; a zero balance is valid.

Idempotency

Send the request key in the Idempotency-Key HTTP header, not in JSON. Create Account, Recipient, Transfer and Batch require it. Account PATCH, Recipient PUT and Recipient DELETE accept it optionally; omitting it does not provide saved-response protection. Keep the same key and input for retries of one operation. Do not generate a new key merely because the connection timed out.

Keys apply to a Customer and operation, even if the Customer uses another API key. For transfers, changing between destination.customer_id and destination.account_id while reusing the same key within 24 hours returns 409 Conflict. For updates and removals, using the same key for a different resource also returns 409 Conflict.

After a request succeeds, matching retries within 24 hours return the original response:

Operation Saved response
Create Account Initial 201 Account body and Location.
Update Account 200 Account body from that update, even after later edits.
Create Recipient Original 201 for a new Recipient, or 200 for an existing or restored one; original body and Location when present.
Replace Recipient 200 Recipient body from that replacement, even after later edits or removal.
Remove Recipient 204, without removing a subsequently re-added Recipient.
Create internal Transfer Initial 201 Transfer body and Location, even after settlement or failure.
Create Batch Initial 202 Batch body and Location, with the original counts.

Replay does not reapply an edit, restore a removed Recipient or send money again. Use GET for current state. It is not proof of settlement.

Concurrent matching requests wait for the first transaction to finish. If it commits, they replay its response; being in flight alone is not a conflict. A changed, valid request under an unexpired idempotency key returns 409 with details.code: idempotency_conflict and details.param: Idempotency-Key, without an existing_id. Each operation defines how it normalizes input before comparing it. Request validation still runs, so invalid input can return 400 instead. Authentication, authorization and maintenance checks also apply to retries.

Replay does not extend the 24-hour window. At or after expiry, the same key can identify new work, including another payment. Save resource IDs separately and reconcile uncertain outcomes before resubmitting. Use a new key for each new intent, not for a retry of an uncertain one.

Pagination

List operations use opaque cursors. Pass next_cursor back as cursor with the same filters and ordering. Do not parse or construct cursors. Stop when has_more is false. An invalid or mismatched cursor returns 400; restart from the first page. Pagination is not a frozen snapshot of resources that continue changing.

Errors

Errors use { code, message, details }. The outer code describes the HTTP error class; details.code, when present, is Pocket's machine-readable reason. Do not branch on message text. Details may be null for transport/authentication failures. Preserve unknown reason codes, and record details.request_id when provided for support.

HTTP status Meaning and next step
400 Invalid fields, IDs, filters or cursor; inspect details.param and correct the request. Recipient policy rejection can include per-item failures.
401 Missing, invalid, expired or revoked API credential, or wrong audience. Check the environment and key.
402 Direct Transfer rejected for insufficient available funds (insufficient_funds); no accepted Transfer is returned.
403 Insufficient scopes, Customer eligibility or permission. Retrying the same request does not grant access.
404 Resource missing or not visible to this Customer. Recipient resolution can use recipient_not_found without revealing the private cause.
409 State conflict or live-key input mismatch. Idempotency conflicts use outer aborted, reason idempotency_conflict and parameter Idempotency-Key; no existing_id is supplied.
429 Rate or rolling-send limit (rate_limited). Respect Retry-After when present; no fixed requests-per-second allowance is promised.
500 Internal failure; reconcile an uncertain mutation and retry with the same key within its replay window.
503 Temporary dependency unavailability or maintenance. Back off and use the same intent/key when retrying.

During maintenance, 503 can instead have details: { "maintenance": true, "retry_after_seconds": 60 }, without a Pocket reason or request ID. Maintenance headers (X-Pocket-Maintenance, Retry-After) may be present; authentication can reject the request before those headers are attached. The body remains the maintenance indicator.

Acceptance, settlement and uncertain outcomes

201 Created for a Transfer means it was accepted, not settled. 202 Accepted for a Batch means the Batch was saved for asynchronous execution, not that its items succeeded. GET returns current progress; replaying POST returns the original acceptance response. A terminal Batch can be completed, partially_completed or failed; reconcile each item, not just the Batch status.

If a response is lost, retry the same request with the same key within 24 hours. Once you have the resource ID, reconcile using GET and, for a Batch, its items and resulting Transfers. Retain each item's item_id and Transaction ID, and use terminal movement status to determine the payment outcome.

If the replay window has elapsed, or the outcome remains uncertain, do not blindly resubmit with the old or a new key: either can create another payment. Reconcile saved IDs and activity first, and contact Pocket when an outcome cannot be established. A reference is a reconciliation label, not a uniqueness constraint. Do not assume a failed HTTP request proves no money movement was accepted.

Payroll integration model

A payroll integration uses an API key for one active, unblocked business Customer. That Customer owns the Recipient roster and the source Accounts, and submits each Batch. Employees are destination Customers, not Accounts or Recipient IDs. Adding a Recipient does not create a Customer.

The normal flow is:

  1. Add each employee as a Recipient using an existing Customer ID or an exact supported identity lookup. Store both the Recipient ID and its destination Customer ID.
  2. Select the source Account and check that Account's available USD balance.
  3. Submit a Batch with either the Customer in source.customer_id to use its default Account, or a chosen Account in source.account_id. The resolved source Account is fixed when the Batch is accepted.
  4. Save the returned Batch ID and reconcile its items and resulting Transfers. Acceptance is asynchronous; it does not reserve the Batch total or guarantee that every item will be paid.

Recipient reads require recipients:read; Recipient writes require recipients:write. Submitting a Batch requires both batches:write and transfers:write. Reading Batches and their items requires batches:read; listing a Batch's Transfers also requires transfers:read. Scopes and Customer eligibility are checked again on retries.

Use the public /v1 endpoints for this integration. The Relay console's CSV preview/execute workflow and its operator permissions are a separate API surface; a public Batch submission queues execution directly.

Set up an employee for payroll

Add each employee to the authenticated business Customer's recipient roster. The Recipient has its own rcp_… ID for roster management. Its linked customer_id is the destination to use in a Batch; never send the Recipient ID as a payment destination.

POST /v1/customers/cus_01j8ma3wv3mepwabfxxs0yc395/recipients
Authorization: Bearer eak_v1_...
Idempotency-Key: add-employee-1042
Content-Type: application/json
{
  "customer_id": "cus_01jm0defbbct46vh2ev5dkkhej",
  "label": "Taylor Reed",
  "reference": "EMP-1042",
  "metadata": {"department": "Operations"}
}

Supply a nonblank label and at least one of customer_id, paytag, email, or phone. If several identifiers are supplied, all must resolve uniquely to the same existing, active, unblocked Customer other than the source Customer. Unresolved, conflicting or unavailable matches return the same privacy-preserving 404 with details.code: recipient_not_found. This operation does not create pending Recipients or onboard employees. Existing pending records remain readable, have customer_id: null, and cannot be used as Batch destinations.

A new Recipient returns 201 Created and a relative Location. An existing non-removed Recipient returns 200 OK without applying the submitted label, reference or metadata. A new request to re-add a removed Customer restores the same Recipient ID with the submitted fields and returns 200 OK.

Creation requires Idempotency-Key. An unchanged retry within 24 hours returns the saved original response, status and Location where present, even after later edits or removal. It does not restore or update the Recipient. Use GET for current state, especially capabilities.can_receive_transfers: a replay's capability value is a saved snapshot. The current capability checks roster and Customer eligibility, not wallet readiness, source funds, limits or the final payment outcome. Batch acceptance and execution perform further checks.

PUT replaces all four mutable fields: label, reference, status and metadata. Supply reference: null to clear it and metadata: {} to clear metadata; status may be active or inactive. DELETE soft-removes the Recipient and returns 204 No Content. Removed records remain readable but are excluded from unfiltered lists.

PUT and DELETE accept an optional Idempotency-Key. Use one to make a retry return the original response without reapplying an old edit or deleting a subsequently restored Recipient. Keys are scoped by business Customer and operation for 24 hours, not by API-key credential or Recipient ID. Changing the target or normalized input under an unexpired idempotency key returns 409 with details.code: idempotency_conflict. Use a new key for a new operation.

Read balances and activity

Each Account holds USD and has its own Account ID. Accounts belong to the Customer attached to your API key. A Customer can have more than one Account in the same currency. Use GET /v1/accounts (accounts:read) to discover them and GET /v1/accounts/{account_id} to read an Account's label, reference and metadata.

An eligible business Customer can create an additional, non-default Account with POST /v1/accounts (accounts:write). Pocket creates the default Account; this API cannot change ownership, currency, default selection or lifecycle status. Account creation requires an active default Account and its payment setup. Supplied labels must be unique within the Customer. References are not unique.

PATCH /v1/accounts/{account_id} updates supplied label, reference or metadata fields. Omitted fields remain unchanged, null clears a label or reference, and {} replaces metadata with an empty object. At least one field is required. Creation requires Idempotency-Key; updates support it optionally. Within 24 hours, matching keyed retries return the original Account response without reapplying the operation. Read the Account to see later changes.

GET /v1/balances requires balances:read and returns one page of Account balances, not one aggregated balance per currency:

{
  "data": [
    {
      "account_id": "acc_01j8ma3wv3mepwabfxxs0yc395",
      "currency": "USD",
      "reference": null,
      "available": "2500.00",
      "settled": "2500.00",
      "reserved": "0.00",
      "as_of": "2026-07-31T09:15:00Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}

Accounts with no ledger activity are included with zero amounts. settled is the posted balance; reserved is the amount held for pending outbound movements; available = settled - reserved. Pending incoming money is not spendable and does not increase these balances before settlement. Amounts are decimal strings; do not depend on a fixed number of trailing zeroes.

Account and balance lists accept currency=USD, status, an exact reference, limit (1–100, default 25) and an opaque cursor. They are ordered newest Account first, by creation time and Account ID. Keep the same filters and follow next_cursor while has_more is true. For one current balance, use GET /v1/accounts/{account_id}/balance with balances:read.

Use /v1/transactions for the combined activity feed, /v1/transfers for outbound movements and /v1/deposits for inbound movements. Every returned account_id identifies the exact Account-relative view. A transfer between two of your Accounts appears twice in the combined feed: one outbound view and one inbound view sharing the same transaction ID. Another Customer's private balances and activity are not accessible with your key.

Run a payroll batch

Submit one asynchronous Batch from the authenticated business Customer to its employee Customers:

POST /v1/batches
Authorization: Bearer eak_v1_...
Idempotency-Key: a15e4ae4-52f3-460c-9c1c-5f6b7b90cc44
Content-Type: application/json
{
  "source": {
    "account_id": "acc_01j8ma3wv3mepwabfxxs0yc395"
  },
  "recipient_policy": "recipients_only",
  "items": [
    {
      "item_id": "line-001",
      "destination": {
        "customer_id": "cus_01jm0defbbct46vh2ev5dkkhej"
      },
      "amount": "100.00",
      "currency": "USD",
      "reference": "EMP-1042"
    }
  ],
  "reference": "northwind-payroll-2026-07"
}

Use a new key for each pay run and keep the same key and input for retries of that submission. Send 1–4,096 items, each with a unique, case-sensitive, nonblank item_id of at most 255 characters. Amounts are positive USD decimal strings with at most two decimal places. Every destination must be another Customer; Batch items do not accept Account destinations.

recipients_only checks the entire request before acceptance: each destination must be an active eligible roster member. Pocket also checks that the destination can receive the payment. can_receive_transfers: true does not guarantee that the recipient can receive this payment. A failed pre-check returns 400, details.code: recipient_policy_failed, and details.failures entries containing item_id, an indexed param, and a privacy-preserving failure code. No Batch is created. Omitting the policy selects any_customer, which skips the roster pre-check and checks destination eligibility and payability when each item first executes.

Success returns 202 Accepted, a Batch body, and Location: /v1/batches/{batch_id}. Acceptance does not reserve the whole Batch amount. Each item checks the pinned source Account's available balance and applicable transfer rules when it runs; processing order is not guaranteed. The roster policy is not checked again after acceptance. There is no public cancel endpoint for an accepted Batch.

An unchanged retry within 24 hours returns the saved initial 202 response and Location, without resubmitting items or refreshing counts. GET the Batch for progress. A changed request under the same unexpired idempotency key returns 409 with details.code: idempotency_conflict; it does not include an existing Batch ID. Retries do not extend the 24-hour window. After expiry the key can create another Batch.

Use GET /v1/batches/{batch_id}/items to reconcile every submitted item_id. A pending item, or an internal_error item with transfer_id: null, is not proof that no payment started. The Transfer list contains only linked, visible Transfers, so an empty list is not proof that the Batch did nothing. If a submission result is lost, retry within the replay window using the same input and key. For an older uncertain result, locate the Batch by its saved ID or list/filter by your reference and creation time, then reconcile its items and source activity before submitting anything again. References are not unique and item_id only identifies a line within one Batch. Escalate an unresolved payment outcome for reconciliation; do not automatically copy ambiguous lines into a new Batch.

Send money to another Pocket customer

Create an internal transfer from the authenticated Customer to another Pocket Customer using a key with transfers:write:

POST /v1/transfers/internal
Authorization: Bearer eak_v1_...
Idempotency-Key: d9f80740-7f8e-4f16-b5cc-4fd5bf7d3b21
Content-Type: application/json
{
  "source": {
    "account_id": "acc_01j8ma3wv3mepwabfxxs0yc395"
  },
  "destination": {
    "customer_id": "cus_01jm0defbbct46vh2ev5dkkhej"
  },
  "amount": "100.00",
  "currency": "USD",
  "reference": "payrun-2026-07",
  "memo": "July salary"
}

Supply exactly one source selector and one destination selector. source.customer_id may instead identify the Customer attached to the API key, in which case Pocket uses its immutable default Account. An explicit source Account must belong to that Customer. A Customer destination must be a different Customer; it receives into its default Account. Its private Account ID, balance and unrelated activity are never exposed to the sender. The destination uses a Customer ID, not a Recipient relationship ID.

To move money between your own Accounts, set destination.account_id to another owned Account instead. It must differ from the resolved source Account. You cannot select another Customer's Account directly.

amount is a positive decimal string in USD, with at most two decimal places. reference (up to 255 characters) and memo (up to 100) are optional strings, not null. The reference is private to your Customer and is not a uniqueness constraint. The destination Customer can see the memo.

Both destination types require the Idempotency-Key header. Matching retries within 24 hours return the original response. Keep the same key and body when retrying a payment. Changing between destination.customer_id and destination.account_id with the same key returns 409 Conflict, as does changing the amount. Retries return the original 201, Location, status and timestamps. Reusing an expired key can create another transfer.

201 Created means the payment was accepted. While pending, the source amount is reserved and its available balance decreases; the destination is not yet credited. Settlement posts the source debit and destination credit. Failure or voiding releases the reservation without a destination credit. Use GET /v1/transfers/{transfer_id} with transfers:read to observe current state. For an owned-Account move, the transaction feed contains both Account views.

Transfers between your own Accounts are not subject to the 30-day sending limit. A Customer-to-Customer payment that exceeds this limit returns 429. Insufficient funds return 402. Both responses reject the new payment. If a response is lost, retry with the same key within its 24-hour window. After expiry, reconcile known transaction IDs and references before deciding whether to send again; a reference alone does not prevent duplicates.

Accounts

Directly addressable funds containers owned by the authenticated Customer.

List Accounts

Required scope: accounts:read.

Lists Accounts owned by the authenticated Customer, ordered by (created_at DESC, id DESC). More than one Account may use the same currency. All lifecycle states are included unless status filters them. The page size defaults to 25 and may be 1–100. Follow next_cursor while has_more is true, retaining the same filters.

Authorizations:
bearerAuth
query Parameters
currency
string (Currency)
Value: "USD"
Example: currency=USD

Public settlement currency debited from or credited to a Customer balance.

  • USD is the only value emitted in V1.
  • Clients must preserve and tolerate future values.
  • Cross-currency payouts may define separate destination-currency and foreign exchange (FX) fields.
status
string (AccountStatus)
Enum: "active" "suspended" "closed"

Account lifecycle state. It is read-only through the public API.

reference
string <= 255 characters ^[^\x00]*$

Exact match on your opaque reference for the object. Not unique. May match more than one object, and results are paginated. Values are not trimmed or case-folded. An empty value means no filter. Maximum 255 Unicode characters; NUL is invalid.

cursor
string <= 8192 characters

Opaque cursor (next_cursor from a prior page). Re-supply the same filters. A mismatch or invalid cursor returns 400.

limit
integer [ 1 .. 100 ]
Default: 25

Responses

Response samples

Content type
application/json
{
  • "has_more": true,
  • "next_cursor": "string",
  • "data": [
    ]
}

Create an Account

Required scope: accounts:write.

Creates an additional active USD Account. The authenticated Customer must be an eligible business. The immutable default Account is created by Pocket, not through this operation. Creation requires an active default Account with its payment setup. This endpoint does not provision receiving instructions or change the Customer's default routing.

Supplied labels must be unique within the Customer, including the empty string, and are compared exactly without trimming or case-folding. References are not unique. Duplicate labels and unusable Account/payment setup return 409 with details.code: conflict.

Idempotency: The Idempotency-Key HTTP header is required. Keys are scoped to the authenticated Customer and Account-create operation, not to the API-key credential. Matching input includes the Customer and the requested label, reference and metadata. Omitted metadata and {} have the same request identity. Omitted label/reference and empty strings are distinct. Reusing an unexpired idempotency key for different input returns 409.

For the default 24-hour window, an unchanged retry returns the saved initial 201 Created Account response and original Location without creating another Account. Later changes to the Account's label, status, metadata or timestamps do not change that response. It does not return or overwrite the Account's current state. Use GET /v1/accounts/{account_id} for current state. Authentication and authorization apply to every attempt.

Concurrent requests with the same key and input wait for the first attempt to commit successfully, then replay its response. Being in flight alone does not produce an idempotency conflict. Retries do not extend the window. Keys do not permanently identify Accounts: at or after 24-hour expiry, reusing the key can create another Account. Retain the returned Account ID for subsequent operations.

Example request headers:

POST /v1/accounts
Authorization: Bearer eak_v1_...
Idempotency-Key: create-payroll-account-1042
Content-Type: application/json
Authorizations:
bearerAuth
header Parameters
Idempotency-Key
required
string [ 1 .. 255 ] characters ^[^\x00]*[^\s\x00][^\x00]*$
Example: create-payroll-account-1042

Required, case-sensitive request key. Supply exactly one nonblank value containing at most 255 Unicode code points, encoded as valid UTF-8 with no NUL character. Missing, empty, blank, invalid or repeated header values return 400 with details.param set to Idempotency-Key.

Request Body schema: application/json
required
label
string <= 255 characters ^[^\x00]*$

Customer-scoped unique label, compared exactly as supplied.

reference
string <= 255 characters ^[^\x00]*$

Private, non-unique reference.

object (AccountMetadata) <= 50 properties

Private string metadata for an Account. At most 50 entries. Keys must be nonempty and at most 64 Unicode characters; values may be empty and at most 256 Unicode characters. Neither keys nor values may contain NUL. Values are preserved without trimming, case-folding or normalization.

Account validation reports 400 invalid_request with param: metadata for an invalid key or value. Use opaque business identifiers and non-sensitive labels, not credentials or personal financial information.

Responses

Request samples

Content type
application/json
{
  • "label": "Payroll",
  • "reference": "payroll-1042",
  • "metadata": { }
}

Response samples

Content type
application/json
{
  • "id": "acc_01j8ma3wv3mepwabfxxs0yc395",
  • "currency": "USD",
  • "status": "active",
  • "is_default": true,
  • "label": "string",
  • "reference": "string",
  • "metadata": {
    },
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Retrieve an Account

Required scope: accounts:read.

Returns the owned Account's current state, including inactive Accounts. A malformed Account ID returns 400; a missing or unowned Account returns 404. This read is not a replay of an earlier create or update response.

Authorizations:
bearerAuth
path Parameters
account_id
required
string^acc_[0-7][0-9abcdefghjkmnpqrstvwxyz]{25}$
Example: acc_01j8ma3wv3mepwabfxxs0yc395

An Account owned by the authenticated Customer.

Responses

Response samples

Content type
application/json
{
  • "id": "acc_01j8ma3wv3mepwabfxxs0yc395",
  • "currency": "USD",
  • "status": "active",
  • "is_default": true,
  • "label": "string",
  • "reference": "string",
  • "metadata": {
    },
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Update an Account

Required scope: accounts:write.

Replaces supplied presentation fields. Ownership, currency, default selection and lifecycle status are read-only in 0.6.0.

Supply at least one of label, reference or metadata. Omitted fields remain unchanged; null clears label or reference, and {} clears metadata. A supplied metadata object replaces the complete map. Supplied labels must be unique within the Customer, including the empty string; duplicate labels return 409 conflict. References are not unique.

Idempotency: The optional Idempotency-Key header binds a request for 24 hours. Omitting the header performs an unkeyed update. The body must not contain idempotency_key.

Keys are scoped to the authenticated Customer and Account-update operation, not to the API-key credential or target Account. The request identity includes the Customer, target Account, and supplied presentation fields, distinguishing omitted fields from explicit clears. Reusing an unexpired idempotency key for another Account or different patch input returns 409.

An unchanged retry returns the saved successful 200 OK Account response, including the original updated_at, without applying the update again. A later change to the Account does not change this saved response. Use GET /v1/accounts/{account_id} to retrieve current state. Authentication and authorization still apply to every attempt.

Concurrent requests with the same key and input wait for the first attempt to commit successfully and then replay its response. Being in flight alone does not produce an idempotency conflict. Retries do not extend the window; at or after 24-hour expiry, the key can identify a new update.

Authorizations:
bearerAuth
path Parameters
account_id
required
string^acc_[0-7][0-9abcdefghjkmnpqrstvwxyz]{25}$
Example: acc_01j8ma3wv3mepwabfxxs0yc395

An Account owned by the authenticated Customer.

header Parameters
Idempotency-Key
string [ 1 .. 255 ] characters ^[^\x00]*[^\s\x00][^\x00]*$
Example: update-payroll-account-1042

Optional, case-sensitive request key. Supply exactly one nonblank value containing at most 255 Unicode code points, encoded as valid UTF-8 with no NUL character. Empty, blank, invalid or repeated header values return 400 with details.param set to Idempotency-Key.

Request Body schema: application/json
required
Any of
label
required
string or null <= 255 characters ^[^\x00]*$
reference
string or null <= 255 characters ^[^\x00]*$
object (AccountMetadata) <= 50 properties

Private string metadata for an Account. At most 50 entries. Keys must be nonempty and at most 64 Unicode characters; values may be empty and at most 256 Unicode characters. Neither keys nor values may contain NUL. Values are preserved without trimming, case-folding or normalization.

Account validation reports 400 invalid_request with param: metadata for an invalid key or value. Use opaque business identifiers and non-sensitive labels, not credentials or personal financial information.

Responses

Request samples

Content type
application/json
{
  • "label": "string",
  • "reference": "string",
  • "metadata": {
    }
}

Response samples

Content type
application/json
{
  • "id": "acc_01j8ma3wv3mepwabfxxs0yc395",
  • "currency": "USD",
  • "status": "active",
  • "is_default": true,
  • "label": "string",
  • "reference": "string",
  • "metadata": {
    },
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Retrieve an Account balance

Required scope: balances:read.

Returns current ledger-derived funds for one owned Account, not a Customer-wide total. An Account without ledger activity has zero amounts. A malformed Account ID returns 400; a missing or unowned Account returns 404. as_of is the snapshot time, not a synchronization token.

Authorizations:
bearerAuth
path Parameters
account_id
required
string^acc_[0-7][0-9abcdefghjkmnpqrstvwxyz]{25}$
Example: acc_01j8ma3wv3mepwabfxxs0yc395

An Account owned by the authenticated Customer.

Responses

Response samples

Content type
application/json
{
  • "account_id": "acc_01j8ma3wv3mepwabfxxs0yc395",
  • "currency": "USD",
  • "reference": null,
  • "available": "2500.00",
  • "settled": "2500.00",
  • "reserved": "0.00",
  • "as_of": "2026-07-31T09:15:00Z"
}

Balances

Read current ledger-derived funds by Account.

Retrieve an Account balance

Required scope: balances:read.

Returns current ledger-derived funds for one owned Account, not a Customer-wide total. An Account without ledger activity has zero amounts. A malformed Account ID returns 400; a missing or unowned Account returns 404. as_of is the snapshot time, not a synchronization token.

Authorizations:
bearerAuth
path Parameters
account_id
required
string^acc_[0-7][0-9abcdefghjkmnpqrstvwxyz]{25}$
Example: acc_01j8ma3wv3mepwabfxxs0yc395

An Account owned by the authenticated Customer.

Responses

Response samples

Content type
application/json
{
  • "account_id": "acc_01j8ma3wv3mepwabfxxs0yc395",
  • "currency": "USD",
  • "reference": null,
  • "available": "2500.00",
  • "settled": "2500.00",
  • "reserved": "0.00",
  • "as_of": "2026-07-31T09:15:00Z"
}

List balances

Required scope: balances:read.

Returns one page of balances for matching Accounts owned by the authenticated Customer, not an aggregate per currency. Accounts with no ledger activity are included with zero amounts. All lifecycle states are included unless status filters them.

Accounts are ordered by (created_at DESC, id DESC). The page size defaults to 25 and may be 1–100. Follow next_cursor while has_more is true, retaining the same filters.

Authorizations:
bearerAuth
query Parameters
currency
string (Currency)
Value: "USD"
Example: currency=USD

Only balances in this currency.

status
string (AccountStatus)
Enum: "active" "suspended" "closed"

Account lifecycle state. It is read-only through the public API.

reference
string <= 255 characters ^[^\x00]*$

Exact match on your opaque reference for the object. Not unique. May match more than one object, and results are paginated. Values are not trimmed or case-folded. An empty value means no filter. Maximum 255 Unicode characters; NUL is invalid.

cursor
string <= 8192 characters

Opaque cursor (next_cursor from a prior page). Re-supply the same filters. A mismatch or invalid cursor returns 400.

limit
integer [ 1 .. 100 ]
Default: 25

Responses

Response samples

Content type
application/json
{
  • "has_more": true,
  • "next_cursor": "string",
  • "data": [
    ]
}

Recipients

Recipients are business-owned roster resources identified by rcp_…. A pending Recipient has no matched Customer. A linked Recipient returns the Customer cus_… ID that Transfers and Batch items use as their destination.

Recipient resources belong to the authenticated business Customer. An authenticated individual Customer returns 403. Another Customer returns 404 when used as business_customer_id.

  • A Recipient id manages the roster resource; it is never a payment destination.
  • A linked Recipient's customer_id is the Customer destination for Transfers and Batch items. It is null while the Recipient is pending.
  • Adding a Recipient resolves one or more exact identifiers to an existing Customer. An incomplete or conflicting match creates nothing.
  • A Recipient never grants access to or changes a linked Customer.
  • The business supplies the recipient label. Pocket does not return the Customer's profile name, email, phone, government identity, KYC state, or onboarding reason.
  • Raw email and phone inputs are sensitive. Pocket never returns them or includes them in errors, logs, traces, metadata, events, or webhook subjects.

Pocket normalizes identity inputs before matching:

  • customer_id: exact Pocket cus_… ID.
  • paytag: surrounding whitespace and one optional leading @ removed, then normalized with Pocket's paytag case rules.
  • phone: E.164 international phone-number format.
  • email: surrounding whitespace trimmed, then compared case-insensitively after Pocket's email normalization.

Supply at least one identifier. When several are supplied, every value must resolve uniquely to the same Customer. A malformed identifier returns 400 with param naming the field. Conflicting matches, ambiguity, blocks, and unavailable targets return the same generic 404 recipient_not_found and create nothing.

Add a recipient to a business

Required scope: recipients:write.

Customer eligibility: authenticated business Customer only.

Create a Recipient (rcp_…) on this business's roster. Pocket first attempts to resolve one or more exact identifiers to an existing Customer. A linked Recipient returns that Customer's customer_id, which is the destination to use in internal Transfers and Batch items. The Recipient id manages the roster resource and is never a payment destination.

business_customer_id must identify the authenticated business Customer. Another Customer returns 404.

Lookup and assurance

Supply at least one of customer_id, exact paytag, email, or phone. You may supply several identifiers to increase confidence in the match. Every supplied identifier must resolve uniquely to the same Customer.

Government-ID and name lookup are not supported. Email and phone lookups match the identifiers stored for an existing Customer; the response does not provide identity-verification evidence or an assurance level.

When the identifiers do not resolve completely to one Customer, Pocket returns 404 recipient_not_found and creates nothing. This includes an unknown explicit customer_id, conflicting matches, ambiguous identity data, blocked relationships, and unavailable targets. The response does not identify which value failed. The target must be active, unblocked, and different from the authenticated business Customer. This operation does not create a Customer or a pending Recipient. Existing pending Recipient records remain readable.

Recipient data

label, reference, and metadata belong to this business's roster relationship. Pocket does not compare label with or replace it from the Customer's profile.

Retries and re-adding

The Idempotency-Key HTTP header is required. Keys are scoped to the authenticated Customer and Recipient-create operation. Matching input includes the Customer and normalized lookup identifiers, label, reference and metadata. A changed normalized request with the same live key returns 409 idempotency_conflict.

For the default 24-hour window, a matching retry returns the first successful response without executing the operation again. Replay preserves the saved Recipient body, original HTTP status, and original Location header value or absence. Later edits or removal do not change the saved body, including its status, capabilities and timestamps. Replay does not re-add or restore a removed Recipient. Use GET to retrieve current state.

Concurrent requests with the same key and normalized input wait for the first attempt to commit successfully, then replay its response. Being in flight alone does not produce an idempotency conflict. Retries do not extend the window; at or after 24-hour expiry, the key can identify new work. Authentication and authorization apply to every attempt.

A new linked Recipient returns 201 Created. A request that identifies an existing non-removed Recipient returns 200 OK without changing its business-owned fields; use PUT to replace their mutable state. A replay preserves that original 201 or 200 status.

Re-adding a removed Recipient restores it using the same ID and the newly submitted label, reference and metadata, and returns 200 OK.

Privacy

The response returns the Recipient id and a nullable resolved customer_id, but no Pocket profile name, email, phone, paytag, government identity, KYC state, onboarding state, or private capability reason. Raw email and phone are sensitive lookup inputs; they are not returned on the Recipient or echoed in public lookup errors. Do not copy them into metadata.

Authorizations:
bearerAuth
path Parameters
business_customer_id
required
string^cus_[0-7][0-9abcdefghjkmnpqrstvwxyz]{25}$
Example: cus_01j8ma3wv3mepwabfxxs0yc395

The authenticated business Customer whose recipient roster the API manages. An individual Customer returns 403. Another Customer returns 404.

header Parameters
Idempotency-Key
required
string [ 1 .. 255 ] characters ^[^\x00]*[^\s\x00][^\x00]*$
Example: add-employee-1042

Required, case-sensitive request key. Supply exactly one nonblank value containing at most 255 Unicode code points, encoded as valid UTF-8 with no NUL character. Missing, empty, blank, invalid or repeated header values return 400 with details.param set to Idempotency-Key.

Request Body schema: application/json
required
Any of
label
required
string [ 1 .. 120 ] characters ^[^\x00]*\S[^\x00]*$

Business-supplied nonblank display label. The length limit is checked before surrounding whitespace is trimmed.

customer_id
required
string (RecipientCustomerIdLookup) ^cus_[0-7][0-9abcdefghjkmnpqrstvwxyz]{25}$

Match by the customer's exact Pocket ID (cus_…). This value is not normalized.

paytag
string (RecipientPaytagLookup) [ 1 .. 32 ] characters ^[^\x00]*[^\s\x00][^\x00]*$

Match by exact paytag after trimming surrounding whitespace, removing at most one leading @, and lowercasing. The input limit is 32 code points before normalization. The canonical value must be 1–30 ASCII letters, digits or underscores, contain a letter, and not start or end with an underscore. Reserved paytags are invalid.

email
string <email> (RecipientEmailLookup) <= 254 characters ^[^\x00]*$

Match a stored Customer email after trimming surrounding whitespace and lowercasing it. A syntactically valid mailbox address is required; display names are not accepted. This sensitive lookup input is not returned on the Recipient and does not provide verification evidence in the response.

phone
string (RecipientPhoneLookup) ^\+[1-9][0-9]{1,14}$

Match a stored Customer phone using E.164 form (for example, +61412345678). The number must also pass phone-number validation; permitted Sandbox test numbers are the environment-specific exception. This sensitive lookup input is not returned on the Recipient and does not provide verification evidence in the response.

reference
string or null <= 255 characters ^[^\x00]*$

Optional business-owned reconciliation reference. Omitted and null are equivalent; an empty string is preserved and is distinct from null.

object (Metadata) <= 50 properties

Up to 50 caller-defined string key/value pairs. An empty object is valid.

Keys

  • 1–64 Unicode characters.
  • Compared exactly as supplied.
  • Not trimmed, case-folded, or Unicode-normalized.
  • Any Unicode character except NUL (U+0000).

OpenAPI 3.0 cannot express the key-length limit for arbitrary properties, so Pocket enforces it at runtime. An invalid key returns 400 invalid_request with param=metadata. The key is not echoed.

Values

  • String values only.
  • Empty values are allowed.
  • Maximum 256 Unicode characters.
  • NUL (U+0000) is not allowed.
  • Preserved exactly as supplied, without trimming or Unicode normalization.

An overlong value returns 400 invalid_request. param is metadata.<key> when the key is valid, or metadata when the key is invalid. The value is never echoed.

Privacy

The API shows metadata only in the Customer view where the caller set it. The API does not share metadata with money-movement counterparties. Use it for opaque business identifiers and non-sensitive labels only.

Do not include:

  • Government identity values.
  • Bank-account, routing, or payment-card details.
  • Passwords, secrets, access tokens, or credentials.
  • KYC documents.
  • Biometric or health information.
  • Names, email addresses, or phone numbers when a dedicated field exists.

Pocket may reject metadata that appears sensitive, but the caller remains responsible for the data it supplies.

Responses

Request samples

Content type
application/json
{
  • "label": "Taylor Reed",
  • "customer_id": "cus_01jm0defbbct46vh2ev5dkkhej",
  • "reference": "EMP-1042",
  • "metadata": {
    }
}

Response samples

Content type
application/json
{
  • "id": "rcp_01k2p7m4sd6t8v0x3y5z9a1bcf",
  • "customer_id": "cus_01jm0defbbct46vh2ev5dkkhej",
  • "label": "Taylor Reed",
  • "reference": "EMP-1042",
  • "association_status": "linked",
  • "status": "active",
  • "capabilities": {
    },
  • "metadata": {
    },
  • "created_at": "2026-07-31T09:05:00Z",
  • "updated_at": "2026-07-31T09:05:00Z"
}

List a business's recipients

Required scope: recipients:read.

Customer eligibility: authenticated business Customer only.

List this business's linked and pending Recipient resources. A linked Recipient returns its destination customer_id; a pending Recipient returns customer_id: null. The API returns business-owned relationship fields. It does not expose Customer profile, contact, KYC, onboarding, balance, activity, or payment details.

  • Default lists exclude removed recipients.
  • Use status=removed to list removed recipients.
  • Filter by relationship/association status, current capability, or exact business reference.
  • Filter by an exact linked customer_id.
  • q searches only this business's Recipient ID, label, reference, metadata keys and values, and linked customer_id; it never searches Pocket Customer identity data.
  • Retrieve one relationship from GET /v1/customers/{business_customer_id}/recipients/{recipient_id}.

Results are ordered by creation time and Recipient ID. Use order to return the oldest or newest Recipients first. A cursor is bound to the owning business Customer, order, and normalized status, association_status, can_receive_transfers, customer_id, reference, and q filters. The default is newest first, with limit 25 (allowed range 1–100). Filters apply before pagination; repeat them with a cursor. The limit may change. Empty scalar query values are treated as omitted. Results are current views, not a frozen multi-page snapshot; restart a traversal to observe records whose status or capability changed.

Authorizations:
bearerAuth
path Parameters
business_customer_id
required
string^cus_[0-7][0-9abcdefghjkmnpqrstvwxyz]{25}$
Example: cus_01j8ma3wv3mepwabfxxs0yc395

The authenticated business Customer whose recipient roster the API manages. An individual Customer returns 403. Another Customer returns 404.

query Parameters
status
string (RecipientStatus)
Enum: "active" "inactive" "removed"

Only recipients in this relationship state. Without this filter, the list excludes removed recipients.

association_status
string (RecipientAssociationStatus)
Enum: "linked" "pending" "unavailable"

Only recipients in this association state.

can_receive_transfers
boolean

Only recipients with this current advisory roster/Customer capability. Supply exactly true or false; numeric or uppercase boolean aliases are invalid. This does not check wallet readiness.

customer_id
string^cus_[0-7][0-9abcdefghjkmnpqrstvwxyz]{25}$

Only the linked Recipient for this exact Customer ID. Pending Recipients never match.

reference
string <= 255 characters ^[^\x00]*$

Exact business-owned recipient reference.

q
string [ 1 .. 255 ] characters ^[^\x00]*\S[^\x00]*$

Case-insensitive substring search over this business's label, reference, and metadata keys and values, plus prefix Recipient ID and linked Customer ID matching. SQL pattern characters % and _ act as wildcards. It never searches Customer names, paytags, email, or phone.

order
string
Default: "desc"
Enum: "asc" "desc"

Sort by creation time. desc returns newest first; asc returns oldest first. IDs break ties in the same direction.

cursor
string <= 8192 characters

Opaque cursor (next_cursor from a prior page). Re-supply the same filters. A mismatch or invalid cursor returns 400.

limit
integer [ 1 .. 100 ]
Default: 25

Responses

Response samples

Content type
application/json
{
  • "data": [
    ],
  • "has_more": true,
  • "next_cursor": "string"
}

Retrieve a recipient

Required scope: recipients:read.

Customer eligibility: authenticated business Customer only.

Retrieve this business's linked or pending Recipient by recipient_id. Soft-removed Recipients are included. The response contains a nullable destination Customer ID and business-owned relationship fields, not private Customer data or matching inputs.

Authorizations:
bearerAuth
path Parameters
business_customer_id
required
string^cus_[0-7][0-9abcdefghjkmnpqrstvwxyz]{25}$
Example: cus_01j8ma3wv3mepwabfxxs0yc395

The authenticated business Customer whose recipient roster the API manages. An individual Customer returns 403. Another Customer returns 404.

recipient_id
required
string^rcp_[0-7][0-9abcdefghjkmnpqrstvwxyz]{25}$
Example: rcp_01k2p7m4sd6t8v0x3y5z9a1bcf

The Recipient (rcp_…) owned by this business Customer.

Responses

Response samples

Content type
application/json
{
  • "id": "rcp_01k2p7m4sd6t8v0x3y5z9a1bcf",
  • "customer_id": "cus_01jm0defbbct46vh2ev5dkkhej",
  • "label": "Taylor Reed",
  • "reference": "EMP-1042",
  • "association_status": "linked",
  • "status": "active",
  • "capabilities": {
    },
  • "metadata": {
    },
  • "created_at": "2026-07-31T09:05:00Z",
  • "updated_at": "2026-07-31T09:05:00Z"
}

Update a recipient

Required scope: recipients:write.

Customer eligibility: authenticated business Customer only.

Replace this business's mutable recipient state. Supply all of label, reference, status, and metadata.

  • label is the business's display name for the recipient.
  • reference is required but may be null to clear it.
  • status may be active or inactive.
  • metadata replaces the whole metadata object. Use {} to clear it.
  • Idempotency-Key is an optional HTTP header.

This changes only the Recipient resource. It does not change a linked Pocket Customer. A pending Recipient's lookup identifiers cannot be changed in V1.

active means the business considers the recipient active. It does not guarantee that the customer can receive a transfer.

  • Use a fresh GET's capabilities.can_receive_transfers as an advisory roster/Customer eligibility check. It does not check wallet readiness, source funds, limits or execution-time payment rules.
  • An active update cannot bypass private Customer state.
  • A new PUT does not restore a removed recipient. Re-add it through the recipient collection endpoint.
  • An unavailable state transition returns 409.

Idempotency: Omitting the header performs an unkeyed replacement. A supplied key binds this operation for the default 24-hour window. Keys are scoped to the authenticated Customer and Recipient-update operation, not to the API-key credential or target Recipient. Matching input includes the Customer, target Recipient, and normalized label, reference, status and metadata. Reusing an unexpired idempotency key for another Recipient or a different normalized replacement returns 409.

An unchanged retry returns the first successful 200 OK Recipient response, including its saved status, capabilities and updated_at. Later changes or removal do not change that response. Replay does not reapply the update or restore a removed Recipient. Retrieve the Recipient with GET for its current state. Authentication and authorization apply to every attempt.

Concurrent requests with the same key and normalized input wait for the first attempt to commit successfully, then replay its response. Being in flight alone does not produce an idempotency conflict. Retries do not extend the window; at or after 24-hour expiry, the key can identify a new replacement.

Authorizations:
bearerAuth
path Parameters
business_customer_id
required
string^cus_[0-7][0-9abcdefghjkmnpqrstvwxyz]{25}$
Example: cus_01j8ma3wv3mepwabfxxs0yc395

The authenticated business Customer whose recipient roster the API manages. An individual Customer returns 403. Another Customer returns 404.

recipient_id
required
string^rcp_[0-7][0-9abcdefghjkmnpqrstvwxyz]{25}$
Example: rcp_01k2p7m4sd6t8v0x3y5z9a1bcf

The Recipient (rcp_…) owned by this business Customer.

header Parameters
Idempotency-Key
string [ 1 .. 255 ] characters ^[^\x00]*[^\s\x00][^\x00]*$
Example: update-employee-1042

Optional, case-sensitive request key. Supply exactly one nonblank value containing at most 255 Unicode code points, encoded as valid UTF-8 with no NUL character. Empty, blank, invalid or repeated header values return 400 with details.param set to Idempotency-Key.

Request Body schema: application/json
required
label
required
string [ 1 .. 120 ] characters ^[^\x00]*\S[^\x00]*$

Business-supplied nonblank display label. The length limit is checked before surrounding whitespace is trimmed.

reference
required
string or null <= 255 characters ^[^\x00]*$

Set to null to clear.

status
required
string (RecipientWritableStatus)
Enum: "active" "inactive"

The recipient relationship states a business may request. Setting active changes only the business-owned recipient record and cannot bypass private Customer eligibility.

required
object <= 50 properties

Whole-object replacement; use an empty object to clear.

Responses

Request samples

Content type
application/json
{
  • "label": "Taylor Reed",
  • "reference": "EMP-1042",
  • "status": "inactive",
  • "metadata": {
    }
}

Response samples

Content type
application/json
{
  • "id": "rcp_01k2p7m4sd6t8v0x3y5z9a1bcf",
  • "customer_id": "cus_01jm0defbbct46vh2ev5dkkhej",
  • "label": "Taylor Reed",
  • "reference": "EMP-1042",
  • "association_status": "linked",
  • "status": "active",
  • "capabilities": {
    },
  • "metadata": {
    },
  • "created_at": "2026-07-31T09:05:00Z",
  • "updated_at": "2026-07-31T09:05:00Z"
}

Remove a recipient

Required scope: recipients:write.

Customer eligibility: authenticated business Customer only.

Soft-remove a linked or pending Recipient from this business's roster by recipient_id.

  • The recipient becomes removed and can_receive_transfers becomes false.
  • It remains available through direct retrieval and GET .../recipients?status=removed.
  • Unfiltered recipient lists exclude it.
  • A new POST resolving the same existing Customer restores the Recipient using the same ID and the newly submitted fields. Creating a Recipient requires a match to an existing Customer.

Removing a Recipient cancels future pending resolution and does not affect a linked Pocket Customer or payment history.

Idempotency: Omitting the Idempotency-Key header performs an unkeyed removal. A supplied key binds this operation for the default 24-hour window. Keys are scoped to the authenticated Customer and Recipient-remove operation, not to the API-key credential or target Recipient. Matching input includes the Customer and target Recipient. Reusing an unexpired idempotency key for another Recipient returns 409.

An unchanged retry returns the saved successful 204 No Content outcome, with no response body, without executing the removal again. If the Recipient has since been re-added, that retry leaves the restored relationship intact. Use a new key to request another removal. Authentication and authorization apply to every attempt.

Concurrent requests with the same key and target wait for the first attempt to commit successfully, then replay its success. Being in flight alone does not produce an idempotency conflict. Retries do not extend the window; at or after 24-hour expiry, the key can identify a new removal.

Authorizations:
bearerAuth
path Parameters
business_customer_id
required
string^cus_[0-7][0-9abcdefghjkmnpqrstvwxyz]{25}$
Example: cus_01j8ma3wv3mepwabfxxs0yc395

The authenticated business Customer whose recipient roster the API manages. An individual Customer returns 403. Another Customer returns 404.

recipient_id
required
string^rcp_[0-7][0-9abcdefghjkmnpqrstvwxyz]{25}$
Example: rcp_01k2p7m4sd6t8v0x3y5z9a1bcf

The Recipient (rcp_…) owned by this business Customer.

header Parameters
Idempotency-Key
string [ 1 .. 255 ] characters ^[^\x00]*[^\s\x00][^\x00]*$
Example: remove-employee-1042

Optional, case-sensitive request key. Supply exactly one nonblank value containing at most 255 Unicode code points, encoded as valid UTF-8 with no NUL character. Empty, blank, invalid or repeated header values return 400 with details.param set to Idempotency-Key.

Responses

Response samples

Content type
application/json
Example
{
  • "code": "invalid_argument",
  • "message": "The request is invalid.",
  • "details": {
    }
}

Transactions

Account-relative money movement visible to the authenticated Customer. Both transaction endpoints require transactions:read. Each (id, account_id) pair identifies one view: outbound (kind: transfer) or inbound (kind: deposit). A transfer between two owned Accounts produces two views with the same txn_… ID. A transfer to another Customer exposes only your own Account's view and the counterparty Customer ID, not their Account ID or balance.

GET /v1/transactions supports account_id, kind, repeated method values, status, amount_min, amount_max, exact reference, creation and settlement time ranges, order, limit and cursor. Amount and time bounds are inclusive; settlement-time filters exclude entries without a settlement timestamp. Lists default to 25 items, newest first; the maximum page size is 100. Use order=asc for oldest first. Keep the same filters and order when following next_cursor. An Account filter must refer to an owned Account.

The account_id filter is optional. If supplied, it must identify an existing Account you own; otherwise the API returns 404. A malformed Account ID returns 400. An owned Account with no matching activity returns an empty page.

GET /v1/transactions/{transaction_id} returns current state. If the transaction touches two of your Accounts, supply ?account_id=acc_… to select one view; omitting it returns 400. An inaccessible transaction, or a transaction that does not touch the selected Account, returns 404.

Accepted internal movements appear as pending before settlement. Their source and destination views share the terminal status: settled, failed or voided. Use the corresponding Transfer or Deposit endpoint for its resource view. Card activity is feed-only and has no Transfer or Deposit resource. Verification microdeposits and zero-net Account activity are excluded.

List transactions

Required scope: transactions:read.

List money-movement activity for the authenticated Customer. Recipient Customers' private activity is never included.

Narrow results with account_id, kind, method, status, amount, reference, created date, or settled date.

When supplied, account_id must identify an Account owned by the authenticated Customer.

An Account filter identifying a nonexistent or unowned Account returns the same privacy-preserving 404, with outer code: not_found and details.code: not_found. A malformed Account ID returns 400. A valid owned Account with no matching activity returns an empty page.

Results are ordered by (created_at, id, account_id). Use order=asc for oldest first or order=desc (the default) for newest first. Page size defaults to 25 and may be 1–100. Retain the filters and order when following next_cursor.

Each item is a transaction (txn_…) seen by the authenticated Customer:

  • Each (id, account_id) view appears at most once. An Account transfer has two views.
  • Use kind and id to fetch a transfer or deposit object when one exists.

An accepted internal transfer creates the source and destination Account views at once:

  • The source entry is kind=transfer and starts pending.
  • The destination entry is kind=deposit and starts pending.
  • Both become settled, failed, or voided together. Each Customer can read only its owned Account views; a same-Customer transfer has two such views.

A rejected transfer request creates no activity entry.

The current response exposes common movement fields, not method-specific details, FX pricing, payment purpose codes or bank retrieval references. Read a transaction to retrieve its current status.

If you sent money to a customer, your feed shows your transfer and its status, not the customer's resulting balance or unrelated activity. The feed excludes verification microdeposits, declined card authorizations, and Pocket-internal treasury operations.

Authorizations:
bearerAuth
query Parameters
account_id
string^acc_[0-7][0-9abcdefghjkmnpqrstvwxyz]{25}$
Example: account_id=acc_01j8ma3wv3mepwabfxxs0yc395

Filter an activity list to an Account owned by the authenticated Customer.

  • Malformed or wrong-prefix value: 400. param is account_id.
  • No matching activity for an owned Account returns an empty page.
  • Both the Deposit and Transaction lists return the same privacy-preserving 404 for a missing or unowned Account.
kind
string (TransactionKind)
Enum: "transfer" "deposit"
Example: kind=deposit

Only transfers (outbound) or only deposits (inbound).

method
Array of strings (Method)
Items Enum: "internal" "ach" "wire" "onchain" "ibft" "swift" "faster_payments" "payment_link" "card"
Example: method=ach&method=wire

Filter transactions by method. Repeat the parameter to match more than one (e.g. method=ach&method=wire).

status
string (TransactionStatus)
Enum: "pending" "settled" "failed" "voided"

pending (accepted, not yet settled), settled (posted to the balance), failed, or voided. Pocket assigns voided. It does not mean that the caller canceled the transaction.

amount_min
string (MoneyAmount) <= 19 characters ^(?:0(?:\.[0-9]{1,2})?|[1-9][0-9]{0,15}(?:\.[...
Example: amount_min=100.00

Minimum amount (inclusive), as a decimal string in major units, e.g. 100.00.

amount_max
string (MoneyAmount) <= 19 characters ^(?:0(?:\.[0-9]{1,2})?|[1-9][0-9]{0,15}(?:\.[...
Example: amount_max=100.00

Maximum amount (inclusive), as a decimal string in major units, e.g. 100.00.

reference
string <= 255 characters ^[^\x00]*$

Exact match on your opaque reference for the object. Not unique. May match more than one object, and results are paginated. Values are not trimmed or case-folded. An empty value means no filter. Maximum 255 Unicode characters; NUL is invalid.

created_after
string <date-time>
Example: created_after=2026-07-31T09:00:00Z

Only items created at/after this time (inclusive).

created_before
string <date-time>
Example: created_before=2026-07-31T09:00:00Z

Only items created at/before this time (inclusive).

settled_after
string <date-time>
Example: settled_after=2026-07-31T09:00:00Z

Only items settled at/after this time (inclusive). This filter excludes pending, failed, and voided items because they have no settlement time.

settled_before
string <date-time>
Example: settled_before=2026-07-31T09:00:00Z

Only items settled at/before this time (inclusive). This filter excludes pending, failed, and voided items because they have no settlement time.

order
string
Default: "desc"
Enum: "asc" "desc"

Sort by creation time. desc returns newest first; asc returns oldest first. IDs break ties in the same direction.

cursor
string <= 8192 characters

Opaque cursor (next_cursor from a prior page). Re-supply the same filters. A mismatch or invalid cursor returns 400.

limit
integer [ 1 .. 100 ]
Default: 25

Responses

Response samples

Content type
application/json
{
  • "has_more": true,
  • "next_cursor": "string",
  • "data": [
    ]
}

Retrieve a transaction

Required scope: transactions:read.

Fetch one transaction entry by ID, e.g. /v1/transactions/txn_….

The endpoint returns the authenticated Customer's view. A transaction that is not visible to that Customer returns 404.

A same-Customer Account transfer has two owned views. Supply account_id to select one; omitting it when multiple views exist returns 400 with details.param: account_id. An Account that the transaction does not touch, or that the caller does not own, returns 404, not an empty page.

Authorizations:
bearerAuth
path Parameters
transaction_id
required
string^txn_[0-7][0-9abcdefghjkmnpqrstvwxyz]{25}$
Example: txn_01jjftqbz0n1vzey0pqrq3qmnn
query Parameters
account_id
string^acc_[0-7][0-9abcdefghjkmnpqrstvwxyz]{25}$
Example: account_id=acc_01j8ma3wv3mepwabfxxs0yc395

Select one owned Account's viewpoint of this transaction.

  • If exactly one owned Account view exists, omit this parameter or supply that Account ID.
  • If several owned Account views exist, supply account_id. Without it, the request returns 400 with param=account_id.
  • A malformed or wrong-prefix value returns 400. param is account_id.
  • An unowned Account, or one the transaction does not touch, returns 404.

Responses

Response samples

Content type
application/json
{
  • "id": "txn_01jjftqbz0n1vzey0pqrq3qmnn",
  • "account_id": "acc_01j8ma3wv3mepwabfxxs0yc395",
  • "kind": "deposit",
  • "method": "ach",
  • "source": null,
  • "destination": {
    },
  • "amount": "2500.00",
  • "currency": "USD",
  • "status": "settled",
  • "reference": null,
  • "memo": null,
  • "created_at": "2026-07-30T14:10:00Z",
  • "settled_at": "2026-07-31T09:00:00Z"
}

Transfers

Money leaving one of the authenticated Customer's Accounts. V1 creates internal transfers to another owned Account or another Pocket Customer. Transfer reads also expose supported outbound activity already processed by Pocket; this does not enable ACH, wire, onchain or IBFT creation through Relay. Card spends appear only in the transaction feed.

Reads require transfers:read; internal transfer creation requires transfers:write. Creation is not restricted to business Customers by this operation, but the source credential, Customer and Accounts must be eligible. Customer-to-Customer transfers may have a 30-day sending limit. Transfers between your own Accounts are not subject to this limit.

GET /v1/transfers accepts repeated method values, source_account_id, destination_id, status, amount and timestamp bounds, exact reference, order, limit and cursor. A destination filter can identify an owned Account, a counterparty Customer, or an external instrument visible on a transfer. Unowned Account filters return 404; an unmatched Customer or instrument filter returns an empty page. Historical transfers without an instrument ID cannot match that instrument filter. Page size defaults to 25 (maximum 100), newest first. Retain filters and order when following the returned cursor.

GET /v1/transfers/{transfer_id} returns the current outbound view, including its exact source Account, status and transition timestamps. For an internal movement, the same transaction ID identifies the destination's Deposit view. For an owned-Account transfer, both views are accessible to the same Customer. Otherwise, the destination Customer's private activity remains hidden.

Creation requires Idempotency-Key and returns 201 with a relative Location. For 24 hours, an unchanged retry replays the saved initial response; it does not refresh status or send money again. A changed request using an unexpired idempotency key returns 409. Reads return current state. A created, pending transfer is accepted, not settled. Preserve its ID and poll the read endpoint to reconcile the outcome.

The Transfer response does not expose method-specific details or FX data.

List transfers

Required scope: transfers:read.

Outbound money movements from the authenticated Customer.

  • The list includes every method that produces a transfer object.
  • Card activity is feed-only. See Transactions.

Narrow results with method, source_account_id, destination_id, status, amount, reference, created date, or settled date.

When supplied, source_account_id must identify an owned Account.

destination_id may identify another owned Account, a Pocket Customer or an external instrument visible on a transfer. An Account filter identifying a nonexistent or unowned Account returns 404. An unmatched Customer or instrument filter returns an empty page, without revealing whether the counterparty exists. Historical activity without an instrument ID is not matched by that instrument filter.

Results are ordered by creation time and Transfer ID. Use order to return the oldest or newest Transfers first.

Transfer reads expose supported historical outbound activity; they do not enable ACH, wire, onchain or IBFT creation through Relay. Only internal transfer creation is implemented in this release.

Authorizations:
bearerAuth
query Parameters
method
Array of strings (TransferMethod)
Items Enum: "internal" "ach" "wire" "onchain" "ibft"
Example: method=ach&method=wire

Filter transfers by method. Repeat the parameter to match more than one (e.g. method=ach&method=wire). A method that cannot produce a Transfer returns 400 invalid_request with param=method rather than an empty result.

source_account_id
string^acc_[0-7][0-9abcdefghjkmnpqrstvwxyz]{25}$

Filter by an Account owned by the authenticated Customer. An Account that does not exist or belongs to another Customer returns privacy-preserving 404.

destination_id
string^(?:acc_|cus_|epi_)[0-7][0-9abcdefghjkmnpqrst...

Filter by an owned Account, a Pocket Customer, or an external instrument already visible on a matching transfer.

  • Malformed or wrong-prefix value: 400. param is destination_id.
  • An Account that does not exist or belongs to another Customer returns 404.
  • An owned Account with no matching visible transfers returns an empty page.
  • A Customer or instrument with no matching visible transfers returns an empty page, without disclosing whether that counterparty exists.
status
string (TransferStatus)
Enum: "pending" "settled" "failed" "voided"

pending (accepted, not yet settled), settled (posted to the balance), failed, or voided. Pocket assigns voided to an accepted transfer that will not settle. There is no public cancel or void endpoint.

amount_min
string (MoneyAmount) <= 19 characters ^(?:0(?:\.[0-9]{1,2})?|[1-9][0-9]{0,15}(?:\.[...
Example: amount_min=100.00

Minimum amount (inclusive), as a decimal string in major units, e.g. 100.00.

amount_max
string (MoneyAmount) <= 19 characters ^(?:0(?:\.[0-9]{1,2})?|[1-9][0-9]{0,15}(?:\.[...
Example: amount_max=100.00

Maximum amount (inclusive), as a decimal string in major units, e.g. 100.00.

reference
string <= 255 characters ^[^\x00]*$

Exact match on your opaque reference for the object. Not unique. May match more than one object, and results are paginated. Values are not trimmed or case-folded. An empty value means no filter. Maximum 255 Unicode characters; NUL is invalid.

created_after
string <date-time>
Example: created_after=2026-07-31T09:00:00Z

Only items created at/after this time (inclusive).

created_before
string <date-time>
Example: created_before=2026-07-31T09:00:00Z

Only items created at/before this time (inclusive).

settled_after
string <date-time>
Example: settled_after=2026-07-31T09:00:00Z

Only items settled at/after this time (inclusive). This filter excludes pending, failed, and voided items because they have no settlement time.

settled_before
string <date-time>
Example: settled_before=2026-07-31T09:00:00Z

Only items settled at/before this time (inclusive). This filter excludes pending, failed, and voided items because they have no settlement time.

order
string
Default: "desc"
Enum: "asc" "desc"

Sort by creation time. desc returns newest first; asc returns oldest first. IDs break ties in the same direction.

cursor
string <= 8192 characters

Opaque cursor (next_cursor from a prior page). Re-supply the same filters. A mismatch or invalid cursor returns 400.

limit
integer [ 1 .. 100 ]
Default: 25

Responses

Response samples

Content type
application/json
{
  • "has_more": true,
  • "next_cursor": "string",
  • "data": [
    ]
}

Create an internal transfer

Required scope: transfers:write.

Move funds from the authenticated Customer's default Account (source.customer_id) or an exact owned Account (source.account_id) either to another Customer (destination.customer_id) or another Account owned by the same Customer (destination.account_id). A Customer destination receives into its immutable default Account. The counterparty Customer's private balance and activity remain hidden.

Supply exactly one source and one destination selector. A Customer source must identify the authenticated Customer. A Customer destination must be another Customer, not a Recipient relationship ID. An Account destination must be owned by the authenticated Customer and differ from the resolved source Account. Selecting another Customer's Account directly is not supported.

Individuals and businesses can use this operation. Customer-to-Customer transfers may have 30-day sending limits. Transfers between your own Accounts are not subject to these limits.

Idempotency: The Idempotency-Key HTTP header is required for both Account-to-Account and Customer-to-Customer P2P transfers. Keys are scoped to the authenticated Customer and executing transfer operation, not to the API-key credential. Account transfers and P2P share one operation scope. Matching input includes the requested source and destination, normalized amount, currency, reference and memo. Decimal-equivalent amounts such as "1", "1.0" and "1.00" match. Reusing an unexpired idempotency key for different input within that operation returns 409. A default-Customer source selector and an explicit Account selector remain different input, even if they currently resolve to the same Account.

For the default 24-hour window, an unchanged retry returns the saved initial 201 Created Transfer response and original Location. Replay preserves the saved Account routing, status, reference, memo and timestamps even if the Transfer later settles, fails or is voided. It does not move money again or reload the Transfer's current state. Use GET /v1/transfers/{transfer_id} for current state. Authentication and authorization apply to every attempt.

Concurrent requests with the same key and input wait for the first attempt to commit successfully, then replay its response. Being in flight alone does not produce an idempotency conflict. Retries do not extend the window; at or after 24-hour expiry, reusing the key can create another transfer. Keep one key and unchanged body for one payment intent. Use a new key for a different intent, including changing between Account-transfer and P2P destinations.

At acceptance

201 Created confirms acceptance, not settlement.

  • Source: kind is transfer, method is internal, and status is pending.
  • Destination: kind is deposit, method is internal, and status is pending.
  • Each Customer can immediately read its own Account views; an owned-Account transfer gives the same Customer both views.
  • Source available decreases and reserved increases.

At settlement

Both views become settled. The source reservation posts out. Pocket credits the destination settled and available amounts.

On failure or voiding

Both views remain visible with the terminal status. Pocket releases the source reservation and never credits the destination.

If the API rejects a request before it creates a txn_…, it produces no transfer, deposit, or transaction resource.

A credential without permission to send returns 403 forbidden. A destination Customer without a payable default Account returns 403 recipient_not_payable, without revealing private setup details. A missing, unowned or inactive P2P source Account returns 404. For an owned-Account transfer, an explicitly selected inactive source or destination Account returns 409 conflict; missing or unowned Accounts return 404.

A P2P request that exceeds the source Customer's 30-day sending limit returns 429, with outer code: resource_exhausted and details.code: rate_limited. It creates no transfer, deposit or transaction. This is a rejected request, not the limit_exceeded failure code on a Batch item.

If a response is lost, retry the original key and request while the key is live. Once expired, reconcile known transaction IDs and references before deciding whether to send again. A reference is not unique and does not prevent another transfer.

Authorizations:
bearerAuth
header Parameters
Idempotency-Key
required
string [ 1 .. 255 ] characters ^[^\x00]*[^\s\x00][^\x00]*$
Example: d9f80740-7f8e-4f16-b5cc-4fd5bf7d3b21

Required, case-sensitive request key. Supply exactly one nonblank value containing at most 255 Unicode code points, encoded as valid UTF-8 with no NUL character. Missing, empty, blank, invalid or repeated header values return 400 with details.param set to Idempotency-Key.

Request Body schema: application/json
required
required
CustomerReference (object) or AccountReference (object) (AccountSource)

The Account that funds an internal Transfer or Batch. Supply exactly one:

  • customer_id must identify the authenticated Customer and selects its default Account.
  • account_id selects that exact Account and must be owned by the authenticated Customer.
required
object (InternalTransferDestination) = 1 properties

Exactly one destination: another Customer's immutable default Account, or another Account owned by the authenticated Customer. A Customer destination must differ from the authenticated Customer. An Account destination must differ from the resolved source Account. Recipient relationship IDs and another Customer's explicit Account IDs are not supported.

amount
required
string (WriteAmount) <= 19 characters ^(?:0\.(?:0[1-9]|[1-9][0-9]?)|[1-9][0-9]{0,15...

A positive amount to move, expressed as a JSON decimal string in major units. For example, "100.00" means 100 USD.

  • The value must be greater than zero.
  • USD supports 1–16 integer digits and at most two fractional digits.
  • The maximum value is "9999999999999999.99".
  • Signs, commas, whitespace, scientific notation, leading-dot forms, trailing-dot forms, and unnecessary leading zeros are invalid.
  • The complete value may contain at most 19 characters.
  • For Batch idempotency, decimal-equivalent forms such as "1", "1.0", and "1.00" are treated as the same amount.

An invalid, non-positive or overflowing amount returns 400 invalid_request. param names the request field, such as amount, source_amount or items[n].amount. The request creates or changes nothing. Other Customer and rail limits may impose a lower maximum.

currency
required
string (Currency)
Value: "USD"

Public settlement currency debited from or credited to a Customer balance.

  • USD is the only value emitted in V1.
  • Clients must preserve and tolerate future values.
  • Cross-currency payouts may define separate destination-currency and foreign exchange (FX) fields.
reference
string <= 255 characters ^[^\x00]*$

Your private external reference for this transfer (e.g. a pay-run or employee ID). Optional, not unique. Filter by it. Counterparties do not see it.

memo
string <= 100 characters ^[^\x00]*$

Payment note carried on the movement and visible to the destination customer when the method supports a note. Internal transfer memos can be up to 100 characters. The API rejects longer values with 400 (param is memo) and never truncates them.

Responses

Request samples

Content type
application/json
Example
{
  • "source": {
    },
  • "destination": {
    },
  • "amount": "100.00",
  • "currency": "USD",
  • "reference": "payrun-2026-07",
  • "memo": "July salary"
}

Response samples

Content type
application/json
{
  • "id": "txn_01k0d2f6h8j4m7n9p3q5r1s0vw",
  • "method": "internal",
  • "reference": "payrun-2026-07",
  • "memo": "July salary",
  • "source": {
    },
  • "destination": {
    },
  • "batch_id": null,
  • "amount": "100.00",
  • "currency": "USD",
  • "status": "pending",
  • "transitioned_at": {
    },
  • "created_at": "2026-07-31T11:00:00Z",
  • "updated_at": "2026-07-31T11:00:00Z"
}

Retrieve a transfer

Required scope: transfers:read.

Fetch one transfer by ID, e.g. /v1/transfers/txn_….

This endpoint returns the outbound side when its source Customer is the authenticated Customer. Otherwise it returns 404.

For an internal movement, the same txn_… may also be retrievable from GET /v1/deposits/{deposit_id} by the destination Customer. The transfer is retrievable as soon as its txn_… exists and remains retrievable through its terminal status.

When both Accounts belong to this Customer, the Transfer and Deposit endpoints select the outbound and inbound views respectively without an additional Account query parameter. Card activity has no Transfer resource.

Authorizations:
bearerAuth
path Parameters
transfer_id
required
string^txn_[0-7][0-9abcdefghjkmnpqrstvwxyz]{25}$
Example: txn_01k0d2f6h8j4m7n9p3q5r1s0vw

Responses

Response samples

Content type
application/json
{
  • "id": "txn_01k0d2f6h8j4m7n9p3q5r1s0vw",
  • "method": "internal",
  • "reference": "payrun-2026-07",
  • "memo": "July salary",
  • "source": {
    },
  • "destination": {
    },
  • "batch_id": null,
  • "amount": "100.00",
  • "currency": "USD",
  • "status": "pending",
  • "transitioned_at": {
    },
  • "created_at": "2026-07-31T11:00:00Z",
  • "updated_at": "2026-07-31T11:00:00Z"
}

Deposits

Money arriving in an Account owned by the authenticated Customer. Both Deposit endpoints require deposits:read. Deposits are read-only: Pocket creates them from incoming payments and the destination side of an internal transfer. Card refunds are transaction-feed entries, not Deposit resources.

GET /v1/deposits accepts repeated method values, account_id, status, amount and timestamp bounds, exact reference, order, limit and cursor. Page size defaults to 25 (maximum 100), newest first. Amount and time bounds are inclusive; settlement-time filters exclude entries without a settlement time. An Account filter must identify an owned Account. Keep the same filters and order when following next_cursor.

GET /v1/deposits/{deposit_id} returns the current inbound view. The top-level account_id and destination.account_id identify the same Account. source can be null when the external sender has no visible Pocket resource. For an internal payment from another Customer, their Customer ID and memo can be visible, but their private reference is not. Moving money between your own Accounts preserves your reference in both views.

An accepted internal transfer creates a pending Deposit with the same txn_… ID as the source Transfer. It does not increase destination funds before settlement. Failure or voiding leaves the Deposit visible with the terminal status and no destination credit. Fetching a Deposit outside your Customer's visible inbound activity returns 404.

The response reports the Account's USD movement amount and common movement fields. It does not expose method-specific details, original foreign amounts or fee breakdowns.

List deposits

Required scope: deposits:read.

Inbound money movements into Accounts owned by the authenticated Customer.

  • The list includes every method that produces a deposit object.
  • Card refunds are feed-only. See Transactions.
  • Deposits are read-only: you never create one.

Narrow results with method, account_id, status, amount, reference, created date, or settled date.

An internal transfer appears in the destination Customer's results as soon as its txn_… identifier exists.

For an internal movement:

  • The deposit appears immediately as pending.
  • It does not affect destination balances until settlement.
  • A later failure or void leaves the deposit visible with that terminal status.

When supplied, account_id must identify an Account owned by the authenticated Customer. An Account filter identifying a nonexistent or unowned Account returns 404.

Results are ordered by creation time and Deposit ID. Use order to return the oldest or newest Deposits first.

Authorizations:
bearerAuth
query Parameters
method
Array of strings (DepositMethod)
Items Enum: "internal" "ach" "wire" "onchain" "swift" "faster_payments" "payment_link"
Example: method=ach&method=wire

Filter deposits by method. Repeat the parameter to match more than one (e.g. method=ach&method=wire). A method that cannot produce a Deposit returns 400 invalid_request with param=method rather than an empty result.

account_id
string^acc_[0-7][0-9abcdefghjkmnpqrstvwxyz]{25}$
Example: account_id=acc_01j8ma3wv3mepwabfxxs0yc395

Filter an activity list to an Account owned by the authenticated Customer.

  • Malformed or wrong-prefix value: 400. param is account_id.
  • No matching activity for an owned Account returns an empty page.
  • Both the Deposit and Transaction lists return the same privacy-preserving 404 for a missing or unowned Account.
status
string (TransactionStatus)
Enum: "pending" "settled" "failed" "voided"

pending (accepted, not yet settled), settled (posted to the balance), failed, or voided. Pocket assigns voided. It does not mean that the caller canceled the transaction.

amount_min
string (MoneyAmount) <= 19 characters ^(?:0(?:\.[0-9]{1,2})?|[1-9][0-9]{0,15}(?:\.[...
Example: amount_min=100.00

Minimum amount (inclusive), as a decimal string in major units, e.g. 100.00.

amount_max
string (MoneyAmount) <= 19 characters ^(?:0(?:\.[0-9]{1,2})?|[1-9][0-9]{0,15}(?:\.[...
Example: amount_max=100.00

Maximum amount (inclusive), as a decimal string in major units, e.g. 100.00.

reference
string <= 255 characters ^[^\x00]*$

Exact match on your opaque reference for the object. Not unique. May match more than one object, and results are paginated. Values are not trimmed or case-folded. An empty value means no filter. Maximum 255 Unicode characters; NUL is invalid.

created_after
string <date-time>
Example: created_after=2026-07-31T09:00:00Z

Only items created at/after this time (inclusive).

created_before
string <date-time>
Example: created_before=2026-07-31T09:00:00Z

Only items created at/before this time (inclusive).

settled_after
string <date-time>
Example: settled_after=2026-07-31T09:00:00Z

Only items settled at/after this time (inclusive). This filter excludes pending, failed, and voided items because they have no settlement time.

settled_before
string <date-time>
Example: settled_before=2026-07-31T09:00:00Z

Only items settled at/before this time (inclusive). This filter excludes pending, failed, and voided items because they have no settlement time.

order
string
Default: "desc"
Enum: "asc" "desc"

Sort by creation time. desc returns newest first; asc returns oldest first. IDs break ties in the same direction.

cursor
string <= 8192 characters

Opaque cursor (next_cursor from a prior page). Re-supply the same filters. A mismatch or invalid cursor returns 400.

limit
integer [ 1 .. 100 ]
Default: 25

Responses

Response samples

Content type
application/json
{
  • "has_more": true,
  • "next_cursor": "string",
  • "data": [
    ]
}

Retrieve a deposit

Required scope: deposits:read.

Fetch one deposit by ID, e.g. /v1/deposits/txn_….

This endpoint returns the inbound side when its destination Customer is the authenticated Customer. Otherwise it returns 404.

For an internal movement:

  • The same txn_… may resolve at GET /v1/transfers/{transfer_id} for the source Customer.
  • The deposit is retrievable immediately after transaction creation, including while pending.
  • It remains retrievable after becoming failed or voided.
  • For a transfer between two of this Customer's Accounts, this endpoint selects the inbound view and the Transfer endpoint selects the outbound view.
Authorizations:
bearerAuth
path Parameters
deposit_id
required
string^txn_[0-7][0-9abcdefghjkmnpqrstvwxyz]{25}$
Example: txn_01jjftqbz0n1vzey0pqrq3qmnn

Responses

Response samples

Content type
application/json
{
  • "id": "txn_01jjftqbz0n1vzey0pqrq3qmnn",
  • "method": "ach",
  • "account_id": "acc_01j8ma3wv3mepwabfxxs0yc395",
  • "source": null,
  • "destination": {
    },
  • "reference": null,
  • "memo": null,
  • "amount": "2500.00",
  • "currency": "USD",
  • "status": "settled",
  • "created_at": "2026-07-30T14:10:00Z",
  • "settled_at": "2026-07-31T09:00:00Z",
  • "updated_at": "2026-07-31T09:00:00Z"
}

Batch Transfers

An asynchronous group of internal transfers from one authenticated business Customer to destination Customers. Every item has its own status and may fail without changing the result of another item.

The public API supports USD Batch submission, retrieval, Batch listing, item listing and listing the Transfers created by a Batch. POST accepts and queues the work in one operation; it is not the Relay console's CSV preview/execute workflow. It does not reserve funds, guarantee settlement, guarantee execution order, or offer cancellation.

Use Batch counts and item states to track progress. pending means every item is still pending, not necessarily that processing has not started. processing means some items have finished and others remain pending. Terminal states are completed (all settled), partially_completed (some settled), or failed (none settled). An individual item is pending, settled, failed or voided.

Lists return data, has_more and next_cursor; the final cursor is null. The default limit is 25 and the maximum is 100. Batch lists default to newest first and accept order=asc; item lists always use (created_at ASC, item_id ASC), and Transfer lists use (created_at ASC, id ASC). Repeat the same filters when using a cursor; the page size may change. Results are current views, not a frozen multi-page snapshot. When reconciling items whose status may have changed, start again from the first page using the same filters.

Send an Idempotency-Key header. Matching retries within 24 hours return the original response. Reads return current state.

Pocket retries each batch payment using the same idempotency key. If its outcome remains unknown after 24 hours, Pocket stops automatic retries to avoid duplicate payments.

Keep the Batch ID and your submitted item IDs. An item_id only identifies a payment within its Batch. Check uncertain payment outcomes before submitting a replacement Batch; reusing an item_id in a new Batch can pay the recipient again.

Create a batch of transfers

Required scopes: batches:write and transfers:write.

Customer eligibility: authenticated business Customer only.

Submit internal transfers from the authenticated business Customer's default Account (source.customer_id) or an exact owned Account (source.account_id) asynchronously. Acceptance does not move or reserve money. An ineligible source returns 403, and the API creates no batch.

Batch items currently support only USD, funded from the selected USD Account. Another currency returns 400 invalid_request with param=items[n].currency, where n is the zero-based item index. Items accept destination Customer IDs, not Account or Recipient IDs.

  • Each item checks available balance only when it runs.
  • Item processing order is not guaranteed.

Recipient policy pre-check

recipient_policy defaults to any_customer. Set it to recipients_only to check the complete batch against the authenticated Customer's recipient list before acceptance:

  • The authenticated Customer must have a linked Recipient whose customer_id equals every destination.customer_id.
  • Every matched recipient must currently have capabilities.can_receive_transfers set to true.
  • Pocket must also confirm that each destination can receive the payment. can_receive_transfers: true does not guarantee that the recipient can receive this payment.

This check runs after ordinary validation, including duplicate item_id validation. If any item fails, one 400 recipient_policy_failed response lists all failures in request order. A rejected first attempt creates no Batch and saves no key binding. Resolve the reported policy failures before resubmitting; a new key is not required.

The roster policy is not repeated during item processing. Ordinary transfer rules, including available funds, send limits and payability, still apply. With any_customer, destination eligibility and payability are checked when each item first executes rather than by this acceptance pre-check.

Track every submitted line with GET /v1/batches/{batch_id}/items.

  • A failure before transaction creation has transfer_id: null.
  • Once the item is linked to a txn_…, transfer_id identifies it through settlement, failure or voiding. An item awaiting a confirmed payment result can also have transfer_id: null.
  • Insufficient funds at execution produces failure_code: insufficient_funds.
  • Check failed items before submitting a replacement Batch. A missing Transfer ID does not confirm that no payment started. Reusing an item_id in another Batch can pay the recipient again.

Duplicate item ids

Every item_id must be unique within items.

  • Comparison is exact and case-sensitive.
  • Pocket does not trim, case-fold, or normalize the value.
  • Other line fields do not affect duplicate detection.
  • reference and the Idempotency-Key header do not identify batch lines.

Pocket reports the first repeated value. The request returns 400 invalid_request, with param set to the second occurrence (items[n].item_id). The index is zero-based.

A rejected first attempt creates no Batch and saves no key binding. Correct the duplicate item ID and resubmit; this validation error does not require a new key.

You cannot cancel a batch after acceptance.

Idempotency: The Idempotency-Key header is required. Keys are scoped to the authenticated Customer and Batch-create operation. Matching input includes the requested source, recipient policy, ordered items, reference and metadata. The first successful response is saved atomically with the Batch, items and execution messages. An unchanged retry within 24 hours returns that original 202 Accepted body and Location, not current counts or payment state. It does not enqueue the items again or recheck Recipient policy. Caller authorization still applies on replay.

Concurrent matching retries wait for the first commit. Changed input returns 409 with outer aborted, details.code: idempotency_conflict and details.param: Idempotency-Key, without existing_id. Expired keys can create another Batch. Use the saved Batch ID to retrieve an ambiguous older request, or list by reference and creation time, then reconcile its items and source activity. References are not unique. Do not submit a replacement merely because the acceptance response or Transfer link is missing.

Pocket retries each batch payment using the same idempotency key. If its outcome remains unknown after 24 hours, Pocket stops automatic retries to avoid duplicate payments. Check uncertain payment outcomes before submitting a replacement Batch. An item_id identifies a payment within one Batch; reusing it in a new Batch can pay the recipient again.

Authorizations:
bearerAuth
header Parameters
Idempotency-Key
required
string [ 1 .. 255 ] characters ^[^\x00]*[^\s\x00][^\x00]*$

Required, case-sensitive request key. Supply exactly one nonblank value containing at most 255 Unicode code points, encoded as valid UTF-8 with no NUL character. Missing, blank, repeated or invalid keys return 400 with details.param set to Idempotency-Key.

Request Body schema: application/json
required
required
CustomerReference (object) or AccountReference (object) (AccountSource)

The Account that funds an internal Transfer or Batch. Supply exactly one:

  • customer_id must identify the authenticated Customer and selects its default Account.
  • account_id selects that exact Account and must be owned by the authenticated Customer.
recipient_policy
string (BatchRecipientPolicy)
Default: "any_customer"
Enum: "any_customer" "recipients_only"

One-time destination pre-check applied before Pocket accepts a batch.

  • any_customer: skip the acceptance-time recipient pre-check. Each destination is another Pocket Customer whose eligibility and Money payability are checked when its item first executes.
  • recipients_only: every destination must meet all these conditions:
    • The authenticated Customer has a linked Recipient whose customer_id equals this destination.
    • Its current capabilities.can_receive_transfers value is true.
    • Pocket also confirms that the destination can receive the payment. can_receive_transfers: true does not guarantee that the recipient can receive this payment.

The roster policy is not evaluated again after acceptance or as individual Transfers execute. Ordinary funds, limit and payability rules still apply. Omitting the field and sending any_customer are equivalent for Batch idempotency. Explicit null is invalid.

required
Array of objects (BatchTransferItem) [ 1 .. 4096 ] items

Each item_id must be unique in the array. Pocket enforces this at runtime because OpenAPI 3.0 uniqueItems compares whole objects rather than one field.

reference
string <= 255 characters ^[^\x00]*$

Your external reference for this Batch (e.g. a pay-run ID). Optional, non-null, filterable and not unique. An empty string is normalized to absent and returned as null.

object (BatchMetadata) <= 50 properties

Batch and Batch-item metadata: up to 50 string key/value pairs. Keys contain 1–64 Unicode code points and values 0–255. Both must be valid UTF-8 without NUL and are preserved exactly without trimming or case normalization. Key-length limits are enforced at runtime.

Omit request metadata to use {}; explicit null is invalid. Responses always include an object. Invalid keys identify metadata and invalid values identify metadata.<key>; item fields use the items[n]. prefix. Use opaque business identifiers, not personal data, payment details or secrets.

Responses

Request samples

Content type
application/json
{
  • "source": {
    },
  • "recipient_policy": "recipients_only",
  • "items": [
    ],
  • "reference": "northwind-payroll-2026-07",
  • "metadata": {
    }
}

Response samples

Content type
application/json
{
  • "id": "batch_01j5gr81vpavng9emxaepf2be3",
  • "reference": "northwind-payroll-2026-07",
  • "source": {
    },
  • "recipient_policy": "recipients_only",
  • "status": "pending",
  • "item_counts": {
    },
  • "amount_totals": [
    ],
  • "metadata": {
    },
  • "created_at": "2026-07-31T10:00:00Z",
  • "finalized_at": null
}

List batches

Required scope: batches:read.

Customer eligibility: authenticated business Customer only.

List batches submitted by the authenticated Customer.

source_account_id filters only this Customer's Batches. A syntactically valid Account ID with no matching visible Batches returns an empty page, without disclosing whether that Account exists or who owns it.

Results use (created_at, id), newest first by default; order=asc selects oldest first. Filters apply before pagination. A cursor binds Customer, Account/status/reference/time filters and order, not limit. Repeat the same filters on continuation; limit may change (default 25, range 1–100). Empty scalar queries are treated as omitted. Date bounds are inclusive and created_after must not be later than created_before. Results are current views, not a frozen multi-page snapshot.

Authorizations:
bearerAuth
query Parameters
source_account_id
string^acc_[0-7][0-9abcdefghjkmnpqrstvwxyz]{25}$

Filter this Customer's Batches by exact source Account ID. A malformed or zero ID returns 400 with param=source_account_id. A valid ID with no matching visible Batches returns an empty page, without disclosing Account existence or ownership.

status
string (BatchStatus)
Enum: "pending" "processing" "completed" "partially_completed" "failed"

Summary status derived from the batch item counts:

  • pending: item_counts.pending == item_counts.total. Items may already have started executing; this does not mean no payment has begun.
  • processing: 0 < item_counts.pending < item_counts.total.
  • completed: item_counts.settled == item_counts.total; no failed or voided items.
  • partially_completed: item_counts.pending == 0 and some, but not all, items settled.
  • failed: terminal with zero settled items. Items may be failed or voided.

Counts describe recorded item states, not independent proof of payment outcomes. Reconcile an uncertain internal_error against linked Transfers and source activity before sending a replacement payment. Counts satisfy item_counts.pending + item_counts.settled + item_counts.failed + item_counts.voided == item_counts.total.

created_after
string <date-time>
Example: created_after=2026-07-31T09:00:00Z

Only items created at/after this time (inclusive).

created_before
string <date-time>
Example: created_before=2026-07-31T09:00:00Z

Only items created at/before this time (inclusive).

reference
string <= 255 characters ^[^\x00]*$

Exact, case-sensitive reference filter. References are not unique; matches are paginated. Whitespace is retained; an empty query value is treated as omitted.

order
string
Default: "desc"
Enum: "asc" "desc"

Sort by creation time. desc returns newest first; asc returns oldest first. IDs break ties in the same direction.

cursor
string <= 8192 characters

Opaque cursor (next_cursor from a prior page). Re-supply the same filters. A mismatch or invalid cursor returns 400.

limit
integer [ 1 .. 100 ]
Default: 25

Responses

Response samples

Content type
application/json
{
  • "has_more": true,
  • "next_cursor": "string",
  • "data": [
    ]
}

Retrieve a batch

Required scope: batches:read.

Customer eligibility: authenticated business Customer only.

Retrieve a Batch by ID. Use its current item counts and amount totals to track progress; this is not the saved initial POST response. A missing or unowned Batch returns 404; a malformed Batch ID returns 400 with details.param: batch_id.

Authorizations:
bearerAuth
path Parameters
batch_id
required
string^batch_[0-7][0-9abcdefghjkmnpqrstvwxyz]{25}$
Example: batch_01j5gr81vpavng9emxaepf2be3

Responses

Response samples

Content type
application/json
{
  • "id": "batch_01j5gr81vpavng9emxaepf2be3",
  • "reference": "northwind-payroll-2026-07",
  • "source": {
    },
  • "recipient_policy": "recipients_only",
  • "status": "pending",
  • "item_counts": {
    },
  • "amount_totals": [
    ],
  • "metadata": {
    },
  • "created_at": "2026-07-31T10:00:00Z",
  • "finalized_at": null
}

List a batch's items

Required scope: batches:read.

Customer eligibility: authenticated business Customer only.

List every submitted item in the batch, including items that failed before creating a transfer.

Use item_id to reconcile the response with the line you submitted. When an item creates a linked Transfer, transfer_id identifies the resulting txn_…. Items awaiting a confirmed payment result also have transfer_id: null; that value alone is not proof of a known failure or that no payment started. Available failure details are in failure_code and failure_message; these fields are present as null for healthy and voided items.

For retries and payments with an unknown outcome, see Batch Transfers.

Narrow with item_id, status, or reference.

Reconcile uncertain or internal_error outcomes against source activity before submitting a replacement. A new Batch can create new Transfers. item_id does not deduplicate across Batches, so resubmitting a settled or ambiguous line can pay the same Customer twice.

The API orders results by (created_at ASC, item_id ASC). Filters apply before pagination. A cursor binds Customer, Batch and item/status/reference filters, not limit (default 25, range 1–100). Repeat the filters with the cursor. A traversal is not a frozen snapshot; restart it to observe items that changed status. Empty scalar query values are treated as omitted.

Authorizations:
bearerAuth
path Parameters
batch_id
required
string^batch_[0-7][0-9abcdefghjkmnpqrstvwxyz]{25}$
query Parameters
item_id
string [ 1 .. 255 ] characters ^[^\x00]*[^\s\x00][^\x00]*$

Only the batch item with this caller-supplied ID. Unique within the batch.

status
string (BatchItemStatus)
Enum: "pending" "settled" "failed" "voided"

Lifecycle of one submitted Batch item. A linked Transfer's state is projected asynchronously onto its item. Pocket assigns voided to an item that will not settle and is not a failure. Healthy and voided items emit failure_code and failure_message as JSON null, not omitted fields.

reference
string <= 255 characters ^[^\x00]*$

Exact, case-sensitive reference filter. References are not unique; matches are paginated. Whitespace is retained; an empty query value is treated as omitted.

cursor
string <= 8192 characters

Opaque cursor (next_cursor from a prior page). Re-supply the same filters. A mismatch or invalid cursor returns 400.

limit
integer [ 1 .. 100 ]
Default: 25

Responses

Response samples

Content type
application/json
{
  • "has_more": true,
  • "next_cursor": "string",
  • "data": [
    ]
}

List a batch's transfers

Required scopes: batches:read and transfers:read.

Customer eligibility: authenticated business Customer only.

Only items that produced a transfer appear here. Every submitted item appears in GET /v1/batches/{batch_id}/items. An item that fails before transfer creation, such as for insufficient funds when it runs, has transfer_id: null. The API orders results by (created_at ASC, id ASC). A cursor binds Customer, Batch and status, not limit (default 25, range 1–100). Repeat the status filter with the cursor; filtering precedes pagination. Empty scalar query values are treated as omitted.

This list contains only linked, visible Transfer views from the pinned source Account. If a payment's outcome is unknown, check the Batch items and source Account activity before submitting it again.

Authorizations:
bearerAuth
path Parameters
batch_id
required
string^batch_[0-7][0-9abcdefghjkmnpqrstvwxyz]{25}$
query Parameters
status
string (TransferStatus)
Enum: "pending" "settled" "failed" "voided"

pending (accepted, not yet settled), settled (posted to the balance), failed, or voided. Pocket assigns voided to an accepted transfer that will not settle. There is no public cancel or void endpoint.

cursor
string <= 8192 characters

Opaque cursor (next_cursor from a prior page). Re-supply the same filters. A mismatch or invalid cursor returns 400.

limit
integer [ 1 .. 100 ]
Default: 25

Responses

Response samples

Content type
application/json
{
  • "has_more": true,
  • "next_cursor": "string",
  • "data": [
    ]
}

Changelog

0.6.0 — Sandbox release candidate

Same /v1 paths and 23 public operations. No new resource families.

Idempotency

Request keys move from the body to the Idempotency-Key header. Recipient removal gains optional saved-response protection.

Operation Header Matching retry within 24 hours replays
Create internal Transfer Required Saved 201 body and Location.
Create Batch Required Saved 202 body and Location, not execution progress.
Create Recipient Required Saved original response: 201 for new, 200 for existing or restored.
Create Account Required Saved 201 body and Location.
Update Account (PATCH) Optional Saved 200 body.
Replace Recipient (PUT) Optional Saved 200 body.
Remove Recipient (DELETE) Optional Saved 204, without removing a re-added Recipient.

Keys are scoped to the Customer and operation. Replays return the original response, not current state; use GET for that. Changed input under an unexpired idempotency key returns 409 with outer aborted, details.code: idempotency_conflict and details.param: Idempotency-Key, without existing_id. Replay does not extend the 24-hour window. After expiry, reusing the key can create another resource or payment.

Changing between destination.customer_id and destination.account_id while reusing the same idempotency key within 24 hours returns 409 Conflict. Authentication, authorization and request validation still apply to retries.

Behavior changes

  • Payments that exceed the Customer's 30-day sending limit are rejected. Direct transfer requests return 429 rate_limited without creating a Transfer. Batch items report limit_exceeded.
  • Filtering transactions by an Account that does not exist or belongs to another Customer returns 404 instead of 500. The account_id filter is optional.
  • Pocket retries each batch payment using the same idempotency key. If its outcome remains unknown after 24 hours, Pocket stops automatic retries to avoid duplicate payments.

Documentation

  • Added Sandbox URLs and release-candidate labelling.
  • Error schemas now include maintenance 503 responses, which use details.maintenance and details.retry_after_seconds instead of a Pocket reason and request ID.

Adopting 0.6.0

Regenerate clients from this contract. Send request keys in the Idempotency-Key header, remove the body attribute, and keep the same key for retries of one operation. Retries return the original response. Use GET for current state and check the payment outcome before resubmitting after 24 hours.

0.5.0

  • Restored Accounts as the stable funds identity while retaining the focused Recipient, Transfer, Deposit and Batch surface introduced in 0.4.0.
  • Added Account create, list, retrieve, balance and presentation-update operations, plus accounts:read and accounts:write scopes.
  • Changed balances and activity to return Account-relative views. A same-Customer Account transfer appears once for its source Account and once for its destination Account, sharing one Transaction ID.
  • Transfer and Batch sources accept either the authenticated Customer in source.customer_id, which selects its default Account, or an owned Account in source.account_id. A Customer destination still uses destination.customer_id and receives into its immutable default Account.
  • Added same-Customer Account-to-Account transfers through the existing internal Transfer operation. Existing Customer-to-Customer money movement remains P2P.

0.4.0

Customer-scoped V1

  • Replaced the Account-scoped API with a smaller Customer-scoped API for balances, Recipients, Transactions, Transfers, Deposits and Batches.
  • Scoped balances and activity to the Customer represented by the API key. 0.4.0 returns that Customer's single USD balance.
  • Changed Transfer and Batch sources to use the authenticated Customer's ID and kept destination Customer IDs separate.
  • Limited Transfer creation to internal Customer-to-Customer transfers.
  • Added ascending and descending creation-time ordering to Recipient, Transaction, Transfer, Deposit and Batch lists.

Recipients

  • Added stable Recipient IDs (rcp_…). Retrieve, replace and remove operations now address the Recipient rather than its linked Customer.
  • Changed Recipient replacement from PATCH to PUT. The request supplies the complete mutable state: label, reference, status and metadata.
  • Added exact paytag matching during Recipient creation, alongside exact Customer ID, email and phone matching. Removed government-ID lookup and the separate Recipient search operation.
  • Removed creation of unmatched Customers and pending Recipients. An incomplete or conflicting match returns 404 recipient_not_found and creates nothing.
  • Recipient responses contain the stable Recipient ID, linked Customer ID when available, business-owned label, reference and metadata, association and relationship status, transfer capability, and timestamps. Contact details and Customer onboarding state are not returned.

Requests and retries

  • Recipient creation and Batch creation are idempotent for 24 hours. Recipient replacement has the same guarantee when an idempotency_key is supplied. Other operations may accept an idempotency key, but V1 does not guarantee replay or duplicate suppression for them.
  • Bound Recipient list cursors to the owning Customer and normalized filters. 0.4.0 does not define a cursor lifetime. An invalid or mismatched cursor returns 400, and the caller can restart from the first page.
  • Changed all errors to the { code, message, details } envelope. Recipient operations document more specific error detail schemas and HTTP responses. Retry-After is optional on rate-limit responses.
  • Removed unsupported request-body size promises and the corresponding 413 responses.
  • Rejected the NUL character in Recipient idempotency keys, labels, references, metadata and text filters.

Changes since 0.2.0

0.2.0 0.4.0
Account-scoped balances and activity using account_id Customer-scoped balance and activity using customer_id. Accounts are not included.
Internal, ACH, wire, onchain and IBFT Transfer creation Internal Customer-to-Customer Transfer creation only.
Recipients addressed through their linked Customer ID and updated with PATCH Stable rcp_… Recipient IDs and complete replacement with PUT. Responses no longer include contact or onboarding fields.
Instruments, funding rules, FX quotes, events and webhooks These operations are not included.
Flat { code, message, param, request_id } errors { code, message, details } errors.
At least 24 hours of idempotent replay for every operation that accepted a key 24 hours of replay for Recipient creation, Recipient replacement when a key is supplied, and Batch creation. Other operations do not guarantee replay or duplicate suppression.

These are historical 0.4.0 contract notes, not the supported surface or retry rules for 0.6.0. Use the current operation definitions when integrating.

0.2.0

  • Renamed the specification from Pocket Money API to Pocket Money - Relay API and moved its base URL from api.pocketmoney.host to api.relay.pocketmoney.global.
  • Replaced API-created business subcustomers (cus_…) with Customer-owned Accounts (acc_…) as directly addressable funds containers. Removed the public Customer hierarchy and subcustomer management operations.
  • Changed balances, transactions, deposits, Transfer sources, Batch sources and funding rules to address Accounts. Added same-Customer Account-to-Account transfers and default receiving Account selection when an internal Transfer addresses a Customer.
  • Replaced business-member resources with Customer-to-Customer Recipients. Added Recipient endpoints, scopes and the recipients_only Batch policy.
  • Added Customer-owned external instruments (epi_…) for bank accounts and wallet addresses, including Customer-held and third-party bank holders.
  • Added outbound ACH and wire Transfers. The 0.1.0 specification supported only internal Transfer creation.
  • Added onchain Transfers to reusable wallet-address instruments and exposed incoming onchain Deposits.
  • Added FX quotes and USD-to-PKR IBFT Transfers, including supported-bank and purpose-code lists, Transfer-level purpose codes and third-party PKR destinations.

Migrating from 0.1.0

Regenerate typed clients from the 0.2.0 specification before switching endpoints. API keys remain associated with one Customer. Update integrations that used the 0.1.0 subcustomer model as follows:

0.1.0 integration Required 0.2.0 change
Create, list, retrieve or update subcustomers through /v1/customers Use /v1/accounts and /v1/accounts/{account_id}. Store the returned acc_… ID. Do not derive it from or substitute a previous cus_… ID.
Address balances and money activity by customer_id Use account_id. This includes source.account_id, Account-relative balance, Transaction and Deposit fields, and the account_id or source_account_id list filters. A Customer destination for an internal Transfer still uses destination.customer_id.
Read /v1/customers/{customer_id}/balances or filter balances by parent_customer_id For one Account, use /v1/accounts/{account_id}/balance. For a list, use /v1/accounts and /v1/balances.
Manage funding rules below /v1/customers/{customer_id}/funding_rules with destination_customer_id Use /v1/accounts/{account_id}/funding_rules. Supply destination_account_id. Responses use source_account_id and destination_account_id.
Use member endpoints, members:* scopes or recipient_policy=payable_members Use Recipient endpoints, recipients:* scopes and recipient_policy=recipients_only.
Submit Batches with source.customer_id Keep the authenticated Customer to use its default Account, or submit a specific owned Account as source.account_id. Batch-item Customer destinations remain destination.customer_id.
Persist 0.1.0 list cursors or handle member event resource references Restart pagination without the saved cursor. Handle recipient and new Account-related resource references, and continue accepting unknown event types and resource identifiers.
Grant customers:read or customers:write Grant accounts:read or accounts:write. Grant the new instruments:* scopes only when using instrument operations.

Do not mechanically replace every customer_id. Customer destinations, instrument ownership paths and the top-level Event.customer_id continue to use cus_… identifiers.

0.1.0

  • Introduced the initial API specification for Customer balances and activity, internal Transfers, Deposits, Batch transfers, funding rules, members, events and webhook endpoints.
  • Used API-created business subcustomers (cus_…) as separate money holders and Transfer or Batch sources.