Handle event deliveries and retries

Stedi begins sending events to your webhook immediately after you configure an event destination.

Review events and deliveries

You can access events and event delivery attempts manually through the Stedi portal or programmatically through Stedi's Events API.

Stedi portal

You can review all events and attempted event deliveries in the Stedi portal. This allows you to verify your destination is working and helps with troubleshooting.

To review events and event deliveries through the Events page:

  1. Go to the Events page in your Developer settings. Stedi displays a list of all events in your account for the past 30 days.
  2. Click an event to review its details. Stedi shows key information, including the created date and type, as well as a list of every delivery attempt to configured event destinations.
  3. Click a delivery attempt to review its complete details, including the HTTP status code and the full request and response bodies for that attempt.

To review event deliveries for a specific event destination:

  1. Go to the Event destinations page in your Developer settings.
  2. Click an event destination and select its Event deliveries tab. Stedi lists all the event delivery attempts and their status for the past 30 days.
  3. Click an event delivery to review its details, including the HTTP status code and the full request and response bodies for that attempt.

Events API

You can also retrieve events programmatically through the following endpoints:

  • List Events: Retrieve a list of all events generated in your Stedi account within the last 30 days. You can filter by event type, delivery status, and created date.
  • Retrieve Event: Retrieve the full details of a specific event, including the full event payload Stedi sends to event destinations.

Delivery failures

When Stedi can't deliver an event to one or more event destinations, Stedi automatically retries each failed delivery for up to 48 hours. If all attempted retries fail, Stedi temporarily disables the associated event destination to prevent more missed deliveries. Once you address the issue, you can re-enable the event destination to resume event deliveries.

Automatic retries

Stedi automatically retries at the following intervals:

  • 5 minutes
  • 30 minutes
  • 2 hours
  • 8 hours
  • 24 hours
  • 48 hours

You can review all retry attempts from within the Stedi portal.

Manual retries

You can manually retry event deliveries as needed. Manual retries reset Stedi's automated retry process. For example, if you manually retry an event delivery after the second automatic attempt at 2 hours, the next automated retry attempt will occur after 5 minutes and continue according to Stedi's retry schedule for up to 48 hours.

To retry through the Events page:

  1. Go to the Events page in your Developer settings. Stedi displays a list of all events in your account.
  2. Click an event to review its details. Stedi shows key information, including the created date and source, as well as a list of every delivery attempt to configured event destinations.
  3. Click the Retry button next to a failed delivery to start a retry attempt. Stedi adds a new delivery row to the event record so you can monitor the progress.

To retry through an event destination's Event deliveries tab:

  1. Go to the Event destinations page in your Developer settings.
  2. Click an event destination and select its Event deliveries tab. Stedi lists all the event delivery attempts and their status.
  3. Click an event delivery to review its details.
  4. Click Retry delivery to start a retry attempt. Stedi adds a new event delivery row to the Event deliveries tab so you can monitor the progress.

Notification emails

When event deliveries to an event destination repeatedly fail, Stedi sends notification emails to every member of the Stedi account. Members receive emails at the following intervals:

  1. After 2 hours: If retry attempts for all events continue failing for 2 hours, we send an email notifying you that deliveries are failing for the associated event destination. You should log in to your Stedi account and investigate the issue as soon as you can. Stedi will continue automatic retries.
  2. After 24 hours: If retry attempts for all events associated with an event destination continue failing for 24 hours, we send another email informing you that Stedi's retry limit is approaching.

If all attempts fail, Stedi disables the associated event destination to prevent more missed deliveries.

Process undelivered events

Stedi automatically retries event deliveries for up to 48 hours before marking them as failed. However, we recommend implementing a strategy to manually process undelivered events in case your endpoint remains unavailable or event delivery is interrupted.

  1. Call the List Events endpoint to retrieve a list of all events with a FAILED status. The response includes an id for each event.

    {
      "items": [
        {
          "id": "evt_a81659f1-16a5-9bec-03e1-0ba8ab5e9652",
          "eventType": "enrollment.activated",
          "status": "FAILED",
          "createdAt": "2026-02-01T12:00:00Z"
        }
      ]
    }
  2. Call the Retrieve Event endpoint to get the full payload for each event.

    {
      "account": "11111111-2222-3333-4444-555555555555",
      "created": "2026-03-30T23:19:00.501Z",
      "environment": "PRODUCTION",
      "id": "evt_a81659f1-16a5-9bec-03e1-0ba8ab5e9652",
      "object": "v1.event",
      "resource": {
        "id": "019bb508-dc63-73a1-8ddc-9d4720299072",
        "type": "enrollment"
      },
      "type": "enrollment.activated"
    }
  3. Process each event. Note that the receipt time will likely be outside your normal tolerance window.

Message handling best practices

We strongly recommend implementing these best practices for event handling.

Don't rely on event order

Stedi attempts to deliver events as soon as possible after they're generated. However, we can't guarantee that your endpoint will receive events in order. Ensure your implementation isn't dependent on event order.

Verify authenticity and receipt time

We strongly recommend verifying each signed message to ensure it's from Stedi. You should also verify that the message was generated within your expected timeframe for receipt. This helps protect you from replay attacks.

Each event contains two headers you can use to verify a message's authenticity: webhook-signature and webhook-timestamp.

webhook-signature: v1,Z5wMZ2rqRMsnGdfKzOLnRv1SIwivsLFCGQzJH7eCmlU=
webhook-timestamp: 1774641758

To verify the message authenticity and receipt time:

  1. Create a signed_payload string by concatenating the timestamp (as a string) with the character . and the entire JSON request body.

  2. Compute an HMAC with the SHA256 hash function. Use the event destination secret as the key, and use the signed_payload string as the message. This should produce a signature.

  3. Split the signature string in the message's webhook-signature header from the version (v1).

  4. Compare the signature from the event message to your computed signature. If they are equivalent, then the message is from Stedi. If not, reject the message.

  5. Compute the difference between the timestamp provided in the message and the current timestamp. If the difference is outside your tolerance, reject it.

    To protect against timing attacks, use a constant-time string comparison to compare the expected signature to each one you received from Stedi.

Our implementation follows the Standard Webhooks specification. The following TypeScript example uses the standardwebhooks library for signature verification.

import { Webhook } from "standardwebhooks";
import type { CapturedWebhook } from "./types";

export function assertWebhookValid(
  webhook: CapturedWebhook,
  secret: string,
): void {
  const wh = new Webhook(secret);
  try {
    wh.verify(webhook.request.body, webhook.request.headers);
  } catch (err) {
    throw new Error(
      `Webhook signature verification failed: ${err instanceof Error ? err.message : String(err)}. ` +
        `Headers: ${JSON.stringify(webhook.request.headers)}`,
    );
  }
}

Check the resource record

Stedi events use a thin event schema that doesn't include the updated resource's data.

Before taking action based on an event, you should use Stedi APIs to retrieve the resource's details and verify its current state. This approach ensures you're acting on the latest data, even if events arrive out of order.

Check for duplicates

Your webhook may occasionally receive the same event multiple times. For example, your webhook may receive the same event from both automatic retries and a manual recovery process.

We strongly recommend logging the IDs for events you've processed, comparing them against new events, and filtering out duplicates.

We recommend using the webhook-id header for this purpose. It contains a unique Stedi-generated identifier for the event, formatted as msg_{UUID}.

webhook-id: msg_b73ae1d2-9128-90b5-60ff-7479ab8b30ac

Check for processing errors

We recommend periodically listing events to catch cases where events may have been delivered successfully but your processing logic encountered an error.

  1. Call the List Events endpoint to retrieve events generated in your Stedi account. Use the created parameter to filter for events within a specific time range and use the eventType parameter to filter for the event types relevant to your use case.
  2. Compare the event IDs against the your internal records for processed events.

Note that Stedi sends notification emails when event deliveries to an event destination fail repeatedly. You should also monitor for these emails to help catch connectivity issues as soon as possible.

On this page