Skip to content

API reference / Hook endpoints

Hook endpoints

The four hook endpoints, their bodies and their responses.

Every hook endpoint sends one notification to the hook's recipient as a live send. How each shape is turned into a title, body, link and priority is explained in Hooks. This page lists the requests and responses.

Plain text#

POST/v1/hooks/{token}
Auth The hook token in the path (hk_…). No Authorization header.

Headers#

Title
stringoptional
The title. Without it, the first non-empty line of the body is the title.
Rules
Also X-Title or t
Priority
stringoptional
ntfy-style priority. See the mapping.
Rules
1–5, min, low, default, high, max, urgent; also X-Priority or p
Default
high
Tags
stringoptional
Passed to the notification as data.tags.
Rules
Comma-separated, first 10 kept; also X-Tags or ta
Click
stringoptional
Opened when the notification is clicked.
Rules
https, up to 1,024 characters; also X-Click
Idempotency-Key
stringoptional
Makes retries safe. See Idempotency.
Rules
1–255 characters

Body#

Plain text, read whatever the Content-Type is. It may be empty: without a Title header the title is then Notification.

⫻

curl

curl https://api.meslzy.com/push/v1/hooks/hk_9Qm2Xv7Lc4Tz8Rb1Nw6Ks3Jd5Yf0Hg2P \  -H "Title: Backup finished" \  -H "Priority: 4" \  -H "Idempotency-Key: backup-2026-10-05" \  -d "db-1 was copied to cold storage in 4 minutes."

Response#

The same result as Send a notification: 202 Accepted for a new notification, 200 OK for a replay of the same Idempotency-Key.

⫻

202 Accepted

HTTP/1.1 202 AcceptedContent-Type: application/jsonX-Request-Id: req_0199b1c27a4e7c3d9f1e2b6a8d4c5e11
{  "id": "0199b1c2-7a4e-7c3d-9f1e-2b6a8d4c5e10",  "status": "queued",  "attempts": 2,  "mode": "live",  "sendAt": null}

Discord#

POST/v1/hooks/{token}/discord
Auth The hook token in the path (hk_…). No Authorization header.

Body#

Discord's webhook JSON, or multipart/form-data with a payload_json field (or content and username fields). These fields are read:

content
stringoptional
Message text. Its first line is the title when nothing else gives one.
Rules
Required when there is no embed
username
stringoptional
Used as the title when the embed has no title or author.
avatar_url
stringoptional
Becomes the notification's icon.
Rules
https only
embeds[0].title
stringoptional
The title, first choice.
embeds[0].author.name
stringoptional
The title when the embed has none.
embeds[0].description
stringoptional
Added to the body.
embeds[0].fields
object[]optional
Each field becomes a name: value line in the body.
Rules
name and value
embeds[0].footer.text
stringoptional
The last line of the body.
embeds[0].url
stringoptional
Becomes the notification's url.
Rules
https only
embeds[0].image.url
stringoptional
Becomes the notification's image.
Rules
https only

Query#

wait
booleanoptional
As on Discord: without it the answer is an empty 204.
Rules
true to get a message object back
Default
false

⫻

curl

curl "https://api.meslzy.com/push/v1/hooks/hk_9Qm2Xv7Lc4Tz8Rb1Nw6Ks3Jd5Yf0Hg2P/discord?wait=true" \  -H "Content-Type: application/json" \  -d '{ "content": "**api is down**\n3 checks failed in a row" }'

Response#

Discord-style, so tools that check the answer are satisfied. A replay of the same Idempotency-Key gets the same answer as a new send. With wait=true, id is the Push notification id and content echoes the request's content.

HTTP/1.1 204 No ContentX-Request-Id: req_0199b1c27a4e7c3d9f1e2b6a8d4c5e11

Errors#

Errors use Discord's shape instead of Push's:

StatusBodyWhen
401{"message": "Invalid Webhook Token", "code": 50027}Unknown or revoked token.
429{"message": "You are being rate limited.", "retry_after": 12, "global": false}Over a rate limit. The Retry-After header carries the same seconds.
400{"message": "…", "code": 50035}The notification is invalid, or a header such as Idempotency-Key is.
400{"message": "The request body contains invalid JSON.", "code": 50109}The body is not JSON, or not a Discord message.
400{"message": "Cannot send an empty message", "code": 50006}No content and no embed.
400{"message": "…", "code": 0}Any other Push error, such as no connected browsers, no credits, a suspended project or an idempotency conflict. message says which.
500{"message": "500: Internal Server Error", "code": 0}Something failed on Push's side.
HTTP/1.1 401 UnauthorizedContent-Type: application/jsonX-Request-Id: req_0199b1c27a4e7c3d9f1e2b6a8d4c5e11
{  "message": "Invalid Webhook Token",  "code": 50027}

Slack#

POST/v1/hooks/{token}/slack
Auth The hook token in the path (hk_…). No Authorization header.

Body#

Slack's incoming-webhook JSON, or application/x-www-form-urlencoded with the JSON in a payload field. These fields are read:

text
stringoptional
Message text in mrkdwn. Its first line is the title when nothing else gives one.
Rules
Required when there are no blocks and no attachment
blocks
object[]optional
A header block's text is the title. Each section block's text.text and fields[].text are added to the body.
Rules
header and section blocks are read
attachments[0].title
stringoptional
The title when there is no header block.
attachments[0].title_link
stringoptional
Becomes the notification's url.
Rules
https only
attachments[0].pretext
stringoptional
Added to the body.
attachments[0].text
stringoptional
Added to the body.
attachments[0].fields
object[]optional
Each field becomes a title: value line in the body.
Rules
title and value

⫻

curl

curl https://api.meslzy.com/push/v1/hooks/hk_9Qm2Xv7Lc4Tz8Rb1Nw6Ks3Jd5Yf0Hg2P/slack \  -H "Content-Type: application/json" \  -d '{ "text": "*Deploy finished*\napi@4f2c1a is live. <https://example.com/deploys/4f2c1a|See the deploy>" }'

Response#

Slack-style plain text: 200 with ok for a new send and for a replay. Errors are plain text too:

HTTP/1.1 200 OKContent-Type: text/plain; charset=utf-8X-Request-Id: req_0199b1c27a4e7c3d9f1e2b6a8d4c5e11
ok
StatusBodyWhen
403invalid_tokenUnknown or revoked token.
429rate_limitedOver a rate limit. Retry-After says how many seconds to wait.
400invalid_payloadThe body is not valid JSON or not a Slack message, or the notification is invalid.
400no_textNo text, no blocks and no attachment.
400invalid_recipient, quota_exceeded, …Any other Push error, as its code in lower case.
500internal_errorSomething failed on Push's side.

Apprise#

POST/v1/hooks/{token}/apprise
Auth The hook token in the path (hk_…). No Authorization header.

Body#

title
stringoptional
The title. Without it, the first line of the text is the title.
body
stringoptional
The text.
message
stringoptional
The text when there is no body. Apprise's json:// service sends this field.
type
stringoptional
Sets the priority: normal, normal, high or urgent. Without it, high.
Rules
info, success, warning or failure

⫻

curl

curl https://api.meslzy.com/push/v1/hooks/hk_9Qm2Xv7Lc4Tz8Rb1Nw6Ks3Jd5Yf0Hg2P/apprise \  -H "Content-Type: application/json" \  -d '{ "version": "1.0", "title": "Nightly job failed", "message": "Exit code 2", "type": "failure" }'

Response#

The same result as Send a notification: 202 Accepted, or 200 OK for a replay.

Errors#

The plain text and Apprise endpoints answer with Push's error format:

CodeHTTPWhen
UNAUTHORIZED401The token is malformed, unknown or revoked.
PROJECT_SUSPENDED403The hook's project is suspended.
RATE_LIMITED429Over the hook's 60 requests per minute, the project's or the account's limit, or the per-IP limit for invalid tokens. Wait for Retry-After seconds.
VALIDATION_ERROR400The body is over 1 MiB, the Apprise body is not valid JSON or has an unknown type, Idempotency-Key is not valid, or the extracted notification breaks a field rule (a Click that is not https, for example) or the size limit.
IDEMPOTENCY_CONFLICT409The Idempotency-Key was already used with a different notification.
QUOTA_EXCEEDED402The free allowance is used up and the credit balance cannot cover this send.
INVALID_RECIPIENT422The hook's recipient has no connected browsers.
INTERNAL_ERROR500Something failed on Push's side. Safe to retry with the same Idempotency-Key.
  • Hook endpoints accept requests from any origin, so a browser can call them; the Title, Priority, Tags, Click and Idempotency-Key headers are allowed across origins.
  • The order of checks is: the token, the project, the rate limits, Idempotency-Key, the body, then the same checks as an API send.