Skip to Content

Errors

All API errors follow a consistent format:

{ "error": { "type": "invalid_request", "message": "Amount must be greater than 0", "param": "amount", "code": "amount_invalid" } }

Error types

HTTP StatusTypeDescription
400invalid_requestBad or missing parameters
401authentication_errorInvalid or missing API key
404not_foundResource not found (or belongs to different merchant)
429rate_limitToo many requests. See Rate Limits.
500internal_errorServer-side failure. Contact support if persistent.

Error fields

FieldTypeDescription
typestringError category (always present)
messagestringHuman-readable description (always present)
paramstringThe parameter that caused the error (when applicable)
codestringMachine-readable error code (when applicable)

Retired wallet key endpoints

Interactive wallet APIs use a merchant session rather than a payment API key. The retired key POST routes return HTTP 410 in their normal wallet error envelope:

{ "success": false, "error": { "code": "SIGN_WINDOW_REQUIRED", "message": "Reload inzi and open the wallet action in inzi sign.", "details": { "migrationUrl": "https://docs.inzilink.com/docs/wallet-signing", "signingSessionEndpoint": "/api/v1/signing-sessions", "interactive": true } } }

Follow Wallet Signing to select the action and open its Sign window. Retrying a retired POST or changing its Origin does not restore it.


Common errors

Authentication

// Missing or invalid API key { "error": { "type": "authentication_error", "message": "Invalid API key provided" } }

Validation

// Invalid amount { "error": { "type": "invalid_request", "message": "Amount must be greater than 0", "param": "amount", "code": "amount_invalid" } } // Metadata too large { "error": { "type": "invalid_request", "message": "Metadata must be less than 4KB", "param": "metadata", "code": "metadata_too_large" } }

Team

Only the account owner grants the admin role. An admin — or an API key, which acts with the admin role — that invites with "role": "admin" is refused before anything is created or emailed:

// POST /merchants/team/invite { "email": "ops@example.com", "role": "admin" } { "success": false, "error": { "code": "OWNER_INVITES_ADMINS", "message": "Only the account owner can invite admins. You can invite this person as a viewer." } }

The same caller may invite with the viewer role:

// POST /merchants/team/invite { "email": "ops@example.com", "role": "viewer" } → 201

Plans

Returned by POST /merchants/me/plan (owner only) with 409: the request is valid, the account state does not allow it.

// POST /merchants/me/plan { "plan": "scale" } → 409 { "success": false, "error": { "code": "BILLING_UNAVAILABLE", "message": "Paid plans cannot be bought online right now. Write to us and we will switch you over by hand." } }
CodeWhen
BILLING_UNAVAILABLEAn invoice for the new plan cannot be issued right now. Nothing is left pending.
UNPAID_INVOICEAn invoice is overdue. Settle it, then change the plan.
CUSTOM_PLAN_NEEDS_A_CONVERSATIONThe account has custom terms; changing them is done with us.
PLAN_NOT_AVAILABLEThe plan is not on sale yet.

An upgrade that is accepted answers 200 with "applied": "awaiting_payment" and an invoiceUrl — see the changelog.

Actions that depend on the plan answer 403 with the reason in code, the capability and plan in details, and the limit when there is one:

// POST /merchants/team/invite — the eleventh person on Team → 403 { "success": false, "error": { "code": "QUOTA_EXCEEDED", "message": "You have used everything your plan includes for this period.", "details": { "capability": "team_manage", "plan": "scale", "quota": { "metric": "teamMembers", "limit": 10, "used": 10, "remaining": 0, "resetAt": null } } } }
CodeWhat to do
PLAN_REQUIREDThe plan does not include this. Upgrade in Billing.
QUOTA_EXCEEDEDThe plan’s limit for the period is used up.
SUBSCRIPTION_RESTRICTEDA subscription invoice is overdue. Pay it in Billing; data and withdrawals stay available.
CONTRACT_TERMS_REQUIREDCustom terms do not cover this yet. Talk to us.
FEATURE_NOT_RELEASEDNot available yet. Paying does not unlock it.

Returned by POST /api/v1/checkout/{short_code}/pay when the payer’s consent to pay in stablecoin is missing or weaker than the merchant requires. See the changelog entry for the full contract.

// 428 — consent missing { "success": false, "error": { "code": "CONSENT_REQUIRED", "message": "This payment needs the payer's explicit consent to pay in stablecoin…", "details": { "field": "consent", "expected": { "accepted": true, "locale": "en", "acceptanceMode": "checkbox", "version": "v3" } } } } // 400 — a tick-box is required for this merchant { "success": false, "error": { "code": "CONSENT_FORM_TOO_WEAK", "message": "A tick-box is required for this merchant…", "details": { "field": "consent.acceptanceMode", "expected": "checkbox" } } }

Token and currency

Payments are one to one: a USD price is paid in USDC or USDT, a EUR price in EURC. A token that does not settle the link’s currency is refused before anything is recorded:

// 400 { "success": false, "error": { "code": "TOKEN_CURRENCY_MISMATCH", "message": "That token cannot pay this link: payments are one to one, so a USD price is paid in USDC or USDT and a EUR price in EURC. Pick one of the tokens listed in \"acceptedTokens\" of GET /checkout/{short_code}." } }

Rate limiting

{ "error": { "type": "rate_limit", "message": "Rate limit exceeded. Retry after 42 seconds." } }

See the X-RateLimit-* response headers for current limits. Details in Rate Limits.


Handling errors

const res = await fetch('https://api.inzilink.com/api/v1/checkouts', { ... }) if (!res.ok) { const { error } = await res.json() switch (error.type) { case 'authentication_error': // Check your API key break case 'invalid_request': // Fix the request parameters console.error(`Invalid param: ${error.param} — ${error.message}`) break case 'rate_limit': // Back off and retry break case 'internal_error': // Retry with exponential backoff break } }
Last updated on