Amba

Push Scheduling

How Amba runs scheduled pushes — delays, retries, idempotency, and delivery windows.

When you schedule a push, Amba takes care of waiting until the right moment, retrying transient failures, and recording per-token delivery results. You don't manage timers or job queues — schedule the campaign and Amba handles delivery.

What Amba does for a scheduled campaign

When you POST a campaign with scheduled_at, or call /send, the API:

  1. Atomically marks the campaign row (scheduled or sending).
  2. Schedules delivery on the campaign's scheduled_at.
  3. Returns to the caller. The rest happens in the background.

When the scheduled time arrives:

  1. The target segment is resolved.
  2. Active push tokens for matching users are fetched.
  3. Users are sent in batches.
  4. One row per token is written to push_deliveries with status, provider message id, and error detail on failure.
  5. The campaign is marked sent.

Trying to schedule the same campaign twice raises 409 ALREADY_SCHEDULED — delivery is idempotent and double-dispatch is impossible.

Scheduling with scheduled_at

scheduled_at is a UTC ISO-8601 timestamp. The API rejects past and unparseable values:

POST /admin/push
 
{
  "title": "Morning reminder",
  "body": "Time to check your goals.",
  "scheduled_at": "2026-05-01T13:00:00Z"
}

The campaign row is inserted with status = 'scheduled'. Amba fires delivery at the caller's intended time without drift from API-side latency.

Cancelling a scheduled push

There is no DELETE /admin/push/:id today. To kill a scheduled campaign before it fires, contact support. The row stays at scheduled; you can flip it manually if needed.

Trying to /send a scheduled campaign returns 409 ALREADY_SCHEDULED — delivery is already on the way.

Retries and idempotency

Delivery is retried automatically:

  • Transient APNs / FCM 5xx → retried with backoff, bounded by max-attempts.
  • BadDeviceToken / Unregistered → the token is marked inactive, no retry (permanent).
  • Workflow-level failures roll the campaign status to failed so operators see it.

Replaying /send on a failed campaign re-attempts delivery cleanly — the campaign id makes sends idempotent.

Per-project fan-out

Every campaign runs against exactly one project's data. There is no cross-project query. Hot projects are kept in memory so repeated campaigns don't pay a cold-start cost.

Delivery windows

There is no "quiet hours" feature at the protocol layer. To implement user-local delivery windows, schedule the campaign at the earliest UTC time that satisfies the window for every target timezone, or batch per-timezone campaigns. A first-class delivery-windows feature is on the roadmap.

Observability

Per-token delivery rows land in push_deliveries with status (sent / failed), provider_message_id, and error_message on failure. Query that table directly when you need a user-level audit trail.

Next

On this page