Skip to content

Notifications

Pousse une annonce éditée par l’admin — soit ciblée sur une liste de destinataires, soit en broadcast à tous les utilisateurs. Rejoint le pipeline standard : la ligne est persistée immédiatement, les préférences in-app sont honorées au dispatch, et le cron digest pousse la notification au tick suivant (individuelle ou en digest).

Le texte est par locale et figé tel quel : chaque destinataire est rendu dans sa locale (fallback locale par défaut → première fournie). Aucune clé de catalogue : title/body sont fournis par l’admin et gelés dans data.translations.

Corps (JSON)

ChampTypeObligatoireNotes
translations{ "<locale>": { "title", "body" } }ouilocales supportées uniquement ; title/body non vides (title ≤ 150, body ≤ 2000) ; doit inclure la locale par défaut fr-FR (fallback universel)
urlstringnondeep-link gelé dans data.url (payload push)
userIds["<hex>", …]exactement un modeciblé, 1..1000 ids
scope"all"exactement un modebroadcast keyset sur tous les users
announcementIdhex (32)nonnamespace de dedup partagé ; généré et renvoyé si absent

Fournir exactement un de userIds[] ou scope:"all" (les deux ou aucun = 400).

Dedup / idempotence : tous les destinataires d’une même annonce partagent la clé system.announcement:<announcementId>. En broadcast, renvoyer le announcementId retourné sur chaque appel ?cursor= suivant pour qu’une page reprise/rejouée ne notifie pas deux fois un même destinataire.

Query params (broadcast uniquement)

ParamValeursDéfautNotes
cursorhex (32)débutdernier lastId traité par l’appel précédent
batchSize1..1000200taille de lot par appel

Réponse — ciblé (200)

{
"announcementId": "9f1c…",
"mode": "targeted",
"dispatched": 12, // lignes persistées
"skipped": 1, // destinataire opt-out in-app
"failed": [{ "userId": "ab…", "error": "" }]
}

Réponse — broadcast (200) — un lot keyset par appel, boucler jusqu’à done:true

{
"announcementId": "9f1c…",
"mode": "broadcast",
"dispatched": 200,
"skipped": 0,
"failed": [],
"lastId": "<hex>",
"nextCursor": "<hex>", // null quand done
"done": false,
"totalAll": 15234, // COUNT(*) users (snapshot)
"durationMs": 38
}

Comme les autres batches admin, une erreur de dispatch par destinataire n’avorte pas la boucle : elle part dans failed[].

Erreurs

StatusBodySens
400{ "error": "Request body must be a JSON object." }corps absent/non-objet
400{ "error": "Provide exactly one of userIds[] or scope:\"all\"." }ciblage ambigu
400{ "error": "Invalid cursor." } / "Invalid batchSize."pagination broadcast KO
422{ "error": "translations must include the default locale \"fr-FR\" (used as fallback)." }wording invalide (exemples : locale non supportée, title/body vide, trop long, défaut manquant)
422{ "error": "userIds must be a non-empty array of hex user ids." }liste ciblée vide/malformée
403{ "error": "..." }auth KO

Exemples curl

Terminal window
# Ciblé : 2 destinataires, texte bilingue + deep-link
curl -s -X POST -H "Authorization: Bearer $ADMIN_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"translations": {
"fr-FR": { "title": "Nouveauté", "body": "Découvrez les nouvelles villes." },
"en-US": { "title": "What'\''s new", "body": "Discover the new cities." }
},
"url": "https://hydrogen.app/cities",
"userIds": ["ab12…", "cd34…"]
}' \
http://hydrogen.dev.com/admin/notifications
# Broadcast : 1er appel (announcementId généré et renvoyé)
curl -s -X POST -H "Authorization: Bearer $ADMIN_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "translations": { "fr-FR": { "title": "Maintenance", "body": "Service indisponible 22h-23h." } }, "scope": "all" }' \
"http://hydrogen.dev.com/admin/notifications?batchSize=500"
# Broadcast : appels suivants — réutiliser announcementId + nextCursor jusqu'à done:true
curl -s -X POST -H "Authorization: Bearer $ADMIN_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "translations": { "fr-FR": { "title": "Maintenance", "body": "Service indisponible 22h-23h." } }, "scope": "all", "announcementId": "9f1c…" }' \
"http://hydrogen.dev.com/admin/notifications?batchSize=500&cursor=<lastId>"