Push Notifications

Send push notifications to your users' devices with a single server-to-server API call.

The notifications endpoint dispatches push notifications to devices registered through the device SDK. You target one or more persons, provide the message once, and Voidhash fans it out to every registered device — routing each token through the right provider (FCM or APNs) for you.

This is a privileged, server-to-server surface: it authenticates with a project secret key only. Publishable keys and user API keys are rejected, so clients can never push to arbitrary persons.

Feature flag required

Push notifications are gated by an internal feature flag. If the flag is not enabled for your organization, sends fail with 409 Api/PushSendNotEnabledError. Contact us to get it enabled for your project.

Devices become targetable once your app registers them via the SDK push-device endpoints — see push devices on the SDK page.

Send a notification

POST /api/v1/notifications/send

Authenticate with your project secret key:

curl https://api.voidhash.com/api/v1/notifications/send \
  -X POST \
  -H "x-secret-key: vh_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "distinctIds": ["user-123"],
    "title": "Your trial ends tomorrow",
    "body": "Upgrade now to keep your perks.",
    "data": { "screen": "paywall", "locationSlug": "onboarding" },
    "priority": "high",
    "idempotencyKey": "trial-reminder-user-123"
  }'

Request body

FieldTypeRequiredDescription
personIdsstring[]See belowTarget persons by canonical person ID
distinctIdsstring[]See belowTarget persons by distinct ID
titlestringYesNotification title (min length 1)
bodystringYesNotification body (min length 1)
dataobjectNoArbitrary key-value payload delivered to the app
soundstringNoSound to play on delivery
badgenumberNoBadge count to set on the app icon
priority"default" | "high"NoDelivery priority
ttlnumberNoTime-to-live in seconds
channelIdstringNoAndroid notification channel
collapseIdstringNoCollapse key — newer sends replace undelivered ones with the same ID
idempotencyKeystringNoCollapses retried sends into a single dispatch (per project)

Targeting

At least one of personIds or distinctIds must be non-empty — the server rejects requests that target nobody. You can mix both in one request.

Targeting is forgiving by design: a distinctId that does not map to a known person is recorded in the response's unresolvedDistinctIds, never treated as a fatal error. The rest of the send proceeds normally.

Retries are safe with an idempotency key

Pass an idempotencyKey when your caller might retry (queue workers, cron jobs, webhook handlers). Retried sends with the same key collapse into a single dispatch instead of notifying users twice.

Response

The send is enqueued asynchronously — the response confirms acceptance and returns a tracking ID plus up-front counts, not final delivery results.

{
  "pushNotificationSendId": "push_send_...",
  "deviceCount": 3,
  "status": "pending",
  "unresolvedDistinctIds": []
}
FieldTypeDescription
pushNotificationSendIdstringTracking ID for this send (push_send_*)
deviceCountnumberNumber of registered devices resolved for the targeted persons
statusstringSend status — see below
unresolvedDistinctIdsstring[]Distinct IDs that did not resolve to a known person

status is one of:

StatusMeaning
pendingAccepted and queued for dispatch
in_progressDispatch to providers is underway
succeededAll device dispatches succeeded
partial_failedSome device dispatches failed
failedAll device dispatches failed
no_recipientsThe targeted persons have no registered devices

A send that resolves zero devices returns immediately with status: "no_recipients" and deviceCount: 0 — check unresolvedDistinctIds to see whether your targeting was off or the persons simply have no devices registered.

Errors

Status_tagMeaning
400Api/PushDeviceValidationErrorInvalid request payload (e.g. no targets, empty title or body)
401Api/NotAuthenticatedErrorMissing or invalid credentials
403Api/ActionForbiddenErrorCredential is not a project secret key, or lacks permission
409Api/PushSendNotEnabledErrorPush is not enabled for this project — no enabled push provider, or the feature flag is off
500Api/PushSendServiceErrorDownstream dispatch infrastructure failed

See API Overview for the general error shape and status mapping.

Registering devices

This endpoint only delivers to devices your app has registered. Registration, token refresh, and unregistration happen on the device-facing SDK surface (POST /api/v1/sdk/push-devices/register, /refresh, /unregister), authenticated with a publishable key. See push devices on the SDK page for the full contract.