Skip to main content

HTTP Status Codes

A 400 means nothing was submitted on-chain, so there is no transaction to commit or recover.

Commit Status

A rejected, failed, or expired status still means the commit succeeded - the balance is no longer in a pending state, and no further action is required.
The two commit endpoints are not interchangeable. A tx_id from /borrow/initiate or /borrow/settle is committed at /borrow/commit; everything else at /commit.
If /borrow/commit returns 400 Please commit the action again after some time, a two-transaction Jito bundle is still inside its grace window. This is not fatal - wait and commit again with the same tx_id.

Commit Idempotency

Committing the same tx_id twice returns already_processed: true and the original status, so a commit is always safe to retry.

Recovering Pending Commits

If a transaction is submitted but never committed - after a crash, timeout, or network issue - it stays pending. Trades and lending actions are tracked separately, so each has its own pair: Run this when your service starts, and whenever a user loads their account, so nothing is left pending.
already initiated with this signature means an action for those exact values already exists. Do not re-sign them - call /borrow/pending to find it, and commit that tx_id.
Once nothing is left pending, read /account/balances and /borrow/positions for what the user now holds.

Handling 401 Errors

The most common cause of a 401 on signed endpoints is a malformed signature message. Check:
  1. Message format - the ToS header line and Details: line must match exactly, including the newlines. See Signing Requests for the line each endpoint expects.
  2. Timestamp - must be Unix time in milliseconds, not seconds. Stale timestamps are rejected: lending writes accept a signature for 10 minutes, read-scoped requests for 24 hours.
  3. Signing key - must be the Ed25519 secret key of user_address, not any other key.
  4. Encoding - the signature must be base64-encoded, not base58 or hex.
  5. Field name - the account reads take signature; every other signed endpoint, the lending reads included, takes user_signature.
The most common mistake is passing seconds instead of milliseconds for the timestamp. Use Date.now() in TypeScript or timestamp_millis() in Rust - not Date.now() / 1000 or timestamp().

Health Check

Use GET /health to verify API availability or plug it into your monitoring stack for uptime alerts. It requires no signed request - just your API key.

Experiencing an issue not covered here? Join us on Discord - our engineers are available to help.