---
title: "Register a browser Web Push subscription"
method: POST
path: "/api/v1/push/subscribe"
tags: ["Push"]
---

# Register a browser Web Push subscription

`POST /api/v1/push/subscribe`

## Request

- Base URL: `https://api.uptimemonitoring.com`
- URL: `https://api.uptimemonitoring.com/api/v1/push/subscribe`
- Auth: HTTP bearer

Persists the browser's `PushSubscription` object so the dispatcher
can deliver notifications on monitor state transitions. The
`p256dh` and `auth` fields are write-only secrets used server-side
to encrypt push payloads — they are NEVER returned in the
response. The 64 KB body limit and the global per-account
200/min rate limiter both apply. Per-account cap is enforced
atomically: an account holds **exactly 20** active subscriptions
at steady state — concurrent subscribes never produce a 21st
row. The 21st insert evicts the oldest by `created_at` (FIFO).
The `endpoint` must be an `https://` URL with a public hostname;
private/loopback/link-local/mDNS endpoints are rejected with
`400 {"error":"endpoint must be HTTPS"}` (SSRF hardening).

## Request body

- PushSubscriptionRequest
  - `endpoint` string, uri, required: Push service endpoint URL from the browser's `PushSubscription`. Must be HTTPS with a non-empty host.
  - `p256dh` string, required: Base64url-encoded P-256 ECDH public key from the browser. Write-only — never returned in any response.
  - `auth` string, required: Base64url-encoded auth secret from the browser, used server-side to encrypt push payloads. Write-only — never returned in any response.
  - `user_agent` string, nullable: Optional client UA string for operator-facing UI.

## Response `201`

Subscription stored.

- PushSubscriptionResponse: Persisted push subscription as exposed via the REST API. The `p256dh` and `auth` write-only secrets are intentionally omitted to prevent exfiltration via a compromised dashboard token.
  - `id` integer
  - `endpoint` string, uri
  - `user_agent` string, nullable
  - `created_at` string, date-time

## Other responses

- `400`: Invalid request.
- `401`: Missing or invalid credentials.
- `403`: Account is currently suspended. On `POST /api/v1/monitors`, and on a `PUT /api/v1/monitors/{monitorID}` that changes `url` or `type` (issue #335), this can also be returned by the per-account port-scan heuristic when the triggering request crossed the port-fan threshold inside the sliding window (10 minutes by default; `SCAN_WINDOW`). A host-fan trip does NOT suspend the account — see the 429 response instead.
- `413`: Request body exceeds the 64 KB hard limit. Per docs/SPEC.md §Hard Limits the dispatcher rejects any payload larger than 65,536 bytes before it is decoded.
- `415`: The request `Content-Type` is not `application/json`. Write operations (POST, PUT, PATCH) require that header; a missing or non-JSON media type is rejected before the body is read. The `charset` parameter is accepted and ignored, so `application/json; charset=utf-8` is valid.
- `429`: Per-account dispatcher rate limit exceeded (200 requests/minute). The `Retry-After` header gives the integer seconds until at least one token has refilled. The `X-RateLimit-*` trio reflects the bucket whose rejection produced this response — usually the global 200/min cap; on `POST /api/v1/monitors` a 429 may instead reflect the per-route 20/min creation limit.

---

[API](https://skmtc.dev/uptimemonitoring/apis/uptimemonitoring-com-api.md) · [All operations](https://skmtc.dev/uptimemonitoring/apis/uptimemonitoring-com-api/llms.txt) · [OpenAPI document](https://skmtc.dev/uptimemonitoring/apis/uptimemonitoring-com-api/revisions/ddcb2a8bc70f?raw)
