VoidhashDocs
DevelopersHelp centerGuidesAPI reference
Dashboard
Get started
Introduction
Quickstart
How Voidhash works
Glossary
Versions and releases
Concepts
Teams, projects and environments
API keys
Products and perks
Offers
Subscription lifecycle
People and identity
Events
Features
Paywalls in your app
Feature flags and A/B tests
Paywall components
Push notifications
Ad attribution in your app
Client SDKs
React Native
Swift
Kotlin
Web
Your backend
Node.js
PHP
Go
Rust
Other languages
Payment sources
Forward store notifications
Developer tools
CLI
MCP server
Query analytics with VoidQL

Push notifications

Send push notifications to people's iOS and Android devices through APNs and Firebase Cloud Messaging.

Voidhash sends push notifications to people's devices through Apple Push Notification service (APNs) and Firebase Cloud Messaging (FCM). You connect your push credentials, your app registers each device's push token, and you send from an automation, your backend, the CLI or an AI agent.

Early access

Push notifications are in early access. Ask us on Discord, or email support@voidhash.com on a paid plan, to turn them on for your team.

Studio has no screen for writing a one-off push. To message people from Studio, build an automation with a Send push notification step.

Connect a push service

Open Settings → Notifications and click Set up next to the service you use. A service delivers nothing until its credentials are saved and Enabled is on.

Apple Push Notification service delivers to iOS devices with a token-based auth key.

FieldWhat to enter
Team IDYour 10-character Apple Developer Team ID.
Key IDThe Key ID of your APNs auth key.
Bundle IDYour app's bundle identifier, such as com.example.app.
EnvironmentProduction or Sandbox: which APNs servers to use when a device doesn't say.
Private key (.p8)The contents of the .p8 file Apple gave you when you created the key.

Firebase Cloud Messaging delivers to Android devices, and to iOS apps that use Firebase Messaging.

FieldWhat to enter
Firebase project IDThe Firebase project that owns the app.
Service account JSONA service account key from the Firebase console, pasted whole.
Android priorityHigh or Normal.
Android TTLOptional. How long FCM keeps trying, such as 3600s.
APNs priority10 or 5, for iOS devices reached through FCM.

Saved keys are encrypted and never shown again. Leave a key field blank to keep the saved one. To stop sending through a service without losing its credentials, turn Enabled off. Delete it from the ⋮ menu.

From the CLI, voidhash push config set apns --team-id … --key-id … --key-file AuthKey.p8 --bundle-id … and voidhash push config set fcm --service-account-file … do the same. See CLI commands.

Register devices

A person receives a push only on devices your app has registered for them. Each registration ties one platform push token to the distinct ID the app sends.

The SDKs don't register push tokens

No Voidhash SDK registers a device for you, including when a paywall or flow asks for notification permission. Your app gets the push token from the platform and sends it to Voidhash itself. Register devices for push has the app code for Swift, Kotlin and React Native.

The app calls POST /api/v1/sdk/push-devices/register, a client API endpoint. It takes the publishable key and the same headers the SDKs send, and returns a pushDeviceTokenId that the app keeps:

  • When the platform rotates the token, the app calls POST /api/v1/sdk/push-devices/refresh.
  • When the person signs out, it calls POST /api/v1/sdk/push-devices/unregister before the SDK signs out.

A device belongs to the person who registered it last. When APNs or FCM reports that a token no longer exists, Voidhash stops sending to it. Push device API lists every header, field and response.

Send a push

Sending reaches real people and can't be taken back. It needs the admin or owner role.

FromHow
StudioAn automation with a Send push notification step.
HTTP APIPOST /api/v1/projects/{projectId}/push-notifications with a secret key.
Server librariesnotifications.send in PHP, Go and Rust; pushNotifications.create in Node.js.
CLIvoidhash push send --to user_123 --title "…" --body "…". Shows who it reaches and asks first.
MCPsend_push_notification. The agent shows you the recipients and sends only after you agree.
curl https://api.voidhash.com/api/v1/projects/$VOIDHASH_PROJECT_ID/push-notifications \
  -H "Authorization: Bearer vh_sk_..." \
  -H "Idempotency-Key: trial-ending-user_123-2026-11-05" \
  -H "content-type: application/json" \
  -d '{
    "distinctIds": ["user_123"],
    "title": "Your trial ends tomorrow",
    "body": "Keep premium for $4.99 a month.",
    "data": { "screen": "paywall" }
  }'

The API answers 202 once the send is accepted and delivers in the background. It goes to every registered device of each person. Distinct IDs that match no person come back in unresolvedDistinctIds instead of failing the request. Send again with the same Idempotency-Key and you get the first send back, so retries never notify anyone twice.

FieldDescription
personIds, distinctIdsWho to notify. At least one person is required.
title, bodyRequired.
dataKey-value data your app receives with the notification.
sound, badgeThe sound to play and the app icon badge number.
prioritydefault or high.
ttlSecondsHow long the push service keeps trying to deliver.
channelIdThe Android notification channel.
collapseIdNotifications with the same collapse ID replace each other on a device.

Voidhash adds a voidhash_send_id key to data so the SDKs can report opens. A send fails with push_notifications_not_configured when no push service is enabled. Devices of a service that isn't enabled are skipped.

Follow delivery

Activity → Sent notifications lists every send with its status and counts. Open one to see the message, its idempotency key and each device's delivery. The CLI has voidhash push list and voidhash push deliveries <push>.

Send statusMeaning
pendingAccepted, not yet started.
in_progressDeliveries are still going.
succeededEvery device accepted it.
partial_failedSome devices accepted it and some failed.
failedNo device accepted it.
no_recipientsThe people named have no registered device on an enabled service.

Each device's delivery is pending, in_progress, succeeded, failed or exhausted. A delivery that fails with a temporary error is tried up to 5 times, waiting 10 seconds, 30 seconds, 5 minutes and 30 minutes between attempts, or as long as the push service asks. exhausted means it gave up after the last attempt.

"Succeeded" means APNs or FCM accepted the notification, not that the person saw it.

Measure opens

Call trackPushOpened when someone opens a notification, so Voidhash records a $push_opened event for that send. Each SDK page shows where to call it: Swift, Kotlin and React Native.

Studio's push report then shows delivery, opens and revenue after a push; see Push reports.

Next steps

  • Automations: send pushes when people do something, or on a schedule.
  • Register devices for push: app code for each platform.
  • People and identity: how distinct IDs map devices to people.
  • CLI commands: push send, push list and push config.

Something wrong or missing on this page? Tell us by email or on Discord.

PreviousInstalling and capabilitiesNextAd attribution in your app

On this page

Connect a push serviceRegister devicesSend a pushFollow deliveryMeasure opensNext steps