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

# Agent tool examples

> Map common AI agent tasks to Public API v1 endpoints and tool definitions.

These patterns target developers building **AI agent tools** (OpenAI function calling, Claude tools, LangChain, etc.) on top of the Public API. Each workflow uses at most two list calls before drilling into detail.

Prerequisites: API key with `tickets:read`. See [Authentication](/api/authentication).

## Core tool set

Most agent integrations expose four tools:

| Tool                  | API call                                                  |
| --------------------- | --------------------------------------------------------- |
| `list_inbox_views`    | `GET /public/v1/views`                                    |
| `list_tickets`        | `GET /public/v1/tickets` (+ optional `view_id`, `cursor`) |
| `get_ticket`          | `GET /public/v1/tickets/:ticketNumber`                    |
| `get_ticket_messages` | `GET /public/v1/tickets/:ticketNumber/messages?order=asc` |

Link to [Scalar](https://api.armin.cx/docs/v1) for full request/response schemas.

***

## Use case 1 — "Show open tickets from the last 7 days"

Saved views encode filters. The agent discovers views, picks one that matches "open + recent", then lists tickets.

**Steps:**

1. `GET /public/v1/views` — scan `name` and `filters` for a view like "Open — last 7 days".
2. `GET /public/v1/tickets?view_id=<uuid>&limit=40` — return ticket summaries to the user or downstream LLM.

If no suitable view exists, tell the user to create one in [Inbox views](/inbox/views-and-filters) — v1 cannot apply ad-hoc date/status filters.

***

## Use case 2 — "Summarize ticket #1234"

**Steps:**

1. `GET /public/v1/tickets/1234/messages?order=asc&limit=100` — chronological thread.
2. Concatenate `messages[].text` (respect the 10,000 character per-message cap) and pass to your LLM summarizer.

Optional: `GET /public/v1/tickets/1234` first for subject, status, contact, and tags.

Internal notes, drafts, and system events are **excluded** from message responses.

***

## Use case 3 — "How many open tickets in my Returns view?"

**Do not** paginate all tickets to count them. Use the view's `count` field:

**Steps:**

1. `GET /public/v1/views` — find the view where `name` matches "Returns" (or similar).
2. Read `count` from that view object.

```json theme={null}
{
  "id": "019b...",
  "name": "Returns",
  "count": 42,
  "filters": [ ... ]
}
```

`count` reflects the current filter snapshot and may lag the inbox by a few seconds (read replica).

***

## OpenAI tool definitions (example)

```json theme={null}
[
  {
    "type": "function",
    "function": {
      "name": "list_inbox_views",
      "description": "List saved inbox views with filter metadata and ticket counts. Use before list_tickets when the user mentions a view name or filter (open, returns, channel, etc.).",
      "parameters": {
        "type": "object",
        "properties": {
          "limit": { "type": "integer", "description": "Max views per page (1-100)", "default": 50 },
          "cursor": { "type": "string", "description": "Pagination cursor from a previous call" }
        }
      }
    }
  },
  {
    "type": "function",
    "function": {
      "name": "list_tickets",
      "description": "List support tickets. Pass view_id from list_inbox_views to apply saved filters.",
      "parameters": {
        "type": "object",
        "properties": {
          "view_id": { "type": "string", "description": "UUID of a saved inbox view" },
          "limit": { "type": "integer", "default": 40 },
          "cursor": { "type": "string" },
          "include_ticket_fields": { "type": "boolean", "default": false }
        }
      }
    }
  },
  {
    "type": "function",
    "function": {
      "name": "get_ticket_messages",
      "description": "Get the conversation thread for a ticket by ticket number (e.g. 1234). Use order asc for summarization.",
      "parameters": {
        "type": "object",
        "properties": {
          "ticket_number": { "type": "integer", "description": "Human-readable ticket number" },
          "order": { "type": "string", "enum": ["asc", "desc"], "default": "asc" },
          "limit": { "type": "integer", "default": 30 },
          "cursor": { "type": "string" }
        },
        "required": ["ticket_number"]
      }
    }
  }
]
```

**Handler mapping:**

```typescript theme={null}
const BASE = 'https://api.armin.cx/public/v1'
const headers = { 'cx-api-key': process.env.CX_API_KEY! }

async function list_inbox_views(args: { limit?: number; cursor?: string }) {
  const params = new URLSearchParams()
  if (args.limit) params.set('limit', String(args.limit))
  if (args.cursor) params.set('cursor', args.cursor)
  const res = await fetch(`${BASE}/views?${params}`, { headers })
  return res.json()
}

async function list_tickets(args: {
  view_id?: string
  limit?: number
  cursor?: string
  include_ticket_fields?: boolean
}) {
  const params = new URLSearchParams()
  if (args.view_id) params.set('view_id', args.view_id)
  if (args.limit) params.set('limit', String(args.limit))
  if (args.cursor) params.set('cursor', args.cursor)
  if (args.include_ticket_fields) params.set('include', 'ticket_fields')
  const res = await fetch(`${BASE}/tickets?${params}`, { headers })
  return res.json()
}

async function get_ticket_messages(args: {
  ticket_number: number
  order?: 'asc' | 'desc'
  limit?: number
  cursor?: string
}) {
  const params = new URLSearchParams()
  if (args.order) params.set('order', args.order)
  if (args.limit) params.set('limit', String(args.limit))
  if (args.cursor) params.set('cursor', args.cursor)
  const res = await fetch(
    `${BASE}/tickets/${args.ticket_number}/messages?${params}`,
    { headers },
  )
  return res.json()
}
```

***

## Claude tool definitions (example)

```json theme={null}
{
  "name": "list_inbox_views",
  "description": "List saved inbox views. Returns id, name, count, and filters. Call this when the user refers to a view by name or wants filtered ticket lists.",
  "input_schema": {
    "type": "object",
    "properties": {
      "limit": { "type": "integer", "maximum": 100 },
      "cursor": { "type": "string" }
    }
  }
}
```

Use the same HTTP handlers as above. Claude tool use blocks return `tool_result` JSON — pass the API response body through unchanged so the model sees `success`, `data`, and `pagination`.

***

## Agent design tips

* **Resolve view names in tool 1** — let the model pick `view_id` from `list_inbox_views` output instead of hard-coding UUIDs.
* **Use ticket numbers in user-facing paths** — URLs use `#1234` / `ticket_number`, not UUIDs.
* **Paginate messages for long threads** — follow `pagination.next_cursor` when `has_more` is true.
* **Handle 404 gracefully** — `VIEW_NOT_FOUND` often means the linked agent cannot see that view; suggest a service key or different view.
* **Respect rate limits** — cache view lists; avoid re-fetching on every turn.

## Related

* [Filtering & saved views](/api/filtering-and-saved-views)
* [Pagination](/api/pagination)
* [Getting started](/api/getting-started)
