Skip to main content
Cargo hosts two kinds of custom code alongside your resources: workers (serverless edge HTTP handlers) and apps (Vite single-page apps served on *.app.getcargo.run). defineWorker and defineApp deploy a source bundle directory that Cargo Hosting builds on deploy.

Define a worker

workers/webhook.ts

Define an app

apps/dashboard.ts
Cargo validates the required bundle files exist at define time, so a missing file fails in plan. An app’s env vars must be plain strings — secret("NAME") works for workers only. A build compiles an app’s variables into the JavaScript bundle, which is then served publicly, so anyone who loads the app can read them; marking one secret would have hidden the value from your own workspace while still publishing it. Keep API keys and other credentials in a worker, and call that worker from the app. An app whose package.json build script does more than vite build has that script run on deploy instead of the default build — see search-engine indexing for the prerendering setup that relies on it. To serve an app on your own domain, declare it:
apps/website.ts
A declared domain serves nothing until its _cargo-verify TXT record resolves; pass website.domainRecords to defineDomain’s dnsRecords to publish every record in the same deploy. How paths map to pages is decided by the build — an export with <route>/index.html pages, like Next.js with trailingSlash: true, gets clean URLs on its own. See search-engine indexing for both.

Environment variables

A worker reads its variables from c.env, an app from import.meta.env.VITE_*. Both are baked in when the deployment is built, so a change needs a redeploy. Set them on the resource — through env above, or its Environment tab in the Cargo app — or once for the whole workspace under Settings → Environment, which every worker and app inherits without declaring anything. A key set on the resource overrides the workspace value; an app only inherits non-secret VITE_ keys, since its bundle is served to the browser. See Secrets & environments. From the CLI, set, list, and remove a worker’s variables with hosting worker env (an app’s with hosting app env-var). set creates the key or updates it in place; pass --value - to read the value from stdin, or omit --value to take it from your local environment, so a secret stays out of shell history:
A live deployment keeps the values it was deployed with: run hosting deployment create and promote it to pick up a change. Because the inheritance is automatic, a resource’s env takes env() or secret() and not workspaceEnv() — declaring a pointer here would only restate a variable the worker or app already receives. Leave a workspace variable out of env entirely.
defineWorker/defineApp are the deployable resource (the hosted slot). Author the worker’s runtime code in TypeScript with createWorker from @cargo-ai/worker-sdk at src/index.ts — bundles are uploaded as source and built server-side: the hosting build runs npm ci then esbuilds the worker entrypoint (esbuild transpiles TypeScript natively) or vite build for apps. A pre-built index.js at the bundle root is still accepted for backwards compatibility. Bundle sub-directories have their own package.json, so the loader treats them as content to upload, not resource files to import.

Cron triggers

A worker can be called on a schedule: each trigger describes the full request Cargo fires — cron (or Temporal’s @every shorthand), path, method (default POST), a JSON body, and headers. Set an authorization header and guard the route with standard Hono middleware (bearerAuth/basicAuth); ticks only fire once the worker has a promoted deployment. Triggers can be set from the worker’s page in the Cargo app, via defineWorker (above), or with the CLI/API:

Calling the Cargo API

Create a workspace API token and add it to the worker as a CARGO_API_TOKEN secret environment variable — the Cargo API host is allowed without an outboundAllowlist entry. createCargoApi(env) from @cargo-ai/worker-sdk returns the fully-typed @cargo-ai/api client:

Logs

A worker’s runtime logs hold every console.* call drained by createWorker() — an Error passed to console.error keeps its stack under attributes.error — and one error line for each invocation that crashed, timed out, or answered with an HTTP 4xx/5xx. Read them in the worker’s Logs tab, or from the CLI, newest first:
A route that catches its own error and returns a sanitized response logs only the HTTP line, so log the exception before you answer.

Custom integrations

A worker that serves the Custom Integration HTTP contract (createCustomIntegration from @cargo-ai/worker-sdk) can be registered in the connector catalog declaratively:

Scaffolding and deploying standalone

You can also scaffold and ship bundles directly with the CLI:
Slugs are kebab-case and must start with a letter (my-worker).