---
title: "Get updates (long polling)"
method: GET
path: "/v2/updates"
tags: ["Long Polling"]
---

# Get updates (long polling)

`GET /v2/updates`

## Request

- Base URL: `https://api.zenzap.co`
- URL: `https://api.zenzap.co/v2/updates`
- Auth: one of:
  - HTTP bearer and API key in header `X-Signature`
  - OAuth 2
    - Client credentials
    - Token URL: `https://api.zenzap.co/oauth/token` scopes: `updates:read`
- TypeScript: <https://skmtc.dev/zenzap/apis/zenzap-external-integration-api/docs/v2/updates/get.ts> (a function with typed inputs; its response is checked against the schema)

Retrieve outbound events for bots configured with **polling** delivery mode.

Long polling behavior:
- If updates are available after `offset`, they are returned immediately
- If none are available and `timeout > 0`, the request waits up to `timeout` seconds
- If still no updates, returns an empty list with `nextOffset`
- Returns `409` if delivery mode is not polling, or if `offset` is no longer available

Message attachment payloads are included inline in polling events, the same as webhooks:
`data.message.attachments[]` with signed download URLs (including audio transcription metadata when available).

Use `nextOffset` from each response as `offset` in the next request.

## Query parameters

- `offset` string
- `limit` integer
- `timeout` integer

## Headers

- `X-Signature` string
- `X-Timestamp` integer

## Response `200`

Updates retrieved successfully

- UpdatesResponse
  - `updates` PollingUpdate[], required: Ordered list of updates after the provided offset
    - `updateId` string, required: Opaque update identifier. Use as offset in subsequent polling calls.
    - `eventType` 'message.created' | 'message.updated' | 'message.deleted' | 'reaction.added' | 'reaction.removed' | 'member.added' | 'member.removed' | 'topic.updated', required: The update event type
    - `createdAt` integer, required: Unix timestamp in milliseconds when the update was created
    - `data` union, required: Event payload. Structure matches the webhook `data` payload for the same event type.
      - WebhookMessageCreatedData
        - `message` WebhookMessage, required: Message object as delivered in webhook events. Includes additional fields not present in the REST API response, such as message type-specific data.
          - `id` string, uuid, required: The message ID
          - `topicId` string, uuid, required: The topic ID where the message was sent
          - `senderId` string, required: The sender's member ID
          - `senderName` string, required: The sender's display name
          - `senderType` 'user' | 'bot' | 'system', required: The type of sender
          - `type` 'text' | 'image' | 'file' | 'video' | 'audio' | 'location' | 'task' | 'contact' | 'poll' | 'call' | 'admin', required: The message type: - `text`: Regular text message - `image`, `file`, `video`, `audio`: Message with attachment - `location`: Shared location - `task`: Task snapshot (created, updated, completed, etc.) - `contact`: Shared contact - `poll`: Poll - `call`: Audio/video call snapshot - `admin`: System/administrative message
          - `text` string: The message text content (may be empty for non-text types). Mention tokens are preserved as `<@profileId>`.
          - `previousText` string: Previous message text (present on `message.updated` when text changed)
          - `createdAt` integer, required: Unix timestamp in milliseconds when the message was created
          - `updatedAt` integer: Unix timestamp in milliseconds when the message was last updated
          - `parentId` string, uuid: ID of the message being replied to (for threaded replies)
          - `mentionedProfiles` string[]: IDs of mentioned users in the message
          - `attachments` WebhookAttachment[]: Array of file attachments
            - `id` string: The attachment ID
            - `type` 'image' | 'file' | 'video' | 'audio': The attachment type
            - `name` string: The original file name
            - `mimeType` string: Stored MIME type when known. Giphy GIFs are looping MP4s and use `video/mp4` with `type: video`.
            - `searchQuery` string: Giphy picker search text or GIF title, when the client stored one. Omitted when empty. Present on GIF attachments so consumers can treat it as the sender's intended reaction even if the clip is hard to interpret.
            - `url` string, uri: Signed URL to download the attachment. **Important:** URLs expire after 60 minutes.
            - `transcription` WebhookTranscription: Transcription data for audio attachments (voice messages). **Transcription flow:** 1. `message.created` arrives with `transcription.status: "Pending"` 2. `message.updated` arrives when transcription completes with `status: "Done"` and `text` populated
              - …
          - `mentions` MessageMention[]: Array of user mentions in the message
            - `id` string: The mentioned profile ID
            - `name` string: Display name used for the mention text
          - `location` WebhookLocation: Shared location data
            - `latitude` string: Latitude coordinate
            - `longitude` string: Longitude coordinate
            - `name` string: Location name (optional)
            - `address` string: Street address (optional)
          - `task` WebhookTask: Task snapshot sent when a task is created, updated, or its status changes. The `action` field indicates what triggered this snapshot.
            - `id` string: The task ID
            - `action` 'Added' | 'Updated' | 'Deleted' | 'MarkedAsDone' | 'MarkedAsOpened' | 'Replied': What triggered this task snapshot: - `Added`: Task was created - `Updated`: Task details were changed - `Deleted`: Task was removed - `MarkedAsDone`: Task was completed - `MarkedAsOpened`: Task was reopened - `Replied`: Comment was added to task
            - `title` string: Task title
            - `text` string: Task description
            - `status` 'Open' | 'Done': Current task status
            - `assignee` string: User ID of the assignee
            - `dueDate` integer: Due date as Unix timestamp in milliseconds
            - `isDueDateTimeSelected` boolean: Whether a specific time was selected for the due date
            - `parentId` string: Parent task ID (for subtasks)
            - `subItemsCount` integer: Number of subtasks
          - `contact` WebhookContact: Shared contact information
            - `name` string: Contact name
            - `phoneNumbers` string[]: Phone numbers
            - `emails` string[]: Email addresses
            - `role` string: Job title or role
            - `location` string: Location or office
            - `linkedIn` string: LinkedIn profile URL
            - `profileId` string: Zenzap profile ID if the contact is a Zenzap user
          - `poll` WebhookPoll: Poll data
            - `id` string: The poll (attachment) ID
            - `title` string: Poll question
            - `subtitle` string: Optional secondary line
            - `selectionType` 'single' | 'multiple': Whether voters may pick one option or several
            - `anonymous` boolean: Whether votes are anonymous
            - `status` 'active' | 'closed': Current poll status
            - `expiresAt` integer: Expiry as Unix timestamp in milliseconds (optional)
            - `options` object[]: The poll options
              - …
          - `call` WebhookCall: Audio/video call snapshot. The inline fields (`id`, `initiatorId`, `callType`) come from the message; live-state fields (`status`, `startedAt`, `endedAt`, `endReason`, `duration`, `participantCount`) are present only when the call record resolves.
            - `id` string: The call ID
            - `initiatorId` string: Profile ID of whoever started the call
            - `callType` 'audio' | 'video': Whether the call is audio- or video-first
            - `status` 'initiating' | 'calling' | 'active' | 'ended' | 'cancelled': Current call status
            - `startedAt` integer: When the call started, as Unix timestamp in milliseconds
            - `endedAt` integer: When the call ended, as Unix timestamp in milliseconds
            - `endReason` 'caller_not_joined' | 'caller_cancelled' | 'no_one_answered' | 'finished' | 'max_duration' | 'last_participant': Why the call ended
            - `duration` integer: Call duration in milliseconds (`endedAt - startedAt`), when both are known
            - `participantCount` integer: Number of profiles that actually joined the call
        - `truncated` boolean: Whether the payload content was truncated
      - WebhookMessageUpdatedData
        - `message` WebhookMessage, required: Message object as delivered in webhook events. Includes additional fields not present in the REST API response, such as message type-specific data.
          - `id` string, uuid, required: The message ID
          - `topicId` string, uuid, required: The topic ID where the message was sent
          - `senderId` string, required: The sender's member ID
          - `senderName` string, required: The sender's display name
          - `senderType` 'user' | 'bot' | 'system', required: The type of sender
          - `type` 'text' | 'image' | 'file' | 'video' | 'audio' | 'location' | 'task' | 'contact' | 'poll' | 'call' | 'admin', required: The message type: - `text`: Regular text message - `image`, `file`, `video`, `audio`: Message with attachment - `location`: Shared location - `task`: Task snapshot (created, updated, completed, etc.) - `contact`: Shared contact - `poll`: Poll - `call`: Audio/video call snapshot - `admin`: System/administrative message
          - `text` string: The message text content (may be empty for non-text types). Mention tokens are preserved as `<@profileId>`.
          - `previousText` string: Previous message text (present on `message.updated` when text changed)
          - `createdAt` integer, required: Unix timestamp in milliseconds when the message was created
          - `updatedAt` integer: Unix timestamp in milliseconds when the message was last updated
          - `parentId` string, uuid: ID of the message being replied to (for threaded replies)
          - `mentionedProfiles` string[]: IDs of mentioned users in the message
          - `attachments` WebhookAttachment[]: Array of file attachments
            - `id` string: The attachment ID
            - `type` 'image' | 'file' | 'video' | 'audio': The attachment type
            - `name` string: The original file name
            - `mimeType` string: Stored MIME type when known. Giphy GIFs are looping MP4s and use `video/mp4` with `type: video`.
            - `searchQuery` string: Giphy picker search text or GIF title, when the client stored one. Omitted when empty. Present on GIF attachments so consumers can treat it as the sender's intended reaction even if the clip is hard to interpret.
            - `url` string, uri: Signed URL to download the attachment. **Important:** URLs expire after 60 minutes.
            - `transcription` WebhookTranscription: Transcription data for audio attachments (voice messages). **Transcription flow:** 1. `message.created` arrives with `transcription.status: "Pending"` 2. `message.updated` arrives when transcription completes with `status: "Done"` and `text` populated
              - …
          - `mentions` MessageMention[]: Array of user mentions in the message
            - `id` string: The mentioned profile ID
            - `name` string: Display name used for the mention text
          - `location` WebhookLocation: Shared location data
            - `latitude` string: Latitude coordinate
            - `longitude` string: Longitude coordinate
            - `name` string: Location name (optional)
            - `address` string: Street address (optional)
          - `task` WebhookTask: Task snapshot sent when a task is created, updated, or its status changes. The `action` field indicates what triggered this snapshot.
            - `id` string: The task ID
            - `action` 'Added' | 'Updated' | 'Deleted' | 'MarkedAsDone' | 'MarkedAsOpened' | 'Replied': What triggered this task snapshot: - `Added`: Task was created - `Updated`: Task details were changed - `Deleted`: Task was removed - `MarkedAsDone`: Task was completed - `MarkedAsOpened`: Task was reopened - `Replied`: Comment was added to task
            - `title` string: Task title
            - `text` string: Task description
            - `status` 'Open' | 'Done': Current task status
            - `assignee` string: User ID of the assignee
            - `dueDate` integer: Due date as Unix timestamp in milliseconds
            - `isDueDateTimeSelected` boolean: Whether a specific time was selected for the due date
            - `parentId` string: Parent task ID (for subtasks)
            - `subItemsCount` integer: Number of subtasks
          - `contact` WebhookContact: Shared contact information
            - `name` string: Contact name
            - `phoneNumbers` string[]: Phone numbers
            - `emails` string[]: Email addresses
            - `role` string: Job title or role
            - `location` string: Location or office
            - `linkedIn` string: LinkedIn profile URL
            - `profileId` string: Zenzap profile ID if the contact is a Zenzap user
          - `poll` WebhookPoll: Poll data
            - `id` string: The poll (attachment) ID
            - `title` string: Poll question
            - `subtitle` string: Optional secondary line
            - `selectionType` 'single' | 'multiple': Whether voters may pick one option or several
            - `anonymous` boolean: Whether votes are anonymous
            - `status` 'active' | 'closed': Current poll status
            - `expiresAt` integer: Expiry as Unix timestamp in milliseconds (optional)
            - `options` object[]: The poll options
              - …
          - `call` WebhookCall: Audio/video call snapshot. The inline fields (`id`, `initiatorId`, `callType`) come from the message; live-state fields (`status`, `startedAt`, `endedAt`, `endReason`, `duration`, `participantCount`) are present only when the call record resolves.
            - `id` string: The call ID
            - `initiatorId` string: Profile ID of whoever started the call
            - `callType` 'audio' | 'video': Whether the call is audio- or video-first
            - `status` 'initiating' | 'calling' | 'active' | 'ended' | 'cancelled': Current call status
            - `startedAt` integer: When the call started, as Unix timestamp in milliseconds
            - `endedAt` integer: When the call ended, as Unix timestamp in milliseconds
            - `endReason` 'caller_not_joined' | 'caller_cancelled' | 'no_one_answered' | 'finished' | 'max_duration' | 'last_participant': Why the call ended
            - `duration` integer: Call duration in milliseconds (`endedAt - startedAt`), when both are known
            - `participantCount` integer: Number of profiles that actually joined the call
        - `updatedFields` string[]: List of fields that changed
        - `truncated` boolean: Whether the payload content was truncated
      - WebhookMessageDeletedData
        - `messageId` string, uuid, required: The deleted message ID
        - `topicId` string, uuid, required: The topic where the message was deleted
        - `deletedAt` integer: Unix timestamp in milliseconds when deletion occurred
        - `deletedBy` string: The user ID who deleted the message
        - `message` object: Minimal message snapshot
          - `id` string, uuid
          - `topicId` string, uuid
          - `senderId` string
      - WebhookReactionData
        - `messageId` string, uuid, required: The message that was reacted to
        - `topicId` string, uuid, required: The topic containing the message
        - `emoji` string, required: The emoji that was added/removed
        - `userId` string, required: The user who added/removed the reaction
        - `reactionId` string, uuid: Reaction ID (present when available)
        - `createdAt` integer: Unix timestamp in milliseconds when the reaction event was created
        - `reaction` WebhookReactionObject
          - `id` string, uuid: Reaction ID
          - `messageId` string, uuid: The reacted message ID
          - `topicId` string, uuid: Topic ID containing the message
          - `userId` string: The reacting user ID
          - `emoji` string: The reaction emoji
      - WebhookMemberData
        - `topicId` string, uuid, required: The topic where the member was added/removed
        - `memberIds` string[], required: One or more member IDs affected by the operation
        - `memberId` string: Single member ID (present when exactly one member changed)
        - `memberType` 'user' | 'bot': Type of member for single-member events
        - `actorId` string: The actor who triggered the member change
        - `createdAt` integer: Unix timestamp in milliseconds when the event payload was created
      - WebhookTopicUpdatedData
        - `topicId` string, uuid, required: The topic that was updated
        - `name` string: New topic name (only present if changed)
        - `description` string: New topic description (only present if changed)
        - `actorId` string: The actor who made the update
        - `changes` object: Map of changed fields and their new values
  - `nextOffset` string, required: Opaque offset to pass as `offset` in the next `/v2/updates` call

## Other responses

- `400`: Bad request - invalid input
- `401`: Unauthorized - invalid or missing API token
- `409`: Conflict - request cannot be completed in current state
- `500`: Internal server error

---

[API](https://skmtc.dev/zenzap/apis/zenzap-external-integration-api.md) - [All operations](https://skmtc.dev/zenzap/apis/zenzap-external-integration-api/llms.txt) - [OpenAPI document](https://skmtc.dev/zenzap/apis/zenzap-external-integration-api/revisions/37faae2c2036?raw)
