---
title: "Primitive Functions: Deploy, Route, and Manage"
canonical: "https://test.abhinandan.one/cli-overview/cli-functions"
markdown_url: "https://test.abhinandan.one/cli-overview/cli-functions.md"
publisher: "Primitive SDKs"
kind: "guide"
content_type: "reference"
category: "CLI"
parent: "cli-overview"
description: "Deploy a Primitive Function from a bundled handler or a source directory with primitive functions deploy, then bind a route and seed secrets."
keywords: ["primitive functions deploy", "primitive functions init", "functions-deploy --secret", "functions route-set", "primitive functions test --wait", "functions redeploy"]
last_modified: "2026-08-11T18:54:53.941092+00:00"
published_at: "2026-08-11T18:54:53.752499+00:00"
source_files:
  - "cli-node/src/oclif/commands/functions-deploy.ts"
sections:
  - {anchor: "scaffold-a-function", title: "Scaffold a function"}
  - {anchor: "deploy-from-a-bundled-file", title: "Deploy from a bundled file"}
  - {anchor: "step-build-a-self-contained-esm-bundle", title: "Build a self-contained ESM bundle"}
  - {anchor: "step-deploy-the-bundle", title: "Deploy the bundle"}
  - {anchor: "step-confirm-the-deploy-landed", title: "Confirm the deploy landed"}
  - {anchor: "deploy-from-a-source-directory-managed-build", title: "Deploy from a source directory (managed build)"}
  - {anchor: "wait-for-the-deploy-to-finish", title: "Wait for the deploy to finish"}
  - {anchor: "seed-secrets-in-the-same-deploy", title: "Seed secrets in the same deploy"}
  - {anchor: "redeploy-and-test", title: "Redeploy and test"}
  - {anchor: "bind-a-route", title: "Bind a route"}
  - {anchor: "manage-secrets-outside-of-deploy", title: "Manage secrets outside of deploy"}
  - {anchor: "command-reference", title: "Command reference"}
  - {anchor: "next-steps", title: "Next steps"}
---

> Documentation index: https://test.abhinandan.one/llms.txt

# Primitive Functions: Deploy, Route, and Manage

Scaffold, deploy, redeploy, test, and route Primitive Functions from the terminal, and manage their secrets and logs with the primitive functions-* command group.

Primitive Functions are serverless email-handler functions: a single ESM module whose default export implements `async fetch(request, env)`. Use the `primitive functions-*` command group to scaffold, deploy, test, route, and manage secrets and logs for them from the terminal, without leaving your shell. For the platform concept (routes, secrets, logs, test runs as first-class resources), see [Primitive Functions and Recipient Routing](https://test.abhinandan.one/functions-and-routing-concepts.md).

## Scaffold a function

Generate a starter project from an approved template before you write any handler code.

```bash
primitive functions templates
primitive functions init my-fn --template email-reply
cd my-fn && npm install && npm run build
```

`functions templates` lists the templates available to install. `functions init` scaffolds a project directory containing `package.json` and `src/`; `npm run build` produces the bundled handler your deploy commands consume.

## Deploy from a bundled file

`primitive functions deploy` creates and deploys a function from a pre-built ESM bundle on disk, as the agent-grade shortcut for the generic `functions:create-function` operation. It reads the bundle straight off disk instead of forcing you to shell-escape a multi-line JSON body:

```bash
primitive functions deploy --name my-fn --file ./dist/handler.js
```

### 1. Build a self-contained ESM bundle

```bash
esbuild src/handler.ts --bundle --format=esm --outfile=dist/handler.js
```

The handler must be a single self-contained module; `functions deploy` uploads exactly the file you point `--file` at.

### 2. Deploy the bundle

```bash
primitive functions deploy --name my-fn --file ./dist/handler.js
```

`--name` is a slug: lowercase letters, digits, hyphens, underscores, 1-64 characters, unique within your org. Add `--source-map-file ./dist/handler.js.map` to attach a source map so function-log stack traces symbolicate back to your original source.

### 3. Confirm the deploy landed

The command prints the created (or, when secrets were seeded, the redeployed) function record as JSON on stdout, including its `id`, `name`, and `deploy_status`.

> **Tip:** Deploying is idempotent by name when you use `--source` (a project directory) instead of `--file`. Rerunning `primitive functions deploy --name my-fn --source .` on every push either creates the function or redeploys it, whichever applies.

> **Warning:** With secrets present, deploy fans out to multiple API calls instead of one: `create-function`, then `set-secret` for each pair in order, then a final `update-function` redeploy so the running handler actually picks up the new bindings.

### Deploy from a source directory (managed build)

Pass `--source <dir>` instead of `--file` to upload a project directory and let the server install dependencies, bundle for the Workers runtime, and deploy:

```bash
primitive functions deploy --name triage --source .
```

Provide exactly one of `--file` or `--source`; passing both, or neither, is rejected before any network call.

### Wait for the deploy to finish

```bash
primitive functions deploy --name my-fn --file ./dist/handler.js --wait
```

`--wait` polls until the deploy reaches `deployed` or `failed`, writing progress to stderr while stdout stays the final JSON payload. Control timing with `--timeout <seconds>` (pass `0` to wait forever) and `--poll-interval <seconds>`. A timeout exits with status `2`; a failed deploy exits with status `1` and prints `deploy_error` on stderr.

## Seed secrets in the same deploy

Pass one or more secret-source flags to `primitive functions deploy` to seed encrypted `env` bindings without a separate `functions set-secret` round trip:

```bash
primitive functions deploy --name my-fn --file ./dist/handler.js \
  --secret OPENAI_KEY=sk-... \
  --secret OWNER_EMAIL=me@example.com
```

| Flag | Source |
|---|---|
| `--secret KEY=VALUE` | Inline value on the command line |
| `--secret-from-env KEY` | `process.env.KEY` |
| `--secret-from-file KEY=PATH` | Full contents of a UTF-8 file |
| `--secret-from-env-file FILE:KEY` | A dotenv-style file |
| `--secret-from-stdin KEY` | Piped stdin (not repeatable; trailing newline stripped) |

Each flag is repeatable except `--secret-from-stdin`, which consumes stdin once. Every secret `KEY` must match `^[A-Z_][A-Z0-9_]*$` and may only appear once per command.

```bash
primitive functions deploy --name my-fn --file ./dist/handler.js \
  --secret-from-env OPENAI_KEY \
  --secret-from-env-file .env.local:OWNER_EMAIL
```

With secrets present, the CLI fans out to multiple API calls: create the function (or, for `--source` against an existing name, look up its id), write each secret in order, then redeploy the same bundle so the running handler picks up the new bindings.

> **Warning:** If a secret write fails partway through, the function row carries whatever bindings landed but the redeploy has **not** happened yet, so the running handler does not have the new bindings. The command's stderr hint names which keys succeeded, which key failed, and which keys were never attempted:
>
> ```text
> Function my-fn (<function-id>) was created, but writing secret OWNER_EMAIL failed;
> succeeded keys so far: OPENAI_KEY; keys not yet attempted: (none). The redeploy
> is NOT yet live. Re-run `primitive functions set-secret` for each of
> [OWNER_EMAIL], then `primitive functions redeploy --id <function-id> --file <bundle>`
> to push them live.
> ```
>
> Re-run `primitive functions set-secret` for the missing keys, then `primitive functions redeploy --id <function-id> --file <bundle>` to push them live.

## Redeploy and test

`primitive functions redeploy` pushes new code to an existing function id, and `primitive functions test` runs a synthetic inbound invocation against it.

```bash
primitive functions redeploy --id <function-id> --file ./dist/handler.js
primitive functions test --id <function-id> --wait --show-sends
```

`functions redeploy` also accepts a new source map. `functions test --wait --show-sends` waits for the invocation to finish and shows any reply or send the handler triggered, so you can verify a handler end to end before real inbound mail depends on it.

Inspect logs for a deployed function:

```bash
primitive functions logs --id <function-id>
```

## Bind a route

A deployed function receives no inbound mail until a [recipient routing](https://test.abhinandan.one/functions-and-routing-concepts.md) rule points at it, so bind one after a fresh deploy:

```bash
primitive functions route-set --id <function-id> --domain <domain-id>
# or, to catch everything not otherwise routed:
primitive functions route-set --id <function-id> --fallback
```

> **Tip:** `functions deploy` prints a route-status hint after every successful deploy: "Route bound. Function will receive inbound mail." or a warning that no route is bound yet, with the exact `functions route-set` command to run. Don't skip this step. A function with `deploy_status: deployed` still receives zero inbound mail until a route exists.

Inspect the routing topology (which addresses and domains resolve to which function or endpoint) with the function-routing inspection commands, or manage recipient-level rules directly with [`primitive routes`](https://test.abhinandan.one/cli-overview/cli-domains-and-routes.md).

## Manage secrets outside of deploy

`primitive functions set-secret` writes one secret binding on an existing function without re-sending its code.

```bash
primitive functions set-secret --help
```

Run the command with `--help` for its exact flags, then re-run `primitive functions redeploy --id <function-id> --file <bundle>` so the running handler picks up the new binding.

For secrets shared across every function in your org rather than scoped to one function, use [org-wide secrets](https://test.abhinandan.one/cli-overview/cli-org-secrets.md) instead.

## Command reference

| Command | Purpose |
|---|---|
| `functions templates` | List installable function templates |
| `functions init <name> --template <template>` | Scaffold a project directory |
| `functions deploy --name <name> (--file <path> \| --source <dir>)` | Create and deploy a function |
| `functions redeploy --id <id> --file <path>` | Push new code to an existing function |
| `functions test --id <id> --wait --show-sends` | Run a synthetic inbound test |
| `functions logs --id <id>` | View function logs |
| `functions set-secret` | Write one function secret |
| `functions route-set --id <id> (--domain <id> \| --fallback)` | Bind a routing target |

Run `primitive functions <command> --help` for the full flag list of any subcommand, and `primitive functions --help` for the complete command group.
