Gentle Push
REST API

API Reference

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

Overview

The 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

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, email, and sms. 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)
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>" }
}
SMS
json
{
  "type":    "sms",
  "target":  "+14155552671",
  "payload": { "message": "Your verification code is 482910" }
}
Response — bulk web
json
{
  "success": true,
  "results": [
    { "subscriberId": 42, "success": true },
    { "subscriberId": 43, "success": false, "error": "Subscription expired" }
  ]
}
Response — single target (mobile/email/sms)
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 },
    "sms":    { ...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.

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 } }