PushHub REST API Reference
Base URLs:
- Production:
https://api.pushhub.kemegames.com/api/v1 - Local development:
http://localhost:5000/api/v1
The full machine-readable spec is available at docs/openapi.yaml.
Authentication
All endpoints (except health and auth) require one of:
Bearer JWT (dashboard users)
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR...
Obtain a JWT by calling POST /auth/login.
API Key (SDK / server-to-server)
Authorization: ApiKey ph_your_api_key_here
API keys are scoped to a single app. Create and revoke them in the dashboard under App Settings → API Keys.
Common Response Formats
Success
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "My App",
"createdAt": "2024-01-15T12:00:00Z"
}
Validation Error (400)
{
"message": "Validation failed",
"errors": {
"title": ["Title is required"],
"body": ["Body must not exceed 1000 characters"]
}
}
Error (4xx / 5xx)
{
"message": "Resource not found",
"code": "RESOURCE_NOT_FOUND"
}
Pagination
List endpoints return a paged response:
{
"items": [...],
"total": 1250,
"page": 1,
"pageSize": 50,
"totalPages": 25
}
Pass ?page=2&pageSize=50 to paginate.
Endpoints
Health
GET /health
No authentication required. Returns service status.
Response 200:
{
"status": "Healthy",
"version": "1.0.0",
"timestamp": "2024-01-15T12:00:00Z",
"checks": {
"database": "Healthy",
"redis": "Healthy"
}
}
Authentication
POST /auth/register
Create a new account.
Request:
{
"email": "dev@example.com",
"password": "SecurePass123!",
"name": "Alex Johnson"
}
Response 201:
{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR...",
"refreshToken": "dGhpcyBpcyBhIHJlZnJlc2...",
"expiresAt": "2024-01-16T12:00:00Z",
"user": {
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"email": "dev@example.com",
"name": "Alex Johnson",
"createdAt": "2024-01-15T12:00:00Z"
}
}
POST /auth/login
Request:
{
"email": "dev@example.com",
"password": "SecurePass123!"
}
Response 200: Same as register response.
POST /auth/refresh
Request:
{ "refreshToken": "dGhpcyBpcyBhIHJlZnJlc2..." }
Response 200: Same as register response.
GET /auth/me
Returns the current authenticated user.
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"email": "dev@example.com",
"name": "Alex Johnson",
"createdAt": "2024-01-15T12:00:00Z"
}
Apps
GET /apps
List all apps for the authenticated account.
Response 200:
[
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "My Game App",
"bundleId": "com.example.mygame",
"platform": "Android",
"hasFcmConfig": true,
"hasApnsConfig": false,
"createdAt": "2024-01-15T12:00:00Z",
"updatedAt": "2024-01-15T12:00:00Z"
}
]
POST /apps
Request:
{
"name": "My Game App",
"bundleId": "com.example.mygame",
"platform": "Android"
}
Response 201: Returns the created App object.
GET /apps/{appId}
Returns a single App object.
PUT /apps/{appId}
Request:
{ "name": "My Game App v2" }
DELETE /apps/{appId}
Returns 204 No Content.
PUT /apps/{appId}/fcm-config
Request:
{
"serviceAccountJson": "{\"type\":\"service_account\",\"project_id\":\"my-project\",...}"
}
PUT /apps/{appId}/apns-config
Request:
{
"keyId": "ABCDE12345",
"teamId": "XYZ9876543",
"bundleId": "com.example.mygame",
"p8KeyContent": "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----",
"useSandbox": false
}
Devices
POST /devices/register
Auth: API Key
Register or update a device token. Call on every app launch.
Request:
{
"appId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"deviceToken": "fMe4G8kT5Q:APA91bH...",
"platform": "Android",
"userId": "user-123",
"deviceMetadata": {
"country": "US",
"language": "en",
"appVersion": "2.1.0"
}
}
Response 200:
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"appId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"deviceToken": "fMe4G8kT5Q:APA91bH...",
"platform": "Android",
"userId": "user-123",
"deviceMetadata": { "country": "US", "language": "en" },
"isActive": true,
"lastSeenAt": "2024-01-15T12:00:00Z",
"createdAt": "2024-01-10T08:00:00Z"
}
GET /devices?appId={appId}&page=1&pageSize=50
Returns paginated PagedDevices.
Optional filters: platform, userId.
GET /devices/{deviceId}
Returns a single Device.
PUT /devices/{deviceId}
Auth: API Key
Request:
{
"userId": "new-user-456",
"deviceMetadata": { "subscriptionTier": "premium" }
}
DELETE /devices/{deviceId}
Returns 204 No Content.
Segments
POST /segments
Request:
{
"appId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "Premium US Users",
"description": "Users on premium tier in the United States",
"filter": {
"operator": "AND",
"conditions": [
{ "field": "country", "operator": "equals", "value": "US" },
{ "field": "subscriptionTier", "operator": "equals", "value": "premium" }
]
}
}
Response 201: Returns Segment object.
GET /segments?appId={appId}
Returns array of Segment objects.
GET /segments/{segmentId}/device-count
{ "count": 45231 }
Campaigns
POST /campaigns
Request:
{
"appId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "Summer Sale Announcement",
"title": "Summer Sale is LIVE!",
"body": "Up to 50% off all in-game items this weekend only.",
"imageUrl": "https://cdn.example.com/summer-sale.png",
"deepLink": "myapp://store/sale",
"customData": { "campaign_type": "promotional" },
"targetType": "Segment",
"segmentId": "seg-uuid-here"
}
Response 201: Returns Campaign object with status: "Draft".
POST /campaigns/{campaignId}/send
Send immediately.
Response 202:
{
"campaignId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "Queued",
"estimatedRecipients": 45231
}
POST /campaigns/{campaignId}/schedule
Request:
{ "scheduledAt": "2024-01-20T09:00:00Z" }
GET /campaigns/{campaignId}/analytics
Response 200:
{
"campaignId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"sent": 45231,
"delivered": 43891,
"opened": 8234,
"failed": 1340,
"deliveryRate": 0.9703,
"openRate": 0.1876,
"platformBreakdown": {
"android": { "sent": 28000, "delivered": 27300, "opened": 5100, "failed": 700 },
"ios": { "sent": 17231, "delivered": 16591, "opened": 3134, "failed": 640 }
}
}
Notifications
POST /notifications/send
Send a direct notification (up to 1000 devices per call).
Request:
{
"appId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"deviceIds": ["device-uuid-1", "device-uuid-2"],
"title": "Your order shipped!",
"body": "Order #ORD-456 is on its way.",
"deepLink": "myapp://orders/ORD-456",
"customData": { "orderId": "ORD-456" }
}
Response 202:
{
"notificationIds": ["notif-uuid-1", "notif-uuid-2"],
"queued": 2,
"rejected": 0
}
POST /notifications/track-delivery
Auth: API Key
{ "notificationId": "notif-uuid-1" }
Response 204
POST /notifications/track-opened
Auth: API Key
{ "notificationId": "notif-uuid-1" }
Response 204
Analytics
GET /analytics/overview?appId={appId}&from=2024-01-01&to=2024-01-31
{
"appId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"period": { "from": "2024-01-01", "to": "2024-01-31" },
"totalDevices": 125000,
"activeDevices": 98000,
"totalNotificationsSent": 2450000,
"deliveryRate": 0.971,
"openRate": 0.183,
"platformBreakdown": { "android": 82000, "ios": 43000 }
}
GET /analytics/time-series?appId={appId}&from=2024-01-01&to=2024-01-07&granularity=day
{
"granularity": "day",
"data": [
{
"timestamp": "2024-01-01T00:00:00Z",
"sent": 12500,
"delivered": 12100,
"opened": 2200,
"failed": 400
}
]
}
API Keys
POST /api-keys
Request:
{
"appId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "Production SDK Key"
}
Response 201:
{
"id": "key-uuid",
"appId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "Production SDK Key",
"prefix": "ph_abc123",
"key": "ph_abc123def456ghi789jkl012mno345pqr678stu901",
"createdAt": "2024-01-15T12:00:00Z",
"lastUsedAt": null
}
The key field is returned only at creation. Store it immediately.
DELETE /api-keys/{keyId}
Returns 204 No Content. The key is immediately revoked.
Event Triggers
POST /event-triggers
Request:
{
"appId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "Post-Purchase Thank You",
"eventName": "purchase_completed",
"delayMinutes": 5,
"conditions": [
{ "field": "amount", "operator": "greater_than", "value": 20 }
],
"title": "Thanks for your purchase!",
"body": "Your order {{properties.orderId}} is confirmed."
}
Response 201: Returns EventTrigger object.
POST /event-triggers/fire
Auth: API Key
Fire a custom event from the SDK.
Request:
{
"deviceId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"eventName": "purchase_completed",
"properties": {
"orderId": "ORD-789",
"amount": 49.99,
"currency": "USD"
}
}
Response 202:
{ "triggersMatched": 1 }
Rate Limits
| Endpoint group | Limit |
|---|---|
POST /auth/* |
20 req/min per IP |
POST /devices/register |
1000 req/min per API key |
POST /event-triggers/fire |
5000 req/min per API key |
POST /notifications/send |
100 req/min per API key |
| All other endpoints | 300 req/min per token |
Rate limit headers are included in every response:
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 987
X-RateLimit-Reset: 1705312800