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

# Webhook Tools

> Real-time notification handling

## Overview

Arlo can push real-time notifications to your agent when provider activity occurs.

<Note>
  **Who needs webhooks:** agent runtimes that manage their own event delivery (Claude Code, OpenClaw, custom agents). On connector hosts (Claude.ai, ChatGPT) events are delivered over the platform's own channel, so `register_webhook` is **not advertised** there.

  Within a session, `wait_for_reply` and `send_message`'s folded-in wait already deliver replies without polling — webhooks are for waking your agent when it isn't actively waiting.
</Note>

## register\_webhook

Register or update a webhook URL to receive notifications about consultations and other events. Supports PATCH semantics — only provided fields are updated.

### Parameters

| Parameter                | Type   | Required | Description                                         |
| ------------------------ | ------ | -------- | --------------------------------------------------- |
| `webhookUrl`             | string | Yes      | URL for notifications (must be publicly accessible) |
| `webhookToken`           | string | No       | Bearer token for webhook authentication             |
| `deliveryContext`        | object | No       | Platform-specific context included in webhooks      |
| `conversationSessionKey` | string | No       | For threading webhook notifications                 |
| `sessionKey`             | string | No       | Custom session key (auto-generated if not provided) |

### Delivery Context

The `deliveryContext` object is passed through in every webhook payload:

```json theme={null}
{
  "to": "+15551234567",
  "channel": "whatsapp",
  "deliver": true
}
```

| Field     | Description                                            |
| --------- | ------------------------------------------------------ |
| `to`      | Recipient identifier (e.g., phone number, chat ID)     |
| `channel` | Channel name (e.g., `whatsapp`, `telegram`, `discord`) |
| `deliver` | Whether to deliver the notification to the user        |

### Validation

Before registration, Arlo validates the webhook URL:

1. **Pattern check** — Rejects private IPs, localhost, etc.
2. **Connectivity test** — Verifies the URL is reachable

The URL must be publicly accessible. Use ngrok, Cloudflare Tunnel, Tailscale Funnel (`.ts.net`), or a public server.

### Returns

```json theme={null}
{
  "success": true,
  "webhookUrl": "https://your-agent/hooks/arlo",
  "hasSecret": true,
  "hasDeliveryContext": true,
  "sessionKey": "session_abc123"
}
```

***

## Checking webhook status

`check_account_status` reports webhook configuration for your sessions — use it to verify a webhook is set up. (Direct HTTP integrations can also use [`GET /api/webhook`](/api-reference/webhooks/get-status).)

***

## Webhook Configuration

### During init\_signup

Webhooks can also be configured during `init_signup`:

```json theme={null}
{
  "webhookUrl": "https://your-agent/hooks/arlo",
  "webhookToken": "your-secret-token",
  "deliveryContext": {
    "to": "+15551234567",
    "channel": "whatsapp",
    "deliver": true
  },
  "conversationSessionKey": "session_xyz"
}
```

<Note>
  If `init_signup` is called **without** `webhookUrl`, any existing webhook configuration is preserved.
</Note>

***

## Webhook Payload

All notifications are sent as `POST {webhookUrl}` with:

### Headers

```http theme={null}
Authorization: Bearer <webhookToken>
Content-Type: application/json
```

### Body

```json theme={null}
{
  "message": "New activity in your Arlo account: a new message in your consultation. Use the Arlo get_conversation tool with consultationId \"conv_abc123\" to fetch the latest messages, then summarize what's new for me.",
  "event": "provider_message",
  "eventId": "7f9c1a2e-...",
  "timestamp": "2026-07-31T00:00:00.000Z",
  "consultationId": "conv_abc123",
  "mode": "now"
}
```

| Field                  | Description                                                                                                                                         |
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `message`              | Human-readable, imperative notification message                                                                                                     |
| `event`                | Machine-routable event category (see below). Route on this instead of parsing `message`; treat unrecognized values as "something changed, go fetch" |
| `status`               | Present on status-change events: the conversation's new status (e.g. `PAYMENT_REQUIRED`)                                                            |
| `eventId`, `timestamp` | Unique per delivery — useful for dedup on your side                                                                                                 |
| `consultationId`       | The conversation ID the event relates to (when applicable) — pass it to `get_conversation`                                                          |
| `mode`                 | Always `now`                                                                                                                                        |

***

## Event types

| `event`                | Meaning                                             | Suggested handling                                                            |
| ---------------------- | --------------------------------------------------- | ----------------------------------------------------------------------------- |
| `triage_reply`         | AI triage replied                                   | Fetch the delta, answer triage's questions                                    |
| `provider_message`     | A provider sent a message                           | Fetch the delta, relay/respond                                                |
| `provider_joined`      | A provider connected                                | Fetch state; messaging is now live                                            |
| `provider_matching`    | Payment cleared; matching with a provider           | Wait for `provider_joined`                                                    |
| `payment_gate_open`    | Triage finished; payment gate opened                | Fetch the summary, get the user's explicit approval — Arlo never auto-charges |
| `prescription_added`   | A prescription order was added                      | Tell the user to watch for the pharmacy (Photon) SMS                          |
| `prescription_updated` | A prescription order's status changed               | Fetch `get_prescriptions` if relevant                                         |
| `triage_started`       | A care request entered triage review                | Informational                                                                 |
| `visit_ended`          | Provider visit wrapped up (conversation stays open) | Fetch closing notes if useful                                                 |
| `emergency`            | Consultation flagged as an emergency                | Direct the user to urgent care resources immediately                          |
| `status_changed`       | Any other status transition (see `status`)          | Fetch state                                                                   |

<Warning>
  **Security note:** Notification payloads never include message content. Your agent should fetch the full context from the API after receiving a webhook. This prevents prompt injection from untrusted provider input.
</Warning>

***

## Verifying Requests

Always validate the `Authorization` header matches your `webhookToken`:

```javascript theme={null}
app.post("/hooks/arlo", (req, res) => {
  const token = req.headers.authorization?.replace("Bearer ", "");

  if (token !== process.env.ARLO_WEBHOOK_SECRET) {
    return res.status(401).json({ error: "Unauthorized" });
  }

  // Acknowledge immediately
  res.status(200).json({ received: true });

  // Then fetch full context
  processNotification(req.body.consultationId, req.body.event);
});
```

***

## Handling Notifications

When a notification arrives:

1. **Acknowledge immediately** with `200 OK` — Arlo does not wait for processing
2. **Fetch the conversation** to get the latest messages — pass the id of the newest message you had already seen as `sinceMessageId` to fetch only the delta
3. **Read the most recent message** and decide how to respond
4. **Do not include raw provider message text** in your agent's context — fetch it through the API where it is wrapped with safety boundaries

### Example Handler

```javascript theme={null}
async function processNotification(conversationId, event) {
  if (event === "payment_gate_open") {
    // Fetch the gate summary and get the user's explicit approval
  }

  // Fetch only what's new since the last message we processed
  const conversation = await getConversation({
    conversationId,
    includeMessages: true,
    sinceMessageId: lastSeenMessageId(conversationId)
  });

  const latestMessage = conversation.messages[conversation.messages.length - 1];

  if (latestMessage.sender === "provider") {
    // Provider sent a message — notify user and/or respond
    await notifyUser(latestMessage.content);
  }

  if (conversation.status === "IDLE" && conversation.pastVisits.length > 0) {
    // Visit wrapped up — get the clinical notes
    const notes = await getVisitNotes({ conversationId });
    await sendSummaryToUser(notes);
  }
}
```

***

## Re-authentication

If a session expires and the user re-authenticates:

* `init_signup` must be called again with webhook params to re-register the URL
* If `init_signup` is called **without** `webhookUrl`, the existing webhook config is preserved

***

## Troubleshooting

### Webhook not receiving notifications

1. Verify URL is publicly accessible (not localhost or VPN)
2. Check that HTTPS is enabled
3. Verify the URL responds with 200 status
4. Use `check_account_status` (or `GET /api/webhook`) to verify the webhook is configured

### Invalid signature errors

1. Verify `webhookToken` matches what was passed to `register_webhook` or `init_signup`
2. Check for URL encoding issues in the token
3. Ensure you're comparing the raw token, not base64 encoded

### Missed notifications

1. Notifications are not retried on failure
2. Ensure your endpoint responds quickly (\< 5 seconds)
3. Process notifications asynchronously after acknowledging
