Error codes

The Northstake API returns errors in a consistent envelope:

{
  "success": false,
  "error": "<short human-readable message>",
  "code": "<machine-readable code, when applicable>"
}

The OpenAPI spec (components.schemas.ErrorResponse) is the canonical reference for the wire shape.

HTTP status codes

StatusMeaning
200 / 201Success
400Client error: malformed request, validation failure
401Unauthenticated
403Forbidden: wrong role, MFA required, or terms not accepted
404Resource not found
409Conflict: e.g. duplicate webhook URL, address already on allowlist
429Rate limited
500Server error

Authentication / authorization codes

CodeWhen
EXPIRED_TOKENThe access token has expired
TOS_NOT_ACCEPTEDThe account hasn't accepted current terms
MFA_REQUIREDThe operation needs step-up MFA
INSUFFICIENT_ROLEThe caller's account lacks the required scope (e.g. not Pro)

On-chain / contract-level codes

These come from parsing reverts on the partial-transaction submission path.

CodeWhen
VAULT_REPORT_STALEsettle-lido-fees called against a stale vault-hub report
ROLE_MISSINGThe signing wallet doesn't hold the required on-chain role
ALLOWLIST_EXISTSAdding an address already on the allowlist
ALLOWLIST_NOT_FOUNDRemoving an address not on the allowlist

The contract-error parser lives in the frontend at utils/contractErrors.ts and exposes helpers like isVaultReportStaleError.

Webhook / step-up specifics

CodeWhen
STEP_UP_REQUIREDOperation needs step-up MFA (matches x-mfaApiScope on the operation)
STEP_UP_EXPIREDStep-up token has expired

Field validation rules

These rules surface as 400 errors when violated.

Field typeRule
Ethereum address/^0x[a-fA-F0-9]{40}$/
Basis pointsInteger, 0 to 10000
minDelaySeconds, minWithdrawalDelayTime, confirmExpiryUI minimum 1 day (86,400 seconds); below-minimum values rejected by the SVM proxy even if the contract would accept them
Webhook urlMust begin with https://

Idempotency

The API doesn't expose first-class idempotency keys.

  • For obviously idempotent ops (e.g. adding the same address twice): the API returns 409 / ALLOWLIST_EXISTS. Treat as success.
  • For on-chain mutations: the transaction hash is the natural dedupe key. The API returns the same partial transaction for the same logical request if called twice within the same nonce window.
  • For webhook subscriptions: list before creating; check url + eventTypes for an existing match.