Event Failover

When event delivery fails after all retry attempts, Stream can automatically persist the failed event to a storage backend you control. This ensures no events are lost during outages in your endpoint.

Currently, Google Cloud Storage (GCS) is supported as a failover storage backend.

How it Works

  1. Stream attempts to deliver an event to your endpoint
  2. If all delivery retries are exhausted, the event is written to your configured GCS bucket
  3. The event is stored as a JSON file containing the full event payload, metadata about the failure, and the original endpoint URL

You can then process these failed events at your own pace by reading them from GCS.

Configuration

To enable failover, add a failover_config to your event hook:

await client.updateAppSettings({
  event_hooks: [
    {
      enabled: true,
      hook_type: "webhook",
      webhook_url: "https://example.com/webhooks/stream",
      event_types: ["message.new"],
      failover_config: {
        type: "gcs",
        gcs_bucket: "my-failover-bucket",
        gcs_path: "stream/failed-events",
        gcs_credentials: '{"type":"service_account","project_id":"..."}',
      },
    },
  ],
});

GCS credentials are validated when you save the configuration. Make sure the service account JSON key is valid and has write access to the specified bucket before updating your app settings.

Failover is currently supported for webhook hooks. Support for SQS and SNS hooks is planned.

Configuration Options

OptionTypeDescriptionRequired
typestringStorage backend type. Currently only "gcs" is supportedYes
gcs_bucketstringThe name of your GCS bucketYes
gcs_credentialsstringGCS service account JSON key as a stringYes
gcs_pathstringOptional prefix for the object path inside the bucketNo

GCS Permissions

The service account used in gcs_credentials needs the following permissions on the target bucket:

  • storage.objects.create
  • storage.objects.get

The simplest way is to grant the Storage Object Creator (roles/storage.objectCreator) role to the service account on the bucket.

Storage Format

Failed events are stored as JSON files in your GCS bucket with the following path structure:

{gcs_path}/{yyyy}/{mm}/{dd}/{timestamp}-{event_type}-{hook_id}.json

For example:

stream/failed-events/2026/04/03/1743667200-message.new-a1b2c3d4.json

Each file contains a JSON envelope with the following fields:

{
  "original_hook_id": "hook-123",
  "original_webhook_url": "https://example.com/webhooks/stream",
  "event_type": "message.new",
  "error_message": "HTTP 500: Internal Server Error",
  "failed_at": "2026-04-03T12:00:00Z",
  "payload": {
    "type": "message.new",
    "cid": "messaging:general",
    "message": { ... },
    "user": { ... }
  }
}
FieldDescription
original_hook_idThe ID of the event hook that failed
original_webhook_urlThe endpoint URL that was unreachable
event_typeThe type of event that failed to deliver
error_messageThe error returned by the last delivery attempt
failed_atISO 8601 timestamp of when the failure was recorded
payloadThe full event payload that would have been delivered

Disabling Failover

To remove the failover configuration, update the hook without the failover_config field:

await client.updateAppSettings({
  event_hooks: [
    {
      enabled: true,
      hook_type: "webhook",
      webhook_url: "https://example.com/webhooks/stream",
      event_types: [],
      // no failover_config = failover disabled
    },
  ],
});