API V1 · PRIVATE BETA
Pliing API
Base-URL: https://api.pliing.app/v1. JSON in and out, with a Bearer token in the Authorization header.
Private beta. The API may change during testing. This is not a public production release.
Try your own notification
An ID alone is not enough. Use a API key from the app or console. Your key is sent directly to the Pliing API and is not stored by the playground.
The playground sends a real notification. The recipient must approve the project. App-created keys are locked to your own ID.
Show JSON
API response
Recipient and access are separate
PliingID is the recipient's address, for example pliing-7m3k9-xr2n8-q4v6. It is not a secret and does not grant permission to send. The API key authenticates the integration and grants access to one project only. The recipient must approve the project before sending.
The ID format is pliing-xxxxx-xxxxx-xxxx, and the recipient copies it from PliingID in the app. Do not use an Apple ID, email address or APNs device token as to.
Connect another service
For services sending notifications to their own users. Create an organization and API keys in the console at console.pliing.li. Keys require an active monthly subscription.
- The user selects Pliing in your service's notification settings and pastes their PliingID.
- Your backend creates a consent request using its own API key. Never ask for the user's API key.
- Display “Waiting for approval” in your service. The user opens Pliing → Senders and approves the request.
- Your service checks
GET /v1/consent-requests/{id}using its API key. Only display “Connected” when the status isapprovedandnotificationsistrue. - Send to the user's ID. If you later receive
consent_required, stop sending and indicate that the connection needs approval.
A service can only read the status of its own requests. The status revoked does not grant permission to send; a new POST does not approve the sender again. Codes require separate permission.
Your own scripts
Create a project and API key in the Pliing app. App-created keys are locked to your own PliingID and cannot send to others. Your ID goes in to; the key is secret and goes in Authorization.
Send a notification
Use a API key with notifications:send. Keep your key on your server, out of client code and version control.
curl https://api.pliing.app/v1/notifications \
-H "Authorization: Bearer $PLIING_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: deploy-prod-1842" \
-d '{
"to": "pliing-7m3k9-xr2n8-q4v6",
"title": "Deploy complete",
"body": "main → production. Everything is running.",
"level": "info"
}'title: at most 160 characters. body: at most 4000 characters. level: info, important or critical; the default is info. These are display levels and do not grant Apple's special Critical Alerts entitlement.
Sound
The optional field sound selects the notification sound on the recipient's devices. Omitted or default uses the system sound. An unknown value returns HTTP 422.
| Value | Sound |
|---|---|
default | System sound (default) |
pling | Short, bright tone |
chime | Two rising tones |
drop | Soft, low drop |
pulse | Two quick beats |
alert | Three distinct descending tones |
curl https://api.pliing.app/v1/notifications \
-H "Authorization: Bearer $PLIING_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: deploy-prod-1843" \
-d '{"to": "pliing-7m3k9-xr2n8-q4v6", "title": "Deploy failed", "body": "main → production", "level": "important", "sound": "alert"}'Sound belongs to the notification and is included in idempotency checks: the same key with a different sound returns HTTP 409. One-time codes always use the system sound. The user's Do Not Disturb, Focus and iOS sound settings still apply.
Icon
The optional field icon_url gives the notification an icon in the recipient's inbox, for example your service's logo. The address must be a public HTTPS URL pointing straight at an image of exactly 128 × 128 pixels, in PNG, JPEG or WebP, at most 64 KB. Redirects are not followed.
Pliing fetches the image once when the notification is sent, validates it and stores a copy. The recipient's device loads the icon from Pliing and not from your server, so you cannot see when or where a notification is opened. The same URL is fetched again only after 24 hours, so use a new URL when you change the icon. The icon appears in the inbox and the reading view. It is not sent in the push notification and is not used for one-time codes.
curl https://api.pliing.app/v1/notifications \
-H "Authorization: Bearer $PLIING_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: deploy-prod-1844" \
-d '{"to": "pliing-7m3k9-xr2n8-q4v6", "title": "Deploy complete", "body": "main → production", "icon_url": "https://example.com/pliing-icon.png"}'If the icon cannot be fetched or accepted, the whole request is rejected with HTTP 422, and message is one of these codes: icon_unavailable (could not be fetched, is too large or is not a public HTTPS URL), icon_invalid_format (not PNG, JPEG or WebP) or icon_invalid_size (not 128 × 128 pixels). The icon is part of the idempotency check: the same key with a different icon returns HTTP 409.
Actions
The optional field actions adds up to three buttons to the notification. Each button has title (1–30 characters, no line breaks), url (HTTPS, at most 2048 characters) and type.
trigger(default): when the recipient taps the button, Pliing calls your URL with a POST from our servers. The URL must be public. The recipient's IP address and account are not revealed, and the URL is never sent to the device, so you can put a secret token in it.open: the button opens the URL in the recipient's browser.
curl https://api.pliing.app/v1/notifications \
-H "Authorization: Bearer $PLIING_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: deploy-prod-1845" \
-d '{"to": "pliing-7m3k9-xr2n8-q4v6", "title": "Deploy venter", "body": "Godkjenn utrullingen til production.", "actions": [
{"title": "Godkjenn", "url": "https://ci.example.com/hooks/approve/9f3a"},
{"title": "Avvis", "url": "https://ci.example.com/hooks/deny/9f3a", "type": "trigger"},
{"title": "Se logg", "url": "https://ci.example.com/runs/1845", "type": "open"}
]}'The request to a trigger URL is JSON with event (pliing.action), notification_id, idempotency_key (the key you sent the notification with), action (0, 1 or 2), action_title and pressed_at. The header Idempotency-Key: pliing-action-{notification_id}-{action} is the same on retries. Reply with a 2xx response within 8 seconds. Redirects are not followed and the response is not read.
A button that has succeeded cannot be pressed again for that notification. If the URL answers with anything other than 2xx, or too late, the recipient gets an error and can try again. Use single-use or expiring URLs for actions that approve something, because buttons stay in the inbox for 90 days. Buttons only work while the recipient has approved the sender, and do not apply to one-time codes.
Invalid actions return HTTP 422, with the field in errors, for example actions.0.url. Actions are part of the idempotency check. Buttons appear in the app when the notification is opened. Buttons directly in the push notification on the lock screen are not supported yet.
HTTP 202
{"id":1842,"status":"accepted","duplicate":false}Idempotency-Key is required: 8–128 characters, letters, digits or _.:-. The same key and content return the same ID with HTTP 200. The same key with different content returns HTTP 409. The key is scoped to the project.
AI notifications with MCP
Connect your assistant to send progress, ask questions with action buttons and retrieve the recipient’s answer. Use a dedicated key bound to one project and one approved recipient.
MCP setup and complete examples →
Send a one-time code
Use the dedicated endpoint POST /v1/codes. The recipient must approve codes separately under Senders; regular notification permission is not enough. Request codes: true when your integration requests consent.
Does the sender already have notification permission? Send another consent request with codes enabled. Existing notification permission remains active, but the recipient must open the sender under Senders and enable One-time codes. Wait for both approved status and code permission before sending; a previous rejection is not overridden.
curl https://api.pliing.app/v1/codes \
-H "Authorization: Bearer $PLIING_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: login-code-001" \
-d '{"to":"pliing-7m3k9-xr2n8-q4v6","title":"Sign-in code","code":"123456","ttl_seconds":300}'code: 3–64 characters. ttl_seconds: 30–600 seconds, default 300. The same event key and content return the same code notification without duplicates.
Code notifications have a dedicated locked view in the app, with expiry and a copy button after device authentication. The code is stored encrypted and never sent in the push payload or ordinary inbox response. It cannot be retrieved after expiry.
Pliing delivers the code. The issuing service must validate it and ensure it can only be used once.
Ask permission before sending
curl https://api.pliing.app/v1/consent-requests \
-H "Authorization: Bearer $PLIING_TOKEN" \
-H "Content-Type: application/json" \
-d '{"to":"pliing-7m3k9-xr2n8-q4v6","notifications":true,"codes":false}'
HTTP 201
{"id":42,"status":"pending"}The recipient approves under Senders in the app. Before approval and after revocation, new deliveries are rejected with HTTP 403 and {"message":"consent_required"}. A new request does not override a refusal.
curl https://api.pliing.app/v1/consent-requests/42 \
-H "Authorization: Bearer $PLIING_TOKEN"
HTTP 200
{"id":42,"status":"approved","notifications":true,"codes":false,
"requested_notifications":true,"requested_codes":false}Poll the status every few seconds while the user approves. Respect HTTP 429. A service without Pliing support cannot use the ID alone; it must integrate this API.
Endpoints
| Method | Path | Description |
|---|---|---|
| GET | /v1/health | API status (public) |
| POST | /v1/notifications | Send a notification |
| POST | /v1/codes | Send a one-time code (requires separate permission) |
| POST | /v1/consent-requests | Ask a user for permission to send |
| GET | /v1/consent-requests/{id} | Check whether a request is approved |
All endpoints except /v1/health require a API key in Authorization. The key can only send; it does not grant access to the recipient's inbox, one-time codes or other senders.
What does delivered mean?
Accepted: The API has stored the notification. APNs accepted: Apple has accepted the push. Read: The recipient marked the content as read in the app. The APNs receipt does not confirm that a banner was actually displayed or read.
The default push is a generic notice about new content. The recipient decides whether the title and message can appear on the lock screen, and can disable push per device. One-time codes are never included in the push payload. The in-app inbox is always available.
Errors and limits
| 401 | Missing, expired or revoked token. |
| 402 | subscription_required: the key belongs to an account without an active subscription. |
| 403 | Incorrect key type, missing consent, or personal_project_self_only (the key is locked to the owner's own ID). |
| 404 | The resource does not exist or belongs to another account. |
| 409 | The idempotency key has been used with different content. |
| 422 | Invalid input. Field errors are in errors. |
| 429 | Too many requests. Respect Retry-After. |
Sending is rate limited per key. On 429, wait according to Retry-After. On network errors and temporary server errors, retry with backoff and the same idempotency key.