> For AI agents: the documentation index is at https://sdk.nimbusprivacy.xyz/llms.txt, and every page is available as Markdown at its URL with .md appended.

# How a payment works (/explainers/payments)

A Solana transfer is built, signed, sent and confirmed. A Nimbus payment adds two steps: a group of
services approves it, and the browser proves it follows the pool's rules.

## The five steps

This chart follows a Private Send, which a relayer sends for your user.

```mermaid
sequenceDiagram
participant App as Your app
participant SDK
participant Services as Nimbus services
participant Solana
App->>SDK: transfer
SDK->>SDK: prepare and reserve the notes
SDK->>SDK: prove, in the browser
SDK->>Services: approve and relay
Services->>Solana: send the transaction
SDK->>Services: follow until it lands
SDK-->>App: relayed, with its settlement
```

*A Private Send: the SDK prepares and proves it on the device, Nimbus's services approve and relay it, and the SDK follows it until Solana settles it.*

Your app makes one call and gets one result. Each step in between works like this.

### Prepare

The SDK syncs, picks the notes to spend and reserves them. It asks Nimbus's context service for what
the proof needs. It stops here if the balance is too small or cannot cover the fees.

### Approve

A committee of Nimbus services checks the payment and co-signs it. Solana accepts the payment only
with their combined signature. If one refuses, the payment stops before anything is sent.

### Prove

The browser builds a zero-knowledge proof that the payment follows the pool's rules. This takes most
of a payment's time, and closing the tab stops it.

### Send

Your user's wallet signs and the SDK sends it, or a relayer sends it. Your user can decline in the
wallet, and a relayer can refuse.

### Settle

The SDK waits for Solana to confirm the transaction. It checks every 1,000 milliseconds.
It returns once the transaction lands or fails, or when it stops waiting.

For a deposit or registration, approval and proof run at the same time. For a Private Send or
withdrawal, the committee approves as the relayer accepts the payment.

## Who sends it

| Payment      | Who signs                                                  | Who sends it to Solana    |
| ------------ | ---------------------------------------------------------- | ------------------------- |
| Register     | Your user's wallet, then the SDK for their platform wallet | The SDK, from the browser |
| Deposit      | Your user's wallet, then the SDK for their platform wallet | The SDK, from the browser |
| Private Send | Nobody on the device                                       | A relayer                 |
| Withdraw     | Nobody on the device                                       | A relayer                 |

A relayer pays the Solana fee and appears as the signer. That keeps your user's wallet off their
Private Sends and withdrawals.

## How it settles

A relayed payment returns `relayed` with a `settlement`. The SDK checks on it up to 180 times.

| `settlement.kind` | What it means                                                                 |
| ----------------- | ----------------------------------------------------------------------------- |
| `landed`          | It landed, and the SDK synced the private balance after.                      |
| `failed`          | The relayer rejected it, gave it up, or it failed on Solana.                  |
| `pending`         | The SDK stopped waiting first. It may still land, and a later sync will tell. |
| `not-followed`    | You passed `follow: false`, so the SDK returned when the relayer accepted it. |

A transaction your user's wallet sent returns a `TransactionSubmission`. The SDK checks it up to 90 times.

| `kind`    | What it means                                           |
| --------- | ------------------------------------------------------- |
| `landed`  | It landed without error.                                |
| `failed`  | It landed and failed. Only the network fee was charged. |
| `expired` | Its blockhash expired first. It will never land.        |
| `refused` | The RPC would not forward it. It cannot land.           |
| `unknown` | The SDK stopped waiting first. It may still land.       |

## Confirmed or finalized

Solana reports a transaction as `confirmed` within seconds. It becomes `finalized` once it can no
longer be rolled back, some seconds later.

Payments wait for `confirmed` by default. Pass `commitment: "finalized"` when your app needs
certainty more than speed.

## Merging

One payment spends at most three notes. The SDK always takes the three largest, so the rest of the
balance gathers in the change.

When the three largest cannot cover a Private Send or withdrawal, the SDK merges first. Each merge
sends three notes back to your user as one, then waits for it to land. You see each merge as
`merge-prepared`, `merge-relayed` and `merge-settled` stages.

A merge pays the relayer's tip, but no protocol fee. If one fails, the result is `merge-failed`.
Merges that finished stay merged, so a retry needs fewer.

A deposit also merges up to three of your user's notes of that token into its change.

## What this means for your app

- **Show progress.** A payment takes seconds, and most of it is the proof.
- **Ask your user to keep the tab open** until the payment returns.
- **Read the settlement, not just the kind.** `relayed` means accepted, and `landed` means done.

* [Show progress](https://sdk.nimbusprivacy.xyz/api-design/progress): Every stage, with a message to show.
* [Results and errors](https://sdk.nimbusprivacy.xyz/api-design/results-and-errors): How a payment reports what went wrong.


Last updated 21 October 2026
