Saltar al contenido
Notificado

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>&notificationId=<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.