Self-hosting (BYOC)

Self-hosting lets you export a saved flow or agent as a self-contained @runtypelabs/runtime definition and run it on your own infrastructure (Cloudflare Workers, Cloud Run, Node, Vercel Functions, anywhere a runtime adapter is supplied). The exported runtime still reaches back to Runtype for the platform capabilities it needs — model execution through the platform-key proxy, plus hosted memory, crawl, email, search, and asset storage.

Self-hosting (BYOC, bring your own cloud) is an Enterprise-plan capability. The export endpoints and every hosted /v1/runtime/* endpoint below are documented for everyone, but calls from an account that is not on an Enterprise plan return HTTP 403 with the machine code BYOC_PLAN_REQUIRED. Contact support to enable self-hosting for your account.

Exporting a flow or agent

The export endpoints are in limited preview: they are dark-launched and return 404 Not Found in production until general availability. They are reachable in non-production environments today.

Export a saved flow or agent version as a runtime definition:

Export a flow
$curl https://api.runtype.com/v1/flows/FLOW_ID/export-runtime \
> -H "Authorization: Bearer YOUR_API_KEY"
Export an agent
$curl https://api.runtype.com/v1/agents/AGENT_ID/export-runtime \
> -H "Authorization: Bearer YOUR_API_KEY"

The response is a self-contained runtime definition you vendor into your own deployment. On an ineligible plan both endpoints return:

1{
2 "error": "Feature Not Available",
3 "message": "Self-hosting (BYOC) requires an Enterprise plan. Contact support to enable it for your account.",
4 "code": "BYOC_PLAN_REQUIRED"
5}

Hosted capability endpoints

A standalone runtime authenticates to these endpoints with an ordinary Runtype API key (Bearer token) and reaches the same platform capabilities its flow/agent steps use inside Runtype. Tenant identity is always taken from the authenticated key — identity fields in the request body are ignored.

EndpointCapability
POST /v1/runtime/memory/*Hosted MemoryStore (save / recall / summary)
POST /v1/runtime/records/*Hosted RecordStore (upsert / get / query / update)
POST /v1/runtime/crawlHosted SiteCrawler (synchronous crawl)
POST /v1/runtime/email/sendHosted EmailSender (send a rendered email)
POST /v1/runtime/searchHosted WebSearcher (Exa search)
POST /v1/runtime/assetsHosted AssetStore (store an asset)

Each requires an Enterprise plan and returns the same 403 (BYOC_PLAN_REQUIRED) for ineligible accounts. See the API Reference for the full request and response schema of each endpoint.

Where a key is permission-scoped, these endpoints expect an execute scope (FLOWS:EXECUTE or AGENTS:EXECUTE); POST /v1/runtime/assets expects ASSETS:WRITE.

Three capabilities have no hosted endpoint and must be supplied by the host instead: Firecrawl and Massive fetch methods, embedding models beyond the built-ins, and collection schema validation.

Reporting telemetry

POST /v1/telemetry/ingest is separate from the table above. It is not Enterprise-gated, and it is what RuntimeConfig.telemetry posts to so a deployed runtime’s executions appear in the Runtype dashboard.

Give it a key scoped to TELEMETRY:WRITE, minted from the “Telemetry Ingest” preset in the API-key dialog. Ingest is append-only, and this is the credential a deployment tends to copy the furthest, so it should grant push and nothing else.

Make it a separate credential from the one your runtime uses for the hosted endpoints above. A single key serving both still needs the execute scopes those endpoints require, which defeats the point of scoping the telemetry key at all. Two keys cost nothing and the telemetry one can then be handled far more casually than the other.

The endpoint still accepts any valid API key, so an existing deployment keeps reporting after an upgrade. Keys without the scope are recorded server-side so we can tell when the allowance is no longer needed.

If what you want to report on is an agent that runs entirely outside Runtype rather than an exported one, see Reporting telemetry from an external agent, which covers the OpenTelemetry path.

The public POST /v1/assets first-class asset endpoint is not part of self-hosting and is available on every plan — only its runtime-family mount (POST /v1/runtime/assets) is Enterprise-gated.

Firecrawl and Massive fetch methods

fetch-url steps using standard HTTP run through the runtime adapter. Firecrawl and Massive are managed-provider methods: a self-hosted runtime must supply a ManagedFetchExecutor through RuntimeConfig.managedFetchExecutor to resolve credentials, enforce provider policy, and perform those requests.

There is no hosted /v1/runtime/* endpoint for managed fetch. If the seam is omitted, the default context-step executor returns an actionable unsupported error instead of attempting the provider call. If you supply a custom contextStepExecutor, that executor owns the managed-fetch integration itself.

Embedding models beyond the built-ins

generate-embedding steps, and the query embedding inside vector-search, embed natively for the OpenAI and Google model families (text-embedding-3-small, text-embedding-3-large, text-embedding-ada-002, text-embedding-004, gemini-embedding-001). Those run through the runtime adapter and need nothing beyond the provider key your adapter resolves (OPENAI_API_KEY / GOOGLE_API_KEY).

Any other embedding model id is a host capability: supply an EmbeddingProvider through RuntimeConfig.embeddingProvider. Inside Runtype this is how the Weaviate-vectorizer models (text2vec-openai, text2vec-cohere, text2vec-huggingface, and the Snowflake/snowflake-arctic-embed-* family) are served — their vectors are produced by the customer’s own Weaviate instance, so the credentials and configuration cannot travel with an export.

There is no hosted /v1/runtime/* endpoint for embedding. If the seam is omitted, those steps fail with an actionable EmbeddingProviderNotConfiguredError naming the model, rather than falling back to a different model. Built-in models keep first refusal, so wiring a provider never changes how an OpenAI or Google model is embedded. For tests and offline runs the runtime also exports StaticEmbeddingProvider, which is not a real embedder — its vector is a hash of the input text.

Check your exported definition for a non-built-in embeddingModel before you deploy. The model id is authored per step, so a flow can embed natively in one step and require the seam in another.

Record storage and tenancy scoping

The record steps (upsert-record, get-record, list-records, update-record) read and write through RuntimeConfig.recordStore. The runtime ships InMemoryRecordStore for stateless agents and tests, and HostedRecordStore, a client of the hosted POST /v1/runtime/records/* endpoints. The default is NoOpRecordStore, whose methods throw an actionable error so a record step fails loudly instead of silently dropping data.

An exported flow or agent that carries a tenancyStrategy needs two things from the host, and neither happens automatically:

  1. A scope-capable store. Declare supportsRecordScope = true on your RecordStore and honor the scope descriptor on every call: filter every read (including the update target lookup), stamp the scope columns on insert, and partition upsert conflict resolution by scope so the same (type, name) under two tenants stays two rows.
  2. A resolved scope per execution. Resolve tenant and end-user identity yourself and pass the result as ExecuteAgentOptions.recordScope when calling executeAgent, or as ExecuteFlowOptions.recordScope when calling executeFlow directly for a standalone flow export. The engine threads the agent-lane value onto the flows an agent runs internally, but a top-level executeFlow call must set the option itself; omitting it runs the flow in the plain owner namespace. Runtype’s identity resolution does not travel with an export, which is also why tenancyStrategy must be listed in handledAgentConfig before the runtime will accept the agent.

If an execution carries a resolved scope but the wired store has not declared the capability, the record step fails closed with RecordScopeUnsupportedError rather than reading or writing the global namespace. HostedRecordStore deliberately does not declare it: the hosted wire carries no scope descriptor, so the server cannot verify which tenant the execution was admitted under.

A deployment with no tenancy strategy is unaffected. An omitted scope means the plain owner namespace, exactly as before.

Collection schema validation

If a record type is registered as a collection with a metadata schema, the upsert-record and update-record steps consult RuntimeConfig.collectionValidator before every write. upsert-record merges on conflict inside the store, so it validates the incoming payload (per item in batch mode). update-record reads the target row first, so it validates the post-merge metadata and its verdict is exact.

The seam has two methods:

  • validate(type, metadata) returns the verdict for writing metadata as a record of type. It never throws: a lookup failure degrades to unevaluated.
  • prime(type) warms the collection lookup without validating anything. Call it before validating inside an open transaction, which is what the upsert-record batch path does. A cold lookup issued while a single-session driver holds a transaction open deadlocks.

An enforce verdict rejects the write, and the step’s errorHandling setting governs what the flow does with the failure, exactly as it does for a payload-limit violation. A warn verdict lets the write through and surfaces schemaWarnings on the step metadata (the batch branch of upsert-record reports no warnings, matching how Runtype’s own batch writes behave). Either way the verdict rides the store’s schemaValid write field, so a store that persists that field records the same stamp Runtype does. See Defining a schema for the validation modes and the shape of a violation.

There is no hosted /v1/runtime/* endpoint for collection validation, and the hosted POST /v1/runtime/records/* endpoints do not evaluate collection schemas server side. A self-hosted deployment that needs enforcement supplies its own validator, backed by whatever collection definitions it controls.

If the seam is omitted, every verdict is unevaluated: writes proceed exactly as they did before the seam existed and no schemaValid stamp is recorded. This direction is deliberate, because validation is an owner-configured contract on metadata shape rather than a security boundary. It does mean a collection set to enforce in the dashboard is not enforced by an exported flow or agent until you wire a validator.

Next steps