Documentación técnica
API REST de SD Invoice
Referencia pública para autenticar una integración, emitir comprobantes FE 4.4 con seguridad y manejar respuestas, límites y errores sin duplicar documentos.
Base URL
Empezar una integración
Todos los endpoints usan HTTPS y JSON, salvo las descargas de XML, PDF o ticket. Creá una llave exclusiva para cada integración y guardala únicamente en el servidor o gestor de secretos.
https://sdinvoice.com/api/v1Authorization: Bearer cf_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxLos ejemplos usan datos e identificadores ficticios. No copies una llave real en tickets, repositorios, aplicaciones móviles ni código del navegador.
Autenticación y menor privilegio
Una llave, permisos explícitos
Las llaves se crean en Configuración → API y se muestran completas una sola vez. Pueden tener vencimiento, editar sus permisos sin rotarse y revocarse de inmediato. Escribir no incluye leer: seleccioná ambos alcances cuando el flujo necesite crear y luego consultar.
| Permiso | Qué habilita |
|---|---|
| invoices:read | Listar comprobantes y consultar estado, XML, PDF, ticket o respuesta de Hacienda. |
| invoices:write | Emitir facturas, tiquetes, notas y otros comprobantes admitidos. |
| catalog:read | Listar clientes y productos; buscar códigos CABYS. |
| catalog:write | Crear o actualizar clientes por identificación. |
| collections:read / write | Operar endpoints del módulo de Cobranza cuando la licencia está activa. |
| accounting:read / write | Leer libros y estados financieros, y registrar asientos manuales, con el add-on Contabilidad activo. |
| vet:read / write / admin | Operar expedientes y flujos veterinarios según el alcance y la licencia. |
Referencia del núcleo
Endpoints disponibles
Las listas por cursor devuelven { data, count, hasNext, nextCursor }. Enviá nextCursor como cursor hasta que sea nulo. El parámetro limit controla el tamaño y q filtra cuando el recurso lo admite.
Los cuerpos de escritura son estrictos: campos desconocidos o mal escritos devuelven 422, también dentro de filas anidadas. /vet/catalogs no incluye propietarios, productos ni lotes; clientes y productos usan sus catálogos paginados y los lotes viven en /vet/lots.
En consultas contables, desde y hasta son opcionales pero, cuando se envían, exigen una fecha civil real en formato YYYY-MM-DD. Fechas sin ceros o timestamps ISO devuelven 422 invalid_request.
| Método | Ruta | Uso | Permiso |
|---|---|---|---|
| GET | /api/v1/invoices | Lista comprobantes por cursor; acepta status, limit y cursor. | invoices:read |
| POST | /api/v1/invoices | Valida y emite un comprobante FE 4.4. | invoices:write |
| GET | /api/v1/invoices/:id | Consulta estado y totales; poll=true solicita actualización a Hacienda. | invoices:read |
| GET | /api/v1/invoices/:id/xml | Descarga el XML firmado. | invoices:read |
| GET | /api/v1/invoices/:id/pdf | Genera o descarga la representación PDF. | invoices:read |
| GET | /api/v1/invoices/:id/ticket | Descarga la representación de tiquete. | invoices:read |
| GET | /api/v1/invoices/:id/response | Descarga la respuesta XML de Hacienda. | invoices:read |
| GET | /api/v1/clients | Busca clientes con q, limit y cursor. | catalog:read |
| POST | /api/v1/clients | Crea o actualiza un cliente por identificación. | catalog:write |
| GET | /api/v1/products | Busca productos con q, limit y cursor. | catalog:read |
| GET | /api/v1/cabys | Busca CABYS por código o descripción; máximo 50. | catalog:read |
| GET | /api/v1/accounting/accounts | Catálogo de cuentas contables. | accounting:read |
| GET | /api/v1/accounting/periods | Periodos contables y su estado; un mes sin fila está abierto. | accounting:read |
| GET | /api/v1/accounting/entries | Lista asientos por rango y estado; pageSize máximo 200. | accounting:read |
| GET | /api/v1/accounting/entries/:id | Cabecera y líneas de un asiento. | accounting:read |
| GET | /api/v1/accounting/ledger/:accountId | Libro mayor de una cuenta con saldo corrido; pageSize máximo 500. | accounting:read |
| GET | /api/v1/accounting/statements/trial-balance | Balanza de comprobación del rango. | accounting:read |
| GET | /api/v1/accounting/statements/income | Estado de resultados del rango. | accounting:read |
| GET | /api/v1/accounting/statements/balance-sheet | Balance de situación acumulado a una fecha. | accounting:read |
| POST | /api/v1/accounting/entries | Crea un asiento en borrador; requiere Idempotency-Key. | accounting:write |
| PATCH | /api/v1/accounting/entries/:id | Reemplaza un borrador completo; requiere Idempotency-Key. | accounting:write |
| POST | /api/v1/accounting/entries/:id/post | Postea el borrador y devuelve su número. | accounting:write |
| POST | /api/v1/accounting/entries/:id/void | Anula el asiento; los automáticos se rechazan. | accounting:write |
| GET | /api/v1/collections/contracts | Contratos activos disponibles para crear carteras. | collections:read |
| GET | /api/v1/collections/portfolios | Lista carteras por página. | collections:read |
| GET | /api/v1/collections/portfolios/:id | Detalle, contrato y totales de una cartera. | collections:read |
| POST | /api/v1/collections/portfolios | Crea una cartera sobre un contrato activo. | collections:write |
| GET | /api/v1/collections/cases | Descubre casos; filtra por portfolioId, stage o q. | collections:read |
| GET | /api/v1/collections/cases/:id | Expediente con obligaciones y saldos. | collections:read |
| GET | /api/v1/collections/payments | Lista pagos de un caseId. | collections:read |
| POST | /api/v1/collections/payments | Registra un pago idempotente. | collections:write |
| POST | /api/v1/collections/obligations | Carga hasta 100 filas en línea; cargas mayores devuelven un trabajo 202. | collections:write |
| GET | /api/v1/collections/imports/:id | Estado, avance y resultado de una carga 202. | collections:read |
| GET | /api/v1/vet/catalogs | IDs de sucursales, especies, profesionales y catálogos clínicos. | vet:read |
| GET | /api/v1/vet/lots | Lista lotes por cursor; acepta productId, q, limit y cursor. | vet:read |
| GET | /api/v1/vet/lots/:id | Detalle de un lote; recursos de otra empresa responden 404. | vet:read |
| GET | /api/v1/vet/patients | Busca pacientes con q, limit y cursor. | vet:read |
| POST | /api/v1/vet/patients | Crea un paciente; requiere Idempotency-Key. | vet:write |
| GET | /api/v1/vet/patients/:id | Expediente clínico completo del paciente. | vet:read |
| GET | /api/v1/vet/consultations/:id | Consulta clínica indicada por el enlace self. | vet:read |
| POST | /api/v1/vet/appointments | consultations | prescriptions | Crea operaciones clínicas idempotentes. | vet:write |
| POST | /api/v1/vet/vaccinations | lab-orders | hospitalizations | Registra operaciones clínicas idempotentes. | vet:write |
En Cobranza, una carga de más de 100 obligaciones responde 202 con links.self. Consultá ese enlace hasta obtener status: completed o failed; processed, total y result permiten seguir el avance sin mantener una conexión HTTP abierta. El payload personal se elimina al terminar o agotar reintentos; además, cualquier job vence tras 30 días sin actividad aunque nunca haya alcanzado un estado terminal.
POST /api/v1/invoices
Emitir un comprobante
Una factura requiere tipo de documento, receptor —inline o por clientId—, medios de pago y al menos una línea. Cada línea lleva CABYS de 13 dígitos, descripción, unidad, cantidad, precio y tarifa de IVA. Notas de crédito o débito requieren la referencia al documento ajustado.
Payload JSON
{
"tipoDoc": "01",
"receptor": {
"idType": "02",
"idNumber": "3101123456",
"legalName": "Cliente Ejemplo S.A.",
"email": "facturas@cliente.example"
},
"condicionVenta": "01",
"medioPago": ["01"],
"moneda": "CRC",
"tipoCambio": 1,
"lines": [
{
"cabysCode": "8311100000000",
"description": "Servicio profesional",
"unit": "Sp",
"quantity": 1,
"unitPrice": 25000,
"ivaRate": 13
}
],
"notes": "Orden de compra 1842"
}Respuesta 201
{
"id": "6aaebdd3-6d65-4d34-a9ad-85a03db71c95",
"clave": "50610082600310112345600100001010000000001199999999",
"consecutivo": "00100001010000000001",
"status": "processing",
"links": {
"self": "/api/v1/invoices/6aaebdd3-...",
"xml": "/api/v1/invoices/6aaebdd3-.../xml",
"pdf": "/api/v1/invoices/6aaebdd3-.../pdf"
}
}saveDraftOnly: true para validar el esquema, receptor y cálculos y guardar un borrador sin reservar consecutivo, firmar ni enviar a Hacienda. El ensayo no sustituye la verificación final de licencia ni existencia del CABYS.Reintentos seguros
Idempotency-Key evita duplicados
Generá una clave estable desde el identificador de la operación en tu sistema y enviala en cada intento. El mismo cuerpo con la misma clave recupera la respuesta original. Un cuerpo distinto con una clave ya usada devuelve 409. Fuera de la emisión fiscal, operación y respuesta idempotente se confirman en un único commit.
Idempotency-Key: orden-1842- • Conservá la misma clave ante timeouts, errores de red, 429 o 5xx.
- • Si la operación original sigue en vuelo, recibís 409 inmediatamente; aplicá backoff antes de reintentar.
- • No reintentes 403 o 422 sin corregir permisos o datos.
- • No reutilices una clave para una operación comercial diferente.
Control de tráfico
Límites y retroceso
300/min
Lecturas por API key
60/min
Escrituras por API key
600/min
Protección general por IP
Son las cuotas estándar y pueden ajustarse por configuración operativa. Las respuestas incluyen encabezados de límite y remanente. Cuando recibás 429, esperá los segundos indicados en Retry-After y aplicá retroceso exponencial con jitter.
Diagnóstico
Formato uniforme de errores
{ "error": { "code": "validation_error", "message": "lines: Mínimo una línea" } }| HTTP | Código | Qué significa |
|---|---|---|
| 400 | invalid_json / invalid_request | JSON inválido, parámetro ausente o UUID mal formado. |
| 401 | unauthorized | Llave ausente, inválida, revocada o vencida. |
| 403 | forbidden / tenant_suspended | Falta un permiso o la empresa está suspendida. |
| 404 | not_found / xml_unavailable | El recurso o artefacto no existe o todavía no está disponible. |
| 409 | idempotency_in_progress | La solicitud original con esa clave sigue procesándose. |
| 409 | idempotency_key_reuse | La misma clave se reutilizó con un payload distinto. |
| 400 | empresa_not_supported | Una API key opera solo sobre la empresa que la emitió. |
| 409 | entry_not_draft / entry_not_manual | El asiento ya no es editable o lo gobierna el motor de posteo. |
| 422 | unbalanced_entry / unknown_account / unknown_cost_center | El asiento no cuadra o usa una dimensión ajena al catálogo de la empresa. |
| 422 | validation_error / invalid_request | El payload incumple el esquema o una regla de negocio. |
| 429 | rate_limited | Cuota excedida; respetá Retry-After. |
| 500 | internal_error | Fallo interno reintentable con retroceso e idempotencia. |
¿Necesitás validar un caso antes de integrar?
Compartinos el flujo, volumen esperado y tipos de comprobante. Nunca envíes una API key real por correo.