Skip to main content
POST
Add recipient payment detail

fields is defined by the route, not by you

Read the route first — GET /v1/transfer-corridors or GET /v1/currencies/{code}/routes — and send exactly the keys its fieldDescriptors name. Each value is checked for presence, length, and pattern, and a key the route does not define is rejected rather than ignored. Every failure comes back at once, in errors, each entry naming the field and why:

Defaults

The first detail added to a recipient becomes the default whatever isDefault says. Setting isDefault on a later one moves the default to it.

Institution names

For a route with institutionDirectoryEnabled, submit the institution code in fields and Sakawave resolves the canonical name. Sending institutionName yourself is pointless there.

Error codes

Authorizations

Authorization
string
header
required

A secret API key, sent as Authorization: Bearer sk_test_… in sandbox or Bearer sk_live_… in live.

Path Parameters

recipientId
string
required

Saved recipient ULID. A 26-character ULID.

Required string length: 26
Pattern: ^[0-7][0-9A-HJKMNP-TV-Z]{25}$
Example:

"01K2Z3AB4C5D6E7F8G9HJKMNPQ"

Body

application/json
routeId
string
required

A route id from GET /v1/currencies/{code}/routes or GET /v1/transfer-corridors.

Required string length: 26
Pattern: ^[0-7][0-9A-HJKMNP-TV-Z]{25}$
Example:

"01K2Z3AB4C5D6E7F8G9HJKMNPQ"

currency
enum<string>
required
Available options:
NGN,
USD,
GBP,
EUR,
GHS,
ZAR,
UGX,
CAD,
MXN,
AED,
AUD,
SGD,
HKD,
KES,
TZS,
BRL,
CNY,
XAF,
PHP,
USDC
accountHolderName
string
required
Required string length: 2 - 200
fields
object
required

The routing values for this route, keyed exactly as the route’s fieldDescriptors name them. A key the route does not define is rejected.

country
string

Required for a bank-transfer route that is not already scoped to one country. Uppercased on the way in.

Required string length: 2
institutionName
string

Ignored for directory-backed routes, where Sakawave resolves the name from the institution code.

Maximum string length: 160
isDefault
boolean
default:false

The first detail on a recipient becomes the default whatever this says.

Response

Successful response.

id
string
required

A 26-character ULID.

Required string length: 26
Pattern: ^[0-7][0-9A-HJKMNP-TV-Z]{25}$
Example:

"01K2Z3AB4C5D6E7F8G9HJKMNPQ"

recipientId
string
required

A 26-character ULID.

Required string length: 26
Pattern: ^[0-7][0-9A-HJKMNP-TV-Z]{25}$
Example:

"01K2Z3AB4C5D6E7F8G9HJKMNPQ"

routeId
string
required

A 26-character ULID.

Required string length: 26
Pattern: ^[0-7][0-9A-HJKMNP-TV-Z]{25}$
Example:

"01K2Z3AB4C5D6E7F8G9HJKMNPQ"

paymentMethod
enum<string>
required
Available options:
bank-transfer,
momo-transfer
currency
enum<string>
required
Available options:
NGN,
USD,
GBP,
EUR,
GHS,
ZAR,
UGX,
CAD,
MXN,
AED,
AUD,
SGD,
HKD,
KES,
TZS,
BRL,
CNY,
XAF,
PHP,
USDC
country
string | null
required
Required string length: 2
accountHolderName
string
required
institutionName
string | null
required
maskedIdentifier
string
required

The last four characters of the routing identifier, masked.

isDefault
boolean
required
providerLocked
boolean
required

True when the provider holds the routing fields and they cannot be edited here.

createdAt
string<date-time>
required
Example:

"2026-08-14T09:00:00.000Z"