Errors

Lipafy errors are JSON responses with three fields:

1{
2 "error": "<stable_code>",
3 "message": "Human readable description",
4 "details": { "..." }
5}

The error string is the API surface — it’s stable across versions and safe to switch on. The message is for humans and may evolve. details is optional context.

Auth

CodeStatusWhen
unauthenticated401No credential, or credential failed validation
forbidden403Authenticated but lacks permission for this action
invalid_api_key401API key not found
api_key_revoked401Was valid, now revoked
api_key_expired401Past expires_at
account_suspended403Account is status='suspended'
invalid_token401Session token / magic-link token / OAuth refresh token didn’t validate
expired_token401Valid shape but past TTL

OAuth

CodeStatusWhen
oauth_invalid_client401client_id not registered
oauth_invalid_grant400code / refresh_token unknown, used, or expired
oauth_invalid_redirect_uri400redirect_uri doesn’t exactly match a registered one
oauth_pkce_required400code_challenge_method != S256 or missing
oauth_refresh_token_reused401Old refresh token presented — whole grant has been revoked
oauth_state_invalid400state mismatch on Google callback (CSRF defense)
oauth_provider_error502Google / external IDP returned an error
oauth_not_configured501Server has no Google client credentials

Wallet / SIWE

CodeStatusWhen
wallet_address_invalid400Not a valid 0x… EVM address
wallet_nonce_invalid401Nonce unknown, consumed, or doesn’t match this address
wallet_signature_invalid401Signature didn’t recover to the expected address

Gateway / capabilities

CodeStatusWhen
capability_not_found404Slug not registered, or disabled
capability_disabled422Found but status != 'active'
payment_required402Caller didn’t include payment credentials at all
insufficient_balance402Wallet doesn’t have enough available KES
currency_mismatch400Topup/transfer/call currency doesn’t match wallet currency
upstream_error502Capability provider’s upstream returned an error or unreachable

Spending controls (rejection from the gateway)

All return 402 with X-LAP-Rejection-Reason: <code>. These trip pre-flight, before any wallet hold.

CodeWhen
daily_limit_exceededToday’s spend plus this call would exceed the daily cap
transaction_limit_exceededSingle-call price exceeds per-transaction cap
capability_blockedSlug is on the block-list
capability_not_allowedSlug is not on the allow-list (which is set + non-empty)
escalation_requiredPrice exceeds the escalation threshold; agent must request approval first

Approvals

CodeStatusWhen
approval_required402Capability call needs an approval; one is being requested
approval_denied403Approval was decided deny by the user
approval_expired410Approval TTL elapsed before use
approval_used409Approval already consumed by a prior execute
approval_mismatch400Approval slug/amount/currency doesn’t match this call
approval_signature_invalid401Wallet signature didn’t verify
approval_wallet_not_linked403Signature recovered to an address not linked to the account

Intent mandates

CodeStatusWhen
mandate_not_open409Mandate is fulfilled / cancelled / exhausted / expired
mandate_expired410Past expires_at (lazy-flipped on read)
mandate_exhausted409Either fulfillment count maxed or total budget consumed
mandate_amount_exceeded402This fulfillment exceeds max_amount_per_fulfillment
mandate_slug_not_allowed402Slug doesn’t match any capability_slug_patterns
mandate_merchant_not_allowed402First slug segment isn’t in allowed_merchants
mandate_currency_mismatch400Fulfillment currency ≠ mandate currency
mandate_signature_invalid401Signature didn’t verify against any linked wallet
mandate_wallet_not_linked403Signer address isn’t linked to the account

Provider / settlement

CodeStatusWhen
provider_error502Generic payment rail or upstream provider error
provider_auth_failed502Provider rejected Lipafy’s API key
provider_service_balance_low502Provider’s service wallet can’t pay fees
provider_unavailable502Network / provider downtime
provider_rate_limited429Provider has thrown 429 back at us
provider_temporarily_blocked502M-Pesa STK temp ban etc.
provider_kyc_required502Provider says KYC is incomplete
invalid_phone400Phone number didn’t normalize
webhook_signature_invalid401Internal payment callback verification failed
webhook_replay200Idempotent re-delivery — no further action

Generic

CodeStatusWhen
invalid_request400Request shape didn’t validate (Zod errors land here)
not_found404The referenced resource doesn’t exist or isn’t visible to you
conflict409State conflict (duplicate email, double-consume, etc.)
rate_limited429You’ve hit our per-key rate limit
internal_error500Something we didn’t anticipate