API reference
Transfers
Every movement of money is a transfer with a source and a destination. The pair decides the kind: user to user, on-ramp, off-ramp or conversion.
List transfers
GET/v1/transfers
Returns a page of Transfer objects.
Query parameters
limitintegerDefault
10.starting_afterstringCursor; an object ID. Returns the page after it. Cannot be combined with
ending_before.ending_beforestringCursor; an object ID. Returns the page before it. Cannot be combined with
starting_after.created_aftertimestampcreated_beforetimestampuser_idstringTransfers where this user owns the source or the destination.
Starts with
usr_.wallet_idstringTransfers where this wallet is the source or the destination.
Starts with
wal_.statestringTransfers of every kind share these states. Which states a kind uses, and how it moves between them:
user_to_user:pending→completed; orpending→in_review→completedorfailed;pending→canceledonly before posting.conversion:pending→completed, orpending→failed(quote expired, stock or treasury too low). Settles in one ledger transaction, so it is final in the response.onramp:awaiting_funds→funds_received→completed. Exceptions:in_review(after funds arrive),returned(funds sent back to the sender, final),canceled(fromawaiting_funds, final),failed.offramp:pending→payout_submitted→completed. Exceptions:in_review(before submission),undeliverableandreturned(rail rejected the payout; both move on torefunded),refunded(source wallet credited back, final),failed(refused before the wallet was debited, final).
Final states:
completed,failed,canceled,returned(on-ramp),refunded. Each change emitstransfer.state_changed.One of
pending,awaiting_funds,funds_received,in_review,payout_submitted,completed,failed,canceled,returned,undeliverable,refunded.kindstringOne of
user_to_user,conversion,onramp,offramp.integrator_referencestring
Responses
- 200A page of transfers.
- 400The request is malformed.
- 401The API key is missing, invalid or revoked.
- 429The key is over its rate limit.
- 5XXServer error. Safe to retry with the same idempotency key.
curl "https://sandbox.api.ndeipi.example/v1/transfers?user_id=usr_01JA8Z2F5G6H7J8K9M0N1P2Q3R&limit=20" \
-H "Api-Key: $NDEIPI_API_KEY"{
"data": [
{
"id": "trf_01JA8ZC4QK7N2Y5W3V0T9S8R6P",
"object": "transfer",
"kind": "user_to_user",
"state": "completed",
"state_reason": null,
"source": {
"type": "wallet",
"wallet_id": "wal_01JA8Z4K2V9QW3H6Y7XKD2M0RS",
"user_id": "usr_01JA8Z2F5G6H7J8K9M0N1P2Q3R",
"asset": "ndeipi-points"
},
"destination": {
"type": "wallet",
"wallet_id": "wal_01JA8Z5B7C1DE2F3G4H5J6K7LM",
"user_id": "usr_01JA8Z3H6J7K8M9N0P1Q2R3S4T",
"asset": "ndeipi-points"
},
"amount": "25.00",
"deposit_instructions": null,
"receipt": {
"amount_debited": "25.00",
"amount_credited": "25.00",
"earned_amount": "0.00",
"fees": [],
"initiated_at": "2026-10-16T09:30:00Z",
"completed_at": "2026-10-16T09:30:00Z"
},
"integrator_reference": "invoice-7781",
"metadata": {
"note": "Lunch"
},
"version": 2,
"created_at": "2026-10-16T09:30:00Z",
"updated_at": "2026-10-16T09:30:00Z"
}
],
"has_more": false
}Create a transfer
POST/v1/transfers
One resource for every money movement. The kind follows from the source and destination:
| Source | Destination | Kind |
|---|---|---|
wallet | another user's wallet in the same asset | user_to_user |
wallet | the same user's wallet in another asset | conversion |
fiat | wallet | onramp |
wallet | payout_account | offramp |
Any other combination returns 422 unsupported_route. A user_to_user transfer posts to the ledger in one transaction and returns completed (or in_review) in the response without calling the provider. An onramp returns awaiting_funds with deposit instructions. An offramp debits the source wallet when accepted and returns pending.
Points keep their kind when sent: a user_to_user transfer of points spends the sender's earned points first and credits the recipient with the same split, so a reward never becomes cashable by changing hands. The receipt shows both parts.
A conversion buys or sells NdeipiCoin with Ndeipi Points. It needs a quote_id from POST /quotes that matches its wallets and amount and has not expired. It settles from Ndeipi's own stock in one ledger transaction and returns completed, so it never waits on the OTC desk. Only cashable (purchased) points can buy NdeipiCoin; earned points return 422 amount_exceeds_cashable. Points from selling NdeipiCoin are cashable. Off-ramps and on-ramps carry points only: NdeipiCoin is cashed out by converting it to points first.
With dry_run: true the request is validated exactly as a real one, including KYC, balance, route and limit checks, and returns 200 with a TransferPreview. Nothing is created and no funds move. A failing dry run returns the same error a real create would.
Returns TransferPreview.
Headers
Idempotency-KeyheaderrequiredA unique key for this write, up to 255 characters. Send the same key to retry safely; see idempotency.
Body
sourceobject | objectrequiredsource.wallet_idstringrequiredwhentypeiswalletStarts with
wal_.source.currencystringrequiredwhentypeisfiatLower-case ISO 4217 fiat currency code.
source.railstringrequiredwhentypeisfiatA payment rail code, published by
GET /routes. Not an enum by design.destinationobject | objectrequireddestination.wallet_idstringrequiredwhentypeiswalletStarts with
wal_.destination.payout_account_idstringrequiredwhentypeispayout_accountStarts with
pa_.amountstringrequiredIn the source's unit: the wallet's asset for
walletsources, the fiat currency forfiatsources. For an on-ramp this is the amount expected; what is credited depends on what arrives (seeTransfer.receipt).quote_idstringRequired for a
conversion, refused on any other kind. The quote's assets, wallets and amount must match this request (422 quote_mismatch) and it must not have expired (422 quote_expired). A quote is used at most once (409 quote_used).Starts with
qt_.integrator_referencestringReturned unchanged. Not required to be unique.
metadataobjectIntegrator key-value data, returned unchanged. Up to 50 keys; keys up to 40 characters, values up to 500.
dry_runbooleanValidate only; nothing is created and no funds move.
Default
false.
Responses
- 200Dry run passed. Nothing was created.
- 201Transfer created.
- 400The request is malformed.
- 401The API key is missing, invalid or revoked.
- 403The key may not perform this action, or the user is not approved.
- 404The object does not exist or belongs to another integrator.
- 409The request conflicts with the object's current state or an earlier request.
- 422The request is well formed but fails validation or a business rule.
- 429The key is over its rate limit.
- 503The on-ramp or off-ramp provider is unavailable and the request was refused rather than queued. Code
provider_unavailable. User-to-user transfers never return this. - 5XXServer error. Safe to retry with the same idempotency key.
curl -X POST "https://sandbox.api.ndeipi.example/v1/transfers" \
-H "Api-Key: $NDEIPI_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"source": {
"type": "wallet",
"wallet_id": "wal_01JA8Z4K2V9QW3H6Y7XKD2M0RS"
},
"destination": {
"type": "wallet",
"wallet_id": "wal_01JA8Z5B7C1DE2F3G4H5J6K7LM"
},
"amount": "25.00",
"integrator_reference": "invoice-7781",
"metadata": {
"note": "Lunch"
}
}'{
"id": "trf_01JA8ZC4QK7N2Y5W3V0T9S8R6P",
"object": "transfer",
"kind": "user_to_user",
"state": "completed",
"state_reason": null,
"source": {
"type": "wallet",
"wallet_id": "wal_01JA8Z4K2V9QW3H6Y7XKD2M0RS",
"user_id": "usr_01JA8Z2F5G6H7J8K9M0N1P2Q3R",
"asset": "ndeipi-points"
},
"destination": {
"type": "wallet",
"wallet_id": "wal_01JA8Z5B7C1DE2F3G4H5J6K7LM",
"user_id": "usr_01JA8Z3H6J7K8M9N0P1Q2R3S4T",
"asset": "ndeipi-points"
},
"amount": "25.00",
"deposit_instructions": null,
"receipt": {
"amount_debited": "25.00",
"amount_credited": "25.00",
"earned_amount": "0.00",
"fees": [],
"initiated_at": "2026-10-16T09:30:00Z",
"completed_at": "2026-10-16T09:30:00Z"
},
"integrator_reference": "invoice-7781",
"metadata": {
"note": "Lunch"
},
"version": 2,
"created_at": "2026-10-16T09:30:00Z",
"updated_at": "2026-10-16T09:30:00Z"
}Get a transfer
GET/v1/transfers/{transfer_id}
Returns Transfer.
Path parameters
transfer_idstringrequiredStarts with
trf_.
Responses
- 200The transfer.
- 401The API key is missing, invalid or revoked.
- 404The object does not exist or belongs to another integrator.
- 429The key is over its rate limit.
- 5XXServer error. Safe to retry with the same idempotency key.
curl "https://sandbox.api.ndeipi.example/v1/transfers/trf_01JA8ZC4QK7N2Y5W3V0T9S8R6P" \
-H "Api-Key: $NDEIPI_API_KEY"{
"id": "trf_01JA8ZC4QK7N2Y5W3V0T9S8R6P",
"object": "transfer",
"kind": "user_to_user",
"state": "completed",
"state_reason": null,
"source": {
"type": "wallet",
"wallet_id": "wal_01JA8Z4K2V9QW3H6Y7XKD2M0RS",
"user_id": "usr_01JA8Z2F5G6H7J8K9M0N1P2Q3R",
"asset": "ndeipi-points"
},
"destination": {
"type": "wallet",
"wallet_id": "wal_01JA8Z5B7C1DE2F3G4H5J6K7LM",
"user_id": "usr_01JA8Z3H6J7K8M9N0P1Q2R3S4T",
"asset": "ndeipi-points"
},
"amount": "25.00",
"deposit_instructions": null,
"receipt": {
"amount_debited": "25.00",
"amount_credited": "25.00",
"earned_amount": "0.00",
"fees": [],
"initiated_at": "2026-10-16T09:30:00Z",
"completed_at": "2026-10-16T09:30:00Z"
},
"integrator_reference": "invoice-7781",
"metadata": {
"note": "Lunch"
},
"version": 2,
"created_at": "2026-10-16T09:30:00Z",
"updated_at": "2026-10-16T09:30:00Z"
}Cancel a transferComing soon
POST/v1/transfers/{transfer_id}/cancel
501 not_implemented until this operation ships.Allowed only before the transfer's ledger posting: an onramp in awaiting_funds, or any transfer in pending that has not yet posted. Otherwise returns 409 transfer_not_cancelable. Funds that arrive for a canceled on-ramp are returned to the sender and reported with a deposit.returned event.
Returns Transfer.
Path parameters
transfer_idstringrequiredStarts with
trf_.
Headers
Idempotency-KeyheaderrequiredA unique key for this write, up to 255 characters. Send the same key to retry safely; see idempotency.
Responses
- 200The canceled transfer.
- 401The API key is missing, invalid or revoked.
- 404The object does not exist or belongs to another integrator.
- 409The request conflicts with the object's current state or an earlier request.
- 429The key is over its rate limit.
- 5XXServer error. Safe to retry with the same idempotency key.
curl -X POST "https://sandbox.api.ndeipi.example/v1/transfers/trf_01JA8ZC4QK7N2Y5W3V0T9S8R6P/cancel" \
-H "Api-Key: $NDEIPI_API_KEY" \
-H "Idempotency-Key: $(uuidgen)"{
"id": "trf_01JA8ZD7RM8P3Z6X4W1V0T9S8R",
"object": "transfer",
"kind": "onramp",
"state": "canceled",
"state_reason": null,
"source": {
"type": "wallet",
"wallet_id": "wal_01JA8Z4K2V9QW3H6Y7XKD2M0RS",
"user_id": "usr_01JA8Z2F5G6H7J8K9M0N1P2Q3R",
"asset": "ndeipi-points"
},
"destination": {
"type": "wallet",
"wallet_id": "wal_01JA8Z5B7C1DE2F3G4H5J6K7LM",
"user_id": "usr_01JA8Z3H6J7K8M9N0P1Q2R3S4T",
"asset": "ndeipi-points"
},
"amount": "25.00",
"deposit_instructions": null,
"receipt": null,
"integrator_reference": "invoice-7781",
"metadata": {
"note": "Lunch"
},
"version": 2,
"created_at": "2026-10-16T09:30:00Z",
"updated_at": "2026-10-16T09:30:00Z"
}