createJob() registry and every onTrigger closure execute inside a Node process you operate, behind your network, with your environment variables and secrets.
If you want to run the control plane in your network as well, see Self-hosting the control plane. For an overview of all three deployment options, see Hosting overview.
Self-hosting the data plane is about where handlers execute, not about running your own event processor. Trigger ingestion, webhook URLs, scheduling, integration auth, signing, retries, and run
history all stay on the Terse Cloud control plane.
When the Hybrid deployment is the right choice
Pick this when your workflow needs something only your environment can provide.
If none of those apply, the default Managed deployment is simpler. There is nothing to operate, and triggers, scheduling, and runs are fully managed.
Setting it up
1
Attach the project
From the root of your existing repo, run:This authenticates, links the repo to a Terse project, writes
terse.config.json with the self-hosted data plane enabled, and prints TERSE_PROJECT_KEY and TERSE_SIGNING_SECRET once. Save both into your data plane environment before you start the server. See terse attach for the full flow.2
Point Terse at your server
Set
remoteServerUrl to the public URL of your service:terse.config.json
3
Set the required environment variables
Your data plane needs two values, both printed by Load both into your process the same way you load any other secret —
terse attach exactly once. They are always issued as a pair: if your project is missing either one, the next terse attach replaces both. Lost them? Re-run terse attach and confirm the replacement prompt, or rotate from the project’s page in the dashboard. Copy them into your environment before you start the server:.env
TERSE_API_KEY is a separate credential: your own user token, used by the CLI to deploy and manage integrations. Keep it out of your data plane environment, and do not put a project key in it..env for local development, your platform’s secret store in production.4
Mount the trigger handler
Expose the SDK’s webhook handler on your HTTP server so the control plane can deliver events to your data plane. Mount it at The side-effect import of
TERSE_JOB_WEBHOOK_TRIGGER_PATH so URLs match what the platform expects.src/server.ts
./terse.jobs (or whichever entry file holds your createJob() calls) is what makes the registry available at request time. handleTrigger reads TERSE_SIGNING_SECRET and TERSE_PROJECT_KEY from the process env on every call. See Terse in the SDK reference for the full client surface.5
Deploy your code, then sync workflows
Ship your data plane the way you normally ship Node code (Render, Fly, ECS, your own Kubernetes, …). Once it’s reachable at The CLI reads
remoteServerUrl, run:remoteServerUrl, registers your workflows on the control plane, and routes every future trigger to your server.Identity verification
handleTrigger verifies every incoming request against TERSE_SIGNING_SECRET and rejects anything older than 5 minutes or with a mismatched signature. Failing requests never reach your handler.
If you parse the request body before calling
handleTrigger (for example with express.json()), the SDK re-serializes it with JSON.stringify to recompute the signature. Don’t mutate req.body
between parsing and the call, or verification will fail.Who runs what
When a trigger fires, the control plane signs the event and POSTs it to your data plane. Your server verifies the signature with
handleTrigger, looks up the registered workflow by name, and runs your onTrigger closure in your process. Runs, waitForInput pauses, and history all keep working.
What stays the same
Even though execution moves into your infrastructure, the only difference is the box youronTrigger runs inside. createJob(), step(), waitForInput, runs, and the Activity tab all keep working as they do for managed workflows.
Where to go next
Hosting overview
The control plane / data plane split and the three deployment options.
Self-host the control plane
Run the orchestrator in your network with
npx create-terse.terse attach
CLI reference for linking an existing repo to a self-hosted data plane.
Terse client
SDK reference for
handleTrigger and signed payload verification.