Monitor And Respond To Community Messages#
Use this journey when a server-side agent, Codex task, or Claude Code task needs to monitor normal channel messages and then reply or react through the Returning.AI public API.Keep the Community API key server-side. A browser login token is not a Community API key. Start agents in read-only monitoring mode, and enable replies or reactions only under an explicit automation policy.
Channel support#
| Channel surface | Read path | Write path | Agent status |
|---|
| Normal/text | GET /v1/messages | /v1/messages/send, /reply, and /react | Ready. This guide applies to normal/text channel agents. |
| Direct message | No public DM history/list endpoint | DM-aware source branches exist, but authentication and participant requirements differ | Not ready for autonomous monitoring. Do not use this polling loop for DMs. |
| Forum | Current list projection does not preserve enough forum-topic context | Same send route with forumTopicId | Not agent-ready. Use only after a separate forum contract is proven. |
| Iframe | No separate public API contract | No separately verified contract | Not agent-ready. The old channel-labelled copies are internal-only. |
The old Apidog Message Actions Text, Direct, Forum, and Iframe pages were duplicate records for the same operations and used stale /apis/v1/messages public paths. They are intentionally hidden; use only the canonical endpoint links at the end of this guide.Target and actor are different#
The incoming message ID selects what receives the reply or reaction. The configured agent email or exact username selects who authors that action. Never substitute the incoming author's numeric user_id for either role.Identifier map#
A successful GET /v1/messages item looks like this:{
"id": "<messageId>",
"message": "Can someone help with my account?",
"user": {
"user_id": "<numericPlatformUserId>",
"email": "member@example.com"
},
"channel": {
"channel_id": "<channelObjectId>",
"name": "support"
},
"timestamp": "2026-08-15T04:00:00.000Z"
}
| Returned field | What it means | Use it for |
|---|
id | Message ID | messageId in reply and react requests. This chooses the message being acted on. |
user.user_id | Numeric Returning.AI ID of the message author | Filtering later GET requests or looking up that user. Do not put it in mutation sender. |
user.email | Email of the message author | Identifying or filtering the incoming author. For a normal reply, it is not the recipient field. |
channel.channel_id | Channel ID | Scoping the next message poll. |
timestamp | Message timestamp | Logging and observability. Use message IDs, not timestamps alone, for deduplication. |
The important distinction is target versus actor:messageId identifies the incoming message to reply to or react to.
sender identifies the existing community user whose identity the agent is acting as.
Configure a dedicated agent user's exact email or platform username as sender. Do not copy the incoming author's user_id into sender, and do not use a display name.
GET messages returns the author's email and numeric ID, but not their username. If a workflow genuinely needs the username, call POST /v1/users/info with the email or numeric ID and read data.username. Username lookup is not required merely to reply.
Executable sequence#
1
Resolve a channel
Call GET /v1/channels and save the target channel's _id as channel_id.
2
Poll newest normal messages
The response is newest-first. Process each page oldest-to-newest so replies remain conversational, and checkpoint every processed message id. There is no cursor API, so overlap polls and deduplicate by ID. 3
Reply as the configured agent user
{
"messageId": "<incomingMessage.id>",
"message": "I can help with that. What account detail should I check?",
"sender": "community-agent@example.com"
}
sender is the agent's community user, not the incoming author. Email is the recommended stable value; the exact platform username also works.4
React when a lightweight acknowledgement is enough
{
"messageId": "<incomingMessage.id>",
"emoji": ":eyes:",
"sender": "community-agent@example.com"
}
Use a registered shortname such as :eyes: or :smile:. Raw Unicode such as 👀 is rejected.5
Verify and checkpoint
For normal channel sends and replies, poll GET /v1/messages again and match a unique correlation marker in your text. GET messages does not expose reaction state; verify reactions in the UI or another reaction-capable surface. Save the incoming message ID only after the configured action reaches its chosen verification boundary.
Node.js monitoring example#
alreadyProcessed and markProcessed are application-owned durable checkpoint functions. Do not use an in-memory set for a production worker that may restart.Optional user lookup#
Use lookup only when the workflow needs more author context or an exact username:{
"idOrEmail": "<incoming.user.user_id>"
}
Read data.username from the response. The lookup needs getUserData; it does not replace messageId when replying or reacting.Polling, retries, and failure recovery#
| Situation | Action |
|---|
Empty messages array | Normal result. Wait for the next bounded poll. |
| Poll transport error or 5xx | GET is read-only; retry with bounded exponential backoff. |
| Reply times out after submission | Do not immediately retry. A duplicate reply can be created. Read recent normal messages for your unique correlation marker first. |
400 Sender field is required | Set sender to the configured agent user's email or exact username. |
404 User not found | The sender does not resolve in this community. Do not substitute display name, numeric user ID, or MongoDB _id. |
400 Emoji not found | Send a registered emoji shortname, not raw Unicode. |
401 | Confirm this is a Community API key, not a browser session token, and that the Authorization header is correct. |
403 | Check the relevant message permission and channel/user access. |
Send and reply have no documented idempotency key. Exact retries can create duplicates. Include a non-secret correlation marker in agent-authored text when deterministic readback matters.
Boundaries#
GET /v1/messages returns normal messages, not direct-message history.
Omitting channel_id returns a community-wide newest-first normal-message feed.
Direct-message replies have additional participant and recipient rules and need a separate tested flow.
data.total is the number returned in this response, not the total stored history.
Use snake_case query parameters: channel_id and user_id.
Endpoint reference#