MCPNotify API & MCP server

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.

Open /docs/llm/api.md

Getting started

Base URL

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.

Authentication

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" }`.

Sending a notification (the 10-second version)

`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).

MCP

`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_…"}}`.

Content type

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`.

Rate limiting

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

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.

Endpoints

POST
/api/v1/messages

Send a notification

Sends 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`).

Auth: Bearer tokenAccess: A project key (`mcpn_…`) that covers the target project. Rate limited per key (default 120/minute).
NameInTypeReq.Description
appbodystring (1–100)yesWho is sending: your app, script, agent or service. Shown as the notification heading.
messagebodystring (1–4000)yesThe notification text.
titlebodystring (≤200)noHeading to use instead of `app`.
urlbodystring (http/https)noOpened when the notification is clicked. Default: the subscriber's settings page.
projectbodystring | string[]noProject slug or id (or a list). Omit to send to every project the key covers. Unknown → 404.
bodybodystring | object | array (≤ 1 MB)noLong 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.
attachmentsbodystring[] (≤ 20)noAttachment 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
    }
  ]
}
  • Status `201` on success. `400` for a missing/invalid field (the `error` names it), `401` for a bad key, `404` for an unknown `project`, `429` when rate limited.
  • One message row is created per target project, so a key covering three projects without `project` produces three entries in `messages`.
  • With `body` or `attachments`, each entry has a `viewUrl` (the message page; also what the notification opens — it links on to your `url`) and an `attachments` count. Notifications with files get a 📎 in front of the text.
  • **Multipart:** instead of JSON you may send `multipart/form-data` with the same fields (`app`, `message`, `title`, `url`, `project`, `body`, `attachments`) plus file parts — every file part becomes an attachment (≤ 50 MB each; use `POST /api/v1/attachments` for bigger files). `curl -F app=ci -F message=done -F files=@report.pdf …`
  • Attachments are kept 30 days, then deleted.
POST
/api/v1/attachments

Upload a (large) attachment

Uploads 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.

Auth: Bearer tokenAccess: A project key (`mcpn_…`). Counts against the send rate limit.
NameInTypeReq.Description
filenamequerystringnoFile name shown to readers (also accepted as `X-Filename` header). Characters outside `[A-Za-z0-9._-]` become `_`.
Content-TypeheaderstringnoThe 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.mp4

Response

{
  "id": "att_3Fq9xT2mWpL8vZk1aB0c",
  "filename": "screen-recording.mp4",
  "contentType": "video/mp4",
  "size": 48213377,
  "sha256": "9c1e…",
  "expiresAt": "2026-10-03T16:00:00.000Z"
}
  • Then: `curl -X POST {BASE_URL}/api/v1/messages -H "Authorization: Bearer mcpn_…" -H "Content-Type: application/json" -d '{"app":"qa","message":"Repro video attached","attachments":["att_3Fq9…"]}'`
  • `413` above the size limit, `501` when the server has no storage configured. Do not send multipart here — send the bytes as the body.
GET
/api/v1/projects

List the projects a key can send to

Returns the projects covered by the calling project key — use a `slug` as the `project` field when sending.

Auth: Bearer tokenAccess: A project key (`mcpn_…`).

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" }
}
POST
/api/v1/mcp

MCP server (also at /mcp)

Model 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).

Auth: Bearer tokenAccess: A project key (`mcpn_…`).
NameInTypeReq.Description
jsonrpcbody"2.0"yesJSON-RPC 2.0 envelope (`id`, `method`, `params`).
methodbodystringyes`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": [ … ] }
  }
}
  • Claude Code: `claude mcp add --transport http mcpnotify {BASE_URL}/mcp --header "Authorization: Bearer mcpn_…"`.
  • A failed send is a tool result with `isError: true` and the reason in `content[0].text` (bad input, unknown project, rate limit).
GET
/api/v1/health

Health check

Confirms the API is up and your key is valid. Handy as a first call to verify credentials and connectivity.

Auth: Bearer tokenAccess: Any valid API key.

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"
}
GET
/api/v1/stats

Account & API usage stats

Returns the calling user together with API-usage counters (requests today / this week / this month, error rate, API-key count).

Auth: Bearer tokenAccess: Any valid API key (scoped to the key owner).

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" }
}
GET
/api/v1/users

List users

Lists users. A regular key returns only its own user record; an admin key returns all users with pagination.

Auth: Bearer tokenAccess: Any valid API key (admin keys see all users; others see themselves).
NameInTypeReq.Description
limitqueryintegernoPage size, 1–100 (default 10). Admin only; ignored for non-admins.
offsetqueryintegernoRows 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" }
}
POST
/api/v1/users

Create user (scaffold)

Admin-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.

Auth: Bearer tokenAccess: Admin API keys only (others get 403).
NameInTypeReq.Description
emailbodystringyesNew user email.
namebodystringyesNew user display name.
rolebodystringno'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"
}
  • This is a template stub — no user is actually created yet.
POST
/api/v1/files

Upload a file

Uploads 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.

Auth: Bearer tokenAccess: Any valid API key (the file is owned by the key owner).
NameInTypeReq.Description
fileformfileyesThe 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_..."
}
  • Default max size is 100MB (configurable via MAX_FILE_SIZE). Oversized uploads return 413.
  • The returned `url` is the Bearer-gated download endpoint below.
GET
/api/v1/files/:id

Download / preview a file

Streams 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.

Auth: Bearer tokenAccess: Any valid API key.
NameInTypeReq.Description
idpathstringyesFile 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-file

Response

Raw binary body with the stored `Content-Type` and `Content-Disposition: inline; filename="..."`. Returns `404 { "error": "File not found" }` if unknown.
DELETE
/api/v1/files/:id

Delete a file

Deletes a file owned by the calling key.

Auth: Bearer tokenAccess: Any valid API key (only the owner may delete).
NameInTypeReq.Description
idpathstringyesFile 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
API Documentation · MCPNotify