Receive proactive notifications via polling

Retrieve system-initiated messages by polling the Conversations API events endpoint with a per-user checkpoint.

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.

Poll the events endpoint on your own schedule to retrieve system-initiated messages that have been routed to CAPI users. Polling is the recommended path when an inbound HTTPS receiver isn’t viable on your backend, and the source of truth for reconciling events after a webhook outage. If you can run an inbound receiver, prefer outbound webhook push for lower latency to your users and lower load on Moveworks.

Before reading this page, review Proactive notifications overview for architecture, routing behavior, and prerequisites.

The examples below use 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.

The events endpoint

Retrieve system-initiated messages for a user:

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

Substitute:

  • <YOUR_CHECKPOINT> — the event_id you last successfully processed for this user. Passed as starting_after (exclusive lower bound). Omit on the first-ever call for a user.
  • <MW_ACCESS_TOKEN> — a valid Moveworks bearer token for the recipient user. See the auth guides for token issuance.
  • <YOUR_BOT_NAME> — the Bot Name provided by your account team.

The - in the URL is a wildcard that returns events across all of the user’s conversations, including new conversations Moveworks created for proactive messages with no prior user input.

Query parameters

ParameterTypeRequiredDescription
starting_afterstringNoEvent ID (base-62, evt_ prefix). Exclusive lower bound. Pass your stored last_event_id to retrieve events after your last known checkpoint.
ending_atstringNoEvent ID (base-62, evt_ prefix). Inclusive upper bound. Used to bound recovery queries.
cursorstringNoPagination cursor from a prior response.
limitintegerNoResults per page. Range: 1-100. Default: 20.

When both starting_after and ending_at are supplied, ending_at must refer to an event created after the one identified by starting_after.

Response

1{
2 "events": [
3 {
4 "event_id": "evt_340xdCuTfBHbSriGYQz6J",
5 "conversation_id": "conv_340xdCMeTfceLCg8Gt6LP",
6 "created_at": "2026-08-03T22:03:22.965068Z",
7 "event_payload": {
8 "type": "MESSAGE_EVENT",
9 "message": {
10 "message_id": "msg_340xdCrqXA6LvcoIgVbfs",
11 "conversation_id": "conv_340xdCMeTfceLCg8Gt6LP",
12 "response_id": "resp_340xdCMeTfceLCg0s1LRL",
13 "actor": "ASSISTANT",
14 "content": {
15 "type": "MARKDOWN_TEXT",
16 "markdown_text": { "text": "Reminder: Your password expires in 3 days." }
17 },
18 "created_at": "2026-08-03T22:03:22.965068Z"
19 }
20 }
21 }
22 ],
23 "metadata": {}
24}

Response fields

FieldTypeDescription
events[].event_idstringUnique event identifier. Base-62 identifier prefixed with evt_ (for example, evt_32bt9WkPqRnEtYsLmXc4v), consistent with other Conversations API resource IDs.
events[].conversation_idstringThe conversation this event belongs to.
events[].created_atstring (ISO 8601)When the event was created.
events[].event_payload.typestringEvent payload type discriminator. Currently MESSAGE_EVENT.
events[].event_payload.message.message_idstringMessage identifier.
events[].event_payload.message.conversation_idstringThe conversation this message belongs to (matches the outer conversation_id).
events[].event_payload.message.response_idstringThe response this message belongs to.
events[].event_payload.message.actorstring"ASSISTANT" for system-generated messages.
events[].event_payload.message.content.typestringContent type. Common values: MARKDOWN_TEXT, COMMONMARK_TEXT, PLAIN_TEXT.
events[].event_payload.message.content.markdown_text.textstringMessage content (for MARKDOWN_TEXT). Field name matches the content type.
events[].event_payload.message.created_atstring (ISO 8601)When the message was created.
metadata.next_cursorstring / nullCursor for the next page when the result set exceeds limit; null if no more results.

The events endpoint returns only final messages; intermediate reasoning or in-progress states are not included.

Scope. The events endpoint is scoped to a single user, consistent with all other Conversations API endpoints. Polling and recovery must be performed per user using each user’s stored last_event_id.

Response headers. Every response includes an x-trace-id header identifying the request. Capture it if you need to correlate to Moveworks-side logs when opening a support case. The legacy x-moveworks-root-uuid header is also returned for backward compatibility but is deprecated; prefer x-trace-id.

Advancing through the event stream requires tracking your position per user. Maintain a {user_id, last_event_id} record per user in your backend and pass last_event_id as starting_after on each poll.

RuleDetail
One checkpoint per userThe events endpoint is user-scoped; polling and recovery are per user.
Advance after processingOnly update last_event_id after successfully delivering the content to your client.
Never regressOnly advance the checkpoint forward.
Initialize on first eventIf no checkpoint exists, the first event_id returned by the events endpoint becomes the initial value.

Event IDs are base-62 identifiers with an evt_ prefix, consistent with other Conversations API resource IDs. Use each processed event’s ID as your next cursor value for exact iteration through the stream.

The same checkpoint pattern underlies webhook-based delivery — see Per-user checkpoint on the outbound webhook page.

Recovery after an outage

If your backend was unavailable when new events were created, call the events endpoint per affected user using that user’s stored checkpoint:

$curl "https://api.moveworks.ai/assistant/v1/conversations/-/events?starting_after=<USER_CHECKPOINT>" \
> -H "Authorization: Bearer <MW_ACCESS_TOKEN>" \
> -H "Assistant-Name: <YOUR_BOT_NAME>"

Omit ending_at to fetch all events since the checkpoint up to the present. Paginate using next_cursor if the result set exceeds one page.

Testing end-to-end

Because of the routing behavior, testing requires establishing the CAPI integration as the recipient’s most-recent-interaction channel before triggering the source-system webhook.

1

Send a user message through your CAPI integration

Create a conversation, send at least one user message, and poll the response until it completes so the recipient’s most-recent-interaction channel is CAPI. Creating a conversation alone does not count as an interaction with the bot — the bot must actually process a message and generate a response.

$# 1. Create a conversation
$curl -X POST "https://api.moveworks.ai/assistant/v1/conversations" \
> -H "Authorization: Bearer <MW_ACCESS_TOKEN>" \
> -H "Assistant-Name: <YOUR_BOT_NAME>" \
> -H "Content-Type: application/json" \
> -d '{"title":"pre-interaction for proactive test"}'
$
$# 2. Send a user message. This returns 202 with status IN_PROGRESS.
$curl -X POST "https://api.moveworks.ai/assistant/v1/conversations/<CONV_ID>/responses" \
> -H "Authorization: Bearer <MW_ACCESS_TOKEN>" \
> -H "Assistant-Name: <YOUR_BOT_NAME>" \
> -H "Content-Type: application/json" \
> -d '{"input":{"text":"hello"}}'
$
$# 3. Poll the response until status becomes COMPLETED before proceeding.
$curl "https://api.moveworks.ai/assistant/v1/conversations/<CONV_ID>/responses/<RESP_ID>" \
> -H "Authorization: Bearer <MW_ACCESS_TOKEN>" \
> -H "Assistant-Name: <YOUR_BOT_NAME>"
2

Fire the source-system trigger

POST your test payload to the Agent Studio Listener URL configured for your trigger. See the Agent Studio Webhook Triggers Quickstart if you need to set one up.

$curl -i -X POST "<YOUR_LISTENER_URL>" \
> -H "Content-Type: application/json" \
> -d '<YOUR_TEST_PAYLOAD>'

The response body is {"message":"Event received successfully","status":"RECEIVED"} on HTTP 200. Capture the x-moveworks-root-uuid response header for tracing.

3

Poll the events endpoint

Within a few seconds of the trigger, the proactive message should appear:

$curl "https://api.moveworks.ai/assistant/v1/conversations/-/events" \
> -H "Authorization: Bearer <MW_ACCESS_TOKEN>" \
> -H "Assistant-Name: <YOUR_BOT_NAME>"

Expect one event with a new conversation_id (title auto-populated as “System Initiated Conversation”) and an actor: ASSISTANT message.

4

Store the checkpoint

Persist the returned event_id as your last_event_id checkpoint for that user. Subsequent runs should use starting_after=<checkpoint> to receive only new events.

Common test failures and diagnostic paths

SymptomLikely causeHow to confirm
Listener returns 200 RECEIVED but the events endpoint stays emptyRecipient’s most-recent-interaction channel is not CAPI (message went to Slack/Teams instead)Check the user’s Slack DMs — if the message is there, that’s the routing behavior. Follow the pre-interaction pattern above.
Listener returns 200 RECEIVED but no message appears on any channelThe plugin in Agent Studio is in a draft state, or its compound action has a schema mismatchCheck the listener log for skipped: [...]. Publish the plugin and compound action, verify Event Filter is blank (not TRUE).
Listener returns 429Unsecured listener rate limit (1 request per 10 seconds per org)Wait for the interval to clear, or configure signature/credential verification on the listener to raise the limit.
Listener returns 200 RECEIVED but a downstream service errorsCompound action input type mismatch, or a step references an undefined variableInspect the trace via the x-moveworks-root-uuid response header; downstream failures produce error spans your Moveworks account team can look up.