Authentication
Sign every request with the current API key flow and EXN-* headers.
How signed requests work
The client signs request metadata with an Ed25519 private key. The server validates the API key, timestamp, signed payload fields, path and query, body hash, signature, and access scope.
Signed request headers
| Header | Required | Purpose |
|---|---|---|
EXN-API-KEY | Yes | Public API key identifier. Must match api_key inside EXN-DATA. |
EXN-IDEMPOTENCY-KEY | Yes | Empty for signed GET requests; non-empty for mutating requests. |
EXN-TIMESTAMP | Yes | Unix timestamp in milliseconds. Must match timestamp inside EXN-DATA. |
EXN-SIGN-VERSION | Yes | Signature protocol version. Current value: 1. |
EXN-DATA | Yes | Base64url-encoded signed payload without = padding. |
EXN-SIGN | Yes | Ed25519 signature of the decoded EXN-DATA payload bytes, base64url-encoded without = padding. |
Decoded EXN-DATA payload
| Field | Description |
|---|---|
api_key | Same value as EXN-API-KEY. |
idempotency_key | Same value as EXN-IDEMPOTENCY-KEY; empty string for signed GET requests. |
timestamp | Same value as EXN-TIMESTAMP, in Unix milliseconds. |
sign_version | Same value as EXN-SIGN-VERSION. |
method | Uppercase HTTP method, for example GET or POST. |
path | Request path with query string exactly as sent by the client. |
body_hash | SHA-256 hash of the exact request body, base64url-encoded without padding. |
Signing rules
EXN-SIGNsigns the decodedEXN-DATApayload bytes, not the base64url string.EXN-DATAandEXN-SIGNmust use base64url without=padding.- For requests without a body,
body_hashis the SHA-256 hash of the empty byte sequence:47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU. - Query strings must be signed exactly as transmitted. Do not reorder, normalize, or re-encode query parameters after signing.
- Duplicate query parameters are rejected.
- For signed GET requests,
EXN-IDEMPOTENCY-KEYmust be present with an empty value. - For mutating requests,
EXN-IDEMPOTENCY-KEYmust be non-empty. - Generate a fresh timestamp and signature for every request.
Example
curl -X GET "https://api.exness.com/v1/configuration/accounts/123456/account" \
-H "EXN-API-KEY: exnsk_your_public_api_key" \
-H "EXN-IDEMPOTENCY-KEY:" \
-H "EXN-TIMESTAMP: 1773656070123" \
-H "EXN-SIGN-VERSION: 1" \
-H "EXN-DATA: <base64url_encoded_payload>" \
-H "EXN-SIGN: <base64url_encoded_signature>"
{
"api_key": "exnsk_your_public_api_key",
"idempotency_key": "",
"timestamp": 1773656070123,
"sign_version": 1,
"method": "GET",
"path": "/v1/configuration/accounts/123456/account",
"body_hash": "47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU"
}
Signature helper validation
If you publish a fully runnable Python signing helper, validate the private-key format and serialization rules with API/Auth owners first.
Common auth errors
| Code | error_message | Client action |
|---|---|---|
| 1000 | AUTH_INVALID_API_KEY | Check EXN-API-KEY, key expiry, and api_key inside EXN-DATA. |
| 1001 | AUTH_INVALID_SIGNATURE | Check EXN-DATA, EXN-SIGN, private key, payload serialization, body hash, timestamp, and base64url encoding. |
| 1002 | AUTH_PERMISSION_DENIED | Check account access, source IP restrictions, and API key permissions. |