REST API
The SDKs are thin wrappers over this API. Use it directly from any language.
Basics
Section titled “Basics”| Item | Value |
|---|---|
| Base URL | https://api.notific.me (the SDKs page of app.notific.me shows the address of your account) |
| Version | Every route starts with /v0 |
| Authentication | Authorization: Bearer nm_svc_… (a service token) |
| Format | JSON in and out, UTF-8, snake_case fields, RFC 3339 dates |
| Request id | Send X-Request-Id to correlate; every response returns one |
Errors
Section titled “Errors”Every error has the same envelope, with a stable code you can rely on:
{ "error": { "code": "validation_failed", "message": "evento inválido", "details": { "schema": "event", "errors": [{ "path": "/severity", "message": "…" }] } }, "request_id": "019a…"}| Status | Common codes |
|---|---|
| 400 | bad_request |
| 401 | unauthorized (missing, invalid, revoked or expired token; disabled service) |
| 403 | forbidden (missing scope), approval_required |
| 404 | not_found, hook_not_found |
| 409 | idempotency_conflict, conflict |
| 413 | payload_too_large |
| 422 | validation_failed (with details), idempotency_key_reused |
| 429 | rate_limited (with Retry-After) |
| 503 | unavailable |
The message is meant for logs; show your users a text based on the code.
Events
Section titled “Events”POST /v0/events
Section titled “POST /v0/events”Scope events:write. Body: an event.
POST /v0/events HTTP/1.1Authorization: Bearer nm_svc_...Content-Type: application/json
{ "id": "inv-42-failed", "source": "billing", "type": "invoice.failed", "severity": "high", "occurred_at": "2026-11-05T12:00:00Z", "to": ["jimmy"], "payload": { "invoice": 42 }, "content": { "summary": "Invoice #42 failed" }}HTTP/1.1 202 AcceptedContent-Type: application/json
{ "id": "019a4f2e-…", "status": "queued", "sender_id": "inv-42-failed", "received_at": "2026-11-05T12:00:00.120Z"}statusisqueued, orduplicate(withduplicate_of) when merged bydedup_key.- Sending the same
idagain returns the stored response withIdempotent-Replayed: true. The sameidwith a different body returns409 idempotency_conflict. - An unknown recipient in
toreturns422. - Limits: 1 MiB per body; 600 events per minute per token (burst 100).
Asks (question sets)
Section titled “Asks (question sets)”POST /v0/asks
Section titled “POST /v0/asks”Scope asks:write. Body: a question set. Send an Idempotency-Key header (up to 200 characters) so a retry never creates two question sets.
POST /v0/asks HTTP/1.1Authorization: Bearer nm_svc_...Content-Type: application/jsonIdempotency-Key: refund-881
{ "to": ["jimmy"], "title": "Refund order #881 (USD 240)?", "context": "The customer reported a duplicate charge.", "questions": [ { "id": "decision", "type": "single", "text": "What should we do?", "options": ["Refund", "Partial refund", "Deny"] }, { "id": "note", "type": "text", "text": "Note for the customer", "optional": true } ], "level": 2, "timeout": "30m", "on_timeout": { "decision": "Deny" }, "callback_url": "https://crm.example.com/hooks/notificme"}HTTP/1.1 201 CreatedContent-Type: application/json
{ "id": "019a4f30-…", "status": "open", "version": 1, "level": 2, "title": "Refund order #881 (USD 240)?", "created_at": "2026-11-05T12:00:00Z", "timeout_at": "2026-11-05T12:30:00Z", "recipients": ["jimmy@idie.ai"], "notification_id": "019a4f30-…", "content_hash": "…", "poll_url": "/v0/asks/019a4f30-…"}- Body up to 256 KiB. Invalid question set:
422 validation_failed. - The same
Idempotency-Keywith a different body:422 idempotency_key_reused. A concurrent request with the same key:409 conflict.
GET /v0/asks/{id}?wait=60
Section titled “GET /v0/asks/{id}?wait=60”Scope asks:read. Returns the same view. With wait (seconds, or a duration such as 30s, up to 60), the call long-polls: it returns as soon as the question set leaves open, or when the wait ends.
HTTP/1.1 200 OKContent-Type: application/json
{ "id": "019a4f30-…", "status": "answered", "version": 1, "level": 2, "title": "Refund order #881 (USD 240)?", "created_at": "2026-11-05T12:00:00Z", "timeout_at": "2026-11-05T12:30:00Z", "content_hash": "…", "poll_url": "/v0/asks/019a4f30-…", "reply": { "question_set_id": "019a4f30-…", "version": 1, "status": "answered", "answers": [ { "question_id": "decision", "question_text": "What should we do?", "value": "Refund" }, { "question_id": "note", "question_text": "Note for the customer", "free_text": "Sorry for the trouble!" } ], "as_text": "…", "answered_by": { "identity": "jimmy@idie.ai", "device_id": "019a…", "level_used": 2, "auth_method": "totp" }, "answered_at": "2026-11-05T12:03:10Z", "content_hash": "…", "signature": { "alg": "Ed25519", "key_id": "…", "value": "…" } }}status is open, answered, timeout, expired or cancelled. reply is present for answered and timeout.
POST /v0/hooks/{slug}
Section titled “POST /v0/hooks/{slug}”Scope events:write. Any body (JSON, form or text) from another system, turned into an event by the hook mapping. The token can be sent as ?token= for systems that only accept a URL.
curl -sS -X POST "https://api.notific.me/v0/hooks/dokploy?token=$NOTIFICME_TOKEN" \ -H "Content-Type: application/json" \ -d '{"type": "build-success", "applicationName": "api", "projectName": "idie"}'{ "status": "queued", "rule": "deploy-ok", "event": { "id": "019a…", "status": "queued", "received_at": "2026-11-05T12:00:00Z" }}statusisqueued,duplicateorignored(no rule matched,200).?dry_run=1returns{"status": "dry_run", "rule", "event"}with the generated event, without ingesting it.- A wrong HMAC signature:
401 invalid_signature. No mapping or preset for the slug:404 hook_not_found.
Managing mappings
Section titled “Managing mappings”With a token (events:write):
| Route | Description |
|---|---|
GET /v0/hooks |
Lists the hooks of the workspace (saved and presets). |
GET /v0/hooks/{slug}/mapping |
The YAML mapping, with the HMAC secret masked. |
PUT /v0/hooks/{slug}/mapping |
Saves a mapping (YAML or JSON). |
DELETE /v0/hooks/{slug}/mapping |
Deletes it; a preset with the same slug applies again. |
The app uses the workspace routes (/v0/workspaces/{handle}/hooks…, including a dry-run with sample body, headers and query); they are also available to tokens with workspace:read and hooks:write.
Webhook keys
Section titled “Webhook keys”GET /.well-known/notificme-keys.json
Section titled “GET /.well-known/notificme-keys.json”Public. The Ed25519 keys (JWKS) that sign webhooks and replies. The SDK downloads and caches them.
curl -sS "https://api.notific.me/.well-known/notificme-keys.json"Approvals (AI agents)
Section titled “Approvals (AI agents)”| Route | Description |
|---|---|
POST /v0/approvals |
Asks a person to approve a locked write (method, path, body, reason). |
GET /v0/approvals/{id}?wait=60 |
Approval status (long-poll up to 60 s). |
Then repeat the original request with Notificme-Approval: <id>. See AI agents and MCP.
Workspace administration
Section titled “Workspace administration”Everything the app does is also an API under /v0/workspaces/{handle}/…: services, tokens, rules and the rule simulator, hooks, webhooks (list and resend), events, notifications, questions, usage, members and the audit log. These routes accept a person’s session or a token with the matching scope (workspace:read, services:write, tokens:write, rules:write, hooks:write, webhooks:write, audit:read). The easiest way to use them from an AI agent is the MCP server.