Web Push Sender API
Generate VAPID keys and send Web Push notifications to browser subscriptions with RFC 8291 payload encryption, TTL, urgency and topic.
Call with no parameters (or action=generate_keys) to get a fresh P-256 VAPID key pair in base64url: public_key is the applicationServerKey for pushManager.subscribe() in the browser, private_key stays on your server. Keys are generated for the response only and never stored or logged.
action=send posts a notification to one subscription (the PushSubscription JSON from the browser: endpoint + keys.p256dh + keys.auth) or up to 50 subscriptions. The optional payload (text or JSON, max 4,078 bytes) is encrypted with aes128gcm (RFC 8291) using WebCrypto ECDH, HKDF and AES-GCM, and the request is signed with a VAPID ES256 JWT (RFC 8292) built from your vapid object (subject = mailto: or https: contact, public_key, private_key). Set ttl (seconds, default 86400), urgency (very-low, low, normal, high) and topic (replaces an undelivered message with the same topic).
Each result has the push service HTTP status; gone: true (404 / 410) means the subscription expired and should be deleted. action=encrypt returns the encrypted body and headers without sending, for debugging or sending yourself. Nothing is stored: subscriptions and keys live only in the request.
Parameters
| Name | In | Type | Required | Default | |
|---|---|---|---|---|---|
action | body|query | string | no | generate_keys | generate_keys (default without a subscription), send or encrypt (build the request without sending). |
subscription | body | object | no | PushSubscription JSON: {"endpoint": "https://...", "keys": {"p256dh": "...", "auth": "..."}}. | |
subscriptions | body | array | no | Up to 50 subscriptions for one send. | |
payload | body | string | no | Message text or JSON object (max 4,078 bytes); omit for a push without data. | |
vapid | body | object | no | {"subject": "mailto:you@example.com", "public_key": "...", "private_key": "..."}, required for send. | |
ttl | body|query | number | no | 86400 | Seconds the push service keeps the message if the device is offline (0-2,419,200). |
urgency | body|query | string | no | normal | very-low, low, normal or high. |
topic | body|query | string | no | Up to 32 base64url characters; a newer message with the same topic replaces an undelivered one. |
Example
curl -X POST "https://apibox.io.vn/v1/web-push" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"action":"send","subscription":{"endpoint":"https://fcm.googleapis.com/fcm/send/abc","keys":{"p256dh":"BCVxsr7N_eNgVRqvHtD0zTZsEc6-VV-JvLexhqUzORcxaOzi6-AYWXvTBHm4bjyPjs7Vd8pZGH6SRpkNtoIAiw4","auth":"BTBZMqHH6r4Tts7J_aSIgg"}},"payload":{"title":"Đơn hàng #1024","body":"Đã giao thành công"},"ttl":3600,"urgency":"high","vapid":{"subject":"mailto:ops@example.com","public_key":"BECWGWmqQ8K_C-FP8oHoC2GdtyNmQRTEHe8z3qsK5VDKHjkpMUG-RRrKyy-jczLPSqC1y15k6r_iPaGLt39VFOw","private_key":"(your private key)"}}'const res = await fetch('https://apibox.io.vn/v1/web-push', {
method: 'POST',
headers: {
Authorization: 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
"action": "send",
"subscription": {
"endpoint": "https://fcm.googleapis.com/fcm/send/abc",
"keys": {
"p256dh": "BCVxsr7N_eNgVRqvHtD0zTZsEc6-VV-JvLexhqUzORcxaOzi6-AYWXvTBHm4bjyPjs7Vd8pZGH6SRpkNtoIAiw4",
"auth": "BTBZMqHH6r4Tts7J_aSIgg"
}
},
"payload": {
"title": "Đơn hàng #1024",
"body": "Đã giao thành công"
},
"ttl": 3600,
"urgency": "high",
"vapid": {
"subject": "mailto:ops@example.com",
"public_key": "BECWGWmqQ8K_C-FP8oHoC2GdtyNmQRTEHe8z3qsK5VDKHjkpMUG-RRrKyy-jczLPSqC1y15k6r_iPaGLt39VFOw",
"private_key": "(your private key)"
}
}),
});
const data = await res.json();
console.log(data);import requests
res = requests.post(
"https://apibox.io.vn/v1/web-push",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={"action": "send", "subscription": {"endpoint": "https://fcm.googleapis.com/fcm/send/abc", "keys": {"p256dh": "BCVxsr7N_eNgVRqvHtD0zTZsEc6-VV-JvLexhqUzORcxaOzi6-AYWXvTBHm4bjyPjs7Vd8pZGH6SRpkNtoIAiw4", "auth": "BTBZMqHH6r4Tts7J_aSIgg"}}, "payload": {"title": "Đơn hàng #1024", "body": "Đã giao thành công"}, "ttl": 3600, "urgency": "high", "vapid": {"subject": "mailto:ops@example.com", "public_key": "BECWGWmqQ8K_C-FP8oHoC2GdtyNmQRTEHe8z3qsK5VDKHjkpMUG-RRrKyy-jczLPSqC1y15k6r_iPaGLt39VFOw", "private_key": "(your private key)"}},
timeout=30,
)
print(res.json())<?php
$ch = curl_init('https://apibox.io.vn/v1/web-push');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Authorization: Bearer YOUR_API_KEY', 'Content-Type: application/json'],
CURLOPT_POSTFIELDS => '{"action":"send","subscription":{"endpoint":"https://fcm.googleapis.com/fcm/send/abc","keys":{"p256dh":"BCVxsr7N_eNgVRqvHtD0zTZsEc6-VV-JvLexhqUzORcxaOzi6-AYWXvTBHm4bjyPjs7Vd8pZGH6SRpkNtoIAiw4","auth":"BTBZMqHH6r4Tts7J_aSIgg"}},"payload":{"title":"Đơn hàng #1024","body":"Đã giao thành công"},"ttl":3600,"urgency":"high","vapid":{"subject":"mailto:ops@example.com","public_key":"BECWGWmqQ8K_C-FP8oHoC2GdtyNmQRTEHe8z3qsK5VDKHjkpMUG-RRrKyy-jczLPSqC1y15k6r_iPaGLt39VFOw","private_key":"(your private key)"}}',
]);
$response = curl_exec($ch);
print_r(json_decode($response, true));Response
{
"sent": 0,
"failed": 1,
"gone": 1,
"ttl": 3600,
"urgency": "high",
"topic": null,
"payload_bytes": 61,
"results": [
{
"index": 0,
"host": "fcm.googleapis.com",
"ok": false,
"status": 410,
"gone": true,
"retry_after": null,
"message_id": null,
"response": "push subscription has unsubscribed or expired.\n"
}
]
}
Use cases
- Send order or delivery notifications from a PHP, Apps Script or no-code backend
- Create VAPID keys for a new PWA without installing the web-push library
- Clean up expired subscriptions using the gone flag
- Debug a service worker with a known-good encrypted push message
FAQ
Do you store my VAPID private key or the subscriptions?
No. Keys and subscriptions are used for the request only and never written to storage or logs. Send them with every call.
Which browsers does it work with?
Any browser that implements the standard Push API with VAPID: Chrome / Edge (FCM), Firefox (Mozilla autopush), Safari 16+ (Apple), Opera, Samsung Internet. The endpoint in the subscription decides the push service.
What does status 410 mean?
The push service says the subscription no longer exists (the user unsubscribed or the browser data was cleared). The result has gone: true; delete that subscription from your database.
Related APIs
UUID Generator
Generate up to 1,000 UUID v4, time-ordered UUID v7, ULID, Nano ID or random hex IDs per request. Secure randomness, optional uppercase.
⏱Cron Expression Parser
Validate a cron expression and get its next run times in any timezone, with a plain-English description. Supports 5, 6 and 7-field cron syntax.
🕒Time & Timezone Converter
Get the current time in any timezone, convert between timezones and Unix timestamps (seconds or ms), with UTC offset and weekday for up to 30 zones.