# HoneyNotify: product, platform, API, plans, infrastructure, and service reference

> Audience: AI assistants, search systems, integration engineers, technical evaluators, and customers.
>
> Last reviewed against this repository: 17 September 2026.
>
> Canonical product name: HoneyNotify.

This document is a consolidated, machine-readable description of HoneyNotify. It combines the public website, product documentation, REST API contract, SDK behaviour, migration tooling, security model, infrastructure description, and service-level commitments.

## Source-of-truth rules

- Use `https://docs.honeynotify.com` for the current public integration documentation.
- Use `https://api.honeynotify.com` as the production API base URL.
- Use `https://app.honeynotify.com` for account, application, API-key, provider-credential, usage, billing, and support management.
- The authenticated dashboard is the source of truth for a customer's current package, usage, limits, subscription state, and billing effect of a plan change.
- A `202 Accepted` response means HoneyNotify accepted asynchronous work. It does not prove that APNs, FCM, or Web Push accepted it, that a device displayed it, or that a person read it.
- A provider `accepted` result is not the same as confirmed device delivery or engagement.
- Client SDKs register devices, maintain identity and token state, parse payloads, and report events. Notification sends must originate from a trusted server using the REST API.
- Public client keys and secret server keys are not interchangeable.

## What HoneyNotify is

HoneyNotify is a multi-tenant push-notification platform for iOS, Android, and browsers. It gives product teams one server-side REST API for sending through:

- Apple Push Notification service (APNs);
- Firebase Cloud Messaging (FCM); and
- Web Push.

HoneyNotify manages the broader notification lifecycle, including:

- device registration and token refresh;
- anonymous and identified users;
- aliases, tags, and custom user properties;
- direct targeting and reusable audience segments;
- notification templates and personalisation;
- immediate, scheduled, local-time, and automated sends;
- per-recipient delivery attempts and provider results;
- received, confirmed-delivered, opened, clicked, dismissed, custom, and outcome events;
- account-wide and app-level analytics, AI-assisted performance analysis, and opt-in Daily Digest emails;
- webhooks, exports, and journeys;
- supported-provider device migration;
- app-level pacing, quiet hours, frequency caps, intelligent delivery, and identity verification;
- team, API-key, billing, audit, retention, privacy, and operational controls.

The public positioning is “Push notifications. Sweetly simple.” HoneyNotify is intended to provide a clean boundary in which clients register and trusted servers send.

## Supported platforms and SDKs

| Platform | Delivery provider | Client integration |
|---|---|---|
| iOS 15+ | APNs | Swift package |
| Android API 26+ | FCM | Kotlin library with Firebase Messaging helpers |
| iOS 15+ and Android API 26+ | APNs or FCM | Flutter package with Firebase Messaging helpers |
| iOS 15+ and Android API 26+ | APNs or FCM | React Native package with React Native Firebase helpers |
| Modern browsers | Web Push | JavaScript ES module and service worker |
| WordPress 6.0+ | Web Push | Ready-made HoneyNotify plugin ZIP with settings, root service worker, and subscribe shortcode |
| Magento 2 | Web Push | Ready-made HoneyNotify module ZIP with Stores configuration and storefront opt-in |
| Drupal 10/11 | Web Push | Ready-made HoneyNotify module ZIP with configuration, block, and optional automatic opt-in |
| Joomla 4/5 | Web Push | Ready-made HoneyNotify system plugin ZIP with native plugin configuration and site opt-in |

Official SDK source is published through these HoneyNotify repositories:

- Web: `https://github.com/HoneyNotify/web-sdk`
- iOS: `https://github.com/HoneyNotify/ios-SDK`
- Android: `https://github.com/HoneyNotify/android-SDK`
- Flutter: `https://github.com/HoneyNotify/flutter-sdk`
- React Native: `https://github.com/HoneyNotify/react-native-sdk`
- iOS and Android WebView demo applications: `https://github.com/HoneyNotify/demo-apps`

The Flutter package manifest is named `honeynotify`, requires Dart 3.3+ and Flutter 3.19+, and is prepared for pub.dev publication. The React Native package manifest is named `@honeynotify/react-native`, requires React Native 0.75+ with React Native Firebase 21+, and is prepared for public npm publication. Both are currently versioned `0.1.0` under the MIT licence. The GitHub repositories remain the safe canonical links unless registry publication has been independently verified.

Ready-made CMS integrations are listed at `https://honeynotify.com/cms-plugins.php`. Installable ZIPs are available at `/downloads/honeynotify-wordpress.zip`, `/downloads/honeynotify-magento.zip`, `/downloads/honeynotify-drupal.zip`, and `/downloads/honeynotify-joomla.zip`. They use only a restricted `ps_public_` client key and the Web channel's VAPID public key. They do not accept a secret notification-send key or VAPID private key.

The WordPress package supports an automatic opt-in button or the `[honeynotify_subscribe]` shortcode. Magento supplies Stores configuration and a storefront opt-in. Drupal supplies configuration, an optional automatic opt-in, and a reusable block. Joomla supplies a system plugin with native plugin settings. All four bundle the Web SDK, expose a root-scoped service-worker route, require HTTPS, and wait for a visitor action before requesting browser permission. A typical non-CMS website can use the dashboard's one-click generator to create a restricted public key and paste-ready snippet that imports `https://app.honeynotify.com/cdn/honeynotify-v1.js`. The site must still serve `honeynotify-sw.js` from its own root because browsers do not allow a cross-origin service worker to control the site. Both files remain available to download for fully self-hosted installations.

The Flutter and React Native SDKs obtain the APNs token on iOS and the FCM registration token on Android. They handle HoneyNotify registration, stored device identity, provider-token refresh, identify/logout operations, payload parsing, and lifecycle/custom events. The customer application remains responsible for native Firebase configuration, platform capabilities and permissions, notification-channel or foreground presentation, background-handler registration, deep-link routing, and avoiding duplicate event submission.

The SDKs are deliberately focused. They do not expose notification-send authority. Their common lifecycle is:

1. Ask for operating-system or browser notification permission in context.
2. Obtain an APNs token, FCM token, or browser Push subscription.
3. Register it with HoneyNotify using a restricted public client key.
4. Store the returned HoneyNotify `device_id`.
5. Identify the device after login, optionally using a backend-issued identity token.
6. Refresh registration when tokens or audience attributes change.
7. Parse HoneyNotify payload fields and report relevant lifecycle events.
8. Disable the HoneyNotify device on logout or unsubscribe when it should stop receiving notifications.

## High-level delivery flow

1. A customer's app or website obtains push permission and a provider destination.
2. The client SDK registers the destination through `POST /v1/devices/register` using a `ps_public_` key.
3. The customer's backend creates a notification through `POST /v1/notifications` using a scoped secret server key and an `Idempotency-Key`.
4. HoneyNotify validates the request and snapshots the content and audience definition.
5. Immediate work enters one of four Redis Stream priority queues: `transactional`, `high`, `normal`, or `bulk`. Future work remains scheduled until due.
6. Workers expand the audience into idempotent recipient batches and deliver through APNs, FCM, or Web Push.
7. HoneyNotify records provider attempts, acceptance, failure, invalid-token state, and usage. Permanently invalid destinations are disabled.
8. SDK events, notification inspection, analytics, signed webhooks, and exports show what happened after the send.

## Public product capabilities

### Audience and identity

- Register iOS, Android, and Web Push destinations.
- A repeated registration is an upsert: it refreshes the same logical destination rather than deliberately creating a duplicate.
- Associate devices with a customer-controlled `external_user_id`.
- Attach aliases, tags, locale, timezone, and custom properties.
- Use optional ES256 identity verification to prevent an untrusted client from impersonating another external user.
- Disable a device at logout or unsubscribe.
- Delete a user as a destructive privacy action; this disables all of that user's devices.
- Provider tokens and browser subscription secrets are never returned by normal API reads.

### Targeting

Every notification uses one primary target:

| Target type | Required target fields | Meaning |
|---|---|---|
| `device` | `device_id` | One HoneyNotify device UUID |
| `user` | `external_user_id` | Every enabled device associated with one user |
| `tag` | `key`, `value` | Devices whose associated user has the exact tag value |
| `segment` | `segment_id` | Devices matching a saved segment snapshot |
| `all` | none | Every enabled, valid device in the app |

Every target may also include `platform`. Use `both` for iOS and Android, `ios` for iOS only, or `android` for Android only. The dashboard defaults to `both`. API requests that omit `target.platform` retain all-platform delivery, including Web Push.

Notifications may also reference up to 50 `included_segment_ids` and 50 `excluded_segment_ids` for compound audience rules.

Segments can filter by platform, locale, timezone, app version, enabled state, creation/last-seen time, user tags, standard events, and named custom events. Groups use nested `all` or `any` conditions. The maximum nesting depth is five and the maximum number of leaf conditions is 50. Segment versions are immutable, and the selected definition is snapshotted into the notification.

### Content and delivery controls

- Title and body.
- Internal campaign name.
- Image URL and click/deep-link URL.
- Custom JSON data.
- Locale-specific variants.
- Templates with recipient and send-time placeholders.
- Sound, badge, category/channel hint, and up to three interactive actions.
- Provider/platform overrides.
- Queue priority.
- Absolute scheduling with `scheduled_at`.
- Dashboard scheduling in the user's browser timezone, converted to an absolute UTC time before storage.
- Recipient-local wall-clock delivery with `local_delivery_time`.
- Time to live (`ttl`) from zero to 2,419,200 seconds, or 28 days.
- Collapse identifier, up to 64 characters.
- Background-content flag.
- Optional frequency-cap override.
- Cancellation while work remains `draft`, `queued`, or `processing`. Cancellation cannot recall a push already accepted by a provider.

The dashboard can use either a public HTTPS image URL or a direct image upload. An upload takes precedence over a typed URL, must be no larger than 1 MiB, and is stored under a random non-guessable name. Valid GIF, JPEG, and PNG files retain their format; another decodable image format is converted to PNG. The resulting immutable HTTPS media URL is snapshotted into the notification. An uploaded image is a portal convenience; API callers continue to supply `image_url`.

Paid-plan dashboard users can select one of three delivery modes:

| Mode | Behaviour | Limits |
|---|---|---|
| Immediate | Use normal worker batching without intentional pacing | Default for every plan |
| Recipient delay | Release one recipient at a time with a fixed gap | 1 to 3,600 seconds between recipients |
| Batch window | Split the audience into batches and spread their start times across a window | 1 to 10,000 recipients per batch; 0.25 to 168 hours |

These Intelligent Delivery modes pace queue release; they do not guarantee the exact time at which a provider or device will receive or display a notification. App quiet hours and recipient-local delivery rules still apply.

Notification `priority` controls HoneyNotify queue processing. Notification `interruption_level` controls presentation hints at the destination. They are independent.

| Interruption level | iOS | Android | Web Push |
|---|---|---|---|
| `passive` | Quiet presentation without sound | Low-importance HoneyNotify channel | Low urgency |
| `active` | Normal presentation | Default-importance HoneyNotify channel | Normal behaviour |
| `time_sensitive` | Time-sensitive presentation | High-importance HoneyNotify channel | High urgency |
| `critical` | Critical Alert request | Strongest generally permitted HoneyNotify urgent channel | High urgency |

Apple must approve the host app's Critical Alerts entitlement and the user must grant critical-alert permission. HoneyNotify cannot grant either. Android users remain in control of channel importance, sound, and Do Not Disturb; HoneyNotify does not request full-screen-intent or DND-policy access.

### Templates

Templates are reusable, versioned notification content. They can be `draft`, `published`, or `archived`. A new template requires a name plus non-empty `content.title` and `content.body`.

Supported placeholder sources include:

- `{{ external_user_id }}`
- `{{ tags.plan }}`
- `{{ properties.first_name }}`
- `{{ template.order_id }}` from send-time `template_data`

Fields supplied directly on a notification override corresponding template content. HoneyNotify snapshots resolved content into the notification, so later template edits do not rewrite historical sends. Archived templates cannot be used for new notifications.

### Journeys

Journeys are versioned automation graphs triggered by an active segment or a named custom event. Supported node types are:

- `wait`;
- `wait_until`;
- `branch`;
- weighted `split`;
- `push`;
- `webhook`;
- `goal`; and
- `exit`.

A journey contains between one and 100 nodes. A wait can last from one second to 31 days. Journey states include `draft`, `active`, `paused`, and `archived`. Existing executions remain on the immutable version with which they started. Archiving cancels active or waiting executions but retains history.

### Events and analytics

Accepted event types are:

- `received`;
- `confirmed_delivered`;
- `opened`;
- `clicked`;
- `dismissed`;
- `custom`; and
- `outcome`.

`custom` and `outcome` require an `event_name`. Notification and device UUIDs are optional but, when supplied, must belong to the authenticated app. The event endpoint is not idempotent. Client applications should avoid duplicate callbacks and should deduplicate financially significant outcomes in their own business logic.

Outcome events can include a numeric `metadata.value`. HoneyNotify aggregates values but does not convert currencies. An outcome associated with a notification is `direct`; one without a notification is `unattributed`.

Aggregate analytics supports reporting windows from one to 365 days and groups event totals by date, event type, and platform, plus outcome totals by name and attribution. The portal provides account-wide totals across every app, an app filter, daily and platform breakdowns, campaign results, active-device counts, and an Apps view showing mobile MAU, Web Push subscribers, total records, subscribed records, configured providers, and recent activity.

### AI analysis and Daily Digest

When the operator has configured an OpenAI API key and model, an authenticated user can generate two kinds of on-demand analysis:

- a 30-day workspace overview compared with the preceding 30-day period, using aggregated campaign, delivery, engagement, and audience-growth facts; and
- a campaign analysis that reviews the title and body alongside that campaign's measured results and 30-day workspace context.

The generated result has a fixed structure: headline, summary, what is working, improvements, trends, and growth opportunities. HoneyNotify instructs the model to use only supplied facts, treat message content as untrusted data, distinguish low-volume observations, and avoid inventing causes or benchmarks. The OpenAI Responses request sets `store: false`, uses a hashed organisation safety identifier, and requests strict structured JSON. The metrics snapshot, result, model name, generating user where applicable, and timestamps are retained in HoneyNotify. AI observations can be inaccurate and should be checked against the underlying metrics.

Daily Digest is an opt-in preference for each owner or sub-user, not one workspace-wide switch. At the configured UTC digest hour, HoneyNotify prepares one previous-day analysis per organisation, compares it with the day before, and may assess up to 25 messages sent that day. Each subscribed user receives the same organisation analysis by email, with per-user/date delivery state preventing a successfully sent digest from being sent twice. The digest covers sends, provider acceptance, failures, opens, clicks, new devices, message observations, trends, and growth. Campaign titles and bodies plus aggregated performance facts are sent for digest analysis; email addresses and device identifiers are not included in the model input.

### Webhooks

Webhook destinations must use HTTPS and pass destination-safety checks intended to prevent private-network and server-side request-forgery targets. Each endpoint subscribes to between one and 50 unique event names; `*` selects all supported events.

HoneyNotify signs deliveries with:

- `X-HoneyNotify-Timestamp`; and
- `X-HoneyNotify-Signature: v1=<hex HMAC-SHA256>`.

The signed bytes are:

```text
timestamp + "." + raw_request_body
```

The signing secret is returned only on creation or explicit rotation. Receivers should verify the raw body, enforce timestamp freshness, deduplicate deliveries, enqueue business work, and return a `2xx` response quickly. Failed webhook deliveries are retried and become terminal after five attempts.

### Exports and app settings

Asynchronous CSV export types are `audience`, `messages`, `recipients`, `events`, and `audit`. The export list returns the newest 100 jobs. Completed files expire after 24 hours.

App settings exposed through the API include:

- default locale;
- ES256 identity public key and verification requirement;
- frequency-cap count and window;
- delivery throttle per minute; and
- local quiet-hours start and end.

Frequency-cap count and window must be configured or cleared together. Accurate local-time delivery and quiet hours depend on current device or user timezones.

## Plans and packages

### Public plans

| Feature | Free | Unlimited |
|---|---:|---:|
| Price | £0 per month | £24.99 per month |
| Intended use | Testing, side projects, early launches | Production teams |
| Monthly active devices | 2,500 | Unlimited |
| Push notifications | Unlimited, subject to service rules and upstream limits | Unlimited, subject to service rules and upstream limits |
| Apps | 4 | Unlimited |
| Platforms | iOS, Android, Web Push | iOS, Android, Web Push |
| Segments, templates, journeys, analytics | Included | Included |
| AI analysis and per-user Daily Digest | Included when the service is configured | Included when the service is configured |
| Intelligent Delivery pacing | Immediate delivery only | Recipient-delay and batch-window modes included |
| Webhooks and exports | Included | Included |
| Team members | 2 | Unlimited |
| Initial ticket response target | 48 hours | 2 hours, 24/7/365 |
| Card required to start | No | Paid subscription |
| SLA service credits | No public credit commitment stated for free service | Eligible after qualifying HoneyNotify-controlled downtime |

A monthly active device is a unique enabled device active for the app during the billing period. Registered iOS, Android, and Web Push destinations count toward device limits. The dashboard shows the customer's current plan usage and is authoritative for account-specific entitlements.

“Unlimited” does not mean ungoverned. Usage remains subject to the Terms of Service, Acceptable Use Policy, technical safeguards, abuse controls, rate limits, and upstream provider constraints. Customers must not evade package limits or use HoneyNotify for unsolicited, deceptive, malicious, unlawful, or abusive messaging.

## REST API fundamentals

### Base URL and transport

```text
https://api.honeynotify.com/v1
```

- Use HTTPS.
- Send `Authorization: Bearer <key>` on every `/v1` request.
- JSON write requests require `Content-Type: application/json`.
- Request bodies must be JSON objects, not arrays or scalars.
- The configured maximum body size is commonly 1 MiB.
- API resource identifiers are UUIDs and should be treated as opaque strings.
- Timestamps use ISO 8601 with seconds and `Z` or a numeric offset.
- Every response includes an `X-Request-ID` UUID. A caller may supply its own valid UUID; an invalid value is replaced.
- Page-number pagination starts at page 1, defaults to 50 results, and clamps `limit` between 1 and 250.
- API-generated errors use a stable JSON envelope.
- `/v1` supports browser CORS and preflight requests. This enables the restricted public Web SDK registration and event calls; it does not make a secret server key safe to embed in browser code.

### API keys and scopes

Secret server keys begin with `ps_live_`. They belong only in trusted backend applications, jobs, reporting services, and secret managers.

Restricted public client keys begin with `ps_public_`. The service forces them to these routes only:

- `POST /v1/devices/register`;
- `DELETE /v1/devices/{device_id}`; and
- `POST /v1/events`.

Public client keys cannot list audiences, read analytics, administer apps, or send notifications.

| Scope | Permission |
|---|---|
| `devices:write` | Register, inspect, disable, and manage devices, users, and aliases |
| `notifications:write` | Create/cancel sends and manage segments, templates, webhooks, journeys, exports, and app delivery settings |
| `notifications:read` | Read sends, recipients, events, segments, templates, webhooks, journeys, analytics, and exports |
| `events:write` | Submit lifecycle, custom, and outcome events |
| `*` | All server-key operations; use only for tightly controlled administration |

Device and user reads require `devices:write`, not `notifications:read`.

### Complete API v1 route index

| Method | Path | Required scope | Normal success | Purpose |
|---|---|---|---|---|
| `POST` | `/v1/devices/register` | `devices:write` | `201` new or `200` refreshed | Register or refresh a destination |
| `GET` | `/v1/devices` | `devices:write` | `200` | Paginated device list; optional `platform` and `enabled` filters |
| `GET` | `/v1/devices/{device_id}` | `devices:write` | `200` | Device metadata and association |
| `DELETE` | `/v1/devices/{device_id}` | `devices:write` | `200` | Disable and invalidate a device |
| `GET` | `/v1/users/{external_user_id}` | `devices:write` | `200` | User profile, aliases, and devices |
| `PATCH` | `/v1/users/{external_user_id}` | `devices:write` | `200` | Replace supplied locale, timezone, tags, or properties |
| `DELETE` | `/v1/users/{external_user_id}` | `devices:write` | `200` | Delete user and disable associated devices |
| `POST` | `/v1/users/{external_user_id}/aliases` | `devices:write` | `201` | Add or move an alias |
| `DELETE` | `/v1/users/{external_user_id}/aliases` | `devices:write` | `200` | Delete exact alias label/value |
| `GET` | `/v1/segments` | `notifications:read` | `200` | List saved segments |
| `POST` | `/v1/segments` | `notifications:write` | `201` | Create a segment |
| `POST` | `/v1/segments/estimate` | `notifications:write` | `200` | Estimate saved or inline filter audience |
| `GET` | `/v1/segments/{segment_id}` | `notifications:read` | `200` | Read a segment |
| `PATCH` | `/v1/segments/{segment_id}` | `notifications:write` | `200` | Create a new immutable segment version |
| `DELETE` | `/v1/segments/{segment_id}` | `notifications:write` | `200` | Archive a segment |
| `GET` | `/v1/templates` | `notifications:read` | `200` | List template metadata |
| `POST` | `/v1/templates` | `notifications:write` | `201` | Create a template |
| `GET` | `/v1/templates/{template_id}` | `notifications:read` | `200` | Read current template content |
| `PATCH` | `/v1/templates/{template_id}` | `notifications:write` | `200` | Create a new immutable template version |
| `DELETE` | `/v1/templates/{template_id}` | `notifications:write` | `200` | Archive a template |
| `GET` | `/v1/notifications` | `notifications:read` | `200` | Paginated send list |
| `POST` | `/v1/notifications` | `notifications:write` | `202` | Create an immediate or scheduled notification |
| `GET` | `/v1/notifications/{notification_id}` | `notifications:read` | `200` | Summary and aggregate counts |
| `POST` | `/v1/notifications/{notification_id}/cancel` | `notifications:write` | `200` | Stop work not yet sent to a provider |
| `GET` | `/v1/notifications/{notification_id}/recipients` | `notifications:read` | `200` | Paginated per-recipient results |
| `GET` | `/v1/notifications/{notification_id}/events` | `notifications:read` | `200` | Paginated notification event history |
| `POST` | `/v1/events` | `events:write` | `202` | Record a lifecycle, custom, or outcome event |
| `GET` | `/v1/analytics?days=30` | `notifications:read` | `200` | One-to-365-day aggregate metrics |
| `GET` | `/v1/webhooks` | `notifications:read` | `200` | List webhook destinations |
| `POST` | `/v1/webhooks` | `notifications:write` | `201` | Create destination and return its secret once |
| `PATCH` | `/v1/webhooks/{webhook_id}` | `notifications:write` | `200` | Update, enable/disable, or rotate secret |
| `DELETE` | `/v1/webhooks/{webhook_id}` | `notifications:write` | `200` | Permanently remove destination |
| `POST` | `/v1/webhooks/{webhook_id}/test` | `notifications:write` | `202` | Queue signed `webhook.test` delivery |
| `GET` | `/v1/journeys` | `notifications:read` | `200` | List journeys |
| `POST` | `/v1/journeys` | `notifications:write` | `201` | Create a journey |
| `GET` | `/v1/journeys/{journey_id}` | `notifications:read` | `200` | Read current journey definition |
| `PATCH` | `/v1/journeys/{journey_id}` | `notifications:write` | `200` | Create a new journey version/state |
| `DELETE` | `/v1/journeys/{journey_id}` | `notifications:write` | `200` | Archive and cancel active/waiting executions |
| `GET` | `/v1/journeys/{journey_id}/stats` | `notifications:read` | `200` | Execution counts by status |
| `GET` | `/v1/exports` | `notifications:read` | `200` | Newest 100 export jobs |
| `POST` | `/v1/exports` | `notifications:write` | `202` | Start an export |
| `GET` | `/v1/exports/{export_id}/download` | `notifications:read` | `200` CSV | Download completed, unexpired export |
| `PATCH` | `/v1/app/settings` | `notifications:write` | `200` | Update supplied app safeguards and defaults |

Unversioned service routes:

| Method | Path | Authentication | Purpose |
|---|---|---|---|
| `GET` | `/health` | None | Machine-readable API, database, Redis, queue, retry, and dead-letter health |
| `GET` | `/status` | None | Human-readable public service status |

### Device registration request

```bash
curl --request POST https://api.honeynotify.com/v1/devices/register \
  --header "Authorization: Bearer ps_public_replace_me" \
  --header "Content-Type: application/json" \
  --data '{
    "platform": "ios",
    "push_token": "apns-device-token",
    "external_user_id": "customer_123",
    "app_version": "4.8.1",
    "locale": "en-GB",
    "timezone": "Europe/London",
    "tags": {
      "plan": "pro",
      "marketing_opt_in": true
    }
  }'
```

Required registration fields:

- `platform`: `ios`, `android`, or `web`;
- `push_token`: provider token or JSON browser subscription, maximum 2,048 characters.

Optional registration fields include `external_user_id`, `identity_token`, `app_version`, `device_model`, `os_version`, `locale`, `timezone`, and `tags`. Tags must be a JSON object of scalar values. Values are stored as strings; `null` becomes an empty string.

Good new-registration response:

```http
HTTP/1.1 201 Created
Content-Type: application/json
X-Request-ID: c1a26486-1da3-4fcc-9273-623ad0d52f68
```

```json
{
  "device_id": "0e83059e-715d-45b8-911b-30d36519c863",
  "created": true
}
```

A refresh returns `200 OK` and `"created": false`.

Bad registration example:

```json
{
  "platform": "windows",
  "push_token": "example-token"
}
```

```http
HTTP/1.1 422 Unprocessable Content
```

```json
{
  "error": {
    "code": "validation_error",
    "message": "platform must be ios, android, or web",
    "details": {}
  },
  "request_id": "c1a26486-1da3-4fcc-9273-623ad0d52f68"
}
```

### Notification creation request

Every notification creation request requires:

- a non-empty `title`, maximum 250 characters, after template resolution;
- a non-empty `body`, maximum 4,096 characters, after template resolution;
- a valid `target` object; and
- an `Idempotency-Key` header, maximum 190 characters.

Use one durable idempotency key per logical business send. Reuse that same key only when retrying the same send.

```bash
curl --request POST https://api.honeynotify.com/v1/notifications \
  --header "Authorization: Bearer $HONEYNOTIFY_API_KEY" \
  --header "Content-Type: application/json" \
  --header "Idempotency-Key: order-ready-7815-v1" \
  --data '{
    "name": "Order 7815 ready",
    "title": "Your order is ready",
    "body": "Tap to see collection details.",
    "target": {
      "type": "user",
      "external_user_id": "customer_123"
    },
    "click_url": "myapp://orders/7815",
    "data": {
      "order_id": "7815"
    },
    "priority": "transactional",
    "interruption_level": "active"
  }'
```

Good immediate-send response:

```http
HTTP/1.1 202 Accepted
```

```json
{
  "notification_id": "b39ddcd7-2892-4c3d-8145-0ce3bc9e3ec4",
  "status": "queued",
  "interruption_level": "active"
}
```

A future scheduled notification is returned with `status: "draft"` until due.

Good idempotent replay response:

```json
{
  "notification_id": "b39ddcd7-2892-4c3d-8145-0ce3bc9e3ec4",
  "status": "queued",
  "interruption_level": "active",
  "idempotent_replay": true
}
```

Bad request without an idempotency key:

```http
HTTP/1.1 422 Unprocessable Content
```

```json
{
  "error": {
    "code": "validation_error",
    "message": "A valid Idempotency-Key header is required",
    "details": {}
  },
  "request_id": "c1a26486-1da3-4fcc-9273-623ad0d52f68"
}
```

Bad request with an invalid target:

```json
{
  "title": "Hello",
  "body": "World",
  "target": {"type": "email"}
}
```

```http
HTTP/1.1 422 Unprocessable Content
```

```json
{
  "error": {
    "code": "validation_error",
    "message": "target.type is invalid",
    "details": {}
  },
  "request_id": "c1a26486-1da3-4fcc-9273-623ad0d52f68"
}
```

### Event request

```bash
curl --request POST https://api.honeynotify.com/v1/events \
  --header "Authorization: Bearer ps_public_replace_me" \
  --header "Content-Type: application/json" \
  --data '{
    "event_type": "opened",
    "notification_id": "b39ddcd7-2892-4c3d-8145-0ce3bc9e3ec4",
    "device_id": "0e83059e-715d-45b8-911b-30d36519c863",
    "occurred_at": "2026-09-16T14:22:08Z",
    "metadata": {"action_id": "view_order"}
  }'
```

Good response:

```http
HTTP/1.1 202 Accepted
```

```json
{"accepted": true}
```

Bad custom event without a name:

```json
{"event_type": "custom"}
```

```json
{
  "error": {
    "code": "validation_error",
    "message": "event_name is required for custom events and outcomes",
    "details": {}
  },
  "request_id": "c1a26486-1da3-4fcc-9273-623ad0d52f68"
}
```

### Error envelope and response policy

All API errors have this shape:

```json
{
  "error": {
    "code": "machine_readable_code",
    "message": "Human-readable explanation",
    "details": {}
  },
  "request_id": "UUID"
}
```

Clients should branch on HTTP status and `error.code`, not exact message text. Log the request ID but never log a full API key or provider credential.

| Status | Code or meaning | Client action |
|---:|---|---|
| `200` | Request completed | Process response |
| `201` | Resource created | Store returned identifier or one-time secret |
| `202` | Work accepted | Poll or await events; do not assume downstream completion |
| `400` | `invalid_json` | Correct malformed/non-object JSON |
| `401` | `unauthorised` | Replace missing, invalid, revoked, or inactive key |
| `403` | `insufficient_scope` | Use an appropriately scoped key |
| `404` | `not_found` | Check route, resource ID, and app ownership |
| `402` | `package_limit_reached` | Review current package usage or upgrade |
| `413` | `payload_too_large` | Reduce request size |
| `415` | `unsupported_media_type` | Send `application/json` |
| `422` | `validation_error` | Correct fields, references, or resource state |
| `429` | `rate_limit_exceeded` | Back off with jitter and reduce concurrency |
| `500` | `database_error` or `internal_error` | Retry safely with bounded backoff and retain request ID |

Reads are safe to retry. For notification creation, a network error, `429`, or `5xx` retry must reuse the original `Idempotency-Key`. Do not retry `400`, `401`, `403`, `413`, `415`, or `422` unchanged.

Authenticated API calls are limited per organisation and app in minute buckets. The normal deployment default is 120 requests per minute. Responses include:

```http
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 87
```

## Identity verification

A public client key restricts capability but does not prove the end user's identity. HoneyNotify can require a short-lived ES256 JWT issued by the customer's trusted backend.

The token must include:

- `iss`: the HoneyNotify app public ID;
- `sub`: the customer's external user ID, or the same value in `identity.external_user_id`; and
- `exp`: an expiry no more than one hour in the future.

The ES256 private key stays on the customer's backend. HoneyNotify stores the corresponding public key in app settings. If the client also sends `external_user_id`, it must match the verified value. Anonymous registration remains possible when no user association is supplied.

## Migration from other push providers

HoneyNotify supports staged migration from 16 sources:

1. OneSignal
2. Airship
3. Amazon SNS
4. Azure Notification Hubs
5. Batch
6. Braze
7. CleverTap
8. Customer.io
9. Expo
10. Firebase
11. Iterable
12. Klaviyo
13. Kumulos
14. Pusher Beams
15. Pushwoosh
16. WonderPush

Only company owners can download a migration template or upload a migration. HoneyNotify provides a normalised CSV template and recognises common provider column aliases. Native OneSignal exports are recognised directly.

The migration lifecycle is:

1. Configure HoneyNotify with the same APNs app/environment or Firebase project as the existing provider.
2. Export currently subscribed iOS and Android destinations.
3. Upload the CSV through the app's Device migration area.
4. HoneyNotify validates the complete file and stages each source destination in a separate lookup; it does not immediately create an active device.
5. Release an app version containing the HoneyNotify SDK.
6. When normal SDK registration supplies the same platform and current token, HoneyNotify claims the staged record.
7. UUID source IDs carry forward as the HoneyNotify `device_id`. Other source-ID formats receive a stable, provider-scoped HoneyNotify UUID.

Customers do not add the old provider ID to their app. The normal SDK registration call performs the match. If there is no match, HoneyNotify safely registers a new device.

Imports are transactional. A validation or ownership conflict rolls back the file. Imports skip invalid or unsubscribed rows, record audit and import history, and store hashes of imported tokens rather than exposing raw imported tokens. The active-device allowance is enforced when a staged device later connects and becomes active.

Migration does not itself reset the user's operating-system notification permission. Automatic matching does require the exported token still to be current and HoneyNotify to use the matching provider application/environment. If the provider token rotates before first HoneyNotify registration, the installation receives a new HoneyNotify device ID.

## Security model

HoneyNotify security controls described by the product include:

- tenant- and app-scoped API queries;
- secret server keys stored hashed and shown only on creation;
- restricted `ps_public_` client keys;
- scoped permissions, rotation, and revocation;
- optional ES256 verified identity;
- individual team accounts, roles, TOTP MFA, and authentication throttling;
- encrypted APNs, FCM, and Web Push credentials at rest;
- validated APNs `.p8` and Firebase service-account uploads, plus dashboard generation of standards-compatible P-256 VAPID credentials;
- provider credentials kept outside public SDK configuration;
- one-time, short-lived, state-bound sign-in codes for seamless movement from the authenticated dashboard to the Support portal;
- HMAC-SHA256 signed webhooks;
- webhook destination validation;
- private database and Redis networking;
- separate service identities and least-privilege database/Redis access;
- administrative audit records;
- request IDs, body validation, size limits, and rate limits;
- package quotas and abuse review controls;
- expiring exports and configurable retention workflows;
- encrypted backups and restore-verification procedures.

Security boundaries customers must preserve:

- Never embed a `ps_live_` key in a mobile app, browser bundle, public repository, support ticket, URL, or analytics event.
- Never put APNs private keys, Firebase service-account credentials, webhook signing secrets, or identity-signing private keys in a client.
- Put only the VAPID public key and a `ps_public_` key in a website or CMS integration; the VAPID private key remains encrypted within HoneyNotify.
- Do not put confidential data in notification copy; lock-screen content can be visible without unlocking a device.
- Treat device tokens, external user IDs, tags, and event metadata as personal data.
- Verify webhook signatures before parsing and processing the payload.

Independent penetration testing, live provider credentials, and off-site backup accounts remain operator-owned external operational requirements; the presence of code or documentation alone is not proof that a specific production control has been independently certified.

## Infrastructure and operations

### Application architecture

The documented core deployment separates public web/API work from asynchronous delivery:

- PHP 8.2+ web application and REST API;
- MariaDB/MySQL as the authoritative store for organisations, apps, audience, sends, recipients, events, and delivery state;
- Redis Streams for asynchronous queue transport, retry scheduling, dead-letter work, and rate-limit counters;
- dedicated delivery workers for APNs, FCM, Web Push, webhooks, and journey/background processing;
- four notification priority queues;
- encrypted provider credentials shared only with authorised delivery processes;
- scheduled processing for future sends, retention, background automation, exports, Daily Digest generation, and backups.

Redis queue processing uses consumer acknowledgements, stale-job recovery, exponential backoff with jitter, and a dead-letter stream after the configured attempt limit. Delivery work is idempotently expanded into batches. Redis preserves in-flight transport state; MariaDB remains authoritative for campaign and recipient results.

The documented production topology uses:

- a WHM/cPanel PHP/API and MariaDB host;
- a dedicated Ubuntu `push-01` host for Redis and push workers;
- a private WireGuard link between service hosts;
- private-only MySQL and Redis listeners;
- Supervisor-managed production worker processes, one for each priority queue;
- immutable application and worker releases;
- Redis AOF persistence;
- daily encrypted database backups, checksums, an off-server copy, and periodic restore testing; and
- a scheduled monitor that checks service health, public hosts, redirects, security headers, installer closure, workers, private connectivity, and backup freshness.

Public host responsibilities are separated:

- `app.honeynotify.com`: account dashboard;
- `api.honeynotify.com`: REST API, health, provider/billing callbacks as configured;
- `docs.honeynotify.com`: generated public documentation;
- `support.honeynotify.com`: customer support, entered from an authenticated HoneyNotify session through a single-use sign-in exchange; and
- `blog.honeynotify.com` and `status.honeynotify.com`: public editorial and operational-status surfaces.

### Global server locations

The public infrastructure page lists six locations across three regions. Each listed location is described as connected on a 10 Gbps pipe.

| Code | Region | Country | City/location | Listed connectivity |
|---|---|---|---|---:|
| GB | Europe | United Kingdom | Erith | 10 Gbps |
| FR | Europe | France | Gravelines | 10 Gbps |
| DE | Europe | Germany | Limburg | 10 Gbps |
| PL | Europe | Poland | Warsaw | 10 Gbps |
| CA | North America, Canada East | Canada | Beauharnois | 10 Gbps |
| SG | Asia Pacific | Singapore | Singapore | 10 Gbps |

The public service description says user connections and push traffic use closest-available-location routing, with another available server taking traffic during a node failure or planned maintenance. Final delivery still depends on APNs, FCM, browser push services, recipient networks, devices, operating systems, and browsers.

### Monitoring and status

- Human-readable status: `https://api.honeynotify.com/status`
- Machine-readable health: `https://api.honeynotify.com/health`

The health endpoint is designed to expose overall health plus database and queue information, including queued/pending work, oldest-job age, retry state, and dead-letter state. It contains operational status, not customer notification content or secrets.

## Service Level Agreement

### Availability

HoneyNotify publicly commits to 99.95% availability for the HoneyNotify-controlled network. The stated resilience design includes automatic failover and moving traffic to additional nodes for planned maintenance.

Availability does not mean that every push is displayed. Upstream APNs, FCM, Web Push, customer-controlled systems, devices, operating systems, browsers, and recipient connectivity are outside HoneyNotify's network availability measurement.

### Support response targets

- Free service: initial ticket response within 48 hours.
- Paid/Unlimited service: initial ticket response within two hours, with ticket coverage 24 hours a day, seven days a week, 365 days a year.

These are initial-response targets, not resolution guarantees. Resolution time depends on the incident, affected systems, investigation, upstream providers, and required remediation.

### Service credits

If verified HoneyNotify-controlled downtime causes the paid network availability commitment to be missed, the affected paid customer is eligible for a service credit. The applicable credit is confirmed under the customer's subscription terms or Order Form.

The process is:

1. Verify the affected HoneyNotify service, impact window, and HoneyNotify-controlled cause.
2. Assess measured availability against the 99.95% commitment.
3. Document and apply any qualifying credit to the affected paid account.

### Incidents and maintenance

Material incidents are described as progressing through investigation, mitigation, recovery, and closure, with updates when meaningful information becomes available. Completed incident communication is intended to cover the timeline, impact, cause, remediation, restoration, and follow-up prevention work.

The public maintenance design states that HoneyNotify brings additional nodes online and shifts traffic away from affected infrastructure before planned work, with the objective of avoiding customer-visible downtime. During server failure, traffic is rerouted while the failed node is investigated and repaired.

## Privacy and data lifecycle

HoneyNotify is a business-facing platform. It ordinarily acts as:

- controller for its own account, authentication, billing, support, security, and service-operation data; and
- processor or service provider when handling a customer's recipients, tokens, identifiers, message content, delivery data, and engagement data under customer instructions.

Customers remain responsible for recipient notices, lawful basis, consent and permission handling, opt-outs, age restrictions, message purpose, data accuracy, and the custom data they submit.

HoneyNotify may process:

- account and team information;
- plan, subscription, and billing references;
- app and provider configuration;
- device tokens and browser subscriptions;
- HoneyNotify and imported provider device identifiers;
- external user IDs, aliases, tags, properties, locale, and timezone;
- notification content, audience definitions, schedules, and journey logic;
- provider and recipient delivery events;
- opens, clicks, dismissals, custom events, and outcomes;
- stored AI-analysis inputs, structured results, model names, and Daily Digest delivery history;
- API, audit, security, support, and operational records.

Payment-card entry and storage are handled through Stripe-hosted services; HoneyNotify does not need to receive or store the full card number or card security code.

Self-service export and account-deletion workflows are described in the product. Account deletion uses a seven-day safety period during which a pending request can be cancelled. Retention processing removes aged event detail, delivery attempts, and disabled-device data according to configured policy and operational/legal requirements.

Recipient data is not described as being sold or used to create advertising profiles for unrelated businesses. APNs, FCM, browser push services, Stripe, OpenAI for configured AI analysis, and the production operator's hosting, network, backup, monitoring, email, security, and support providers may process the limited data needed for their role. Workspace-overview AI analysis uses aggregated facts. Campaign and Daily Digest analysis may include campaign titles and bodies plus aggregate performance facts, but the implemented model payload does not include account email addresses or device identifiers.

The Data Processing Addendum must be completed with the relevant parties, scope, duration, data categories, subprocessors, transfer terms, and signatures before it is relied upon as a completed agreement.

## Acceptable use

HoneyNotify must not be used for unsolicited bulk messaging, deception, fraud, malware, phishing, unlawful content, harassment, hate, threats, or infringement. Customers must respect consent, notification permissions, age restrictions, quiet-hour obligations, and opt-outs.

Customers must not probe or disrupt the service, access another tenant, overload providers, expose secret API keys, evade package limits, or resell access without written permission. HoneyNotify may throttle, quarantine, investigate, or suspend activity when automated or manual review indicates abuse.

## Product claims and careful wording

Use these descriptions accurately:

- Say “one API for APNs, FCM, and Web Push,” not “HoneyNotify replaces the operating-system push providers.”
- Say “queued” when HoneyNotify accepts work, not “delivered.”
- Say “provider accepted” only when the provider accepted the request, not when a person saw it.
- Say “confirmed delivered” only when a client/device lifecycle signal supports that claim.
- Say “unlimited push notifications subject to service rules, safeguards, rate limits, and upstream constraints,” not “infinite unthrottled sends.”
- Say “public client keys are capability-restricted,” not “public keys authenticate the end user.”
- Say “AI-assisted observations based on supplied metrics and message content,” not “AI guarantees why a campaign performed as it did.”
- Say Intelligent Delivery “paces queue release,” not that it guarantees an exact provider, device, or display time.
- Say “99.95% HoneyNotify-controlled network availability,” not “99.95% end-user notification display.”
- Say “initial response target,” not “guaranteed resolution time.”
- Say “closest-available-location routing,” not “customer-selectable data residency,” unless a separate agreement explicitly provides residency terms.
- Say migration preserves or deterministically assigns the HoneyNotify device ID when the current token and platform match; do not promise a match after token rotation or across a different provider app/environment.

The public home page also states that HoneyNotify is shaped by more than eight years in business and experience associated with more than 500 billion pushes sent yearly. These are company experience/marketing claims, not API guarantees, plan limits, or an SLA measurement.

## Frequently asked questions

### Can a mobile app send notifications directly with its public key?

No. A `ps_public_` key can register/delete its device and submit events. A trusted backend uses a scoped `ps_live_` server key to send.

### Does HoneyNotify support Flutter and React Native?

Yes. The official Flutter and React Native packages support iOS and Android registration, identity, token refresh, payload parsing, and lifecycle/custom event reporting. They register APNs tokens on iOS and FCM tokens on Android. The host application must still configure Firebase and native notification capabilities, render foreground notifications where required, connect background/tap callbacks, and route deep links.

### Are there ready-made integrations for existing websites?

Yes. HoneyNotify provides installable Web Push ZIPs for WordPress 6.0+, Magento 2, Drupal 10/11, and Joomla 4/5, plus direct JavaScript and service-worker downloads for a typical or custom website. Each website receives only a restricted `ps_public_` client key and VAPID public key. Sending still occurs through the dashboard or a trusted backend.

### Can a dashboard user upload a notification image?

Yes. The dashboard accepts an image of up to 1 MiB, stores it under a random name, and uses its generated HTTPS URL in the notification snapshot. GIF, JPEG, and PNG are retained; another supported image type is converted to PNG. API clients provide their own public `image_url` instead.

### What does Intelligent Delivery do?

On a paid plan, dashboard users can insert a 1-to-3,600-second gap between individual recipients or spread batches of 1-to-10,000 recipients over 0.25 to 168 hours. This paces HoneyNotify queue release and is not an exact provider or device delivery-time guarantee.

### What data is used for AI analysis?

The workspace overview sends aggregated performance facts. Individual campaign and Daily Digest analysis can also send campaign titles and bodies. The implemented model payload excludes account email addresses and device identifiers, requests non-stored OpenAI Responses processing, and returns structured observations that users should verify against the source metrics.

### How does Daily Digest work?

Each team member chooses independently whether to receive it. At the configured daily hour, HoneyNotify compares the previous day with the day before, can review up to 25 messages, creates one organisation-level analysis, and emails it to each opted-in user while tracking delivery per user and date.

### Does `202 Accepted` mean delivered?

No. It means the request was accepted or queued. Inspect recipient results and SDK lifecycle events for later evidence.

### Can HoneyNotify target all of a user's devices?

Yes. Use a `user` target with the customer's stable `external_user_id`.

### Is notification creation idempotent?

Yes, when callers reuse the same `Idempotency-Key` for retries of the same logical send. The event endpoint is not idempotent.

### Are provider tokens returned in API reads or exports?

Normal device API responses do not return provider tokens. Provider credentials are write-only. Imported migration tokens are staged using hashes and are not exposed through normal dashboard/API reads.

### Can a customer schedule in each recipient's local time?

Yes, with `local_delivery_time`, provided current timezone data is available. `scheduled_at` instead represents one absolute ISO-8601 moment.

### Can HoneyNotify bypass iOS Focus or Android Do Not Disturb?

Not unconditionally. iOS Critical Alerts require Apple's entitlement and user permission. Android users control notification channels and DND; HoneyNotify does not request full-screen-intent or DND-policy access.

### Can customers migrate from OneSignal?

Yes. Native OneSignal exports are directly recognised, including Subscription ID/legacy Player ID handling. HoneyNotify also supports normalised imports from 15 other providers.

### Do migrated users need to grant permission again?

Changing orchestration provider does not itself reset the existing operating-system permission. The destination must still be current and belong to the same APNs or Firebase application for automatic token matching.

### Is HoneyNotify multi-tenant?

Yes. API keys belong to one app, and resources are resolved within the authenticated app and organisation context.

### Does HoneyNotify offer data export and deletion?

Yes. The API creates expiring CSV exports, and the account product includes privacy/export and scheduled-deletion workflows.

### How should a failed API call be debugged?

Capture the HTTP status, JSON error code, `X-Request-ID`, and approximate UTC time. Verify the host, path, key type, scope, content type, UUIDs, timestamp format, and app ownership. Preserve the idempotency key for safe send retries. Never send support a full secret key.

## Public links

- Website: `https://honeynotify.com`
- Dashboard: `https://app.honeynotify.com`
- Documentation: `https://docs.honeynotify.com`
- API: `https://api.honeynotify.com`
- Getting started: `https://docs.honeynotify.com/getting-started.html`
- API fundamentals: `https://docs.honeynotify.com/api.html`
- Complete API reference: `https://docs.honeynotify.com/api-reference.html`
- Authentication: `https://docs.honeynotify.com/authentication.html`
- SDK integration: `https://docs.honeynotify.com/sdk-integration.html`
- Web Push setup: `https://docs.honeynotify.com/web-push.html`
- CMS plugins: `https://honeynotify.com/cms-plugins.php`
- WordPress plugin: `https://honeynotify.com/wordpress.php`
- Devices and migration: `https://docs.honeynotify.com/devices-and-users.html`
- Errors and limits: `https://docs.honeynotify.com/errors-and-limits.html`
- Demo apps: `https://github.com/HoneyNotify/demo-apps`
- Flutter SDK source: `https://github.com/HoneyNotify/flutter-sdk`
- React Native SDK source: `https://github.com/HoneyNotify/react-native-sdk`
- Support: `https://support.honeynotify.com`
- Status: `https://status.honeynotify.com`
- Blog: `https://blog.honeynotify.com`

For account-specific billing, operational, contractual, privacy, or support questions, use the authenticated HoneyNotify dashboard and the applicable account, Order Form, invoice, subscription terms, or support ticket. Do not infer customer-specific entitlements from this general reference.
