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

# Procedures

> An agent's playbooks, one per kind of request: when to use it and the steps. The agent finds the one that fits and follows it, and writes new ones from first runs that work.

A procedure is the agent's playbook for one kind of request: what it's for, in your customers' words, and the steps that resolve it. Your team writes them from the requests it already handles, or the agent writes one itself after a first run that works. Either way, the next request of that kind is handled the same way, faster, and every step can be checked and fixed in plain text.

```json theme={null}
{
  "object": "procedure",
  "id": "prc_8fK2mQ0aLp4x",
  "agent_id": "support",
  "title": "Find the warranty end date from a serial number",
  "use_when": "Someone asks when their warranty ends, whether a laptop is still covered, or what cover a serial number has.",
  "inputs": [
    { "name": "serial", "type": "text", "description": "The laptop's serial number", "required": true, "example": "PF4A1QCG" }
  ],
  "goal": "The person knows when their warranty ends and what it covers.",
  "instructions": "1. Look the serial up with [[tool:tl_4c1d9e02ab]].\n2. Several laptops? Follow [[procedure:prc_Q2n8xT0wLk1a]].\n3. Read the end date and the cover type from the result.",
  "checks": "- The serial in the result matches {serial}.",
  "references": [
    { "type": "tool", "id": "tl_4c1d9e02ab", "name": "warranty_lookup", "missing": false },
    { "type": "procedure", "id": "prc_Q2n8xT0wLk1a", "title": "Several laptops at once", "missing": false }
  ],
  "body": "## Inputs\nGet these before you start (ask for any you don't have):\n- {serial} (text, required): The laptop's serial number. Like PF4A1QCG.\n\n## Goal\n…",
  "status": "active",
  "kind": "browser",
  "tools": [],
  "written_by": "person",
  "source_task_id": null,
  "revision": 3,
  "uses_30d": 41,
  "last_used_at": "2026-10-18T09:58:12.004Z",
  "avg_run_cents_30d": 6.4,
  "created_at": "2026-10-12T15:03:36.037Z",
  "updated_at": "2026-10-17T11:20:04.511Z"
}
```

| Field | What it does |
| - | - |
| `title` | What it does, up to 120 characters. Required. |
| `use_when` | The requests it's for, in the words people use, up to 500 characters. Required. This is what the agent matches requests against, so write it the way your customers ask. |
| `inputs` | What it needs before it starts, each typed: `name` (used in the text as `{name}`), `type` (`text`, `number`, `date`, `email`, `phone`, `url`, `country`, `choice` with its `options`, or `yes_no`), `description`, `required` (true by default) and an `example`. Up to 20. An `{input}` the text uses that isn't listed is added as required text. |
| `goal` | What done looks like, in a sentence or two. Markdown. |
| `instructions` | The steps, in order. Markdown: numbered steps work well. Required. |
| `checks` | What the agent confirms before it answers, as a list. Markdown. |
| `references` | Every link in the text, with what it points at now. See [below](#links). Read-only. |
| `body` | The whole text as the agent reads it, under `## Inputs`, `## Goal`, `## Instructions` and `## Checks`, with links as the tool's name and the procedure's title. You can send `body` instead of the parts (not with them): it's split into them by those headings (or a few "Check:" lines at the end), and tool names and quoted procedure titles in it are linked. The parts together are up to 20,000 characters. |
| `status` | `active`: the agent uses it. `draft`: kept, not used. `archived`: deleted the safe way, so it's never used and left out of lists; set it back to `active` or `draft` to restore it, with its revisions and test cases. |
| `kind` | What it needs to run: `browser` (the default), `desktop` (one of the agent's computers) or `chat` (nothing beyond the conversation, such as a policy answer from files and tools). |
| `tools` | Up to 10 of the agent's HTTP tools this procedure unlocks. See [below](#tools-a-procedure-unlocks). |
| `file_access` | `read_only` (the default) or `read_write`: the agent may create, change and delete its own files while a conversation follows this procedure, each change kept with the version it replaced. Test chats and evals change a copy only. Changing it makes no new revision. See [Procedures that edit files](https://docs.guidinghand.ai/concepts/agents.md#procedures-that-edit-files). |
| `written_by` | `person`, or `agent` for one the agent wrote, with `source_task_id`: the task it learned from. Read-only. |
| `revision` | 1 when made, one more with each change. Read-only. |
| `uses_30d`, `last_used_at` | How often the agent used it in the last 30 days, and when it last did. |
| `avg_run_cents_30d` | What a run that used it cost on average in the last 30 days, in cents at list price: a conversation (or a task on its own), priced by the tasks and chat replies that got the procedure. Only finished runs count; `null` when there are none. |

An agent can have up to 500 procedures. Manage them in the console on the agent's **Procedures** tab, or with the API:

| Endpoint | Does |
| - | - |
| `GET /v1/agents/{agent_id}/procedures` | Lists them, newest change first, a page at a time (without their text). `status` filters (archived ones only show with `status=archived`), `q` searches titles and "use when". |
| `POST /v1/agents/{agent_id}/procedures` | Adds one. |
| `GET /v1/agents/{agent_id}/procedures/{procedure_id}` | One, with its text: its parts, `body` and `references`. |
| `PATCH /v1/agents/{agent_id}/procedures/{procedure_id}` | Changes one: only the fields you send. A change is a new revision. |
| `DELETE /v1/agents/{agent_id}/procedures/{procedure_id}` | Deletes it for good, with its revisions. Its test cases stay, no longer tied to it. To keep a way back, archive it instead (`PATCH` with `status: "archived"`). |
| `GET /v1/agents/{agent_id}/procedures/{procedure_id}/revisions` | Every revision, newest first, with who saved it. |
| `POST /v1/agents/{agent_id}/procedures/match` | Tests a lookup. See [below](#how-the-agent-finds-one). |
| `POST /v1/agents/{agent_id}/procedures/batch` | Adds and changes many at once, each with its test cases. See [below](#many-at-once). |

```bash theme={null}
curl -X POST https://guidinghand.ai/v1/agents/support/procedures \
  -H "Authorization: Bearer $EAGER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Find the warranty end date from a serial number",
    "use_when": "Someone asks when their warranty ends, whether a laptop is still covered, or what cover a serial number has.",
    "inputs": [{ "name": "serial", "type": "text", "description": "The laptop'\''s serial number", "example": "PF4A1QCG" }],
    "goal": "The person knows when their warranty ends and what it covers.",
    "instructions": "1. Open https://support.example.com/warranty?serial={serial}.\n2. Read the end date and the cover type.",
    "checks": "- The serial on the page matches {serial}.",
    "kind": "browser"
  }'
```

### Links

A procedure's text can link the agent's tools and its other procedures. In the console, type `@` (or `[[` for a procedure) and pick one; it shows as a chip, with what it points at on hover. In the API, write `[[tool:<name or id>]]` and `[[procedure:<title or id>]]`: they're kept by id, so renaming the tool or the procedure keeps the link, and `references` lists each with what it points at now. A link to something that isn't there is refused (400), unless it was already in the text (a tool removed since, say): then it stays, marked `missing`. A tool a procedure links is unlocked with it, as if it were in `tools`. The agent reads a tool link as its name and a procedure link as its title and id, so it can open that procedure. Type `{` for one of its inputs.

### Many at once

`POST /v1/agents/{agent_id}/procedures/batch` takes up to 50 procedures in `procedures`. One without an `id` is added; one with an `id` changes only the fields you send (with `revision`, only while it's still at that revision, or you get a 409). Each can bring up to 20 `test_cases` (500 in all), written as for [`POST /v1/test_cases`](https://docs.guidinghand.ai/guides/evals): they're tied to that procedure, and one whose `key` you already have is replaced. With `test_set_id`, the cases are added to that test set too. All of it is saved, or none: the first thing that can't be saved says where (`procedures[1].test_cases[0]: …`). It returns the procedures in the order sent, each with its `test_cases`, and the counts `created`, `updated`, `unchanged` and `test_cases.created` / `test_cases.updated`.

```bash theme={null}
curl -X POST https://guidinghand.ai/v1/agents/support/procedures/batch \
  -H "Authorization: Bearer $EAGER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "procedures": [
      { "title": "Refunds", "use_when": "A customer asks for a refund", "body": "1. Look the order up.\n2. ...", "kind": "chat",
        "test_cases": [{ "key": "REF-01", "name": "Refund in 30 days", "prompt": "I want my money back for order 1234.", "runs_on": "chat", "expected": "It looks the order up before promising anything." }] },
      { "id": "prc_8f2k1m9qa", "revision": 3, "status": "draft" }
    ]
  }'
```

This is what the coach uses when the team accepts a change with procedures in it. The coach can also propose deleting a procedure: nothing happens until an admin accepts the change, and then the procedure is archived, never deleted for good. Undoing the change puts it back.

Anyone in the org can read procedures. Adding, changing and deleting them needs the admin role or an API key, and each change is in the [audit log](https://docs.guidinghand.ai/concepts/audit-log.md). Procedures aren't versioned with the agent: every version and the draft see the same ones, and deleting the agent deletes them.

## How the agent finds one

* With up to 20 active procedures, they're listed in the agent's instructions (title and "use when"), and it reads the one that fits with `read_procedure`.
* With more, it searches them with `find_procedure`, describing the request; Eager picks the best match, or none.
* It follows the steps itself, or passes the procedure to the browser or computer task it starts, so the task follows it too. `GET /v1/tasks/{task_id}` lists the `procedures` a task used, and a task that looked one up itself has a `procedure` event ("Following …").

A conversation keeps the list of procedures it started with: one added while it's going is listed from the agent's next conversation (with more than 20, `find_procedure` searches them as they are now).

`POST /v1/agents/{agent_id}/procedures/match` with `{ "need": "is my laptop still under warranty?" }` shows what a lookup would pick: the `procedure`, how sure it is (`confidence`), why (`reason`), and the `candidates` it chose from. The **Test lookup** box on the Procedures tab does the same. It needs the admin role or an API key, 30 a minute per org.

## Procedures the agent writes

When a browser or computer task the agent ran in a conversation works without a procedure, Eager reads what the task did and writes the procedure for next time: when to use it, the steps with placeholders for the inputs, and what to check. When the answer came from a JSON or API address that can be filled in directly, that address becomes the first step, so the next run is one page load instead of a click-through. (Tasks you start on a session don't write procedures.)

It's saved `active`, with `written_by: "agent"` and the `source_task_id` it came from, so you can replay the run it learned from and check each step against what really happened. Fix any step in the console like any procedure; your change is the next revision. No procedure is written when an active one already covers the same requests, or during a [cold eval run](https://docs.guidinghand.ai/guides/evals.md#cold-runs-and-procedures).

## Approval

Turn on **Approval** for a procedure whose runs someone must sign off first, like an order or a refund: `approval: { required: true, by, who, covers, note }`.

* Before a browser or computer task follows it, the conversation is asked to approve the run, and the reply waits (see [Approvals](https://docs.guidinghand.ai/concepts/conversations.md#approvals)). That holds when the agent names it as it starts the task, when it read the procedure and starts a task without naming it (the most recently read one asks), and when a task finds it with `find_procedure` (the task gets its steps and the tools it unlocks once approved; denied, it's told not to follow it, and those tools stay locked, also after a deploy).
* `by` is `anyone` (the person in the chat, or your team) or `team`. A run only your team may approve keeps its whole task your team's: every step the task hands back is yours to decide. `who` labels who signs off, and `note` says what to check; both show on the request.
* `covers` lists the agent's "Ask first" categories (`purchase`, `send`, …) the approved run may do without asking again. Anything else still asks.

A procedure the agent writes turns Approval on when its steps need a sign-off, with `covers: []`: every "Ask first" step still asks until your team reviews it and widens it.

## Model

A procedure runs on the agent's model unless you give it its own: `model` (or its **Model** row in the console), one of the agent [model](https://docs.guidinghand.ai/concepts/agents.md) values, or `null` for the agent's own.

* Once a conversation reads the procedure, Eager hands the conversation to that model: it reads the conversation so far and carries on, for the rest of the reply and the replies after it. Reading a procedure without a model of its own hands it back to the agent's model.
* While it's on the procedure's model, each new message from the person is checked first by a small model (the cheapest of the same provider, not charged): more of the same job, or a quick side question, stays on the procedure's model; a clear move to another job goes back to the agent's model before the reply, which reads the conversation so far; and when it's unclear, it stays and the agent is told to ask the person first.
* A browser or computer task that follows the procedure runs on its model too.
* A task you start on a session, which finds a procedure partway through, stays on the agent's model.
* The easy way to pick one: run the procedure's evals, then **Compare models** on the run. The run's pick sets the procedure's model, not the agent's (see [Comparing models](https://docs.guidinghand.ai/guides/evals.md)). An eval run that tries the agent on models keeps the models it tries.

## Category

`category` (or its **Category** box in the console) is the kind of request the procedure is for, like `Warranty status`. It comes from the same set as your [test cases'](https://docs.guidinghand.ai/guides/evals.md) `category`, which the eval charts group pass rates by, so a procedure and the cases that check it line up.

* A test case tied to the procedure is always in the procedure's category: a `category` sent for it is ignored, and when the procedure's category changes, its cases move with it. A case tied to no procedure, or to one without a category, keeps its own.
* A category is spelled as the org already has it, whatever its capitals: `warranty status` is saved as `Warranty status` when a case or procedure has that already.
* Changing only the category makes no new revision. `null` is none.
* List an agent's procedures in one category with `?category=Warranty%20status` (any capitals), or those with none with `?category=`.

Coach gives every procedure it writes a category, and its test cases are in it.

### Managing categories

The console's Procedures tab groups procedures by category. Its **Categories** button, or the categories API (`client.categories` in the SDKs), manages the org's set, which every agent and test case shares:

* **Add** one before anything is in it (`POST /v1/categories`). It shows on the Procedures tab as an empty group, with a way to start a procedure in it. Giving a procedure or test case a new category adds it too.
* **Rename** one (`PATCH /v1/categories/{name}`): every procedure and test case in it moves, and so do past eval runs' copies of the cases, so the charts keep it as one. Renaming to a name the org already has merges the two.
* **Remove** one (`DELETE /v1/categories/{name}`): whatever was in it has no category any more. Past eval runs keep theirs.

`GET /v1/categories` lists them A–Z with how many procedures and test cases are in each (`?agent_id=` also counts one agent's procedures).

## Tools a procedure unlocks

Some HTTP tools should only be called the way a procedure says, such as a refund after checking the order. List them in the procedure's `tools` (or its **Tools** row in the console): in a conversation, and in a task you start on a session, a tool any active procedure lists stays locked until the agent has read that procedure, so the steps come before the call. The browser and computer tasks a conversation starts aren't locked this way yet: they can call every one of the agent's HTTP tools.

```bash theme={null}
curl -X PATCH https://guidinghand.ai/v1/agents/support/procedures/prc_8fK2mQ0aLp4x \
  -H "Authorization: Bearer $EAGER_API_KEY" -H "Content-Type: application/json" \
  -d '{ "tools": ["lookup_order"] }'
```

* Names must be [HTTP tools](https://docs.guidinghand.ai/concepts/agents.md#http-tools) of the agent's published version; `[]` detaches them all. Publish a new tool before attaching it.
* If you later remove a tool from the agent, procedures keep its name (the console marks it "not on the agent") until you take it off.
* A tool no active procedure lists isn't locked: the agent can call it any time.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.