Inicio rápido de la API
De un token de agente a un borrador de notificación listo para que el abogado lo confirme, con curl y TypeScript sobre la API /v1.
Actualizado:
Esta guía recorre el flujo completo sobre https://www.notificado.co/v1: crear el proceso, agregar una parte, cargar el auto admisorio, preparar la notificación y pedir su envío. El envío siempre lo confirma un abogado en el panel: ningún token puede enviar.
| Paso | Operación | Alcance |
|---|---|---|
| 1 | POST /v1/create-case |
cases:write |
| 2 | POST /v1/add-party |
cases:write |
| 3 | POST /v1/request-upload, PUT de los bytes, POST /v1/confirm-upload |
cases:write |
| 4 | POST /v1/create-notification |
notifications:write |
| 5 | POST /v1/attach-document |
notifications:write |
| 6 | Destinatarios, con el juramento del art. 8 | el abogado, en el panel |
| 7 | POST /v1/send-notification → awaiting_confirmation |
notifications:write |
| 8 | Confirmar el envío (confirmSend) |
el abogado, en el panel |
| 9 | GET /v1/get-notification, GET /v1/get-case |
notifications:read, cases:read |
| 10 | Listados, una página a la vez: GET /v1/notification-list |
notifications:read |
0. Token y firma
Emita un token en Cuenta → API (/cuenta/api) con cases:write y notifications:write, y guárdelo en una variable de entorno, nunca en el código:
export NOTIFICADO_TOKEN='ult_prod_…'Varias operaciones reciben orgId, el identificador de su firma. El mismo token le dice cuál es con la herramienta whoami del servidor MCP:
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":{}}}'En TypeScript (Bun, Node 18+ o Deno), una sola función para todas las llamadas de abajo. Cada llamada lleva un Idempotency-Key nuevo (las operaciones que no crean lo ignoran); cuándo reutilizarlo está en Reintentos idempotentes.
const BASE = 'https://www.notificado.co/v1';
const token = process.env.NOTIFICADO_TOKEN;
const orgId = '<orgId>'; // de 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. Crear el proceso
radicado es el número de 23 dígitos tal como aparece en el auto; se admiten guiones y espacios. Si la firma ya lo tiene, la respuesta es X_CASE_RADICADO_TAKEN: use el proceso existente.
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. Agregar una parte
Una parte no es un destinatario: a quién se notifica, y en qué dirección, lo jura el abogado en el 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. Cargar el documento
Tres llamadas, porque los bytes de un archivo nunca viajan dentro de una llamada a la API (límite de 1 MB): pida un permiso, haga PUT de los bytes a la url que responde (con exactamente el contentType que responde, antes de expiresAt) y confirme. La confirmación relee los bytes guardados, verifica su tipo real (PDF, PNG o JPEG) y calcula su 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}'
# Si la url de la respuesta es una ruta (/_storage/…), antepóngale 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(`carga rechazada: ${put.status}`);
const document = await call('confirm-upload', {
key: grant.key,
kind: 'otro',
filename: 'auto-admisorio.pdf',
contentType: 'application/pdf',
});4. Preparar la notificación
Un borrador: no se envía nada ni se usa ningún crédito. providenciaFecha va en AAAA-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. Adjuntar el documento
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. Destinatarios: el abogado, en el panel
Agregar un destinatario es el juramento del art. 8 (Ley 2213 de 2022) sobre el origen de la dirección: una declaración que solo un abogado puede hacer, así que ninguna operación de la API la hace. Entréguele al abogado el enlace de la notificación, /notificaciones/<notificationId>.
7. Pedir el envío
Con confirm: true, y solo después de que el abogado haya visto los destinatarios y los adjuntos. La respuesta nunca es una notificación enviada: es awaiting_confirmation con el enlace del panel.
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. El abogado confirma
El abogado abre confirmUrl en el panel y confirma dentro de las 24 horas (después, X_SEND_CONFIRMATION_EXPIRED). Solo entonces se usa un crédito por destinatario y el mensaje se congela, se sella en el tiempo y se envía.
9. Leer la notificación y el proceso
Las lecturas de un objeto responden el objeto mismo. Si el identificador no existe, o es de otra firma, la respuesta es 404 X_NOT_FOUND: no reintente con el mismo identificador.
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 });Una vez entregada la notificación, descargue la constancia o el paquete de evidencia con POST /v1/download-evidence (evidence:read); cómo comprobar el paquete está en Paquete para el perito.
10. Listados: una página a la vez
Todo listado se pagina igual; el ejemplo lee las notificaciones de la firma, de 100 en 100. Pida _first filas (el tamaño de página); la respuesta es un sobre con rows, nextCursor y hasMore. Mientras hasMore sea true, pida la página siguiente enviando ese nextCursor como _after, con el mismo _first y los mismos filtros. En la última página nextCursor es null. Sin _first, el listado responde las filas sin sobre, como antes, hasta su tamaño predeterminado (Límites).
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);Un _first mayor que el máximo del listado se rechaza con 400 X_INPUT_INVALID; nunca se recorta en silencio. El cursor es opaco y firmado: no lo arme ni lo modifique, y no lo use en otro listado (X_CURSOR_INVALID). El tamaño predeterminado y el máximo de cada listado están en Límites.
Siguiente: Autenticación, alcances y límites · Errores · Cambios de la API · cada operación en la referencia.