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

# Operations

> OneSignal environment variables, the DLQ/replay runbook, health probe and the mobile integration checklist.

## Environment variables

| Variable                         | Required | Purpose                                  |
| -------------------------------- | -------- | ---------------------------------------- |
| `INTEGRATION_ONESIGNAL_API_KEY`  | Yes      | REST API key (`Key <api_key>` header).   |
| `INTEGRATION_ONESIGNAL_APP_ID`   | Yes      | OneSignal App ID.                        |
| `INTEGRATION_ONESIGNAL_BASE_URL` | No       | Defaults to `https://api.onesignal.com`. |

<Note>
  Without `API_KEY` / `APP_ID` the client degrades to log-only (does not throw)
  and the health probe reports the component as down.
</Note>

## Idempotency, retries and observability

* **Idempotency**: the consumer uses `queue_event_id` as
  `queue_event.source_event_id`; a duplicate redelivery is a no-op.
* **Retries**: exponential backoff on transient (5xx / network) errors.
  `NonRetryableNotificationError` becomes a logged skip with no retry.
* **Event types**: segmented by channel
  (`vercel_queue.notification.push|in_app|sms|live_activity`, `…unified`
  fallback).
* **Success context**: handlers return `context.oneSignalId` for correlation.

## Runbook — DLQ and replay

Failures that exhaust retries land in the shared DLQ infrastructure
(`queue_dead_letter` + `queue_replay_job`).

<Steps>
  <Step title="Detect">
    Query `queue_dead_letter` filtering by
    `event_type LIKE 'vercel_queue.notification.%'`.
  </Step>

  <Step title="Diagnose">
    Review `queue_event_attempt` (last error) and handler logs by
    `correlation` / `queue_event_id`.
  </Step>

  <Step title="Fix the root cause">
    OneSignal credentials, phone number or payload.
  </Step>

  <Step title="Reprocess">
    Enqueue a `queue_replay_job` for the affected `queue_event` rows.
  </Step>

  <Step title="Verify">
    The row moves to `processed` with `context.oneSignalId`.
  </Step>
</Steps>

<Note>
  Forcing an invalid API key in dev is the fastest way to observe the
  retry → DLQ → replay cycle end-to-end.
</Note>

## Health

A cheap active probe (`GET /apps/{app_id}`, without consuming quota) combined
with real traffic paints the `INTEGRATION_ONESIGNAL` heartbeat on the status
page.

## Mobile integration checklist

<Steps>
  <Step title="Initialize the SDK">
    Use the environment's OneSignal App ID.
  </Step>

  <Step title="Bind the user">
    On authentication call `OneSignal.login(auth.users.id)` to set the
    `external_id`.
  </Step>

  <Step title="Render in-app">
    Handle the data-only push of the `in_app` channel.
  </Step>

  <Step title="Register phone">
    Persist the user's E.164 phone for SMS.
  </Step>

  <Step title="Respect preferences">
    Read `GET /v3/notification/preference/me` and persist changes with
    `PUT/PATCH .../me`.
  </Step>
</Steps>
