PushMesh
Sign in Request access
Open section navigation

Index

Headers

Every HTTP header the PushMesh API accepts and emits, including the proprietary X-Pm-* set.

Headers

PushMesh uses its own namespace, X-Pm-* (and X-PM-* on webhook deliveries). Anything not on this page is a plain HTTP header.

Headers the API accepts

HeaderRoutesWhat it does
Authorizationauthenticated routesBasic pm_live_… (app key) or Bearer <platform key> (application creation and key rotation). See Authentication and errors.
Content-Type: application/jsonroutes with a bodythe body is JSON.
Idempotency-KeyPOST /api/v1/notificationsthe same key never fires two campaigns. It beats the body’s idempotency_key, which in turn beats external_id.
X-Pm-PriorityPOST /api/v1/notificationstransactional (high) or marketing (normal). The body’s priority field, when present, beats the header. Unknown value ⇒ named 400.
X-Dry-Run: truePOST /api/v1/notificationsrehearsal: resolves the target and counts recipients, but persists nothing, delivers nothing and does not consume the idempotency key.
If-None-MatchGET /api/v1/inapp/pendingif the presented ETag still holds, the answer is 304 with no body.

Example: a rehearsal before firing for real

curl -X POST https://api.pushmesh.io/api/v1/notifications \
  -H "Authorization: Basic $PUSHMESH_KEY" \
  -H "Content-Type: application/json" \
  -H "X-Dry-Run: true" \
  -d '{
        "app_id": "0198e7c4-77a1-7bd2-b0f1-6b1d3f2a9c40",
        "included_segments": ["Subscribed Users"],
        "contents": { "en": "Rehearsal — nothing was delivered" }
      }'
{ "id": "", "recipients": 12840, "dry_run": true }

Headers the API emits

HeaderWhereWhat it carries
X-Pm-Request-Idevery response, success or errorUUIDv7 of the request. Same value as explain.request_id. Keep it: it correlates your call with the service’s trace.
X-Pm-LanePOST /api/v1/notificationsthe dispatch lane chosen: 0 = transactional, 1 = marketing.
X-Pm-Recipients-ExatoPOST /api/v1/notificationstrue when recipients is an exact count (identifier targeting); false when it is an estimate (segment targeting).
X-Idempotent-ReplayPOST /api/v1/notificationspresent and true only when the response is a byte-for-byte replay of an earlier call with the same Idempotency-Key.
ETagGET /api/v1/inapp/pendingfingerprint of the In-App package. Send it back in If-None-Match next session. The body repeats it in the etag field.
Retry-After429 on device routesseconds to wait (60). See the note in Limits.
X-Pm-Instanceresponses that pass through the edgeidentifier of the instance that served you — support information.

Reading a send result from the headers

HTTP/1.1 200 OK
X-Pm-Request-Id: 0198f0b2-6e31-7a4c-9f10-2c9a1d4e7b55
X-Pm-Lane: 0
X-Pm-Recipients-Exato: true
Content-Type: application/json

{"id":"0198e7c4-77a1-7bd2-b0f1-6b1d3f2a9c40","recipients":2,"external_id":"cmp-123"}

Repeat the call with the same Idempotency-Key and the same body and the body comes back identical, now with X-Idempotent-Replay: true — that is how you tell, in your own logs, a second campaign from a safe retry.


Headers on webhook deliveries

When PushMesh calls your endpoint, the request carries:

HeaderContent
Content-Typeapplication/json
X-PM-Eventodelivery.received, delivery.clicked, notification.completed or teste.ping
X-PM-Entrega-Ididentifier of this delivery. It is your deduplication key.
X-PM-Canalwebhook — which channel this confirmation went out on. The same value travels inside the signed body.
X-PM-Assinaturasha256=<hex(HMAC-SHA256(secret, body))>

Verification details in Webhooks.


CORS

The API answers with permissive CORS: any origin, any method, any header, max-age of 3600 seconds — and no credentials.

That is safe by construction here because no route authenticates via cookie: the device identifies itself with app_id + a per-IP ceiling, the API with a key in a header, the dashboard with a Bearer token. With no ambient authority in the browser there is no CSRF — the browser merely gains the ability to read what curl always could.

The practical consequence is serious: a pm_live_ key must never go into browser code or inside an APK/IPA. Open CORS is not permission to expose the key; it is permission to try the API from a documentation page. Authenticated calls belong on your server.