TESTNET
Markets
Trade
Lending Strategies
More
User Docs Developer Docs API Reference Playground Help
Developer Docs Overview
Withdrawals and Transfers Security
Multi-Factor Authentication
Guard Signer
Ed25519 API Keys
ConnectRPC Guide
API Key Replay Policy
REST Guide
  1. Authentication & Security
  2. /
  3. Ed25519 API Keys

Ed25519 API Keys

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.

Private keys stay client-side

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_ed25519

  • label

  • 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:

  1. Builds a canonical string from the request data.

  2. Signs it with the private key.

  3. Sends the API key identifier, timestamp, signature, and optional nonce.

Polyester:

  1. Loads the public key for X-API-KEY-ID.

  2. Rebuilds the same canonical string.

  3. Verifies the signature.

  4. 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>

Clock sync matters

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:

  • timestamp is the exact value in X-API-TIMESTAMP

  • method is the uppercase HTTP method, such as POST

  • path is the URL path only, such as /v1/orders

  • canonical_query contains query parameters sorted by key and value, or an empty string if there is no query

  • body_sha256_hex is the lowercase SHA-256 hash of the raw request body bytes

Sign exactly what you send

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-USDT

  • body bytes: {"side":"BUY","qty":"0.1"}

Canonical query:

recvWindow=5000&symbol=BTC-USDT

Canonical 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-NONCE is 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:

  • UNAUTHENTICATED

  • SIGNATURE_INVALID

  • TIMESTAMP_SKEW

  • MISSING_HEADERS

  • KEY_DISABLED

  • KEY_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

Previous

Guard Signer

Next

ConnectRPC Guide