Safravo Logosafravo.com
Guides

Webhook Debugging

Monitor, test, and troubleshoot webhook deliveries from the Safravo dashboard.


Sending Test Events

You can instantly verify connectivity and signature validation by sending a test event directly from the dashboard:

  1. Go to Settings → Webhooks.
  2. Locate the endpoint in the list and click the Test button.
  3. A webhook.ping event will be dispatched immediately.
  4. Click the Logs button to see the result, including the HTTP status code and response body your server returned.

This simulates a real webhook delivery using the exact same infrastructure, making it the fastest way to debug signature and connectivity issues.


Endpoint Verification

When you create or update a webhook endpoint, Safravo performs a synchronous verification check:

  1. Safravo sends a POST request with a webhook.ping event to your URL.
  2. Your server must respond with a 2xx HTTP status code within 5 seconds.
  3. If the request fails, times out, or returns an error status (e.g., 404, 500), the endpoint will not be saved.

Ensure your server is running, publicly accessible via HTTPS, and ready to receive requests before adding the endpoint in the dashboard.


Accessing the monitoring panel

Every webhook endpoint has a dedicated monitoring panel in the dashboard.

  1. Go to Settings → Webhooks.
  2. Click Logs on the endpoint you want to inspect.

Delivery logs

The Logs tab shows the 50 most recent delivery attempts for that endpoint, sorted newest-first. Each entry includes:

FieldDescription
Event typee.g. message.created
StatusHTTP response code returned by your server
TimestampWhen the delivery was attempted
LatencyHow long your server took to respond

Logs expire automatically after 30 days.


Troubleshooting common errors

401 Unauthorized

Your signature verification logic is rejecting the request.

  • Check your secret — confirm the WEBHOOK_SECRET in your environment matches the secret shown when you created the endpoint in the dashboard. The secret is in whsec_<64 hex chars> format.
  • Use the raw body — compute the HMAC against the raw request body bytes, before any JSON parsing. See Security → Webhook Signature Verification.

500 Internal Server Error

Your server received the event but threw an exception while processing it.

  • Check your application logs for stack traces.
  • Respond immediately — Safravo waits up to 10 seconds for a 2xx response. If your processing takes longer, acknowledge the webhook immediately with 200 OK and move the work to a background queue.

Connection refused / Timeout

Safravo cannot reach your server.

  • Public access — your endpoint must be reachable from the public internet. localhost URLs will not work in production.
  • Firewall rules — check that your server is not blocking inbound requests from Safravo's IP ranges.
  • Local testing — if testing locally, use ngrok or Cloudflare Tunnel to expose your local server. See Testing Locally below.

Circuit breaker (auto-disable)

Safravo uses an automatic circuit breaker to prevent overwhelming failing endpoints.

StatusCondition
activeReceiving events normally
failing5 or more consecutive delivery failures — investigate immediately
disabled25 consecutive failures — auto-disabled, manual re-enable required

The failure counter resets to zero on any successful delivery, so a brief outage will not permanently penalise a healthy endpoint.

When an endpoint is auto-disabled, Safravo sends an email notification to the workspace owner.

Re-enabling a disabled endpoint

  1. Fix the issue on your server.
  2. Go to Settings → Webhooks and find the disabled endpoint.
  3. Click Re-enable.
  4. The first delivery attempt is delayed by 5 minutes as a cooldown.

Example Payload

When an event occurs, Safravo sends a POST request with a JSON body similar to the one below. Use the type field to determine how to process the data object.

{
  "id": "evt_1778293944479_wfway",
  "object": "event",
  "type": "message.created",
  "api_version": "v1",
  "created": "2026-05-09T02:32:25.364Z",
  "workspaceId": "69ea13a14bcf38af4b8670fb",
  "data": {
    "message": {
      "id": "69fe9cb887ab45e4342c6c60",
      "direction": "inbound",
      "status": "delivered",
      "type": "text",
      "content": "Hello",
      "createdAt": "2026-05-09T02:32:24.441Z"
    }
  }
}

Payload Fields

FieldTypeDescription
idstringUnique identifier for this event. Use this to prevent duplicate processing.
objectstringAlways event.
typestringThe event type (e.g., message.created, message.updated).
api_versionstringThe version of the webhook payload schema.
createdstringISO 8601 timestamp of when the event was generated.
workspaceIdstringThe unique ID of the workspace where the event occurred.
dataobjectThe actual resource associated with the event (e.g., the message object).

Security

Safravo signs all webhook events to allow you to verify that they originated from us.

Signature Verification

Each request includes an X-Safravo-Signature header. This is a HMAC-SHA256 signature generated using your endpoint's Secret.

  1. Retrieve the signature from the X-Safravo-Signature header.
  2. Generate a HMAC-SHA256 signature of the raw request body using your secret.
  3. Compare your generated signature with the one in the header.

Preventing Replay Attacks

Each request also includes an X-Safravo-Timestamp header. We recommend verifying that the timestamp is recent (e.g., within the last 5 minutes) before processing the event to prevent replay attacks.


Delivery retries

Failed deliveries are retried with exponential backoff:

AttemptDelay
1 (initial)Immediate
25 seconds
330 seconds
42 minutes
510 minutes
6–101 hour each

After 10 failed attempts, that specific event delivery is marked permanently failed and will not be retried.


Testing locally

Use ngrok or Cloudflare Tunnel to expose your local server during development.

# ngrok
ngrok http 3000
# → https://abc123.ngrok-free.app

# Cloudflare Tunnel
cloudflared tunnel --url http://localhost:3000

Register the tunnel URL as your webhook endpoint in Settings → Webhooks. Update it whenever the tunnel restarts — the free tier of ngrok generates a new URL on every restart. Use a paid ngrok account or Cloudflare Tunnel for a stable URL.

Cloudflare Tunnel is free and provides a stable URL that persists across restarts, making it a good alternative to ngrok for local development.


Deduplication

Safravo may deliver the same event more than once during retries. Use the X-Safravo-Delivery header as an idempotency key to avoid processing duplicates.

const deliveryId = req.headers['x-safravo-delivery'];

const alreadyProcessed = await redis.get(`webhook:${deliveryId}`);
if (alreadyProcessed) {
  return res.status(200).json({ received: true }); // idempotent
}

// ... process event ...

await redis.setex(`webhook:${deliveryId}`, 86400, '1'); // TTL: 24 hours

On this page