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

# POST /tasks/:id/ship — merge and deploy a task

> Trigger a merge-and-deploy operation for an approved task. Requires CI to be green, review to be approved, and the correct head commit SHA.

When CI passes and review is approved, call `POST /tasks/:id/ship` to merge the PR and deploy the changes. AgentRail validates all preconditions before accepting the request. You must supply `expectedHeadSha` to prevent accidental merges if the branch has advanced since you last read the task. Always gate this call on `availableActions.includes("ship")` — if `"ship"` is not in the list, the task is not ready.

<Note>
  Required scope: **`ship:write`**
</Note>

<Warning>
  Never call ship without first verifying that CI has passed, review is approved, and `availableActions` includes `"ship"`. Calling ship when these conditions are not met returns `409`. Passing an incorrect `expectedHeadSha` also returns `409`.
</Warning>

## Path parameters

<ParamField path="id" type="string" required>
  Stable task ID. Must match the pattern `tsk_[A-Za-z0-9]+`.
</ParamField>

## Headers

<ParamField header="Idempotency-Key" type="string" required>
  Unique key for safe retries. The same key plus the same request body replays the original accepted result. The same key with a different body returns `409 conflict`. Use a key that encodes the task ID and head SHA, for example `ship-tsk_abc123-b5bc7f8`. Must be 8–128 characters.
</ParamField>

## Request body

<ParamField body="mode" type="string" required>
  Ship mode. Use `merge_and_deploy` to merge the PR and trigger deployment. Use `merge_only` to merge without deploying.

  Allowed values: `merge_only`, `merge_and_deploy`
</ParamField>

<ParamField body="targetEnvironment" type="string" required>
  Deployment target. Use `production` for production deploys or `staging` for staging.

  Allowed values: `staging`, `production`
</ParamField>

<ParamField body="expectedHeadSha" type="string" required>
  The full 40-character commit SHA you expect to be at the head of the task branch. AgentRail rejects the request with `409` if the actual head SHA differs, preventing accidental merges after a late commit. Read this value from `data.headSha` on the task, or from the CI status or review feedback responses.
</ParamField>

## Example

```bash theme={null}
curl -s -X POST "$AGENTRAIL_BASE_URL/tasks/tsk_DEMOISSUETOSHIP01/ship" \
  -H "authorization: Bearer $AGENTRAIL_API_KEY" \
  -H "content-type: application/json" \
  -H "idempotency-key: ship-demo-1" \
  -d '{
    "mode": "merge_and_deploy",
    "targetEnvironment": "production",
    "expectedHeadSha": "b5bc7f86b9ad94f4f18f83d28bdf3e27a31e53a0"
  }'
```

## Response

A `202` response means the ship operation was accepted and queued.

<ResponseField name="data" type="object" required>
  Ship operation record.

  <Expandable title="Ship operation fields">
    <ResponseField name="taskId" type="string" required>
      Task this ship operation belongs to.
    </ResponseField>

    <ResponseField name="operationId" type="string" required>
      Stable ship operation ID. Begins with `shp_`.
    </ResponseField>

    <ResponseField name="status" type="string" required>
      Current operation status. One of: `queued`, `running`, `succeeded`, `failed`.
    </ResponseField>

    <ResponseField name="queuedAt" type="string" required>
      ISO 8601 timestamp when the operation was queued.
    </ResponseField>

    <ResponseField name="availableActions" type="string[]" required>
      Next steps, for example `["view_ci_status"]`.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="availableActions" type="string[]" required>
  Top-level available actions.
</ResponseField>

### Example response

```json theme={null}
{
  "data": {
    "taskId": "tsk_01JY4X8Q6J5Q3P7M0N2K3R4T5V",
    "operationId": "shp_01JY4YP2N9PTDP8PHV7ZV8B7XS",
    "status": "queued",
    "queuedAt": "2026-05-01T03:12:02Z",
    "availableActions": ["view_ci_status"]
  },
  "availableActions": ["view_ci_status"]
}
```

## Preconditions checklist

All of the following must be true before AgentRail accepts a ship request:

| Condition                                  | How to verify                                                                 |
| ------------------------------------------ | ----------------------------------------------------------------------------- |
| `"ship"` is in `availableActions`          | Read the task with `GET /tasks/:id`                                           |
| CI has passed                              | `GET /tasks/:id/ci-status` returns `overallStatus: "passed"`                  |
| Review is approved                         | `GET /tasks/:id/review-feedback` returns `latestDecision.outcome: "approved"` |
| `expectedHeadSha` matches the current head | Use `headSha` from the task, CI status, or review feedback response           |

## Error responses

| Status | Code                 | Meaning                                                                                                                                                                |
| ------ | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | `validation_error`   | `expectedHeadSha` is not a valid 40-character hex SHA.                                                                                                                 |
| `401`  | `unauthorized`       | Bearer token is missing or invalid.                                                                                                                                    |
| `403`  | `insufficient_scope` | Key does not have `ship:write`.                                                                                                                                        |
| `404`  | `not_found`          | Task not found, or no live adapter matches the task.                                                                                                                   |
| `409`  | `conflict`           | CI is not green, review is not approved, the task is not in a shippable state, the head SHA does not match, or the `Idempotency-Key` was reused with a different body. |

<Tip>
  If you receive `409` because of a SHA mismatch, re-read the task with `GET /tasks/:id` to get the updated `headSha`, then retry with a new `Idempotency-Key` that encodes the new SHA.
</Tip>
