• Gentle Push
REST API

API Reference

Gentle Push has a REST API for sending web push notifications with an API key. Using it is optional: the dashboard needs no code and the free plan is unlimited either way.

Complete reference for the Push REST API. Use bearer tokens for dashboard integrations or project API keys for server-side notification delivery.

Overview

The Gentle Push API is a RESTful HTTP API returning JSON. All endpoints are scoped tohttps://api.push.yourdomain.com in production (or http://localhost:4000 in development).

Base URL
text
https://api.push.yourdomain.com

Authentication

Gentle Push supports two authentication mechanisms. Both are accepted on all protected endpoints.

Bearer Token (JWT)

Obtained from POST /auth/verify-code. Valid for 7 days. Use for dashboard and user-scoped actions.

http
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI...
API Key

Project-level key from the dashboard. Use for server-side notification delivery.

http
x-api-key: pk_live_a1b2c3d4e5f6...

Errors

All errors follow a consistent shape with an HTTP status code and JSON body:

json
// 400 Bad Request
{
  "success": false,
  "error": "Email is required"
}

// 401 Unauthorized
{
  "success": false,
  "error": "Invalid or expired code"
}

// 404 Not Found
{
  "success": false,
  "error": "Project not found"
}
StatusMeaning
200Success
400Bad request — missing or invalid fields
401Unauthenticated — missing or invalid credentials
403Forbidden — resource belongs to another entity
404Not found
500Internal server error

Authentication Endpoints

POST/auth/request-codePublic

Sends a 6-digit one-time code to the provided email address. The code expires after 10 minutes.

Request Body
json
{
  "email": "[email protected]"  // required
}
Response
json
// 200 OK
{
  "success": true,
  "message": "Code sent"
}
POST/auth/verify-codePublic

Verifies the OTP code. Automatically creates a Customer account and default Project if this is a first-time login. Returns a signed JWT valid for 7 days.

Request Body
json
{
  "email": "[email protected]",  // required
  "code":  "483920"             // required — 6-digit OTP
}
Response
json
// 200 OK
{
  "success": true,
  "token":   "eyJhbGciOiJIUzI1NiIsInR5cCI...",
  "customer": { "id": 1, "email": "[email protected]", "name": null },
  "project":  { "id": 1, "name": "My Project", "api_key": "pk_..." }
}
GET/auth/me🔑 Auth required

Returns the authenticated customer profile and their default project.

Response
json
// 200 OK
{
  "success":  true,
  "customer": { "id": 1, "email": "[email protected]" },
  "project":  { "id": 1, "name": "My Project", "api_key": "pk_..." }
}

Projects

GET/projects🔑 Auth required

Lists all projects belonging to the authenticated customer.

Response
json
// 200 OK
{
  "success":  true,
  "projects": [
    { "id": 1, "name": "My Project", "api_key": "pk_a1b2c3..." }
  ]
}
POST/projects🔑 Auth required

Creates a new project. An API key is automatically generated.

Request Body
json
{
  "name": "My New Project"  // required
}
Response
json
// 200 OK
{
  "success": true,
  "project": { "id": 2, "name": "My New Project", "api_key": "pk_..." }
}
PUT/projects/:id🔑 Auth required

Renames an existing project. Only the owning customer can update it.

Request Body
json
{
  "name": "Renamed Project"  // required, non-empty
}
Response
json
// 200 OK
{
  "success": true,
  "project": { "id": 2, "name": "Renamed Project" }
}
POST/projects/:id/regenerate-key🔑 Auth required

Invalidates the current API key and issues a new one. The old key immediately stops working.

Response
json
// 200 OK
{
  "success": true,
  "apiKey":  "pk_new_a1b2c3d4..."
}
GET/projects/branding🔑 Auth required

Fetches branding settings for the project in the auth context (from token or API key). Does not require an :id param.

Response
json
// 200 OK
{
  "success":   true,
  "projectId": 1,
  "branding": {
    "brandName":    "Acme Corp",
    "iconUrl":      "https://acme.com/icon.png",
    "badgeUrl":     "https://acme.com/badge.png",
    "heroImageUrl": "https://acme.com/hero.jpg",
    "themeColor":   "#0070F3"
  }
}
POST/projects/branding🔑 Auth required

Upserts branding for the project in the auth context. All fields are optional — omit any field to leave it unchanged.

Request Body
json
{
  "brandName":    "Acme Corp",       // optional
  "iconUrl":      "https://...",     // optional — used as notification icon
  "badgeUrl":     "https://...",     // optional — used as notification badge
  "heroImageUrl": "https://...",     // optional — subscribe page hero
  "themeColor":   "#0070F3"          // optional — hex color
}
Response
json
// 200 OK
{
  "success":  true,
  "branding": { ...updatedBranding }
}
GET/projects/:id/brandingPublic

Public endpoint — fetches branding for any project by ID. Used by hosted subscribe pages to apply your brand. When called with auth, performs ownership verification.

Response
json
// 200 OK
{
  "success":  true,
  "branding": { "brandName": "Acme Corp", ... }
}
GET/projects/:id/analytics🔑 Auth required

Returns aggregated analytics for a project: subscriber count, notification metrics, CTR, top-performing notifications, and subscriber growth over time.

Response
json
// 200 OK
{
  "success": true,
  "analytics": {
    "totalSubscribers":    142,
    "notificationsSent":   58,
    "impressions":         3420,
    "clicks":              210,
    "ctr":                 6.14,
    "topNotifications": [
      { "notificationId": "uuid", "clicks": 87, "impressions": 1200 }
    ],
    "subscriberGrowth": [
      { "date": "2026-03-01", "count": 3 },
      { "date": "2026-03-02", "count": 7 }
    ]
  }
}

Subscribers

POST/subscribers/registerPublic

Registers a browser push subscription for a project. Called automatically by the Push service worker when a user opts in. Deduplicates by endpoint — re-registration of an existing endpoint updates metadata.

Request Body
json
{
  "projectId":    1,              // required
  "subscription": {               // required — PushSubscription object
    "endpoint":   "https://fcm.googleapis.com/...",
    "keys": {
      "p256dh": "BNcRd...",
      "auth":   "tBHIt..."
    }
  },
  "browser":    "Chrome",         // optional
  "os":         "Windows",        // optional
  "deviceType": "desktop"         // optional
}
Response
json
// 200 OK
{
  "success":      true,
  "subscriberId": 42,
  "subscriber": {
    "id":         42,
    "projectId":  1,
    "browser":    "Chrome",
    "os":         "Windows",
    "deviceType": "desktop",
    "createdAt":  "2026-03-01T10:00:00.000Z"
  }
}
GET/subscribers🔑 Auth required

Lists all subscribers scoped to the authenticated project (API key) or customer (JWT).

Response
json
// 200 OK
{
  "success":     true,
  "subscribers": [
    { "id": 42, "browser": "Chrome", "deviceType": "desktop", "createdAt": "..." }
  ]
}
POST/subscribers🔑 Auth required

Manually creates or updates a subscriber record. Upserts by subscription endpoint.

Request Body
json
{
  "subscription": {              // required
    "endpoint": "https://...",
    "keys": { "p256dh": "...", "auth": "..." }
  },
  "browser":    "Firefox",       // optional
  "deviceType": "mobile"         // optional
}
Response
json
// 200 OK
{ "success": true, "subscriber": { "id": 43, ... } }
DELETE/subscribers/:id🔑 Auth required

Deletes a subscriber. The subscriber must belong to the authenticated project or customer.

Response
json
// 200 OK
{ "success": true, "deleted": 1 }

Notifications

POST/notifications/send🔑 Auth required

Sends a notification. Supports four channels: web, mobile and email. Project branding is automatically applied to web notifications (icon, badge, title prefix).

Web push — bulk to all subscribers
http
POST /notifications/send
Content-Type: application/json
x-api-key: pk_live_...

{
  "type": "web",
  "target": { "all": true },
  "payload": {
    "title":    "New feature launched 🎉",
    "body":     "Click to learn more",
    "clickUrl": "https://yourdomain.com/features",
    "icon":     "https://yourdomain.com/icon.png",   // optional, falls back to branding
    "badge":    "https://yourdomain.com/badge.png",  // optional
    "image":    "https://yourdomain.com/banner.png", // optional
    "data":     { "custom": "value" }               // optional
  }
}
Web push — specific subscribers
json
{
  "type": "web",
  "target": { "subscriberIds": [42, 43, 44] },
  "payload": { "title": "Hello!", "body": "..." }
}
Web push — direct to subscription object
json
{
  "type": "web",
  "target": {
    "endpoint": "https://fcm.googleapis.com/...",
    "keys": { "p256dh": "BNcRd...", "auth": "tBHIt..." }
  },
  "payload": { "title": "Hello!", "body": "Direct push" }
}
Mobile (FCM / APNs) — planned, not yet available
json
{
  "type":   "mobile",
  "target": { "platform": "android", "token": "fcm_token_..." },
  "payload": { "title": "Update available", "body": "v2.0 is here", "data": {} }
}
Email
json
{
  "type":    "email",
  "target":  "[email protected]",
  "payload": { "subject": "Your order shipped", "body": "<p>Track your order...</p>" }
}
Response — bulk web
json
{
  "success": true,
  "results": [
    { "subscriberId": 42, "success": true },
    { "subscriberId": 43, "success": false, "error": "Subscription expired" }
  ]
}
Response — single target (mobile/email)
json
{ "success": true, "data": { ...providerResponse } }

// or on failure:
{ "success": false, "error": { "type": "delivery_failure", "message": "..." } }
GET/notifications/history🔑 Auth required

Returns all notification records for the authenticated project, ordered newest first.

Response
json
// 200 OK
{
  "success": true,
  "data": [
    {
      "id":             "uuid-1234",
      "type":           "web",
      "payload":        { "title": "...", "body": "..." },
      "status":         "delivered",
      "recipientCount": 42,
      "createdAt":      "2026-03-20T14:30:00.000Z"
    }
  ]
}
POST/notifications/test/sendPublic

Exercises all configured notification providers using test tokens from environment variables (TEST_WEB_SUBSCRIPTION, TEST_FCM_TOKEN, etc.). Used for integration testing and provider health checks.

Response
json
// 200 OK
{
  "success": true,
  "data": {
    "web":    { ...result },
    "mobile": { ...result },
    "email":  { ...result },
  }
}

Events

POST/events/trackPublic

Records a notification lifecycle event. Called automatically by the Push service worker on the end-user's browser. Valid event types are delivered, shown, and clicked.

Request Body
json
{
  "notificationId": "uuid-1234",         // required
  "subscriberId":   42,                   // required
  "eventType":      "clicked",            // required: "delivered" | "shown" | "clicked"
  "projectId":      1,                    // optional but recommended for ownership check
  "userAgent":      "Mozilla/5.0 ...",    // optional
  "timestamp":      "2026-03-20T14:31:00Z"// optional — defaults to now
}
Response
json
// 200 OK
{ "success": true }
GET/events/subscriber/:id🔑 Auth required

Returns all events for a specific subscriber, ordered newest first.

Response
json
// 200 OK
{
  "success": true,
  "events":  [
    { "id": 1, "notificationId": "uuid", "eventType": "clicked", "createdAt": "..." }
  ]
}
GET/events/notifications/:notificationId🔑 Auth required

Returns the full detail view for a single notification: its record, aggregated metrics (delivery, impressions, clicks, CTR), and the raw event timeline.

Response
json
// 200 OK
{
  "success": true,
  "notification": { "id": "uuid", "type": "web", "payload": {}, "createdAt": "..." },
  "metrics": {
    "delivery":    42,
    "impressions": 38,
    "clicks":      12,
    "ctr":         31.58
  },
  "timeline": [
    { "eventType": "delivered", "subscriberId": 1, "createdAt": "..." },
    { "eventType": "clicked",   "subscriberId": 1, "createdAt": "..." }
  ]
}

Customers

Note: Customer endpoints are primarily used for programmatic account management. The POST /customers endpoint creates accounts without auth. The GET|PUT|DELETE /customers/:id endpoints currently do not enforce ownership — access is gated only by valid authentication.
POST/customersPublic

Creates a new customer account. If a customer with the same email already exists, the existing record is returned. Includes an auto-generated customer-level API key.

Request Body
json
{
  "name":  "Acme Corp",          // optional
  "email": "[email protected]",     // optional — used for deduplication
  "phone": "+14155552671"        // optional
}
Response
json
// 200 OK
{
  "success": true,
  "customer": {
    "id":      1,
    "name":   "Acme Corp",
    "email":  "[email protected]",
    "apiKey": "ck_..."
  }
}
GET/customers/me🔑 Auth required

Returns the customer record for the owner of the provided API key. Uses customer-level API key (x-api-key) only — bearer tokens are not accepted on this endpoint.

Response
json
// 200 OK
{ "success": true, "customer": { "id": 1, "name": "...", "email": "..." } }
POST/customers/regenerate-key🔑 Auth required

Regenerates and returns a new customer-level API key. Requires customer-level x-api-key authentication.

Response
json
// 200 OK
{ "success": true, "apiKey": "ck_new_..." }
GET/customers/:id🔑 Auth required

Fetches a customer record by ID.

Response
json
// 200 OK
{ "success": true, "data": { "id": 1, "name": "...", "email": "..." } }
PUT/customers/:id🔑 Auth required

Partially updates a customer record.

Request Body
json
{
  "name":          "New Name",      // optional
  "email":         "[email protected]", // optional
  "phone":         "+1...",         // optional
  "preferences":   {},              // optional — arbitrary JSON object
  "device_tokens": {}               // optional — arbitrary JSON object
}
Response
json
// 200 OK
{ "success": true, "data": { "changes": 1 } }
DELETE/customers/:id🔑 Auth required

Permanently deletes a customer record and all associated data.

Response
json
// 200 OK
{ "success": true }
POST/customers/:id/device-token🔑 Auth required

Registers or updates a mobile device token (FCM for Android, APNs for iOS) for a customer. Stores tokens by platform. Native Android and iOS delivery is planned and not yet in use; tokens are accepted but nothing is sent to them today.

Request Body
json
{
  "platform": "android",    // required: "android" | "ios"
  "token":    "APA91bH..."  // required — FCM or APNs token
}
Response
json
// 200 OK
{ "success": true, "data": { "android": "APA91bH...", "ios": null } }