Event Feed
Overview
The event feed is a read-only API that stores a chronological log of all domain events emitted by your tenant. Unlike push webhooks, which require your endpoint to be available at the moment an event occurs, the event feed retains events for a configurable period (30 days by default) so you can poll at your own pace.
Use the event feed when you need to:
- Reconcile - catch up on events missed during consumer downtime
- Audit - build a complete history of entity changes for compliance
- Batch process - consume events in bulk on a schedule rather than reacting in real-time
- Debug - inspect the exact sequence and content of events during development
Polling the Event Feed
List Events
Returns events in chronological order using cursor-based pagination. Each event has a ULID identifier that serves as the cursor.
Endpoint: GET /api/v1/webhooks/events
Authentication: Required (Bearer token)
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
after | string | ULID cursor; returns events created after this ID (exclusive) |
event_type | string | Filter by event type pattern; supports wildcards (product.*, *) |
entity_type | string | Filter by entity type (product, customer, order, invoice) |
per_page | integer | Results per page (default: 50, max: 100) |
Example Request:
GET /api/v1/webhooks/events?after=01jevt100abc000def000ghi0&event_type=product.*&per_page=25Response (200 OK):
{
"success": true,
"data": [
{
"id": "01jevt101abc001def001ghi1",
"event_type": "product.created",
"entity_type": "product",
"entity_id": "01jprod789abc012def345ghi6",
"occurred_at": "2026-03-15T10:30:00Z",
"payload": {
"id": "01jprod789abc012def345ghi6",
"name": "Wireless Keyboard",
"sku": "KB-WIRELESS-001",
"status": "active",
"selling_price": 49.99,
"currency": "USD"
}
},
{
"id": "01jevt102abc002def002ghi2",
"event_type": "product.price_changed",
"entity_type": "product",
"entity_id": "01jprod789abc012def345ghi6",
"occurred_at": "2026-03-15T11:00:00Z",
"payload": {
"id": "01jprod789abc012def345ghi6",
"name": "Wireless Keyboard",
"sku": "KB-WIRELESS-001",
"status": "active",
"selling_price": 44.99,
"currency": "USD"
}
}
],
"meta": {
"per_page": 25,
"has_more": true,
"cursor": "01jevt102abc002def002ghi2"
},
"links": {
"next": "/api/v1/webhooks/events?after=01jevt102abc002def002ghi2&event_type=product.*&per_page=25"
}
}Cursor-Based Pagination
The event feed uses ULID-based cursors rather than page numbers. ULIDs are time-ordered, which means the cursor also represents a point in time. This approach has several advantages:
- No skipped or duplicated events - new events inserted between polls do not shift page boundaries
- Efficient queries - the database uses an index range scan starting from the cursor
- Stateless consumers - store only the last cursor value to resume from where you left off
To paginate:
- Make an initial request without the
afterparameter to start from the oldest available event - Process the returned events
- Store the
cursorvalue from themetaobject - On the next poll, pass
after={cursor}to fetch events newer than your last position - Repeat until
has_moreisfalse, then wait before polling again
Empty Responses
When no new events are available, the response returns an empty data array with has_more: false. This is not an error - it means you are caught up.
Filtering
By Event Type
The event_type parameter supports the same wildcard patterns as subscription event types:
# All product events
GET /api/v1/webhooks/events?event_type=product.*
# Only customer creation events
GET /api/v1/webhooks/events?event_type=customer.created
# All events (no filter or explicit wildcard)
GET /api/v1/webhooks/events?event_type=*By Entity Type
The entity_type parameter filters by the entity that emitted the event. This is useful when you need all events for a particular domain object regardless of the specific event type:
# All order-related events
GET /api/v1/webhooks/events?entity_type=order
# Combine with event_type for precise filtering
GET /api/v1/webhooks/events?entity_type=product&event_type=product.price_changedExample Polling Loop
The following pseudocode demonstrates a reliable polling consumer that persists its cursor position between runs:
import time
import requests
API_BASE = "https://api.synthesq.com/api/v1"
TOKEN = "your-bearer-token"
CURSOR_FILE = "/var/data/webhook-cursor.txt"
POLL_INTERVAL = 30 # seconds
def load_cursor():
try:
with open(CURSOR_FILE, "r") as f:
return f.read().strip() or None
except FileNotFoundError:
return None
def save_cursor(cursor):
with open(CURSOR_FILE, "w") as f:
f.write(cursor)
def poll_events():
cursor = load_cursor()
while True:
params = {"per_page": 100}
if cursor:
params["after"] = cursor
response = requests.get(
f"{API_BASE}/webhooks/events",
headers={"Authorization": f"Bearer {TOKEN}"},
params=params,
)
data = response.json()
for event in data["data"]:
process_event(event)
if data["meta"]["has_more"]:
cursor = data["meta"]["cursor"]
save_cursor(cursor)
# Continue immediately - more events available
else:
if data["data"]:
cursor = data["meta"]["cursor"]
save_cursor(cursor)
# Caught up - wait before next poll
time.sleep(POLL_INTERVAL)
def process_event(event):
print(f"Processing {event['event_type']}: {event['entity_id']}")
# Your business logic here
poll_events()Choosing a Poll Interval
A 30-second interval works well for most use cases. For near-real-time requirements, poll every 5-10 seconds. For batch processing, a longer interval (5-15 minutes) reduces API calls without losing events, since the feed retains data for 30 days.
Retention Policy
Events in the feed are retained for 30 days by default. After the retention period, events are permanently deleted and can no longer be retrieved through the API.
The retention period is measured from the occurred_at timestamp of each event. Events are purged in background cleanup jobs that run daily.
Plan for Retention
If your consumer is offline for longer than the retention period, events emitted during the gap are lost. For critical integrations, combine push webhooks with event feed polling to minimize the risk of data loss.
Related Documentation
- Managing Subscriptions - Configure push webhook delivery
- Delivery & Reliability - Push delivery lifecycle and retry strategy
- Webhooks Overview - Module architecture and quick start