Send push notifications (browser, Telegram, Slack) to everyone subscribed to a project — over REST or as an MCP tool.
Pointing an AI agent at this API?
Hand it the LLM-ready Markdown version — self-contained instructions an agent can follow with just a URL and an API key.
All endpoints live under `https://mcpnotify.com/api/v1`. `https://mcpnotify.com` is the origin you were given (scheme + host, e.g. `https://example.com`). Do not add a trailing slash.
Every endpoint requires an API key sent as a Bearer token. There are two kinds: - **Project keys** `mcpn_…` — for sending: `/api/v1/messages`, `/api/v1/projects` and the MCP server `/mcp` (= `/api/v1/mcp`). `Authorization: Bearer mcpn_your_key_here`. Create them on **API keys** in the dashboard; a key covers one project, several, or all of its owner's projects, may expire, and can be revoked. An unknown, expired or revoked key returns `401`. - **Account keys** `sk_…` — for the account endpoints (health, stats, users, files): `Authorization: Bearer sk_your_key_here`, created under Profile → API Keys. A missing or invalid key returns `401 { "error": "Invalid or missing API key" }`.
`POST https://mcpnotify.com/api/v1/messages` with `{"app":"<who is sending>","message":"<text>"}` and a `mcpn_` key. `app` and `message` are required; `title`, `url` and `project` are optional. Without `project` the message goes to every project the key covers. Every browser, Telegram chat and Slack channel subscribed to the project gets it (instantly, or bundled into a digest if that subscriber chose one).
`https://mcpnotify.com/mcp` is a Model Context Protocol server (Streamable HTTP transport, stateless, JSON responses) with two tools: `send_notification` (`app`, `message` required; `title`, `url`, `project`, `body` and files optional — `attachments: [{filename, text | content_base64, content_type}]` up to 50 MB each, or `attachment_ids` from `POST /api/v1/attachments`) and `list_projects`. Authenticate with the same `Authorization: Bearer mcpn_…` header. Claude Code: `claude mcp add --transport http mcpnotify https://mcpnotify.com/mcp --header "Authorization: Bearer mcpn_…"`. Other clients: `{"type":"http","url":"https://mcpnotify.com/mcp","headers":{"Authorization":"Bearer mcpn_…"}}`.
Responses are JSON unless noted (file download returns raw bytes). Request bodies are JSON (`Content-Type: application/json`) except file upload, which is `multipart/form-data`.
Requests are rate limited per API key. When you exceed a limit you get `429` (or `403` if the limit is configured to block) with an `error` message and, when applicable, a `Retry-After` header (seconds). Back off and retry.
Errors are JSON with an `error` string and a matching HTTP status (`400` bad input, `401` unauthenticated, `403` forbidden, `404` not found, `413` payload too large, `429` rate limited, `500` server error).
Your base URL is https://mcpnotify.com. Create API keys under Profile → API Keys.
/api/v1/messagesSends one message to every subscriber of the target project(s): browser push, Telegram and Slack. Delivery happens in the background; the response tells you how many subscribers it reached (`recipients`) and how many of those hold it for a digest (`queuedForDigest`).
| Name | In | Type | Req. | Description |
|---|---|---|---|---|
| app | body | string (1–100) | yes | Who is sending: your app, script, agent or service. Shown as the notification heading. |
| message | body | string (1–4000) | yes | The notification text. |
| title | body | string (≤200) | no | Heading to use instead of `app`. |
| url | body | string (http/https) | no | Opened when the notification is clicked. Default: the subscriber's settings page. |
| project | body | string | string[] | no | Project slug or id (or a list). Omit to send to every project the key covers. Unknown → 404. |
| body | body | string | object | array (≤ 1 MB) | no | Long payload — text, Markdown, logs or JSON (objects are pretty-printed). Not in the notification itself: clicking the notification opens a message page that shows it in full. |
| attachments | body | string[] (≤ 20) | no | Attachment ids from `POST /api/v1/attachments` (unused, uploaded by the same account, within 24 h). The message page previews/streams images, video, audio, PDF and text, and offers a download. |
Request
curl -X POST https://mcpnotify.com/api/v1/messages \
-H "Authorization: Bearer mcpn_your_key_here" \
-H "Content-Type: application/json" \
-d '{"app":"nightly-backup","message":"Backup finished in 4m12s","project":"ops"}'Response
{
"ok": true,
"recipients": 3,
"messages": [
{
"id": "k2j4h5g6f7d8s9",
"project": { "id": "p1a2b3c4d5", "slug": "ops", "name": "Ops" },
"recipients": 3,
"queuedForDigest": 1
}
]
}/api/v1/attachmentsUploads one file as the **raw request body** (streamed straight to storage — up to 2 GB) and returns an id. Pass the id in `attachments` of `POST /api/v1/messages` (or `attachment_ids` of the MCP tool) within 24 hours; unused uploads are deleted. Readers never see a storage credential: the message page mints a short-lived read link per file for preview, streaming and download.
| Name | In | Type | Req. | Description |
|---|---|---|---|---|
| filename | query | string | no | File name shown to readers (also accepted as `X-Filename` header). Characters outside `[A-Za-z0-9._-]` become `_`. |
| Content-Type | header | string | no | The file's MIME type (decides the preview: image/*, video/*, audio/*, application/pdf, text/*, application/json). Guessed from the extension when omitted or `application/octet-stream`. |
Request
curl -X POST "https://mcpnotify.com/api/v1/attachments?filename=screen-recording.mp4" \
-H "Authorization: Bearer mcpn_your_key_here" \
-H "Content-Type: video/mp4" \
--data-binary @screen-recording.mp4Response
{
"id": "att_3Fq9xT2mWpL8vZk1aB0c",
"filename": "screen-recording.mp4",
"contentType": "video/mp4",
"size": 48213377,
"sha256": "9c1e…",
"expiresAt": "2026-10-03T16:00:00.000Z"
}/api/v1/projectsReturns the projects covered by the calling project key — use a `slug` as the `project` field when sending.
Request
curl https://mcpnotify.com/api/v1/projects \
-H "Authorization: Bearer mcpn_your_key_here"Response
{
"projects": [
{ "id": "p1a2b3c4d5", "slug": "ops", "name": "Ops", "description": "Deploys and backups" }
],
"key": { "name": "CI", "prefix": "mcpn_Ab12Cd", "expiresAt": "2027-01-01T00:00:00.000Z" }
}/api/v1/mcpModel Context Protocol endpoint, Streamable HTTP transport, stateless (no session id), JSON responses. Supports `initialize`, `ping`, `tools/list` and `tools/call` (single messages or batches); notifications get `202`. Tools: `send_notification` {app, message, title?, url?, project?, body?, attachments?: [{filename, text | content_base64, content_type?}], attachment_ids?} and `list_projects` {}. As a shortcut, a plain `{"app":"…","message":"…"}` body (no `jsonrpc` field) sends directly, exactly like `POST /api/v1/messages`. `GET` and `DELETE` answer `405` (no server-initiated stream, no sessions).
| Name | In | Type | Req. | Description |
|---|---|---|---|---|
| jsonrpc | body | "2.0" | yes | JSON-RPC 2.0 envelope (`id`, `method`, `params`). |
| method | body | string | yes | `initialize`, `tools/list`, `tools/call`, `ping`. |
Request
curl -X POST https://mcpnotify.com/mcp \
-H "Authorization: Bearer mcpn_your_key_here" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"send_notification","arguments":{"app":"claude-code","message":"Refactor done, tests green"}}}'Response
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [{ "type": "text", "text": "Sent. Ops: 3 recipients (1 in digest)" }],
"structuredContent": { "ok": true, "recipients": 3, "messages": [ … ] }
}
}/api/v1/healthConfirms the API is up and your key is valid. Handy as a first call to verify credentials and connectivity.
Request
curl https://mcpnotify.com/api/v1/health \
-H "Authorization: Bearer sk_your_key_here"Response
{
"status": "healthy",
"timestamp": "2026-07-19T12:00:00.000Z",
"uptime": 1234.56,
"version": "1.0.0",
"apiKey": "My key",
"userId": "usr_...",
"message": "API is running successfully"
}/api/v1/statsReturns the calling user together with API-usage counters (requests today / this week / this month, error rate, API-key count).
Request
curl https://mcpnotify.com/api/v1/stats \
-H "Authorization: Bearer sk_your_key_here"Response
{
"user": { "id": "usr_...", "email": "you@example.com", "name": "You", "role": "user", "createdAt": "..." },
"apiStats": {
"totalApiKeys": 2,
"requestsToday": 14,
"requestsThisWeek": 98,
"requestsThisMonth": 412,
"errorRate": "1.20%",
"errorCount": 5
},
"meta": { "timestamp": "...", "apiKey": "My key" }
}/api/v1/usersLists users. A regular key returns only its own user record; an admin key returns all users with pagination.
| Name | In | Type | Req. | Description |
|---|---|---|---|---|
| limit | query | integer | no | Page size, 1–100 (default 10). Admin only; ignored for non-admins. |
| offset | query | integer | no | Rows to skip (default 0). Admin only. |
Request
curl "https://mcpnotify.com/api/v1/users?limit=20&offset=0" \
-H "Authorization: Bearer sk_your_key_here"Response
{
"users": [
{ "id": "usr_...", "email": "you@example.com", "name": "You", "role": "user", "emailVerified": null, "createdAt": "..." }
],
"meta": { "limit": 20, "offset": 0, "total": 1, "apiKey": "My key" }
}/api/v1/usersAdmin-only endpoint scaffold for creating a user. Ships as a stub in this starter — it validates input and echoes it back rather than persisting. Fill in real creation logic before relying on it.
| Name | In | Type | Req. | Description |
|---|---|---|---|---|
| body | string | yes | New user email. | |
| name | body | string | yes | New user display name. |
| role | body | string | no | 'user' (default) or 'admin'. |
Request
curl -X POST https://mcpnotify.com/api/v1/users \
-H "Authorization: Bearer sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{"email":"new@example.com","name":"New User","role":"user"}'Response
{
"message": "User creation endpoint - implementation needed",
"requestedData": { "email": "new@example.com", "name": "New User", "role": "user" },
"apiKey": "My key"
}/api/v1/filesUploads a file and stores its raw bytes. Use this instead of a form/Server Action for any real upload (Server Actions cap the body at ~1MB; this endpoint does not). Send `multipart/form-data` with a single `file` field.
| Name | In | Type | Req. | Description |
|---|---|---|---|---|
| file | form | file | yes | The file to upload (multipart field name must be "file"). |
Request
curl -X POST https://mcpnotify.com/api/v1/files \
-H "Authorization: Bearer sk_your_key_here" \
-F "file=@./photo.png"Response
{
"id": "fil_...",
"filename": "photo.png",
"url": "/api/v1/files/fil_..."
}/api/v1/files/:idStreams the raw file bytes with the stored Content-Type. Because it is Bearer-gated you cannot put it directly in an `<img src>`; fetch it with the token and build an object URL client-side.
| Name | In | Type | Req. | Description |
|---|---|---|---|---|
| id | path | string | yes | File id returned by the upload endpoint. |
Request
curl https://mcpnotify.com/api/v1/files/fil_your_file_id \
-H "Authorization: Bearer sk_your_key_here" \
--output downloaded-fileResponse
Raw binary body with the stored `Content-Type` and `Content-Disposition: inline; filename="..."`. Returns `404 { "error": "File not found" }` if unknown./api/v1/files/:idDeletes a file owned by the calling key.
| Name | In | Type | Req. | Description |
|---|---|---|---|---|
| id | path | string | yes | File id to delete. |
Request
curl -X DELETE https://mcpnotify.com/api/v1/files/fil_your_file_id \
-H "Authorization: Bearer sk_your_key_here"Response
{ "deleted": true } // { "deleted": false } with status 404 if not found / not owned