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

# Recipient and destination use cases

> Four flows — recurring payouts, one-off transfers, paying inside Sakawave, and rotating a bank account.

## 1. Pay the same people every month

Save each payee once, then each run is one destination call per payee.

**Onboarding a payee** — three calls, done once:

```ts theme={null}
const recipient = await post('/v1/transfer-recipients', {
  type: 'individual',
  displayName: payee.name,
  firstName: payee.firstName,
  lastName: payee.lastName,
  relationship: 'contractor',
  email: payee.email,
  address: { line1: payee.line1, city: payee.city, country: payee.country },
});

await post(`/v1/transfer-recipients/${recipient.id}/payment-details`, {
  routeId: route.id,
  currency: route.destinationCurrency,
  country: payee.country,
  accountHolderName: payee.name,
  fields: collectedFromDescriptors,     // see routing fields and verification
  isDefault: true,
});

await store(payee.id, recipient.id);    // your join, kept on your side
```

**Each payout run** — one destination and one transfer per payee:

```ts theme={null}
for (const payee of run.payees) {
  const destination = await post(`/v1/transfer-destinations/recipients/${payee.recipientId}`, {});

  if (destination.displayName !== payee.expectedAccountName) {
    // Verification returned a different name than we hold. Stop and review;
    // this is the cheapest fraud and typo check available.
    await flagForReview(payee, destination.displayName);
    continue;
  }

  await post('/v1/transfers', {
    destinationId: destination.id,
    externalReference: `payroll_${run.id}_${payee.id}`,
    // …amounts
  });

  await pace();   // 120/min covers destinations and transfers together
}
```

The name comparison is worth keeping. On routes with account verification, `displayName` is the name the bank holds — a mismatch means the account number is wrong, or belongs to someone else.

## 2. One-off payout

A refund to someone you will never pay again does not need a recipient.

```bash theme={null}
curl --request POST \
  --url "$SAKAWAVE_BASE_URL/v1/transfer-destinations" \
  --header "Authorization: Bearer $SAKAWAVE_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "type": "external",
    "routeId": "01K2Z3AB4M5N6P7Q8RSTVWXYZA",
    "currency": "KES",
    "country": "KE",
    "accountName": "Ada Okafor",
    "recipientRelationship": "customer",
    "recipientEmail": "ada@example.com",
    "recipientAddressLine1": "14 Marina Road",
    "recipientCity": "Nairobi",
    "bankCode": "001",
    "accountNumber": "0123456789"
  }'
```

Two differences from the saved path:

* **Routing fields are top-level**, not inside a `fields` object. `bankCode` and `accountNumber` sit beside `routeId`.
* **The counterparty profile is on you.** The four `recipient*` fields are what the payout rail requires; the parser will accept the request without them and the provider will not.

Add `"saveRecipient": true` and Sakawave keeps the recipient for next time — the middle ground between the two paths.

## 3. Pay inside Sakawave

Two shapes, and the difference catches people out.

```jsonc theme={null}
// Your own account. Not a customer wallet — those are excluded.
{ "type": "own_account", "accountId": "01K2Z3AB4D5E6F7G8H9JKMNPQR", "currency": "NGN" }

// Any Sakawave account by its public handle, including customer wallets.
{ "type": "sakawave_tag", "tag": "AA0271849366" }
```

Both fail the same way when the target cannot receive:

```json theme={null}
{ "statusCode": 404, "message": "Destination account not found.", "code": "destination_account_not_found" }
```

One code covers every cause — wrong id, inactive, `canCredit` false, currency mismatch, a wallet given to `own_account`, or an account that is not yet `transferReady`. That is deliberate: it stops the endpoint being used to discover which accounts exist. When it happens against an account you own, read the account and check those five things yourself.

## 4. Rotate a bank account

A payee changes bank. There is no update endpoint, so this is add-then-remove — in that order.

```ts theme={null}
// 1. Add the new detail and make it the default.
const replacement = await post(`/v1/transfer-recipients/${recipientId}/payment-details`, {
  routeId: newRoute.id,
  currency: newRoute.destinationCurrency,
  country: payee.country,
  accountHolderName: payee.name,
  fields: newFields,
  isDefault: true,
});

// 2. Prove it resolves before discarding the old one.
const destination = await post(`/v1/transfer-destinations/recipients/${recipientId}`, {
  paymentDetailId: replacement.id,
});
if (destination.displayName !== payee.expectedAccountName) throw new Error('verification mismatch');

// 3. Only now remove the old detail.
await del(`/v1/transfer-recipients/${recipientId}/payment-details/${oldDetailId}`);
```

Deleting first would leave the payee unpayable if the new detail turned out to be wrong, and you cannot read the old value back to restore it.

<Note>
  Deleting the default detail promotes another automatically. Add the replacement as the default **before** removing the old one, or the promotion will pick whichever detail happens to be next.
</Note>

## Offboarding a payee

```bash theme={null}
curl --request DELETE \
  --url "$SAKAWAVE_BASE_URL/v1/transfer-recipients/$RECIPIENT_ID" \
  --header "Authorization: Bearer $SAKAWAVE_API_KEY"
```

The recipient and its details are deactivated. Transfers already made keep their masked snapshot of where the money went, so history stays readable.

Two caveats: there is no undelete — recreating mints a new id — and a destination already resolved from this recipient stays spendable until it expires.

## What you cannot do here

* **Update anything.** No `PATCH` on a recipient or a payment detail, except promoting a default.
* **Read routing values back.** `maskedIdentifier` is final.
* **Reuse a destination.** Single use, 24 hours.
* **Search or page recipients.** `GET /v1/transfer-recipients` takes no parameters and returns everything.
* **Pay a customer wallet by `own_account`.** Use the tag.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.