Pacifica authenticates every state-changing request using Ed25519 cryptographic signatures. GET requests and WebSocket subscriptions do not require signing — only POST requests that modify state (placing orders, managing positions, withdrawals, etc.) must carry a valid signature. This approach ties each request directly to the wallet that owns the account, with no API secret stored on a server. The official Pacifica Python SDK handles all signing steps automatically and is the recommended path for most integrations. The sections below explain the underlying mechanics for developers who need to implement signing in another language or want to understand exactly what is being signed.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/pacifica-fi/docs-migrate/llms.txt
Use this file to discover all available pages before exploring further.
Signing at a Glance
How Signing Works
Assemble the signature header and payload
Every signed request starts with a signature header that identifies the operation type, carries the current timestamp in milliseconds, and optionally specifies an expiry window:Combine the header with the operation payload under a
"data" key:Recursively sort all JSON keys
Deterministic serialisation requires that every key at every nesting level is sorted alphabetically before the message is serialised. This guarantees that two logically identical payloads always produce an identical byte sequence — and therefore an identical signature.After sorting, the example payload becomes:
Serialise to compact JSON
Produce a compact JSON string with no whitespace and standardised separators:The resulting string looks like:
Sign with Ed25519 and encode as Base58
Encode the compact JSON string as UTF-8 bytes, sign with the Ed25519 private key, and Base58-encode the resulting signature bytes for transmission:
Operation Types
Every signed request includes a"type" field in the signature header that identifies the operation being performed. The server uses this to reconstruct and verify the signed message.
| Operation Type | API Endpoint |
|---|---|
"create_order" | POST /api/v1/orders/create |
"create_stop_order" | POST /api/v1/orders/stop/create |
"cancel_order" | POST /api/v1/orders/cancel |
"cancel_all_orders" | POST /api/v1/orders/cancel_all |
"cancel_stop_order" | POST /api/v1/orders/stop/cancel |
"update_leverage" | POST /api/v1/account/leverage |
"update_margin_mode" | POST /api/v1/account/margin |
"set_position_tpsl" | POST /api/v1/positions/tpsl |
"withdraw" | POST /api/v1/account/withdraw |
"subaccount_initiate" | POST /api/v1/account/subaccount/create |
"subaccount_confirm" | POST /api/v1/account/subaccount/create |
"create_market_order" | POST /api/v1/orders/create_market |
"subaccount_transfer" | POST /api/v1/account/subaccount/transfer |
"bind_agent_wallet" | POST /api/v1/agent/bind |
"create_api_key" | POST /api/v1/account/api_keys/create |
"revoke_api_key" | POST /api/v1/account/api_keys/revoke |
"list_api_keys" | POST /api/v1/account/api_keys |
"create_lake" | POST /api/v1/lake/create |
"claim_lake_referral" | POST /api/v1/lake/claim_referral_code |
"deposit_to_lake" | POST /api/v1/lake/deposit |
"claim_lake_manager" | POST /api/v1/lake/claim_manager |
"withdraw_from_lake" | POST /api/v1/lake/withdraw |
"update_lake_deposit_cap" | POST /api/v1/lake/update_deposit_cap |
"add_lake_whitelist" | POST /api/v1/lake/add_whitelist |
"remove_lake_whitelist" | POST /api/v1/lake/remove_whitelist |
"add_lake_blacklist" | POST /api/v1/lake/add_blacklist |
"remove_lake_blacklist" | POST /api/v1/lake/remove_blacklist |
"add_lake_max_leverage" | POST /api/v1/lake/add_max_leverage |
"remove_lake_max_leverage" | POST /api/v1/lake/remove_max_leverage |
The batch order endpoint does not have its own operation type. Each individual action inside the batch is signed independently using its own operation type (e.g.
"create_order", "cancel_order").Request Fields Reference
The public key of the wallet that owns the account — always the main account’s public key, even when using an agent key.
Base58-encoded Ed25519 signature over the deterministic compact JSON message. When using a hardware wallet, pass an object with
"type": "hardware" and "value": "<base58-sig>" instead of a plain string.Current Unix timestamp in milliseconds. The server rejects requests where
timestamp + expiry_window < server_time.How long (in milliseconds) the signed message remains valid after
timestamp. Defaults to 30000 (30 seconds) if omitted. Use a smaller value (e.g. 5000) to reduce replay risk.Public key of the API agent key being used to sign this request. Set to
null when signing with the main account key directly.API Agent Keys
API agent keys (also called “agent wallets”) allow a sub-key to sign requests on behalf of the main account. This mirrors the API key model used by most centralised exchanges and means the main wallet’s private key never needs to touch the trading program.Generate an agent key
Agent keys can be created through the Pacifica web app or programmatically via the Python SDK:Each account supports up to 5 active API config keys at one time.
Hardware Wallet Support
Pacifica supports hardware wallet signing via Ed25519 off-chain message signing. After constructing the compact JSON message bytes (step 4 above), prepend the Solana off-chain message header (\xffsolana offchain followed by version and length information) before signing.
In the request body, pass the signature as an object rather than a plain string:
Signing Error Reference
When a POST request carries an invalid signature, the server returnsHTTP 400 with one of the following messages:
Invalid signature
Invalid signature
The signature field could not be parsed or validated:
- Not valid Base58 encoding
- Decoded bytes do not form a valid Ed25519 signature
- Malformed signature data
Invalid message
Invalid message
The signed message itself is structurally invalid or has expired:
timestamp + expiry_window < current_server_time(message expired)- Payload cannot be serialised to JSON
- Message structure is malformed
Invalid public key
Invalid public key
The
account field does not represent a valid Ed25519 public key:- Address does not decode to a valid 32-byte public key
- Public key bytes are malformed
Verification failed
Verification failed
The signature is structurally valid but does not match the reconstructed message:
- Signature was produced by a different private key
- Wrong operation type or payload was used during signing
- Message content was modified between signing and submission
Signing errors can be ambiguous to debug. Following the step-by-step implementation above precisely — or using the Python SDK — avoids the vast majority of signing issues.