Transport Types
Robots receive events through one of three transport mechanisms. Choose the one that best fits your infrastructure and reliability requirements.
Webhook
Lag sends an HTTP POST request to your configured URL each time an event occurs. This is the most common transport for production integrations.
How It Works
- An event occurs in your server (message sent, user joins, etc.)
- Lag constructs a JSON payload with the event data
- Lag sends an HTTP POST to your webhook URL
- Your server processes the event and responds with a 2xx status code
Request Format
Each webhook request includes these headers:
| Header | Description |
|---|---|
Content-Type | application/json |
webhook-id | Unique identifier for this delivery attempt |
webhook-timestamp | Unix timestamp (seconds) when the event was sent |
webhook-signature | HMAC-SHA256 signature for verification |
User-Agent | Lag-Webhooks/1.0 |
The request body contains the event payload as JSON.
Signature Verification
Webhook requests are signed following the Standard Webhooks specification. The signature uses HMAC-SHA256 with your webhook secret.
To verify a signature:
- Decode the base64 portion of your
whsec_prefixed secret - Construct the signed content:
{webhook-id}.{webhook-timestamp}.{body} - Compute HMAC-SHA256 of the signed content using the decoded secret
- Base64-encode the result and compare with the signature in the header
The webhook-signature header contains one or more signatures prefixed with v1,. Match against any of them (multiple signatures appear during secret rotation).
Retry Logic
If your endpoint does not respond with a 2xx status within 15 seconds, Lag retries the delivery. The retry schedule uses exponential backoff:
| Attempt | Delay |
|---|---|
| 1 | Immediate |
| 2 | 5 seconds |
| 3 | 25 seconds |
| 4 | 2 minutes |
| 5 | 10 minutes |
| 6 | 30 minutes |
| 7 | 1 hour |
| 8 | 3 hours |
| 9 | 8 hours |
| 10 | 24 hours |
After 10 failed attempts, the event is marked as failed.
If your endpoint responds with 410 Gone, Lag immediately disables the robot to prevent further delivery attempts. This is useful for intentionally decommissioning a webhook endpoint.
Requirements
- Your URL must use HTTPS
- Your server must respond within 15 seconds
- Return a 2xx status code to acknowledge receipt
- Verify signatures to ensure requests are from Lag
SSE (Server-Sent Events)
Your application maintains a persistent HTTP connection to Lag and receives events as a real-time stream.
How It Works
- Your application sends a GET request to
/robots/@me/events/sse - Lag holds the connection open and sends events as they occur
- Events arrive as standard SSE text/event-stream data
- Your client processes each event as it arrives
Connection Setup
GET /robots/@me/events/sse
Authorization: Robot lag_robot_<prefix>_<secret>
Accept: text/event-stream The response is a text/event-stream with events formatted as:
id: evt_01H1234567890
event: room.message
data: {"type":"room.message","serverId":"...","data":{...}}
Keepalive
Lag sends a keepalive comment (:keepalive) every 30 seconds to prevent proxy timeouts. If you do not receive any data (events or keepalives) for more than 60 seconds, your connection has likely been dropped and should be re-established.
Replay with Last-Event-ID
If your connection drops, you can resume from where you left off by including the Last-Event-ID header with the ID of the last event you received:
GET /robots/@me/events/sse
Authorization: Robot lag_robot_<prefix>_<secret>
Last-Event-ID: evt_01H1234567890 Lag will replay any events that occurred after the specified ID.
Event Delivery
Events are marked as delivered as soon as they are sent over the SSE connection. There is no acknowledgment mechanism - if your client crashes after receiving but before processing an event, that event will not be redelivered unless you reconnect with the appropriate Last-Event-ID.
Long-Poll
Your application periodically requests pending events from Lag. This is the simplest transport to implement and works well behind restrictive firewalls.
How It Works
- Your application sends a GET request to
/robots/@me/events/poll - If events are pending, Lag returns them immediately
- If no events are pending, Lag holds the connection for up to 30 seconds
- After 30 seconds with no events, Lag returns an empty array
- Your application immediately sends another request
Request Format
GET /robots/@me/events/poll
Authorization: Robot lag_robot_<prefix>_<secret> Response Format
{
"events": [
{
"id": "evt_01H1234567890",
"type": "room.message",
"timestamp": "2024-01-15T10:30:00Z",
"data": { ... }
}
]
} Each poll returns up to 50 events at a time. If more events are pending, poll again immediately to retrieve the next batch.
An empty response looks like:
{
"events": []
} Delivery Semantics
Events are marked as delivered once they are included in a poll response. Poll immediately after processing to minimize latency.
Comparison
| Feature | Webhook | SSE | Long-Poll |
|---|---|---|---|
| Latency | Low (push) | Lowest (stream) | Medium (up to poll interval) |
| Implementation | Medium | Medium | Simple |
| Firewall-friendly | Requires inbound HTTPS | Outbound only | Outbound only |
| Reliability | High (retries) | Medium (reconnect needed) | Medium (poll on failure) |
| Server required | Yes (public HTTPS) | No | No |
| Ordering | Guaranteed per event | Guaranteed | Guaranteed |
| Best for | Production services | Real-time bots | Simple integrations |