Reference
The API
Read the whole contract before you talk to us. The endpoint list below is read from the platform's own description at the moment you load this page, so it is whatever the platform actually serves rather than whatever we last remembered to write down.
Credentials are issued to onboarded institutions. Every endpoint below refuses an unauthenticated caller, which is why the reference can be public and the platform cannot.
Every request carries a bearer token. A session token is short lived and is intended for interactive use; an API key is for your systems and is scoped to a subset of what your own role can do. A key can never hold authority you do not have.
curl http://api.railway.internal:8000/api/v1/conversions \
-H "Authorization: Bearer ${VAPS_API_KEY}"Send an Idempotency-Key header on every request that moves money. A retry with the same key returns the original result rather than acting twice. This matters most when a request times out: the safe response to a timeout is to retry with the same key, never to send a new one.
curl -X POST http://api.railway.internal:8000/api/v1/conversions \
-H "Authorization: Bearer ${VAPS_API_KEY}" \
-H "Idempotency-Key: 8f14e45f-ea1a-4a52-9c9f-2c1f7f2f8d21" \
-H "Content-Type: application/json" \
-d '{
"customer_id": "cus_...",
"source_asset": "GHS",
"source_amount": "500000.00",
"destination_asset": "USDC",
"destination_network": "BASE",
"destination_address": "0x..."
}'Note what is absent from that body: no rate, no fee and no venue. Those are outputs, and a request carrying one is refused rather than quietly stripped.
Paying a supplier invoice is the other question: not "what will GHS 5,000 buy" but "what does 500 USDC cost". Send destination_amount instead of source_amount, and the cost is quoted back to you. Exactly one of the two, and sending both is refused.
curl -X POST http://api.railway.internal:8000/api/v1/conversions -H "Authorization: Bearer ${VAPS_API_KEY}" -H "Idempotency-Key: 2f9c1b7e-3d64-4f18-9a02-7b5d1c8e4a63" -H "Content-Type: application/json" -d '{
"customer_id": "cus_...",
"source_asset": "GHS",
"destination_amount": "500.00",
"delivery": "EXACT",
"destination_asset": "USDC",
"destination_network": "BASE",
"destination_address": "0x..."
}'Fixing a quantity is not fixing a price. The rate, the fees, the venue and the cost are still ours to derive, and a value you send for any of them is still refused.
Under EXACT the venue commits to the figure: the execution floor is the target itself, so a venue that cannot fill it does not execute at all and your funds are released. Fewer venues quote, because committing means carrying the risk of landing on it. Under TOLERATED the usual deviation applies and the receiver may get slightly less.
source_amount is null on the conversion until a venue has quoted, and nothing is reserved until you accept. Read it as "not yet known", never as zero.
- 1. POST a conversion. It enters compliance review. Nothing has moved.
- 2. Poll the conversion, or subscribe to webhooks, until it is awaiting acceptance.
- 3. GET the quotes. Each carries the venue's rate, the destination amount and every fee component.
- 4. POST the acceptance with the quote id. Expiry is checked at that moment, not before.
- 5. The venue executes and delivers to your address. GET the execution record for what actually filled.
The delivered amount is the venue's actual fill, which can differ from the quote within the agreed tolerance. A fill outside it fails the conversion and releases your funds.
Each delivery carries a signature over the raw request body and a timestamp. Verify the signature against the bytes you received, before parsing them, and reject a timestamp outside your tolerance. Every attempt, including the failures, is retained in a delivery log you can read.
A webhook is a notification, not a source of truth. Treat it as a signal to read the resource, and never as an instruction to move money on its own.
Errors carry a machine-readable code, a message, and the request id. Quote the request id when contacting support: it finds the exact request in our logs.
{
"error": {
"code": "quote_expired",
"message": "This quote has expired.",
"request_id": "req_01J..."
}
}One code deserves special handling. If a conversion reports EXECUTION_UNKNOWN, do not retry it. The outcome is genuinely undetermined and a retry could execute twice. Read the conversion and wait; it is resolved by a person, not by a timer.
Endpoints
Live from the platform
Authentication
Exchange credentials for a short-lived access token, or use an API key.
- POST/api/v1/auth/accept-invitationRedeem an invitation and set a password
- POST/api/v1/auth/loginSign in to the dashboard
- POST/api/v1/auth/logoutRevoke the current session
- GET/api/v1/auth/meDescribe the authenticated caller
- POST/api/v1/auth/refreshExchange a refresh token for a new access token
Institutions
Your organisation and the accounts within it.
- POST/api/v1/compliance/customers/{customer_id}/screenScreen a customer
Operational endpoints used by our own staff are deliberately not listed and are refused to institutional credentials.
How a conversion works, step by step
GET/api/v1/customersList customersPOST/api/v1/customersCreate a customerGET/api/v1/customers/{customer_id}Retrieve a customerPOST/api/v1/customers/{customer_id}/approveApprove verificationPOST/api/v1/customers/{customer_id}/beneficial-ownersAdd a beneficial ownerPOST/api/v1/customers/{customer_id}/rejectReject verificationPOST/api/v1/customers/{customer_id}/submitSubmit for verificationPOST/api/v1/customers/{customer_id}/suspendSuspend a customerPOST/api/v1/organizationsOnboard an organizationGET/api/v1/organizations/{organization_id}Retrieve an organizationPOST/api/v1/organizations/{organization_id}/activateActivate an organizationPOST/api/v1/organizations/{organization_id}/capabilitiesGrant or revoke capabilitiesGET/api/v1/organizations/{organization_id}/invitationsList invitationsPOST/api/v1/organizations/{organization_id}/invitationsInvite a colleaguePOST/api/v1/organizations/{organization_id}/invitations/{invitation_id}/revokeRevoke an invitationGET/api/v1/organizations/{organization_id}/membersList membersPOST/api/v1/organizations/{organization_id}/members/{membership_id}/reinstateReinstate a memberPOST/api/v1/organizations/{organization_id}/members/{membership_id}/removeRemove a memberPOST/api/v1/organizations/{organization_id}/members/{membership_id}/roleChange a member's rolePOST/api/v1/organizations/{organization_id}/members/{membership_id}/suspendSuspend a memberGET/api/v1/organizations/{organization_id}/rolesList assignable rolesGET/api/v1/organizations/{organization_id}/sandboxList the sandboxes issued to an institutionPOST/api/v1/organizations/{organization_id}/sandboxProvision a sandbox for an institutionPOST/api/v1/organizations/{organization_id}/sandbox/resetReplace a sandbox with a fresh onePOST/api/v1/organizations/{organization_id}/suspendSuspend an organizationConversions and quotes
Instruct a conversion, read the quotes, accept one, and follow what happened.
- GET/api/v1/conversionsList conversions
- POST/api/v1/conversionsCreate a conversion
- GET/api/v1/conversions/{conversion_id}Retrieve a conversion
- POST/api/v1/conversions/{conversion_id}/acceptAccept a quote
- POST/api/v1/conversions/{conversion_id}/cancelCancel a conversion
- GET/api/v1/conversions/{conversion_id}/execution-recordRetrieve the execution record
- GET/api/v1/conversions/{conversion_id}/quotesList quotes for a conversion
- GET/api/v1/conversions/{conversion_id}/rfqList every provider response
- GET/api/v1/conversions/{conversion_id}/timelineRetrieve the state transition history
Transactions and balances
What you hold, and everything that has moved.
- GET/api/v1/balancesList balances
- POST/api/v1/compliance/transactions/{transaction_id}/rejectReject a held transaction
- POST/api/v1/compliance/transactions/{transaction_id}/releaseRelease a held transaction
- GET/api/v1/compliance/transactions/{transaction_id}/travel-ruleRetrieve the Travel Rule record
- GET/api/v1/paymentsList payments
- POST/api/v1/paymentsCreate a payment
- GET/api/v1/payments/{payment_id}Retrieve a payment
- POST/api/v1/payments/{payment_id}/cancelCancel a payment
- GET/api/v1/payments/{payment_id}/timelineRetrieve the state transition history
- GET/api/v1/transactionsList transactions
Destinations
Approved addresses. A destination must be approved before it can receive anything.
- GET/api/v1/allowlistList allowlist entries
- POST/api/v1/allowlistRequest an allowlisted destination
- POST/api/v1/allowlist/{entry_id}/approveApprove an allowlisted destination
- POST/api/v1/allowlist/{entry_id}/removeRemove an allowlisted destination
- GET/api/v1/wallet-policiesList wallet policies
- POST/api/v1/wallet-policiesCreate a wallet policy
- GET/api/v1/walletsList wallets
- POST/api/v1/walletsCreate a wallet
- GET/api/v1/wallets/{wallet_id}Retrieve a wallet
- GET/api/v1/wallets/{wallet_id}/addressesList wallet addresses
- POST/api/v1/wallets/{wallet_id}/freezeFreeze a wallet
- POST/api/v1/wallets/{wallet_id}/screenScreen a wallet
- POST/api/v1/wallets/{wallet_id}/unfreezeUnfreeze a wallet
Reports and statements
Statements derived from ledger entries.
- GET/api/v1/reportsList report snapshots
- POST/api/v1/reportsGenerate a report snapshot
- GET/api/v1/reports/{report_id}Retrieve a report snapshot
- POST/api/v1/reports/segmentedGenerate a report larger than one snapshot, as parts under a manifest
- GET/api/v1/reports/statements/{customer_id}Generate a customer statement
API keys
Create, rotate and revoke keys. A secret is returned once.
- GET/api/v1/api-keysList API keys
- POST/api/v1/api-keysCreate an API key
- POST/api/v1/api-keys/{key_id}/revokeRevoke an API key
- POST/api/v1/api-keys/{key_id}/rotateRotate an API key
Webhooks
Endpoints, subscribable events, and the delivery log.
- GET/api/v1/webhooks/deliveriesList deliveries
- POST/api/v1/webhooks/deliveries/{delivery_id}/replayReplay a delivery
- GET/api/v1/webhooks/endpointsList endpoints
- POST/api/v1/webhooks/endpointsRegister an endpoint
- DELETE/api/v1/webhooks/endpoints/{endpoint_id}Disable an endpoint
- POST/api/v1/webhooks/endpoints/{endpoint_id}/rotate-secretRotate the signing secret
- GET/api/v1/webhooks/eventsList subscribable event types
Compliance
What your organisation may read about its own compliance position.
- GET/api/v1/compliance/alertsList compliance alerts
- GET/api/v1/compliance/alerts/{alert_id}Retrieve an alert
- POST/api/v1/compliance/alerts/{alert_id}/assignAssign an alert
- POST/api/v1/compliance/alerts/{alert_id}/closeClose an alert
- GET/api/v1/compliance/casesList cases
- POST/api/v1/compliance/casesOpen a case
- GET/api/v1/compliance/cases/{case_id}Retrieve a case
- POST/api/v1/compliance/cases/{case_id}/assignAssign a case
- POST/api/v1/compliance/cases/{case_id}/closeClose a case
- POST/api/v1/compliance/cases/{case_id}/escalateEscalate a case
- GET/api/v1/compliance/cases/{case_id}/notesList case notes
- POST/api/v1/compliance/cases/{case_id}/notesAdd a case note
- POST/api/v1/compliance/cases/{case_id}/str-filingRecord a suspicious transaction report filing
- POST/api/v1/compliance/customers/{customer_id}/screenScreen a customer
- GET/api/v1/compliance/risk-scoresList risk assessments
- GET/api/v1/compliance/rulesList compliance rules and how they have been performing
- GET/api/v1/compliance/screening-resultsList screening results
- POST/api/v1/compliance/transactions/{transaction_id}/rejectReject a held transaction
- POST/api/v1/compliance/transactions/{transaction_id}/releaseRelease a held transaction
- GET/api/v1/compliance/transactions/{transaction_id}/travel-ruleRetrieve the Travel Rule record