neonFin
Client & API

Errors

Common API and client errors.

The REST API returns JSON errors with a human-readable error message and a stable machine-readable code:

{
  "error": "Insufficient credits",
  "code": "insufficient_credits"
}

Branch on code (or the HTTP status) - never on the message text, which may change. The typed clients throw NeonfinError carrying status, code, and the message. For the common out-of-credits case you don't need to remember either - check err.isInsufficientCredits.

Common statuses

StatusMeaningWhat to do
400Invalid request or missing required fieldCheck request body and project setup.
401Missing, invalid, or revoked API keyCreate a new key or check the bearer token.
402Insufficient creditsShow purchase UI.
403Wrong key type or blocked originUse the correct key or update allowed origins.
404Wallet, price, or order not foundCheck ids and project ownership.
409Already subscribed to this productSend the user to the billing portal to switch tiers.
410Anonymous code wallet expiredAsk the user to start fresh or restore another code.
429Rate limit exceededWait for Retry-After seconds.
502Provider checkout/portal errorCheck provider credentials and account setup.

Error codes

codeStatusMeaning
invalid_body400Request body failed validation.
missing_bearer_token401No Authorization: Bearer header.
invalid_api_key401Unknown or revoked key.
wrong_key_kind403Endpoint needs the other key type (pk vs sk).
secret_key_from_browser403Secret keys must be used server-side, not from browser-origin requests.
origin_not_allowed403Browser origin is not on the project allowlist.
redirect_origin_not_allowed400successUrl/cancelUrl origin not allowed.
rate_limited429Too many requests - wait for Retry-After.
mode_mismatch400Endpoint doesn't apply to this project mode.
wallet_not_found404No wallet for that code / user id.
wallet_expired410Inactive anonymous code wallet was expired.
already_subscribed409Wallet already subscribes to this product - use the portal to switch.
insufficient_credits402Balance too low - includes balance/requested.
product_required400Multi-product project: pass productId.
unknown_product400productId doesn't belong to this project.
price_not_found404Unknown price id.
price_inactive400Price or product is deactivated.
price_not_synced400Price has no provider yet - connect one first.
order_not_found404Unknown order/checkout reference.
no_billing_customer400Wallet has no completed purchase yet (portal).
provider_account_missing400The product's provider account was removed.
provider_error502Stripe/Polar rejected the call - check credentials.

Insufficient credits

For deductions, 402 includes the current balance and requested amount:

{
  "error": "Insufficient credits",
  "code": "insufficient_credits",
  "balance": 4,
  "requested": 10
}

Client handling:

try {
  await neonfin.deduct(10, { idempotencyKey: "job_123" });
} catch (err) {
  if (err instanceof NeonfinError && err.isInsufficientCredits) {
    // Open your purchase dialog. err.balance / err.requested are available.
  }
}

Expired credit codes

Anonymous projects can expire inactive unpaid codes. The registry client recovers automatically for its own stored code, but explicit recovery-code lookups surface the 410 so your UI can explain that the code is no longer valid.