---
title: "Site action manifest"
canonical: https://guavaindex.com/specs/agent-actions
modified: 2026-11-10T08:36:22Z
lang: en
---

# Site action manifest

Status: draft 0.1 (`agent-actions/0.1`). Covers friction #6 (forms driven by pixels or guessed selectors), #11 (most sites have no action API), #22 (no discovery of which service can do a task) and #23 (multi-step flows have no machine-readable steps). Reference implementation: `agent_index/actions.py`.

A site publishes `/.well-known/agent-actions.json`. Agents call the actions it lists instead of clicking through pages, so a redesign no longer breaks them.

## Manifest

```json
{
  "version": "agent-actions/0.1",
  "site": "shop.example",
  "auth": {"type": "agent-grant"},
  "actions": [
    {
      "name": "add_to_cart",
      "description": "Add a product to the cart",
      "method": "POST",
      "path": "/api/cart",
      "effect": "write",
      "scope": "cart:write",
      "input": {
        "type": "object",
        "required": ["product_id", "quantity"],
        "properties": {
          "product_id": {"type": "string"},
          "quantity": {"type": "integer", "minimum": 1, "maximum": 20}
        }
      }
    }
  ],
  "flows": [
    {
      "name": "buy_product",
      "steps": [
        {"action": "search_products", "with": {"query": "$input.query"}},
        {"action": "add_to_cart", "with": {"product_id": "$steps.0.items.0.id", "quantity": "$input.quantity"}},
        {"action": "checkout", "with": {"cart_id": "$steps.1.cart_id"}}
      ]
    }
  ]
}
```

| Field | Meaning |
|---|---|
| `input` | JSON Schema (2020-12). Agents validate before sending and get every problem back as structured errors. |
| `effect` | `read`, `write`, `send`, `spend` or `delete`. Agents feed it to their [approval policy](/specs/approvals), so spending or deleting can wait for the person. |
| `scope` | The grant scope the action needs (see [identity](/specs/agent-identity)). |
| `flows` | Ordered steps. `$input.x` reads the flow's inputs; `$steps.N.path` reads an earlier step's output. |
| `cost` | For `spend` actions: `{"field": "price", "currency": "USD"}` names the input that holds the amount, or `{"amount": 25, "currency": "USD"}` fixes it, so the agent can check its mandate and ask before sending. |
| `endpoints` | Added by the middleware: where receipts, refunds, revocations and negotiation live. |

## Errors

Actions fail with `{"error": {"code": "...", "message": "...", "field": "...", "retry_after": 30}}` using the codes in [agent errors](/specs/agent-errors).

## Discovery

agent-index loads manifests (`discover_actions`) and searches across them (`find_action "buy printer paper"`), returning which site offers which action. A shared registry of manifests is the same idea at web scale, and fits the index naturally: the crawler picks up `/.well-known/agent-actions.json` alongside pages.

## Relation to existing work

The input schemas match MCP tool definitions, so a site can serve the same actions as an MCP server. OpenAPI describes endpoints but not effects, scopes for delegated agents, or flows; a manifest can be generated from an OpenAPI file plus those three additions.

## Serving actions

A site doesn't write an API layer. It hands the middleware one function per action:

```python
def checkout(inp, ctx):          # inp is already validated against the schema
    deal = ctx.agreement({"product_id": inp["product_id"]})   # a negotiated price, if any
    receipt = ctx.charge(price, "USD", item=inp["product_id"])  # under the caller's mandate
    return {"order_id": receipt["id"]}

app = AgentFriendly(app, host="shop.example", actions=MANIFEST, merchant=shop,
                    handlers={"checkout": checkout, ...})
```

The middleware routes `method` and `path` to the handler, rejects bad input with every problem listed (`problems`), requires a grant for any action with a `scope` or an effect other than `read`, checks the scope, and turns `AgentError`s and crashes into structured errors (never a traceback).

## Calling actions

`AgentClient.call_action` and `run_flow` (MCP tools of the same names) fetch the manifest, validate input, sign each request with the narrowest grant that has the action's scope, and ask the person through the approval policy before `spend`, `send` and `delete` actions (`needs_human`, then `confirm=true`). If a flow fails, the error names the step and carries the outputs completed so far. Manifests the agent or the crawler sees are stored in the shared index, so `find_action` searches every site any agent has seen.
