PushHub JavaScript and TypeScript SDK
Release: 2.0.0-beta.1 source candidate
Runtime: Node.js 18+ or a modern fetch implementation
Security: client-safe installation credentials
The Phase Four client replaces embedded ph_live_ server keys with an app-bound publishable key and one rotatable credential per installation. The package currently builds and tests from source in this repository. It is not yet published to npm; use the release archive only after it appears in the verified PushHub downloads manifest.
Build from source
cd sdk/javascript
npm ci
npm test
npm run build
The build must produce dist/index.js, dist/index.cjs, dist/index.d.ts, and dist/index.d.cts. npm pack --dry-run verifies the public package contents.
Configure
Create a publishable key in Workspace → Apps → your app → Integrations. A publishable key begins with ph_pub_ and can be included in a client bundle. A privileged ph_live_ API key must remain on a trusted server.
import PushHubClient from 'pushhub-js'
const pushhub = new PushHubClient({
appId: 'your-app-uuid',
publishableKey: 'ph_pub_your_publishable_key',
storage: secureStorage,
})
storage implements asynchronous getItem, setItem, and removeItem. Mobile and game clients must use Keychain, Keystore-backed, or equivalent encrypted storage. The default in-memory adapter is intended only for server processes and tests.
Register an installation
Ask for notification permission in your own product flow, obtain the current FCM or APNs token, then register it:
const installation = await pushhub.registerInstallation({
deviceToken: providerToken,
platform: 'Android', // or 'iOS'
userId: currentUser?.id,
metadata: {
appVersion: '3.4.0',
language: 'en',
},
})
PushHub creates a five-minute, single-use challenge and exchanges it for an installation credential. The SDK stores the returned state through the configured storage adapter. Call registerInstallation again when the provider token rotates; PushHub rotates the installation credential without exposing a server key.
Identity and consent
await pushhub.identify('player-42', { level: 18 })
await pushhub.setConsent('Granted')
// On logout:
await pushhub.unlinkUser()
// When notification consent is withdrawn:
await pushhub.setConsent('Denied')
Denied consent deactivates the device for future targeting. Identification does not grant notification consent.
Track an event
await pushhub.trackEvent({
name: 'level_up',
properties: { level: '18', world: 'north-realm' },
idempotencyKey: 'level-up-player-42-18',
})
Event properties are string values. Use a stable idempotency key for the same logical action.
Track receipt and open
PushHub notifications sent to an SDK installation contain pushhub_receipt_token. Do not construct or replace it.
await pushhub.trackReceipt(message.data.pushhub_receipt_token, 'received')
await pushhub.trackReceipt(message.data.pushhub_receipt_token, 'opened')
await pushhub.trackReceipt(message.data.pushhub_receipt_token, 'action', 'claim_reward')
The signed token binds the notification to one installation. “Provider accepted,” “received,” and “opened” remain separate outcomes.
Errors and retry
Methods reject with PushHubError, containing statusCode, optional code, and a retryable convenience property. HTTP 429 and 5xx failures are classified as retryable. Authentication and validation failures require configuration or input correction.
Do not create an unbounded retry loop. Native SDKs add the persistent bounded queue in Phase 4B; JavaScript hosts should use their platform's durable job mechanism.
API summary
| Method | Purpose |
|---|---|
restore() |
Restore installation state from configured storage |
registerInstallation(options) |
Bootstrap or recover one provider-token installation |
updateInstallation(options) |
Refresh token, identity, attributes, consent, or SDK metadata |
identify(userId, metadata?) |
Associate the installation with a customer user ID |
unlinkUser() |
Remove that association on logout |
setConsent(status) |
Store Unknown, Granted, or Denied |
trackEvent(options) |
Submit an installation-bound event |
trackReceipt(token, event, actionId?) |
Report signed receipt/open/action evidence |
reset() |
Remove locally stored installation state |
See the hosted OpenAPI reference for the exact /sdk contract. Direct server-to-server event and campaign operations continue to use scoped ph_live_ API keys.