2. Core Concepts

Read this before integrating. These concepts appear throughout the API.


Payment intent

A payment intent is the central object. It represents "I expect a payment to this address." When you create one (POST /intents), we:

  1. Derive a unique deposit address for it — never reused across intents.
  2. Give it a stable derivation_index you use to track it.
  3. Persist it so detection and settlement survive restarts.

An intent records:

FieldMeaning
derivation_indexThe stable integer id. Use this to poll status and reference the payment.
addressThe Solana deposit address to show the payer.
mintThe asset accepted: null = native SOL, else an SPL mint address.
mint_decimalsDecimals of the token (null for SOL).
expected_lamportsExpected amount in base units (see below). null = accept any amount.
received_lamportsAmount received so far, in base units.
statusLifecycle state (see below).
referenceYour own id (order id, user id, memo). Optional, opaque to us.
expires_atWhen the payment window closes. Optional.

Naming note: the fields are called expected_lamports / received_lamports for historical reasons, but they hold base units of the intent's asset — lamports for SOL, token base units for an SPL token. Treat them as generic integer amounts.


Assets: SOL or any SPL token

Each intent accepts exactly one asset, chosen at creation:

Base units = the smallest indivisible unit. To convert:

base_units = ui_amount × 10^decimals
ui_amount  = base_units ÷ 10^decimals

Examples:

Both classic SPL Token and Token-2022 mints are supported automatically.


The settlement lifecycle

Once a payment arrives, we settle it automatically: sweep the funds to your sweep destination, refund any excess, and so on. You don't trigger this — you just observe the status.

Status values

status (and the derived action in /status, which is a friendlier alias):

statusactionMeaning
pendingwaitingCreated, no payment detected yet.
underpaidunderpaidReceived less than expected. Waiting for the rest.
paidpaidReceived the expected amount (or any, for open-ended intents). About to sweep.
overpaidoverpaidReceived more than expected. Will sweep expected + refund excess.
sweeping / settlingpaidSettlement transaction in progress.
sweptsweptTerminal success. Funds are in your destination wallet.
refundingrefundingBeing refunded (expired with a partial balance).
refundedrefundedTerminal. Funds returned to the sender.
expiredexpiredTerminal. Window closed with nothing received.
refund_failed / settle_failedrefund_failedTerminal-ish. Automatic settlement/refund exhausted retries; contact support.

The status you care about most is swept — that means the money is yours and settlement tells you exactly how much.

Normal happy path

pending → paid → sweeping → swept

Overpayment

pending → overpaid → settling → swept
(expected amount → your wallet; excess → back to sender)

Underpayment then completion

pending → underpaid → (sender sends the rest) → paid → swept

Underpayment then expiry (SOL)

pending → underpaid → (expires) → refunding → refunded
(the partial amount is returned to the sender)

What "settled" means and the settlement object

When an intent reaches swept, /status includes a settlement object — the exact on-chain amounts that were delivered. This is your source of truth for accounting:

"settlement": {
  "asset": "SOL",
  "decimals": 9,
  "destination_amount": 499995000,
  "destination_ui": 0.499995,
  "platform_fee_amount": 0,
  "platform_fee_ui": 0.0,
  "signatures": ["4bd..."]
}

destination_amount can be less than received_lamports — that's expected. received_lamports is the gross amount the payer sent; destination_amount is what you actually net after fees/refunds. Use settlement for accounting.


Fees, refunds, and edge cases (summary)

We handle these automatically:


Idempotency & safety guarantees

Next: API reference →