Sign in →

Error Codes

Hit a code you don’t recognise? Find it here. Every Aforo API error is an RFC 7807 Problem Detail. Read the HTTP status for the category, then the code field for the exact business reason and how to fix it.

The error envelope

A business-rule error looks like this. The two fields you act on first are code (stable, machine-readable — map it to your own UX) and traceId (the correlation id — quote it to support). instance, when present, is the request path.

HTTP/1.1 422 Unprocessable Entity
Content-Type: application/problem+json

{
  "type":      "https://api.aforo.ai/problems/business-rule-violation",
  "title":     "Business Rule Violation",
  "status":    422,
  "detail":    "Rate plan has active subscriptions and cannot be deleted",
  "code":      "RP004",
  "errorName": "RATE_PLAN_HAS_ACTIVE_SUBSCRIPTIONS",
  "instance":  "/api/v1/rate-plans/rp_123",
  "timestamp": "2026-06-30T10:24:11Z",
  "traceId":   "a1b2c3d4…"
}

Storefront and embed-checkout responses use the same Problem Detail shape, but carry the code in errorCode (storefront) or code (embed cart) — see the sections below. Usage-ingestion errors instead carry a Problem Detail type URI plus typed fields — see Usage ingestion codes.

Two responses don’t carry a code field at all — branch on the HTTP status, not a code:

  • Platform rate limiting (429). The per-tenant rate limiter returns a simpler envelope — not Problem Detail — with Retry-After and X-RateLimit-* headers:
    { "success": false,
      "errors": [{ "code": "RATE_LIMITED",
                   "message": "Rate limit exceeded. Max N requests per minute per tenant." }] }
    Honour Retry-After and back off with jitter. (Business-logic 429s from the management API use the RFC 7807 shape with code GEN004 instead.)
  • Storefront / auth 401. Authentication failures on the storefront auth API return a 401 Problem Detail with a detail message but no code — treat any 401 as “re-authenticate”.

HTTP status codes

StatusMeaningWhat to do
400Bad Request
The request is malformed or a parameter has the wrong type.
Check the body and query params against the API reference. The detail field names the offending field.
401Unauthorized
Missing or invalid Authorization header.
Send a valid Bearer token: Authorization: Bearer sk_live_xxx. Confirm the key has not been revoked or expired.
403Forbidden
Authenticated, but your role lacks permission for this operation.
Your key needs a role like OWNER, ADMIN, or BILLING_ADMIN. Ask a workspace admin to grant it.
404Not Found
The resource does not exist — or belongs to a different tenant.
Verify the ID. Cross-tenant requests return 404 (not 403) on purpose, to prevent ID enumeration.
409Conflict
The operation conflicts with current state.
Read the code field for the specific business reason (duplicate name, active subscriptions, in-progress bill run, …).
422Unprocessable Entity
Well-formed request that fails a business rule.
The default status for business-rule violations. The code + detail fields explain which rule and how to satisfy it.
429Too Many Requests
Rate limit exceeded.
Back off and retry with exponential jitter. The Retry-After header gives the wait in seconds; X-RateLimit-* headers show your budget.
500Internal Server Error
An unexpected error on our side.
Retry once; if it persists, email support@aforo.ai with the instance / traceId from the response body.
503Service Unavailable
A downstream service is temporarily unavailable.
Retry with exponential backoff. Most 503s clear within ~30 seconds; check the status page if they do not.

Business error codes

The value of the code field on a 4xx response from the management API. Each is PREFIX + number — the prefix is the domain.

Product & Catalog · P*

CodeName & meaningHow to resolve
P001PRODUCT_NOT_FOUND
Product not found
Verify the product ID; an ID from another tenant returns 404, not this code.
P002PRODUCT_ALREADY_MONETIZED
Product is monetized and cannot be deleted
Cancel or migrate the rate plans / subscriptions on the product, then delete.
P003PRODUCT_DUPLICATE_NAME
Product with this name already exists
Product names are unique per tenant (case-insensitive). Pick a different name.
P004PRODUCT_INVALID_TYPE
Invalid product type
Use one of API, AI_AGENT, MCP_SERVER, AGENTIC_API.
P005PRODUCT_SYNC_FAILED
Product sync from gateway failed
Check the gateway connection under Governance → Integrations, then retry the sync.
P006PRODUCT_ARCHIVED
Product is archived and cannot be modified
Restore the product before editing it.

Billable Units (Metrics) · M*

CodeName & meaningHow to resolve
M001METRIC_NOT_FOUND
Metric not found
Verify the billable-unit ID.
M002METRIC_DUPLICATE_NAME
Metric with this name already exists
Billable-unit names are unique per tenant. Choose a different name.
M003METRIC_IN_USE
Metric is used by active rate plans and cannot be deleted
Remove the unit from those rate plans first, or archive it instead of deleting.
M004METRIC_INVALID_AGGREGATION
Invalid aggregation type for this metric
Aggregation cannot change once a unit is priced in a plan. Clone the unit to change it.

Rate Plans (Rate Cards) · RP*

CodeName & meaningHow to resolve
RP001RATE_PLAN_NOT_FOUND
Rate plan not found
Verify the rate plan ID.
RP002RATE_PLAN_INVALID_PRICING_MODEL
Invalid pricing model configuration
Check the rate, included units, and tier config for the selected pricing model.
RP003RATE_PLAN_DUPLICATE_NAME
Rate plan with this name already exists
Choose a name unique within your tenant.
RP004RATE_PLAN_HAS_ACTIVE_SUBSCRIPTIONS
Rate plan has active subscriptions and cannot be deleted
Subscriptions are pinned to this plan. Migrate them to another plan first.
RP005RATE_PLAN_VERSION_CONFLICT
Rate plan version conflict — concurrent modification detected
Another request changed the plan. Refetch the latest version and retry.
RP006RATE_PLAN_INVALID_TIERS
Pricing tiers are invalid or overlapping
Tiers must be contiguous and non-overlapping. Fix the tier boundaries.

Offerings · O*

CodeName & meaningHow to resolve
O001OFFERING_NOT_FOUND
Offering not found
Verify the offering ID.
O002OFFERING_INVALID_STATE_TRANSITION
Cannot transition offering from current state
Check the offering lifecycle for the allowed transitions from its current state.
O003OFFERING_HAS_ACTIVE_SUBSCRIPTIONS
Offering has active subscriptions
Migrate the subscriptions off this offering before the change.
O004OFFERING_EMPTY_ITEMS
Offering must have at least one rate card item
Add at least one rate card to the offering.
O005OFFERING_DUPLICATE_CODE
Offering with this code already exists
Offering codes are unique per tenant. Pick a different code.

Subscriptions · S*

CodeName & meaningHow to resolve
S001SUBSCRIPTION_NOT_FOUND
Subscription not found
Verify the subscription ID.
S002SUBSCRIPTION_INVALID_STATE_TRANSITION
Cannot transition from current subscription state
Check the 9-state lifecycle for the allowed moves from the current state.
S003SUBSCRIPTION_ALREADY_CANCELLED
Subscription is already cancelled
Create a new subscription instead of modifying a cancelled one.
S004SUBSCRIPTION_TRIAL_EXPIRED
Trial period has expired
Convert the trial to a paid plan, or start a new subscription.
S005SUBSCRIPTION_DOWNGRADE_BLOCKED
Downgrade blocked — current usage exceeds target plan limits
Reduce usage below the target plan limits, or pick a higher plan.
S006SUBSCRIPTION_PAYMENT_REQUIRED
Payment is required to activate subscription
Add a payment method or settle the outstanding amount to activate.

Customers · C*

CodeName & meaningHow to resolve
C001CUSTOMER_NOT_FOUND
Customer not found
Verify the customer ID.
C002CUSTOMER_HAS_ACTIVE_SUBSCRIPTIONS
Cannot delete customer with active subscriptions
Cancel the customer’s active subscriptions before deleting.
C003CUSTOMER_DUPLICATE_EMAIL
Customer with this email already exists
A customer with this email already exists in the tenant. Reuse it or use a different email.
C004CUSTOMER_HAS_UNSETTLED_INVOICES
Customer has unsettled invoices
Settle or void the outstanding invoices first.
C005CUSTOMER_SUSPENDED
Customer account is suspended
Reactivate the customer before this operation.

Teams · T*

CodeName & meaningHow to resolve
T001TEAM_NOT_FOUND
Team not found
Verify the team ID.
T002TEAM_HAS_MEMBERS
Cannot delete team with active members
Remove or reassign members before deleting the team.
T003TEAM_DUPLICATE_NAME
Team with this name already exists
Team names are unique per tenant.
T004TEAM_BUDGET_EXCEEDED
Team budget limit exceeded
Raise the team budget or reduce spend before retrying.

Members · MB*

CodeName & meaningHow to resolve
MB001MEMBER_NOT_FOUND
Member not found
Verify the member ID.
MB002MEMBER_DUPLICATE_EMAIL
Member with this email already exists in team
This email is already a member of the team.
MB003MEMBER_LAST_OWNER
Cannot remove the last owner from the team
Assign another owner before removing this one.

Invoices · I*

CodeName & meaningHow to resolve
I001INVOICE_NOT_FOUND
Invoice not found
Verify the invoice ID.
I002INVOICE_ALREADY_PAID
Invoice is already paid
No action needed — the invoice is settled. mark-paid is idempotent and safe to ignore here.
I003INVOICE_ALREADY_VOIDED
Invoice is already voided
The invoice is already voided. Issue a new invoice if needed.
I004INVOICE_INVALID_STATE_TRANSITION
Cannot transition invoice from current state
Follow the lifecycle DRAFT → OPEN → PAID / VOID / UNCOLLECTIBLE.
I005INVOICE_FINALIZE_FAILED
Invoice finalization failed
Check line items and tax / ERP configuration, then retry finalize.

Bill Runs · BR*

CodeName & meaningHow to resolve
BR001BILL_RUN_NOT_FOUND
Bill run not found
Verify the bill run ID.
BR002BILL_RUN_OVERLAP
Bill run overlaps with an existing run for this period
A run already covers this period. Wait for it, or choose a non-overlapping period.
BR003BILL_RUN_ALREADY_COMPLETED
Bill run is already completed
The run finished. Start a new run for the next period.
BR004BILL_RUN_LOCKED
Another bill run is in progress for this tenant
Wait for the in-progress run to finish before starting another.

Wallets · W*

CodeName & meaningHow to resolve
W001WALLET_NOT_FOUND
Wallet not found
Verify the wallet ID.
W002WALLET_INSUFFICIENT_BALANCE
Insufficient wallet balance
Top up the wallet before retrying the charge / hold.
W003WALLET_EXPIRED
Wallet has expired
The wallet has expired. Provision a new wallet.
W004WALLET_HOLD_FAILED
Failed to place hold on wallet
Confirm available balance and retry; the prior hold may still be open.

Payments · PAY*

CodeName & meaningHow to resolve
PAY001PAYMENT_GATEWAY_ERROR
Payment gateway returned an error
Check gateway status + credentials, then retry. Transient gateway errors are safe to retry under the same idempotency key.
PAY002PAYMENT_METHOD_NOT_FOUND
Payment method not found
Verify the payment-method ID, or add a payment method first.
PAY003PAYMENT_DECLINED
Payment was declined
The card was declined. Use a different payment method.
PAY004PAYMENT_DUPLICATE
Duplicate payment detected
Idempotency caught a repeat. The original charge most likely succeeded — check the transaction before retrying.

Integrations · INT*

CodeName & meaningHow to resolve
INT001INTEGRATION_NOT_FOUND
Integration not found
Verify the integration ID.
INT002INTEGRATION_DUPLICATE_CONFIG
Duplicate integration configuration for this environment
An integration already exists for this provider + environment. Edit it instead of adding another.
INT003INTEGRATION_CONNECTION_FAILED
Failed to connect to integration provider
Check credentials and network reachability to the provider, then retry.
INT004INTEGRATION_CREDENTIAL_EXPIRED
Integration credentials have expired
Reconnect the integration to refresh its credentials.
INT005INTEGRATION_SYNC_IN_PROGRESS
A sync operation is already in progress
Wait for the running sync to finish before starting another.

Organization & Settings · ORG*

CodeName & meaningHow to resolve
ORG001ORG_SETTINGS_NOT_FOUND
Organization settings not found
Configure organization settings before this operation.
ORG002ORG_DOMAIN_NOT_ALLOWED
Email domain is not in the approved list
Add the email domain to the approved list under Settings.

API Keys · AK*

CodeName & meaningHow to resolve
AK001API_KEY_NOT_FOUND
API key not found
Verify the API key ID.
AK002API_KEY_REVOKED
API key has been revoked
Mint a new key — revocation is permanent.
AK003API_KEY_EXPIRED
API key has expired
Rotate the key to get a fresh credential.
AK004API_KEY_LIMIT_REACHED
Maximum number of API keys reached
Revoke an unused key before minting another.

General & Cross-cutting · GEN*

These come from any service. Most map to a matching HTTP status above (e.g. GEN004 → 429, GEN007 → 401, GEN008 → 403).

CodeName & meaningHow to resolve
GEN001TENANT_NOT_FOUND
Tenant not found
The X-Tenant-Id does not resolve to a tenant. Check the value.
GEN002TENANT_CONTEXT_MISSING
Tenant context is required but missing
Include the X-Tenant-Id header on the request.
GEN003CONCURRENT_MODIFICATION
Resource was modified by another request
Refetch the latest state and retry your change.
GEN004RATE_LIMIT_EXCEEDED
Rate limit exceeded — please retry later
Back off and retry; honour the Retry-After header. Returned as HTTP 429.
GEN005SERVICE_UNAVAILABLE
Downstream service is temporarily unavailable
Retry with exponential backoff. Returned as HTTP 503.
GEN006VALIDATION_FAILED
Request validation failed
Check the field-level errors in the response detail.
GEN007UNAUTHORIZED
Authentication required
Send a valid Bearer token. Returned as HTTP 401.
GEN008FORBIDDEN
Insufficient permissions
Your role lacks permission for this operation. Returned as HTTP 403.
GEN009CONSTRAINT_VIOLATION
Data constraint violation
A unique / foreign-key constraint was violated. Check the referenced IDs and uniqueness.

Storefront API codes

The headless / storefront API (the one you build your own storefront against) returns these in the errorCode field.

CodeStatusMeaningHow to resolve
RESOURCE_NOT_FOUND404The endpoint or resource does not exist.Check the path. Storefront APIs are versioned under /api/v1/.
METHOD_NOT_ALLOWED405The HTTP method is not supported for this endpoint.See the allowedMethods field for what the endpoint accepts.
VALIDATION_ERROR400A request field failed validation.The detail field carries the first failing rule. Fix the field and resend.
MISSING_PARAMETER400A required query parameter is missing.The parameter field names what is missing. Add it.
WEBHOOK_URL_BLOCKED422The webhook URL is private, loopback, or otherwise disallowed (SSRF guard).Use a public HTTPS URL that does not resolve to a private / link-local address.
INVALID_PARAMETER400A parameter has the wrong type or an invalid value.Coerce the value to the expected type (the detail field shows the bad value).

Embed checkout codes

The embed checkout flow (CheckoutFlow widget + cart API) returns these in the code field. The widget branches on them; if you build your own checkout, handle each below.

CodeStatusMeaningHow to resolve
PAYMENT_FAILED402The gateway declined the card.Surface the error and let the customer retry with a different payment method. gatewayCode carries the processor reason.
REQUIRES_ACTION4023DS / SCA challenge — additional verification is required.Redirect or iframe the customer to nextActionUrl to complete the challenge, then re-confirm.
ALREADY_SUBSCRIBED409The customer already has an active subscription to this offering.Send them to manage the existing subscription (existingSubscriptionId) instead of subscribing again.
INVOICE_ALREADY_PAID409The invoice was paid through another channel between cart-create and confirm.Treat the purchase as complete — no second charge is needed.
CART_STATE_INVALID409The cart is not in the expected state for the requested transition.Re-create the cart; do not confirm a cart that has not reached the payment step.

Usage ingestion codes

Errors from the event-ingestion API (the endpoint your SDK / gateway plugin posts usage to). These are RFC 7807 Problem Details, but they carry a type URI (https://api.aforo.ai/problems/<type>) plus typed fields — not a single code. Branch on the type slug (below) or the HTTP status.

typeStatusMeaning & fieldsHow to resolve
unknown-metric422The event references a metric (billable unit) that doesn’t exist for your tenant.
fields: metricName, tenantId
Create the billable unit first, or fix the metric name. The default ingestion policy rejects unknown metrics (an operator can switch it to auto-create).
ingestion-validation-error422The payload is well-formed but fails an ingestion rule (missing/invalid field, bad timestamp).Read detail for the failing rule. Events more than ~5 min in the future or older than 90 days are rejected.
duplicate-event409An event with this idempotency key was already ingested.
fields: idempotencyKey
Safe to ignore — the original event was accepted. Idempotency caught a retry.
quota-exceeded429The subscription has consumed its quota for this metric in the current window.
fields: metricName, currentUsage, quotaLimit, timeWindow, overageStrategy, resetsAt
Behaviour follows overageStrategy. If blocking, the customer upgrades or waits until resetsAt.
budget-exceeded402The team has hit its spend budget for the period.
fields: teamId, currentSpend, budgetLimit, periodKey
Raise the team budget or wait for the next period. 402 signals budget/payment required.
rate-limit-exceeded429Per-agent ingestion rate limit exceeded.
fields: agentId, limitType, currentCount, limit
Back off and smooth the agent’s send rate below the limit.
session-limit-exceeded429The agent has too many concurrent sessions open.
fields: agentId, activeSessionCount, maxConcurrentSessions
Close idle sessions before opening new ones, or raise the concurrency limit.
session-error400 / 409A session operation is invalid for the session’s current state.Follow the session lifecycle; don’t start/close a session out of order.
file-upload-error400 / 413A batch file upload failed (too large or wrong format).Check file size + format. Oversized uploads return 413; malformed ones return 400.