Authentication

Register a signer, manage API keys, and sign OBSDN API requests

OBSDN authentication has three pieces, each with a distinct job:

  1. Sender wallet - owns your funds, positions, and account. You sign with it for actions that move funds (withdraw, transfer) and to register a signer. Verified on-chain by the Obsdn contract.
  2. Signer wallet - a separate wallet authorized to sign orders on the sender's behalf, verified on-chain by the Obsdn contract at settlement. Holds no funds.
  3. API key pair - api_key, a public identifier sent with every request, plus api_secret, the confidential half used to sign requests. Verified off-chain by the OBSDN backend; has no on-chain effect.

You register a signer once. From then on, API keys issued for that signer carry the authority to act for the sender, so trading happens through API credentials rather than a raw private key on every request.

Register a Signer

The first step authorizes a signer wallet and issues the account's first API key pair in one call. Both the sender and the signer sign EIP-712 typed data to prove ownership and consent, then a single POST /auth/signers call posts both signatures together.

See the Register Signer reference for the request schema, Getting Started for an end-to-end curl example, and the Signing Guide for the EIP-712 payloads.

The response (under data) returns the first API key pair:

{
  "api_key": {
    "api_key": "a1b2c3d4e5f6a7b8a1b2c3d4e5f6a7b8",
    "api_secret": "a1b2c3d4e5f6a7b8a1b2c3d4e5f6a7b8a1b2c3d4e5f6a7b8a1b2c3d4e5f6a7b8",
    "nm": "My Trading Bot",
    "sndr": "0x1234...",
    "signer": "0xabcd...",
    "crt_ts": "1734000000000000000",
    "exp_ts": "1734604800000000000",
    "is_ro": false
  }
}

Timestamps (crt_ts, exp_ts) are Unix nanoseconds.

Save your api_secret immediately. It is returned only once, in the response that creates the key; list endpoints never return it.

API Key Types

A key's scope is fixed at creation by the is_ro (read-only) flag and cannot be changed afterward.

Typeis_roPlace / cancel ordersRead account data
Full-accessfalseYesYes
Read-onlytrueNoYes

Read-only keys cannot place or cancel orders, modify positions or leverage, transfer or withdraw funds, or create/delete keys or register signers. Use them anywhere you need data but never need to trade: dashboards, monitoring, analytics, and backtests. Reserve full-access keys for the services that actually submit orders. Scoping by purpose limits the blast radius if a key leaks.

Managing Keys

All three endpoints require the signed headers from Signing REST Requests.

Create

Once a signer is registered, mint more key pairs for it without re-registering: one per service or environment, or to stage a rotation. Each new key inherits the signer's authority.

POST /auth/api-keys
{ "nm": "Read-Only Dashboard", "is_ro": true }

Both fields are optional; is_ro defaults to false. The response matches POST /auth/signers: { "api_key": { ... } } with a fresh api_key and api_secret. Save the secret immediately. See Create an additional API key.

IP Whitelisting

Lock a key to specific client IPs at creation by passing ip_whitelist:

{ "nm": "Prod Bot", "ip_whitelist": ["203.0.113.10", "198.51.100.5"] }
  • Up to 10 IPv4 addresses per key.
  • Empty list (default) allows any IP.
  • Set once at creation; to change, delete the key and create a new one.
  • The server checks the IP reported by Cloudflare (CF-Connecting-IP), which cannot be spoofed by the caller.
  • Use GET /client to see the exact IP the server observes for your connection.

Requests from an IP not in the whitelist receive 403 client IP <ip> not allowed for this API key.

List

GET /auth/api-keys

Returns every key for the authenticated account. Each record includes its name (nm), signer, sender (sndr), creation and expiry timestamps (crt_ts, exp_ts), last write timestamp (wrt_ts), read-only flag (is_ro), and ip_whitelist. The api_secret is always empty here. See List API keys.

wrt_ts is the timestamp of the most recent write operation made with the key, letting you tell an idle key from an active one. It is recorded once a minute, so treat it as accurate to about a minute rather than as an exact time, and it is 0 for a key that has never placed an order. Only order placements count as writes: read requests never update it.

Delete

DELETE /auth/api-keys
{ "api_keys": ["a1b2c3d4e5f6a7b8a1b2c3d4e5f6a7b8"] }

Deletion is immediate and irrevocable; the keys can no longer authenticate any request. See Delete API keys.

Expiry and Rotation

Key typeInitial TTLAuto-extend triggerExtension
Full-access7 daysAny order placement7 days from the order
Read-only10 yearsNoneN/A

Each time a full-access key places an order (regular, conditional, or group), its expiry extends to 7 days from the order timestamp, so an actively trading key stays alive indefinitely with no manual renewal.

Auto-extend happens on order placement only. A full-access key that authenticates read requests but never places orders expires after 7 days. Track exp_ts for keys that trade infrequently.

There is no edit operation for a key's scope or secret. To rotate, create the replacement before deleting the old one:

  1. Create a new key pair for the signer.
  2. Deploy the new credentials and confirm they work.
  3. Delete the old key once nothing depends on it.

The old key stays valid through steps 1-2, so there is no downtime. Rotate immediately whenever a key may have been exposed: a leaked log, a shared screenshot, a departed teammate, or a compromised host.

Sub-Account Keys

Sub-accounts register their own signers and carry their own API keys, separate from the parent account's credentials. Each sub-account's trading authority is isolated: a key issued for one sub-account cannot act on another or on the parent.

Signing REST Requests

Every protected REST endpoint requires three headers:

HeaderValue
x-api-keyAPI key identifier (api_key)
x-api-timestampCurrent Unix time in seconds (string)
x-api-signaturebase64(HMAC-SHA256(api_secret, timestamp + method + path + body))

The prehash concatenates the timestamp, the uppercase HTTP method, the URL path (no scheme, host, or query string), and the raw request body. The API secret is the HMAC key and is never sent over the wire.

Requests with a timestamp more than 5 seconds off the server clock are silently treated as unauthenticated, and the endpoint returns 401 authentication required. Regenerate the signature for every call.

TS=$(date +%s)
METHOD=GET
URL_PATH=/portfolio
BODY=""
SIG=$(printf '%s%s%s%s' "$TS" "$METHOD" "$URL_PATH" "$BODY" \
  | openssl dgst -sha256 -hmac "$API_SECRET" -binary \
  | base64)

curl "https://api.obsdn.trade$URL_PATH" \
  -H "x-api-key: $API_KEY" \
  -H "x-api-timestamp: $TS" \
  -H "x-api-signature: $SIG"

For requests with a JSON body, pre-serialize the body once and reuse the same string in both the prehash and the request. Whitespace or key-order differences between the signed and sent bytes invalidate the signature.

Authenticating WebSocket

Private WebSocket channels (order, position, portfolio) require an auth message before subscription. The signature uses the same api_key / api_secret, but the prehash is ${api_key},${timestamp} and the timestamp window is ±60 seconds. See WebSocket Overview for the full recipe.

Security Best Practices

  • Guard the secret. Shown once at creation. Store it in a secrets manager, never in source control, client-side code, or logs.
  • Scope to least privilege. Use read-only keys wherever trading is not required.
  • One key per service. Separate credentials let you revoke a single compromised service without disrupting the others.
  • Use separate signer wallets. If a signer key leaks, only signing authority is exposed; funds stay under the sender wallet, which can revoke the signer.
  • Rotate on exposure. No scheduled rotation is needed, since active full-access keys auto-renew.
  • Delete unused keys. Every live key is attack surface.
  • Pin production keys to known IPs. Use ip_whitelist on keys that only run from fixed infrastructure; a leaked key is useless from any other address.

Error Codes

CodeHTTPMessageFix
E0001401authentication requiredMissing/invalid x-api-key, x-api-signature, or x-api-timestamp; or timestamp more than 5 s off the server clock
E0006403this operation requires a full API keyUse a full-access (non-read-only) key
E0005401invalid signatureFor orders/withdrawals, verify the EIP-712 payload (see Signing); for the request HMAC, verify the base64 digest and prehash format (timestamp + METHOD + path + body)
403client IP <ip> not allowed for this API keyRequest came from an IP not in the key's ip_whitelist. Use GET /client to check what IP the server sees, or delete and recreate the key with the correct list.

Rate-limit responses (429, codes E0501 / E0502) are retryable. Back off with exponential delay and jitter; see Error Handling.

Next Steps

On this page