Authentication with Ed25519 API Keys
Polyester API keys use Ed25519 signatures, a modern public-key digital signature scheme.
This means:
You generate a keypair locally:
private key: stays on your machine and is never sent to Polyester
public key: sent once to Polyester when you create the API key
Every authenticated request is signed with your private key
Polyester verifies the signature using your public key
This design removes the need for shared secrets and significantly reduces the blast radius of server-side leaks.
Polyester only needs your public key to verify requests. Never send your private key to Polyester or any third party.
Why Ed25519 instead of HMAC?
Signing model | How it works | Tradeoff |
|---|---|---|
HMAC | Client and server share the same secret. | Simple, but the server must store signing material that can produce valid requests. |
Ed25519 | Client signs with a private key; Polyester verifies with a public key. | Safer key custody and easier rotation. |
With Ed25519:
the private key never leaves the client
Polyester stores only a public key
public keys can verify requests but cannot sign them
rotation is simple: create a new keypair, register the new public key, then revoke the old key
signatures are fast enough for high-throughput trading workloads
High-level flow
1) Create an API key
On creation, your client generates an Ed25519 keypair locally, then calls the API-key creation endpoint and sends:
public_key_ed25519label
scopes or policy settings
optional subaccount binding
Polyester stores the public key and metadata such as status, account binding, and policy.
2) Sign private requests
For each authenticated request, your client:
Builds a canonical string from the request data.
Signs it with the private key.
Sends the API key identifier, timestamp, signature, and optional nonce.
Polyester:
Loads the public key for
X-API-KEY-ID.Rebuilds the same canonical string.
Verifies the signature.
Enforces account, subaccount, API-key, and product policy.
Required headers
Every signed request must include:
X-API-KEY-ID: <key_id>X-API-TIMESTAMP: <milliseconds since Unix epoch>X-API-SIGNATURE: <ed25519_signature encoded as hex or base64>
Optional:
X-API-NONCE: <unique value>
Signature validation depends on timestamp freshness. Keep client clocks synchronized to avoid authentication failures caused by clock drift.
Canonical string
To prevent ambiguity, the signature must be computed over a canonical representation of the request.
Canonical string format:
<timestamp>
<method>
<path>
<canonical_query>
<body_sha256_hex>Where:
timestampis the exact value inX-API-TIMESTAMPmethodis the uppercase HTTP method, such asPOSTpathis the URL path only, such as/v1/orderscanonical_querycontains query parameters sorted by key and value, or an empty string if there is no querybody_sha256_hexis the lowercase SHA-256 hash of the raw request body bytes
Any mismatch in path, query ordering, JSON formatting, whitespace, or raw body bytes will invalidate the signature.
Example canonicalization
Request:
POST /v1/orders?recvWindow=5000&symbol=BTC-USDTbody bytes:
{"side":"BUY","qty":"0.1"}
Canonical query:
recvWindow=5000&symbol=BTC-USDTCanonical string:
1700000000123
POST
/v1/orders
recvWindow=5000&symbol=BTC-USDT
<sha256_hex_of_raw_body>Then sign that string with Ed25519 and send the signature in X-API-SIGNATURE.
Verification rules
Polyester verifies:
Check | Description |
|---|---|
API key status | The key exists, is active, and has not expired. |
Timestamp freshness | Requests outside the allowed clock-skew window are rejected. |
Signature validity | Polyester rebuilds the canonical string and verifies the Ed25519 signature. |
Authorization | Scopes, policy, account binding, and subaccount binding are enforced after authentication. |
Nonce and replay behavior
X-API-NONCEis accepted for API-key requests and can be supplied by clients.Replay protection is currently enforced on write and money-moving API paths.
Replay protection is currently not enforced on read-only API paths.
Replay detection is based on in-process deduplication of signed request material within the freshness window.
For client behavior, always send a nonce on private requests. This keeps your client forward-compatible if stricter replay enforcement is expanded.
See API Key Replay Policy for replay policy details.
Common auth error codes
You may receive one of the following auth-related error codes:
UNAUTHENTICATEDSIGNATURE_INVALIDTIMESTAMP_SKEWMISSING_HEADERSKEY_DISABLEDKEY_EXPIRED
Security best practices
Never share your private key.
Store private keys in a secure keystore, password manager, hardware security module, or dedicated signing service.
Rotate keys regularly.
Keep clocks accurate with NTP or equivalent time synchronization.
Always use TLS. Signatures authenticate requests, but they do not replace encryption.
Use scoped API keys and subaccount bindings where possible.
Revoke unused keys immediately.
Summary
With Ed25519 API keys:
the private key stays client-side
Polyester only stores a public key
every private request is authenticated through deterministic request signing
policy and account authorization still apply after signature verification