01 Getting started
The OneSix API lets you accept payments with invoices and send withdrawals (transfers) programmatically. Requests are authorized with a project API key and an HMAC signature.
Integration takes three steps:
- Create a project in your dashboard and confirm domain ownership (DNS).
- Get the project API key: an
id+secretpair. - Sign requests with the key (HMAC-SHA256) and call the API methods.
Registration → Project (pending) → Domain verification → Moderation → Project (active) → API key → API requestsAn API key works only when both the key and the project are active. While the project is under moderation (pending) or rejected (rejected), key requests are refused.
Identifier formats
| Entity | Format |
|---|---|
id of a project, invoice, API key | Plain UUID (aabbccdd-...) |
user.id, logo_img_id, swap id in conversions, operation id | Encoded string (as elsewhere in the dashboard) |
02 Request signing (HMAC)
Every public API request is authorized with an API key and signed with HMAC-SHA256. Sessions (cookie / Authorization: Bearer) are not used for the API.
Keys
| Key | Where to get it | Purpose |
|---|---|---|
id (X-API-Key-ID) | Dashboard → project → API keys, field id | Key identifier sent in a header |
key (secret) | Same place, field key (hex, 64 chars) | Secret used to compute the signature |
A project has one active key (status: active). Key rotation is not available yet. The key secret is returned by GET /api/projects/{project_id}/api-keys/, keep it somewhere safe.
Request headers
| Header | Description |
|---|---|
X-API-Key-ID | Key UUID (field id, not the secret) |
X-Timestamp | Unix timestamp in seconds. Must be within ±15 seconds of server time |
X-Signature | HMAC-SHA256 in hex |
Content-Type | application/json for requests with a body |
The signature is required for every public API method (GET and POST).
Signing algorithm
Step 1. Build the data object:
{
"params": "url_encoded_query_string",
"body": {},
"path": "/api/invoice/"
}params: the query string as in the URL, without?(e.g.project_id=...&page=1). Empty string""when there is no query.body: the parsed JSON request body. Empty object{}for GET requests without a body.path: the request path with slashes, including the/apiprefix (e.g./api/invoice/).
Step 2. Serialize the object with sorted keys:
json.dumps(data, separators=(",", ":"), sort_keys=True)sort_keys=True is mandatory. You sign the object after the body is parsed as JSON, not the raw string.
Step 3. Compute the signature:
payload = str(timestamp) + json_string
signature = HMAC-SHA256(key=secret, message=payload).hexdigest()Step 4. Send X-API-Key-ID, X-Timestamp and X-Signature as headers.
Important
X-Timestampmust be within ±15 seconds of server time. If your clock drifts, sync withGET /api/-/time/.- A signature is single-use. Reusing the same
X-Signaturereturns401 Signature already used!. Every request needs a fresh timestamp and signature. - TOTP (2FA) is not required for API calls.
- If the key has an IP allowlist, requests are accepted only from those addresses.
Generating the signature
import hmac, hashlib, json, time
def sign(secret: str, timestamp: int, data: dict) -> str:
s = json.dumps(data, separators=(",", ":"), sort_keys=True)
payload = f"{timestamp}{s}"
return hmac.new(secret.encode(), payload.encode(), hashlib.sha256).hexdigest()
secret = "<key>"
api_key_id = "<uuid>"
path = "/api/invoice/"
params = ""
body = {
"name": "Order 1",
"target_amount": 100,
"is_termless": True,
"is_payer_comission": True,
"description": "",
"private_description": "",
}
ts = int(time.time())
data = {"params": params, "body": body, "path": path}
signature = sign(secret, ts, data)
headers = {
"X-API-Key-ID": api_key_id,
"X-Timestamp": str(ts),
"X-Signature": signature,
"Content-Type": "application/json",
}In JS make sure JSON.stringify produces the same output as Python json.dumps(..., separators=(",",":"), sort_keys=True): no spaces and keys sorted at every level. Sort keys of nested objects in body recursively if needed.
Request examples
# params = "" (no query)
# body = {} (no body)
# path = "/api/invoice/my/<invoice_id>/"
# data = {"body":{},"params":"","path":"/api/invoice/my/<invoice_id>/"}
curl "https://devonesite.site/api/invoice/my/<invoice_id>/" \
-H "X-API-Key-ID: <UUID>" \
-H "X-Timestamp: 1718659200" \
-H "X-Signature: <hex>"Server time
/api/-/time/No authServer time (unix)Returns the current server time (unix, a number). Use it to sync X-Timestamp if the client clock may drift by more than 15 seconds.
Which methods accept an API key
| Authorization | Methods |
|---|---|
| API key (HMAC) | POST /api/invoice/ · GET /api/invoice/my/{invoice_id}/ · POST /api/gw/transfer/ |
| No authorization | GET /api/invoice/{invoice_id}/ · GET /api/gw/currencies/ · GET /api/gw/currencies/rates/ · GET /api/gw/fees/ · GET /api/gw/fees/calculate/ · GET /api/-/time/ |
| Session only (dashboard) | Project management, verification, api-keys, operations, conversions, lists filtered by project_id |
Any method outside the "API key (HMAC)" list returns 401 API key not allowed for this endpoint! when called with a key.
Signing and authorization errors
| Code 401, detail | Reason |
|---|---|
Invalid API key! | Wrong X-API-Key-ID |
API key deactivated! | The key is archived or the project is not active |
Invalid signature! | Signature mismatch (check serialization, path, timestamp) |
Signature already used! | A one-time signature was reused |
API key not allowed for this endpoint! | The method does not accept API key authorization |
Token not provided! | No authorization provided |
03 Invoices
An invoice is a request to pay a fixed amount. Once created, it returns a payment address and a payment page. The amount is set in fiat (target_amount), payment arrives in USDT at the current rate (rate_pair: USDT_RUB, rate is in the response).
/api/invoice/API keyCreate an invoice/api/invoice/my/{invoice_id}/API keyYour invoice (with private_description)/api/invoice/{invoice_id}/No authPublic invoice pagePOST /api/invoice/
Request body (InvoiceCreateDTO)
| Field | Type | Req. | Description |
|---|---|---|---|
name | string | ✔ | Name. Min 3 chars, Latin/Cyrillic |
target_amount | number | ✔ | Amount to pay. Minimum 0.1 |
is_termless | bool | No deadline. Defaults to false | |
deadline | datetime (ISO 8601) | Invoice deadline. Required when is_termless=false; must be null or omitted when is_termless=true | |
is_payer_comission | bool | The payer covers the fee. Defaults to true | |
description | string | Public description | |
private_description | string | Private description (visible only to you) | |
img | string | null | Invoice image | |
project_id | uuid | null | Not used on this method. With a key, project_id is taken from the key’s project automatically |
2FA is not required with an API key.
Response (MyInvoiceDTO)
| Field | Type | Description |
|---|---|---|
id | uuid | Invoice ID |
address | string | Payment address |
name | string | Name |
img | string | null | Image |
description | string | Public description |
private_description | string | Private description |
status | enum | opened / expired / executed / closed |
target_amount | number | Amount to pay |
current_amount | number | Amount paid so far |
comission | number | Fee |
is_termless | bool | No deadline |
is_payer_comission | bool | The payer covers the fee |
deadline | datetime | null | Deadline |
rate_pair | enum | Rate pair (USDT_RUB, ALTYN_USDT_RUB) |
rate | number | Rate at creation time |
created_at | datetime | Created at |
closed_at | datetime | null | Closed at |
project_id | uuid | null | Invoice project |
GET /api/invoice/my/{invoice_id}/
Returns your invoice (MyInvoiceDTO, with private_description). With a key an extra filter applies: only invoices of the key’s project are available.
GET /api/invoice/{invoice_id}/
Public invoice page, no authorization. Returns PublicInvoiceDTO: same as MyInvoiceDTO but without private_description and project_id. Used for the payment page.
Invoice statuses
| status | Description |
|---|---|
opened | Open, awaiting payment |
executed | Paid (amount reached) |
expired | Deadline passed without full payment |
closed | Closed |
Status tracking. The current API version has no outgoing status webhooks. Poll GET /api/invoice/my/{invoice_id}/ (status, current_amount) for the current status.
04 Withdrawals (Transfer)
Create a withdrawal to an external blockchain address.
/api/gw/transfer/API keyCreate a withdrawalPOST /api/gw/transfer/
Request body (WithdrawalCreateDTO)
| Field | Type | Req. | Description |
|---|---|---|---|
symbol_id | string | ✔ | Symbol ID (currency on a network), e.g. USDTTRC20. List: GET /api/gw/currencies/ |
to_address | string | ✔ | Recipient address |
amount | number | ✔ | Amount. Greater than 0 |
2FA is not required with an API key.
Response: a JSON object of the withdrawal transaction (type, status, operation ID).
As with invoices, there are no withdrawal webhooks. Track the status by polling project operations (GET /api/projects/{project_id}/operations/) or in the dashboard.
05 Reference data
Public methods without authorization that are useful for integration.
/api/gw/currencies/No authCurrencies and symbols/api/gw/currencies/rates/No authCurrency pair rates/api/gw/fees/No authFees and limits per symbol/api/gw/fees/calculate/No authFee calculation for an amount/api/-/time/No authServer time (unix)GET /api/gw/currencies/
Supported currencies. Use the symbol field as symbol_id when creating a withdrawal.
Query: search - filter. The response is paginated (data[], pages_count, current_page, total).
Each item (CurrencyDTO):
| Field | Description |
|---|---|
symbol | Symbol ID for the API (symbol_id) |
short_name | Short currency name |
name | Full name |
blockchain | Network (id, name, explorer links) |
contract_address | Token contract address, if any |
precision | Precision |
is_active | Whether the symbol is available |
logo_url | Logo |
min_deposit_confirms / min_withdrawal_confirms | Confirmations for deposit / withdrawal |
GET /api/gw/fees/
Fees and minimum amounts per symbol (GwFeeDTO[]):
| Field | Description |
|---|---|
symbol_id | Symbol ID |
min_deposit_amount | Minimum deposit |
min_withdrawal_amount | Minimum withdrawal |
deposit_fee_amount / deposit_fee_percentage | Deposit fee (fixed / percent) |
withdrawal_fee_amount / withdrawal_fee_percentage | Withdrawal fee (fixed / percent) |
swap_fee_amount / swap_fee_percentage | Swap fee |
swap_from_min_amount / swap_to_min_amount | Swap minimums |
GET /api/gw/fees/calculate/
Fee calculation for a specific amount.
Query (all required): amount, operation_type (deposit / withdrawal / swap), symbol_id.
Response (GwServiceFeeCalculationDTO): symbol_id, operation_type, amount, fee_amount, net_amount, gross_amount.
GET /api/gw/currencies/rates/
Currency pair rates (CurrencyRateDTO[]):
| Field | Description |
|---|---|
from_symbol_id | Source currency symbol/code |
to_symbol_name | Quote currency code |
value | Rate |
created_at | Updated at |
06 Project management
Creating and configuring projects, domain verification and key issuance are done from the dashboard with a session (cookie or Authorization: Bearer), without an API key. You only need this section to automate the dashboard; for a regular integration just follow the steps in the UI.
Projects
/api/projects/SessionYour projects. Without status - all except disabled. Filter ?status=pending|active|rejected|disabled/api/projects/SessionCreate a project (status pending). The key is created right away but is not in the response: get it via api-keys/api/projects/{project_id}/SessionProject details/api/projects/{project_id}/SessionUpdate a project (all fields optional). disabled projects cannot be edited/api/projects/{project_id}/SessionSoft delete → status: disabled. Response {"status": true}POST /api/projects/ (ProjectCreateDTO)
| Field | Type | Req. | Description |
|---|---|---|---|
name | string | ✔ | 3-128 chars, unique per user |
category | enum | ✔ | goods / services / digital / other |
site_url | string (uri) | ✔ | http(s), a public host (not localhost or a private IP) |
contact_telegram | string | ✔ | 1-64 chars |
description | string | null | Description |
PATCH /api/projects/{project_id}/ (ProjectUpdateDTO): category, description, logo_img_id (encoded id of your image), brand_color (# + 6 hex). The logo must belong to you.
Project details (ProjectDTO)
{
"id": "uuid",
"public_id": "aabbccddeeff",
"name": "Shop",
"status": "pending",
"category": "goods",
"site_url": "https://shop.example.com",
"contact_telegram": "@shop",
"description": null,
"logo_img_id": null,
"brand_color": null,
"is_site_verified": false,
"verify_method": null,
"verify_token": null,
"verified_at": null,
"rejected_reason": null,
"created_at": "...",
"updated_at": "..."
}status: pending → active (after verification and moderator approval) / rejected / disabled.
Domain verification (DNS)
/api/projects/{project_id}/verification/SessionStart verification. Only when status=pending and is_site_verified=false/api/projects/{project_id}/verification/check/SessionCheck the TXT record (no body)POST /verification/: body {"method": "dns"}. Response:
{
"method": "dns",
"verify_token": "<hex>",
"instruction": "Add a TXT record for shop.example.com with value j1ulwfmkyj0-site-verification=<token>"
}Tell the user to add a TXT record on the site_url host with the value j1ulwfmkyj0-site-verification=<verify_token>.
POST /verification/check/: if the TXT record is found, is_site_verified=true and verified_at is set, the status stays pending (awaiting moderation). If already verified, returns 200 without an error. If there is no TXT record, returns 400 Site verification failed.
Project API keys
/api/projects/{project_id}/api-keys/SessionProject keys with the secret[
{
"id": "key uuid, this is X-API-Key-ID",
"key": "hex secret, 64 chars",
"status": "active",
"created_at": "...",
"archived_at": null
}
]status: active / archived. A project has one active key. Rotation is not available yet.
Project operations and conversions
/api/projects/{project_id}/operations/?page=1&per_page=10SessionProject transactions/api/projects/{project_id}/conversions/?page=1&per_page=10SessionProject swapsPagination: page starts at 1, per_page defaults to 10. The response has current_page = page - 1 (zero-based), pages_count, total, data[].
Operation (ProjectAdminTransactionDTO): id, type, subject (usdt/rub), symbol_id, is_incoming, amount, state (created/pending/dirty_lock/executed/cancelled), tx_hash, description, executed_at, created_at, project_id.
Conversion (SwapOrderResponseDTO): id, from_symbol_id, to_symbol_id, from_amount, to_amount, rate, status (created/processing/executed/cancelled/failed), created_at, project_id, user.
Dashboard lists filtered by project
GET /api/invoice/?project_id=<uuid>
GET /api/transaction/?project_id=<uuid>
GET /api/gw/swap_order/?project_id=<uuid>07 Error codes
Projects and verification (400)
| detail | Reason |
|---|---|
Project with this name already exists | The project name is taken |
site_url must be an http(s) URL | Invalid URL format |
site host must be a public address | The host is localhost / a private IP |
brand_color must be a HEX color like #1A2B3C | Invalid color |
Image not found | Logo image not found |
Project is disabled | The project is disabled, editing is not allowed |
Project is not pending verification | Verification cannot start in the current status |
Site is already verified | The domain is already verified |
Verification is not started | Check called before verification started |
Site verification failed | TXT record not found |
Project is not pending | Only available for pending |
Site is not verified | The domain is not verified |
Authorization and signature (401)
| detail | Reason |
|---|---|
Invalid API key! | Wrong X-API-Key-ID |
API key deactivated! | The key is archived or the project is not active |
Invalid signature! | Signature mismatch (check serialization, path, timestamp) |
Signature already used! | A one-time signature was reused |
API key not allowed for this endpoint! | The method does not accept API key authorization |
Token not provided! | No authorization provided |
Not found (404)
| detail | Reason |
|---|---|
Project not found | Project not found / not accessible |
Invoice not found | Invoice not found |