---
title: "Deploy Worker"
method: POST
path: "/workers"
tags: ["workers"]
---

# Deploy Worker

`POST /workers`

Explicitly deploy a worker.

The body is discriminated on ``platform``: a Slack deploy names the channel to
bind to, a web deploy names the worker instead, because web mints its own handle
and has no channel to pick. Slack is the default variant, so a body written before
``platform`` existed still parses.

State-changing, so it goes through the standard ``AuthorizedUserDep`` chain
(authenticated + CSRF-validated). The organization comes from the
authenticated user, never the request body, so a caller cannot deploy into
another org's channel — and the deploying identity likewise comes from the
session, so the announcement can't be attributed to someone else.

## Request body

- union
  - SlackDeployWorkerRequest — Deploy a worker onto an existing Slack channel (PRD-3444). The non-channel-join origination path: the operator picks the channel and the mode instead of a rule matching on join. ``mode`` is required — an explicit deploy always states the worker's standing mission, rather than inheriting a rule default that doesn't exist on this path.
    - `platform` 'slack'
    - `channel_id` string, required
    - `mode` 'incident' | 'alerts', required — The kind of channel the agent lives in — its standing mission. Drives the swappable mode block in the system prompt (and, later, the toolset). ``incident`` and ``alerts`` have mode blocks today; ``escalation`` and others land as localized drop-ins (their standing block + tooling) when that work begins.
    - `custom_instruction` string, nullable
  - WebDeployWorkerRequest — Deploy a channel-less web worker (PRD-3495). No ``channel_id``: a web worker has no external channel to bind to and mints its own handle at origination, so there is nothing for the operator to pick. That is the whole reason this is a separate shape rather than an optional field — a web deploy that accepted a channel id would be accepting something it must ignore. ``name`` is required for the same asymmetry. Slack reads a channel's name; web has no external source for one, so an unnamed web worker would render in ``/workers`` as its ``resource_id`` — a uuid. There is no rename endpoint, so this is where the name is set.
    - `platform` 'web', required
    - `name` string, required
    - `mode` 'incident' | 'alerts', required — The kind of channel the agent lives in — its standing mission. Drives the swappable mode block in the system prompt (and, later, the toolset). ``incident`` and ``alerts`` have mode blocks today; ``escalation`` and others land as localized drop-ins (their standing block + tooling) when that work begins.
    - `custom_instruction` string, nullable

## Response `201`

Successful Response

- WorkerResponse
  - `id` string, required
  - `resource_id` string, required
  - `display_name` string, required
  - `platform` 'slack' | 'teams' | 'web', required — A supported chat platform. Values align with ``integration_instances.type`` so a chat install's platform and its integration row stay in lockstep.
  - `agent_type` string, required
  - `mode` 'incident' | 'alerts', required — The kind of channel the agent lives in — its standing mission. Drives the swappable mode block in the system prompt (and, later, the toolset). ``incident`` and ``alerts`` have mode blocks today; ``escalation`` and others land as localized drop-ins (their standing block + tooling) when that work begins.
  - `custom_instruction` string, nullable
  - `created_at` string, date-time, required
  - `updated_at` string, date-time, required
  - `stopped_at` string, date-time, nullable, required
  - `status` 'critical' | 'attention' | 'working' | 'stable', required — The worker's present-tense status top-line, shown as a pill on the /workers list. Only these agent-picked values are ever stored in the `status` column; a null column means none has been emitted yet (the UI renders that as `starting`), and the terminal `stopped` state is derived from `stopped_at` — neither is stored here, so there is no second source of truth. The agent-facing definitions live on the ``status`` field in ``prompts.py`` (the only copy the model reads); keep this summary in sync with it. Values: CRITICAL: Immediate coordinated response is warranted because user-facing impact is severe or escalating. ATTENTION: A specific person or team should act now, but immediate coordinated incident response is not warranted. STABLE: Resting floor — no specific person or team needs to act now and no coordinated incident response is warranted. WORKING: Legacy — no longer emittable, see ``EmittableWorkerStatus``.
  - `status_message` string, nullable, required
  - `status_updated_at` string, date-time, nullable, required

## Other responses

- `422` — Validation Error

---

[API](https://skmtc.net/traversal/apis/fastapi.md) · [All operations](https://skmtc.net/traversal/apis/fastapi/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/traversal/fastapi/revisions/2134ebffd1ef/schema)
