> Solana Developer Platform docs, served as Markdown. The [docs index](https://platform-beta.solana.com/docs.md) lists every page and [llms.txt](https://platform-beta.solana.com/docs/ai/llms.txt) the key ones; [SKILL.md](https://platform-beta.solana.com/SKILL.md) has the rules for building on SDP.

# How payments work on SDP

Custody wallets, tokens, amounts, fees and the transfer record behind every SDP payment.

An SDP payment moves tokens from one of your custody wallets to a Solana address, and SDP records it as a transfer. This page covers the pieces the rest of the section builds on.

## Custody wallets

Every payment leaves from a **custody wallet** in your organization, optionally scoped to a project. A custody provider holds the wallet's key, and SDP asks that provider to sign. Limit what an API key can do with a wallet through [grants](/docs/developing-with-sdp/manage-api-keys#grants-by-module) and [wallet access](/docs/developing-with-sdp/manage-api-keys#wallet-access).

Create wallets first: see [Set Up Wallets](/docs/guides/setup-wallets). A transfer request names its source by the wallet's SDP `id` (`cwlt_…`) in `sourceCustodyWalletId`. Responses return that id as `custodyWalletId` and keep the provider's own wallet id in `providerWalletId`. The **destination** is the recipient's Solana address.

## Tokens and amounts

Pass `token` as `SOL` for native SOL, a well-known symbol such as `USDC`, or an SPL mint address. Pass a custom token by its mint. SDP resolves a symbol to its mint on your project's cluster and stores the mint on the transfer. The `token` filter on `GET /v1/payments/transfers` takes the same forms, and a well-known token matches whether you filter by symbol or by mint.

Amounts are decimal strings in whole-token units: `"100.00"` is 100 tokens, whatever the mint's decimals. Every amount you send SDP takes this form, and SDP converts it to base units when it builds the transaction. Wallet balance responses return both forms: `amount` in base units and `uiAmount` as a decimal string.

## Transfer execution

`POST /v1/payments/transfers` runs the whole transfer on the server. SDP builds the transaction, has the source wallet's custody provider sign it, submits it to your project's cluster, and waits for the cluster to finalize it before it answers.

## Fees and sponsorship

SDP pays the network fee, and the rent for any new recipient token account, on each transfer it signs from a Privy wallet. Those wallets need only the tokens they send, with no SOL for fees. Sponsorship has limits: when the sponsor can't pay, SDP refuses the transfer rather than charge the source wallet. A provider that submits its own transfers pays their fees from its own account, and SDP does not sponsor them.

## The transfer data model

Every transfer SDP sends or records is a `Transfer`. The fields most flows use:

| Field | Meaning |
| --- | --- |
| `id` | The SDP transfer id (`xfr_…`). Use it in API paths. |
| `status` | `processing`, `confirmed`, `finalized` or `failed` for a wallet transfer. See [Verifying a payment](/docs/payments/accept-verification). |
| `direction` | `outbound` (you paid) or `inbound` (you were paid). |
| `source`, `destination` | Solana addresses. |
| `counterpartyId` | The counterparty (`cpty_…`) the transfer is tied to, such as a [transfer batch](/docs/payments/send-payouts#native-transfer-batches) recipient or a transfer sent with `counterpartyAccountId`. |
| `token`, `amount` | What moved and how much. `token` is a mint address (some older records hold a symbol); `amount` is a decimal string such as `"100.00"`. |
| `memo` | Your note, up to 256 characters. SDP stores it; it is not written on chain. See [Payment with memo](/docs/payments/send-payment-with-memo). |
| `signature` | The Solana transaction signature, once there is one. Unique across SDP transfers. |
| `slot`, `blockTime`, `confirmedAt` | The slot the transaction landed in, its block time, and when SDP saw it land. `blockTime` is often `null` on transfers SDP sends. |
| `fee` | The network fee in lamports, when known. |
| `error` | Why the transfer failed. |

An outbound transfer starts at `processing`, and a successful `POST` usually answers `finalized`. See [Verifying a payment](/docs/payments/accept-verification#status-lifecycle) for every status and what it means.

## What SDP does and does not give you

You get what you need to **send**, **track** and **reconcile** payments transfer by transfer: status reads, filtered lists, wallet balances, `Idempotency-Key` replay, and a unique `signature` per transfer (see [Basic payment](/docs/payments/send-basic-payment#deduplication)). The public API has no checkout session or invoice object. [Accept payments](/docs/payments/accept-overview) covers the dashboard's payment links and how to build checkout on transfers.

## Related

- [Payments API reference](/docs/reference/api/payments): every payments endpoint.
- [End-to-end payment flow](/docs/tutorials/end-to-end-payment-flow): the same lifecycle as a step-by-step tutorial.
