API quickstart
From an agent token to a draft notification ready for the lawyer to confirm, with curl and TypeScript against the /v1 API.
Updated:
This guide walks the whole flow against https://www.notificado.co/v1: create the case, add a party, upload the auto admisorio, prepare the notification and request its send. The send itself is always confirmed by a lawyer in the panel: no token can send.
| Step | Operation | Scope |
|---|---|---|
| 1 | POST /v1/create-case |
cases:write |
| 2 | POST /v1/add-party |
cases:write |
| 3 | POST /v1/request-upload, PUT the bytes, POST /v1/confirm-upload |
cases:write |
| 4 | POST /v1/create-notification |
notifications:write |
| 5 | POST /v1/attach-document |
notifications:write |
| 6 | Recipients, with the art. 8 sworn statement | the lawyer, in the panel |
| 7 | POST /v1/send-notification → awaiting_confirmation |
notifications:write |
| 8 | Confirm the send (confirmSend) |
the lawyer, in the panel |
| 9 | GET /v1/get-notification, GET /v1/get-case |
notifications:read, cases:read |
| 10 | Lists, one page at a time: GET /v1/notification-list |
notifications:read |
0. Token and firm
Issue a token under Account → API (/cuenta/api) with cases:write and notifications:write, and keep it in an environment variable, never in code:
export NOTIFICADO_TOKEN='ult_prod_…'Several operations take orgId, your firm's id. The same token tells you which it is through the MCP server's whoami tool:
curl -X POST 'https://www.notificado.co/mcp' \
-H "authorization: Bearer $NOTIFICADO_TOKEN" \
-H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"whoami","arguments":{}}}'In TypeScript (Bun, Node 18+ or Deno), one helper for every call below. It sends a fresh Idempotency-Key each time (operations that are not creates ignore it); see Idempotent retries for when to reuse one.
const BASE = 'https://www.notificado.co/v1';
const token = process.env.NOTIFICADO_TOKEN;
const orgId = '<orgId>'; // from whoami
async function call(path: string, body: unknown, key = crypto.randomUUID()) {
const response = await fetch(`${BASE}/${path}`, {
method: 'POST',
headers: {
authorization: `Bearer ${token}`,
'content-type': 'application/json',
'idempotency-key': key,
},
body: JSON.stringify(body),
});
const result = await response.json();
if (!response.ok) throw new Error(`${result.code}: ${result.cause}. Fix: ${result.fix}`);
return result;
}1. Create the case
radicado is the 23-digit number as printed on the auto; dashes and spaces are allowed. If the firm already has it, the answer is X_CASE_RADICADO_TAKEN: use the existing case.
curl -X POST 'https://www.notificado.co/v1/create-case' \
-H "authorization: Bearer $NOTIFICADO_TOKEN" \
-H "idempotency-key: $(uuidgen)" \
-H 'content-type: application/json' \
-d '{"radicado":"05001310300320260012300","juzgado":"Juzgado 3 Civil del Circuito de Medellín","juzgadoEmail":"j03cctomed@cendoj.ramajudicial.gov.co","ciudad":"Medellín","especialidad":"Civil","claseProceso":"Verbal","cliente":"Inversiones Andinas S.A.S."}'const legalCase = await call('create-case', {
radicado: '05001310300320260012300',
juzgado: 'Juzgado 3 Civil del Circuito de Medellín',
juzgadoEmail: 'j03cctomed@cendoj.ramajudicial.gov.co',
ciudad: 'Medellín',
especialidad: 'Civil',
claseProceso: 'Verbal',
cliente: 'Inversiones Andinas S.A.S.',
});2. Add a party
A party is not a recipient: who is notified, and at which address, is sworn by the lawyer in the panel.
curl -X POST 'https://www.notificado.co/v1/add-party' \
-H "authorization: Bearer $NOTIFICADO_TOKEN" \
-H "idempotency-key: $(uuidgen)" \
-H 'content-type: application/json' \
-d '{"caseId":"<caseId>","role":"demandado","name":"Comercializadora del Valle S.A.S.","idNumber":"900123456-7"}'await call('add-party', {
caseId: legalCase.id,
role: 'demandado',
name: 'Comercializadora del Valle S.A.S.',
idNumber: '900123456-7',
});3. Upload the document
Three calls, because file bytes never travel inside an API call (1 MB limit): ask for a grant, PUT the bytes to the url it answers (with exactly the contentType it answers, before expiresAt), and confirm. The confirmation re-reads the stored bytes, checks their real type (PDF, PNG or JPEG) and computes their SHA-256.
curl -X POST 'https://www.notificado.co/v1/request-upload' \
-H "authorization: Bearer $NOTIFICADO_TOKEN" \
-H "idempotency-key: $(uuidgen)" \
-H 'content-type: application/json' \
-d '{"kind":"otro","filename":"auto-admisorio.pdf","contentType":"application/pdf","size":48213}'
# If the answer's url is a path (/_storage/…), prefix https://www.notificado.co
curl -X PUT '<url>' \
-H 'content-type: application/pdf' \
--data-binary @auto-admisorio.pdf
curl -X POST 'https://www.notificado.co/v1/confirm-upload' \
-H "authorization: Bearer $NOTIFICADO_TOKEN" \
-H 'content-type: application/json' \
-d '{"key":"<key>","kind":"otro","filename":"auto-admisorio.pdf","contentType":"application/pdf"}'import { readFile } from 'node:fs/promises';
const bytes = await readFile('auto-admisorio.pdf');
const grant = await call('request-upload', {
kind: 'otro',
filename: 'auto-admisorio.pdf',
contentType: 'application/pdf',
size: bytes.byteLength,
});
const target = new URL(grant.url, 'https://www.notificado.co');
const put = await fetch(target, {
method: grant.method,
headers: { 'content-type': grant.contentType },
body: bytes,
});
if (!put.ok) throw new Error(`upload refused: ${put.status}`);
const document = await call('confirm-upload', {
key: grant.key,
kind: 'otro',
filename: 'auto-admisorio.pdf',
contentType: 'application/pdf',
});4. Prepare the notification
A draft: nothing is sent and no credit is used. providenciaFecha is YYYY-MM-DD.
curl -X POST 'https://www.notificado.co/v1/create-notification' \
-H "authorization: Bearer $NOTIFICADO_TOKEN" \
-H "idempotency-key: $(uuidgen)" \
-H 'content-type: application/json' \
-d '{"orgId":"<orgId>","caseId":"<caseId>","template":"art8-personal","providencia":"Auto admisorio de la demanda","providenciaFecha":"2026-09-21"}'const notification = await call('create-notification', {
orgId,
caseId: legalCase.id,
template: 'art8-personal',
providencia: 'Auto admisorio de la demanda',
providenciaFecha: '2026-09-21',
});5. Attach the document
curl -X POST 'https://www.notificado.co/v1/attach-document' \
-H "authorization: Bearer $NOTIFICADO_TOKEN" \
-H 'content-type: application/json' \
-d '{"orgId":"<orgId>","notificationId":"<notificationId>","documentId":"<documentId>","kind":"auto_admisorio"}'await call('attach-document', {
orgId,
notificationId: notification.id,
documentId: document.id,
kind: 'auto_admisorio',
});6. Recipients: the lawyer, in the panel
Adding a recipient is the art. 8 sworn statement (Ley 2213 de 2022) about where the address came from: a statement only a lawyer can make, so no API operation does it. Hand the lawyer the notification's link, /notificaciones/<notificationId>.
7. Request the send
With confirm: true, and only after the lawyer has seen the recipients and attachments. The answer is never a sent notification: it is awaiting_confirmation with the panel link.
curl -X POST 'https://www.notificado.co/v1/send-notification' \
-H "authorization: Bearer $NOTIFICADO_TOKEN" \
-H 'content-type: application/json' \
-d '{"orgId":"<orgId>","notificationId":"<notificationId>","confirm":true}'const request = await call('send-notification', {
orgId,
notificationId: notification.id,
confirm: true,
});
// { status: 'awaiting_confirmation', confirmUrl, expiresAt }8. The lawyer confirms
The lawyer opens confirmUrl in the panel and confirms within 24 hours (X_SEND_CONFIRMATION_EXPIRED after that). Only then is one credit per recipient used and the message frozen, timestamped and sent.
9. Read the notification and the case
Single-object reads answer the object itself. When the id does not exist, or belongs to another firm, the answer is 404 X_NOT_FOUND: do not retry with the same id.
curl 'https://www.notificado.co/v1/get-notification?orgId=<orgId>¬ificationId=<notificationId>' \
-H "authorization: Bearer $NOTIFICADO_TOKEN"
curl 'https://www.notificado.co/v1/get-case?orgId=<orgId>&caseId=<caseId>' \
-H "authorization: Bearer $NOTIFICADO_TOKEN"async function read(path: string, params: Record<string, string>) {
const response = await fetch(`${BASE}/${path}?${new URLSearchParams(params)}`, {
headers: { authorization: `Bearer ${token}` },
});
const result = await response.json();
if (!response.ok) throw new Error(`${result.code}: ${result.cause}. Fix: ${result.fix}`);
return result;
}
const current = await read('get-notification', { orgId, notificationId: notification.id });
const sameCase = await read('get-case', { orgId, caseId: legalCase.id });Once the notification is delivered, download the constancia or the evidence package with POST /v1/download-evidence (evidence:read); how to check the package is in The perito package.
10. Lists: one page at a time
Every list pages the same way; the example reads the firm's notifications, 100 at a time. Ask for _first rows (the page size); the answer is an envelope with rows, nextCursor and hasMore. While hasMore is true, ask for the next page by sending that nextCursor as _after, with the same _first and the same filters. On the last page nextCursor is null. Without _first, the list answers bare rows, as before, up to its default size (Limits).
curl 'https://www.notificado.co/v1/notification-list?orgId=<orgId>&_first=100' \
-H "authorization: Bearer $NOTIFICADO_TOKEN"
# { "rows": [ … ], "nextCursor": "<cursor>", "hasMore": true }
curl 'https://www.notificado.co/v1/notification-list?orgId=<orgId>&_first=100&_after=<cursor>' \
-H "authorization: Bearer $NOTIFICADO_TOKEN"const all = [];
let after: string | null = null;
do {
const page = await read('notification-list', {
orgId,
_first: '100',
...(after === null ? {} : { _after: after }),
});
all.push(...page.rows);
after = page.hasMore ? page.nextCursor : null;
} while (after !== null);A _first above the list's maximum is refused with 400 X_INPUT_INVALID; it is never cut down silently. The cursor is opaque and signed: do not build or edit it, and do not use it on another list (X_CURSOR_INVALID). Each list's default and largest page size are in Limits.
Next: Authentication, scopes and limits · Errors · API changelog · every operation in the reference.