Nordlet

← Documentación / Guías

Convenciones de la API

Estructura de las solicitudes, ámbitos de autenticación, consultas de listados, importes monetarios, errores, idempotencia y límites de frecuencia.

Estructura de las solicitudes

Cada operación es una petición POST de estilo RPC:

POST /v1/{module}/{resource}/{action}

Módulos: reference, partners, catalog, sales, purchases, ledger, bank, declarations, files, webhooks, audit. Las acciones son verbos: create, get, update, delete, list, además de acciones de dominio como issue, register, match, apply-advance, generate.

Autenticación y ámbitos

Authorization: Bearer <api key>. Las claves incorporan ámbitos (scopes) que se comprueban en cada acción:

  • {module}:read - acciones de lectura y listado
  • {module}:write - modificaciones
  • {module}:* o * - comodines

Si falta el ámbito, se devuelve un error 403 forbidden. Si la clave no es válida o no se proporciona, se devuelve un error 401 unauthorized.

Consultas de listados

Todas las acciones de tipo list comparten un contrato común:

{
  "page": 1,
  "pageSize": 50,
  "sort": [{ "field": "createdAt", "dir": "desc" }],
  "filter": [{ "field": "paymentStatus", "op": "eq", "value": "unpaid" }]
}

Operadores de filtrado: eq (igual a), ne (distinto de), contains (coincidencia de texto sin distinguir mayúsculas y minúsculas, solo para campos de texto), gte (mayor o igual que), lte (menor o igual que), in (cualquiera de los valores de una lista de hasta 500 elementos). Los campos en los que se puede ordenar y filtrar están predefinidos en una lista blanca para cada extremo (endpoint) (consulte la especificación OpenAPI); los campos desconocidos devuelven un error 400. Añada totals: ["field", …] a la solicitud para sumar campos numéricos de todas las filas coincidentes; cuando los registros tienen una divisa asociada, totalsByCurrency proporciona las mismas sumas desglosadas por código de divisa. Respuestas: { rows, total, page, pageSize, totals?, totalsByCurrency? }.

Importes monetarios y fechas

Los valores monetarios son cadenas de texto decimales con un máximo de 4 decimales ("121.0000") y nunca números de coma flotante (floats). El IVA se calcula línea por línea aplicando el redondeo al alza a la mitad (half-up). Las fechas tienen el formato YYYY-MM-DD y las marcas de tiempo (timestamps) siguen el estándar ISO 8601 en UTC.

Errores

Se utiliza una estructura unificada en todas partes:

{
  "error": {
    "code": "validation",
    "message": "Request validation failed",
    "requestId": "k1x…",
    "key": "request_invalid",
    "fieldErrors": { "lines.0.unitPriceExclVat": ["Invalid value"] }
  }
}

Códigos: validation (400/422, y 413 cuando un archivo subido o el cuerpo de la solicitud es demasiado grande), unauthorized (401), payment_required (402, el periodo de prueba o el saldo prepago ha finalizado; el mensaje indica cómo continuar), forbidden (403), not_found (404), conflict (409), idempotency_key_reuse (422), idempotency_in_progress (409), rate_limited (429), internal (5xx). Cuando el mensaje es uno de los mensajes fijos, la propiedad key lo identifica para que el cliente pueda mostrar su propia traducción, y params contiene los valores necesarios para completarlo (por ejemplo, { "period": "2026-09" } para un periodo contable cerrado). Incluya el identificador requestId al informar de incidencias.

Idempotencia

Añada la cabecera Idempotency-Key: <any string ≤255 chars> a cualquier llamada que modifique datos:

  • Reintentar con la misma clave y carga útil (payload): se vuelve a reproducir la respuesta almacenada byte a byte con la cabecera x-idempotent-replay: true.
  • Misma clave, carga útil diferente: error 422 idempotency_key_reuse.
  • Duplicado simultáneo mientras la primera solicitud está en curso: error 409 idempotency_in_progress.
  • Las claves pertenecen a la clave de API o al usuario que las envía, dentro de la empresa para la cual actúa la llamada, de modo que dos integraciones de una misma empresa pueden utilizar la misma clave. Las llamadas realizadas antes de que exista una empresa y las llamadas a account/* mantienen las claves por usuario, por lo que una llamada repetida a account/companies/create creará una sola empresa.
  • La respuesta almacenada solo se devuelve una vez que el emisor supera de nuevo las comprobaciones de permisos pertinentes (ámbito, rol, empresa y estado de facturación); de lo contrario, la llamada recibe el error actual.
  • Las claves caducan transcurridas 24 horas. Las respuestas de error (4xx) también se reproducen; las respuestas 5xx no se almacenan, por lo que los reintentos vuelven a ejecutar la operación.

Límites de frecuencia

300 solicitudes por minuto por clave de API (según las credenciales presentadas; las solicitudes sin autenticar se limitan por dirección IP). Ante un error 429, respete la cabecera retry-after. Las cabeceras x-ratelimit-* informan sobre su cupo disponible.

Webhooks

Los eventos se escriben en una bandeja de salida transaccional (transactional outbox) dentro de la misma transacción de base de datos que el cambio; un documento revertido nunca emite un evento y ningún evento se pierde. Entregas:

  • Una solicitud POST a su URL con el evento en formato JSON
  • x-nordlet-timestamp: <unix seconds>, la hora en que se firmó la entrega
  • x-nordlet-signature-v2: t=<timestamp>,v1=<hex HMAC-SHA256 of "<timestamp>.<raw body>"> utilizando su secreto de suscripción
  • x-nordlet-signature: sha256=<hex HMAC-SHA256 of the raw body>, que se mantiene sin cambios para los receptores existentes
  • x-nordlet-delivery contiene el identificador de entrega, el cual se mantiene idéntico en cada reintento de una misma entrega
  • Una respuesta que no sea de tipo 2xx o una redirección se reintenta transcurridos 30 segundos, 2 minutos, 10 minutos, 30 minutos, 1 hora, 3 horas, 6 horas y 12 horas (aproximadamente 24 horas en total) antes de que la entrega se marque como fallida
  • Cada entrega es enviada por un único servidor a la vez, por lo que ejecutar varias instancias de la API no provocará envíos duplicados

Verifique la cabecera x-nordlet-signature-v2 mediante una comparación de tiempo constante antes de confiar en la carga útil, y rechace cualquier entrega cuya marca de tiempo difiera en más de 5 minutos de su reloj interno. La marca de tiempo forma parte del texto firmado, por lo que una entrega interceptada no se puede reproducir más tarde con una marca de tiempo reciente. Almacene los identificadores de x-nordlet-delivery que ya haya procesado para ignorar duplicados dentro de esa ventana temporal. Una entrega fallida se puede volver a enviar mediante webhooks/deliveries/redeliver.

Auditoría

Cada modificación queda registrada (actor, action, entity, diff) y se puede consultar a través de audit/list.

Garantías contables

  • Los asientos en el libro diario están siempre cuadrados; esto se aplica mediante un disparador de base de datos diferido (deferred database trigger) en el momento de la confirmación (commit), no solo mediante el código de la aplicación.
  • Los números de documento son secuenciales sin saltos por serie y año; la asignación es segura frente a la concurrencia.
  • Los periodos contables cerrados rechazan cualquier asiento fechado en su interior (error 409).
  • Los documentos eliminados de forma lógica (soft-deleted) conservan su pista de auditoría; los documentos emitidos o registrados no se pueden eliminar.