Skip to content

REST API

The SDKs are thin wrappers over this API. Use it directly from any language.

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

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.

Scope events:write. Body: an event.

POST /v0/events HTTP/1.1
Authorization: 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 Accepted
Content-Type: application/json
{
"id": "019a4f2e-…",
"status": "queued",
"sender_id": "inv-42-failed",
"received_at": "2026-11-05T12:00:00.120Z"
}
  • status is queued, or duplicate (with duplicate_of) when merged by dedup_key.
  • Sending the same id again returns the stored response with Idempotent-Replayed: true. The same id with a different body returns 409 idempotency_conflict.
  • An unknown recipient in to returns 422.
  • Limits: 1 MiB per body; 600 events per minute per token (burst 100).

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.1
Authorization: Bearer nm_svc_...
Content-Type: application/json
Idempotency-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 Created
Content-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-Key with a different body: 422 idempotency_key_reused. A concurrent request with the same key: 409 conflict.

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 OK
Content-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.

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.

Terminal window
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" }
}
  • status is queued, duplicate or ignored (no rule matched, 200).
  • ?dry_run=1 returns {"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.

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.

Public. The Ed25519 keys (JWKS) that sign webhooks and replies. The SDK downloads and caches them.

Terminal window
curl -sS "https://api.notific.me/.well-known/notificme-keys.json"
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.

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.