Notifications

Pushy Notifications

Device token registration, topic subscriptions, and dev push sends.

Overview

The backend uses PushyService for two separate concerns:

  1. Managing device tokens and topic subscriptions.
  2. Sending push notifications to device tokens from a dev-oriented API endpoint.

Relevant routes:

  • POST /api/v1/device-info
  • GET /api/v1/device-info
  • POST /api/v1/device-info/subscribe
  • POST /api/v1/device-info/unsubscribe
  • GET /api/v1/device-info/topics/{topic}/subscribers
  • POST /api/v1/notifications/send-dev

All of these routes should be treated as protected and called with:

Authorization: Bearer <access_token>

1. Register Device Token

POST /api/v1/device-info

Stores the current device token for the authenticated user.

This route uses DecryptField() middleware, so plain JSON and encrypted wrapper payloads are both accepted.

Request

{
  "token": "pushy-device-token",
  "platform": "ANDROID",
  "language": "en"
}

Notes

  • platform must be one of IOS, ANDROID, or WEB.
  • language currently accepts en or zh.
  • The service enforces a one-user/one-active-session pattern: it deactivates other device rows for the same user and also deactivates the same token for other users.

Success response

{
  "status_code": 201,
  "message": "Success",
  "data": {
    "token": "pushy-device-token",
    "platform": "ANDROID"
  }
}

2. List Registered Device Tokens

GET /api/v1/device-info

Returns device token rows linked to the authenticated mobile user.

Success response

{
  "status_code": 200,
  "message": "Success",
  "data": [
    {
      "token": "pushy-device-token",
      "platform": "ANDROID"
    }
  ]
}

3. Subscribe a Device Token to Topics

POST /api/v1/device-info/subscribe

Subscribes a token to one or more Pushy topics.

Request

{
  "token": "pushy-device-token",
  "topics": ["announcements", "system-updates"]
}

Success response

{
  "status_code": 200,
  "message": "Success",
  "data": {
    "success": true,
    "message": "Successfully subscribed to topics"
  }
}

4. Unsubscribe a Device Token

POST /api/v1/device-info/unsubscribe

Unsubscribes a token from specific topics.

Request

{
  "token": "pushy-device-token",
  "topics": ["announcements"]
}

Notes

  • If topics is omitted or empty, the handler falls back to ["*"].
  • Invalid device-token errors are surfaced as a bad request.

5. Inspect Topic Subscribers

GET /api/v1/device-info/topics/{topic}/subscribers

Returns the Pushy device tokens currently subscribed to the requested topic.

Success response

{
  "status_code": 200,
  "message": "Success",
  "data": {
    "topic": "announcements",
    "subscribers": ["pushy-device-token"]
  }
}

6. Send a Dev Push Notification

POST /api/v1/notifications/send-dev

Sends a push notification to one or more device tokens through Pushy.

This route also passes through DecryptField().

Request

{
  "title": "Balance Updated",
  "content": "Your wallet has been credited.",
  "to": ["pushy-device-token"],
  "action": "wallet_refresh",
  "additional_data": {
    "wallet_id": 17,
    "source": "topup"
  }
}

Payload behavior

  • title and content are required.
  • to must contain at least one device token.
  • action, when present, is merged into the notification data payload.
  • additional_data is merged into the same Pushy data payload as top-level keys.

Success response

{
  "status_code": 200,
  "message": "Success",
  "data": {
    "success": true,
    "message": "Notification sent successfully"
  }
}

Important implementation note

The API currently returns only a success flag and message. The detailed Pushy send result (notification ID, total devices, failed tokens) is not returned by this endpoint, even though the underlying service calculates it.

Pushy Service Limits

The backend Pushy integration currently enforces:

  • Maximum payload size: 4096 bytes
  • Maximum devices per request: 100000

Common underlying Pushy error mappings:

  • NO_RECIPIENTS
  • NO_APNS_AUTH
  • PAYLOAD_LIMIT_EXCEEDED
  • INVALID_DEVICE_TOKEN

If you hit these conditions, the API responds with the project’s normal JSON error envelope.

Copyright © 2026