> ## 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 Actions

> Reach into a live call from your own server — make the agent say something, tell it something, or drive a screen app.

Everything else in this API happens *before* a meeting (configure the agent) or
*after* it (read the transcript). Meeting Actions happen **during** it: your
server posts one action and it lands in the running conversation within a second.

```bash theme={null}
curl -X POST https://api.waterr.ai/v1/meetings/{meeting_id}/actions \
  -H "Authorization: Bearer wai_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{ "type": "context", "text": "CRM: this lead is on the Enterprise tier, renewal due 2026-12-01." }'
```

```json theme={null}
{ "ok": true, "action_id": "9c1f…", "meeting_id": "…", "type": "context", "delivered_to": 1 }
```

## The six actions

| `type` | What happens | Use it for |
| - | - | - |
| `say` | The text is spoken **verbatim** — no model in between. If the agent is mid-sentence it finishes, then speaks yours; `"interrupt": true` cuts it off. The sentence is added to the conversation history so the agent stays consistent with it. | Fixed sentences that must not be paraphrased: "Payment confirmed." "This call is being recorded." |
| `context` | A silent note is appended to the agent's context. It never starts a turn — the agent simply knows more from now on. | Facts from your systems the agent should use when relevant. |
| `message` | A message the agent **reads and answers now**, in its own words and persona. | "The participant just submitted the form: budget 50k, timeline Q1." |
| `stage.open` | Opens one of the scenario's [screen apps](https://docs.waterr.ai/api-reference/screen-apps.md) in a slot — exactly what the agent's own `present_screen` tool does, token and all. | Put your UI on the participant's screen from your backend. |
| `stage.patch` | Delivers a JSON payload to the open app (`update_screen`). | Highlight a row, advance a step, prefill a field. |
| `stage.close` | Closes it (`close_screen`). Idempotent. | — |

`context` and `message` are the two ways of *telling* the agent something. Neither
changes the agent's system prompt or persona — that is fixed for the call — they
add to the conversation, the same way a participant's words do.

```jsonc theme={null}
{ "type": "say",         "text": "Payment confirmed.", "interrupt": false }
{ "type": "context",     "text": "Lead is Enterprise tier." }
{ "type": "message",     "text": "Form submitted: budget 50k, timeline Q1. Acknowledge and move on." }
{ "type": "stage.open",  "app": "pricing_sheet", "placement": "side", "initial_state": { "plan": "growth" } }
{ "type": "stage.patch", "app": "pricing_sheet", "payload": { "highlight": "seat_price" } }
{ "type": "stage.close", "app": "pricing_sheet" }
```

Limits: `say.text` ≤ 2,000 characters; `context` / `message` ≤ 4,000; `initial_state`
and `payload` ≤ 8 KB of JSON; **30 actions per minute per meeting** per caller
(`429 rate_limited`). Send an `Idempotency-Key` header on retries, as on every write.

## Who may call it

Two kinds of credential, with different reach:

| | Workspace API key (`wai_…`) or Studio session | [Stage token](https://docs.waterr.ai/api-reference/screen-apps.md#verifying-the-meeting) (the one your screen app received) |
| - | - | - |
| `say` | ✅ | ❌ `403 scope_denied` — a page must never put words in the agent's mouth |
| `context` | ✅ | ✅ |
| `message` | ✅ | ✅ only if the app has `allow_participant_events: true` (the same switch that lets its events make the agent speak) |
| `stage.*` | ✅ any screen app of the scenario | ✅ its own app only |
| Which meetings | any meeting of a scenario your workspace owns | the one meeting the token was minted in |

Use the stage token as the bearer exactly as you received it:

```bash theme={null}
curl -X POST https://api.waterr.ai/v1/meetings/{meeting_id}/actions \
  -H "Authorization: Bearer <waterr_token from the page>" \
  -H "Content-Type: application/json" \
  -d '{ "type": "stage.patch", "payload": { "step": 3 } }'
```

This is how a screen app's backend talks to the call it was opened in — without a
workspace key anywhere near the browser.

## Responses

| Status | Body `error` | Meaning |
| - | - | - |
| `202` | — | Applied. `delivered_to` is the number of agent pipelines that took it (normally 1). `say` adds `mode`: `tts`, `overlay` or `model_voiced` (see below). |
| `400` | `invalid_action` | Unknown `type`, missing `text` / `app`, bad `placement`. |
| `401` | — | No usable credential. |
| `403` | `scope_denied` | A stage token asked for more than it may do. |
| `404` | `not_found` | No such meeting in your workspace (also what a foreign meeting answers). |
| `409` | `meeting_not_live` | The meeting exists but no agent is running in it right now. |
| `409` | `surface_unavailable` | `stage.*` on a call with no screen to render on — a Google Meet / Zoom / Teams bot call, or a scenario without screen apps. |
| `422` | `text_too_long`, `payload_too_large`, `unknown_app`, `no_app_open`, `wrong_app` | The action is well-formed but cannot apply as asked. |
| `429` | `rate_limited` | Over 30 actions / minute for this meeting. |

## How `say` is voiced

On the standard pipeline (speech-to-text → LLM → text-to-speech) your text goes
straight to text-to-speech: `mode: "tts"`, verbatim by construction.

On **Gemini Live**, the model produces the audio itself and there is no
text-to-speech stage to hand the sentence to. `say` is pushed straight into the
live session as a tagged system message the agent is instructed to voice word for
word, immediately (`mode: "live_inject"`). It is spoken within about a second and
is reliable, but not guaranteed byte-exact — a persona may soften punctuation.
Where that matters, prefer the standard pipeline for the scenario, or use
`message` and let the agent phrase it. (`mode: "overlay"` / `"model_voiced"` are
fallbacks used only while the session is still starting.)

## What the agent learns

* `say` — the sentence enters the history as something the agent said.
* `context` / `message` — your text, as a note or a message.
* `stage.open` / `stage.close` — a silent note naming the app and who opened it
  (`api_key:…`, `skill:…` or `stage_token:<app>`), so the agent can refer to what
  is on screen. The agent still cannot *see* the page; use `context` to tell it
  what matters.

Every action is also recorded on the meeting's event log as `action.<type>`
(`action.say`, `action.stage.open`, …) — the same log that will carry
screen-share and camera events.

## Not supported

* Google Meet, Zoom, Teams and phone calls: `say`, `context` and `message` work;
  `stage.*` answers `409 surface_unavailable` because there is no Waterr screen
  in those calls.
* Changing the persona or system prompt mid-call. Use `context`.
* Per-participant targeting: an action reaches the agent, and what it shows or
  says reaches everyone in the call.


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