Overview
The send endpoint returns HTTP 202 because it accepts work into a server-side queue. It does not wait for every device before responding.
Always give each logical API send an Idempotency-Key. For example, order-1042-shipped identifies one business event. If your backend times out and retries with the same key, PulseGrid returns the existing notification instead of creating another one.
Duplicate protection exists at two levels:
- one notification per project and idempotency key
- one delivery per notification and device
An atomic database claim prevents repeated workers from processing the same notification concurrently.
Setup
1. Create a stable idempotency key from the business event.
2. Send a title, optional body, target_type and matching target_value.
3. Supported targets: device, app_user, app_users, topic, project_role and custom.
4. Treat HTTP 202 as queued.
5. Read the dashboard or GET /api/push/notifications/ for final state.
6. sent means FCM accepted all attempted deliveries.
7. partial means at least one succeeded and at least one failed.
8. failed means none succeeded or a safety/configuration rule prevented delivery.
Code example
POST https://your-pulsegrid-domain.com/api/push/send/
Idempotency-Key: order-1042-shipped
X-PulseGrid-Public-Key: pk_your_public_key
X-PulseGrid-Secret-Key: sk_your_secret_key
Content-Type: application/json
{
"project_id": "210d1f81-1d6e-412b-a0a0-b3cd843e82fb",
"title": "Order shipped",
"body": "Order 1042 is on its way.",
"target_type": "app_user",
"target_value": {"app_user_id": "CUSTOMER_UUID"},
"data": {"screen": "orders", "order_id": "1042"}
}
Notes
Custom data values are converted to strings for FCM. Do not include passwords, payment-card data or private secrets. If Redis is briefly unavailable, the notification remains pending in the database and Celery Beat collects it after the queue reconnects.