PCI Agent Runtimes
Run your AI agent inside a Basis Theory Reactor so it can work with card data while your application never touches it.An agent that helps a customer pay eventually needs the card. It may need to check whether the saved card works for an order, type it into a merchant's checkout, or send it to a processor. If the card number passes through your application server or the service that hosts your agent, both are in PCI DSS scope, along with their logs, traces, and crash dumps.
A PCI agent runtime moves the part of your agent that touches card data into a Reactor. Your application invokes the Reactor with token expressions instead of card numbers. Basis Theory replaces each expression with the card data inside the Reactor, your agent code runs there with the model provider and tools you choose, and the Reactor returns only the result your application needs. The Reactor runs on the generally available Node.js runtime, so there is nothing to enable on your tenant.
Running an agent in a Reactor keeps your application out of the cardholder data path. It does not bring your model provider into PCI compliance. Anything the Reactor sends to the model, a browser provider, or any other third party is in your cardholder data flow, so keep card numbers and security codes out of prompts and tool results, and assess every third party that does receive them.
Start with a Guide
Run an Agent on Card Data
Agents and Browsers
If you are automating a checkout with fixed browser code and no model in the loop, see Browser Proxy instead.
How It Works
- Your application invokes the Reactor with the task and a token expression for each card, for example
"card": "{{ token: <CARD_TOKEN_ID> }}". - Basis Theory checks that the calling application is allowed to use those tokens, replaces each expression with the token's data, and passes the result to your code as
event.req. - Your agent code calls the model with only the context it needs, such as the card brand, last four digits, or whether the card has expired.
- Tools that need the card itself, such as filling a checkout form, run as code inside the Reactor. The model asks for the action; it never receives the card data.
- The Reactor returns the agent's result to your application.
Choose How the Model Sees Card Data
The model never needs the card number to reason about a card. Decide what it does need, and derive it in code.
Derive Context Before the Model Call
Your Reactor code reads the card from event.req, computes the facts the task depends on, and puts only those facts in the prompt. This is the simplest pattern to audit, because every value that reaches the model appears in one function. Use it when you know in advance what the model needs to know.
Give the Model Narrow Tools
When the model should decide what to look up, give it tools that return derived facts: a summary with brand and last four digits, or a yes-or-no answer to "can this card pay a merchant that accepts these brands?". Each tool runs in the Reactor and closes over the card data, so the model only sees the tool's output.
Do not give the model a general detokenization tool or any tool that returns the card number. A prompt injection in a merchant page, a product description, or a customer message can make the model call that tool and repeat the result in its answer, which the Reactor then returns to your application.
Run an Agent on Card Data shows both patterns.
Choose an Agent Framework
Any agent framework that runs on Node.js can run in a Reactor if it fits the runtime's limits. The Run an Agent on Card Data examples run on the Vercel AI SDK, the OpenAI SDK, the OpenAI Agents SDK, and LlamaIndex. These limits decide whether another framework works:
Restricted Node.js modules. Reactor code cannot use child_process, worker_threads, cluster, vm, inspector, repl, or dgram. Frameworks that spawn subprocesses or worker threads fail, for example to run a code-execution tool or a local MCP server over stdio. A dependency that loads a restricted module when it is imported fails provisioning, and the Reactor's requested.error_code is restricted_operation. A dependency that loads one later fails the invocation with a 422 response. See Sandbox Restrictions.
Execution time. A synchronous Reactor invocation runs for at most 30 seconds. Each model call in an agent loop typically takes one to a few seconds, and the first invocation after provisioning or a period of inactivity also pays a cold start, which grows with the size of your dependencies. Keep synchronous agent loops to a few steps, set warm_concurrency for latency-sensitive workloads, and use an asynchronous Reactor, which runs for up to 900 seconds, when the agent needs longer.
Dependency scanning. Dependencies must be pinned to exact versions, and provisioning fails if any of them has a HIGH or CRITICAL vulnerability. Agent SDKs release often and pull in large dependency trees, so run the same scan in your CI before you deploy. See Vulnerability Scanning.
Network and memory. Reactors reach the internet over HTTPS, so your model provider and tools must be served over HTTPS. The standard resource tier has 256 MB of memory; choose large or xlarge with runtime.resources when your framework or a browser automation library needs more.
FAQ
Does running the agent in a Reactor make my model provider PCI compliant?
No. The Reactor keeps card data out of your application, but the model provider receives everything the Reactor sends it. Send the model derived facts instead of card data, turn off framework features that export prompts or tool output to a third party (for example, OpenAI Agents SDK tracing), and use a provider whose data retention and contract terms meet your compliance requirements.
Which permissions does the calling application need?
A private application with reactor:invoke can invoke the Reactor and send any token in the tenant to it. To limit which tokens a caller can send, create the application with an access rule that grants token:use on only the containers it needs. That rule also allows the application to invoke Reactors, and expressions for tokens outside those containers fail with a 400 response.
Can the agent call Basis Theory APIs?
Yes. Grant the permissions the code needs with runtime.permissions, then use event.applicationOptions.apiKey and event.applicationOptions.baseUrl to create a @basis-theory/node-sdk client inside the Reactor. These permissions apply to the code, not to the application that invokes the Reactor.
How do I keep card data out of logs?
Never log event.req or a tool's input. Runtime logs are off by default, and Basis Theory redacts sensitive values it recognizes when they are on, but your code controls what it writes. Check your agent framework's logging and tracing options as well, since some print prompts and tool calls by default.
Where do model provider keys go?
Put them in the Reactor's configuration, which Basis Theory encrypts at rest, and read them from event.configuration. Never put them in the Reactor code, which Basis Theory scans for plaintext secrets.
How long can an agent run?
Synchronous Reactors time out after the runtime.timeout you set, up to 30 seconds. Asynchronous Reactors, an Enterprise feature, run for up to 900 seconds; your application submits the request and then polls for the result. See Invoke Async Reactors.