> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://help.moveworks.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://help.moveworks.com/_mcp/server.

# Receive proactive notifications via outbound webhook

The Conversations API is in **Controlled Availability**. Endpoints, behaviors, and configuration surfaces may change during CA. Contact your Moveworks account team to participate.

Receive push notifications from Moveworks when a new system-initiated message is available for one of your users. Outbound webhook push is the recommended delivery path for proactive notifications: lower latency to your users and lower load on Moveworks compared to [polling](/api-reference/conversations-api/proactive-notifications/polling). On each notification, retrieve the full content via the events endpoint.

Before reading this page, review [Proactive notifications overview](/api-reference/conversations-api/proactive-notifications/overview) for architecture, routing behavior, and prerequisites.

## Register the outbound webhook

Navigate to **Setup → Core Platform → Outbound Webhooks** in your Moveworks Admin Portal and click **Create**. Configure the following fields:

| Field                     | Required | Description                                                                                                                                    |
| ------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| **Webhook URL**           | Yes      | Your HTTPS endpoint. Must be publicly accessible and return a `2xx` response within 15 seconds.                                                |
| **Event Type**            | Yes      | The event(s) to subscribe to. Currently only `CONVERSATION_RESPONSE_RECEIVED` is supported.                                                    |
| **Rotation Interval**     | No       | How often Moveworks rotates the signing key, in days. Omit to disable rotation.                                                                |
| **Rotation Grace Period** | No       | Overlap window during key rotation when both the old and new signing keys remain valid, so in-flight deliveries complete without interruption. |

You can update subscribed event types or delete a webhook endpoint through the same dashboard.

### Event types

| Event                            | Description                                                                                                                                                                                                                                                                                                      |
| -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `CONVERSATION_RESPONSE_RECEIVED` | A new system-initiated message is available in a conversation for the specified user. Fires for any Moveworks proactive notification routed to the user's CAPI channel — including Concierge Notifications, native approvals, employee comms, async action callbacks, and custom Agent Studio Listener triggers. |

## Webhook payload

Each delivery is a `POST` request to your endpoint with a JSON body.

**Envelope structure**

```json
{
  "id": "evt_32bt9WkPqRnEtYsLmXc4v",
  "type": "CONVERSATION_RESPONSE_RECEIVED",
  "timestamp": "2026-03-27T12:34:56Z",
  "data": {
    "user_id": "user@yourcompany.com"
  }
}
```

| Field          | Type   | Description                                                                                                                                                                                                                                           |
| -------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`           | string | Unique identifier for this webhook event. Base-62 identifier prefixed with `evt_` (for example, `evt_32bt9WkPqRnEtYsLmXc4v`), consistent with other Conversations API resource IDs. Remains constant across retries; use this for idempotency checks. |
| `type`         | string | The event type (for example, `CONVERSATION_RESPONSE_RECEIVED`).                                                                                                                                                                                       |
| `timestamp`    | string | ISO 8601 timestamp of when the event occurred.                                                                                                                                                                                                        |
| `data.user_id` | string | The user associated with the event.                                                                                                                                                                                                                   |

The payload is a **thin envelope**: it signals that a system-initiated message exists for a user, but does not embed the full message content. After receiving a webhook, call the events endpoint to retrieve the message(s) in canonical order.

## Request headers

Every webhook delivery includes the following HTTP headers:

| Header              | Description                                                                                                                                   |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `webhook-id`        | The unique event identifier (matches `id` in the payload, prefixed with `evt_`). Constant across retries.                                     |
| `webhook-timestamp` | ISO 8601 timestamp of when the delivery was attempted (for example, `2026-08-03T23:23:57.846096+00:00`).                                      |
| `webhook-signature` | Base64-encoded RSA-SHA256 signature. Space-delimited when multiple signatures are present (during a key-rotation grace period).               |
| `webhook-kid`       | Key ID of the signing key used. Use this to look up the corresponding public key from the Moveworks JWKS endpoint for signature verification. |
| `user-agent`        | `mw/v1beta1`, identifies Moveworks as the sender.                                                                                             |

## Signature verification

Moveworks uses asymmetric RSA signature verification (RSA-SHA256). Moveworks holds a private key and signs each webhook payload. Your integration verifies the signature using the corresponding public key, fetched from the Moveworks JWKS endpoint for your tenant.

**JWKS endpoint.** Your tenant's JWKS endpoint is at your Admin Portal domain, at the standard `/.well-known/jwks.json` path:

```
https://{org}.moveworks.com/.well-known/jwks.json
```

The endpoint is publicly accessible (no auth required) and returns a standard JWKS document with one or more registered public keys, each identified by its `kid` field.

#### Retrieve the public key

Use the `webhook-kid` header value on the incoming delivery to look up the matching key (by its `kid` field) in `https://{org}.moveworks.com/.well-known/jwks.json` for your tenant.

#### Construct the signing message

Concatenate the `webhook-id`, `webhook-timestamp`, and raw request body.

#### Verify the signature

Apply RSA-SHA256 verification using the public key against the value(s) in the `webhook-signature` header.

#### Reject unverified requests

If no signature matches, discard the request and return a non-2xx response.

During a key-rotation grace period, the `webhook-signature` header may contain more than one signature. Verify against each and accept the request if any signature is valid.

### Key rotation

Moveworks rotates the webhook signing key on the interval you configure. Rotation is transparent to your integration as long as your verifier looks up the public key by the `webhook-kid` header on each request.

**What happens on rotation:**

1. Moveworks generates a new signing key with a new `kid` in the JWKS document.
2. New webhook deliveries are signed with the new key. The `webhook-kid` header on those deliveries reflects the new key.
3. During the **Rotation Grace Period**, the previous key remains available at `https://{org}.moveworks.com/.well-known/jwks.json` so your verifier can still validate late-arriving webhooks (for example, retried deliveries) that were signed with the previous key.
4. When the grace period expires, the previous key is removed from the JWKS endpoint.

**What your verifier needs to do:**

* Look up the public key by the `webhook-kid` header on each request; do not hardcode a single key.
* If you cache JWKS responses, invalidate on an unknown `webhook-kid` so a newly rotated key is picked up on demand.
* The signature verification steps above are unchanged across rotations; only the key lookup differs.

## After receiving a webhook

The example below uses `https://api.moveworks.ai`, which is only correct for orgs on the US Production data center. Replace it with the base URL for your data center from the [Base URLs table](/api-reference/overview#base-urls) (for example, `https://api.am-eu-central.moveworks.ai` for EU). Requests to the wrong host return 404 or `invalid_audience`.

Once a webhook is verified and acknowledged, retrieve the message content via the events endpoint:

```bash
curl "https://api.moveworks.ai/assistant/v1/conversations/-/events?starting_after=<YOUR_CHECKPOINT>&ending_at=<WEBHOOK_ID>" \
  -H "Authorization: Bearer <MW_ACCESS_TOKEN>" \
  -H "Assistant-Name: <YOUR_BOT_NAME>"
```

Substitute:

* `<WEBHOOK_ID>` — the `id` from the webhook envelope. Passed as `ending_at` (inclusive upper bound), bounding the query to events at or before this webhook.
* `<YOUR_CHECKPOINT>` — the `event_id` you last successfully processed for this user. Passed as `starting_after` (exclusive lower bound).

See [Receive via polling](/api-reference/conversations-api/proactive-notifications/polling) for the full response schema and checkpoint semantics.

## Delivery semantics

**Acknowledgment.** Your webhook endpoint must respond with a `2xx` status code within 15 seconds. No response or a non-2xx response is treated as a failed delivery.

**At-least-once delivery.** Moveworks delivers webhooks with at-least-once semantics: the same event may be delivered more than once across retries. Implement idempotency using `webhook-id`. On receipt, check whether you have already processed that ID and discard duplicates.

**Retry schedule.** Failed deliveries retry with pure exponential backoff, doubling the wait time between each attempt. The full schedule spans up to 11 retries capped at a 24-hour total window:

| Attempt          | Wait before retry          | Cumulative time elapsed     |
| ---------------- | -------------------------- | --------------------------- |
| Initial delivery | 0 minutes                  | 0 minutes                   |
| Retry 1          | 1 minute                   | 1 minute                    |
| Retry 2          | 2 minutes                  | 3 minutes                   |
| Retry 3          | 4 minutes                  | 7 minutes                   |
| Retry 4          | 8 minutes                  | 15 minutes                  |
| Retry 5          | 16 minutes                 | 31 minutes                  |
| Retry 6          | 32 minutes                 | 1 hour, 3 minutes (63m)     |
| Retry 7          | 1 hour, 4 minutes (64m)    | 2 hours, 7 minutes (127m)   |
| Retry 8          | 2 hours, 8 minutes (128m)  | 4 hours, 15 minutes (255m)  |
| Retry 9          | 4 hours, 16 minutes (256m) | 8 hours, 31 minutes (511m)  |
| Retry 10         | 8 hours, 32 minutes (512m) | 17 hours, 3 minutes (1023m) |
| Retry 11         | 6 hours, 57 minutes (417m) | 24 hours (1440m)            |

The final retry is truncated so total elapsed time lands exactly on the 24-hour mark rather than continuing the pure doubling pattern.

No further attempts are made after 24 hours. Use the events endpoint with your last stored checkpoint to recover any events that exhausted all retries.

**HTTP status handling.**

| Status            | Behavior                                                        |
| ----------------- | --------------------------------------------------------------- |
| `2xx`             | Success. Delivery acknowledged.                                 |
| Any other non-2xx | Treated as failure and subject to retry per the schedule above. |

**Scope.** Webhooks fire at the org level: a single registered endpoint receives events for all users. The events endpoint, in contrast, is scoped to a single user, consistent with all other Conversations API endpoints. Per-user recovery still uses each user's stored `last_event_id`.

## Recommended patterns

### Acknowledge first, process asynchronously

Return `200 OK` immediately after signature verification and idempotency checks. Perform all downstream work (fetching content, pushing to clients) asynchronously. The 15-second timeout applies to your response only.

### Idempotency

Persist processed `webhook-id` values and short-circuit duplicates on receipt. The `webhook-id` is stable across all retries of the same event.

### Per-user checkpoint

Maintain a `{user_id, last_event_id}` record per user, following the same rules described in [Per-user checkpoint](/api-reference/conversations-api/proactive-notifications/polling#per-user-checkpoint) on the polling page. The webhook envelope's `id` is the value you use as the upper bound (`ending_at=<id>`) when fetching content via the events endpoint after a webhook.

## Related

* [Proactive notifications overview](/api-reference/conversations-api/proactive-notifications/overview)
* [Receive via polling](/api-reference/conversations-api/proactive-notifications/polling)
* [Agent Studio Webhook Triggers Quickstart](https://docs.moveworks.com/agent-studio/quickstart-guide/webhook-triggers-quickstart-guide)