Skip to main content
Every piece of managed work in ModuleX passes through an admission usage gate before it runs. The gate resolves your organization’s plan entitlements, reserves the credit the work will cost, and checks your rate and quota limits — all before any database row is written or any model is called. If the gate declines, the request fails with a structured denial and no work is performed and nothing is charged. This page is the reference for that gate: what it checks, the exact response it returns when it declines, which surfaces it covers, and the surfaces it deliberately leaves alone.
The gate fails closed. When the gate cannot confirm you have budget — including when its backing cache is unavailable — it denies the request rather than letting it through unmetered. You are never billed for work the gate did not admit.

What the gate is

The gate is a synchronous reject-before-write check that runs at the start of every managed run or turn. It does three things in order, then either admits the work or raises a denial:
1

Resolve entitlements

The gate loads your organization’s active subscription period — the plan’s monthly credit allowance, rate limits, quotas, and wallet flags. A suspended organization (an unpaid or past-grace subscription) is denied here and does not fall back to the free tier. See Subscriptions & Stripe for how suspension happens.
2

Reserve credit

The gate hard-reserves the credit the work will cost against your current billing period (for paid plans) or your per-user free pool (on Free). The reservation is atomic, so two concurrent runs cannot both spend the last credit. If the plan allowance is exhausted, the reservation cascades to your prepaid wallet — but only if overage is enabled and the balance covers it.
3

Consume the rate class

For runs, the gate also consumes one unit of your plan’s per-period run-rate limit. If that limit is hit, the gate releases the credit reservation it just made and denies with a rate error.
Only after all three pass does the work begin. When it finishes, the reserved credit is charged and the reservation is settled; if the work errors out early, the reservation is released so the held budget self-heals. The full lifecycle — reserve, charge, settle, release — is covered in Credits & metering.

The denial response: DenialEnvelope

When the gate declines, it returns a single, flat JSON shape called the DenialEnvelope — the same shape for every denial layer. It is not wrapped in a detail key (unlike the standard {"detail": "…"} errors the rest of the API returns). Front-end and SDK code branches on the top-level code and layer fields.
Example: credits exhausted (402)

Fields

string
required
The machine-stable denial code. Branch on this. One of credit_plan_exhausted, wallet_overage_disabled, wallet_insufficient, upgrade_payment_failed, quota_exceeded, or rate_limit_exceeded.
string
required
Which gate layer denied the request — one of credit, wallet, quota, or rate. The layer fixes the HTTP status: credit and wallet return 402, quota returns 403, and rate returns 429.
string | null
The specific limit or counter key involved — for example the organization id for a credit denial, sync_exec for a run-rate denial, or a quota key such as max_knowledge_bases. May be null.
number | null
The observed usage or count at the moment of denial. May be null when not applicable (for example, the credit layer often reports null here).
number | null
The limit that was hit. May be null when the limit is not applicable or not exposed.
string | null
A short reason token, often identical to code (for example overage_disabled, insufficient_balance).
The DenialEnvelope is flat — its fields are at the top level of the response body, with no detail wrapper. A 402/403/429 that arrives wrapped as {"detail": …} is not a DenialEnvelope; it comes from a non-gated route (see Plain CRUD is never gated). For the complete catalog of every error-envelope shape ModuleX returns and how each SDK maps them to a typed error class, see Errors & status codes.

The four denial layers

Each denial belongs to one of four layers. The layer determines the HTTP status, and each layer has one or more codes.
Returned when the plan allowance is gone and there is no wallet to fall back on, or when the subscription is suspended for non-payment. Resolve it by topping up the wallet (with overage enabled), upgrading your plan, or waiting for the monthly allowance to reset.
402 credit_plan_exhausted

Rate-limit headers on a 429

A rate-layer denial is accompanied by the standard rate-limit headers. Each numeric header is omitted when its value is not known, so a client may see only some of them. Retry-After is always present.
integer (seconds)
How long to wait before retrying. Defaults to 60 when no more specific value is available.
integer
The run-rate limit for your plan’s run-rate class. Omitted if not set.
integer
Remaining units in the current window (typically 0 at the moment of denial). Omitted if not set.
integer (Unix epoch seconds)
When the window resets. Omitted if not set.
The gate’s run-rate rate_limit_exceeded (a flat DenialEnvelope) is distinct from the API-key/user request rate limit, which also returns 429 but with a {"detail": "…"} string body and the same headers. Both are real and both can occur. For the full picture of every 429 path, see Rate limiting.

Which surfaces are gated

The usage gate runs on ModuleX’s managed-usage surfaces — the places where ModuleX does paid work on your behalf. On these surfaces a 402/403/429 can be a flat DenialEnvelope.

Workflow runs

POST /workflows/run with the workflow id in the JSON body (workflow_id). Each run reserves one run credit and consumes the run-rate class before it executes.

AI Composer

The Composer chat and resume endpoints. Each turn is admitted and metered before the agent edits your workflow.

Assistant

The Assistant chat and resume endpoints. Each turn is admitted before the agent reasons or calls a tool.

Managed knowledge

Retrieval and document ingest on ModuleX-managed (modulexdb) knowledge bases. BYOK vector stores are not metered and not gated.

Per-surface behavior

A managed run reserves one run credit (RUN_CREDIT = 1) and consumes the sync_exec run-rate class. If the rate class is exhausted, the credit reservation is released first, then the run is denied with rate_limit_exceeded (429). A credit or wallet shortfall denies with the matching 402 before the workflow starts. The run endpoint requires an owner or admin caller. See Run via API.
Each Composer message is one turn. The gate admits the turn (reserving one credit) and consumes the run-rate class before the agent runs; resuming a paused turn re-enters through the resume endpoint and is not double-charged. Composer requires an owner or admin caller. See AI Composer in the builder.
Each Assistant message is one turn, admitted and rate-checked exactly like a Composer turn, even though the Assistant has no workflow tools. The Assistant requires an owner or admin caller. Permissions and limits are detailed in Permissions & limits.
On a ModuleX-managed knowledge base, each retrieval reserves one credit (RETRIEVAL_BASE = 1) and each document ingest reserves one credit (FILE_INGEST_BASE = 1) before the work begins, both against the api rate class. A bring-your-own vector store (Qdrant, Pinecone, MongoDB Atlas, Weaviate) is not managed usage, so it is neither metered nor gated. See Managed knowledge.

Plain CRUD is never gated

Everyday actions that do not run managed work are not gated and never return a DenialEnvelope. Listing, reading, creating, updating, and deleting resources, and changing organization settings, all bypass the gate entirely. A 402/403/429 from one of these routes is a standard {"detail": …} error, never the flat envelope.
Routes such as GET/POST/PATCH/DELETE /workflows and /workflows/{workflow_id}, GET/POST /knowledge-bases, /credentials, /api-keys, /schedules, and GET /organizations or GET /integrations do not call the usage gate. If you get a 403 on one of these, it is an authorization or quota error in {"detail": …} form, not a billing denial. Do not parse a flat DenialEnvelope from a CRUD response.

Handling a denial in code

Run a workflow and handle a billing denial across cURL, Python, and JavaScript. Authenticate with your API key as a bearer token plus the organization header, exactly as on every ModuleX request — see Authentication.
The two SDKs model denials differently. The Python SDK has a BillingError family — CreditExhaustedError, WalletError, QuotaExceededError, and a base for rate-layer envelopes — so you can catch each layer by type. The JavaScript SDK has no dedicated billing subclasses: a 402 or a rate-layer envelope surfaces as the base ModulexError with .code, .layer, and .reason populated, while a header-path 429 is a RateLimitError. The full status-to-class mapping for both SDKs is in Errors & status codes and Errors & retries.

What is not billed or gated

The gate only meters managed usage — work ModuleX runs through its own provisioned models, tools, and vector stores. The following are not gated and never cost credits:
  • Bring-your-own-key (BYOK) usage. When you connect your own LLM provider or knowledge provider, that usage is billed directly by the provider and recorded for analytics only — no credit is charged and the gate does not deny it.
  • Plain CRUD and org-settings routes, as covered above.
  • Reading runs, history, and dashboards.
For exactly what a credit is and how managed model, tool, and retrieval usage is converted into credits, see Credits & metering.

Errors & status codes

Every error-envelope shape, the full HTTP status taxonomy, and how the SDKs map each status to a typed error class.

Credits & metering

What a credit is, the reserve-charge-settle lifecycle, and exactly what consumes credits.

Rate limiting

Per-key and per-user rate limits and all three live 429 paths.

Wallet & top-ups

Enable overage, top up the prepaid wallet, and set up auto top-up.

Plans & pricing

Allowances, quotas, and rate limits for Free, Pro, Max, and Enterprise.

Credits & the billing model

The concept behind metering and where the gate applies.