Plush API
Send customizable, encrypted push notifications to every linked Apple device or one device by name.
Base URLhttps://api.easypush.app
export PLUSH_API_TOKEN="push_..."
curl https://api.easypush.app/v1/devices \
-H "Authorization: Bearer $PLUSH_API_TOKEN"
Auth
The iOS app signs in with Apple and receives a family API token from the server. API calls use bearer auth.
POST
/v1/auth/apple
App-only exchange for a Sign in with Apple identity token.
{
"identityToken": "eyJhbGciOi..."
}
Devices
Devices register their APNs token and an encryption public key. Device names must be camelCase, kebab-case, or snake_case.
GET
/v1/devices
List enabled devices linked to the Apple account.
POST
/v1/devices
Register or refresh the current device.
{
"installationId": "device-local-uuid",
"name": "workPhone",
"platform": "iOS",
"pushToken": "apns-token",
"publicKey": "base64-public-key",
"publicKeyAlgorithm": "x25519-hkdf-sha256-aes-gcm",
"appVersion": "1.0",
"osVersion": "iOS 18"
}
PATCH
/v1/devices/:id
Rename or disable a device.
DELETE
/v1/devices/:id
Remove a device.
Pushes
Every push includes APNs-visible copy and encrypted payload envelopes keyed by target device ID or device name.
POST
/v1/pushes
Send to all devices, named devices, or explicit device IDs.
{
"target": { "kind": "all" },
"visibleAlert": {
"title": "Build passed",
"subtitle": "Plush",
"body": "main deployed successfully",
"sound": "default",
"threadId": "deploys",
"interruptionLevel": "active",
"relevanceScore": 0.8
},
"encryptedPayloads": {
"workPhone": {
"algorithm": "x25519-hkdf-sha256-aes-gcm",
"ciphertext": "...",
"nonce": "...",
"tag": "...",
"ephemeralPublicKey": "...",
"sentAt": "2026-06-12T00:00:00Z"
}
}
}
Target kindShape
all
{ "kind": "all" }deviceNames
{ "kind": "deviceNames", "names": ["workPhone"] }deviceIds
{ "kind": "deviceIds", "ids": ["..."] }Self-host
The server is a small Cloudflare Worker with D1. Custom deployments can set BILLING_MODE=custom to skip hosted subscription checks.
cd server
npm install
npx wrangler d1 create plush-api
npm run db:migrate:remote
npx wrangler secret put APNS_PRIVATE_KEY
npx wrangler deploy
Errors
Errors return JSON with a message and optional details.
StatusMeaning
401Missing or invalid bearer token.
402Hosted API access needs an active subscription.
422Missing encrypted payloads for a target device.
503APNs credentials are not configured.