Receive proactive notifications via outbound webhook

Receive push notifications from Moveworks when a new system-initiated message is available, and reconcile content via the events endpoint.

View as Markdown

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. On each notification, retrieve the full content via the events endpoint.

Before reading this page, review 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:

FieldRequiredDescription
Webhook URLYesYour HTTPS endpoint. Must be publicly accessible and return a 2xx response within 15 seconds.
Event TypeYesThe event(s) to subscribe to. Currently only CONVERSATION_RESPONSE_RECEIVED is supported.
Rotation IntervalNoHow often Moveworks rotates the signing key, in days. Omit to disable rotation.
Rotation Grace PeriodNoOverlap 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

EventDescription
CONVERSATION_RESPONSE_RECEIVEDA 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

1{
2 "id": "evt_32bt9WkPqRnEtYsLmXc4v",
3 "type": "CONVERSATION_RESPONSE_RECEIVED",
4 "timestamp": "2026-03-27T12:34:56Z",
5 "data": {
6 "user_id": "user@yourcompany.com"
7 }
8}
FieldTypeDescription
idstringUnique 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.
typestringThe event type (for example, CONVERSATION_RESPONSE_RECEIVED).
timestampstringISO 8601 timestamp of when the event occurred.
data.user_idstringThe 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:

HeaderDescription
webhook-idThe unique event identifier (matches id in the payload, prefixed with evt_). Constant across retries.
webhook-timestampISO 8601 timestamp of when the delivery was attempted (for example, 2026-08-03T23:23:57.846096+00:00).
webhook-signatureBase64-encoded RSA-SHA256 signature. Space-delimited when multiple signatures are present (during a key-rotation grace period).
webhook-kidKey ID of the signing key used. Use this to look up the corresponding public key from the Moveworks JWKS endpoint for signature verification.
user-agentmw/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.

1

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.

2

Construct the signing message

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

3

Verify the signature

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

4

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 (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:

$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 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:

AttemptWait before retryCumulative time elapsed
Initial delivery0 minutes0 minutes
Retry 11 minute1 minute
Retry 22 minutes3 minutes
Retry 34 minutes7 minutes
Retry 48 minutes15 minutes
Retry 516 minutes31 minutes
Retry 632 minutes1 hour, 3 minutes (63m)
Retry 71 hour, 4 minutes (64m)2 hours, 7 minutes (127m)
Retry 82 hours, 8 minutes (128m)4 hours, 15 minutes (255m)
Retry 94 hours, 16 minutes (256m)8 hours, 31 minutes (511m)
Retry 108 hours, 32 minutes (512m)17 hours, 3 minutes (1023m)
Retry 116 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.

StatusBehavior
2xxSuccess. Delivery acknowledged.
Any other non-2xxTreated 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.

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 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.