Self-hosting (BYOC)
Use self-hosting to export a saved flow or agent as a self-contained
@runtypelabs/runtime definition. Run the definition on Cloudflare Workers,
Cloud Run, Node, Vercel Functions, or another environment with a runtime
adapter. The runtime calls model providers with your own provider keys, through
the Runtype platform-key proxy, or both, depending on the key mode you
configure. It can also call hosted Runtype endpoints for asset storage and
other platform capabilities.
Before you begin
You need the following:
- An Enterprise plan. Self-hosting (BYOC, or bring your own cloud)
requires one. When an account without it calls an export endpoint with a
correctly scoped API key, it receives
HTTP 403with the codeBYOC_PLAN_REQUIRED. Contact support to enable self-hosting for your account. - An API key with the Runtime Export permission. In the Create New API
Key dialog, select Runtime Export in the Permissions list. A
resource wildcard such as
AGENTS:*orFLOWS:*doesn’t grant this scope. Only theRUNTIME:EXPORTscope itself or the global*scope, which the Full Access permission template grants, does. For more information, see Managing API keys.
Export a flow or agent
The export endpoints return fully resolved definitions that the
@runtypelabs/runtime package loads at startup. The flow export inlines step
configurations. The agent export inlines capabilities, flows, and nested
sub-agents through up to three levels, and replaces MCP credential values with
secret-name references.
An inline MCP server with api_key or custom_header auth sends its token
reference under the server’s configured header name. Tools of a server with
basic auth are not listed in the export. The runtime discovers them when the
agent starts and builds the Authorization: Basic header from the username and
password secrets.
The export never copies a credential that an author typed into a tool
configuration or an MCP server. The export returns 422 with code
raw_credential_in_export in these cases:
- A literal value sits under a credential-shaped field, such as
apiKeyor anX-Api-Keyheader. - A value in a known secret format, such as an OpenAI, Anthropic, or GitHub key, sits under any field.
Each entry in the response’s details array names the tool or MCP server, the
field, and the JSON path. The response never includes the value. To fix the
export, store the value as a Runtype secret, replace the literal with a
{{secret:NAME}} reference, and provide NAME to your runtime.
You can export agents that run with a model and system prompt, and external
(A2A) agents. You can’t export a Claude Managed agent: exporting one returns
404 with Agent not found. If an agent you export references a Claude
Managed agent as a sub-agent, the export returns 200 without that
sub-agent and lists it in warnings. A flow that references a Claude Managed
agent gets a stub that the runtime can’t run, which warnings also lists. The
flow export is in limited preview.
To export a flow, send this GET request:
To export an agent, send this GET request:
Replace the placeholders in both requests as follows:
FLOW_ID: ID of the flow to export.AGENT_ID: ID of the agent to export.YOUR_API_KEY: Runtype API key with theRUNTIME:EXPORTscope.
The TypeScript SDK exposes the same exports as flows.exportRuntime(id) and
agents.exportRuntime(id, selector). The Runtype MCP server exposes them as
the export_flow_runtime and export_agent_runtime tools. The
export_agent_runtime tool takes the selector as version_id or alias.
An export is a snapshot. Without a selector, the agent export follows your
organization’s live-default setting. With the setting on, the export captures
the version that the agent’s live alias points at, which is the version
that hosted execution runs. Saved changes that you haven’t published or
deployed to live aren’t included. With the setting off, the export captures
the agent’s current saved configuration. Each referenced sub-agent follows the
same setting. The deployed runtime makes no lookups back to Runtype for its
definition, so after you change the agent, a flow, or a tool in Runtype,
publish the change, export again, and redeploy.
Export a specific version or alias
To export a published version, or the version that a release alias points at, add one selector to the agent export:
alias: name of a release alias, such asstaging,live, orpr-42. The export captures the version that the alias points at when you call it, even whenlivepoints at a different version.versionId: ID of a published version of the agent.
Pass at most one selector. A request with both returns 400. The alias
resolves the same way as the execution selector agent: { agentId, alias }.
In the SDK, pass agents.exportRuntime(id, { alias: 'staging' }) or
agents.exportRuntime(id, { versionId }).
An export that contains a published version carries a release object that
records which version it contains:
versionId: the version the export contains.aliasandaliasRevision: the alias that selected the version and the alias revision at the time of the export. Present only when an alias selected the version, including thelivealias that an export without a selector follows.
An export of the agent’s current saved configuration has no release object.
Export a vendorable bundle
Add format=bundle to the agent export to download one zip archive that
holds everything a self-hosted deployment needs. You don’t need access to the
runtypelabs/core repository or to an npm registry for Runtype packages.
The versionId and alias selectors work the same way as for the JSON export.
The archive contains these files:
agent.json: the same definition that the JSON export returns.runtime/: the prebuilt@runtypelabs/runtimepackage for the platform’s current runtime version. It is minified ESM with its dependencies bundled in. To use it, add"@runtypelabs/runtime": "file:./runtime"to yourpackage.json.manifest.json: the runtime version and source commit, the agentversionIdand alias, the entrypoints,hostDependencies, andcontentHash.LICENSE: the terms for the vendored runtime code. The runtime ships under the Functional Source License, Version 1.1, with an Apache 2.0 future license (FSL-1.1-ALv2).
Two exports of the same version return byte-identical archives.
contentHash is the SHA-256 of the sha256sum listing of every file in the
archive except manifest.json: one <sha256> <path> line per file, sorted
by path. The response also sends it in the X-Runtype-Bundle-Content-Hash
header. Pin this value to detect any change to the definition or the runtime.
With the CLI, run runtype agents export-runtime AGENT_ID --format bundle. In
the SDK, call agents.exportRuntimeBundle(id, selector). The MCP tool
export_agent_runtime with format: "bundle" reports the content hash and
the runtime version, but it doesn’t return the archive bytes.
If the platform has no prebuilt runtime artifact for its current version, a
bundle export returns 503 with code RUNTIME_ARTIFACT_UNAVAILABLE and
reason: "artifact_missing".
The following table lists the errors that the export endpoints return:
A bundle export returns 503 with one of these reason values:
artifact_missing, artifact_invalid, artifact_integrity,
binding_missing, or source_commit_unknown.
A child that the export can’t embed doesn’t fail the export. The export
returns 200 and lists the child in warnings. This includes sub-agents and
flows that nest deeper than three levels.
Export warnings
Both export responses carry warnings. Each entry names a sub-agent, flow,
or question set that the export left out of the artifact, or embedded only as
a stub, and gives the reason. The tool or capability that references a dropped
child is absent from the artifact. The array is empty when the export embedded every
child. A bundle export lists the same entries in the warnings field of
manifest.json.
The following table lists the fields of an entry:
Check warnings before you deploy an export. An empty array means that the
artifact contains every sub-agent, flow, and question set that the definition
references.
On an account without an Enterprise plan, both endpoints return this response:
Host dependencies
Some things a flow or agent can do are not self-contained. The runtime hands
them to your host instead of performing them itself. An export is never
refused for containing one. Instead, both export responses carry
hostDependencies. It lists the host hooks that you wire before the artifact
can run everything it describes.
The following table lists the dependency kinds:
ExecuteAgentOptions.onDurablePause is the agent equivalent of the flow hook,
with the same contract. You meet the durable-pause dependency in one of two
ways:
- Wire
onDurablePauseon everyexecuteFloworexecuteAgentcall. - Consume the
durable-pausedFlowStreamOutcomeyourself on theexecuteFlowToWriterpath, if your host already arms its own scheduler.
The runtime has no default implementation of either hook. The following failures occur when a hook is missing:
- If you wire neither durable-pause channel, a run that reaches a durable step
fails with
execution_errorand the codeDURABLE_PAUSE_HOST_NOT_CONFIGURED. - If you wire no coordinator, spawning a detached subagent throws
BackgroundRunCoordinatorNotConfiguredError.
To catch a missing dependency at startup instead of mid-run, call
runtime.assertHostDependencies(definition) at boot. It throws
HostDependenciesNotConfiguredError (code
HOST_DEPENDENCIES_NOT_CONFIGURED) with the full list. To report the gaps
without throwing, for example in a health check, call
runtime.unmetHostDependencies(definition), which returns the list.
The boot check counts the durable-pause dependency as met only when you set
durablePauseHost: true in the createRuntime config. That setting declares
that every executeFlow and executeAgent call supplies onDurablePause. A
host that wires the hook but leaves the setting unset fails the check.
hostDependencies is always present. An empty array means the artifact is
fully self-contained. The following response shows a flow that needs a
durable-pause host:
Each entry names the sites that caused it. A durable-pause-host entry lists
them in steps, each with stepId, stepType, and owner fields. A
background-run-coordinator entry lists them in agents, each with owner
fields, plus stepId when the site is a step’s tool block.
The scan covers the artifact’s entire executable closure, not only its own
top-level steps. It includes conditional branches, embedded agents’
capabilities, and the flow or sub-agent definitions carried inline by a runtime
tool. A site therefore often names a step that does not appear in the flow you
exported. That is why each site carries ownerKind (flow, agent,
capability, or inline-tool), with ownerId and ownerName where they
exist.
Treat the list as open-ended. New kind values can appear, so render
interface and reason for a kind that you don’t recognize instead of
failing. An unrecognized kind deserializes as an entry that carries kind,
interface, and reason, plus any kind-specific evidence. A client on an
older SDK keeps working and can still tell you what to wire.
Bound skills
An agent export includes the skills bound to the agent in skills. Each
entry holds the skill’s description, its body, and its resolved capability
tools. It also records the skill it came from (skillId) and the skill
version that supplied it (skillVersionId), so you can see exactly which
skill versions an artifact contains.
The runtime loads an exported agent’s skills on every run. You don’t need
to wire anything in your host. The export is a snapshot: a skill you edit,
publish, bind, or unbind afterward doesn’t reach the artifact until you export
again. If a bound skill’s tools can’t be resolved when you export, the export
fails instead of leaving the skill out. Only the exported agent’s own skills
are included; sub-agents run without their bound skills, as they do on
Runtype.
To change the skills for one run, pass skills in ExecuteAgentOptions (or
in the dispatch host’s executeOptions, which applies to every agent the host
serves). A skill you pass replaces the exported skill with the same slug, and
a skill with a new slug is added. Pass an empty array to run the agent with no
skills.
Capability tools that a skill brings count toward hostDependencies. A
skill’s sites are attributed to the skill, with ownerName set to
skill:<slug>.
Managed external-agent exports
An exported external agent with skillOrchestration: 'managed' is
self-contained. At execution, the runtime turns the agent-card skills that the
agent uses as tools into A2A runtime tools. It then runs the configured
skillOrchestrator model through the standard agent loop. To give the
orchestrator more tools, pass them in
managedSkillOrchestration.additionalTools.
The export never embeds external credentials as plaintext. It handles them as follows:
- If the credential was already an exact
{{secret:NAME}}reference, the export keepsNAME. - If Runtype stored the credential, the export writes the reference
{{secret:RUNTYPE_AGENT_EXTERNAL_AUTH_AGENT_ID}}, whereAGENT_IDis the agent’s ID.
Provide that secret name through your runtime adapter’s getSecret and
resolveSecretReferences methods, mapped to the matching secret in your own
secret store.
Deploy an agent with the CLI
The runtype deploy command exports one or more agents and writes a
deployment project for Cloud Run, Cloudflare Workers, or Vercel Functions. It
signs in with your stored CLI API key, which needs the RUNTIME:EXPORT scope.
To scaffold a Cloudflare Workers deployment, run this command:
The command accepts these options:
--agent: ID of an agent to export. Repeat the option to deploy several agents.--target:cloud-run,cloudflare, orvercel. Defaults tocloud-run.--output: directory for the project. Defaults to./runtype-deploy.--name: project name forpackage.json. Defaults to the output directory name.--alias: export the version that this release alias points at for each agent, such asstaging.--version-id: export this published version. Requires exactly one--agent. You can’t combine it with--alias.
To build a staging image from the version behind the staging alias, run this
command:
For each agent, the command prints the version it exported and, when an alias selected it, the alias and its revision.
When it finishes, runtype deploy prints the next steps for your target: run
./setup.sh in the output directory, then the local test and deploy commands.
The script builds @runtypelabs/runtime and installs dependencies. The package
is not published to npm, so the script builds and packs it from the Runtype
source checkout that contains the directory where you ran runtype deploy.
The generated server registers a streaming POST /agent/AGENT_NAME endpoint
for each agent, where AGENT_NAME is the agent’s name as it appears in
Runtype. GET /health lists the registered agent names.
runtype deploy scaffolds agents only. To deploy a flow, attach it to an
agent as a flow runtime tool (toolType: 'flow' with config.flowId), then
deploy that agent.
Provide secrets and provider keys
The runtime reads each secret through the adapter’s getSecret method. This
covers model provider keys such as OPENAI_API_KEY, {{secret:NAME}}
references, and MCP secret names in the export. The Cloudflare, Cloud Run, and
Vercel adapters read each name from the deployment’s environment. A Cloudflare
Worker reads it from its env bindings.
runtype deploy lists the required secrets in the generated README.md, with
a command to set each one, and prints the list when it finishes. The following
commands set a secret for each target:
- Cloudflare Workers:
wrangler secret put SECRET_NAME - Vercel Functions:
vercel env add SECRET_NAME - Cloud Run:
gcloud run deploywith--set-secrets, or an exported environment variable for local runs
Replace SECRET_NAME with a name from the list. A deployment without a
required secret fails on its first model or MCP call.
Run an export in your own code
To host an export without the scaffold, load the export JSON, create a runtime, and call it. The following sample runs an exported agent on Node:
The runtime registers each agent under its key in agents, and executeAgent
resolves to a streaming Response. For a flow export, pass flows and call executeFlow.
RuntimeToolExecutor runs the agent’s HTTP, MCP, A2A, sub-agent, and flow
tools. Pass it to createRuntime as toolExecutor. Without it, every tool call
returns an error to the model, and the run still ends with execution_complete.
The runtime and the tool executor share one RuntimeContextStepExecutor, so the
steps inside a flow tool, such as tool-call and record steps, run the same way
as the agent’s own steps. Pass stores and providers, such as recordStore,
memoryStore, or a sandbox for code steps, to RuntimeContextStepExecutor
instead of createRuntime. To run custom code tools, also pass a sandbox to
RuntimeToolExecutor.
The keys.mode setting chooses how the runtime pays for model calls:
own: The runtime calls each provider directly with the provider key thatgetSecretreturns.platform: The runtime sends every model call to the Runtype platform-key proxy.hybrid: The runtime uses your key for the model’s direct provider when one is set, and falls back to the proxy when it’s missing. A model family with no direct provider in the runtime, such asglm-5.2, always uses the proxy in this mode, even whenVERCEL_AI_GATEWAY_API_KEYis set.
The platform and hybrid modes require keys.platformKeyEndpoint, set to
https://api.runtype.com/v1/runtime/proxy, and keys.platformApiKey, set to
a Runtype API key with the RUNTIME:EXECUTE scope. To create one, select
Self-hosted Runtime in the Permission Template list of the
Create New API Key dialog. For the scope each hosted endpoint requires, see
Hosted capability endpoints.
Declare host-owned agent settings
Some agent settings need your host to enforce them. The runtime rejects an
agent that uses one of these settings before it streams, unless you pass the
setting’s name in handledAgentConfig on each executeAgent call:
The following table lists each value and the agent configuration that requires it:
An agent that runs with a model and system prompt doesn’t need artifacts
declared. Declare a value only after your host implements that behavior.
Declaring tenancyStrategy tells the runtime that hostContext carries the
identity your host admitted. The runtime then enforces the strategy against that
identity before the first model call. For details, see
Record storage and tenancy scoping.
Nested calls don’t need temporal declared. An execute-agent flow step and
an attached subagent tool have no previous turn, so the runtime applies
config.temporal itself: groundNow adds the current time to the delegated
message, and injectElapsed adds nothing. A detached subagent tool still
rejects config.temporal, because your BackgroundRunCoordinator runs that
child.
For an agent that runs with a model and system prompt, the runtime also
rejects config.tools.perToolLimits, and no declaration changes that. Remove
per-tool limits from such an agent before you export it.
If an agent fails before it streams, catch UnsupportedAgentConfigError and
read its unsupportedPaths property. It names each configuration path, such as
config.memory, that the runtime couldn’t accept.
Serve SDK clients that run local tools
An SDK client drives an exported agent whose tools are toolType: 'local'
through POST /v1/dispatch, a paused envelope, and
POST /v1/dispatch/resume. RuntimeToolExecutor raises
ClientToolRequiredError for such a tool. A custom ToolExecutor receives
local tools like any other tool.
The @runtypelabs/runtime/host/dispatch module provides that host as a WHATWG
fetch handler. It parks each local tool call in memory, answers with the same
events that the Runtype API streams, and continues the turn when the resume
arrives. To use it, point an unchanged SDK client at the host by changing the
client’s base URL. When you set upstream, the host forwards any request that
it does not handle to the Runtype API with the caller’s API key.
The following sample creates a dispatch host that serves one set of exported agents:
Replace the following:
AGENTS: the exported agents, keyed by agent name, as in Run an export in your own code.HOST_TOKEN: an environment variable that holds the token your SDK clients send.
The host does not validate the API key that the SDK client sends, so it
refuses every request with 401 until you set authorize. Pass your own
check, or the literal 'allow-unauthenticated' for a host that only a trusted
network can reach.
The host also accepts these options:
checkpointStore: ACheckpointStorethat makes local-tool pauses durable, so a resume works after a process restart.concurrency: How the host handles overlapping turns in one conversation:reject,supersede, orqueue. Thex-runtype-concurrencyrequest header overrides it.executeOptions: Timeouts and other options that the host passes to everyexecuteAgentcall, includinghandledAgentConfig. For the settings thathandledAgentConfigdeclares, see Declare host-owned agent settings.resolveHostContext: A function that returns the end user and tenant for one request. For details, see Give each request its own end user.
Give each request its own end user
Tools that reference {{_endUser.*}} or {{_tenant.*}}, and tools that send
{{_identity.token}}, need a verified identity for each request. The host
ignores the endUser, tenant, and identityProof fields in the dispatch
body, because any caller that passes authorize can set them. To admit an
identity, verify the caller in resolveHostContext and return the identity as
a hostContext:
Replace verifySession with your own check. It receives the request and the
parsed dispatch body.
The function can return one of the following values:
- A
hostContextobject: The host merges it overexecuteOptions.hostContextkey by key. A key that you omit keeps the host-wide value. undefined: The turn runs withexecuteOptions.hostContextonly.- A
Response: The host returns that response and does not start the turn.
If the function throws, the host returns 500 with the code
HOST_CONTEXT_RESOLUTION_FAILED.
The host calls resolveHostContext once, when the turn starts. A resume runs
with the context that the turn started with, including the host-wide part, even
if executeOptions.hostContext has changed since. With a
checkpointStore, the host saves that identity in the checkpoint, so a resume
after a restart also uses it. To make {{_identity.token}} name the end user
or tenant, set assurance on each identity, and pass an identity signer to
your adapter as mintIdentityAssertion.
The same identities satisfy an agent’s tenancyStrategy. The runtime reads the
assurance that you set on each identity when it checks the strategy’s
assurance floor. If hostContext doesn’t carry the scope that the strategy
requires, the turn fails with TenancyLockdownRefusedError before the first
model call. For details, see
Record storage and tenancy scoping.
The following sample serves an agent whose tenancyStrategy uses the
end-user-isolated preset and whose tools send {{_identity.token}}. It
combines the three pieces that such an agent needs: resolveHostContext admits
the end user, executeOptions.handledAgentConfig declares that the host
enforces tenancyStrategy, and mintIdentityAssertion signs the token.
Replace signIdentity with your identity signer and verifySession with your
own check. Without handledAgentConfig, every turn of the agent fails with
UnsupportedAgentConfigError. If resolveHostContext admits no end user, the
turn fails with the code end_user_scope_required before the first model call.
A JSON response carries that code with status 403. A streamed response has
already sent status 200, so it carries the code in an execution_error event.
The host does not serve POST /v1/dispatch/approve or history: 'stored'
turns. It persists no transcript, so a stored-history dispatch returns
STORED_HISTORY_UNSUPPORTED. Send the full conversation in messages, or keep
that conversation on the Runtype API.
Execution timeouts
Set execution deadlines on each executeFlow or executeAgent call:
stepTimeoutMslimits one step attempt. An authored fallback retry receives a fresh attempt budget.flowTimeoutMslimits each virtual-flow, reflection, or final-reply turn. Each turn receives a fresh relative budget. For a delegated external agent, it limits the whole A2A call, including retries.wallClockTimeoutMsis the hard budget for the whole agent invocation. Every primary, reflection, and final-reply turn receives only the execution’s remaining time, and the runtime releases the caller even when a provider or tool promise ignores cancellation. Expiry during a primary turn fails the execution withAGENT_TURN_DEADLINE_EXCEEDED. Expiry between completed turns remains a normalstopReason: 'timeout'loop stop. Delegated external agents apply the same hard cap to their A2A call. When it andflowTimeoutMsboth apply, the earlier finite deadline wins.
An external-agent input checkpoint persists flowTimeoutMs. The resumed peer
turn receives a fresh relative budget, so time spent waiting for human input is
not charged. To turn off only flowTimeoutMs, pass Infinity.
Hosted capability endpoints
The exported runtime uses these hosted endpoints for platform capabilities.
Authenticate with a Runtype API key in the Authorization: Bearer header. The
API derives tenant identity from the authenticated API key, not from request
body fields.
The following table lists the hosted endpoint families, their capabilities, and the API key scope each one requires:
The Self-hosted Runtime permission template grants RUNTIME:EXECUTE and
TELEMETRY:WRITE, which covers every endpoint in the table except
POST /v1/runtime/assets. RUNTIME:EXECUTE lets a key call these endpoints and
nothing else. Unlike FLOWS:EXECUTE or AGENTS:EXECUTE, it can’t run your
hosted Flows or Agents.
Keys created before RUNTIME:EXECUTE existed keep working. The proxy, memory,
records, judgments, and extractions endpoints also accept FLOWS:EXECUTE or AGENTS:EXECUTE, and the
crawl, email, and search endpoints also accept FLOWS:EXECUTE. Runtype records
each call that one of these older scopes admits, so plan to move such keys to
RUNTIME:EXECUTE.
Until November 9, 2026, POST /v1/runtime/proxy still accepts an API key without an execution
scope, such as a key with only TELEMETRY:WRITE. It returns the x-runtype-warning response
header, and the runtime logs that warning once per process. From November 9, 2026, the proxy
refuses such a key with HTTP 403. Add RUNTIME:EXECUTE to the key before then.
Every /v1/runtime/* endpoint requires an Enterprise plan and
returns HTTP 403 with BYOC_PLAN_REQUIRED for an account without one. For
the full request and response schemas, see the
API Reference.
POST /v1/runtime/crawl serves a synchronous crawl only. It is not a host for
the crawl flow step, which parks a durable pause and declares a
durable-pause-host dependency instead. For more information, see
Host dependencies.
Use an API key with RUNTIME:EXECUTE for proxy, memory, records, judgments,
extractions, crawl, email, and search requests. Use an API key with ASSETS:WRITE for
POST /v1/runtime/assets.
The public POST /v1/assets endpoint is separate from self-hosting and is
available on every plan. The runtime endpoint, POST /v1/runtime/assets,
requires an Enterprise plan.
Report telemetry
RuntimeConfig.telemetry reports the deployed runtime’s executions to Runtype
as OpenTelemetry traces, posted to POST /v1/otel/v1/traces. Reporting is not
part of the hosted endpoint families and does not require an Enterprise plan.
A reported run reaches the same fidelity as one Runtype ran itself: it appears in the Runs view with its trace tree, transcript, tool arguments and results, token usage, and a display-only cost estimate, and it can be captured as an eval case.
The scaffolds that runtype deploy generates read the same settings from the
environment, so you can change them without editing code. A Cloudflare Worker
reads them from its env binding instead of process.env.
Use a separate API key with the TELEMETRY:WRITE scope. To create it, select
Telemetry Ingest in the Permission Template list of the Create New
API Key dialog. Ingest also accepts keys with AGENTS:WRITE, but ingest is
append-only, so give this API key no other scopes.
Use a separate API key for the hosted endpoints. A shared API key also needs the execution scopes for those endpoints, which gives it more access than telemetry requires. If you use one key for both, create it from the Self-hosted Runtime template.
POST /v1/telemetry/ingest is the deprecated lifecycle endpoint. It carried pipeline rows only:
no run, no trace tree, and no transcript. The runtime posts to it only when you set
telemetry.endpoint explicitly, which no current deployment needs. Remove that field to report to
the OpenTelemetry endpoint.
Redacting PII from telemetry
Telemetry payloads carry step output, tool results, and agent completions, so
they can contain end-user personal data. Set telemetry.piiRedaction to
'redact' to have Runtype mask detected PII (emails, phone numbers, and the
rest of the detector set) before it is stored:
An individual agent’s config.piiRedaction takes precedence over this
deployment-wide setting, in both directions: an agent exported with
piiRedaction: 'redact' is redacted even when the deployment sets nothing, and
an agent exported with piiRedaction: 'off' is not redacted even when the
deployment sets 'redact'. 'default' on an agent defers to the deployment
setting.
If an exported agent sets piiRedaction: 'redact', pass
handledAgentConfig: ['piiRedaction'] on each executeAgent call. Without
it, the runtime rejects the agent before it streams. For more information, see
Declare host-owned agent settings. The
deployment-wide telemetry.piiRedaction setting needs no declaration, because
it is not agent config.
The runtime resolves the policy per execution and sends it with each event, so one deployment can run redacted and unredacted agents side by side. Omitting both settings leaves redaction off. Secret redaction is always applied and is not affected by this setting.
If you need to report on an agent that runs entirely outside Runtype, see Reporting telemetry from an external agent.
Configure runtime providers
Some steps need a provider that you supply in the createRuntime config. No
hosted /v1/runtime/* endpoint handles managed fetch, embedding models outside
the built-ins, or collection validation.
Steps that reach an unconfigured provider
If you omit a provider, createRuntime uses a no-op default that raises a
ProviderNotConfiguredError, such as its subclass
RecordStoreNotConfiguredError. The error’s providerKind names the
RuntimeConfig field to set. The following table shows how a step’s
errorHandling setting handles that error:
When errorHandling is unset, a failed step’s metadata carries
providerNotConfigured with the provider kind. An explicit 'fail' stops the
flow without adding this metadata field. Without errorHandling, a missing
provider can’t look like a successful step.
The following table lists the steps and the provider each one needs:
Other steps handle a missing provider in their own way:
get-recordandlist-recordsreturnsuccess: falsefor anyerrorHandlingsetting.generate-embedding,store-vector, andvector-searchreturnsuccess: falseunless you seterrorHandlingto'continue'.- A
templatestep without atemplateRendererreturnssuccess: falsefor anyerrorHandlingsetting. - A
transform-datastep without asandboxreturnssuccess: truewithdefaultValueunless you seterrorHandlingto'fail'.
Firecrawl and Massive fetch methods
Standard HTTP requests in fetch-url steps run through the runtime adapter.
For Firecrawl or Massive, supply a ManagedFetchExecutor through
RuntimeConfig.managedFetchExecutor. The executor resolves credentials,
enforces provider policy, and sends the requests.
If you omit ManagedFetchExecutor, the default context-step executor returns a
not-supported error instead of calling the provider. With errorHandling not
set, the step returns success: false. For details, see
Steps that reach an unconfigured provider.
If you supply a custom contextStepExecutor, that executor handles the
managed-fetch integration.
Embedding models beyond the built-ins
generate-embedding steps and the query embedding inside vector-search use
the runtime adapter for these OpenAI and Google model IDs:
text-embedding-3-small, text-embedding-3-large, text-embedding-ada-002,
text-embedding-004, and gemini-embedding-001. The adapter resolves
OPENAI_API_KEY or GOOGLE_API_KEY for these models.
For any other embedding model ID, supply an EmbeddingProvider through
RuntimeConfig.embeddingProvider. This includes Weaviate vectorizer IDs such
as text2vec-openai, text2vec-cohere, text2vec-huggingface, and the
Snowflake/snowflake-arctic-embed-* family. Your Weaviate instance produces
these vectors, so the export does not include their credentials or
configuration.
If you omit an EmbeddingProvider, the affected steps fail with
EmbeddingProviderNotConfiguredError and name the model. They do not fall
back to another model. Built-in models continue to use their native provider
when you configure an EmbeddingProvider.
For tests and offline runs, use StaticEmbeddingProvider. It is not a real
embedder. It returns a vector derived from a hash of the input text.
Inspect each step’s embeddingModel before deployment. A flow can use a built-in model in one
step and require an EmbeddingProvider in another.
Decision judgments
The Decision judgment tool (builtin:decision_judge) runs in the runtime, as
an agent tool and from a tool-call step. A configured instance judges with
the question set that the export embeds in the tool’s config.questionSet.
For what the export embeds, the
credentials that each decision model needs, and how each key mode routes a
judgment, see
Run judgments in a self-hosted runtime.
To judge with your own implementation, supply a DecisionProvider through
RuntimeConfig.decisionProvider. It implements judge(request). To judge a
configured instance whose question set the export didn’t embed, it can also
implement judgeByReference(request). For tests and offline runs, use StaticDecisionProvider. It makes no network
call and answers each boolean question with 0.9, each choice question
with its first option, and each score question with its highest level. To
set an answer, pass a map of question name to answer to its constructor.
If you set no decisionProvider and the key mode gives the runtime no
credential and no platform key for the model, the tool call fails with
DecisionProviderNotConfiguredError. The message names the secret or binding
that the model needs. The agent receives the error as a failed tool call, and
the run continues.
If you pass a builtinToolExecutor to RuntimeToolExecutor, that executor
runs the Decision judgment tool and the runtime doesn’t use a
DecisionProvider.
Extract step
The extract step reads typed fields through the runtime’s
ExtractionProvider. If you set no extractionProvider, the key mode picks
one. With own keys, the runtime extracts with the Workers AI binding that
adapter.getBinding("AI") returns. With platform keys, it calls
POST /v1/runtime/extractions. With hybrid keys, it tries the binding first
and then Runtype. For tests and offline runs, use StaticExtractionProvider.
It makes no network call and answers each field with its override, or null.
If no provider can serve the step, it fails with
ExtractionProviderNotConfiguredError, and errorHandling applies as described in
Steps that reach an unconfigured provider.
For the models and key modes, see
Self-hosted runtimes.
Record storage and tenancy scoping
The record steps, including upsert-record, get-record, list-records, and
update-record, use RuntimeConfig.recordStore. The runtime includes
InMemoryRecordStore for stateless agents and tests, and HostedRecordStore
for the hosted POST /v1/runtime/records/* endpoints. The default
NoOpRecordStore raises RecordStoreNotConfiguredError when a record step
runs. With errorHandling not set, the step returns success: false.
If an exported flow or agent includes tenancyStrategy, the runtime enforces
it on every top-level executeAgent and executeFlow call. An agent also needs
tenancyStrategy in handledAgentConfig. Without it, the runtime rejects the
agent with UnsupportedAgentConfigError.
Pass the identity that your host verified for the execution as
hostContext.tenant and hostContext.endUser. Before the first model call,
the runtime checks that identity against the strategy’s preset:
internal: No identity is required. Records use the product scope.tenant-isolated: RequireshostContext.tenant. Records are scoped to that tenant.end-user-isolated: RequireshostContext.endUser. Records are scoped to that end user, withinhostContext.tenantwhen you pass one.
The runtime reads each identity’s assurance level from
hostContext.assurance.tenant or hostContext.assurance.endUser. If you don’t
set that, it uses the assurance field on the identity object itself, such as
endUser: { id: 'u_123', assurance: 'asserted' }. An identity with neither
counts as verified, because your host admitted it. A strategy’s assurance
floor is verified unless the strategy lowers it to asserted.
When the check fails, the call rejects with TenancyLockdownRefusedError and
makes no model call. The error’s code property is one of the following values:
tenant_scope_required: The strategy requires a tenant, andhostContexthas none.tenant_assurance_below_floor: The tenant’s assurance is below the floor.end_user_scope_required: The strategy requires an end user, andhostContexthas none.end_user_assurance_below_floor: The end user’s assurance is below the floor.
The runtime derives the record scope from the strategy and the admitted
identity. It keys each scope by the identity’s projectedId, or by its id
when projectedId is absent. An agent execution passes its scope to the flows
that it runs. For an agent with tenancyStrategy, the runtime doesn’t read
_tenant or _endUser from the variables that the caller passes. These
values come only from hostContext.
If your host runs its own tenancy check and resolves a record scope itself,
pass that scope as ExecuteAgentOptions.recordScope or
ExecuteFlowOptions.recordScope. The runtime then skips its own check and uses
your scope unchanged. A null scope makes every record step fail instead of
falling back to the global namespace.
Your RecordStore must also support scopes. Set supportsRecordScope = true
on your implementation, and honor the scope descriptor on every call. Filter
every read, including the update-record target lookup and the record input of
generate-embedding. Add the scope columns on insert. Partition upsert conflict
resolution by scope, so that two tenants can each have a record with the same
(type, name).
If an execution carries a resolved scope but the store does not declare support for it, the record
step fails with RecordScopeUnsupportedError. The step does not read or write the global
namespace. HostedRecordStore does not declare record-scope support because the hosted API does
not accept a scope descriptor, so the server cannot verify the execution’s tenant.
If you omit tenancyStrategy, the runtime uses the plain owner namespace.
Collection schema validation
If you register a record type as a collection with a metadata schema, the
upsert-record and update-record steps call
RuntimeConfig.collectionValidator before each write. upsert-record merges
on conflict inside the store, so validation checks the incoming payload for
each batch item. update-record reads the target row first, so validation
checks the post-merge metadata.
The validator exposes two methods:
validate(type, metadata): Returns the verdict for writingmetadataas a record oftype. It does not throw. If the collection lookup fails, it returns{ status: 'lookup_failed', type }. Do not returnunevaluatedfor a failed lookup, because the write would then skip validation.prime(type): Warms the collection lookup without validating data. Call it before validation inside an open transaction. A cold lookup in that transaction can deadlock a single-session driver. If the lookup fails duringprime, keep returninglookup_failedfor that type so the transaction does not query again.
With an enforce or lookup_failed verdict, the step rejects the write. A
lookup_failed rejection contains COLLECTION_LOOKUP_FAILED. The step’s
errorHandling setting controls the failure, as it does for a payload-limit
violation. With a warn verdict, the step writes and adds schemaWarnings to
the step metadata. The upsert-record batch path does not report warnings.
The verdict is stored in the schemaValid write field. A store that persists
that field records the same validation result as Runtype. For validation modes
and violation details, see Defining a schema.
The hosted record endpoints do not evaluate collection schemas. If your self-hosted deployment needs enforcement, supply a validator backed by your collection definitions.
If you omit the validator, every verdict is unevaluated. Writes proceed without a schemaValid
stamp. This behavior is intentional because validation is an owner-configured contract for
metadata shape, not a security boundary. A collection set to enforce in the dashboard is not
enforced by an exported flow or agent until you wire a validator.
Next steps
Continue with these guides:
- Runtime tools: attach a flow to an agent as a runtime tool
- Reporting telemetry from an external agent: report runs from agents that Runtype didn’t author
- Agent versioning and publishing: control which sub-agent version an export captures
- API Reference: inspect full request and response schemas
- Logging policy and PII redaction: control verbose logging and PII redaction, and see which setting wins