Skip to main content
GET
/
v2
/
topics
/
{topicId}
/
messages
Get topic messages
curl --request GET \
  --url https://api.zenzap.co/v2/topics/{topicId}/messages \
  --header 'Authorization: Bearer <token>' \
  --header 'X-Signature: <api-key>'
import requests

url = "https://api.zenzap.co/v2/topics/{topicId}/messages"

headers = {
    "Authorization": "Bearer <token>",
    "X-Signature": "<api-key>"
}

response = requests.get(url, headers=headers)

print(response.text)
const options = {
  method: 'GET',
  headers: {Authorization: 'Bearer <token>', 'X-Signature': '<api-key>'}
};

fetch('https://api.zenzap.co/v2/topics/{topicId}/messages', options)
  .then(res => res.json())
  .then(res => console.log(res))
  .catch(err => console.error(err));
<?php

$curl = curl_init();

curl_setopt_array($curl, [
  CURLOPT_URL => "https://api.zenzap.co/v2/topics/{topicId}/messages",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "GET",
  CURLOPT_HTTPHEADER => [
    "Authorization: Bearer <token>",
    "X-Signature: <api-key>"
  ],
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;
}
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://api.zenzap.co/v2/topics/{topicId}/messages"

	req, _ := http.NewRequest("GET", url, nil)

	req.Header.Add("Authorization", "Bearer <token>")
	req.Header.Add("X-Signature", "<api-key>")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(string(body))

}
HttpResponse<String> response = Unirest.get("https://api.zenzap.co/v2/topics/{topicId}/messages")
  .header("Authorization", "Bearer <token>")
  .header("X-Signature", "<api-key>")
  .asString();
require 'uri'
require 'net/http'

url = URI("https://api.zenzap.co/v2/topics/{topicId}/messages")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
request["X-Signature"] = '<api-key>'

response = http.request(request)
puts response.read_body
{
  "messages": [
    {
      "id": "660e8400-e29b-41d4-a716-446655440001",
      "topicId": "550e8400-e29b-41d4-a716-446655440000",
      "senderId": "550e8400-e29b-41d4-a716-446655440001",
      "senderName": "Alice Johnson",
      "senderType": "user",
      "text": "Hello team!",
      "createdAt": 1699564800000,
      "updatedAt": 1699564800000,
      "isEdited": false,
      "isSystem": false,
      "attachments": [],
      "mentions": [],
      "reactions": []
    },
    {
      "id": "660e8400-e29b-41d4-a716-446655440002",
      "topicId": "550e8400-e29b-41d4-a716-446655440000",
      "senderId": "b@660e8400-e29b-41d4-a716-446655440003",
      "senderName": "API Bot",
      "senderType": "bot",
      "text": "Task created successfully",
      "createdAt": 1699564700000,
      "updatedAt": 1699564700000,
      "isEdited": false,
      "isSystem": false,
      "attachments": [],
      "mentions": [],
      "reactions": []
    }
  ],
  "nextCursor": "eyJ0IjoxNjk5NTY0NzAwMDAwLCJpZCI6IjY2MGU4NDAwLWUyOWItNDFkNC1hNzE2LTQ0NjY1NTQ0MDAwMiIsIm8iOiJkZXNjIn0=",
  "hasMore": true
}
"text is required"
"unauthorized"
"Topic not found"
"internal server error"

Authorizations

Authorization
string
header
required

Bearer token for the request. Two flavors:

  • Static API key — pass your API key (the value returned as apiKey when the bot was created). Must be paired with X-Signature + X-Timestamp (the hmacSignature scheme).
  • OAuth access token — pass the JWT returned by POST /oauth/token. No signature headers are required.
X-Signature
string
header
required

HMAC-SHA256 signature for request verification. Required only when authenticating with a static API key. Omit when using an OAuth access token.

Headers

X-Signature
string

HMAC signature of the request for authentication and replay protection.

Required only when authenticating with a static API key. If you are using an OAuth access token (issued by POST /oauth/token), omit this header — the JWT carries all the authentication and integrity guarantees.

Replay Protection: The signature includes a timestamp to prevent replay attacks. Requests with timestamps older than 5 minutes are rejected.

The signature payload differs by HTTP method:

  • POST/PUT/PATCH/DELETE: HMAC-SHA256 of {timestamp}.{body}
  • GET: HMAC-SHA256 of {timestamp}.{uri}

The signature is calculated as:

  1. Get the current Unix timestamp in milliseconds
  2. Determine the payload:
    • For POST/PUT/PATCH/DELETE: Use {timestamp}.{body} where body is the request body
    • For GET: Use {timestamp}.{uri} where uri is the full request URI (e.g., /v2/members?limit=10)
  3. Calculate HMAC-SHA256 of the combined payload using your API secret
  4. Hex-encode the output
  5. Include the timestamp in the X-Timestamp header

Example for GET request to /v2/members?limit=10:

timestamp = 1699564800000
payload = "1699564800000./v2/members?limit=10"
signature = HMAC-SHA256(secret, payload)
X-Signature: hex(signature)
X-Timestamp: 1699564800000

Example for POST request with body {"topicId":"123","text":"Hello"}:

timestamp = 1699564800000
payload = '1699564800000.{"topicId":"123","text":"Hello"}'
signature = HMAC-SHA256(secret, payload)
X-Signature: hex(signature)
X-Timestamp: 1699564800000

For multipart/form-data requests, sign the exact raw request body bytes (including boundaries and file bytes) as transmitted.

Pattern: ^[a-f0-9]{64}$
Example:

"a3d5f8e7c2b1d4f6a8e9c7b5d3f1a2e4b6c8d0f2e4a6b8c0d2e4f6a8b0c2d4e6"

X-Timestamp
integer<int64>

Unix timestamp in milliseconds when the request was created. Used for replay protection — requests older than 5 minutes are rejected.

Required only when authenticating with a static API key. Omit when using an OAuth access token.

Example:

1699564800000

Path Parameters

topicId
string<uuid>
required

The ID of the topic to get messages from

Query Parameters

limit
integer
default:50

Maximum number of messages to return (default: 50, max: 100)

Required range: 1 <= x <= 100
cursor
string

Opaque cursor for pagination. Use the value from nextCursor in the response. Cursors are cryptographically signed and validated server-side to prevent tampering.

before
integer<int64>

Return messages created before this timestamp (Unix milliseconds). Mutually exclusive with after.

after
integer<int64>

Return messages created after this timestamp (Unix milliseconds). Mutually exclusive with before.

senderId
string

Filter messages by sender ID

order
enum<string>
default:desc

Sort order for messages (default: desc - newest first)

Available options:
asc,
desc
includeSystem
boolean
default:true

Include system messages (default: true)

threadId
string<uuid>

Filter to messages within a specific thread (replies to a parent message)

includeReactionUsers
boolean
default:false

Include user IDs in reaction data (default: false, heavier payload)

Response

Messages retrieved successfully

messages
object[]

Array of message objects

nextCursor
string

Cursor for the next page. Pass this value to the cursor parameter to get the next page.

Example:

"eyJ0IjoxNjk5NTY0NzAwMDAwLCJpZCI6IjY2MGU4NDAwLWUyOWItNDFkNC1hNzE2LTQ0NjY1NTQ0MDAwMiIsIm8iOiJkZXNjIn0="

hasMore
boolean

Whether there are more messages to fetch

Example:

true