Errors and retries
Handle rejected charges and retry requests without duplicate debits or deposits.
API errors return a code and message:
{
"error": "insufficient_balance",
"message": "This wallet does not have enough credits."
}The SDK exposes rejected API requests as OrbyteApiError, with status, code, and message.
Handle a rejected charge
import { OrbyteApiError, track } from "@orbytelabs/credit";
try {
await track("api-call", "customer_123", { id: "request_123:api-call" });
} catch (error) {
if (
error instanceof OrbyteApiError &&
error.code === "insufficient_balance"
) {
// Ask the customer to add credits, or stop further work.
} else {
throw error;
}
}An insufficient-balance error creates no usage transaction or debit. After funding the wallet, retry the same event ID and payload. An allowed check does not reserve funds, so tracking can still fail if another request spends the balance first.
An unaffordable check itself returns allowed: false with HTTP 200. Invalid input, credentials, or pricing configuration can still cause a check to throw.
For included plan usage, check can return reason: "feature_limit_exceeded" or reason: "subscription_inactive". Tracking the same usage returns a 409 error with that code. A blocked allowance requires a plan change or a new allowance period; an inactive subscription requires restoring its active state. Adding prepaid credits alone does not resolve either denial.
Error codes
| Status | Codes | What to do |
|---|---|---|
400 | invalid_request, invalid_event, invalid_json, invalid_quantity | Fix the request shape, quantity, or pricing inputs. Unknown properties are rejected. |
401 | unauthenticated, invalid_api_key | Check the secret key and whether it has expired or been revoked. |
403 | forbidden, forbidden_api_key | Use a secret key with the required permission. |
404 | not_found, feature_not_found, wallet_not_found | Check the resource key or identity and the application selected by your API key. |
409 | feature_limit_exceeded | Wait for the quota period to reset, change plans, or use a plan with credit overage. |
409 | subscription_inactive | Restore an active subscription before using its included features. |
409 | insufficient_balance | Fund the wallet before retrying usage. |
409 | idempotency_conflict | Reuse the original payload for a retry. Use a new ID only for a new operation. |
409 | conflict | Inspect the message for an existing catalog key, locked period policy, balance overflow, or wallet balance limit. |
409 | stripe_not_connected, stripe_not_ready | Connect Stripe, complete onboarding, or finish plan synchronization before retrying checkout. |
413 | payload_too_large | Keep the JSON request body within 16 KiB. |
415 | unsupported_media_type | Send Content-Type: application/json on write requests. |
422 | credit_not_found, invalid_billing_period, unsupported_pricing, invalid_charge | Fix the feature's credit, billing period, or charge calculation. |
429 | rate_limited | Wait for the Retry-After delay before retrying. |
500 | internal_error | Retry eligible operations with the same IDs and payloads. |
502 | stripe_error | Inspect the payment state before retrying with the same operation key. |
Missing resources are scoped to the API key's application. For example, a feature deployed in Development is not available to a Production key until you deploy it there.
Retry the same operation
If a response is lost, the server may already have accepted the request. Keep the identifiers for that operation:
- Usage uses
id, unique within the application. An identical retry returns the original transaction withstatus: "duplicate". - Funding uses
idempotencyKey, also unique within the application. An identical retry returns the original deposit withduplicate: true. - Checkout and subscription changes use stable
idempotencyKeyvalues for each purchase or change. Reuse the same inputs on retries.
The namespaces are separate. The same string can identify one event and one deposit, but two different usage events must not share an ID.
For callback prices, a retry must resolve to the same nanocredit amount. Automatic SDK retries preserve the serialized request. If your own job calls the SDK again, preserve the original pricing inputs and calculation too.
Automatic SDK retries
For check, track, topup, reads, and supported updates, the SDK makes up to three attempts for transport failures, interrupted responses, HTTP 429, and server errors. Each attempt has a 20-second timeout. Retries use exponential backoff and honor a positive Retry-After delay up to 30 seconds.
After retries are exhausted, network failures raise OrbyteTransportError; valid API error responses raise OrbyteApiError. Local input validation and unexpected response shapes can raise other errors, so retain an unknown-error path in your handler.
Catalog creation does not automatically retry uncertain network or server failures. Read the catalog before trying another creation. The SDK can retry a catalog request explicitly rejected with rate_limited because that request was not admitted.
Rate and size limits
API requests are limited per key to 600 per minute, with a burst capacity of 120. Failed authenticated requests also use request capacity. If your own retry loop runs beyond the SDK's attempt limit, keep it bounded and respect Retry-After.
JSON bodies are limited to 16 KiB. Unknown JSON fields and repeated query parameters are rejected. API amounts must be nonnegative integer strings within the supported 64-bit range; deposits must be positive. See pricing for precision rules.
Use the API reference for each operation's required permissions and schemas.