Getting Started
This guide walks you from zero to your first push notification in five steps.
Step 1: Register your app
Today, onboarding is done through the admin API (a self-serve dashboard is planned).
You receive:
- App ID — stable slug for the app (for example
my-cool-app) - API Key — secret Bearer token used only on your servers
- VAPID public key — available from
GET https://api.gesher.pro/vapid-keyaspublicKey(or pass it explicitly into the SDK)
Create an app with your admin key:
curl -X POST https://api.gesher.pro/admin/apps \
-H "Authorization: Bearer YOUR_ADMIN_KEY" \
-H "Content-Type: application/json" \
-d '{"id": "new-app", "name": "New App", "api_key": "YOUR_GENERATED_SECRET_AT_LEAST_32_CHARS"}'See API Reference — Admin for full create/list/update payloads.
Step 2: Install the client SDK
npm install @gesher/sdkimport { GesherClient } from '@gesher/sdk'
const gesher = new GesherClient({
subscribeEndpoint: '/api/push/subscribe', // your backend proxy
unsubscribeEndpoint: '/api/push/unsubscribe', // your backend proxy
vapidPublicKey: 'YOUR_VAPID_PUBLIC_KEY', // optional; omit to auto-fetch from Gesher
serviceWorkerPath: '/gesher-sw.js', // default
})
// Subscribe user
const result = await gesher.subscribe(userId)
if (!result.ok) console.error(result.error)
// Unsubscribe
await gesher.unsubscribe(userId)
// Check state
const state = await gesher.getState()
// → 'subscribed' | 'unsubscribed' | 'denied' | 'unsupported'The SDK POSTs JSON to your endpoints using camelCase (userId, subscription, endpoint). Your proxy must translate that into the shape Gesher expects (also camelCase — see POST /subscribe).
Step 3: Add the service worker
Copy the template from the package export @gesher/sdk/sw into your app’s static root (for example public/gesher-sw.js):
# Example: copy from node_modules into public/
cp node_modules/@gesher/sdk/gesher-sw.js public/gesher-sw.jsYou can instead maintain a custom worker. See the Service Worker guide for behavior, RTL, and deep linking.
Step 4: Create a backend proxy
Why: The Gesher API key must never reach the browser. The SDK only talks to URLs you control; those routes forward to https://api.gesher.pro with Authorization: Bearer <APP_API_KEY>.
Important: Gesher’s subscribe/unsubscribe APIs use camelCase JSON (userId, not user_id). The unsubscribe API on Gesher is DELETE /unsubscribe with a JSON body.
1. Cloudflare Pages Function (subscribe)
// functions/api/push/subscribe.ts
export const onRequestPost = async ({ request, env }) => {
// 1. Authenticate YOUR user (Supabase, session cookie, etc.)
const body = await request.json()
const { userId, subscription } = body
// 2. Forward to Gesher (use your app API key from env)
const res = await fetch('https://api.gesher.pro/subscribe', {
method: 'POST',
headers: {
Authorization: `Bearer ${env.GESHER_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
userId,
subscription,
tags: body.tags ?? [],
}),
})
return new Response(await res.text(), { status: res.status })
}2. Supabase Edge Function (Deno)
Deno.serve(async (req) => {
const { userId, subscription, tags } = await req.json()
const res = await fetch('https://api.gesher.pro/subscribe', {
method: 'POST',
headers: {
Authorization: `Bearer ${Deno.env.get('GESHER_API_KEY')}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ userId, subscription, tags: tags ?? [] }),
})
return new Response(await res.text(), { status: res.status })
})3. Node.js / Express
app.post('/api/push/subscribe', async (req, res) => {
const response = await fetch('https://api.gesher.pro/subscribe', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.GESHER_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
userId: req.body.userId,
subscription: req.body.subscription,
tags: req.body.tags ?? [],
}),
})
const text = await response.text()
res.status(response.status).send(text)
})For unsubscribe, forward the SDK’s POST body to Gesher with DELETE:
app.post('/api/push/unsubscribe', async (req, res) => {
const response = await fetch('https://api.gesher.pro/unsubscribe', {
method: 'DELETE',
headers: {
Authorization: `Bearer ${process.env.GESHER_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
userId: req.body.userId,
endpoint: req.body.endpoint,
}),
})
const text = await response.text()
res.status(response.status).send(text)
})Step 5: Send notifications
From your server:
# To a specific user (all their devices)
curl -X POST https://api.gesher.pro/notify/USER_ID \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "New message!", "body": "You got a reply", "url": "/messages/456"}'
# Broadcast to all subscribers
curl -X POST https://api.gesher.pro/notify \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "Maintenance tonight", "body": "We will be down 2-4am"}'Broadcast returns 202 immediately while delivery continues in the background. See POST /notify.
Next steps
- API Reference — status codes, payloads, and admin routes
- SDK Reference — configuration and types
- Troubleshooting — permission, HTTPS, and 401/403/429 issues