Chats and history

List chats, read a conversation page by page, and ask the phone for older history.

  • GET/v1/sessions/<CHANNEL_ID>/chats

    List chats, up to 100.

  • GET/v1/sessions/<CHANNEL_ID>/chats/<CHAT_JID>/messages

    Read one conversation, newest first.

  • GET/v1/sessions/<CHANNEL_ID>/history-sync

    See whether synced history is stored.

  • PUT/v1/sessions/<CHANNEL_ID>/history-sync

    Turn storing synced history on or off.

  • POST/v1/sessions/<CHANNEL_ID>/chats/<CHAT_JID>/history-requests

    Ask the phone for older messages of one chat.

Chats and messages come from WAgate's own log of this channel. The log starts when the number was linked, unless synced history is on.

List chats

bash
curl "https://api-wagate.socialground.co/v1/sessions/<CHANNEL_ID>/chats" -H "Authorization: Bearer <CHANNEL_TOKEN>"
json
{
  "chats": [
    { "chat_jid": "[email protected]", "last_at": "2026-09-17T03:00:00Z", "message_count": 42, "unread": 2 }
  ]
}

Up to 100 chats. unread can be null.

Read a conversation

bash
curl "https://api-wagate.socialground.co/v1/sessions/<CHANNEL_ID>/chats/[email protected]/messages?limit=50" \
  -H "Authorization: Bearer <CHANNEL_TOKEN>"
json
{
  "messages": [
    {
      "id": "3EB0C767D26A1D8E2A41",
      "chat_jid": "[email protected]",
      "sender_jid": "[email protected]",
      "direction": "in",
      "type": "text",
      "text": "hello",
      "from_me": false,
      "timestamp": "2026-09-17T03:00:00Z",
      "source": "live",
      "source_chat": "[email protected]"
    }
  ],
  "chats": ["[email protected]"],
  "merge": "none_known",
  "next_before": "AbC123"
}
  • Messages come newest first, 50 to a page by default. limit is 1 to 500.
  • When older messages exist, the response has next_before. Pass it back as before=<value>, exactly as returned. Stop when it is absent. Any other before value is a 400.
  • limit and before are the only query parameters this route reads.
  • direction is in or out. from_me is the more reliable field: a message sent from the paired phone is stored as in with from_me: true.
  • source is live for a message captured while the channel was connected, or history_sync for one the phone sent as history.
  • type includes text, image, video, document, audio, sticker, contact, contact_list, location, live_location, poll, poll_vote, reaction, protocol, short, group_invite and unknown. New values can be added, so treat one you do not recognize as unknown.
  • Other fields on a message when they apply: media_id, push_name, mentions, quoted_id, quoted_author, poll, reaction, revoke, revoked_at, ambiguous, chat_lid, sender_lid, sender_pn_resolved, quoted_author_lid.
  • ambiguous: true marks a send that answered 504 send_unconfirmed. Its id starts with ambiguous: and is not a WhatsApp message ID. The message may or may not have been delivered. The row records the attempt, not the outcome.
  • revoked_at is set when a message was deleted for everyone. The log keeps the message's content.

A paging loop:

js
let before;
do {
  const url = new URL(`${API}/sessions/<CHANNEL_ID>/chats/${chat}/messages`);
  url.searchParams.set('limit', '200');
  if (before) url.searchParams.set('before', before);
  const res = await fetch(url, { headers: { Authorization: `Bearer ${TOKEN}` } });
  const page = await res.json();
  handle(page.messages);
  before = page.next_before;
} while (before);

One person, two addresses

WhatsApp may file a person's messages under their phone number and under a LID. On a channel that holds the lid_lookup grant, both are read and merged into one timeline. merge says what happened:

  • merged: both addresses were read.
  • none_known: no other address is known for this person.
  • not_granted: your channel does not hold the grant, so only the address you named was read.
  • not_a_person: the chat is a group.

Each message's source_chat says which address it is filed under. In a group, each sender is shown once. sender_jid is their phone number where known, and sender_lid is the LID they were stored under.

Reads are recorded

Each page you read is recorded in your API access records: which token read it, which chats, and how many messages. The content is never recorded there.

History from the phone

Storing history the phone sends is off by default.

bash
curl -X PUT "https://api-wagate.socialground.co/v1/sessions/<CHANNEL_ID>/history-sync" \
  -H "Authorization: Bearer <CHANNEL_TOKEN>" -H "Content-Type: application/json" \
  -d '{"store_history_sync": true}'
# {"store_history_sync":true}

curl "https://api-wagate.socialground.co/v1/sessions/<CHANNEL_ID>/history-sync" -H "Authorization: Bearer <CHANNEL_TOKEN>"
# {"store_history_sync":true}
  • The body must be exactly that one field, true or false. Any other body is refused with 400 field_not_allowed.
  • With it on, history your phone sends is kept with this channel's messages, under the same retention, and returned by the conversation read with source: history_sync. Turning it off stops storing. What was stored stays until retention removes it. The gateway does not read or use this content. It only stores it for your channel.
  • A phone sends history when you link it and when you ask. Turn the setting on before linking to receive up to 200 recent messages per chat, from within the message retention period.

Ask for older messages of one chat:

bash
curl -X POST "https://api-wagate.socialground.co/v1/sessions/<CHANNEL_ID>/chats/[email protected]/history-requests" \
  -H "Authorization: Bearer <CHANNEL_TOKEN>" -H "Content-Type: application/json" \
  -d '{"count": 50}'
json
{
  "status": "requested",
  "chat_jid": "[email protected]",
  "before_message_id": "3EB0C767D26A1D8E2A41",
  "before": "2026-09-01T03:00:00Z",
  "count": 50
}
  • The body is optional. count is 1 to 50 and defaults to 50. The request asks for messages older than the oldest one this channel holds for the chat.
  • The answer is 202. The phone replies later, in its own time, and the messages then appear in the conversation read with source: history_sync. Whether the phone answers, and how much, is up to the phone.
  • If store_history_sync is off, it answers 409 history_sync_off. If the channel holds no message of that chat, it answers 404 no_history_anchor.
  • Limits: one request per channel every 30 seconds, and one per chat every 5 minutes. Over that, 429 history_request_too_soon with a Retry-After header and the same number as retry_after in the body. Wait that long.
  • Each request is recorded in your API access records.

Errors on these routes

403 blocked, 404 not_found, 400 invalid_request or field_not_allowed, 429 history_request_too_soon. See Errors.

Chats and history · WAgate docs