When a delivery attempt fails (non-2xx response or timeout), Arowana retries with exponential backoff. Your handler must be idempotent and respond quickly.

Retry schedule

Failed deliveries are retried up to 5 times over ~24 hours:
AttemptDelay after failure
1Immediate
2~1 minute
3~5 minutes
4~1 hour
5~24 hours
After 5 failed attempts, the event is marked as permanently failed. You can view failed deliveries in the dashboard and replay them manually.

Ordering

Events are delivered best-effort ordered. Network conditions, retries, and parallel delivery mean events may arrive out of order. Always use the id and timestamp fields on the event envelope for deduplication and sequencing — not arrival order.

Timeout

Your handler must respond with a 2xx status code within 5 seconds. If it takes longer, the delivery is treated as failed and retried.
Process events asynchronously — never block on database writes, API calls, or downstream work inside the handler. Accept the event, return 200, and process it in a background job.

Idempotency

The same event id may arrive more than once due to retries or replays. Your handler must be idempotent:
  • Store processed event IDs and skip duplicates
  • Use upsert operations rather than inserts
  • Design side-effects to be safe to repeat
app.post("/webhooks/arowana", async (req, res) => {
  const event = JSON.parse(req.body);

  const alreadyProcessed = await db.webhookEvents.findById(event.id);
  if (alreadyProcessed) {
    return res.status(200).send("Already processed");
  }

  await db.webhookEvents.insert({ id: event.id, processed_at: new Date() });
  await processEvent(event);

  res.status(200).send("OK");
});