Autenticación
Cada llamada lleva una API key del workspace en el header Authorization:
Authorization: Bearer demult_sk_xxxxxxxx...Las keys se crean y revocan desde Configuración → API (Owner/Admin), disponible desde el plan Estudio. El secreto se muestra una sola vez al crearla — guardalo seguro; en la base solo queda su hash. Una key opera siempre acotada a su workspace. Rate-limit: 120 solicitudes/minuto por key.
/requestsCrea y envía un pedido: genera el link, manda el email al destinatario y programa los recordatorios. Respeta el cupo de pedidos del plan (402 si lo superás).
Body
{
"template_id": "uuid de una plantilla (ver GET /templates)",
"recipient": { "name": "Juan Pérez", "email": "juan@ejemplo.com" },
"expires_in_days": 30,
"message": "Texto opcional para el cliente"
}Respuesta 201
{
"id": "uuid del pedido",
"status": "sent",
"recipient": { "name": "Juan Pérez", "email": "juan@ejemplo.com" },
"link": "https://demult.com/p/<token>",
"expires_at": "2026-08-01T00:00:00.000Z"
}/requestsLista los pedidos del workspace (más nuevos primero). Query: status, limit (1–100, default 20), offset.
{
"data": [
{
"id": "uuid",
"status": "in_progress",
"recipient": { "name": "Juan Pérez", "email": "juan@ejemplo.com" },
"template_name": "Ganancias 2026",
"progress": { "validated": 2, "total": 4 },
"created_at": "2026-07-01T12:00:00.000Z",
"expires_at": "2026-08-01T00:00:00.000Z",
"completed_at": null
}
],
"limit": 20,
"offset": 0
}/requests/{id}Detalle de un pedido: cada casillero con su estado y, si la IA lo procesó, los datos extraídos.
{
"id": "uuid",
"status": "in_progress",
"recipient": { "name": "Juan Pérez", "email": "juan@ejemplo.com" },
"template_name": "Ganancias 2026",
"documents": [
{
"label": "Recibo de sueldo",
"document_type": "payslip",
"status": "validated",
"confidence": 0.96,
"filename": "recibo.pdf",
"extracted_fields": { "cuit": "20-12345678-9", "neto": "845200" }
}
]
}/templatesLista las plantillas activas (para conocer los template_id que usás al crear pedidos).
{ "data": [ { "id": "uuid", "name": "Ganancias 2026", "document_count": 4 } ] }Errores
Los errores vienen como { "error": "mensaje" } con el status correspondiente:
401— sin header o API key inválida/revocada.403— tu plan no incluye API (disponible desde Estudio).402— superaste el cupo de pedidos del plan.404— el recurso no existe o no es de tu workspace.429— demasiadas solicitudes (120/min por key).
Ejemplo completo
# 1) Conocer las plantillas
curl https://demult.com/api/v1/templates \
-H "Authorization: Bearer demult_sk_..."
# 2) Crear y enviar un pedido
curl -X POST https://demult.com/api/v1/requests \
-H "Authorization: Bearer demult_sk_..." \
-H "Content-Type: application/json" \
-d '{ "template_id": "<id>", "recipient": { "name": "Juan", "email": "juan@ejemplo.com" } }'
# 3) Traer el estado + datos extraídos
curl https://demult.com/api/v1/requests/<id> \
-H "Authorization: Bearer demult_sk_..."Todo sobre HTTPS. Nunca pongas la API key en una URL ni en el front: usala servidor-a-servidor.
Webhooks
En lugar de hacer polling, Demult le avisa a tu sistema cuando pasan cosas. Registrá un endpoint (URL https) en Configuración → Webhooks (Owner/Admin, desde Estudio) y elegí a qué eventos suscribirte. Cada evento se entrega con un POST y se reintenta si falla.
Eventos
request.created— se envió un pedido a un cliente.request.completed— un pedido quedó completo.document.received— un cliente subió un documento.document.validated— un documento fue validado.document.rejected— un documento fue rechazado.
Cada entrega es un JSON con esta forma:
{
"event": "document.validated",
"occurred_at": "2026-07-05T18:30:00.000Z",
"workspace_id": "uuid",
"data": {
"request_id": "uuid",
"recipient_name": "Juan Pérez",
"slot_id": "uuid",
"label": "DNI (frente)",
"document_type": "id_front"
}
}Y trae estas cabeceras (la firma protege integridad y evita replays):
X-Demult-Event: document.validated
X-Demult-Delivery: <id de la entrega>
X-Demult-Timestamp: <epoch en segundos>
X-Demult-Signature: sha256=<hmac hex>Verificá la firma recomputando el HMAC-SHA256 de timestamp + "." + body con el secreto que te mostramos una vez al crear el endpoint:
import { createHmac, timingSafeEqual } from 'node:crypto'
function verify(secret, req, rawBody) {
const ts = req.headers['x-demult-timestamp']
const sig = (req.headers['x-demult-signature'] || '').replace('sha256=', '')
const expected = createHmac('sha256', secret).update(ts + '.' + rawBody).digest('hex')
const a = Buffer.from(expected), b = Buffer.from(sig)
return a.length === b.length && timingSafeEqual(a, b)
}Respondé 2xx para confirmar la recepción. Si respondés otra cosa (o no respondés), reintentamos con backoff; tras muchas fallas seguidas, el endpoint se pausa solo. Usá el timestamp para rechazar entregas viejas.