# Agent instructions

Agent instructions teach a team's agents how to write tickets and plans: templates, writing guidance and, for tickets, default values. A workspace and each team can set their own; a team's replace the workspace's, which replace Clanker Ship's defaults.

## The AgentInstructions object

The instructions of one kind that a team's agents follow, and where they come from.

### Attributes

- `kind` (string, one of: `ticket`, `plan`, `test-cases`, `test-code`, `fixtures`, `test-ci`, required): What the instructions shape: `ticket` for a ticket's summary, `plan` for its plan, `test-cases` for test cases, `test-code` for specs, `fixtures` for a product's Fixture API and `test-ci` for the CI that runs tests.
- `source` (string, one of: `team`, `workspace`, `default`, required): Where these come from: the team's own (team), the workspace's (workspace) or Clanker Ship's defaults (default).
- `team` (nullable object, required): The team asked about, or null for the workspace's instructions.
  - `id` (string, required): Unique ID of the team.
  - `key` (string, required): 2 to 5 capital letters that start the team's ticket IDs, such as WEB.
  - `name` (string, required): The team's display name.
- `templates` (array of objects, required): One template for every ticket, and at most one more per tag.
  - `tag` (nullable string, required): The tag whose tickets start from this template, or null for the template every other ticket uses.
  - `body` (string, required): The Markdown a ticket's summary or plan starts from.
- `guidance` (string, required): How the agent should write, in Markdown.
- `defaults` (nullable object, required): What new tickets from agents get; null for plan instructions.
  - `status` (nullable string, required): The name of the status new tickets get, or null for the team's default status.
  - `tags` (array of strings, required): The names of the tags new tickets get.
  - `owner` (string, one of: `creator`, `none`, required): `creator` makes the agent that creates the ticket its owner; `none` leaves the ticket without one.
- `updatedAt` (nullable string, required): When these were last set, as an ISO 8601 timestamp; null for Clanker Ship's defaults.
- `updatedBy` (nullable object, required): Who last set these; null for Clanker Ship's defaults or when an account API key set them.
  - `person` (nullable object, required): The person who acted, or the person the agent works for; null for an account API key.
    - `id` (string, required): The person's user ID.
    - `name` (string, required): The person's display name.
    - `email` (string, required): The person's email address.
  - `agent` (nullable object, required): The agent that acted for the person, or null when the person acted directly.
    - `id` (string, required): Unique ID of the registered agent.
    - `name` (string, required): The coding agent, such as Codex.
    - `person` (object, required): A person in the workspace.
      - `id` (string, required): The person's user ID.
      - `name` (string, required): The person's display name.
      - `email` (string, required): The person's email address.

### Example

```json
{
  "kind": "ticket",
  "source": "team",
  "team": {
    "id": "Tm6PzN3kWq1J",
    "key": "WEB",
    "name": "Web"
  },
  "templates": [
    {
      "tag": null,
      "body": "## Problem\nWhat is wrong or missing, and for whom.\n\n## Outcome\nWhat is true when this ticket is done."
    },
    {
      "tag": "bug",
      "body": "## Steps to reproduce\n\n## Expected\n\n## Actual"
    }
  ],
  "guidance": "- Keep the title short: what changes, and where.\n- Use the team's tags. Do not invent new ones.",
  "defaults": {
    "status": "Todo",
    "tags": [],
    "owner": "creator"
  },
  "updatedAt": "2026-10-14T09:05:12.000Z",
  "updatedBy": {
    "person": {
      "id": "Us3NfP8kLq2V",
      "name": "Maya Patel",
      "email": "maya@acme.dev"
    },
    "agent": null
  }
}
```

## Get agent instructions

`GET /v1/workspaces/{slug}/agent-instructions`

Returns the instructions a team's agents follow: the team's own, else the workspace's, else Clanker Ship's defaults. Without `team`, returns the workspace's or Clanker Ship's. `source` says which.

### Parameters

- `slug` (string, in path, required): The workspace's slug or ID, such as acme.
- `kind` (string, one of: `ticket`, `plan`, `test-cases`, `test-code`, `fixtures`, `test-ci`, in query, required): What the instructions shape: `ticket` for a ticket's summary, `plan` for its plan, `test-cases` for test cases, `test-code` for specs, `fixtures` for a product's Fixture API and `test-ci` for the CI that runs tests.
- `team` (string, in query): A team's key, such as WEB. Leave it out for the workspace's instructions.

### Returns

Returns the [AgentInstructions](https://docs.qa.clankership.net/api/agent-instructions/object.md) object. The status is 200.

### Request

```bash
curl "https://api.qa.clankership.net/v1/workspaces/{slug}/agent-instructions?kind=ticket" \
  -H "Authorization: Bearer $CLANKER_API_KEY"
```

### Response

```json
{
  "kind": "ticket",
  "source": "team",
  "team": {
    "id": "Tm6PzN3kWq1J",
    "key": "WEB",
    "name": "Web"
  },
  "templates": [
    {
      "tag": null,
      "body": "## Problem\nWhat is wrong or missing, and for whom.\n\n## Outcome\nWhat is true when this ticket is done."
    },
    {
      "tag": "bug",
      "body": "## Steps to reproduce\n\n## Expected\n\n## Actual"
    }
  ],
  "guidance": "- Keep the title short: what changes, and where.\n- Use the team's tags. Do not invent new ones.",
  "defaults": {
    "status": "Todo",
    "tags": [],
    "owner": "creator"
  },
  "updatedAt": "2026-10-14T09:05:12.000Z",
  "updatedBy": {
    "person": {
      "id": "Us3NfP8kLq2V",
      "name": "Maya Patel",
      "email": "maya@acme.dev"
    },
    "agent": null
  }
}
```

### Errors

- `400` `InvalidInput`: The request is malformed, or a value is not allowed.
- `401` `Unauthorized`: The request has no valid API key.
- `403` `Forbidden`: The key's user or role may not do this.
- `404` `NotFound`: The workspace or object does not exist, or the caller cannot see it.
- `409` `Conflict`: The change conflicts with current data, such as a slug that is already in use.
- `500` `Internal`: Clanker Ship failed to handle the request. Retry, or report the response's x-request-id.

## Set agent instructions

`PUT /v1/workspaces/{slug}/agent-instructions`

Sets the workspace's or a team's own instructions. Fields left out keep their current values, so a team's first change copies what it inherits. Owners and admins set the workspace's, and a team's members also set the team's; viewers cannot. Only ticket instructions take `defaults`.

### Parameters

- `slug` (string, in path, required): The workspace's slug or ID, such as acme.
- `kind` (string, one of: `ticket`, `plan`, `test-cases`, `test-code`, `fixtures`, `test-ci`, required): What the instructions shape: `ticket` for a ticket's summary, `plan` for its plan, `test-cases` for test cases, `test-code` for specs, `fixtures` for a product's Fixture API and `test-ci` for the CI that runs tests.
- `team` (nullable string): A team's key, such as WEB. Leave it out for the workspace's instructions.
- `templates` (array of objects): Replaces the templates: exactly one without a tag, and at most one more per tag, up to 20 in all.
  - `tag` (nullable string, required): The tag whose tickets start from this template, or null for the template every other ticket uses.
  - `body` (string, required): The Markdown a ticket's summary or plan starts from.
- `guidance` (string): How the agent should write, in Markdown, up to 20,000 characters.
- `defaults` (object): What a ticket an agent creates gets for each of these fields the agent leaves out.
  - `status` (nullable string, required): The name of the status new tickets get, or null for the team's default status.
  - `tags` (array of strings, required): The names of the tags new tickets get.
  - `owner` (string, one of: `creator`, `none`, required): `creator` makes the agent that creates the ticket its owner; `none` leaves the ticket without one.

### Returns

Returns the [AgentInstructions](https://docs.qa.clankership.net/api/agent-instructions/object.md) object. The status is 200.

### Request

```bash
curl -X PUT https://api.qa.clankership.net/v1/workspaces/{slug}/agent-instructions \
  -H "Authorization: Bearer $CLANKER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "kind": "ticket",
    "team": "WEB",
    "templates": [
      {
        "tag": null,
        "body": "## Problem\nWhat is wrong or missing, and for whom.\n\n## Outcome\nWhat is true when this ticket is done."
      },
      {
        "tag": "bug",
        "body": "## Steps to reproduce\n\n## Expected\n\n## Actual"
      }
    ],
    "defaults": {
      "status": "Todo",
      "tags": [],
      "owner": "creator"
    }
  }'
```

### Response

```json
{
  "kind": "ticket",
  "source": "team",
  "team": {
    "id": "Tm6PzN3kWq1J",
    "key": "WEB",
    "name": "Web"
  },
  "templates": [
    {
      "tag": null,
      "body": "## Problem\nWhat is wrong or missing, and for whom.\n\n## Outcome\nWhat is true when this ticket is done."
    },
    {
      "tag": "bug",
      "body": "## Steps to reproduce\n\n## Expected\n\n## Actual"
    }
  ],
  "guidance": "- Keep the title short: what changes, and where.\n- Use the team's tags. Do not invent new ones.",
  "defaults": {
    "status": "Todo",
    "tags": [],
    "owner": "creator"
  },
  "updatedAt": "2026-10-14T09:05:12.000Z",
  "updatedBy": {
    "person": {
      "id": "Us3NfP8kLq2V",
      "name": "Maya Patel",
      "email": "maya@acme.dev"
    },
    "agent": null
  }
}
```

### Errors

- `400` `InvalidInput`: The request is malformed, or a value is not allowed.
- `401` `Unauthorized`: The request has no valid API key.
- `403` `Forbidden`: The key's user or role may not do this.
- `404` `NotFound`: The workspace or object does not exist, or the caller cannot see it.
- `409` `Conflict`: The change conflicts with current data, such as a slug that is already in use.
- `500` `Internal`: Clanker Ship failed to handle the request. Retry, or report the response's x-request-id.

## Reset agent instructions

`DELETE /v1/workspaces/{slug}/agent-instructions`

Removes the workspace's or a team's own instructions, so it follows what it inherits again, and returns the instructions that now apply. The people who can set the instructions can reset them.

### Parameters

- `slug` (string, in path, required): The workspace's slug or ID, such as acme.
- `kind` (string, one of: `ticket`, `plan`, `test-cases`, `test-code`, `fixtures`, `test-ci`, in query, required): What the instructions shape: `ticket` for a ticket's summary, `plan` for its plan, `test-cases` for test cases, `test-code` for specs, `fixtures` for a product's Fixture API and `test-ci` for the CI that runs tests.
- `team` (string, in query): A team's key, such as WEB. Leave it out for the workspace's instructions.

### Returns

Returns the [AgentInstructions](https://docs.qa.clankership.net/api/agent-instructions/object.md) object. The status is 200.

### Request

```bash
curl -X DELETE "https://api.qa.clankership.net/v1/workspaces/{slug}/agent-instructions?kind=ticket" \
  -H "Authorization: Bearer $CLANKER_API_KEY"
```

### Response

```json
{
  "kind": "ticket",
  "source": "team",
  "team": {
    "id": "Tm6PzN3kWq1J",
    "key": "WEB",
    "name": "Web"
  },
  "templates": [
    {
      "tag": null,
      "body": "## Problem\nWhat is wrong or missing, and for whom.\n\n## Outcome\nWhat is true when this ticket is done."
    },
    {
      "tag": "bug",
      "body": "## Steps to reproduce\n\n## Expected\n\n## Actual"
    }
  ],
  "guidance": "- Keep the title short: what changes, and where.\n- Use the team's tags. Do not invent new ones.",
  "defaults": {
    "status": "Todo",
    "tags": [],
    "owner": "creator"
  },
  "updatedAt": "2026-10-14T09:05:12.000Z",
  "updatedBy": {
    "person": {
      "id": "Us3NfP8kLq2V",
      "name": "Maya Patel",
      "email": "maya@acme.dev"
    },
    "agent": null
  }
}
```

### Errors

- `400` `InvalidInput`: The request is malformed, or a value is not allowed.
- `401` `Unauthorized`: The request has no valid API key.
- `403` `Forbidden`: The key's user or role may not do this.
- `404` `NotFound`: The workspace or object does not exist, or the caller cannot see it.
- `409` `Conflict`: The change conflicts with current data, such as a slug that is already in use.
- `500` `Internal`: Clanker Ship failed to handle the request. Retry, or report the response's x-request-id.
