BETA
Skip to content

Subscribers ​

Register and manage push notification subscribers. A subscriber represents a device or browser endpoint that can receive notifications.

Base URL: https://app.krafter.dev/api/v1

Subscriber retention

PushCleanupWorker runs daily at 03:00 UTC (config.exs:89) and performs two passes (push_cleanup_worker.ex:31-62):

  • Mark inactive after 90 days — subscribers whose last_active_at is older than 90 days and whose status is not unsubscribed are flipped to status: "inactive".
  • Delete after 180 days — inactive subscribers whose last_active_at (or inserted_at, when last_active_at is nil) is older than 180 days are deleted from the database. After both passes the parent PushApp.subscriber_count is recomputed from the count of remaining active subscribers, so the field always reflects reality.

If you re-register a deleted subscriber's token after this window, you get a fresh row with a new id. Tokens that the provider has already revoked (gone / unregistered errors during delivery) are marked inactive immediately by Push.mark_inactive/1, regardless of the 90-day rule.

List Subscribers ​

Retrieve subscribers for a specific app, with optional filtering by platform and status.

GET /push/apps/:app_id/subscribers?platform=...&status=...&limit=50&offset=0

Required scope: push:read

Query Parameters ​

ParameterTypeDefaultDescription
platformstringFilter by platform: web, ios, android.
statusstringFilter by status: active, inactive, unsubscribed (lib/krafter/push/push_subscriber.ex:34). inactive is set by Push.mark_inactive/1 after delivery failures or token expiry.
limitinteger50Maximum number of subscribers to return.
offsetinteger0Number of subscribers to skip for pagination.

Example Request ​

bash
curl "https://app.krafter.dev/api/v1/push/apps/a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d/subscribers?platform=web&limit=20" \
  -H "Authorization: Bearer kr_live_abc123def456"

Example Response ​

json
{
  "data": [
    {
      "id": "c3d4e5f6-7a8b-9c0d-1e2f-3a4b5c6d7e8f",
      "app_id": "a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
      "platform": "web",
      "user_id": "usr_123",
      "tags": ["beta", "premium"],
      "properties": {
        "browser": "Chrome",
        "os": "macOS"
      },
      "status": "active",
      "last_active_at": "2025-06-14T18:30:00Z",
      "created_at": "2025-06-01T10:00:00Z",
      "updated_at": "2025-06-14T18:30:00Z"
    },
    {
      "id": "d4e5f6a7-8b9c-0d1e-2f3a-4b5c6d7e8f9a",
      "app_id": "a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
      "platform": "web",
      "user_id": null,
      "tags": [],
      "properties": {
        "browser": "Firefox",
        "os": "Windows"
      },
      "status": "active",
      "last_active_at": "2025-06-13T09:15:00Z",
      "created_at": "2025-06-03T14:20:00Z",
      "updated_at": "2025-06-13T09:15:00Z"
    }
  ]
}

Register Subscriber ​

Register a new subscriber or update an existing one. If a subscriber with the same token already exists, it is upserted with the new data. Returns 200 OK for both insert and update — the controller does not distinguish create from upsert (push_controller.ex:166).

POST /push/apps/:app_id/subscribers

Required scope: push:write

Rate limit (strict)

Subscriber registration uses the strict bucket — 30 requests / 60 seconds per team, shared with flags/{evaluate,bootstrap,track} and audit/scans/run. Have your client register once at app launch (not on every page load), and consider deduplicating on the SDK side before posting.

Request Body ​

FieldTypeRequiredDescription
platformstringYesDevice platform: web, ios, or android.
tokenstringYesThe push token. For web this is the JSON of the browser's PushSubscription (JSON.stringify(subscription)): an https:// endpoint plus keys.p256dh (base64url, 65 bytes) and keys.auth (base64url, 16 bytes). Anything else — an endpoint URL on its own included — is a 422 on token. The token must be a JSON object with an https:// endpoint, a 65-byte p256dh and a 16-byte auth, both base64url; that is exactly what JSON.stringify(subscription) produces. For ios it is the APNs device token, for android the FCM registration token.
user_idstringNoYour application's user identifier for targeting.
tagsstring[]NoTags for segmenting subscribers (e.g., ["beta", "premium"]).
propertiesobjectNoCustom key-value properties for the subscriber.

Example Request ​

bash
curl -X POST https://app.krafter.dev/api/v1/push/apps/a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d/subscribers \
  -H "Authorization: Bearer kr_live_abc123def456" \
  -H "Content-Type: application/json" \
  -d '{
    "platform": "web",
    "token": "{\"endpoint\":\"https://fcm.googleapis.com/fcm/send/eKqL...\",\"keys\":{\"p256dh\":\"BNcRd...\",\"auth\":\"tBHI...\"}}",
    "user_id": "usr_123",
    "tags": ["beta", "premium"],
    "properties": {
      "browser": "Chrome",
      "os": "macOS"
    }
  }'

Example Response ​

json
// 200 OK
{
  "data": {
    "id": "c3d4e5f6-7a8b-9c0d-1e2f-3a4b5c6d7e8f",
    "app_id": "a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
    "platform": "web",
    "user_id": "usr_123",
    "tags": ["beta", "premium"],
    "properties": {
      "browser": "Chrome",
      "os": "macOS"
    },
    "status": "active",
    "last_active_at": "2025-06-01T10:00:00Z",
    "created_at": "2025-06-01T10:00:00Z",
    "updated_at": "2025-06-01T10:00:00Z"
  }
}

Update Subscriber ​

Update an existing subscriber's user_id, tags, or properties. The push token, platform, and status cannot be changed via this endpoint — re-register the subscriber with a fresh token to rotate it. Only fields explicitly present in the request body are touched (push_controller.ex:175-194); omitted keys keep their existing values.

PATCH /push/apps/:app_id/subscribers/:subscriber_id

Required scope: push:write

Request Body ​

FieldTypeRequiredDescription
user_idstringNoReplace your application's user identifier. Send null to clear it.
tagsstring[]NoReplace the full tag list (this is a set, not a delta — pass the desired final state).
propertiesobjectNoReplace the full custom-properties map.

Example Request ​

bash
curl -X PATCH https://app.krafter.dev/api/v1/push/apps/a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d/subscribers/c3d4e5f6-7a8b-9c0d-1e2f-3a4b5c6d7e8f \
  -H "Authorization: Bearer kr_live_abc123def456" \
  -H "Content-Type: application/json" \
  -d '{
    "user_id": "usr_456",
    "tags": ["beta", "premium", "ios"]
  }'

Example Response ​

json
{
  "data": {
    "id": "c3d4e5f6-7a8b-9c0d-1e2f-3a4b5c6d7e8f",
    "app_id": "a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
    "platform": "web",
    "user_id": "usr_456",
    "tags": ["beta", "premium", "ios"],
    "properties": {
      "browser": "Chrome",
      "os": "macOS"
    },
    "status": "active",
    "last_active_at": "2025-06-14T18:30:00Z",
    "created_at": "2025-06-01T10:00:00Z",
    "updated_at": "2025-06-15T09:00:00Z"
  }
}

Unsubscribe ​

Remove a subscriber. The subscriber will no longer receive notifications.

DELETE /push/apps/:app_id/subscribers/:subscriber_id

Required scope: push:write

Example Request ​

bash
curl -X DELETE https://app.krafter.dev/api/v1/push/apps/a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d/subscribers/c3d4e5f6-7a8b-9c0d-1e2f-3a4b5c6d7e8f \
  -H "Authorization: Bearer kr_live_abc123def456"

Example Response ​

// 204 No Content

Built by Krafter Studio