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

# Verified actions (your backend)

> Let your Animam agent trigger a real operation in YOUR system — cancel, claim, unlock — with the visitor's server-verified email, an SSRF-guarded call and an encrypted secret.

Site actions run in the visitor's **browser**. Verified actions run in **your backend**: Animam POSTs to an endpoint you own, carrying an identity the server has verified.

Nothing you write ever executes on Animam's infrastructure. You hand us a URL and a contract — not a program.

<Note>
  Verified actions require a **Builder** plan or higher (the `customTools` capability), alongside external MCP servers and custom form webhooks. On lower plans, creating the tool is refused.
</Note>

## When to use which

|          | Site action (WebMCP)                        | Verified action                                      |
| -------- | ------------------------------------------- | ---------------------------------------------------- |
| Runs in  | the visitor's browser                       | your backend                                         |
| Identity | the visitor's session on your site          | an email verified by Animam (one-time code or JWT)   |
| Good for | cart, filters, in-page flows                | account operations, entitlements, anything auditable |
| Guide    | [Site actions](/guides/webmcp-site-actions) | this page                                            |

## The guarantees

Enforced on every call, with nothing to configure:

* **The identity is never dictated by the model.** The `email` sent to your endpoint is the one the server verified. If the model proposes another one, it is dropped before the request is built.
* **Only your parameters get through.** The model can fill the params you declared, and nothing else.
* **No calls into a private network.** The URL is re-validated before every call, DNS resolution included — loopback, RFC1918 ranges, link-local and cloud metadata endpoints are refused, which also defeats DNS rebinding.
* **Your secret is encrypted at rest** (AES-256-GCM) and only decrypted at call time. No API reads it back.
* **The response is filtered** before it reaches the model: keys matching `token|secret|password|credential|api_key|bearer` are stripped, strings are capped at 2 000 characters, arrays at 20 items, depth at 4.
* **The call times out after 5 seconds.** A slow backend degrades one answer, never the conversation.

## Declare one

You create them through the API, with a key that carries `tools:write`.

<Note>
  Two agents, two roles. **Your personal agent** (Claude, Hermes, ClawBot) is the one that configures Animam over MCP — and this is the one tool type it may **not** create: `VERIFIED_ACTION` is absent from `CONFIGURABLE_TOOL_TYPES`, and any `secret` key is stripped from a config an agent submits. A secret is not delegated to a model. **Your site agent** is the one that uses the tool, firing the call mid-conversation. There is no dashboard screen for it either — yet.
</Note>

```bash theme={null}
curl -X POST https://api.animam.ai/tenants/YOUR_SLUG/tools \
  -H "X-API-Key: $ANIMAM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "claim_listing",
    "description": "Claim ownership of a listing for the verified visitor.",
    "type": "VERIFIED_ACTION",
    "config": {
      "url": "https://your-backend.example/api/claim",
      "secret": "whatever-your-backend-checks",
      "intent": "Let a verified owner claim their listing without leaving the chat.",
      "params": [{ "name": "listing_id", "required": true }],
      "messages": {
        "ok": "Done — check your inbox for the confirmation.",
        "already_claimed": "That listing is already claimed."
      },
      "failureMessage": "I could not reach the system. Try again shortly."
    }
  }'
```

The `secret` is sent in plaintext **once**, at write time, and stored encrypted. Sending an already-encrypted value is rejected.

## What your endpoint receives

```json theme={null}
POST https://your-backend.example/api/claim
Authorization: Bearer <your secret>
Content-Type: application/json

{ "email": "visitor@example.com", "listing_id": "W881000537" }
```

`email` is always present and always server-verified. The other keys are exactly the params you declared — no more.

## What your endpoint should answer

Return a JSON object with a `status` field. Animam looks it up in your `messages` map and makes the agent say that sentence verbatim:

```json theme={null}
{ "status": "already_claimed" }
```

If `status` is missing, Animam falls back to `ok` on a 2xx and `error` otherwise. On a non-2xx status with no matching message, the agent relays your `failureMessage` — it never invents an outcome.

<Warning>
  Design your endpoint as if it were public: authenticate the `Authorization` header, make the operation idempotent, and never return tokens or credentials in the response body. The response filter is defense in depth, not your authorization layer.
</Warning>

## Before the action can fire

The visitor must have proven their email inside the conversation. If they have not, the agent is told to run the verification first (`send_verification_code`, then `verify_visitor_code`) and to retry afterwards. An action can therefore never be triggered by someone merely claiming to be someone else.
