> ## Documentation Index
> Fetch the complete documentation index at: https://docs.crewship.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Hire / Rehire Commands

> Spawn a short-lived ephemeral agent into a crew (PR-D F5) and renew its TTL when the work isn't finished.

# crewship hire / crewship rehire

`hire` provisions a short-lived agent from a template into an existing crew, gives it a TTL, and writes a `hire_reason` audit row. The agent runs like any permanent crew member until its TTL elapses, at which point a background sweeper ghosts it (sets `expired_at`, keeps the row + memory files for audit + rehire).

`rehire` resets the TTL on a ghost (or a still-live ephemeral whose operator wants to extend before it ghosts). Same row, same `id`, same memory files — the agent picks up exactly where it left off, with full continuity.

For the lifecycle walkthrough and operator workflows see [Ephemeral agents](/guides/ephemeral-agents). This page is the canonical CLI reference: flags, error codes, exit semantics.

```bash theme={null}
crewship hire   --crew <slug> --template <name> --reason <text> [flags]
crewship rehire <agent-slug-or-id>             --reason <text> [flags]
```

***

## Per-autonomy-level response

Both commands route through the per-crew `autonomy_level` policy gate (PR-B F2). The crew's setting determines what happens to the hire:

| Crew autonomy | Hire response                                                                                                                                            | Rehire response                                                  |
| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- |
| `strict`      | **Rejected** (403) — ephemeral spawn is forbidden on compliance-grade crews.                                                                             | **Rejected** (403) — same reason.                                |
| `guided`      | **Pending review** (202) — agent row created with `status='PENDING_REVIEW'`. Chatbridge refuses to start the agent until an operator approves via inbox. | Same — re-promotion of a ghost still requires operator approval. |
| `trusted`     | **Live immediately** (201) + journal-only logging (`decision=auto_log_journal`; no inbox row).                                                           | Same.                                                            |
| `full`        | **Live immediately** (201) + journal-only logging (`decision=auto_journal`).                                                                             | Same.                                                            |

See [Autonomy + self-learning](/guides/autonomy-and-self-learning) for the matrix and where to set the autonomy level.

***

## `crewship hire`

```bash theme={null}
crewship hire \
  --crew on-call \
  --template incident-responder \
  --ttl 240 \
  --reason "P1 incident #4582 needs sustained eyes-on"
```

### Flags

| Flag              | Required | Default          | Description                                                                                                                                                                                                                                                                                |
| ----------------- | -------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `--crew`          | yes      | —                | Crew slug or ID to hire into. The agent inherits this crew's MCP config, network policy, container image, and autonomy\_level.                                                                                                                                                             |
| `--template`      | yes      | —                | Built-in agent template that defines the role + system prompt skeleton + CLI adapter (e.g. `incident-responder`, `code-reviewer`, `release-manager`).                                                                                                                                      |
| `--reason`        | yes      | —                | Free-form audit text. Appended to `agents.hire_reason` as a chronological history (rehires also append; the column reads as the agent's lifecycle narrative).                                                                                                                              |
| `--ttl`           | optional | `30`             | Time-to-live in minutes. Cobra registers `0`, which the server treats as "use the default" and clamps into `[30, 1440]` (30 min minimum prevents trivial-hire abuse; 24 hr maximum enforces the "ephemeral" framing — longer work hires a permanent agent). The effective default is `30`. |
| `--model`         | optional | template default | Override the LLM model for this hire. Useful when the template defaults to Sonnet but the work needs Opus.                                                                                                                                                                                 |
| `--parent-lead`   | optional | —                | Agent ID of the LEAD that initiated this hire (LEAD-driven sub-agent spawning). Links the rows for journal queries.                                                                                                                                                                        |
| `--yes`           | optional | `false`          | Skip interactive confirmation prompts.                                                                                                                                                                                                                                                     |
| `--wait`          | optional | `false`          | Block until a `guided`-crew (`202` `PENDING_REVIEW`) hire is resolved — approved (`crewship hire approve`) or ghosted (TTL elapses before anyone approves it). No-op when the hire is already live (`201`).                                                                                |
| `--wait-timeout`  | optional | `30m`            | Max time to block with `--wait` (`0` = forever).                                                                                                                                                                                                                                           |
| `--wait-interval` | optional | `2s`             | Poll interval for `--wait`.                                                                                                                                                                                                                                                                |

### Response

```json theme={null}
{
  "id": "agt_…",
  "crew_id": "crw_…",
  "workspace_id": "ws_…",
  "slug": "incident-responder-a3f8",
  "name": "incident-responder-a3f8",
  "status": "IDLE",
  "ephemeral": true,
  "expires_at": "2026-05-22T05:42:00Z",
  "expired_at": null,
  "parent_lead_id": null,
  "hire_reason": "2026-05-21T01:42:00Z: P1 incident #4582 needs sustained eyes-on",
  "pending_review": false,
  "decision": "auto_log_journal"
}
```

(Shape mirrors `hireResponse` in `internal/api/agents_hire.go`. `inbox_item_id` is added only when a `guided` hire creates a blocking inbox waitpoint.)

| Field            | Notes                                                                                                                                              |
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `status`         | `IDLE` for `trusted`/`full` crews; `PENDING_REVIEW` for `guided` crews (chatbridge refuses to start until approved via inbox).                     |
| `decision`       | The policy decision that landed this hire — `auto_journal` (full) / `auto_log_journal` (trusted) / `inbox_approve` (guided) / `rejected` (strict). |
| `pending_review` | `true` only for `guided` hires (mirrors `status=PENDING_REVIEW`).                                                                                  |
| `expires_at`     | RFC3339 UTC. Set to `now() + ttl_minutes`.                                                                                                         |
| `hire_reason`    | Accumulating history — each hire/rehire appends `<timestamp>: <reason>`.                                                                           |

### Auto-assigned credentials

On a successful hire, Crewship automatically assigns the available workspace
Anthropic credential (type `API_KEY` or `AI_CLI_TOKEN`, ordered by creation
date) to the new ephemeral agent, so it can authenticate on first run. This is
best-effort and never blocks the hire:

* No Anthropic credential in the workspace → the hire still succeeds and the
  agent journals `credential.auto_assign_empty`; the agent will fail to chat
  until you [add one](/guides/credentials) and rehire (or assign manually).
* You can always override afterwards with
  `crewship credential assign <agent> <credential>`.

### Exit codes

The CLI exits `0` on success (a `201` live hire or a `202` pending-review hire) and `1` on any failure — the error text printed to stderr names the cause. Common failure causes and the message they carry:

| Cause                            | What you'll see                                                                                 |
| -------------------------------- | ----------------------------------------------------------------------------------------------- |
| Validation failure               | Missing required flag (`--crew is required`, `--template is required`, `--reason is required`). |
| Policy rejection (`strict` crew) | Server `403` — the response body names the `autonomy_level` that forbids `ephemeral_spawn`.     |
| Quota exceeded                   | Server `429` — the body names the live count and `crews.max_ephemeral_agents` max.              |
| Network / server error           | The underlying HTTP/transport error (e.g. `5xx`).                                               |

### Waiting for a guided approval (`--wait`)

```bash theme={null}
crewship hire --crew on-call --template incident-responder \
  --reason "P1 incident #4582 needs sustained eyes-on" --wait
```

On a `guided`-autonomy crew the hire lands as `202` `PENDING_REVIEW` and
needs an operator to run `crewship hire approve <agent>` before the agent
can chat. `--wait` polls `GET /api/v1/agents/{id}` (the same route
`crewship agent get` uses) every `--wait-interval` until the status flips
away from `PENDING_REVIEW` (approved) or the hire ghosts — its TTL elapses
before anyone approves it, which the CLI also treats as a resolved (failing)
outcome so `--wait` doesn't hang forever on an abandoned approval. There is
no explicit "denied" state today; a rejected hire is left to expire.

### `--ttl` clamping

The clamp is entirely server-side and silent — the CLI forwards `ttl_minutes` as-is and does not pre-validate or warn. An out-of-range value (e.g. `--ttl 10`) still produces a successful hire; the server clamps it into `[30, 1440]` and the effective value comes back in `expires_at` (`now() + clamped_ttl`). Read `expires_at` on the response to confirm the TTL that actually landed.

***

## `crewship rehire`

```bash theme={null}
crewship rehire incident-responder-a3f8 \
  --ttl 120 \
  --reason "incident #4582 stretched into a sustained-fire investigation"
```

### Flags

| Flag                 | Required | Default | Description                                                                                                                                                                              |
| -------------------- | -------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `agent` (positional) | yes      | —       | Slug or ID of the ephemeral agent. Can be a ghost (`expired_at != NULL`) — that's the canonical rehire case — or a still-live ephemeral whose operator wants to extend before it ghosts. |
| `--reason`           | yes      | —       | Audit text appended to `hire_reason`.                                                                                                                                                    |
| `--ttl`              | optional | `30`    | New TTL in minutes. Resets `expires_at` to `now() + ttl`. Cobra registers `0` (server treats as default) and applies the same `[30, 1440]` clamp as hire; effective default `30`.        |
| `--yes`              | optional | `false` | Skip the interactive confirmation prompt (`-y` shorthand).                                                                                                                               |

### Effect on the agent row

| Column                                   | Before rehire (ghost) | After rehire                               |
| ---------------------------------------- | --------------------- | ------------------------------------------ |
| `expires_at`                             | past timestamp        | `now() + ttl`                              |
| `expired_at`                             | non-NULL              | `NULL` (un-ghost)                          |
| `hire_reason`                            | initial-hire history  | history + new line with timestamp + reason |
| `id` / `slug` / `crew_id` / memory files | unchanged             | unchanged — full continuity                |

### Quota interaction

* **Rehiring a ghost** does NOT count against `crews.max_ephemeral_agents` — the ghost row already exists; rehire just toggles `expired_at` to NULL.
* **Rehiring a still-live ephemeral** is also free for the same reason (same row).
* **Hiring a fresh ephemeral** when the quota is full returns 429 — at that point the operator either raises the quota or rehires a ghost instead of hiring fresh.

### Exit codes

Same as `hire`: `0` on success, `1` on any failure with the cause in the stderr message. Rehire-specific failures:

* The agent isn't ephemeral (you can't rehire a permanent agent — the lifecycle doesn't apply).
* The policy rejects rehire on a `strict` crew (matches the hire rejection — re-promoting a ghost still needs policy approval).

***

## `crewship hire approve`

Approve a `guided`-autonomy hire that is parked in `PENDING_REVIEW`, flipping it to `IDLE` so it can serve work. This is the terminal counterpart to the UI inbox "Approve" button.

```bash theme={null}
crewship hire approve incident-responder-a3f8
```

Takes the ephemeral agent's slug or ID (printed by `crewship hire` and shown in `crewship agent list`) as its single positional argument, and calls `POST /api/v1/agents/{id}/approve-hire`. A `409` comes back if the agent isn't in `PENDING_REVIEW` (already approved, or hired under a non-`guided` autonomy level).

***

## Listing ephemeral agents

`crewship agent list` shows every agent in the workspace, ephemerals included. Scope to one crew with `--crew`:

```bash theme={null}
crewship agent list --crew on-call
```

Ephemeral agents appear alongside permanent ones; live vs. ghost state is read off each agent's `expired_at` / `expires_at` fields. Use `--format json` and `jq` if you want to filter to just the live or ghost rows:

```bash theme={null}
crewship agent list --crew on-call --format json | jq '.[] | select(.ephemeral and (.expired_at == null))'
```

***

## TTL mid-mission grace

The sweeper that ghosts expired ephemerals (`internal/ephemeral/expiry.go`, 5-minute ticker) **skips agents with `status='RUNNING'`**. A mission in flight finishes even if its TTL elapsed mid-call; the agent ghosts on the next sweep after it idles back to `IDLE`. This avoids the worst-case "agent was mid-tool-call when ghost flag fired" scenario.

The trade-off is that a long-running ephemeral can outlive its TTL by up to one sweep interval (5 min). For stricter timing, provision a permanent agent and `crewship agent delete <id>` explicitly.

***

## LEAD-driven hire from inside a container

A `LEAD`-mode agent in active orchestration can hire a sub-agent via the sidecar `/spawn` endpoint without an operator running `crewship hire`. The sidecar proxies the request to `POST /api/v1/internal/agents/hire` and injects `MANAGER` role into the request context; the policy gate still fires (a `strict` crew rejects LEAD-driven hires too — `autonomy_level` is the security boundary, not RBAC).

See [Ephemeral agents — LEAD-driven hire](/guides/ephemeral-agents#lead-driven-hire-from-inside-a-container) for the full pattern, including the [AgentBrief](/guides/agent-brief) primitive for handing curated context to the sub-agent.

***

## Related

* [Ephemeral agents](/guides/ephemeral-agents) — operator playbook with workflow scenarios
* [Autonomy + self-learning](/guides/autonomy-and-self-learning) — the policy matrix that gates hire decisions
* [AgentBrief](/guides/agent-brief) — sub-agent briefing primitive used by LEAD-driven hires
* [`crewship policy`](/cli/policy) — set the crew's autonomy\_level before hiring against it
* [`crewship inbox`](/cli/inbox) — approve / dismiss the pending-review hire entries for guided crews
