# Working with tasks and tickets

> List and update tasks, move tasks between stages, read ticket conversations, create and assign tickets, and understand operations that are still rolling out.

URL: https://www.agencytitan.com/help/guides/working-with-tasks-and-tickets/

Tasks represent planned work. Tickets represent inbound requests and reactive work. The public API lets integrations read and update both while preserving the permissions and state rules used by AgencyTitan.

Start with the [API Quickstart](/help/getting-started/api-quickstart/) for authentication, envelopes, pagination, errors, and rate limits. The examples below assume:

```bash
BASE_URL="https://api.agencytitan.com"
```

## List and filter tasks

[`GET /v1/tasks`](/docs/tag/tasks/GET/v1/tasks) returns task-grain rows. Several row fields, including `status`, `assigned_to`, `due_date`, `priority_level`, and `start_date`, describe the current stage.

Use first-class query parameters for common filters:

```bash
curl "$BASE_URL/v1/tasks?status_scope=active&client_ids=66666666-6666-4666-8666-666666666666&sort=due_date&order=asc&limit=50" \
  --header "Authorization: Bearer $AGENCYTITAN_API_KEY" \
  --header "Accept: application/json"
```

Useful filters include:

- `status_scope`, `statuses`, `assigned_to`, `due_date_from`, and `due_date_to` for the current stage
- `process_status_scope`, `process_statuses`, `process_assigned_to`, and `process_due_date_from` for the parent task
- `client_ids`, `process_definition_id`, `priority_level`, and `tags` for task-level selection
- `filter_group` for supported custom-field and nested conditions

Values within one array filter are combined with OR. Different filters are combined with AND. Resolve status, priority, and tag values with [`GET /v1/tasks/options`](/docs/tag/tasks/GET/v1/tasks/options) instead of guessing tenant-configured values.

For assigned work at stage grain, use [`GET /v1/task-stages`](/docs/tag/task-stages/GET/v1/task-stages) and filter by `assigned_to`.

## Update current-stage status, assignment, or due date

[`POST /v1/tasks/manage`](/docs/tag/tasks/POST/v1/tasks/manage) is an action-enum composite for three same-risk updates. The required `action` selects one of:

- `update_status`, with `status`
- `assign`, with an `assigned_to` array that replaces the current-stage assignment
- `set_due_date`, with an ISO 8601 `due_date` or `null` to clear it

For example:

```bash
curl --request POST "$BASE_URL/v1/tasks/manage" \
  --header "Authorization: Bearer $AGENCYTITAN_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "action": "set_due_date",
    "task_id": "11111111-1111-4111-8111-111111111111",
    "due_date": "2026-08-15T17:00:00Z"
  }'
```

Use the status values returned by `GET /v1/tasks/options` for `update_status`.

## Update custom task fields

[`POST /v1/tasks/update-fields`](/docs/tag/tasks/POST/v1/tasks/update-fields) patches custom fields by registered field key:

```bash
curl --request POST "$BASE_URL/v1/tasks/update-fields" \
  --header "Authorization: Bearer $AGENCYTITAN_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "task_id": "11111111-1111-4111-8111-111111111111",
    "field_values": {
      "campaign_url": "https://example.com/campaign",
      "estimated_hours": 8
    }
  }'
```

The operation rejects system task fields. Use the dedicated task or stage operation for status, assignment, and dates. Valid custom fields are attempted independently, and the response reports each field as `applied` or `rejected`; inspect every result instead of treating HTTP 200 as proof that every field changed.

## Move a task to another stage

[`POST /v1/tasks/transition-stage`](/docs/tag/tasks/POST/v1/tasks/transition-stage) moves a task only along a declared transition from its current stage:

```bash
curl --request POST "$BASE_URL/v1/tasks/transition-stage" \
  --header "Authorization: Bearer $AGENCYTITAN_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "task_id": "11111111-1111-4111-8111-111111111111",
    "target_stage_id": "22222222-2222-4222-8222-222222222222",
    "reason": "Client approved the deliverable"
  }'
```

An undeclared target, unmet dependency, or missing required field returns a conflict without changing the task. The optional `reason` is invocation context returned to the caller; it is not stored on the task.

## Create and update tickets

[`GET /v1/tickets`](/docs/tag/tickets/GET/v1/tickets) supports search, resource-specific sorting, and filters for status, client, assignee, department, inbox, priority, tags, SLA state, and creation time.

Create a ticket with [`POST /v1/tickets`](/docs/tag/tickets/POST/v1/tickets). `subject` is required; `description`, `client_id`, `assigned_to`, and `priority` are optional.

Use [`PATCH /v1/tickets/{id}`](/docs/tag/tickets/PATCH/v1/tickets/{id}) to replace provided fields only. It can change status, priority, client link, and the full assignee set. An empty `assigned_to` array or `null` clears assignment.

The composite [`POST /v1/tickets/manage`](/docs/tag/tickets/POST/v1/tickets/manage) can create or update a ticket and can add a safe internal note to an existing ticket. Its `comment` field never queues an outbound reply.

## Assign tickets

[`POST /v1/tickets/{id}/assign`](/docs/tag/tickets/POST/v1/tickets/{id}/assign) supports two actions:

- `manual` replaces the full assignee set. An empty array unassigns the ticket. You can also provide `target_inbox_id` to move and assign atomically and `reason` for audit context.
- `smart` runs AgencyTitan's live assignment scorer. Do not send manual-assignment fields with this action.

```bash
curl --request POST "$BASE_URL/v1/tickets/99999999-9999-4999-8999-999999999999/assign" \
  --header "Authorization: Bearer $AGENCYTITAN_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "action": "manual",
    "assigned_to": ["33333333-3333-4333-8333-333333333333"],
    "reason": "Route to the web support team"
  }'
```

## Read ticket conversations

[`GET /v1/ticket-messages`](/docs/tag/ticket-messages/GET/v1/ticket-messages) requires `ticket_id` and returns inbound and outbound messages plus internal notes. The default order is chronological. Use `is_internal_note` as the stable discriminator.

```bash
curl "$BASE_URL/v1/ticket-messages?ticket_id=99999999-9999-4999-8999-999999999999&order=asc" \
  --header "Authorization: Bearer $AGENCYTITAN_API_KEY" \
  --header "Accept: application/json"
```

Attachment references are metadata only. Follow [Files and attachments](/help/guides/files-and-attachments/) to obtain a download URL.

[`GET /v1/ticket-messages/{id}`](/docs/tag/ticket-messages/GET/v1/ticket-messages/{id}) also requires the parent `ticket_id` query parameter because conversation-item reads are ticket-scoped.

## Operations rolling out

These capabilities are not yet fully exposed by the current OpenAPI document:

- **Task creation.** The existing `tasks.manage` operation intentionally omits creation. Live task creation can apply billing and deferred-creation rules and can optionally advance a stage, so the planned operation is approval-gated instead of being treated as a simple insert.
- **Outbound ticket sending.** Conversation reads, internal notes, and reply drafts are available now: [`POST /v1/ticket-drafts/save`](/docs/tag/ticket-drafts/POST/v1/ticket-drafts/save) creates or updates a draft, and the `ticket-drafts` tag includes read and review-queue operations. A saved draft always keeps `draft` delivery status; the operation that actually sends an outbound message is still rolling out.
- **Linking tasks to tickets.** Ticket and task records can be managed independently, but no public link or unlink operation is in the current reference.

Do not guess route names or request bodies for rolling-out operations. Wait until an operation appears in the generated [API reference](/docs/) and the current OpenAPI document before calling it.
