> ## Documentation Index
> Fetch the complete documentation index at: https://docs.spendin.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Quickstart

> From zero to a funded payout, including the two prerequisites people miss.

This walks a real payout end to end: 500 GHS to an MTN mobile money number in
Ghana, settled in USDT.

You need an approved account and a key with `refunds:manage`, `identity:enquiry`,
`rates:read`, `payouts:write`, and `payouts:read`.

```bash theme={null}
export SPENDIN_BASE_URL="https://api.spendin.app"
export SPENDIN_API_KEY="sk_live_..."
```

## 1. Register a refund destination

Do this once per settlement currency, before your first payout.

We hold no balance for you. If a payout fails *after* your settlement has
arrived, the money has to go somewhere — so we require the destination up front
rather than asking for it at the worst possible moment.

```bash theme={null}
curl -X PUT "$SPENDIN_BASE_URL/v1/refund-destinations" \
  -H "X-API-Key: $SPENDIN_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "currency": "USDT",
    "type": "CRYPTO_ADDRESS",
    "network": "TRON",
    "address": "TXyz1234abcd..."
  }'
```

<Warning>
  Skip this and `POST /v1/payouts` returns `422 REFUND_DESTINATION_REQUIRED`. The
  destination must match the `settlement_currency` of the payout — a USDT payout
  needs a USDT destination, not an NGN one.
</Warning>

Settling in NGN instead? Register a Nigerian bank account. We run name enquiry on
it before saving, so a typo fails here rather than when a refund is already owed.
See [Refunds](/guides/refunds).

## 2. Find the provider code

The processor routes on a machine code, never on a display name. `"MTN"` is not a
routing value — `destination_provider_code` is.

```bash theme={null}
curl "$SPENDIN_BASE_URL/v1/banks?country=GH&currency=GHS&destination_type=MOBILE_MONEY" \
  -H "X-API-Key: $SPENDIN_API_KEY"
```

```json theme={null}
[
  { "provider_name": "MTN", "provider_code": "<code>", "destination_type": "MOBILE_MONEY" },
  { "provider_name": "VODAFONE", "provider_code": "<code>", "destination_type": "MOBILE_MONEY" }
]
```

Each row's `provider_name` and `provider_code` are named exactly as the payout
request expects them, so you can pass them straight through.

<Warning>
  Read the codes from this endpoint — do not hardcode them from this page. Codes
  are corridor-specific and set by the upstream institution directory, so they
  differ in format between countries and can change without a version bump on our
  side. Banks and mobile money providers are never mixed in one response;
  `destination_type` selects which list you get.
</Warning>

## 3. Confirm the beneficiary

Name enquiry tells you who actually holds the account, before you commit money to
it.

```bash theme={null}
curl -X POST "$SPENDIN_BASE_URL/v1/banks/resolve" \
  -H "X-API-Key: $SPENDIN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "destination_type": "MOBILE_MONEY",
    "destination_currency": "GHS",
    "destination_account_unique": "0244000000",
    "destination_provider_code": "MTN"
  }'
```

```json theme={null}
{
  "destination_account_name": "KWAME MENSAH",
  "destination_account_unique": "0244000000",
  "destination_provider_code": "MTN",
  "destination_type": "MOBILE_MONEY"
}
```

Use the returned `destination_account_name` on the payout — it is what the
institution holds, not what your user typed. This endpoint needs no
`Idempotency-Key`; it writes nothing.

## 4. Price it

An indicative rate is live and non-binding — use it to show a price. A quote
freezes the rate and fees for a short TTL.

<CodeGroup>
  ```bash Indicative theme={null}
  curl "$SPENDIN_BASE_URL/v1/rates?from=USDT&to=GHS" \
    -H "X-API-Key: $SPENDIN_API_KEY"
  ```

  ```bash Executable quote theme={null}
  curl -X POST "$SPENDIN_BASE_URL/v1/rates/quotes" \
    -H "X-API-Key: $SPENDIN_API_KEY" \
    -H "Idempotency-Key: $(uuidgen)" \
    -H "Content-Type: application/json" \
    -d '{
      "settlement_currency": "USDT",
      "destination_currency": "GHS",
      "destination_amount": 500
    }'
  ```
</CodeGroup>

```json theme={null}
{
  "id": "b7e2f1a0-...",
  "rail": "CRYPTO",
  "exchange_rate": 12.2265,
  "destination_amount": 500,
  "destination_currency": "GHS",
  "settlement_amount": 42.32,
  "settlement_currency": "USDT",
  "total_fee_amount": 1.41,
  "total_fee_currency": "USDT",
  "status": "PENDING",
  "expires_at": "2026-08-16T09:41:30.000Z"
}
```

Quotes expire in about 30 seconds. This step is optional — quoting is for showing
a price to your user. Payout creation locks its own rate regardless. See
[Rates and quotes](/guides/quotes).

## 5. Create the payout

`destination_amount` is what the beneficiary receives. We derive what you owe.

```bash theme={null}
curl -X POST "$SPENDIN_BASE_URL/v1/payouts" \
  -H "X-API-Key: $SPENDIN_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "destination_type": "MOBILE_MONEY",
    "destination_country": "GH",
    "destination_currency": "GHS",
    "destination_amount": 500,
    "destination_account_unique": "0244000000",
    "destination_provider_name": "MTN",
    "destination_provider_code": "MTN",
    "destination_account_name": "KWAME MENSAH",
    "settlement_currency": "USDT",
    "settlement_network": "TRON",
    "merchant_reference": "order_1041"
  }'
```

The payout returns with status `CREATED`, `settlement_amount: 0`, and
`payment_instruction: null`. All three change within seconds — the settlement
amount is not known until the rate is locked.

<Warning>
  Generate a fresh `Idempotency-Key` per payout and reuse it only when retrying
  *that* payout. Two different payouts sharing a key means the second silently
  returns the first one's response and is never created.
</Warning>

## 6. Read the payment instruction

Instruction generation is kicked off immediately and swept every 30 seconds as a
safety net. Wait for the `payout.pending_payment` webhook rather than polling.

```bash theme={null}
curl "$SPENDIN_BASE_URL/v1/payouts/$PAYOUT_ID/payment-instruction" \
  -H "X-API-Key: $SPENDIN_API_KEY"
```

```json theme={null}
{
  "id": "c4d5e6f7-...",
  "payout_id": "a3f1c2d4-...",
  "rail": "CRYPTO",
  "account_unique": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
  "provider_name": "USDT (TRC-20)",
  "network": "TRC20",
  "expected_amount": 42.32,
  "expected_currency": "USDT",
  "payment_status": "UNPAID",
  "expires_at": "2026-08-16T10:11:00.000Z"
}
```

<Note>
  A `404 PAYMENT_INSTRUCTION_NOT_FOUND` here means the instruction does not exist
  *yet* — not that the payout is invalid. Retry, or wait for the webhook.
</Note>

## 7. Settle it

Send **exactly** `expected_amount` to `account_unique` before `expires_at`.

* Send less and the payout is held for review, not partially processed.
* Send more and it proceeds; the excess comes back with any later refund.
* Send nothing and the payout ends `EXPIRED` — terminal. Create a new one.

Crypto settlements also wait for network confirmations before advancing. See
[Settlement](/guides/settlement).

## 8. Track it

```bash theme={null}
curl "$SPENDIN_BASE_URL/v1/payouts/$PAYOUT_ID" \
  -H "X-API-Key: $SPENDIN_API_KEY"
```

The response carries the current `status` and a `status_timeline` — every
transition with the reason it happened and when. That timeline, not webhook
arrival order, is the authoritative history.

## Next steps

<CardGroup cols={2}>
  <Card title="Set up webhooks" icon="webhook" href="/guides/webhooks">
    Stop polling. Every transition, pushed and signed.
  </Card>

  <Card title="Payout lifecycle" icon="arrow-progress" href="/guides/payout-lifecycle">
    All seven statuses and what moves a payout between them.
  </Card>

  <Card title="Handle failures" icon="rotate-left" href="/guides/refunds">
    What happens to your money when a payout fails.
  </Card>

  <Card title="Go live" icon="circle-check" href="/guides/go-live">
    The checklist before you point real volume at this.
  </Card>
</CardGroup>
