Importing Push Tokens
PostHogOfficialPublished by PostHog
- Skill
- Workflow
- Automation and integrations
- MIT
- No issues found
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This workflow guides you through migrating mobile push device tokens from external platforms like OneSignal, Braze, Customer.io, Airship, or custom Firebase backends into PostHog. It ensures that users who installed your app prior to the switch continue to receive push notifications by correctly mapping existing device tokens and user IDs to PostHog distinct IDs. The process includes exporting data, validating mappings via dry runs, and batch importing tokens into the appropriate push channels.
What it does
- Maps external platform user IDs to PostHog distinct IDs.
- Identifies the correct push channel (APNs or Firebase) for device tokens.
- Provides a dry-run mechanism to validate data before final import.
- Handles batch processing of device tokens to ensure system stability.
- Filters out incompatible tokens such as Expo push tokens.
- Verifies successful migration by checking individual user push subscriptions.
When to use it
- When migrating push notification infrastructure to PostHog.
- When you need to backfill existing mobile push subscribers.
- When users who installed your app before the switch are not receiving notifications.
- When you have exported device tokens and user IDs from a legacy push provider.
Advantages
- Maintains continuity of push notification delivery for existing users.
- Prevents data ingestion errors through mandatory dry-run validation.
- Supports major third-party push providers and custom backends.
- Provides clear error handling for mismatched tokens or unknown users.
Usage examples
- “How do I migrate my push tokens from OneSignal to PostHog?”
- The agent will guide you through exporting your OneSignal CSV, filtering for valid push rows, mapping external IDs to PostHog distinct IDs, and using the integration tools to batch import the tokens.
- “I am getting token_mismatch errors during my push token import.”
- The agent will explain that the token format does not match the selected channel (APNs vs Firebase) and help you re-verify your source data or channel selection.
Install
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/posthog/importing-push-tokens/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip importing-push-tokens.zip -d .agents/skills
Documentation by PostHog
Importing push tokens from another platform
A push notification reaches a device only when PostHog knows that device's token and which person it belongs to. The mobile SDK registers new devices by itself. Devices that installed the app before the switch stay unknown until their token is imported.
The import pairs each device token with a distinct ID.
The distinct ID has to be the ID the app passes to identify, so the device lands on the person a workflow targets.
A pairing that is off by one prefix or uses another platform's internal ID imports fine but never receives a push. Getting the pairing right is most of the job.
The tools
integrations-list: find the push channel.kindisapnsfor iOS andfirebasefor Android (and for iOS apps that send through FCM).integrations-push-tokens-create: import up to 1000 rows per call, withdry_runto check without storing anything.
The same import is in the app under Workflows → Channels → Import devices, for a person who has a CSV.
Which token goes to which channel
| Channel kind | Token it takes | Looks like |
|---|---|---|
apns | The APNs device token, hex encoded | 64 hex characters, for example 9f2c…e1a0 |
firebase | The FCM registration token | Around 150 characters of letters, digits, -, _, often with a : |
Expo tokens (
ExponentPushToken[...]) can't be imported. They address Expo's own push service, not Apple or Google. Ask for the native APNs or FCM token instead: Expo apps can read it withgetDevicePushTokenAsync, so the app has to start sending it to PostHog first.An iOS app that sends through Firebase has FCM tokens, so those go to the
firebasechannel, notapns.A row with the wrong kind of token fails as
token_mismatch. The check rejects only shapes that cannot work, so passing it does not prove the token is live.
Workflow
Find the channel. Call
integrations-listand pick theapnsorfirebaseentry for the app. If there is none, the push channel has to be set up first.Export devices from the old platform. You need, for each device: its token, its platform, and the old platform's user ID. See references/exporting-devices.md.
Map the user ID to the PostHog distinct ID. Usually the old platform's external ID is the same value the app passes to PostHog's
identify. Confirm it with the user, because some apps prefix IDs or identify by email in one tool and by database ID in the other. Drop devices with no user ID: an anonymous device has no person to land on.Dry run a sample. Send 50 to 100 rows with
dry_run: true. Readfailed:Many
unknown_personrows mean the mapping is wrong. Stop and fix the mapping. Don't switch oncreate_missing_personsto make them pass, because that creates people nobody's app identifies.token_mismatchrows are on the wrong channel or are Expo tokens. Split iOS and Android rows, and drop Expo tokens.
Dry run everything, then import. Send the whole set with
dry_run: truein batches of 1000, then again without it. Keep to 60 calls per minute per project. On HTTP 429, wait a minute and send the same batch again.Handle what is left.
not_storedandperson_processing_disabledrows were not saved by ingestion. Sendnot_storedrows again later. Usecreate_missing_persons: trueonly forunknown_personrows the user confirms are real people who haven't opened the new app yet.Check one person. Look up one imported person and confirm they have a
$device_push_subscription_<app>:<hash>property. A test push from a workflow to that person is the real proof.
Importing the same row twice stores it once, so a batch can be sent again safely. Each stored device is one event, so a large import counts toward event usage.
Files
- ATTRIBUTION.mdAdded by AutoGineer861 B
- LICENSELicence file added by AutoGineer1.0 KiB
- SKILL.md4.6 KiB
- references/exporting-devices.md3.1 KiB
references/exporting-devices.md
Exporting devices from other platforms
Each platform stores the same three facts under different names: the device token, the platform, and the user ID your app gave it. APIs and export screens change, so check the platform's current docs before relying on a field name.
| Platform | Where the devices are | Token field | User ID field |
|---|---|---|---|
| OneSignal | POST /players/csv_export (CSV of subscriptions), or the dashboard CSV export | identifier (push rows only) | external_user_id, which you must request in extra_fields |
| Braze | POST /users/export/ids or a segment export | push_tokens[].token, with push_tokens[].platform and app | external_id |
| Customer.io | People → Export to CSV → Export other → All devices in this workspace | the device id | the person's id |
| Airship | GET /api/channels (paged), or GET /api/named_users | push_address | named_user_id |
| Firebase (own backend) | Your own database. FCM has no API that lists tokens | the token your app saved | the user ID saved next to it |
Notes per platform
OneSignal: email and SMS subscriptions come out in the same export. Keep the push rows only. Drop unsubscribed rows:
invalid_identifierist, ornotification_typesis negative. These rows can still carry a token, and an imported token can be sent to again. Also drop rows with a null token.device_typetells iOS from Android.Braze: a profile can hold several tokens for several apps. Keep the ones for this app and platform. An anonymous profile has no
external_idand cannot be mapped.Customer.io: devices are listed per person, with a platform. A person identified only by email in Customer.io needs that email mapped to the distinct ID the app identifies with.
Airship: a channel with a null
push_addressor nonamed_user_idcannot be imported.device_typeisiosorandroid.Firebase: tokens go stale when an app is reinstalled. Prefer tokens your backend saw recently. FCM tokens go to the
firebasechannel even for iOS devices.
Expo
Expo tokens (ExponentPushToken[...]) cannot be imported anywhere. They only work through Expo's push service. The app has to read the native token with getDevicePushTokenAsync and register it through the PostHog SDK, which happens as people open the updated app.
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Security
No issues found by our static scan· No issues found
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Published by
- PostHog
- Publisher
- PostHog (Official)
- Source
- PostHog/skills · skills/omnibus/importing-push-tokens · commit a5d30e7
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by PostHog.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download importing-push-tokens.zip