> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Push Notifications API

> Register mobile device tokens (FCM / APNs) and send push notifications to your end users.

# Push Notifications API

Send push notifications to your end users' iOS and Android devices through Apple Push Notification service (APNs) and Firebase Cloud Messaging (FCM). Orbit holds your provider credentials and exposes a single send endpoint that fans out to the right transport.

**Base path:** `/api/v1/push`

**Authentication:** API key (`X-API-Key`) or session JWT.

## Device tokens

Your mobile app registers each device's push token after the user grants permission. Re-register on token rotation; APNs and FCM both rotate tokens periodically.

| Method   | Path                              | Purpose                                  |
| -------- | --------------------------------- | ---------------------------------------- |
| `POST`   | `/api/v1/push/register`           | Register a device token (mobile app SDK) |
| `POST`   | `/api/v1/push/device-tokens`      | Register or update a token (server-side) |
| `GET`    | `/api/v1/push/device-tokens`      | List tokens for the current user         |
| `DELETE` | `/api/v1/push/device-tokens/{id}` | Unregister a token                       |

## Sending

| Method | Path                         | Purpose                                                                                 |
| ------ | ---------------------------- | --------------------------------------------------------------------------------------- |
| `POST` | `/api/v1/push/send`          | Send a push notification to specific devices (`device_token_ids`) or users (`user_ids`) |
| `GET`  | `/api/v1/push/notifications` | List sent notifications                                                                 |

## Example — send to a user's devices

Target a send with either `user_ids` (every registered device for those users) or `device_token_ids` (specific device tokens). At least one of the two is required.

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/push/send \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "user_ids": ["usr_abc123"],
    "title": "Your order has shipped",
    "body": "Tracking #1Z999AA10123456784",
    "data": { "order_id": "ord_42" },
    "deep_link": "myapp://orders/ord_42"
  }'
```

To notify specific devices instead, pass `device_token_ids` with the ids returned from `/api/v1/push/device-tokens`:

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/push/send \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "device_token_ids": ["dvt_abc123"],
    "title": "Your order has shipped",
    "body": "Tracking #1Z999AA10123456784"
  }'
```

Each entry in the response `notifications[]` array carries a `transport` field — `apns`, `fcm`, `webpush`, or `hms` — identifying which transport was used to reach that device, alongside its `deviceTokenId` and delivery `status`.

## Scheduled pushes

To send later, add an ISO 8601 `send_at` to `POST /api/v1/push/send`. When `send_at` is in the future the notification is stored instead of dispatched and the request returns `202 Accepted`; a background worker replays it through the same fan-out and suppression logic when the time arrives. Omit `send_at` (or pass a past instant) to send immediately.

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/push/send \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "user_ids": ["usr_abc123"],
    "title": "Flash sale starts soon",
    "body": "20% off for the next hour",
    "send_at": "2026-06-20T09:00:00Z"
  }'
```

Manage scheduled pushes that have not yet been sent:

| Method   | Path                          | Purpose                                                                                      |
| -------- | ----------------------------- | -------------------------------------------------------------------------------------------- |
| `GET`    | `/api/v1/push/scheduled`      | List scheduled pushes (optional `status` filter: `scheduled`, `sent`, `cancelled`, `failed`) |
| `GET`    | `/api/v1/push/scheduled/{id}` | Read a single scheduled push                                                                 |
| `DELETE` | `/api/v1/push/scheduled/{id}` | Cancel a scheduled push that has not been sent yet                                           |

### Cancelling a scheduled push

`DELETE /api/v1/push/scheduled/{id}` cancels a push that is still in the `scheduled` state. Cancel is a guarded status flip, not a hard delete — the row remains queryable with `status: "cancelled"`.

```bash theme={null}
curl -X DELETE https://api.orbit.devotel.io/api/v1/push/scheduled/sched_abc123 \
  -H "X-API-Key: dv_live_sk_your_key_here"
```

Cancellation only succeeds while the push is still pending. If the worker has already dispatched it (`sent` / `failed`) or it was cancelled by an earlier request, the endpoint returns **`409 Conflict`** with error code **`SCHEDULED_PUSH_NOT_CANCELLABLE`** — the push has already left and can no longer be recalled. Branch on this code rather than treating the cancel as a silent success. An unknown id returns `404 Not Found` (`NOT_FOUND`).

```json theme={null}
{
  "error": {
    "code": "SCHEDULED_PUSH_NOT_CANCELLABLE",
    "message": "Scheduled push 'sched_abc123' is already 'sent' and can no longer be cancelled",
    "status": 409
  }
}
```

## See also

* [Webhooks → push events](/webhooks/events) — `push.delivered`, `push.opened`
* [Messaging API → unified send](/api-reference/endpoints/messaging) — push as one channel in a flow
