# Register explicit storefront notification permission (/api-reference/survey-channels/registerSurveyPushSubscription)

`POST /v1/retention/push-subscriptions`

Base URL: `https://api.bitelio.com`

Merchant backend only, retention:view and retention:edit. Authenticate your customer and derive contactId on the server; never trust a browser-supplied contactId. Requires explicit permission, an allowed exact HTTPS origin, and a browser subscription to a supported push service. The endpoint and keys are encrypted. Maximum10 active subscriptions/contact. Existing endpoints cannot be transferred between customers. Limit120 requests/minute shared perproject. Scope defaults to SURVEY. Only an explicit new customer opt-in may pass RETENTION, permitting help and offers in addition to surveys. Existing subscriptions retain SURVEY scope until a new consent is registered. Optional timeZone is the IANA zone reported by the authenticated customer device, for example America/New_York; never infer it from IP or country. It adjusts configured contact hours for 90 days, then falls back to the store zone. It does not expand consent or create a schedule.

## Request body

- `contactId`: string (required)
- `origin`: string (uri) (required)
- `subscription`: object (required)
  - `endpoint`: string (uri) (required)
  - `expirationTime`: integer | null
  - `keys`: object (required)
    - `p256dh`: string (required)
    - `auth`: string (required)
- `consent`: object (required)
- `scope`: enum ("SURVEY" | "RETENTION")
- `timeZone`: string — Optional device-declared IANA time zone, captured only after explicit permission and tenant/customer verification.

Example:

```json
{
  "contactId": "string",
  "origin": "https://example.com",
  "subscription": {
    "endpoint": "https://example.com",
    "keys": {
      "p256dh": "string",
      "auth": "string"
    }
  },
  "consent": null
}
```

## Responses

### `200` — Success

- `id`: string (required)
- `status`: string (required)

```json
{
  "id": "string",
  "status": "string"
}
```

### `400` — Invalid input or unavailable channel

### `401` — Secret API key required

### `403` — Required project permission is missing

### `429` — Project rate limit exceeded. Retry-After identifies the wait in seconds.

### `503` — Rate limiter temporarily unavailable; retry later.

## Example request

```bash
curl -X POST 'https://api.bitelio.com/v1/retention/push-subscriptions' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"contactId":"string","origin":"https://example.com","subscription":{"endpoint":"https://example.com","keys":{"p256dh":"string","auth":"string"}},"consent":null}'
```
