ndeipipayments APIv1 · 0.1.0-draftAPI reference

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

limitinteger

Default 10.

starting_afterstring

Cursor; an object ID. Returns the page after it. Cannot be combined with ending_before.

ending_beforestring

Cursor; an object ID. Returns the page before it. Cannot be combined with starting_after.

created_aftertimestamp
created_beforetimestamp
user_idstring

Transfers where this user owns the source or the destination.

Starts with usr_.

wallet_idstring

Transfers where this wallet is the source or the destination.

Starts with wal_.

statestring

Transfers of every kind share these states. Which states a kind uses, and how it moves between them:

  • user_to_user: pending → completed; or pending → in_review → completed or failed; pending → canceled only before posting.
  • conversion: pending → completed, or pending → 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 (from awaiting_funds, final), failed.
  • offramp: pending → payout_submitted → completed. Exceptions: in_review (before submission), undeliverable and returned (rail rejected the payout; both move on to refunded), 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 emits transfer.state_changed.

One of pending, awaiting_funds, funds_received, in_review, payout_submitted, completed, failed, canceled, returned, undeliverable, refunded.

kindstring

One 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.
Request
curl "https://sandbox.api.ndeipi.example/v1/transfers?user_id=usr_01JA8Z2F5G6H7J8K9M0N1P2Q3R&limit=20" \
  -H "Api-Key: $NDEIPI_API_KEY"
Response 200
{
  "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:

SourceDestinationKind
walletanother user's wallet in the same assetuser_to_user
walletthe same user's wallet in another assetconversion
fiatwalletonramp
walletpayout_accountofframp

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-Keyheaderrequired

A unique key for this write, up to 255 characters. Send the same key to retry safely; see idempotency.

Body

sourceobject | objectrequired
source.wallet_idstringrequiredwhen type is wallet

Starts with wal_.

source.currencystringrequiredwhen type is fiat

Lower-case ISO 4217 fiat currency code.

source.railstringrequiredwhen type is fiat

A payment rail code, published by GET /routes. Not an enum by design.

destinationobject | objectrequired
destination.wallet_idstringrequiredwhen type is wallet

Starts with wal_.

destination.payout_account_idstringrequiredwhen type is payout_account

Starts with pa_.

amountstringrequired

In the source's unit: the wallet's asset for wallet sources, the fiat currency for fiat sources. For an on-ramp this is the amount expected; what is credited depends on what arrives (see Transfer.receipt).

quote_idstring

Required 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_referencestring

Returned unchanged. Not required to be unique.

metadataobject

Integrator key-value data, returned unchanged. Up to 50 keys; keys up to 40 characters, values up to 500.

dry_runboolean

Validate 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.
Request
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"
    }
  }'
Response 200
{
  "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_idstringrequired

Starts 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.
Request
curl "https://sandbox.api.ndeipi.example/v1/transfers/trf_01JA8ZC4QK7N2Y5W3V0T9S8R6P" \
  -H "Api-Key: $NDEIPI_API_KEY"
Response 200
{
  "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

Not available yet: the server answers 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_idstringrequired

Starts with trf_.

Headers

Idempotency-Keyheaderrequired

A 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.
Request
curl -X POST "https://sandbox.api.ndeipi.example/v1/transfers/trf_01JA8ZC4QK7N2Y5W3V0T9S8R6P/cancel" \
  -H "Api-Key: $NDEIPI_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"
Response 200
{
  "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"
}