Authentication
API keys, scopes, IP allowlists, rate limits and the response envelope.
API keys
Every request to https://deriapi.site/api/v2 needs a secret API key. Create keys under API keys in the dashboard. A key looks like this:
dpk_live_1a2b3c4d5e6f_<48 hex characters>The part up to the second underscore (dpk_live_1a2b3c4d5e6f) is the public prefix. The dashboard shows it so you can tell keys apart. The full secret is shown once, when you create the key. Deripay stores only a hash of it, so a lost secret cannot be recovered. Create a new key and revoke the old one.
Send the key in the Authorization header:
GET /api/v2/ping HTTP/1.1
Host: deriapi.site
Authorization: Bearer dpk_live_1a2b3c4d5e6f_…The X-API-Key: dpk_live_… header is also accepted. Prefer Authorization: Bearer.
All keys are live keys. There is no test mode.
Keep the key on your server
The key can start deposits and withdrawals for every customer you have connected. Treat it like a database password:
- Call Deripay only from your backend. Your website or mobile app calls your backend.
- Never ship the key in browser JavaScript, a mobile app binary, a public repository, logs or screenshots.
- Store it in a secrets manager or a server environment variable such as
DERIPAY_API_KEY. - If a key leaks, revoke it in the dashboard at once and create a new one.
Scopes
Each key has one or more scopes. A request with a key that lacks the scope fails with 403 AUTH_INSUFFICIENT_SCOPE.
| Scope | Allows |
|---|---|
connections |
POST /connections, PATCH /connections/{id}/default-phone |
deposits |
POST /deposits, POST /deposits/{id}/otp |
withdrawals |
POST /withdrawals, POST /withdrawals/{id}/confirm, POST /withdrawals/{id}/resend |
read |
GET /connections, GET /connections/{id}, GET /connections/{id}/balance, GET /transactions, GET /transactions/{id}, GET /transactions/{id}/events |
GET /ping, GET /markets and GET /rates work with any valid key.
Give each server only the scopes it needs. For example, a reporting job needs only read.
IP allowlist
A key can be limited to a list of IP addresses (IPv4 or IPv6, up to 20). Set it when you create the key or later from the key's row in the dashboard. An empty list allows any IP.
A request from an address not on the list fails with 403 AUTH_IP_NOT_ALLOWED. The message names the IP Deripay saw, which helps when your server sits behind a NAT or proxy.
Rate limits
Limits apply per key, per route, over a 60 second window. When you go over, the response is 429 RATE_LIMITED with a Retry-After header in seconds.
| Endpoint | Requests per minute |
|---|---|
POST /connections |
30 |
POST /deposits |
30 |
POST /deposits/{id}/otp |
30 |
POST /withdrawals |
20 |
POST /withdrawals/{id}/confirm |
20 |
POST /withdrawals/{id}/resend |
10 |
GET /transactions/{id}/events |
60 |
| Every other endpoint | 120 |
Routes with an id in the path are counted per path, so polling two different transactions uses two separate budgets.
Some payment network checks also return 429, for example WITHDRAWAL_COOLDOWN. Those also carry Retry-After. See Errors.
Response envelope
Every JSON response has the same shape.
Success:
{
"success": true,
"data": { }
}Error:
{
"success": false,
"error": {
"code": "INVALID_REQUEST",
"message": "Some fields are missing or invalid.",
"details": [{ "field": "amount", "message": "Whole local currency units only" }],
"request_id": "req_9f1c2b3a4d5e6f70"
}
}- Branch on
error.code. It is stable and machine readable. - Show or log
error.messagefor people. Do not parse it. error.detailsis present only on some errors. ForINVALID_REQUESTit lists each failing field.error.request_ididentifies the request. Quote it when you contact support.
The full list of codes is in Errors.
Response headers
| Header | Value |
|---|---|
X-Request-Id |
The request id, for example req_9f1c2b3a4d5e6f70. Sent on every response, including successes. Log it. |
Deripay-Version |
v2 on JSON responses. |
Cache-Control |
no-store. |
Retry-After |
Seconds to wait. Sent with 429 responses and some others that ask you to wait. |
Idempotent-Replayed |
true when a deposit or withdrawal request was a replay of an earlier one. See Idempotency. |
Requests
- Send JSON bodies with
Content-Type: application/json. A body that is not valid JSON fails with400 INVALID_REQUEST. - Field names are
snake_case. - Ids are prefixed:
con_for connections andtxn_for transactions. - Timestamps are ISO 8601 in UTC.