# QueueFly Push Notification API — Reference

Base URL: `https://api.queuefly.com` · REST / JSON · Requires 3 auth headers on every request.

This document is a machine-readable copy of the QueueFly developer docs. Feed it to an AI assistant or LLM to get integration help.

## Contents

1. Authentication
2. Quick Start
3. Device Tokens
4. Push Notifications
5. Topics
6. Scheduling

---

## Authentication

Every request to the Notification API must include three custom HTTP headers. These credentials identify your client account and the tenant (app) you are targeting.

| Header | Type | Description |
| --- | --- | --- |
| `ClientId` | string | Your QueueFly account GUID — found in the QueueFly dashboard under your profile. |
| `TenantId` | string | The GUID of the tenant (application) you want to send notifications for. |
| `ApiKey` | string | The plain-text API key generated when you created the tenant. Shown only once — store it securely. |

Find your **ClientId** in your QueueFly profile and the **TenantId** / **ApiKey** in the Tenants panel after creating a tenant.

Example headers:

```bash
ClientId: 3fa85f64-5717-4562-b3fc-2c963f66afa6
TenantId: 9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d
ApiKey: sk_live_abcdef1234567890
```

### Error responses

A missing or invalid header combination returns `401 Unauthorized`. Malformed request bodies return `400 Bad Request`. Both use the standard `ProblemDetails` shape:

```ts
{
  type?: string     // URI reference identifying the problem type
  title?: string    // Short human-readable summary
  status?: number   // HTTP status code (400 or 401)
  detail?: string   // Human-readable explanation of this specific error
  instance?: string // URI reference identifying the specific occurrence
}
```

---

## Quick Start

Get up and running in under 5 minutes — start sending push notifications immediately. No prior token registration required.

- Base URL: `https://api.queuefly.com`
- REST / JSON
- 3 auth headers required

Prefer Postman? Import the collection and environment to try every endpoint without writing code:

- Collection: https://queuefly.com/postman/QueueFly.postman_collection.json
- Environment: https://queuefly.com/postman/QueueFly.postman_environment.json

### Step 1 — Send your first push notification

You only need a device FCM token — no prior user registration required.

```bash
curl -X POST https://api.queuefly.com/api/v1/push \
  -H "Content-Type: application/json" \
  -H "ClientId: <your-client-id>" \
  -H "TenantId: <your-tenant-id>" \
  -H "ApiKey: <your-api-key>" \
  -d '{
    "deviceToken": "fcm-device-token-here",
    "platform": "android",
    "subject": "Order shipped!",
    "body": "Your order #1234 is on its way.",
    "jsonData": { "orderId": "1234" },
    "imageUrl": "https://cdn.example.com/img.png"
  }'
```

### Step 2 — Optionally pre-register a device token

Pre-registering a token lets you associate it with an `externalUserId`, enabling the `/by-user` endpoints to fan out to all of a user's devices automatically.

```bash
curl -X PUT https://api.queuefly.com/api/v1/device-tokens \
  -H "Content-Type: application/json" \
  -H "ClientId: <your-client-id>" \
  -H "TenantId: <your-tenant-id>" \
  -H "ApiKey: <your-api-key>" \
  -d '{
    "token": "fcm-device-token-here",
    "platform": "android",
    "externalUserId": "user-123"
  }'
```

**Zero friction.** Send to any FCM device token directly — no user registration step needed. Optionally use `PUT /api/v1/device-tokens` to pre-register tokens and then `POST /api/v1/push/by-user` to fan out to all devices of a user. Retries and token rotation are handled automatically.

---

## Device Tokens

QueueFly auto-registers FCM device tokens in the background on the first push. You can also manage tokens explicitly for fine-grained control.

> **Cleaning up invalid tokens.** If a push fails because FCM reports a device token as invalid or unregistered, QueueFly calls your tenant's *Invalid Device Token* webhook to let you know that token is no longer valid — so you can delete it on your side. Configure the webhook URL and payload example in your tenant's settings under the client area.

| Method | Endpoint | Description |
| --- | --- | --- |
| PUT | `/api/v1/device-tokens` | Register or update a device token (upsert by token value) |
| DELETE | `/api/v1/device-tokens` | Remove a specific device token |
| GET | `/api/v1/device-tokens/by-user/{externalUserId}` | List all tokens for a user |
| DELETE | `/api/v1/device-tokens/by-user` | Remove all tokens for a user |
| GET | `/api/v1/device-tokens/usage` | Token usage stats for this tenant |

### Register / update a token

`PUT /api/v1/device-tokens` — idempotent. If the token already exists it is refreshed; otherwise it is created.

```bash
curl -X PUT https://api.queuefly.com/api/v1/device-tokens \
  -H "Content-Type: application/json" \
  -H "ClientId: <your-client-id>" \
  -H "TenantId: <your-tenant-id>" \
  -H "ApiKey: <your-api-key>" \
  -d '{
    "token": "fcm-device-token-here",
    "platform": "android",
    "externalUserId": "user-123"
  }'
```

Request body:

```ts
{
  token: string                      // FCM token from the client SDK (required)
  platform: 'android' | 'ios'        // (required)
  externalUserId?: string            // Your own user ID — used for by-user endpoints
}
```

### Delete a specific token

```bash
curl -X DELETE https://api.queuefly.com/api/v1/device-tokens \
  -H "Content-Type: application/json" \
  -H "ClientId: <your-client-id>" \
  -H "TenantId: <your-tenant-id>" \
  -H "ApiKey: <your-api-key>" \
  -d '{ "token": "fcm-device-token-here" }'
```

### List all tokens for a user

`GET /api/v1/device-tokens/by-user/{externalUserId}`

```bash
curl -X GET https://api.queuefly.com/api/v1/device-tokens/by-user/user-123 \
  -H "ClientId: <your-client-id>" \
  -H "TenantId: <your-tenant-id>" \
  -H "ApiKey: <your-api-key>"
```

Response — `DeviceTokenDto[]`:

```ts
[
  {
    token: string          // FCM token value
    externalUserId: string // Your user identifier
    platform: string       // 'android' | 'ios'
    createdAt: string      // ISO 8601 UTC timestamp
  },
  // ...
]
```

### Delete all tokens for a user

Useful on user account deletion.

```bash
curl -X DELETE https://api.queuefly.com/api/v1/device-tokens/by-user \
  -H "Content-Type: application/json" \
  -H "ClientId: <your-client-id>" \
  -H "TenantId: <your-tenant-id>" \
  -H "ApiKey: <your-api-key>" \
  -d '{ "externalUserId": "user-123" }'
```

### Token usage & billing stats

`GET /api/v1/device-tokens/usage` — active token count and the billing-period peak for the current calendar month.

```bash
curl -X GET https://api.queuefly.com/api/v1/device-tokens/usage \
  -H "ClientId: <your-client-id>" \
  -H "TenantId: <your-tenant-id>" \
  -H "ApiKey: <your-api-key>"
```

Response — `DeviceTokenUsageDto`:

```ts
{
  activeCount: number        // Current number of registered tokens
  billingPeriodPeak: number  // Highest active count this billing month
  billingPeriodStart: string // ISO 8601 UTC — start of billing period
  billingPeriodEnd: string   // ISO 8601 UTC — end of billing period
}
```

---

## Push Notifications

Send push notifications to a specific device token or to all devices belonging to a user.

> **Every call returns an id.** The send, edit, badge and silent endpoints respond with the journal `id` of the notification that was queued — a `number` for device-token calls, or a `number[]` (one id per device) for the `/by-user` calls. Hold on to these ids: pass them to `POST /api/v1/push/sent` to check delivery status later.

> **Before you test: some fields need app-side code to actually show up.** QueueFly reliably delivers exactly what you send — but a few fields depend on the receiving app having matching native code:
> - `imageUrl` — on iOS this requires a Notification Service Extension to download and attach the image; APNs will not fetch it automatically. Android renders `imageUrl` out of the box via FCM.
> - `badge` — iOS applies the badge natively via APNs. On Cordova/Ionic/hybrid apps (and plain Android, which has no OS-level badge API), the app must read the value and set it itself (e.g. `cordova-plugin-badge`). QueueFly only delivers the number.
> - `sound` — the value is a filename reference; the actual sound file must already be bundled in the app build. Use `"default"` for the system sound. On Android, sounds are locked per `androidChannelId` at channel creation — reuse a new channel ID to change the sound.

| Method | Endpoint | Description |
| --- | --- | --- |
| POST | `/api/v1/push` | Send standard notification to a device token |
| POST | `/api/v1/push/edit` | Replace a displayed notification via collapseKey |
| POST | `/api/v1/push/badge` | Update the badge count on a specific device |
| POST | `/api/v1/push/silent` | Send a silent (data-only) notification to a device |
| POST | `/api/v1/push/by-user` | Send to all devices of a user (by externalUserId) |
| POST | `/api/v1/push/by-user/edit` | Replace a notification on all devices of a user |
| POST | `/api/v1/push/by-user/badge` | Update badge on all devices of a user |
| POST | `/api/v1/push/by-user/silent` | Send silent data payload to all devices of a user |
| POST | `/api/v1/push/sent` | Check whether a batch of standard push notifications has been sent |

### Send to a device token

`POST /api/v1/push`

```bash
curl -X POST https://api.queuefly.com/api/v1/push \
  -H "Content-Type: application/json" \
  -H "ClientId: <your-client-id>" \
  -H "TenantId: <your-tenant-id>" \
  -H "ApiKey: <your-api-key>" \
  -d '{
    "deviceToken": "fcm-device-token-here",
    "platform": "android",
    "subject": "Order shipped!",
    "body": "Your order #1234 is on its way.",
    "jsonData": { "orderId": "1234" },
    "imageUrl": "https://cdn.example.com/img.png"
  }'
```

### Send to a user (all devices)

`POST /api/v1/push/by-user`

```bash
curl -X POST https://api.queuefly.com/api/v1/push/by-user \
  -H "Content-Type: application/json" \
  -H "ClientId: <your-client-id>" \
  -H "TenantId: <your-tenant-id>" \
  -H "ApiKey: <your-api-key>" \
  -d '{
    "externalUserId": "user-123",
    "subject": "New message",
    "body": "You have a new message from Alice."
  }'
```

### Standard send payload

```ts
// POST /api/v1/push — by device token:
{
  deviceToken: string          // FCM token (required)
  platform: 'android' | 'ios'  // (required)
  subject?: string             // Notification title
  body?: string                // Notification body text
  jsonData?: string            // JSON string payload delivered to the app
  imageUrl?: string            // URL of notification image
  sound?: string               // iOS sound file; use "default" for system sound
  androidChannelId?: string    // Android notification channel ID
  collapseKey?: string         // Replace a pending notification with same key
  priority?: number            // 1 = Normal (default), 2 = High
}

// POST /api/v1/push/by-user — same fields, replace deviceToken + platform with:
{ externalUserId: string }     // (required)

// Response — 201 Created (by device token): number     — the journal id
// Response — 200 OK (by-user):              number[]   — one journal id per device
```

### Edit / replace a displayed notification

`POST /api/v1/push/edit` (or `POST /api/v1/push/by-user/edit`). The `collapseKey` must match the key used when the original notification was sent.

```bash
curl -X POST https://api.queuefly.com/api/v1/push/edit \
  -H "Content-Type: application/json" \
  -H "ClientId: <your-client-id>" \
  -H "TenantId: <your-tenant-id>" \
  -H "ApiKey: <your-api-key>" \
  -d '{
    "deviceToken": "fcm-device-token-here",
    "platform": "android",
    "collapseKey": "order-status-1234",
    "subject": "Order delivered!",
    "body": "Your order #1234 has been delivered."
  }'
```

Edit payload:

```ts
// POST /api/v1/push/edit — by device token:
{
  deviceToken: string          // FCM token (required)
  platform: 'android' | 'ios'  // (required)
  collapseKey: string          // Must match the original notification (required)
  subject?: string             // Updated notification title
  body?: string                // Updated notification body text
  jsonData?: string            // Updated JSON string payload
  imageUrl?: string            // Updated notification image URL
}

// POST /api/v1/push/by-user/edit — same fields, replace deviceToken + platform with:
{ externalUserId: string }     // (required)

// Response — 201 Created (by device token): number     — the journal id
// Response — 200 OK (by-user):              number[]   — one journal id per device
```

### Update badge count

`POST /api/v1/push/badge` — set `badge` to `0` to clear it. Use `POST /api/v1/push/by-user/badge` for all devices of a user.

```bash
curl -X POST https://api.queuefly.com/api/v1/push/badge \
  -H "Content-Type: application/json" \
  -H "ClientId: <your-client-id>" \
  -H "TenantId: <your-tenant-id>" \
  -H "ApiKey: <your-api-key>" \
  -d '{
    "deviceToken": "fcm-device-token-here",
    "platform": "android",
    "badge": 5
  }'
```

Badge payload:

```ts
// POST /api/v1/push/badge — by device token:
{
  deviceToken: string          // FCM token (required)
  platform: 'android' | 'ios'  // (required)
  badge: number                // Badge count (integer, required). Use 0 to clear.
}

// POST /api/v1/push/by-user/badge — replace deviceToken + platform with:
{ externalUserId: string }     // (required)
```

### Silent (data-only) notification

`POST /api/v1/push/silent` — deliver a JSON payload without showing any visible notification. Ideal for cache invalidation, config refresh, or background sync.

```bash
curl -X POST https://api.queuefly.com/api/v1/push/silent \
  -H "Content-Type: application/json" \
  -H "ClientId: <your-client-id>" \
  -H "TenantId: <your-tenant-id>" \
  -H "ApiKey: <your-api-key>" \
  -d '{
    "deviceToken": "fcm-device-token-here",
    "platform": "android",
    "jsonData": "{\"type\":\"sync\"}"
  }'
```

Silent payload:

```ts
// POST /api/v1/push/silent — by device token:
{
  deviceToken: string          // FCM token (required)
  platform: 'android' | 'ios'  // (required)
  jsonData: string             // JSON string payload delivered to the app (required)
}

// POST /api/v1/push/by-user/silent — replace deviceToken + platform with:
{ externalUserId: string }     // (required)
```

### Check whether pushes were sent

`POST /api/v1/push/sent` — check delivery status in batches of up to 2000, using the journal `ids` returned by the send endpoints.

```bash
curl -X POST https://api.queuefly.com/api/v1/push/sent \
  -H "Content-Type: application/json" \
  -H "ClientId: <your-client-id>" \
  -H "TenantId: <your-tenant-id>" \
  -H "ApiKey: <your-api-key>" \
  -d '{ "ids": [101, 102, 103] }'
```

```ts
// Request:
{
  ids: number[]   // Journal IDs to check, 1–2000 per request (required)
}

// Response — 200 OK:
{
  [journalId: string]: boolean   // true once that notification has been sent
}
```

> **Need to schedule a push for later?** Use `POST /api/v1/push/schedule` (by device token) or `POST /api/v1/push/by-user/schedule` (all user devices) to deliver at a future UTC timestamp. See the [Scheduling](#scheduling) section.

---

## Topics

Topics let you broadcast to groups of devices without maintaining a list yourself. Subscribe devices to topics, then send a single call to reach everyone.

| Method | Endpoint | Description |
| --- | --- | --- |
| POST | `/api/v1/topics/subscribe` | Subscribe a device token to one or more topics |
| POST | `/api/v1/topics/unsubscribe` | Unsubscribe a device token from topics |
| POST | `/api/v1/topics/by-user/subscribe` | Subscribe all devices of a user to topics |
| POST | `/api/v1/topics/by-user/unsubscribe` | Unsubscribe all devices of a user from topics |
| POST | `/api/v1/topics/notify` | Send a notification to all subscribers of a topic |

### Subscribe to topics

Topics are created automatically on first subscription.

```bash
curl -X POST https://api.queuefly.com/api/v1/topics/subscribe \
  -H "Content-Type: application/json" \
  -H "ClientId: <your-client-id>" \
  -H "TenantId: <your-tenant-id>" \
  -H "ApiKey: <your-api-key>" \
  -d '{
    "deviceToken": "fcm-device-token-here",
    "platform": "android",
    "topics": ["news", "promotions"]
  }'
```

### Unsubscribe a device from topics

Pass the same `token`, `platform`, and `topics` array used at subscribe time.

```bash
curl -X POST https://api.queuefly.com/api/v1/topics/unsubscribe \
  -H "Content-Type: application/json" \
  -H "ClientId: <your-client-id>" \
  -H "TenantId: <your-tenant-id>" \
  -H "ApiKey: <your-api-key>" \
  -d '{
    "deviceToken": "fcm-device-token-here",
    "platform": "android",
    "topics": ["news", "promotions"]
  }'
```

### Subscribe by user (all devices)

Returns `TopicManagementResult`.

```bash
curl -X POST https://api.queuefly.com/api/v1/topics/by-user/subscribe \
  -H "Content-Type: application/json" \
  -H "ClientId: <your-client-id>" \
  -H "TenantId: <your-tenant-id>" \
  -H "ApiKey: <your-api-key>" \
  -d '{
    "externalUserId": "user-123",
    "topics": ["news", "promotions"]
  }'
```

### Unsubscribe by user (all devices)

Returns `TopicManagementResult`.

```bash
curl -X POST https://api.queuefly.com/api/v1/topics/by-user/unsubscribe \
  -H "Content-Type: application/json" \
  -H "ClientId: <your-client-id>" \
  -H "TenantId: <your-tenant-id>" \
  -H "ApiKey: <your-api-key>" \
  -d '{
    "externalUserId": "user-123",
    "topics": ["news", "promotions"]
  }'
```

### Response — TopicManagementResult

Returned by the `by-user` subscribe and unsubscribe endpoints only.

```ts
{
  successCount: number    // Tokens successfully subscribed/unsubscribed
  failureCount: number    // Tokens that failed
  failedTopics: Array<{
    topic: string
    errors: Array<{
      token: string       // The FCM token that failed
      reason: string      // Error reason from FCM
    }>
  }>
}
```

### Notify a topic

Topics must already exist (created via subscribe).

```bash
curl -X POST https://api.queuefly.com/api/v1/topics/notify \
  -H "Content-Type: application/json" \
  -H "ClientId: <your-client-id>" \
  -H "TenantId: <your-tenant-id>" \
  -H "ApiKey: <your-api-key>" \
  -d '{
    "topic": "promotions",
    "subject": "Flash sale!",
    "body": "30% off everything for the next 2 hours."
  }'
```

Topic notification payload — response is `201 Created` with the journal `id` (a `number`) of the queued broadcast:

```ts
{
  topic: string              // Topic name to broadcast to (required)
  subject?: string           // Notification title
  body?: string              // Notification body text
  jsonData?: string          // JSON string payload delivered to the app
  imageUrl?: string          // URL of notification image
  sound?: string             // iOS sound file; use "default" for system sound
  androidChannelId?: string  // Android notification channel ID
  collapseKey?: string       // Replace a pending notification with same key
  priority?: number          // 1 = Normal (default), 2 = High
}

// Response — 201 Created: number   — the journal id
```

> **Need to schedule a topic broadcast?** Use `POST /api/v1/topics/notify/schedule` to deliver to topic subscribers at a future time, and `DELETE /api/v1/topics/notify/schedule/{externalRef}` to cancel it. See the [Scheduling](#scheduling) section.

---

## Scheduling

Schedule notifications for future delivery. Use `externalRef` to identify a scheduled notification so you can cancel it later.

| Method | Endpoint | Description |
| --- | --- | --- |
| POST | `/api/v1/push/schedule` | Schedule a push notification to a device token |
| POST | `/api/v1/push/by-user/schedule` | Schedule a push to all devices of a user |
| DELETE | `/api/v1/push/schedule/{externalRef}` | Cancel a scheduled push notification |
| POST | `/api/v1/topics/notify/schedule` | Schedule a topic broadcast |
| DELETE | `/api/v1/topics/notify/schedule/{externalRef}` | Cancel a scheduled topic broadcast |
| POST | `/api/v1/push/schedule/sent` | Check whether a batch of scheduled push notifications has been sent |

### Schedule to a device token

`scheduledTo` is an ISO 8601 UTC timestamp. `externalRef` must be unique per tenant — use it to cancel later.

```bash
curl -X POST https://api.queuefly.com/api/v1/push/schedule \
  -H "Content-Type: application/json" \
  -H "ClientId: <your-client-id>" \
  -H "TenantId: <your-tenant-id>" \
  -H "ApiKey: <your-api-key>" \
  -d '{
    "deviceToken": "fcm-device-token-here",
    "platform": "android",
    "externalRef": "promo-campaign-001",
    "scheduledTo": "2025-08-01T09:00:00Z",
    "subject": "Summer sale starts now!",
    "body": "Up to 50% off — today only."
  }'
```

```ts
// POST /api/v1/push/schedule payload:
{
  deviceToken: string          // FCM token (required)
  platform: 'android' | 'ios'  // (required)
  externalRef: string          // Unique ID per tenant, used to cancel (required)
  scheduledTo: string          // UTC ISO 8601 date-time (required)
  subject?: string             // Notification title
  body?: string                // Notification body text
  jsonData?: string            // JSON string payload delivered to the app
  imageUrl?: string            // URL of notification image
  sound?: string               // iOS sound file; use "default" for system sound
  androidChannelId?: string    // Android notification channel ID
  collapseKey?: string         // Replace a pending notification with same key
  priority?: number            // 1 = Normal (default), 2 = High
}

// Response — 201 Created: number   — the journal id
```

### Schedule to a user (all devices)

```bash
curl -X POST https://api.queuefly.com/api/v1/push/by-user/schedule \
  -H "Content-Type: application/json" \
  -H "ClientId: <your-client-id>" \
  -H "TenantId: <your-tenant-id>" \
  -H "ApiKey: <your-api-key>" \
  -d '{
    "externalUserId": "user-123",
    "externalRef": "promo-campaign-001",
    "scheduledTo": "2025-08-01T09:00:00Z",
    "subject": "Summer sale starts now!",
    "body": "Up to 50% off — today only."
  }'
```

```ts
// POST /api/v1/push/by-user/schedule payload:
{
  externalUserId: string       // (required)
  externalRef: string          // Unique ID per tenant, used to cancel (required)
  scheduledTo: string          // UTC ISO 8601 date-time (required)
  subject?: string             // Notification title
  body?: string                // Notification body text
  jsonData?: string            // JSON string payload delivered to the app
  imageUrl?: string            // URL of notification image
  sound?: string               // iOS sound file; use "default" for system sound
  androidChannelId?: string    // Android notification channel ID
  collapseKey?: string         // Replace a pending notification with same key
  priority?: number            // 1 = Normal (default), 2 = High
}

// Response — 200 OK: number[]   — one journal id per device
```

### Schedule a topic broadcast

```bash
curl -X POST https://api.queuefly.com/api/v1/topics/notify/schedule \
  -H "Content-Type: application/json" \
  -H "ClientId: <your-client-id>" \
  -H "TenantId: <your-tenant-id>" \
  -H "ApiKey: <your-api-key>" \
  -d '{
    "topic": "promotions",
    "externalRef": "black-friday-2025",
    "scheduledTo": "2025-11-28T09:00:00Z",
    "subject": "Black Friday is here!",
    "body": "Doorbuster deals start now."
  }'
```

```ts
// POST /api/v1/topics/notify/schedule payload:
{
  topic: string                // Topic name (required)
  externalRef: string          // Unique ID per tenant, used to cancel (required)
  scheduledTo: string          // UTC ISO 8601 date-time (required)
  subject?: string             // Notification title
  body?: string                // Notification body text
  jsonData?: string            // JSON string payload delivered to the app
  imageUrl?: string            // URL of notification image
  sound?: string               // iOS sound file; use "default" for system sound
  androidChannelId?: string    // Android notification channel ID
  collapseKey?: string         // Replace a pending notification with same key
  priority?: number            // 1 = Normal (default), 2 = High
}

// Response — 201 Created: number   — the journal id
```

### Cancel a scheduled device push

Returns `error` if the scheduled time is within 30 seconds or already dispatched.

```bash
curl -X DELETE https://api.queuefly.com/api/v1/push/schedule/promo-campaign-001 \
  -H "ClientId: <your-client-id>" \
  -H "TenantId: <your-tenant-id>" \
  -H "ApiKey: <your-api-key>"
```

### Cancel a scheduled topic broadcast

```bash
curl -X DELETE https://api.queuefly.com/api/v1/topics/notify/schedule/black-friday-2025 \
  -H "ClientId: <your-client-id>" \
  -H "TenantId: <your-tenant-id>" \
  -H "ApiKey: <your-api-key>"
```

### Check whether scheduled pushes were sent

`POST /api/v1/push/schedule/sent` — batches of up to 2000, using the journal `ids` returned when the notification was scheduled.

```bash
curl -X POST https://api.queuefly.com/api/v1/push/schedule/sent \
  -H "Content-Type: application/json" \
  -H "ClientId: <your-client-id>" \
  -H "TenantId: <your-tenant-id>" \
  -H "ApiKey: <your-api-key>" \
  -d '{ "ids": [201, 202, 203] }'
```

```ts
// Request:
{
  ids: number[]   // Journal IDs to check, 1–2000 per request (required)
}

// Response — 200 OK:
{
  [journalId: string]: boolean   // true once that scheduled notification has been sent
}
```
