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

# Meeting Skills

> Instruction packs the agent loads mid-call and drops when done — define once, attach to any scenario.

A **skill** is a playbook your agent opens only when the moment calls for it.
Its one-line `trigger` sits in the prompt every turn; the full `instructions`
load when the agent invokes the skill mid-meeting, and are dropped when the
flow completes.

You define a skill once on your account and attach it to any number of
scenarios — the same shape as [tools](/api-reference/custom-functions) and
[guardrails](/api-reference/guardrails).

```bash theme={null}
curl --request POST \
  --url https://api.waterr.ai/v1/skills \
  --header 'Authorization: Bearer wai_live_xxx' \
  --header 'Content-Type: application/json' \
  --data '{
    "name": "book_demo",
    "trigger": "book_demo: when the participant asks to see the product live.",
    "instructions": "Confirm intent first. Then collect their preferred time and timezone, one question per turn, and call schedule_demo once.",
    "gated_tool_names": ["schedule_demo"]
  }'
```

<Warning>
  Skills currently run on the **Gemini Live** pipeline only. A skill attached to a
  scenario that runs on the cascaded pipeline is fetched but never registered —
  the agent will not see its trigger and cannot invoke it. Check your scenario's
  model configuration before relying on one.
</Warning>

For the conceptual walkthrough — how invoke/revoke works, and the built-in
Create Scenario skill — see [Meeting Skills](/capabilities/meeting-skills).
This page is the API surface.

## Why a skill instead of a longer prompt

Without skills, every capability lives in the prompt all the time, which
causes two specific failures:

* **Over-eager behaviour.** The agent acts on a capability the participant only
  *mentioned*. A skill has to be deliberately invoked, so a passing remark
  cannot trigger it.
* **Prompt dilution.** Instructions for a rarely-used flow compete with the
  persona's core behaviour on every single turn. A skill costs one line until
  it is needed.

Reach for a skill when the behaviour is a **multi-step flow with a clear
start and end**. If it is a single rule that always applies, use a
[guardrail](/api-reference/guardrails). If it is one function call, use a
[tool](/api-reference/custom-functions).

***

## The Skill object

| Field | Type | Required | Description |
| - | - | - | - |
| `name` | string | ✓ | Identifier the agent passes to `invoke_skill`. Pattern `^[a-zA-Z0-9_-]{1,64}$`. Unique per account. |
| `trigger` | string | ✓ | One line describing *when* to invoke. Max 500 characters — it rides the base prompt on every turn. |
| `instructions` | string | ✓ | The instruction pack, delivered only after the agent invokes. Max 8,000 characters. |
| `gated_tool_names` | string\[] | – | Tool names that refuse to execute until this skill is invoked. Max 20. Default `[]`. |
| `active` | boolean | – | Account-level kill-switch. Default `true`. |
| `id` | uuid | read-only | |
| `scenarios` | array | read-only | Scenarios this skill is attached to. Returned only on the single-skill read. |

```json theme={null}
{
  "id": "9f0e1d2c-3b4a-5968-8776-655443322110",
  "membership_id": "a1b2c3d4-e5f6-7890-abcd-ef0123456789",
  "name": "book_demo",
  "trigger": "book_demo: when the participant asks to see the product live.",
  "instructions": "Confirm intent first. Then collect their preferred time and timezone, one question per turn, and call schedule_demo once.",
  "gated_tool_names": ["schedule_demo"],
  "active": true,
  "created_at": "2026-09-10T09:00:00.000Z",
  "updated_at": "2026-09-10T09:00:00.000Z"
}
```

<Note>
  Single-object endpoints return the object directly. **List** endpoints wrap
  their rows in `data`, which is where [cursor pagination](/api-reference/pagination)
  merges `has_more` and `next_cursor`.
</Note>

***

## Gating tools behind a skill

`gated_tool_names` is what makes a skill more than a prompt trick. Names listed
there refer to [tools](/api-reference/custom-functions) on your account, and
those tools **refuse to execute** until the skill is invoked.

```json theme={null}
{
  "name": "book_demo",
  "gated_tool_names": ["schedule_demo"]
}
```

Now `schedule_demo` cannot fire because the participant said the word "demo" in
passing — the agent has to commit to the flow first. This is the fix for
over-eager tool calls, and it is enforced at execution time, not by asking the
model nicely.

A name that matches no tool on your account simply gates nothing; it is not an
error. An empty array means the skill is purely behavioural.

***

## Attaching to a scenario

A skill does nothing until it is attached. Attach is idempotent, and
re-attaching a disabled pair re-activates it.

```bash theme={null}
curl --request POST \
  --url https://api.waterr.ai/v1/scenarios/{scenarioId}/skills/{id}/attach \
  --header 'Authorization: Bearer wai_live_xxx'
```

| Field | Description |
| - | - |
| `active` | Per-scenario switch. `false` keeps the attachment but hides the skill from the agent. Use it to stage a skill before enabling it. |
| `position` | Order the triggers are listed in. Lower first. |

Changes apply **from the scenario's next meeting**. A call already in progress
keeps the skill set it started with.

***

## Writing a good trigger

The trigger is the only part the agent sees every turn, and it is what decides
whether the skill fires at the right moment.

<CodeGroup>
  ```text Good theme={null}
  book_demo: when the participant asks to see the product live, or asks to
  schedule time with the team.
  ```

  ```text Too vague theme={null}
  book_demo: for demos.
  ```

  ```text Too eager theme={null}
  book_demo: whenever the participant seems interested in the product.
  ```
</CodeGroup>

* **Name the observable request**, not an inferred mood. "Seems interested" is
  a guess; "asks to see the product" is something the participant actually said.
* **Keep it under a line or two.** It is charged on every turn of every meeting.
* **Prefix with the skill name.** It reads naturally in the trigger list the
  agent receives and makes your logs easier to follow.

Instructions are the opposite: they are only loaded on demand, so be explicit.
Number the steps, say what to confirm before acting, and say what "done" means
so the agent knows when to revoke.

***

## Lifecycle

| Action | Effect |
| - | - |
| Create | Nothing changes until you attach it. |
| Attach / detach | Applies from the scenario's next meeting. |
| Edit the definition | Propagates to every attached scenario, next meeting. |
| `active: false` on the definition | Drops it from every scenario at once. |
| `active: false` on the attachment | Drops it from that one scenario. |
| Delete | Permanent, and detaches everywhere. Prefer `active: false`. |

If the skill lookup fails at meeting start, the meeting proceeds **without**
skills rather than failing to connect. Attaching a skill can never stop a call
from starting — but it also means a misconfigured fetch degrades silently, so
verify a new skill on a test meeting before relying on it.

***

## Endpoints

| Method | Path |
| - | - |
| `GET` | [`/skills`](/api-reference/endpoint/get-skills) |
| `POST` | [`/skills`](/api-reference/endpoint/post-skills) |
| `GET` | [`/skills/{id}`](/api-reference/endpoint/get-skills-id) |
| `PATCH` | [`/skills/{id}`](/api-reference/endpoint/patch-skills-id) |
| `DELETE` | [`/skills/{id}`](/api-reference/endpoint/delete-skills-id) |
| `GET` | [`/scenarios/{scenarioId}/skills`](/api-reference/endpoint/get-scenarios-scenarioid-skills) |
| `POST` | [`/scenarios/{scenarioId}/skills/{id}/attach`](/api-reference/endpoint/post-scenarios-scenarioid-skills-id-attach) |
| `PATCH` | [`/scenarios/{scenarioId}/skills/{id}`](/api-reference/endpoint/patch-scenarios-scenarioid-skills-id) |
| `DELETE` | [`/scenarios/{scenarioId}/skills/{id}`](/api-reference/endpoint/delete-scenarios-scenarioid-skills-id) |

## Errors

| Status | Meaning |
| - | - |
| `400` | Invalid payload. The `errors` array names the failing fields. |
| `401` | Missing or invalid API key. |
| `404` | Skill, scenario, or attachment not found on your account. |
| `409` | A skill with that `name` already exists on your account. |


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