Event Streaming
ExternalMessageService.StreamSendRequestEvents is a server-streaming RPC that delivers real-time events for your send requests. It replaces webhooks: your server opens the connection, so there is no public endpoint to expose, and gRPC TLS provides authentication and integrity (no HMAC signatures to verify).
Event types
| Event type | Meaning |
|---|---|
RECIPIENT_DELIVERED | Message delivered to the recipient |
RECIPIENT_FAILED | Delivery failed |
RECIPIENT_PENDING_CONSENT | Awaiting the recipient's consent |
CONSENT_GRANTED | The recipient allowed messages from your app |
CONSENT_DENIED | The recipient declined |
REQUEST_COMPLETED | All recipients in a send request were processed |
POLL_RESPONSE_RECEIVED | A recipient answered a poll |
Event payload
Each SendRequestEvent contains:
| Field | Type | Description |
|---|---|---|
event_id | string | ULID — unique event id; use it as your cursor |
send_request_id | string | The send request this event belongs to |
event_type | enum | One of the types above |
recipient | optional | Present for per-recipient events (ppnum, error_code, …) |
summary | optional | Present for REQUEST_COMPLETED (delivered, failed, …) |
occurred_at | string | Event timestamp (RFC 3339) |
Reconnection
Pass after_event_id to resume from where you left off. Event IDs are ULIDs (chronologically sortable), and events missed during a disconnect are replayed on reconnect. Filter to a single send request with send_request_id, or omit it to receive all events for your app.
let mut cursor = load_saved_cursor().await; // persisted last_event_id
loop {
let mut client = connect_client(&token).await;
if let Err(e) = stream_events(&mut client, cursor.clone()).await {
eprintln!("stream error: {e}; reconnecting…");
// back off before retrying
}
cursor = load_saved_cursor().await; // updated as events arrive
}Persist last_event_id durably — it's how the stream resumes without gaps. See Security.
First-time recipients must consent before they receive messages. Handle the RECIPIENT_PENDING_CONSENT / CONSENT_GRANTED / CONSENT_DENIED events — the bulk-messaging guide walks through the consent flow.