# Berlinesa Developers --- --- > API REST para emitir y constatar comprobantes electrónicos de ARCA/AFIP (facturas, notas de débito y crédito; locales A/B/C/M, exportación E y turismo T) sin manejar certificados ni integraciones propias con ARCA. --- --- Convenciones clave: JSON en snake_case; montos enteros en centavos; fechas yyyy-mm-dd; autenticación con `Authorization: Bearer ` (sesión de usuario o API Client con client_credentials); errores RFC 7807 con un `code` numérico estable. --- --- # Introducción La API de Berlinesa emite y constata **comprobantes electrónicos de ARCA/AFIP**: facturas, notas de débito y notas de crédito, en sus variantes local (letras A, B, C y M), de exportación (E) y de turismo (T). Es una API REST con JSON. Declarás qué comprobante querés emitir con `letter` y `kind` en `POST /invoices`, y la API resuelve toda la comunicación con ARCA. ## Qué te resuelve - **Sin certificados de tu lado.** En el modo por defecto, tu usuario le delega Facturación Electrónica al CUIT de la plataforma en ARCA y listo: no generás claves, CSR ni certificados. - **Sin integraciones propias con ARCA.** La autenticación contra ARCA y la numeración de comprobantes las maneja la API. - **Un solo formato para todos los comprobantes.** Local, exportación y turismo usan el mismo endpoint y el mismo shape de respuesta. - **Reintentos y constatación.** Un comprobante rechazado se reintenta con un solo request, y cualquier comprobante autorizado (incluso uno emitido fuera de Berlinesa) se puede constatar contra ARCA. - **PDF listo para entregar.** `GET /invoices/{id}/pdf` devuelve el comprobante con su código QR. ## Cómo está organizada esta documentación | Sección | Para qué | |---|---| | [Primeros pasos](https://dev-developers.berlinesa.com.ar/docs/primeros-pasos.md) | De cero a tu primera factura. | | [Autenticación](https://dev-developers.berlinesa.com.ar/docs/autenticacion.md) | Cómo conseguir un token: sesión de usuario o API Client. | | [Ambientes](https://dev-developers.berlinesa.com.ar/docs/ambientes.md) | URLs de la API y del servidor de autenticación. | | [Conceptos](https://dev-developers.berlinesa.com.ar/docs/conceptos/modos-de-facturacion.md) | Modos de facturación y convenciones de la API (snake_case, paginación, errores, montos). | | [Guías](https://dev-developers.berlinesa.com.ar/docs/guias/emitir-factura.md) | Paso a paso de los flujos más comunes. | | [Referencia de la API](https://dev-developers.berlinesa.com.ar/api/) | Todos los endpoints, con schemas y ejemplos. | | [Recursos para IA](https://dev-developers.berlinesa.com.ar/docs/recursos-para-ia.md) | `llms.txt`, OpenAPI y cómo usar esta documentación desde un asistente de código. | --- # Primeros pasos Esta guía te lleva de cero a una factura B autorizada por ARCA en el **modo plataforma**, el camino por defecto: el contribuyente que factura no genera ningún certificado, le delega el servicio al CUIT de Berlinesa. Si vas a facturar en nombre de varios clientes con tu propio certificado, mirá además [Modos de facturación](https://dev-developers.berlinesa.com.ar/docs/conceptos/modos-de-facturacion.md). ## Antes de empezar - Una cuenta en la app web de Berlinesa. - El CUIT que va a emitir los comprobantes, con Clave Fiscal nivel 3. - Las URLs de [Ambientes](https://dev-developers.berlinesa.com.ar/docs/ambientes.md) exportadas como variables: ```bash export API_URL="" export TOKEN="" ``` ## 1. Conseguir un token Para una integración, creá un API Client y pedí un token con `client_credentials`. El paso a paso está en [Autenticación](https://dev-developers.berlinesa.com.ar/docs/autenticacion.md). Verificá que funciona: ```bash curl "$API_URL/users/me" -H "Authorization: Bearer $TOKEN" ``` ## 2. Cargar el CUIT que factura ```bash curl -X PATCH "$API_URL/users/me" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "cuit": "20111111112" }' ``` El CUIT va con 11 dígitos, sin guiones. Si el contribuyente está adherido a la RG 2904 (facturación con detalle de ítems), mandá también `"rg2904_adherent": true`: cambia qué datos exige ARCA en sus comprobantes locales. Una vez que emitiste un comprobante con ese CUIT, ya no se puede cambiar (`409`). ## 3. Habilitar la facturación en ARCA Esto lo hace el contribuyente, con su Clave Fiscal, en el sitio de ARCA. La API no puede hacerlo ni verificarlo de antemano: si falta, ARCA rechaza el comprobante recién al emitirlo. 1. **Delegar el servicio.** En *Administrador de Relaciones de Clave Fiscal*, creá una nueva relación para el servicio de **Facturación Electrónica** con el **CUIT de la plataforma** como representante (ver [Ambientes](https://dev-developers.berlinesa.com.ar/docs/ambientes.md)). 2. **Habilitar un punto de venta para facturar por sistema.** En *Administración de puntos de venta y domicilios*, agregá un punto de venta nuevo y elegí como sistema la opción para facturación por sistema que corresponda a la condición frente al IVA (en el sitio de ARCA, por ejemplo, figura como "Factura Electrónica - Monotributo - Web Services" para monotributistas). No reutilices un punto de venta de otro sistema. La navegación del sitio de ARCA cambia seguido. La referencia oficial es la [documentación de Factura Electrónica de ARCA](https://www.afip.gob.ar/ws/documentacion/ws-factura-electronica.asp). ## 4. Consultar los datos de referencia Los campos codificados de una factura salen de los catálogos de ARCA. Como mínimo, confirmá el punto de venta: ```bash curl "$API_URL/catalogs/puntos-venta" -H "Authorization: Bearer $TOKEN" ``` La tabla completa de qué catálogo usar para cada campo está en [Catálogos por campo](https://dev-developers.berlinesa.com.ar/docs/referencia/catalogos-por-campo.md). ## 5. Emitir la factura Una factura B de productos por $121,00 (IVA 21% incluido) a un consumidor identificado por CUIT. **Los montos van en centavos.** ```bash curl -X POST "$API_URL/invoices" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "letter": "B", "kind": "FACTURA", "sales_point_number": 1, "concept_type": "PRODUCTS", "issue_date": "2026-09-29", "receptor": { "identification_category": "NATIONAL_DOCUMENT", "document_type": 80, "identification_value": "20222222223", "tax_condition_id": 5 }, "total_amount": 12100, "taxable_amount": 10000, "non_taxable_amount": 0, "exempt_amount": 0, "vat_amount": 2100, "other_taxes_amount": 0, "currency_code": "PES", "exchange_rate": 1, "vat_items": [ { "vat_rate_id": 5, "taxable_base": 10000, "amount": 2100 } ] }' ``` La respuesta es `201` con el comprobante completo y un header `Location` apuntando a `/invoices/{id}`. Mirá el campo `status`: | `status` | Qué significa | |---|---| | `APPROVED` | ARCA otorgó el CAE. El comprobante es válido. | | `OBSERVED` | ARCA otorgó el CAE con observaciones. El comprobante es válido; las observaciones están en `provider_errors`. | | `REJECTED` | ARCA no lo autorizó. El motivo está en `provider_errors` (ver [Emitir comprobantes](https://dev-developers.berlinesa.com.ar/docs/guias/emitir-factura.md#qué-hacer-con-la-respuesta)). | | `PENDING` | Todavía no hay respuesta final de ARCA. | `issue_date` tiene que estar dentro de la ventana que exige ARCA respecto de hoy: ±5 días para productos, ±10 para servicios. ## 6. Descargar el PDF ```bash curl "$API_URL/invoices//pdf" \ -H "Authorization: Bearer $TOKEN" \ -o factura.pdf ``` ## Siguientes pasos - [Emitir comprobantes](https://dev-developers.berlinesa.com.ar/docs/guias/emitir-factura.md): notas de crédito y débito, servicios, exportación y turismo. - [Convenciones](https://dev-developers.berlinesa.com.ar/docs/conceptos/convenciones.md): errores, paginación y formato de datos. - [Referencia de la API](https://dev-developers.berlinesa.com.ar/api/): todos los endpoints. --- # Autenticación Todos los endpoints requieren un token, excepto `GET /health` y `GET /errors`. El token siempre viaja en el mismo header: ```http Authorization: Bearer ``` Hay dos formas de conseguirlo. Las dos devuelven un JWT emitido por el servidor de autenticación de Berlinesa; lo que cambia es **quién** se autentica. | | Sesión de usuario | API Client | |---|---|---| | Quién se autentica | Una persona, con usuario y contraseña | Tu backend o un agente, sin intervención humana | | Cómo se obtiene | Iniciando sesión en la app web (OIDC) | Grant `client_credentials` contra el token endpoint | | Para qué sirve | Probar la API y **gestionar API Clients** | Integraciones server-to-server | | Esquema en la referencia | `user-session` | `api-client` | Casi todos los endpoints de negocio aceptan **cualquiera de los dos**. La excepción son los de `/api-clients`, que exigen siempre una sesión de usuario: un API Client nunca puede crear ni revocar otros API Clients. ## Integraciones: usá un API Client Para una integración real (tu backend emitiendo facturas), usá un API Client. Es un client OAuth 2.0 propio de tu cuenta, con su `client_id` y `client_secret`. ### 1. Crear el API Client Iniciá sesión en la app web de Berlinesa (ver [Ambientes](https://dev-developers.berlinesa.com.ar/docs/ambientes.md)) y creá un API Client desde la sección **API Clients**. También podés crearlo por API con tu token de sesión: ```bash curl -X POST "$API_URL/api-clients" \ -H "Authorization: Bearer $TOKEN_DE_SESION" \ -H "Content-Type: application/json" \ -d '{ "label": "mi-backend" }' ``` Consultá el body completo y la respuesta en la [referencia de `POST /api-clients`](https://dev-developers.berlinesa.com.ar/api/#tag/api-clients). Guardá el `client_id` y el `client_secret` en tu gestor de secretos. ### 2. Pedir un access token No es un endpoint de la API: es el token endpoint estándar de OAuth 2.0 (ver la URL en [Ambientes](https://dev-developers.berlinesa.com.ar/docs/ambientes.md)). ```bash curl -X POST "$TOKEN_URL" \ -d grant_type=client_credentials \ -d client_id="$CLIENT_ID" \ -d client_secret="$CLIENT_SECRET" ``` ```json { "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI...", "expires_in": 300, "token_type": "Bearer", "...": "..." } ``` Usá `access_token` como Bearer en cada request. Cuando vence (`expires_in`, en segundos), pedí uno nuevo con el mismo grant: no hay refresh token en este flujo. ### 3. Revocar `DELETE /api-clients/{id}` deshabilita el client: no se pueden pedir tokens nuevos y la API responde `401` a los que ya se habían emitido. ## Identidad: siempre sale del token La API nunca toma un id de usuario de la URL, el body o la query: el usuario sale del token verificado. Por eso no hay endpoints para operar sobre recursos de otro usuario, y `GET /users/me` siempre devuelve el perfil del dueño del token. ## Errores de autenticación Todos devuelven `401`, con un `code` distinto según el motivo: - falta el header `Authorization` o no es un Bearer; - el token es inválido o está vencido; - el token es de un API Client revocado; - el token es de un API Client y el endpoint exige una sesión de usuario (`/api-clients`). El cuerpo sigue el formato de errores de la API (ver [Convenciones](https://dev-developers.berlinesa.com.ar/docs/conceptos/convenciones.md#errores)) y el listado completo de códigos está en el [catálogo de errores](https://dev-developers.berlinesa.com.ar/docs/referencia/errores.md). --- # Ambientes | Qué | Producción | Variable en los ejemplos | |---|---|---| | URL base de la API | `https://dev-api.berlinesa.com.ar` | `$API_URL` | | Token endpoint (API Clients) | `a definir` | `$TOKEN_URL` | | App web | `a definir` | | | CUIT de la plataforma | `20390604093` | | Los ejemplos de esta documentación usan dos variables de shell para no repetir estos valores: ```bash export API_URL="" export TOKEN="" ``` ## Homologación y producción ARCA tiene dos ambientes separados: **homologación** (pruebas, los comprobantes no tienen validez fiscal) y **producción**. En Berlinesa son dos deployments completos e independientes, con sus propias cuentas, credenciales y datos: un comprobante emitido en uno nunca aparece en el otro. --- # Modos de facturación Para emitir un comprobante, ARCA exige que el pedido esté firmado con un **certificado digital** autorizado a facturar por el CUIT emisor. La API ofrece tres formas de resolverlo. En las tres el mecanismo es el mismo (un certificado + CUITs que lo autorizan); lo que cambia es **de quién es el certificado**. | Modo | Dueño del certificado | Quién factura | Para quién es | |---|---|---|---| | **Plataforma** (por defecto) | Berlinesa | El CUIT del usuario | Cualquier contribuyente que quiere facturar sin trámites técnicos. | | **Contador** | Un contador o prestador | Los CUITs de sus clientes | Estudios contables y software que factura por terceros. | | **Autoservicio** | El propio usuario | Su propio CUIT | Usuarios técnicos que prefieren gestionar su certificado. | ## Plataforma El usuario carga su CUIT con `PATCH /users/me` y, en ARCA, le delega Facturación Electrónica al CUIT de la plataforma. No genera claves ni certificados. En este modo **nunca mandás `represented_cuit_id`**: `POST /invoices` sin ese campo factura con el CUIT del perfil. Es el flujo de [Primeros pasos](https://dev-developers.berlinesa.com.ar/docs/primeros-pasos.md). ## Contador El contador genera **su propio** certificado a través de la API (ver [Certificado propio](https://dev-developers.berlinesa.com.ar/docs/guias/certificado-propio.md)). Cada cliente le delega Facturación Electrónica al CUIT del contador, y el contador lo registra con `POST /represented-cuits`. Cada request de facturación indica en nombre de qué cliente se emite con `represented_cuit_id`: ```json { "represented_cuit_id": "0b6d2f4e-...", "letter": "A", "kind": "FACTURA", "...": "..." } ``` `GET /represented-cuits` lista los CUITs disponibles bajo cada certificado, incluido el propio. ## Autoservicio Igual que el modo contador, pero el certificado factura solo para el CUIT de su dueño. Al pedir el CSR se crea automáticamente el CUIT propio (`is_self: true`), y ese es el `represented_cuit_id` que se usa. ## Conceptos del modelo | Recurso | Qué es | |---|---| | **Credential** (`/credentials`) | Un certificado de ARCA. La clave privada la genera y guarda el servidor, cifrada: nunca se sube ni se devuelve. | | **Represented CUIT** (`/represented-cuits`) | Un CUIT en cuyo nombre se puede facturar con una credential: el propio o uno delegado. | | **Invoice** (`/invoices`) | Un comprobante: factura, nota de débito o nota de crédito. | --- # Convenciones de la API ## Formato de datos - **JSON en `snake_case`.** Todos los campos de entrada y salida, y los query params: `represented_cuit_id`, `total_amount`, `date_from`. - **Montos en centavos, como enteros.** `12100` son $121,00. Aplica a todos los montos: totales, bases imponibles, IVA, precios unitarios. - **Fechas en formato `yyyy-mm-dd`.** Por ejemplo, `issue_date: "2026-09-29"`. - **CUITs como string de 11 dígitos, sin guiones.** Por ejemplo, `"20111111112"`. - **Códigos de ARCA como números o strings, según el catálogo.** Salen de `GET /catalogs/*`, ver [Catálogos por campo](https://dev-developers.berlinesa.com.ar/docs/referencia/catalogos-por-campo.md). ## Paginación Los endpoints de listado aceptan `page` (empieza en 1, por defecto 1) y `size` (por defecto 20, máximo 100). Un `size` mayor se recorta a 100, no se rechaza. ```bash curl "$API_URL/invoices?page=2&size=50" -H "Authorization: Bearer $TOKEN" ``` La respuesta separa los datos de la metadata: ```json { "data": [ { "id": "…", "status": "APPROVED", "…": "…" } ], "meta": { "current_page": 2, "total_pages": 3, "page_size": 50, "total_elements": 142 } } ``` Los items de un listado son una versión resumida del recurso. Para el objeto completo, usá el endpoint por id (por ejemplo, `GET /invoices/{id}`). ## Creación de recursos Un `POST` que crea un recurso responde `201` con el objeto completo y un header `Location` con su URL: ```http HTTP/1.1 201 Created Location: /invoices/5f0c8a1e-… Content-Type: application/json ``` `POST /invoices/{id}/retry` también crea un recurso: el reintento es un comprobante nuevo, con su propio id. El original queda como estaba. ## Errores Todos los errores usan [RFC 7807](https://www.rfc-editor.org/rfc/rfc7807) (`Content-Type: application/problem+json`) más un campo `code` propio: ```json { "type": "https://errors.arca-invoicing-service.internal/api", "title": "Api", "status": 404, "detail": "Comprobante no encontrado", "instance": "/invoices/abc", "code": 40401015 } ``` - **`code` es estable**: identifica un error puntual y nunca se reutiliza. Usalo para manejar errores en tu código. - **`detail` es para personas**: es un texto legible que puede cambiar de redacción. No lo compares como string. El `code` se compone del status HTTP, un número de API y un número de error (`40401015` es status `404`, API `01`, error `015`). El listado completo está en el [catálogo de errores](https://dev-developers.berlinesa.com.ar/docs/referencia/errores.md), y cada endpoint de la [referencia](https://dev-developers.berlinesa.com.ar/api/) muestra los que puede devolver. Los errores de validación del body (campos faltantes o con el tipo incorrecto) responden `400` sin `code` propio. En ese caso, el array `errors` trae un mensaje por cada problema encontrado: ```json { "type": "https://errors.arca-invoicing-service.internal/bad-request", "title": "Bad Request", "status": 400, "detail": "letter must be one of the following values: A, B, C, M, E, T; salesPointNumber must be an integer number", "instance": "/invoices", "errors": [ "letter must be one of the following values: A, B, C, M, E, T", "salesPointNumber must be an integer number" ] } ``` Estos mensajes están en inglés y son para diagnóstico: mostralos en tus logs, pero no los parsees. ### Errores relacionados con ARCA Hay dos situaciones distintas, y conviene tratarlas por separado: | Situación | Respuesta | Qué hacer | |---|---|---| | ARCA procesó el comprobante y lo rechazó u observó | `201` con `status: "REJECTED"` u `"OBSERVED"` y el detalle en `provider_errors` | Corregir según `provider_errors` (ver [Emitir comprobantes](https://dev-developers.berlinesa.com.ar/docs/guias/emitir-factura.md#provider_errors)). | | La operación con ARCA no se pudo completar | `500` con `code` `50001044`, `50001045` o `50001046` | `50001045` y `50001046` son transitorios: reintentá con backoff. `50001044` indica que ARCA rechazó la operación: revisá los datos antes de reintentar. | En los `500` el `detail` es siempre un texto fijo: el detalle técnico queda en los logs de Berlinesa. Si necesitás soporte, mandá el `code`, el `instance` y la hora del request. ## Rate limiting 100 requests cada 60 segundos. Pasado el límite, la API responde `429 Too Many Requests`: esperá y reintentá. --- # Emitir comprobantes Todos los comprobantes se emiten con `POST /invoices`. El tipo sale de dos campos: | Campo | Valores | Qué define | |---|---|---| | `letter` | `A`, `B`, `C`, `M` | Comprobante local. La letra depende de la condición frente al IVA del emisor y del receptor. | | | `E` | Exportación. Requiere el objeto `export`. | | | `T` | Turismo (reintegro de IVA a turistas extranjeros). Requiere el objeto `tourism`. | | `kind` | `FACTURA`, `NOTA_DEBITO`, `NOTA_CREDITO` | Factura o nota que ajusta una factura anterior. | Con eso, la API resuelve cómo autorizar el comprobante ante ARCA. Vos no indicás nada más. El body completo, con todos los campos y sus validaciones, está en la [referencia de `POST /invoices`](https://dev-developers.berlinesa.com.ar/api/#tag/invoices/operation/InvoicesController_create). Esta guía cubre las decisiones que no son obvias mirando solo el schema. > **Montos en centavos** > > Todos los montos son enteros en centavos: `12100` es $121,00. Los totales tienen que cuadrar exactamente: `total_amount` = gravado + no gravado + exento + IVA + otros tributos. Si no cuadra, la API responde `400` antes de llamar a ARCA. ## Productos o servicios `concept_type` es obligatorio en los comprobantes locales: | `concept_type` | Campos extra obligatorios | Ventana de `issue_date` | |---|---|---| | `PRODUCTS` | — | ±5 días respecto de hoy | | `SERVICES` o `PRODUCTS_AND_SERVICES` | `service_start_date`, `service_end_date`, `payment_due_date` | ±10 días respecto de hoy | ```json { "letter": "C", "kind": "FACTURA", "concept_type": "SERVICES", "service_start_date": "2026-09-01", "service_end_date": "2026-09-30", "payment_due_date": "2026-10-10", "...": "..." } ``` ## Detalle de ítems y RG 2904 `items` es opcional en el circuito estándar: solo se usa para el PDF. Si el emisor está adherido a la **RG 2904** (facturación con detalle de ítems), los ítems se vuelven obligatorios y ARCA los valida uno por uno. La adhesión es un dato del CUIT emisor, no del comprobante. Se declara una vez con `rg2904_adherent` en `PATCH /users/me` (modo plataforma) o en `POST`/`PATCH /represented-cuits` (modo contador y autoservicio). ARCA no la informa por ninguna vía consultable, así que la API no puede detectarla sola. ## Notas de crédito y débito Una nota ajusta uno o más comprobantes anteriores, que se indican en `associations`: ```json { "letter": "B", "kind": "NOTA_CREDITO", "sales_point_number": 1, "concept_type": "PRODUCTS", "associations": [ { "related_voucher_type": 6, "related_sales_point_number": 1, "related_voucher_number": 124 } ], "...": "..." } ``` - `related_voucher_type` es el código de comprobante del original (6 = Factura B), de `GET /catalogs/tipos-comprobante`. - `related_cuit` es opcional: si no lo mandás, se asume que el original es del mismo emisor. - En turismo (`letter: "T"`), `associations` no es obligatorio. ## Exportación (`letter: "E"`) El receptor es del exterior y los datos específicos van en `export`. Los campos de montos que son obligatorios en los comprobantes locales (`taxable_amount`, `vat_amount`, `vat_items`, etc.) no se usan acá: el detalle va en `export.items`. ```json { "letter": "E", "kind": "FACTURA", "sales_point_number": 1, "issue_date": "2026-09-29", "total_amount": 500000, "receptor": { "name": "Foreign Buyer Inc.", "identification_category": "FOREIGN_TAX_ID", "identification_value": "FTX-000111", "country_code": 212, "address": "123 Main St, Miami, FL" }, "export": { "export_type": 1, "has_existing_permit": false, "destination_country_code": 212, "currency_code": "DOL", "exchange_rate": 1000, "invoice_language": 1, "items": [ { "product_code": "SKU-EXPORT-1", "description": "Producto exportado", "quantity": 10, "unit_of_measure_id": 7, "unit_price": 50000, "amount": 500000 } ] } } ``` Los países salen de `GET /catalogs/paises`, las monedas de `GET /catalogs/monedas` y los idiomas de `GET /catalogs/idiomas` (si omitís `invoice_language`, se usa español). ## Turismo (`letter: "T"`) Para prestaciones a turistas extranjeros. Los montos y los ítems van dentro de `tourism`, y cada ítem lleva su propio `tourism_code` y alícuota de IVA. ```json { "letter": "T", "kind": "FACTURA", "sales_point_number": 1, "issue_date": "2026-09-29", "total_amount": 121000, "receptor": { "identification_category": "NATIONAL_DOCUMENT", "document_type": 94, "identification_value": "AB123456", "country_code": 212, "address": "123 Main St, Miami, FL" }, "tourism": { "emitter_receiver_relation_code": 1, "taxable_amount": 100000, "non_taxable_amount": 0, "exempt_amount": 0, "other_taxes_amount": 0, "reimbursement_amount": 0, "currency_code": "PES", "exchange_rate": 1, "settles_in_same_foreign_currency": false, "items": [ { "item_type": 1, "tourism_code": 1, "description": "Hotelería sin desayuno", "vat_rate_id": 5, "vat_amount": 21000, "amount": 121000 } ] } } ``` Un comprobante de turismo no puede tener solamente ítems con `tourism_code: 5` (excedente). ## Qué hacer con la respuesta | `status` | Acción | |---|---| | `APPROVED` | Listo. El CAE y su vencimiento están en `authorization`. | | `OBSERVED` | Válido, con observaciones de ARCA en `provider_errors`. | | `REJECTED` | No tiene validez. Ver abajo. | ### `provider_errors` Cuando ARCA rechaza el comprobante u observa algo, el detalle viene en `provider_errors`, siempre con el mismo formato: ```json { "status": "REJECTED", "provider_errors": [ { "code": "10016", "message": "issue_date está fuera del rango de fechas que acepta ARCA para este comprobante", "type": "error" } ], "...": "..." } ``` - `type` es `error` (bloqueante, el comprobante quedó `REJECTED`) u `observation` (informativo). - `code` es el código que informó ARCA. Usalo para manejar el caso en tu código; puede venir vacío si ARCA no informó uno. - `message` es para personas. Para los códigos más comunes es específico; para el resto es un texto genérico, así que guiate por `code`. ### Reintentar o emitir uno nuevo `POST /invoices/{id}/retry` reconstruye **la misma** request y crea un comprobante nuevo. Sirve cuando la causa estaba fuera del comprobante: faltaba la delegación en ARCA, el punto de venta no estaba habilitado, ARCA no estaba disponible. Si el problema son los datos (una fecha fuera de rango, un monto que no corresponde), corregilos y emití un comprobante nuevo con `POST /invoices`. Para consultar comprobantes emitidos, usá `GET /invoices` con filtros (`status`, `date_from`, `date_to`, `voucher_type`, `sales_point_number`, `receptor_identification_value`) o `GET /invoices/{id}` para el objeto completo. --- # Certificado propio (modo contador) Esta guía es para el modo contador y el autoservicio. Si solo facturás para tu propio CUIT sin certificado, no la necesitás: usá el [modo plataforma](https://dev-developers.berlinesa.com.ar/docs/primeros-pasos.md). El servidor **siempre** genera la clave privada y la guarda cifrada. Nunca sale de Berlinesa y no hay forma de subir una clave propia. El alta tiene dos pasos, con un trámite en ARCA en el medio. ```mermaid sequenceDiagram participant Vos participant API as API de Berlinesa participant ARCA Vos->>API: PATCH /users/me (tu CUIT) Vos->>API: POST /credentials/csr API-->>Vos: id + csr_pem Vos->>ARCA: Pegar el CSR, obtener el certificado (.crt) Vos->>API: POST /credentials/{id}/certificate (cert_pem) API-->>Vos: status ACTIVE Vos->>API: POST /represented-cuits (cada cliente) ``` ## 1. Pedir el CSR El CSR sale con el CUIT de tu perfil, así que primero cargalo si todavía no lo hiciste: ```bash curl -X PATCH "$API_URL/users/me" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "cuit": "20111111112" }' curl -X POST "$API_URL/credentials/csr" -H "Authorization: Bearer $TOKEN" ``` ```json { "id": "8c1d…", "csr_pem": "-----BEGIN CERTIFICATE REQUEST-----\n…\n-----END CERTIFICATE REQUEST-----\n", "status": "PENDING_CERT" } ``` Guardá el `id`. En este paso también se crea tu CUIT propio como represented CUIT (`is_self: true`). ## 2. Obtener el certificado en ARCA Con tu Clave Fiscal (nivel 3), en el **Administrador de Certificados Digitales** de ARCA: 1. Creá un DN nuevo. El nombre simbólico solo admite letras y números. 2. Pegá el `csr_pem` completo, incluidas las líneas `BEGIN` y `END`. 3. ARCA devuelve el certificado en formato PEM (`-----BEGIN CERTIFICATE-----`). Copialo entero. Después, autorizá ese certificado a usar el servicio de **Facturación Electrónica** para cada CUIT que vaya a facturar con él: el tuyo en autoservicio, o el de cada cliente en modo contador. La navegación del sitio de ARCA cambia seguido y el trámite difiere entre homologación y producción. La referencia oficial está en la [documentación de ARCA](https://www.afip.gob.ar/ws/documentacion/ws-factura-electronica.asp). ## 3. Completar la credential ```bash curl -X POST "$API_URL/credentials//certificate" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d "$(jq -n --rawfile cert certificado.crt '{cert_pem: $cert}')" ``` Usá `jq` (o tu lenguaje) para armar el JSON: el PEM tiene saltos de línea que hay que escapar. Si todo está bien, la credential pasa a `ACTIVE`. ## 4. Agregar clientes (modo contador) Cada cliente le delega Facturación Electrónica a **tu** CUIT en ARCA y habilita un punto de venta para facturar por sistema. Después lo registrás: ```bash curl -X POST "$API_URL/represented-cuits" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "credential_id": "", "cuit": "30712345678", "label": "Cliente SA", "rg2904_adherent": false }' ``` La API registra el CUIT pero no puede verificar que la delegación exista en ARCA. Si falta, el primer comprobante vuelve `REJECTED` con un error de `code` `600` en `provider_errors`. Desde acá, facturás en nombre del cliente mandando su `represented_cuit_id` en `POST /invoices`. ## Errores frecuentes | Error | Causa | |---|---| | `400` "El certificado no corresponde a la clave generada por este sistema" | El `.crt` no es el de este CSR. ¿Pediste un CSR nuevo sin querer? | | `400` "El PEM del certificado no es válido" | El PEM está cortado o mal escapado. | | `409` "La credential ya tiene un certificado activo" | Esa credential ya se completó. | | `403` "La credential expiró" / "fue revocada" | El certificado venció o se revocó con `DELETE /credentials/{id}`. Pedí uno nuevo. | | Comprobante `REJECTED` con `provider_errors[].code` `600` | Falta la delegación en ARCA para ese CUIT, o no incluye este tipo de comprobante. | | Comprobante `REJECTED` por el punto de venta | El CUIT no tiene ese punto de venta habilitado para facturar por sistema. | | `500` con `code` `50001044` en todos los comprobantes de una credential recién completada | Revisá que el certificado sea el que emitió ARCA para este ambiente (homologación y producción usan certificados distintos). | Los `code` de cada error están en el [catálogo de errores](https://dev-developers.berlinesa.com.ar/docs/referencia/errores.md). --- # Constatar comprobantes Constatar es preguntarle a ARCA si un comprobante con esos datos existe y está autorizado. Hay dos endpoints, según de dónde venga el comprobante. | Endpoint | Para | |---|---| | `POST /invoices/{id}/verify` | Un comprobante emitido con esta API. Toma los datos del comprobante guardado. | | `POST /voucher-verifications` | **Cualquier** comprobante con CAE, CAI o CAEA, incluso uno emitido por otro sistema (por ejemplo, una factura de un proveedor). | ## Un comprobante propio ```bash curl -X POST "$API_URL/invoices//verify" -H "Authorization: Bearer $TOKEN" ``` Solo funciona con comprobantes que ya tienen autorización de ARCA. Si el comprobante está `REJECTED` o `PENDING`, responde `409`. ## Un comprobante de terceros Mandás los datos tal como figuran en el comprobante: ```bash curl -X POST "$API_URL/voucher-verifications" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "issuer_cuit": "20222222223", "sales_point_number": 1, "voucher_type": 6, "voucher_number": 124, "issue_date": "2026-09-09", "total_amount": 12100, "authorization_code": "71234567890123" }' ``` - `issuer_cuit` es el emisor del comprobante que estás verificando, no el tuyo. - `voucher_type` es el código de comprobante de `GET /catalogs/tipos-comprobante` (6 = Factura B). - `receptor_document_type` y `receptor_document_value` son opcionales, pero ARCA los exige cuando el total supera cierto umbral. - En modo contador, `represented_cuit_id` indica con qué certificado se hace la consulta. No tiene por qué coincidir con el emisor. ## Resultado ```json { "result": "R", "observations": [ { "code": 102, "message": "El importe no coincide" } ], "errors": [] } ``` | `result` | Significado | |---|---| | `A` | ARCA tiene registrado un comprobante autorizado con esos datos. | | `R` | Los datos no coinciden con un comprobante autorizado. `observations` explica qué no coincide. | --- # Catálogos por campo Los campos de `POST /invoices` que reciben un código de ARCA salen de alguno de los catálogos de `GET /catalogs/*`. Esta tabla indica cuál consultar para cada uno. Hay dos tipos de catálogos: - **En vivo**: la API los consulta a ARCA con el certificado del CUIT emisor, así que reflejan lo que ese CUIT tiene habilitado (por ejemplo, sus puntos de venta). Aceptan `represented_cuit_id` igual que `POST /invoices`. - **Estáticos**: tablas que ARCA publica como documento, sin consulta en línea (por ejemplo, unidades de medida). No dependen del CUIT. ## `POST /invoices` | Campo | Catálogo | Notas | |---|---|---| | `sales_point_number` | `GET /catalogs/puntos-venta` | Puntos de venta habilitados en ARCA para el CUIT emisor. | | `currency_code`, `export.currency_code`, `tourism.currency_code` | `GET /catalogs/monedas` | `PES` (pesos) no necesita consultarse. | | `receptor.document_type` | `GET /catalogs/tipos-documento-receptor` | Preferilo sobre el estático `GET /catalogs/tipos-documento`: está siempre actualizado. | | `receptor.tax_condition_id` | `GET /catalogs/condiciones-iva-receptor` | Vigente desde la RG 5616. Reemplaza al estático `GET /catalogs/tipos-responsable`. | | `receptor.country_code` | `GET /catalogs/paises` | Solo en exportación y turismo. | | `items[].unit_of_measure_id`, `export.items[].unit_of_measure_id` | `GET /catalogs/unidad-medida` | Estático. | | `vat_items[].vat_rate_id`, `tourism.items[].vat_rate_id` | `GET /catalogs/alicuotas-iva` | | | `tax_items[].tax_type_id` | `GET /catalogs/tipos-tributo` | | | `associations[].related_voucher_type` | `GET /catalogs/tipos-comprobante` | | | `export.destination_country_code`, `export.permits[].destination_country_code` | `GET /catalogs/paises` | | | `export.invoice_language` | `GET /catalogs/idiomas` | Estático. Si se omite, español. | ### Campos que no usan catálogos Estos campos son vocabulario propio de la API, no códigos de ARCA. Los valores posibles están en la [referencia](https://dev-developers.berlinesa.com.ar/api/#tag/invoices/operation/InvoicesController_create): `letter`, `kind`, `concept_type`, `receptor.identification_category`, `export.export_type`, `tourism.emitter_receiver_relation_code`, `tourism.items[].tourism_code`. `GET /catalogs/conceptos` existe como referencia, pero `concept_type` no se resuelve contra él. ## `POST /voucher-verifications` | Campo | Catálogo | |---|---| | `voucher_type` | `GET /catalogs/tipos-comprobante` | | `receptor_document_type` | `GET /catalogs/tipos-documento-receptor` | ## Filtros de `GET /invoices` `voucher_type` usa el mismo código que `GET /catalogs/tipos-comprobante` (por ejemplo, 1 = Factura A, 6 = Factura B). --- # Catálogo de errores Todos los errores que puede devolver la API, ordenados por `code`. El formato de la respuesta está en [Convenciones](https://dev-developers.berlinesa.com.ar/docs/conceptos/convenciones.md#errores). - Manejá los errores por `code`, que es estable. El texto de `detail` puede cambiar. - `detail` completa los `%s` del mensaje con datos del caso: la columna de la derecha es un ejemplo. - La misma lista está disponible en JSON, sin autenticación, en `GET /errors`. | code | Status | Nombre | Ejemplo de detail | |---|---|---|---| | `40001003` | 400 | `CATALOG_NOT_FOUND` | Catálogo no soportado: tipos-comprobante | | `40001004` | 400 | `STATIC_CATALOG_NOT_FOUND` | Catálogo estático no soportado: provincias | | `40001005` | 400 | `CANNOT_DELETE_SELF_REPRESENTED_CUIT` | No se puede eliminar el RepresentedCuit propio (isSelf) de una credential | | `40001009` | 400 | `USER_CUIT_NOT_CONFIGURED` | El usuario no tiene un CUIT configurado | | `40001016` | 400 | `TOTAL_AMOUNT_MISMATCH` | total_amount debe ser igual a taxable_amount + non_taxable_amount + exempt_amount + vat_amount + other_taxes_amount | | `40001017` | 400 | `VAT_AMOUNT_MISMATCH` | vat_amount debe ser igual a la suma de vat_items[].amount | | `40001018` | 400 | `OTHER_TAXES_AMOUNT_MISMATCH` | other_taxes_amount debe ser igual a la suma de tax_items[].amount | | `40001019` | 400 | `SERVICE_DATES_REQUIRED` | service_start_date, service_end_date y payment_due_date son obligatorios para este concept_type | | `40001020` | 400 | `TOURISM_VOUCHER_ONLY_SURPLUS_ITEMS` | Un comprobante de turismo no puede tener únicamente ítems con tourism_code=5 (Excedente) | | `40001021` | 400 | `ISSUE_DATE_OUT_OF_RANGE` | issue_date debe estar dentro de 5 días de la fecha actual | | `40001031` | 400 | `CERT_DOES_NOT_MATCH_GENERATED_KEY` | El certificado no corresponde a la clave generada por este sistema | | `40001032` | 400 | `INVALID_CERT_PEM` | El PEM del certificado no es válido | | `40001048` | 400 | `ITEMS_REQUIRED_FOR_ITEMIZED_INVOICE` | items es obligatorio: el emisor está adherido a RG 2904, este comprobante necesita el detalle de ítems | | `40101037` | 401 | `MISSING_BEARER_TOKEN` | Falta el header Authorization con un Bearer token | | `40101038` | 401 | `INVALID_TOKEN` | El token es inválido o expiró | | `40101039` | 401 | `SIGNING_KEY_NOT_FOUND` | No se encontró la clave pública para verificar el token (kid) | | `40101040` | 401 | `JWKS_FETCH_FAILED` | No se pudieron obtener las claves públicas para verificar el token | | `40101041` | 401 | `MISSING_API_CLIENT_TOKEN` | Falta el header Authorization con un token de Api Client | | `40101042` | 401 | `INVALID_API_CLIENT` | El Api Client es inválido o fue revocado | | `40301008` | 403 | `CREDENTIAL_REVOKED` | La credential asociada fue revocada | | `40301027` | 403 | `CREDENTIAL_PENDING_CERT` | La credential todavía no tiene certificado (PENDING_CERT) | | `40301028` | 403 | `CREDENTIAL_EXPIRED` | La credential expiró | | `40401002` | 404 | `RESOURCE_NOT_FOUND` | recurso no encontrado | | `40401007` | 404 | `REPRESENTED_CUIT_NOT_FOUND` | RepresentedCuit no encontrado | | `40401015` | 404 | `INVOICE_NOT_FOUND` | Comprobante no encontrado | | `40401022` | 404 | `CREDENTIAL_NOT_FOUND` | Credential no encontrada | | `40401024` | 404 | `PLATFORM_CREDENTIAL_SECRET_NOT_FOUND` | No hay clave ni certificado guardados para la credential de plataforma | | `40401033` | 404 | `USER_NOT_FOUND` | Usuario no encontrado | | `40401035` | 404 | `API_CLIENT_NOT_FOUND` | Api Client no encontrado | | `40401036` | 404 | `CONTRIBUYENTE_NOT_FOUND` | ARCA no devolvió constancia para este CUIT | | `40401050` | 404 | `CBU_SALES_POINT_NOT_FOUND` | No se encontró ese punto de venta marcado como CBU informada | | `40901006` | 409 | `REPRESENTED_CUIT_HAS_INVOICES` | No se puede eliminar el RepresentedCuit: tiene comprobantes asociados | | `40901011` | 409 | `INVOICE_NOT_REJECTED` | Solo se puede reintentar un comprobante REJECTED | | `40901012` | 409 | `INVOICE_REJECTED_NO_PDF` | Un comprobante REJECTED no tiene PDF (nunca tuvo autorización) | | `40901013` | 409 | `INVOICE_PDF_NOT_SUPPORTED_FOR_SERVICE` | El PDF todavía no está soportado para este tipo de comprobante | | `40901014` | 409 | `INVOICE_NOT_AUTHORIZED` | El comprobante no tiene autorización de ARCA todavía | | `40901023` | 409 | `PLATFORM_CREDENTIAL_ALREADY_EXISTS` | Ya existe una credential de plataforma | | `40901025` | 409 | `PLATFORM_CREDENTIAL_STILL_PENDING` | La credential de plataforma todavía no tiene certificado | | `40901026` | 409 | `CREDENTIAL_ALREADY_ACTIVE` | La credential ya tiene un certificado activo | | `40901029` | 409 | `PLATFORM_CREDENTIAL_SECRET_EXISTS_USE_RECOVER` | La credential de plataforma ya tiene clave y certificado guardados — hay que recuperarla, no pedir un CSR nuevo | | `40901034` | 409 | `CUIT_ALREADY_CONFIGURED` | El usuario ya facturó con este CUIT confirmado por ARCA — ya no se puede modificar | | `40901049` | 409 | `CBU_SALES_POINT_ALREADY_EXISTS` | Este punto de venta ya está marcado como CBU informada para este cliente | | `50001030` | 500 | `CREDENTIAL_SECRET_MISSING` | La credential existe pero no se encontró su clave privada | | `50001043` | 500 | `CREDENTIAL_STORE_OPERATION_FAILED` | No se pudo completar la operación sobre las credenciales guardadas — reintentá más tarde | | `50001044` | 500 | `ARCA_BUSINESS_ERROR` | ARCA rechazó la operación — revisá los datos del comprobante e intentá de nuevo | | `50001045` | 500 | `ARCA_HTTP_ERROR` | ARCA no está disponible en este momento — reintentá más tarde | | `50001046` | 500 | `ARCA_UNEXPECTED_RESPONSE` | ARCA devolvió una respuesta que no se pudo interpretar — reintentá más tarde | | `50001047` | 500 | `API_CLIENT_OPERATION_FAILED` | No se pudo completar la operación sobre el API Client — reintentá más tarde | | `50301001` | 503 | `SERVICE_UNAVAILABLE` | El servicio no está disponible en este momento | | `50301010` | 503 | `PLATFORM_CREDENTIAL_NOT_CONFIGURED` | No hay ninguna credential de plataforma activa configurada | --- # Recursos para IA Si integrás la API con la ayuda de un asistente de código (Claude Code, Cursor, Copilot, ChatGPT), dale como contexto alguno de estos archivos en lugar de pegar páginas sueltas. Se regeneran en cada publicación del portal, así que siempre coinciden con la API. | Archivo | Qué tiene | Cuándo usarlo | |---|---|---| | [`/llms-full.txt`](https://dev-developers.berlinesa.com.ar/llms-full.txt) | Todas las guías y la referencia completa de la API en un solo Markdown. | La opción más simple: descargalo y agregalo al contexto del asistente. | | [`/llms.txt`](https://dev-developers.berlinesa.com.ar/llms.txt) | Índice en formato [llmstxt.org](https://llmstxt.org) con links a la versión Markdown de cada página. | Para herramientas que leen `llms.txt` y bajan solo lo que necesitan. | | [`/openapi/arca-invoicing.json`](https://dev-developers.berlinesa.com.ar/openapi/arca-invoicing.json) | El spec OpenAPI 3.0. | Para generar un cliente tipado (`openapi-generator`, `openapi-typescript`, etc.) o importar en Postman/Insomnia. | Además, cada página de guías tiene su copia en Markdown en la misma URL con `.md` al final. Por ejemplo, [`/docs/primeros-pasos.md`](https://dev-developers.berlinesa.com.ar/docs/primeros-pasos.md). ## Ejemplo con un asistente de código ```bash curl -o berlinesa-api.md "https:///llms-full.txt" ``` Después, en tu asistente: > Usando `berlinesa-api.md`, escribí una función en TypeScript que emita una factura B por un monto en pesos, con reintento si queda `REJECTED`. ## Generar un cliente tipado ```bash # TypeScript npx openapi-typescript "https:///openapi/arca-invoicing.json" -o src/berlinesa-api.d.ts ``` Tené en cuenta que el spec describe el wire format real: todo en `snake_case` y montos en centavos (ver [Convenciones](https://dev-developers.berlinesa.com.ar/docs/conceptos/convenciones.md)). --- # Referencia de la API — API de Berlinesa 0.1.0 Emisión y constatación de comprobantes electrónicos de ARCA/AFIP: facturas, notas de débito y notas de crédito, en sus variantes local (letra A/B/C/M), de exportación (E) y de turismo (T) — para N usuarios, en modo contador (un certificado delegado por N CUITs) o autoservicio (certificado propio por usuario). Wire format: JSON en snake_case, montos enteros en centavos, fechas yyyy-mm-dd. Errores en application/problem+json con un `code` numérico estable. Spec OpenAPI: https://dev-developers.berlinesa.com.ar/openapi/arca-invoicing.json # Facturación ## invoices Emisión, consulta, reintento y constatación de comprobantes: facturas, notas de débito y notas de crédito, en sus variantes local, de exportación y de turismo — el corazón de la API. ### GET /invoices **Lista las facturas del usuario, o de un cliente puntual (contador/autoservicio)** **Auth:** Bearer token: API Client (client_credentials) o sesión de usuario (JWT). **Parámetros:** | Nombre | En | Tipo | Obligatorio | Descripción | |---|---|---|---|---| | `page` | query | number | no | Número de página, arranca en 1. Default: 1. (mín. 1) | | `size` | query | number | no | Cantidad de resultados por página. Default 20, máximo 100 (valores mayores se recortan a 100, nunca rechazan la request). (mín. 1) | | `represented_cuit_id` | query | string | no | Opcional — un contador/usuario en autoservicio lo manda para acotar a un cliente puntual. Si no viene, se listan todas las facturas que el usuario autenticado creó, sin importar el tier. | | `date_from` | query | string | no | Filtra por issueDate >= esta fecha. | | `date_to` | query | string | no | Filtra por issueDate <= esta fecha. | | `status` | query | PENDING \| APPROVED \| REJECTED \| OBSERVED | no | PENDING (esperando respuesta de ARCA), APPROVED (CAE otorgado), OBSERVED (CAE otorgado con observaciones) o REJECTED (ARCA no autorizó — reintentable vía POST /invoices/:id/retry). | | `receptor_identification_value` | query | string | no | Filtra por a quién se le emitió la factura (identificationValue del receptor, no el CUIT del emisor). | | `voucher_type` | query | number | no | Código de comprobante de ARCA (ver GET /catalogs/tipos-comprobante), ej. 6=Factura B, 1=Factura A. | | `sales_point_number` | query | number | no | Filtra por punto de venta. | **Respuesta 200**: Resultado paginado. **Errores:** | Status | code | Cuándo | |---|---|---| | 400 | | Campos faltantes/mal tipados (class-validator) | | 401 | `40101037` | Falta el header Authorization con un Bearer token | | 401 | `40101038` | El token es inválido o expiró | | 401 | `40101039` | No se encontró la clave pública para verificar el token (kid) | | 401 | `40101040` | No se pudieron obtener las claves públicas para verificar el token | | 401 | `40101042` | El Api Client es inválido o fue revocado | | 404 | `40401007` | RepresentedCuit no encontrado | ### POST /invoices **Emite un comprobante — factura, nota de débito o nota de crédito, local (A/B/C/M), de exportación (E) o de turismo (T) según letter/kind** **Auth:** Bearer token: API Client (client_credentials) o sesión de usuario (JWT). **Body (JSON):** | Campo | Tipo | Obligatorio | Descripción | |---|---|---|---| | `represented_cuit_id` | string | no | Solo para un contador o usuario en autoservicio que factura por varios clientes — elige a cuál. Un usuario final nunca lo manda: se resuelve solo contra su propio certificado, o contra el certificado de la plataforma si no tiene uno propio. | | `letter` | A \| B \| C \| M \| E \| T | sí | Letra del comprobante. A/B/C/M son facturas locales; E es exportación (requiere `export`); T es turismo (requiere `tourism`). | | `kind` | FACTURA \| NOTA_DEBITO \| NOTA_CREDITO | sí | FACTURA, NOTA_DEBITO o NOTA_CREDITO. Una nota de crédito/débito requiere `associations` apuntando al comprobante original (excepto en turismo). | | `sales_point_number` | number | sí | Punto de venta habilitado en ARCA para el CUIT que emite. | | `issue_date` | string | sí | Fecha de emisión (yyyy-mm-dd). Tiene que estar dentro de la ventana que ARCA exige respecto de hoy (400 si no). | | `total_amount` | number | sí | Total del comprobante, en centavos (ej. 12100 = $121,00). (mín. 0) | | `receptor` | object | sí | A quién se le emite el comprobante. | | `receptor.name` | string | no | Solo se usa en exportación; en el resto de los casos se completa desde el padrón (GET /padron/:cuit) si corresponde. | | `receptor.identification_category` | NATIONAL_DOCUMENT \| FOREIGN_TAX_ID | sí | NATIONAL_DOCUMENT: identificado por CUIT/CUIL/CDI/DNI (`documentType` obligatorio, ver GET /catalogs). FOREIGN_TAX_ID: identificación impositiva de un cliente del exterior (exportación/turismo) — sin `documentType`. | | `receptor.document_type` | number | no | Código de tipo de documento (ver GET /catalogs/tipos-documento-receptor) — obligatorio solo si identificationCategory es NATIONAL_DOCUMENT. | | `receptor.identification_value` | string | sí | CUIT/CUIL/CDI/DNI si identificationCategory es NATIONAL_DOCUMENT, o la identificación impositiva del cliente del exterior si es FOREIGN_TAX_ID. | | `receptor.tax_condition_id` | number | no | Condición frente al IVA (ver GET /catalogs/tipos-responsable) — obligatoria desde RG 5616 para facturas locales. | | `receptor.country_code` | number | no | Código de país — solo en exportación/turismo. | | `receptor.address` | string | no | Domicilio del receptor — solo en exportación/turismo. | | `associations` | object[] | no | Comprobante(s) original(es) que esta nota ajusta — obligatorio para kind=NOTA_DEBITO/NOTA_CREDITO, salvo en turismo. | | `associations[].related_voucher_type` | number | sí | Código de comprobante del original (ver GET /catalogs/tipos-comprobante). | | `associations[].related_sales_point_number` | number | sí | Punto de venta del comprobante original. | | `associations[].related_voucher_number` | number | sí | Número del comprobante original. | | `associations[].related_cuit` | string | no | CUIT del emisor del comprobante original — si se omite, se usa el propio. | | `concept_type` | PRODUCTS \| SERVICES \| PRODUCTS_AND_SERVICES | no | PRODUCTS/SERVICES/PRODUCTS_AND_SERVICES. Requerido para facturas locales (letter A/B/C/M). SERVICES/PRODUCTS_AND_SERVICES exigen serviceStartDate/serviceEndDate. | | `taxable_amount` | number | no | Monto gravado, en centavos. Requerido para facturas locales (letter A/B/C/M). (mín. 0) | | `non_taxable_amount` | number | no | Monto no gravado, en centavos. Requerido para facturas locales. (mín. 0) | | `exempt_amount` | number | no | Monto exento, en centavos. Requerido para facturas locales. (mín. 0) | | `vat_amount` | number | no | IVA total, en centavos — debe coincidir con la suma de `vatItems`. Requerido para facturas locales. (mín. 0) | | `other_taxes_amount` | number | no | Otros tributos, en centavos — debe coincidir con la suma de `taxItems`. Requerido para facturas locales. (mín. 0) | | `currency_code` | string | no | Código de moneda (ver GET /catalogs/monedas) — PES para pesos. Requerido para facturas locales. | | `exchange_rate` | number | no | Cotización de `currencyCode` contra pesos — 1 si `currencyCode` es PES. Requerido para facturas locales. | | `service_start_date` | string | no | Fecha de inicio del servicio facturado. Requerido junto con serviceEndDate y paymentDueDate si conceptType es SERVICES o PRODUCTS_AND_SERVICES. | | `service_end_date` | string | no | Fecha de fin del servicio facturado — ver serviceStartDate. | | `payment_due_date` | string | no | Fecha límite de pago — ver serviceStartDate. | | `items` | object[] | no | Renglones de detalle (qué se vendió). Opcionales en el circuito estándar (solo se usan para el PDF); obligatorios y fiscalmente validados en el circuito itemizado (RG 2904). | | `items[].description` | string | sí | Qué se vendió. | | `items[].quantity` | number | sí | Puede ser fraccionaria (ej. kg, horas). (mín. 0) | | `items[].unit_of_measure_id` | number | sí | Código de GET /catalogs/unidad-medida. | | `items[].unit_price` | number | sí | Precio unitario, en centavos. (mín. 0) | | `items[].discount_amount` | number | no | Descuento de este renglón, en centavos. (mín. 0) | | `items[].amount` | number | sí | Subtotal del renglón, en centavos, ya con el descuento aplicado. (mín. 0) | | `vat_items` | object[] | no | Desglose de IVA por alícuota. Requerido para facturas locales — la suma debe coincidir con `vatAmount`. | | `vat_items[].vat_rate_id` | number | sí | Código de alícuota (ver GET /catalogs/alicuotas-iva). | | `vat_items[].taxable_base` | number | sí | Base imponible de esta alícuota, en centavos. (mín. 0) | | `vat_items[].amount` | number | sí | IVA de esta alícuota, en centavos. (mín. 0) | | `tax_items` | object[] | no | Otros tributos (además del IVA) — la suma debe coincidir con `otherTaxesAmount`. | | `tax_items[].tax_type_id` | number | sí | Código de tributo (ver GET /catalogs/tipos-tributo). | | `tax_items[].description` | string | sí | Descripción del tributo. | | `tax_items[].taxable_base` | number | sí | Base imponible de este tributo, en centavos. (mín. 0) | | `tax_items[].rate` | number | sí | Porcentaje (ej. 1 = 1%), no centavos. | | `tax_items[].amount` | number | sí | Monto de este tributo, en centavos. (mín. 0) | | `export` | object | no | Campos propios de una factura de exportación. Obligatorio si letter=E, ignorado en cualquier otro caso. | | `export.export_type` | 1 \| 2 \| 4 | sí | 1 Bienes / 2 Servicios / 4 Otros — solo 1 (Bienes) exige que el emisor esté registrado como exportador ante ARCA (error 1668 si no lo está). | | `export.has_existing_permit` | boolean | sí | Si ya existe un permiso de embarque para este envío. | | `export.destination_country_code` | number | sí | País destino (ver GET /catalogs/paises). | | `export.currency_code` | string | sí | Código de moneda de la operación. | | `export.exchange_rate` | number | sí | Cotización de `currencyCode` contra pesos. | | `export.payment_method` | string | no | Forma de pago acordada. | | `export.incoterms` | string | no | Incoterm de la operación (ej. FOB, CIF). | | `export.invoice_language` | number | no | Idioma del comprobante (ver GET /catalogs/idiomas) — default 1 (Español) si se omite. | | `export.payment_due_date` | string | no | Fecha límite de pago — obligatorio si exportType es 2 o 4. | | `export.items` | object[] | sí | Renglones exportados. | | `export.items[].product_code` | string | sí | Código propio del producto exportado. | | `export.items[].description` | string | sí | Qué se exportó. | | `export.items[].quantity` | number | sí | Puede ser fraccionaria (ej. kg, toneladas). (mín. 0) | | `export.items[].unit_of_measure_id` | number | sí | Código de GET /catalogs/unidad-medida. | | `export.items[].unit_price` | number | sí | Precio unitario, en centavos. (mín. 0) | | `export.items[].discount_amount` | number | no | Descuento de este renglón, en centavos. (mín. 0) | | `export.items[].amount` | number | sí | Subtotal del renglón, en centavos. (mín. 0) | | `export.permits` | object[] | no | Permisos de embarque, si hasExistingPermit es true. | | `export.permits[].permit_number` | string | sí | Número de permiso de embarque. | | `export.permits[].destination_country_code` | number | sí | País destino de este permiso (ver GET /catalogs/paises). | | `tourism` | object | no | Campos propios de una factura de turismo. Obligatorio si letter=T, ignorado en cualquier otro caso. | | `tourism.emitter_receiver_relation_code` | 1 \| 2 \| 3 \| 4 \| 5 \| 6 | sí | Relación entre emisor y receptor: 1 Alojamiento directo a turista no residente / 2 Alojamiento a agencia de viaje residente / 3 Alojamiento a agencia de viaje no residente / 4 Agencia de viaje residente a agencia de viaje no residente / 5 Agencia de viaje residente a turista no residente / 6 Agencia de viaje residente a agencia de viaje residente. | | `tourism.taxable_amount` | number | sí | Monto gravado, en centavos. (mín. 0) | | `tourism.non_taxable_amount` | number | sí | Monto no gravado, en centavos. (mín. 0) | | `tourism.exempt_amount` | number | sí | Monto exento, en centavos. (mín. 0) | | `tourism.other_taxes_amount` | number | sí | Otros tributos, en centavos. (mín. 0) | | `tourism.reimbursement_amount` | number | sí | Reintegro de IVA a turistas, en centavos — obligatorio si algún ítem tiene tourismCode 1 o 2. (mín. 0) | | `tourism.currency_code` | string | sí | Código de moneda de la operación. | | `tourism.exchange_rate` | number | sí | Cotización de `currencyCode` contra pesos. | | `tourism.settles_in_same_foreign_currency` | boolean | sí | Si la cancelación se hace en la misma moneda extranjera. | | `tourism.observations` | string | no | Observaciones libres del comprobante. | | `tourism.items` | object[] | sí | Renglones del comprobante — al menos uno con tourismCode distinto de 5. | | `tourism.items[].item_type` | number | sí | Tipo de ítem. | | `tourism.items[].tourism_code` | 1 \| 2 \| 5 | no | 1 hotelería sin desayuno / 2 con desayuno / 5 excedente — un comprobante no puede tener únicamente ítems con código 5 (400 si se intenta). | | `tourism.items[].code` | string | no | Código propio del ítem. | | `tourism.items[].description` | string | sí | Qué se vendió. | | `tourism.items[].vat_rate_id` | number | sí | Código de alícuota (ver GET /catalogs/alicuotas-iva). | | `tourism.items[].vat_amount` | number | sí | IVA de este renglón, en centavos. (mín. 0) | | `tourism.items[].amount` | number | sí | Subtotal del renglón, en centavos. (mín. 0) | Ejemplo de body — Factura B — local: ```json { "represented_cuit_id": "{{represented_cuit_id}}", "letter": "B", "kind": "FACTURA", "sales_point_number": 1, "concept_type": "PRODUCTS", "receptor": { "identification_category": "NATIONAL_DOCUMENT", "document_type": 80, "identification_value": "20111111112", "tax_condition_id": 5 }, "issue_date": "2026-09-09", "total_amount": 12100, "taxable_amount": 10000, "non_taxable_amount": 0, "exempt_amount": 0, "vat_amount": 2100, "other_taxes_amount": 0, "currency_code": "PES", "exchange_rate": 1, "items": [ { "description": "Producto de ejemplo", "quantity": 2, "unit_of_measure_id": 7, "unit_price": 5000, "amount": 10000 } ], "vat_items": [ { "vat_rate_id": 5, "taxable_base": 10000, "amount": 2100 } ] } ``` Ejemplo de body — Factura B — emisor adherido a RG 2904: ```json { "represented_cuit_id": "{{represented_cuit_id}}", "letter": "B", "kind": "FACTURA", "sales_point_number": 1, "concept_type": "PRODUCTS", "receptor": { "identification_category": "NATIONAL_DOCUMENT", "document_type": 80, "identification_value": "20111111112", "tax_condition_id": 5 }, "issue_date": "2026-09-09", "total_amount": 12100, "taxable_amount": 10000, "non_taxable_amount": 0, "exempt_amount": 0, "vat_amount": 2100, "other_taxes_amount": 0, "currency_code": "PES", "exchange_rate": 1, "items": [ { "description": "Producto de ejemplo", "quantity": 2, "unit_of_measure_id": 7, "unit_price": 5000, "amount": 10000 } ], "vat_items": [ { "vat_rate_id": 5, "taxable_base": 10000, "amount": 2100 } ] } ``` Ejemplo de body — Factura E — exportación: ```json { "represented_cuit_id": "{{represented_cuit_id}}", "letter": "E", "kind": "FACTURA", "sales_point_number": 1, "receptor": { "name": "Foreign Buyer Inc.", "identification_category": "FOREIGN_TAX_ID", "identification_value": "FTX-000111", "country_code": 212, "address": "123 Main St, Miami, FL" }, "issue_date": "2026-09-09", "total_amount": 500000, "export": { "export_type": 1, "has_existing_permit": false, "destination_country_code": 212, "currency_code": "DOL", "exchange_rate": 1000, "invoice_language": 1, "items": [ { "product_code": "SKU-EXPORT-1", "description": "Producto exportado", "quantity": 10, "unit_of_measure_id": 7, "unit_price": 50000, "amount": 500000 } ] } } ``` Ejemplo de body — Factura T — turismo: ```json { "represented_cuit_id": "{{represented_cuit_id}}", "letter": "T", "kind": "FACTURA", "sales_point_number": 1, "receptor": { "identification_category": "NATIONAL_DOCUMENT", "document_type": 94, "identification_value": "AB123456", "country_code": 212, "address": "123 Main St, Miami, FL" }, "issue_date": "2026-09-09", "total_amount": 121000, "tourism": { "emitter_receiver_relation_code": 1, "taxable_amount": 100000, "non_taxable_amount": 0, "exempt_amount": 0, "other_taxes_amount": 0, "reimbursement_amount": 0, "currency_code": "PES", "exchange_rate": 1, "settles_in_same_foreign_currency": false, "items": [ { "item_type": 1, "tourism_code": 1, "description": "Hotelería sin desayuno", "vat_rate_id": 5, "vat_amount": 21000, "amount": 121000 } ] } } ``` **Respuesta 201**: Comprobante persistido — status puede ser APPROVED/OBSERVED/REJECTED según lo que haya decidido ARCA (un REJECTED sigue siendo 201: el comprobante queda guardado). `detail.type` depende de `letter`: STANDARD/ITEMIZED para A/B/C/M (según si el emisor está adherido a RG 2904), EXPORT para E, TOURISM para T — `amounts` es `null` únicamente en EXPORT. ```json { "id": "b3f1c2a0-1234-4abc-9def-0123456789ab", "represented_cuit_id": "b1a2c3d4-0000-4000-8000-000000000001", "created_by_user_id": "a0f1e2d3-0000-4000-8000-000000000009", "created_at": "2026-09-09T12:00:00.000Z", "associations": [], "voucher": { "letter": "B", "kind": "FACTURA", "sales_point_number": 1, "number": 124 }, "issue_date": "2026-09-09T00:00:00.000Z", "total_amount": 12100, "authorization": { "code": "71234567890123", "expires_at": "2026-09-19T00:00:00.000Z" }, "status": "APPROVED", "provider_errors": [], "receptor": { "name": null, "identification": { "category": "NATIONAL_DOCUMENT", "value": "20111111112", "document_type": { "id": 80, "label": "CUIT" } }, "tax_condition": { "id": 5, "label": "Consumidor Final" }, "country_code": null, "address": null }, "line_items": [ { "description": "Producto de ejemplo", "quantity": 2, "unit_of_measure_id": 7, "unit_price": 5000, "discount_amount": 0, "amount": 10000 } ], "vat_items": [ { "vat_rate_id": 5, "taxable_base": 10000, "amount": 2100 } ], "tax_items": [], "amounts": { "taxable": 10000, "non_taxable": 0, "exempt": 0, "other_taxes": 0, "currency_code": "PES", "exchange_rate": 1 }, "detail": { "type": "STANDARD", "concept_type": "PRODUCTS", "vat_amount": 2100, "service_start_date": null, "service_end_date": null, "payment_due_date": null } } ``` **Errores:** | Status | code | Cuándo | |---|---|---| | 400 | | Campos faltantes/mal tipados (class-validator) | | 400 | `40001016` | total_amount debe ser igual a taxable_amount + non_taxable_amount + exempt_amount + vat_amount + other_taxes_amount | | 400 | `40001017` | vat_amount debe ser igual a la suma de vat_items[].amount | | 400 | `40001018` | other_taxes_amount debe ser igual a la suma de tax_items[].amount | | 400 | `40001019` | service_start_date, service_end_date y payment_due_date son obligatorios para este concept_type | | 400 | `40001020` | Un comprobante de turismo no puede tener únicamente ítems con tourism_code=5 (Excedente) | | 400 | `40001021` | issue_date debe estar dentro de 5 días de la fecha actual | | 400 | `40001048` | items es obligatorio: el emisor está adherido a RG 2904, este comprobante necesita el detalle de ítems | | 401 | `40101037` | Falta el header Authorization con un Bearer token | | 401 | `40101038` | El token es inválido o expiró | | 401 | `40101039` | No se encontró la clave pública para verificar el token (kid) | | 401 | `40101040` | No se pudieron obtener las claves públicas para verificar el token | | 401 | `40101042` | El Api Client es inválido o fue revocado | | 404 | `40401007` | RepresentedCuit no encontrado | ### GET /invoices/{id} **Objeto completo de una factura propia** **Auth:** Bearer token: API Client (client_credentials) o sesión de usuario (JWT). **Parámetros:** | Nombre | En | Tipo | Obligatorio | Descripción | |---|---|---|---|---| | `id` | path | string | sí | Id del comprobante. | **Respuesta 200** ```json { "id": "b3f1c2a0-1234-4abc-9def-0123456789ab", "represented_cuit_id": "b1a2c3d4-0000-4000-8000-000000000001", "created_by_user_id": "a0f1e2d3-0000-4000-8000-000000000009", "created_at": "2026-09-09T12:00:00.000Z", "associations": [], "voucher": { "letter": "B", "kind": "FACTURA", "sales_point_number": 1, "number": 124 }, "issue_date": "2026-09-09T00:00:00.000Z", "total_amount": 12100, "authorization": { "code": "71234567890123", "expires_at": "2026-09-19T00:00:00.000Z" }, "status": "APPROVED", "provider_errors": [], "receptor": { "name": null, "identification": { "category": "NATIONAL_DOCUMENT", "value": "20111111112", "document_type": { "id": 80, "label": "CUIT" } }, "tax_condition": { "id": 5, "label": "Consumidor Final" }, "country_code": null, "address": null }, "line_items": [ { "description": "Producto de ejemplo", "quantity": 2, "unit_of_measure_id": 7, "unit_price": 5000, "discount_amount": 0, "amount": 10000 } ], "vat_items": [ { "vat_rate_id": 5, "taxable_base": 10000, "amount": 2100 } ], "tax_items": [], "amounts": { "taxable": 10000, "non_taxable": 0, "exempt": 0, "other_taxes": 0, "currency_code": "PES", "exchange_rate": 1 }, "detail": { "type": "STANDARD", "concept_type": "PRODUCTS", "vat_amount": 2100, "service_start_date": null, "service_end_date": null, "payment_due_date": null } } ``` **Errores:** | Status | code | Cuándo | |---|---|---| | 401 | `40101037` | Falta el header Authorization con un Bearer token | | 401 | `40101038` | El token es inválido o expiró | | 401 | `40101039` | No se encontró la clave pública para verificar el token (kid) | | 401 | `40101040` | No se pudieron obtener las claves públicas para verificar el token | | 401 | `40101042` | El Api Client es inválido o fue revocado | | 404 | `40401015` | Comprobante no encontrado | ### POST /invoices/{id}/retry **Reintenta un REJECTED reconstruyendo la request original — crea una fila nueva** **Auth:** Bearer token: API Client (client_credentials) o sesión de usuario (JWT). **Parámetros:** | Nombre | En | Tipo | Obligatorio | Descripción | |---|---|---|---|---| | `id` | path | string | sí | Id del comprobante REJECTED a reintentar. | **Respuesta 201**: Fila nueva del reintento — nunca la rechazada original. ```json { "id": "b3f1c2a0-1234-4abc-9def-0123456789ab", "represented_cuit_id": "b1a2c3d4-0000-4000-8000-000000000001", "created_by_user_id": "a0f1e2d3-0000-4000-8000-000000000009", "created_at": "2026-09-09T12:00:00.000Z", "associations": [], "voucher": { "letter": "B", "kind": "FACTURA", "sales_point_number": 1, "number": 124 }, "issue_date": "2026-09-09T00:00:00.000Z", "total_amount": 12100, "authorization": { "code": "71234567890123", "expires_at": "2026-09-19T00:00:00.000Z" }, "status": "APPROVED", "provider_errors": [], "receptor": { "name": null, "identification": { "category": "NATIONAL_DOCUMENT", "value": "20111111112", "document_type": { "id": 80, "label": "CUIT" } }, "tax_condition": { "id": 5, "label": "Consumidor Final" }, "country_code": null, "address": null }, "line_items": [ { "description": "Producto de ejemplo", "quantity": 2, "unit_of_measure_id": 7, "unit_price": 5000, "discount_amount": 0, "amount": 10000 } ], "vat_items": [ { "vat_rate_id": 5, "taxable_base": 10000, "amount": 2100 } ], "tax_items": [], "amounts": { "taxable": 10000, "non_taxable": 0, "exempt": 0, "other_taxes": 0, "currency_code": "PES", "exchange_rate": 1 }, "detail": { "type": "STANDARD", "concept_type": "PRODUCTS", "vat_amount": 2100, "service_start_date": null, "service_end_date": null, "payment_due_date": null } } ``` **Errores:** | Status | code | Cuándo | |---|---|---| | 401 | `40101037` | Falta el header Authorization con un Bearer token | | 401 | `40101038` | El token es inválido o expiró | | 401 | `40101039` | No se encontró la clave pública para verificar el token (kid) | | 401 | `40101040` | No se pudieron obtener las claves públicas para verificar el token | | 401 | `40101042` | El Api Client es inválido o fue revocado | | 404 | `40401015` | Comprobante no encontrado | | 409 | `40901011` | Solo se puede reintentar un comprobante REJECTED | ### POST /invoices/{id}/verify **Constata que este comprobante coincide con lo que ARCA tiene registrado** **Auth:** Bearer token: API Client (client_credentials) o sesión de usuario (JWT). **Parámetros:** | Nombre | En | Tipo | Obligatorio | Descripción | |---|---|---|---|---| | `id` | path | string | sí | Id del comprobante a constatar. | **Respuesta 201** ```json { "result": "A", "observations": [], "errors": [] } ``` **Errores:** | Status | code | Cuándo | |---|---|---| | 401 | `40101037` | Falta el header Authorization con un Bearer token | | 401 | `40101038` | El token es inválido o expiró | | 401 | `40101039` | No se encontró la clave pública para verificar el token (kid) | | 401 | `40101040` | No se pudieron obtener las claves públicas para verificar el token | | 401 | `40101042` | El Api Client es inválido o fue revocado | | 404 | `40401015` | Comprobante no encontrado | | 409 | `40901014` | El comprobante no tiene autorización de ARCA todavía | ### GET /invoices/{id}/pdf **PDF del comprobante — no disponible para todos los tipos de factura** **Auth:** Bearer token: API Client (client_credentials) o sesión de usuario (JWT). **Parámetros:** | Nombre | En | Tipo | Obligatorio | Descripción | |---|---|---|---|---| | `id` | path | string | sí | Id del comprobante. | **Respuesta 200**: application/pdf, no JSON **Errores:** | Status | code | Cuándo | |---|---|---| | 401 | `40101037` | Falta el header Authorization con un Bearer token | | 401 | `40101038` | El token es inválido o expiró | | 401 | `40101039` | No se encontró la clave pública para verificar el token (kid) | | 401 | `40101040` | No se pudieron obtener las claves públicas para verificar el token | | 401 | `40101042` | El Api Client es inválido o fue revocado | | 404 | `40401015` | Comprobante no encontrado | | 409 | `40901012` | Un comprobante REJECTED no tiene PDF (nunca tuvo autorización) | | 409 | `40901013` | El PDF todavía no está soportado para este tipo de comprobante | ## voucher-verification Constatación de cualquier comprobante autorizado (con CAE/CAI/CAEA), incluido uno emitido fuera de este sistema — a diferencia de `invoices`, no depende de que el comprobante exista en esta base. ### POST /voucher-verifications **Constata cualquier comprobante autorizado (con CAE/CAI/CAEA), incluido uno emitido fuera de este sistema** **Auth:** Bearer token: API Client (client_credentials) o sesión de usuario (JWT). **Body (JSON):** | Campo | Tipo | Obligatorio | Descripción | |---|---|---|---| | `represented_cuit_id` | string | no | Con cuya credential se autentica la consulta — no tiene por qué ser el emisor del comprobante que se está constatando (ver issuerCuit). Un usuario final nunca lo manda: se resuelve solo contra su propio certificado, o contra el de la plataforma si no tiene uno propio. | | `issuer_cuit` | string | sí | CUIT del emisor del comprobante a constatar — no tiene por qué coincidir con representedCuitId. | | `sales_point_number` | number | sí | Punto de venta del comprobante a constatar. | | `voucher_type` | number | sí | Código de comprobante de ARCA (ver GET /catalogs/tipos-comprobante). | | `voucher_number` | number | sí | Número del comprobante a constatar. | | `issue_date` | string | sí | Fecha de emisión del comprobante (yyyy-mm-dd). | | `total_amount` | number | sí | Total del comprobante, en centavos. (mín. 0) | | `authorization_code` | string | sí | CAE/CAI/CAEA del comprobante a constatar. | | `receptor_document_type` | number | no | Tipo de documento del receptor (ver GET /catalogs/tipos-documento-receptor) — ARCA lo exige según el tipo de comprobante y su importe. | | `receptor_document_value` | string | no | Identificación del receptor — ver receptorDocumentType. | Ejemplo de body — Constatar una factura de un tercero (ej. de un proveedor): ```json { "represented_cuit_id": "{{represented_cuit_id}}", "issuer_cuit": "20222222223", "sales_point_number": 1, "voucher_type": 6, "voucher_number": 124, "issue_date": "2026-09-09", "total_amount": 12100, "authorization_code": "71234567890123" } ``` **Respuesta 201** ```json { "result": "A", "observations": [], "errors": [] } ``` **Errores:** | Status | code | Cuándo | |---|---|---| | 400 | | Campos faltantes/mal tipados (class-validator) | | 401 | `40101037` | Falta el header Authorization con un Bearer token | | 401 | `40101038` | El token es inválido o expiró | | 401 | `40101039` | No se encontró la clave pública para verificar el token (kid) | | 401 | `40101040` | No se pudieron obtener las claves públicas para verificar el token | | 401 | `40101042` | El Api Client es inválido o fue revocado | # Configuración de la cuenta ## users Perfil del usuario autenticado — solo el propio, sin CRUD de terceros. ### GET /users/me **Perfil del usuario autenticado, con los datos del padrón de ARCA si ya se consultaron para su CUIT** **Auth:** Bearer token: API Client (client_credentials) o sesión de usuario (JWT). **Respuesta 200** **Errores:** | Status | code | Cuándo | |---|---|---| | 401 | `40101037` | Falta el header Authorization con un Bearer token | | 401 | `40101038` | El token es inválido o expiró | | 401 | `40101039` | No se encontró la clave pública para verificar el token (kid) | | 401 | `40101040` | No se pudieron obtener las claves públicas para verificar el token | | 401 | `40101042` | El Api Client es inválido o fue revocado | ### PATCH /users/me **Carga o cambia el CUIT del usuario final para facturar sin certificado propio** Reenviar el mismo CUIT que ya tenía es un no-op idempotente. Cambiarlo a uno distinto da 409 una vez que el usuario ya facturó y el padrón de ARCA confirmó ese CUIT. `rg2904_adherent` es opcional: si viene, declara o corrige la adhesión a RG 2904 del CUIT propio del usuario. **Auth:** Bearer token: API Client (client_credentials) o sesión de usuario (JWT). **Body (JSON):** | Campo | Tipo | Obligatorio | Descripción | |---|---|---|---| | `cuit` | string | sí | CUIT para facturar con el certificado de la plataforma (11 dígitos, sin guiones). Reenviar el mismo que ya tenía es un no-op idempotente; cambiarlo a uno distinto da 409 una vez que ya facturaste con ese CUIT. (patrón `^\d{11}$`) | | `rg2904_adherent` | boolean | no | Si el usuario está adherido a RG 2904 (factura con detalle de ítem) — cambia qué datos exige ARCA en sus comprobantes locales. Se aplica al CUIT propio (autoservicio, o el de la plataforma si factura sin certificado propio), creándolo ahí mismo si todavía no existe. Omitido: no cambia el valor actual. | **Respuesta 200** **Errores:** | Status | code | Cuándo | |---|---|---| | 400 | | Campos faltantes/mal tipados (class-validator) | | 401 | `40101037` | Falta el header Authorization con un Bearer token | | 401 | `40101038` | El token es inválido o expiró | | 401 | `40101039` | No se encontró la clave pública para verificar el token (kid) | | 401 | `40101040` | No se pudieron obtener las claves públicas para verificar el token | | 401 | `40101042` | El Api Client es inválido o fue revocado | | 409 | `40901034` | El usuario ya facturó con este CUIT confirmado por ARCA — ya no se puede modificar | | 503 | `50301010` | No hay ninguna credential de plataforma activa configurada | ## credentials Alta y baja del certificado ARCA (cert + clave privada, cifrados) usado para autenticarse ante ARCA. El servidor siempre genera la clave privada — nunca se acepta una subida directa. ### GET /credentials **Lista las credenciales (certificados ARCA) del usuario autenticado** **Auth:** Bearer token: API Client (client_credentials) o sesión de usuario (JWT). **Parámetros:** | Nombre | En | Tipo | Obligatorio | Descripción | |---|---|---|---|---| | `page` | query | number | no | Número de página, arranca en 1. Default: 1. (mín. 1) | | `size` | query | number | no | Cantidad de resultados por página. Default 20, máximo 100 (valores mayores se recortan a 100, nunca rechazan la request). (mín. 1) | **Respuesta 200**: Resultado paginado. **Errores:** | Status | code | Cuándo | |---|---|---| | 400 | | Campos faltantes/mal tipados (class-validator) | | 401 | `40101037` | Falta el header Authorization con un Bearer token | | 401 | `40101038` | El token es inválido o expiró | | 401 | `40101039` | No se encontró la clave pública para verificar el token (kid) | | 401 | `40101040` | No se pudieron obtener las claves públicas para verificar el token | | 401 | `40101042` | El Api Client es inválido o fue revocado | ### POST /credentials/csr **Genera un par de claves + CSR (paso 1 de 2) — pegar el CSR en ARCA para obtener el .crt** El servidor SIEMPRE genera la clave privada acá — nunca se acepta una subida directa de clave privada. Sin body: usa el CUIT ya configurado en el perfil (PATCH /users/me tiene que haberse llamado antes) y la razón social/nombre que haya en el padrón para ese CUIT. **Auth:** Bearer token: API Client (client_credentials) o sesión de usuario (JWT). **Respuesta 201** **Errores:** | Status | code | Cuándo | |---|---|---| | 400 | `40001009` | El usuario no tiene un CUIT configurado | | 401 | `40101037` | Falta el header Authorization con un Bearer token | | 401 | `40101038` | El token es inválido o expiró | | 401 | `40101039` | No se encontró la clave pública para verificar el token (kid) | | 401 | `40101040` | No se pudieron obtener las claves públicas para verificar el token | | 401 | `40101042` | El Api Client es inválido o fue revocado | ### POST /credentials/{id}/certificate **Completa la credential con el .crt que devolvió AFIP (paso 2 de 2)** **Auth:** Bearer token: API Client (client_credentials) o sesión de usuario (JWT). **Parámetros:** | Nombre | En | Tipo | Obligatorio | Descripción | |---|---|---|---|---| | `id` | path | string | sí | Credential creada en el paso 1 (POST /credentials/csr). | **Body (JSON):** | Campo | Tipo | Obligatorio | Descripción | |---|---|---|---| | `cert_pem` | string | sí | El .crt que devuelve AFIP al procesar el CSR de POST /credentials/csr. | **Respuesta 200** **Errores:** | Status | code | Cuándo | |---|---|---| | 400 | `40001031` | El certificado no corresponde a la clave generada por este sistema | | 400 | `40001032` | El PEM del certificado no es válido | | 400 | | Campos faltantes/mal tipados (class-validator) | | 401 | `40101037` | Falta el header Authorization con un Bearer token | | 401 | `40101038` | El token es inválido o expiró | | 401 | `40101039` | No se encontró la clave pública para verificar el token (kid) | | 401 | `40101040` | No se pudieron obtener las claves públicas para verificar el token | | 401 | `40101042` | El Api Client es inválido o fue revocado | | 403 | `40301008` | La credential asociada fue revocada | | 404 | `40401022` | Credential no encontrada | | 409 | `40901026` | La credential ya tiene un certificado activo | | 500 | `50001030` | La credential existe pero no se encontró su clave privada | ### GET /credentials/{id} **Datos de una credential propia (nunca incluye la clave privada)** **Auth:** Bearer token: API Client (client_credentials) o sesión de usuario (JWT). **Parámetros:** | Nombre | En | Tipo | Obligatorio | Descripción | |---|---|---|---|---| | `id` | path | string | sí | Id de la credential. | **Respuesta 200** **Errores:** | Status | code | Cuándo | |---|---|---| | 401 | `40101037` | Falta el header Authorization con un Bearer token | | 401 | `40101038` | El token es inválido o expiró | | 401 | `40101039` | No se encontró la clave pública para verificar el token (kid) | | 401 | `40101040` | No se pudieron obtener las claves públicas para verificar el token | | 401 | `40101042` | El Api Client es inválido o fue revocado | | 404 | `40401022` | Credential no encontrada | ### DELETE /credentials/{id} **Revoca una credential — no se puede volver a usar para firmar contra ARCA** **Auth:** Bearer token: API Client (client_credentials) o sesión de usuario (JWT). **Parámetros:** | Nombre | En | Tipo | Obligatorio | Descripción | |---|---|---|---|---| | `id` | path | string | sí | Id de la credential a revocar. | **Respuesta 204** **Errores:** | Status | code | Cuándo | |---|---|---| | 401 | `40101037` | Falta el header Authorization con un Bearer token | | 401 | `40101038` | El token es inválido o expiró | | 401 | `40101039` | No se encontró la clave pública para verificar el token (kid) | | 401 | `40101040` | No se pudieron obtener las claves públicas para verificar el token | | 401 | `40101042` | El Api Client es inválido o fue revocado | | 404 | `40401022` | Credential no encontrada | ## represented-cuits CUITs en cuyo nombre se puede facturar bajo una credential — el propio (autoservicio) o delegados por un tercero (modo contador). ### GET /represented-cuits **Lista los CUITs (propio + delegados) bajo una credential, con los datos del padrón de ARCA si ya se consultaron para cada uno** **Auth:** Bearer token: API Client (client_credentials) o sesión de usuario (JWT). **Parámetros:** | Nombre | En | Tipo | Obligatorio | Descripción | |---|---|---|---|---| | `page` | query | number | no | Número de página, arranca en 1. Default: 1. (mín. 1) | | `size` | query | number | no | Cantidad de resultados por página. Default 20, máximo 100 (valores mayores se recortan a 100, nunca rechazan la request). (mín. 1) | | `credential_id` | query | string | sí | Credential bajo la cual buscar los CUITs (propio + delegados). | | `q` | query | string | no | Texto libre — busca por coincidencia parcial contra CUIT, etiqueta, razón social, nombre y apellido. No matchea contra campos de padrón que todavía están en null (ver GET /represented-cuits). | **Respuesta 200**: Resultado paginado. **Errores:** | Status | code | Cuándo | |---|---|---| | 400 | | Campos faltantes/mal tipados (class-validator) | | 401 | `40101037` | Falta el header Authorization con un Bearer token | | 401 | `40101038` | El token es inválido o expiró | | 401 | `40101039` | No se encontró la clave pública para verificar el token (kid) | | 401 | `40101040` | No se pudieron obtener las claves públicas para verificar el token | | 401 | `40101042` | El Api Client es inválido o fue revocado | ### POST /represented-cuits **Agrega un CUIT delegado bajo una credential (modo contador)** El sistema no puede verificar que la delegación exista del lado de ARCA, solo la registra — si no está hecha, POST /invoices va a rechazar con "CUIT representada no incluida en token". Dispara best-effort (nunca bloquea esta respuesta si falla) la consulta al padrón de ARCA para este CUIT y persiste ese snapshot en el propio cliente, para que listarlos después no necesite volver a consultarlo. **Auth:** Bearer token: API Client (client_credentials) o sesión de usuario (JWT). **Body (JSON):** | Campo | Tipo | Obligatorio | Descripción | |---|---|---|---| | `credential_id` | string | sí | Credential bajo la cual se registra este CUIT delegado. | | `cuit` | string | sí | CUIT del cliente delegado (modo contador) — 11 dígitos, sin guiones. (patrón `^\d{11}$`) | | `label` | string | no | Etiqueta descriptiva opcional (ej. nombre del cliente). | | `rg2904_adherent` | boolean | no | Si este CUIT está adherido a RG 2904 (factura con detalle de ítem) — dato que ARCA no informa por ninguna vía consultable, se declara acá. Corregible después vía PATCH /represented-cuits/{id}. | **Respuesta 201** **Errores:** | Status | code | Cuándo | |---|---|---| | 400 | | Campos faltantes/mal tipados (class-validator) | | 401 | `40101037` | Falta el header Authorization con un Bearer token | | 401 | `40101038` | El token es inválido o expiró | | 401 | `40101039` | No se encontró la clave pública para verificar el token (kid) | | 401 | `40101040` | No se pudieron obtener las claves públicas para verificar el token | | 401 | `40101042` | El Api Client es inválido o fue revocado | ### PATCH /represented-cuits/{id} **Corrige la adhesión a RG 2904 y/o el punto de venta default de un CUIT (propio o delegado)** Los 2 campos editables después de crear, cada uno independiente (mandar solo uno deja el otro sin tocar). `rg2904_adherent` lo declara el usuario (ARCA no lo informa por ninguna vía consultable) y puede haberse cargado mal al alta — antes de este endpoint la única forma de corregirlo era borrar y crear de nuevo el CUIT delegado, pero eso es imposible en cuanto ya facturó algo (DELETE devuelve 409 REPRESENTED_CUIT_HAS_INVOICES); cambiarlo solo afecta comprobantes emitidos DESPUÉS de este PATCH. `default_sales_point_number` es pura conveniencia de UI (preseleccionar el punto de venta en "Emitir factura") — nunca se usa server-side, acepta `null` para quitarlo. **Auth:** Bearer token: API Client (client_credentials) o sesión de usuario (JWT). **Parámetros:** | Nombre | En | Tipo | Obligatorio | Descripción | |---|---|---|---|---| | `id` | path | string | sí | Id del CUIT (propio o delegado) a corregir. | **Body (JSON):** | Campo | Tipo | Obligatorio | Descripción | |---|---|---|---| | `rg2904_adherent` | boolean | no | Si este CUIT está adherido a RG 2904 (factura con detalle de ítem). Cambiarlo solo afecta comprobantes emitidos DESPUÉS de este PATCH — los ya emitidos no se re-emiten ni cambian de circuito. | | `default_sales_point_number` | number \| null | no | Punto de venta a preseleccionar en "Emitir factura" para este CUIT — null para quitarlo. No se valida contra ARCA al setearlo. (mín. 1) | **Respuesta 200** **Errores:** | Status | code | Cuándo | |---|---|---| | 400 | | Campos faltantes/mal tipados (class-validator) | | 401 | `40101037` | Falta el header Authorization con un Bearer token | | 401 | `40101038` | El token es inválido o expiró | | 401 | `40101039` | No se encontró la clave pública para verificar el token (kid) | | 401 | `40101040` | No se pudieron obtener las claves públicas para verificar el token | | 401 | `40101042` | El Api Client es inválido o fue revocado | | 404 | `40401007` | RepresentedCuit no encontrado | ### DELETE /represented-cuits/{id} **Quita un CUIT delegado — nunca el propio (isSelf)** **Auth:** Bearer token: API Client (client_credentials) o sesión de usuario (JWT). **Parámetros:** | Nombre | En | Tipo | Obligatorio | Descripción | |---|---|---|---|---| | `id` | path | string | sí | Id del CUIT delegado a quitar. | **Respuesta 204** **Errores:** | Status | code | Cuándo | |---|---|---| | 400 | `40001005` | No se puede eliminar el RepresentedCuit propio (isSelf) de una credential | | 401 | `40101037` | Falta el header Authorization con un Bearer token | | 401 | `40101038` | El token es inválido o expiró | | 401 | `40101039` | No se encontró la clave pública para verificar el token (kid) | | 401 | `40101040` | No se pudieron obtener las claves públicas para verificar el token | | 401 | `40101042` | El Api Client es inválido o fue revocado | | 404 | `40401007` | RepresentedCuit no encontrado | | 409 | `40901006` | No se puede eliminar el RepresentedCuit: tiene comprobantes asociados | ### GET /represented-cuits/{id}/cbu-informada-sales-points **Lista los puntos de venta marcados como CBU informada de un CUIT** **Auth:** Bearer token: API Client (client_credentials) o sesión de usuario (JWT). **Parámetros:** | Nombre | En | Tipo | Obligatorio | Descripción | |---|---|---|---|---| | `id` | path | string | sí | Id del CUIT (propio o delegado). | **Respuesta 200** **Errores:** | Status | code | Cuándo | |---|---|---| | 401 | `40101037` | Falta el header Authorization con un Bearer token | | 401 | `40101038` | El token es inválido o expiró | | 401 | `40101039` | No se encontró la clave pública para verificar el token (kid) | | 401 | `40101040` | No se pudieron obtener las claves públicas para verificar el token | | 401 | `40101042` | El Api Client es inválido o fue revocado | | 404 | `40401007` | RepresentedCuit no encontrado | ### POST /represented-cuits/{id}/cbu-informada-sales-points **Marca un punto de venta para la modalidad "Factura A con leyenda Pago en CBU informada" (RG 5762/2025)** ARCA no informa esta relación por ninguna vía consultable — el contribuyente tiene que haber dado de alta este punto de venta para esta modalidad directamente en ARCA primero (servicio REAR/RECE/RFI); esto solo le dice a esta API qué punto de venta imprimir con esa leyenda en el PDF. **Auth:** Bearer token: API Client (client_credentials) o sesión de usuario (JWT). **Parámetros:** | Nombre | En | Tipo | Obligatorio | Descripción | |---|---|---|---|---| | `id` | path | string | sí | Id del CUIT (propio o delegado). | **Body (JSON):** | Campo | Tipo | Obligatorio | Descripción | |---|---|---|---| | `sales_point_number` | number | sí | Punto de venta ya dado de alta en ARCA para esta modalidad (ver GET /catalogs/puntos-venta para los puntos de venta habilitados en general). (mín. 1) | | `cbu` | string | no | CBU a imprimir en el PDF junto a la leyenda — opcional, recomendado por ARCA para facilitarle el pago al receptor. Sin esto, igual se imprime la leyenda, sin el número de cuenta. | **Respuesta 201** **Errores:** | Status | code | Cuándo | |---|---|---| | 400 | | Campos faltantes/mal tipados (class-validator) | | 401 | `40101037` | Falta el header Authorization con un Bearer token | | 401 | `40101038` | El token es inválido o expiró | | 401 | `40101039` | No se encontró la clave pública para verificar el token (kid) | | 401 | `40101040` | No se pudieron obtener las claves públicas para verificar el token | | 401 | `40101042` | El Api Client es inválido o fue revocado | | 404 | `40401007` | RepresentedCuit no encontrado | | 409 | `40901049` | Este punto de venta ya está marcado como CBU informada para este cliente | ### PATCH /represented-cuits/{id}/cbu-informada-sales-points/{salesPointNumber} **Corrige el CBU de un punto de venta ya marcado como CBU informada** Único campo editable después de marcar un punto de venta — para cambiar el número de punto de venta en sí hay que desmarcarlo y marcar el nuevo. `cbu` acepta `null` para quitarlo sin desmarcar el punto (la leyenda se sigue imprimiendo igual, solo sin el número de cuenta). **Auth:** Bearer token: API Client (client_credentials) o sesión de usuario (JWT). **Parámetros:** | Nombre | En | Tipo | Obligatorio | Descripción | |---|---|---|---|---| | `id` | path | string | sí | Id del CUIT (propio o delegado). | | `salesPointNumber` | path | number | sí | Número de punto de venta a corregir. | **Body (JSON):** | Campo | Tipo | Obligatorio | Descripción | |---|---|---|---| | `cbu` | string \| null | sí | CBU nuevo, o null para quitarlo (la leyenda se sigue imprimiendo igual, sin el número de cuenta). | **Respuesta 200** **Errores:** | Status | code | Cuándo | |---|---|---| | 400 | | Campos faltantes/mal tipados (class-validator) | | 401 | `40101037` | Falta el header Authorization con un Bearer token | | 401 | `40101038` | El token es inválido o expiró | | 401 | `40101039` | No se encontró la clave pública para verificar el token (kid) | | 401 | `40101040` | No se pudieron obtener las claves públicas para verificar el token | | 401 | `40101042` | El Api Client es inválido o fue revocado | | 404 | `40401007` | RepresentedCuit no encontrado | | 404 | `40401050` | No se encontró ese punto de venta marcado como CBU informada | ### DELETE /represented-cuits/{id}/cbu-informada-sales-points/{salesPointNumber} **Desmarca un punto de venta de la modalidad CBU informada** **Auth:** Bearer token: API Client (client_credentials) o sesión de usuario (JWT). **Parámetros:** | Nombre | En | Tipo | Obligatorio | Descripción | |---|---|---|---|---| | `id` | path | string | sí | Id del CUIT (propio o delegado). | | `salesPointNumber` | path | number | sí | Número de punto de venta a desmarcar. | **Respuesta 204** **Errores:** | Status | code | Cuándo | |---|---|---| | 400 | | Campos faltantes/mal tipados (class-validator) | | 401 | `40101037` | Falta el header Authorization con un Bearer token | | 401 | `40101038` | El token es inválido o expiró | | 401 | `40101039` | No se encontró la clave pública para verificar el token (kid) | | 401 | `40101040` | No se pudieron obtener las claves públicas para verificar el token | | 401 | `40101042` | El Api Client es inválido o fue revocado | | 404 | `40401007` | RepresentedCuit no encontrado | | 404 | `40401050` | No se encontró ese punto de venta marcado como CBU informada | # Datos de referencia ## catalogs Catálogos de referencia (tipos de comprobante, alícuotas de IVA, tipos de documento, puntos de venta, monedas, etc.) para completar los campos codificados de POST /invoices y otros endpoints. ### GET /catalogs/provincias **Provincias** Provincias argentinas (más CABA), con su código y nombre. Sin uso directo en esta API todavía — expuesto de referencia. **Auth:** Bearer token: API Client (client_credentials) o sesión de usuario (JWT). **Respuesta 200** ```json [ { "codigo": 0, "descripcion": "Ciudad Autónoma de Buenos Aires" }, { "codigo": 1, "descripcion": "Buenos Aires" }, { "codigo": 3, "descripcion": "Córdoba" } ] ``` **Errores:** | Status | code | Cuándo | |---|---|---| | 401 | `40101037` | Falta el header Authorization con un Bearer token | | 401 | `40101038` | El token es inválido o expiró | | 401 | `40101039` | No se encontró la clave pública para verificar el token (kid) | | 401 | `40101040` | No se pudieron obtener las claves públicas para verificar el token | | 401 | `40101042` | El Api Client es inválido o fue revocado | ### GET /catalogs/tipos-responsable **Condición frente al IVA** 1=Responsable Inscripto, 5=Consumidor Final, 6=Monotributo, etc. Usado en `receptor.tax_condition` de POST /invoices. **Auth:** Bearer token: API Client (client_credentials) o sesión de usuario (JWT). **Respuesta 200** ```json [ { "codigo": 1, "descripcion": "IVA Responsable Inscripto" }, { "codigo": 5, "descripcion": "Consumidor Final" }, { "codigo": 6, "descripcion": "Responsable Monotributo" } ] ``` **Errores:** | Status | code | Cuándo | |---|---|---| | 401 | `40101037` | Falta el header Authorization con un Bearer token | | 401 | `40101038` | El token es inválido o expiró | | 401 | `40101039` | No se encontró la clave pública para verificar el token (kid) | | 401 | `40101040` | No se pudieron obtener las claves públicas para verificar el token | | 401 | `40101042` | El Api Client es inválido o fue revocado | ### GET /catalogs/tipos-documento **Tipos de documento de identidad** CUIT, CUIL, DNI, Pasaporte, etc. Usado en `receptor.identification.document_type` de POST /invoices y `receptor_document_type` de POST /voucher-verifications. **Auth:** Bearer token: API Client (client_credentials) o sesión de usuario (JWT). **Respuesta 200** ```json [ { "codigo": 80, "descripcion": "CUIT" }, { "codigo": 94, "descripcion": "Pasaporte" }, { "codigo": 96, "descripcion": "DNI" } ] ``` **Errores:** | Status | code | Cuándo | |---|---|---| | 401 | `40101037` | Falta el header Authorization con un Bearer token | | 401 | `40101038` | El token es inválido o expiró | | 401 | `40101039` | No se encontró la clave pública para verificar el token (kid) | | 401 | `40101040` | No se pudieron obtener las claves públicas para verificar el token | | 401 | `40101042` | El Api Client es inválido o fue revocado | ### GET /catalogs/idiomas **Idiomas del comprobante** Usado en `export.invoice_language` de POST /invoices (exportación). **Auth:** Bearer token: API Client (client_credentials) o sesión de usuario (JWT). **Respuesta 200** ```json [ { "codigo": 1, "descripcion": "Español" }, { "codigo": 2, "descripcion": "Inglés" }, { "codigo": 3, "descripcion": "Portugués" } ] ``` **Errores:** | Status | code | Cuándo | |---|---|---| | 401 | `40101037` | Falta el header Authorization con un Bearer token | | 401 | `40101038` | El token es inválido o expiró | | 401 | `40101039` | No se encontró la clave pública para verificar el token (kid) | | 401 | `40101040` | No se pudieron obtener las claves públicas para verificar el token | | 401 | `40101042` | El Api Client es inválido o fue revocado | ### GET /catalogs/incoterms **Incoterms** EXW, FOB, CIF, etc. Usado en `export.incoterms` de POST /invoices (exportación). **Auth:** Bearer token: API Client (client_credentials) o sesión de usuario (JWT). **Respuesta 200** ```json [ { "codigo": "EXW", "descripcion": "Ex Works" }, { "codigo": "FOB", "descripcion": "Free On Board" }, { "codigo": "CIF", "descripcion": "Cost, Insurance and Freight" } ] ``` **Errores:** | Status | code | Cuándo | |---|---|---| | 401 | `40101037` | Falta el header Authorization con un Bearer token | | 401 | `40101038` | El token es inválido o expiró | | 401 | `40101039` | No se encontró la clave pública para verificar el token (kid) | | 401 | `40101040` | No se pudieron obtener las claves públicas para verificar el token | | 401 | `40101042` | El Api Client es inválido o fue revocado | ### GET /catalogs/unidad-medida **Unidades de medida** Kg, metros, litros, etc. Usado en `items[].unit_of_measure_id` de POST /invoices (cualquier familia con renglones de detalle). **Auth:** Bearer token: API Client (client_credentials) o sesión de usuario (JWT). **Respuesta 200** ```json [ { "codigo": "01", "descripcion": "KILOGRAMO" }, { "codigo": "02", "descripcion": "METROS" }, { "codigo": "07", "descripcion": "UNIDAD" } ] ``` **Errores:** | Status | code | Cuándo | |---|---|---| | 401 | `40101037` | Falta el header Authorization con un Bearer token | | 401 | `40101038` | El token es inválido o expiró | | 401 | `40101039` | No se encontró la clave pública para verificar el token (kid) | | 401 | `40101040` | No se pudieron obtener las claves públicas para verificar el token | | 401 | `40101042` | El Api Client es inválido o fue revocado | ### GET /catalogs/cuit-pais **CUIT genérico por país** Identificador genérico que ARCA asigna a cada país para clientes del exterior. Usado en `receptor.identification.value` de POST /invoices cuando `identification.category=FOREIGN_TAX_ID` (exportación/turismo). **Auth:** Bearer token: API Client (client_credentials) o sesión de usuario (JWT). **Respuesta 200** ```json [ { "cuit_pais": "50000000024", "descripcion": "PARAGUAY", "codigo_tipo_sujeto": 1 }, { "cuit_pais": "50000002124", "descripcion": "ESTADOS UNIDOS", "codigo_tipo_sujeto": 1 } ] ``` **Errores:** | Status | code | Cuándo | |---|---|---| | 401 | `40101037` | Falta el header Authorization con un Bearer token | | 401 | `40101038` | El token es inválido o expiró | | 401 | `40101039` | No se encontró la clave pública para verificar el token (kid) | | 401 | `40101040` | No se pudieron obtener las claves públicas para verificar el token | | 401 | `40101042` | El Api Client es inválido o fue revocado | ### GET /catalogs/operacion-condicion-iva **Condición IVA/operación (tabla legado)** Alícuotas y valores como "No Corresponde"/"Exento"/"Gravado". Tabla legado de AFIP, sin uso directo en esta API — expuesta de referencia. **Auth:** Bearer token: API Client (client_credentials) o sesión de usuario (JWT). **Respuesta 200** ```json [ { "codigo": 2, "descripcion": "Exento", "observaciones": null }, { "codigo": 4, "descripcion": "10.5%", "observaciones": null }, { "codigo": 5, "descripcion": "21%", "observaciones": null } ] ``` **Errores:** | Status | code | Cuándo | |---|---|---| | 401 | `40101037` | Falta el header Authorization con un Bearer token | | 401 | `40101038` | El token es inválido o expiró | | 401 | `40101039` | No se encontró la clave pública para verificar el token (kid) | | 401 | `40101040` | No se pudieron obtener las claves públicas para verificar el token | | 401 | `40101042` | El Api Client es inválido o fue revocado | ### GET /catalogs/tipos-comprobante **Tipos de comprobante** 1=Factura A, 6=Factura B, 11=Factura C, etc. Usado en `GET /invoices?voucher_type`, `associations[].related_voucher_type` (nota de crédito/débito) y `voucher_type` de POST /voucher-verifications. **Auth:** Bearer token: API Client (client_credentials) o sesión de usuario (JWT). **Parámetros:** | Nombre | En | Tipo | Obligatorio | Descripción | |---|---|---|---|---| | `represented_cuit_id` | query | string | no | Solo para un contador o usuario en autoservicio que factura por varios clientes — elige con cuál credential consultar el catálogo. Un usuario final nunca lo manda: se resuelve solo contra su propio certificado, o contra el certificado de la plataforma si no tiene uno propio. | **Respuesta 200** ```json [ { "id": 1, "description": "Factura A", "start_date": "2010-01-01", "end_date": null }, { "id": 6, "description": "Factura B", "start_date": "2010-01-01", "end_date": null }, { "id": 11, "description": "Factura C", "start_date": "2010-01-01", "end_date": null } ] ``` **Errores:** | Status | code | Cuándo | |---|---|---| | 400 | `40001009` | El usuario no tiene un CUIT configurado | | 400 | | Campos faltantes/mal tipados (class-validator) | | 401 | `40101037` | Falta el header Authorization con un Bearer token | | 401 | `40101038` | El token es inválido o expiró | | 401 | `40101039` | No se encontró la clave pública para verificar el token (kid) | | 401 | `40101040` | No se pudieron obtener las claves públicas para verificar el token | | 401 | `40101042` | El Api Client es inválido o fue revocado | | 403 | `40301008` | La credential asociada fue revocada | | 404 | `40401007` | RepresentedCuit no encontrado | | 503 | `50301010` | No hay ninguna credential de plataforma activa configurada | ### GET /catalogs/alicuotas-iva **Alícuotas de IVA** Usado en `vat_items[].vat_rate_id` de POST /invoices y en `tourism.items[].vat_rate_id`. **Auth:** Bearer token: API Client (client_credentials) o sesión de usuario (JWT). **Parámetros:** | Nombre | En | Tipo | Obligatorio | Descripción | |---|---|---|---|---| | `represented_cuit_id` | query | string | no | Solo para un contador o usuario en autoservicio que factura por varios clientes — elige con cuál credential consultar el catálogo. Un usuario final nunca lo manda: se resuelve solo contra su propio certificado, o contra el certificado de la plataforma si no tiene uno propio. | **Respuesta 200** ```json [ { "id": 3, "description": "0%", "start_date": "2010-01-01", "end_date": null }, { "id": 4, "description": "10.5%", "start_date": "2010-01-01", "end_date": null }, { "id": 5, "description": "21%", "start_date": "2010-01-01", "end_date": null } ] ``` **Errores:** | Status | code | Cuándo | |---|---|---| | 400 | `40001009` | El usuario no tiene un CUIT configurado | | 400 | | Campos faltantes/mal tipados (class-validator) | | 401 | `40101037` | Falta el header Authorization con un Bearer token | | 401 | `40101038` | El token es inválido o expiró | | 401 | `40101039` | No se encontró la clave pública para verificar el token (kid) | | 401 | `40101040` | No se pudieron obtener las claves públicas para verificar el token | | 401 | `40101042` | El Api Client es inválido o fue revocado | | 403 | `40301008` | La credential asociada fue revocada | | 404 | `40401007` | RepresentedCuit no encontrado | | 503 | `50301010` | No hay ninguna credential de plataforma activa configurada | ### GET /catalogs/tipos-documento-receptor **Tipos de documento del receptor** 80=CUIT, 96=DNI, etc. Usado en `receptor.identification.document_type` de POST /invoices y `receptor_document_type` de POST /voucher-verifications. Complementa GET /catalogs/tipos-documento (esa es una tabla local de mantenimiento manual; esta siempre está actualizada). **Auth:** Bearer token: API Client (client_credentials) o sesión de usuario (JWT). **Parámetros:** | Nombre | En | Tipo | Obligatorio | Descripción | |---|---|---|---|---| | `represented_cuit_id` | query | string | no | Solo para un contador o usuario en autoservicio que factura por varios clientes — elige con cuál credential consultar el catálogo. Un usuario final nunca lo manda: se resuelve solo contra su propio certificado, o contra el certificado de la plataforma si no tiene uno propio. | **Respuesta 200** ```json [ { "id": 80, "description": "CUIT", "start_date": "2010-01-01", "end_date": null }, { "id": 96, "description": "DNI", "start_date": "2010-01-01", "end_date": null }, { "id": 99, "description": "Consumidor Final", "start_date": "2010-01-01", "end_date": null } ] ``` **Errores:** | Status | code | Cuándo | |---|---|---| | 400 | `40001009` | El usuario no tiene un CUIT configurado | | 400 | | Campos faltantes/mal tipados (class-validator) | | 401 | `40101037` | Falta el header Authorization con un Bearer token | | 401 | `40101038` | El token es inválido o expiró | | 401 | `40101039` | No se encontró la clave pública para verificar el token (kid) | | 401 | `40101040` | No se pudieron obtener las claves públicas para verificar el token | | 401 | `40101042` | El Api Client es inválido o fue revocado | | 403 | `40301008` | La credential asociada fue revocada | | 404 | `40401007` | RepresentedCuit no encontrado | | 503 | `50301010` | No hay ninguna credential de plataforma activa configurada | ### GET /catalogs/monedas **Códigos de moneda** Usado en `currency_code` de POST /invoices (facturas locales, de exportación y de turismo). **Auth:** Bearer token: API Client (client_credentials) o sesión de usuario (JWT). **Parámetros:** | Nombre | En | Tipo | Obligatorio | Descripción | |---|---|---|---|---| | `represented_cuit_id` | query | string | no | Solo para un contador o usuario en autoservicio que factura por varios clientes — elige con cuál credential consultar el catálogo. Un usuario final nunca lo manda: se resuelve solo contra su propio certificado, o contra el certificado de la plataforma si no tiene uno propio. | **Respuesta 200** ```json [ { "id": "PES", "description": "Pesos Argentinos", "start_date": "2010-01-01", "end_date": null }, { "id": "DOL", "description": "Dolar Estadounidense", "start_date": "2010-01-01", "end_date": null } ] ``` **Errores:** | Status | code | Cuándo | |---|---|---| | 400 | `40001009` | El usuario no tiene un CUIT configurado | | 400 | | Campos faltantes/mal tipados (class-validator) | | 401 | `40101037` | Falta el header Authorization con un Bearer token | | 401 | `40101038` | El token es inválido o expiró | | 401 | `40101039` | No se encontró la clave pública para verificar el token (kid) | | 401 | `40101040` | No se pudieron obtener las claves públicas para verificar el token | | 401 | `40101042` | El Api Client es inválido o fue revocado | | 403 | `40301008` | La credential asociada fue revocada | | 404 | `40401007` | RepresentedCuit no encontrado | | 503 | `50301010` | No hay ninguna credential de plataforma activa configurada | ### GET /catalogs/puntos-venta **Puntos de venta** Puntos de venta habilitados para el CUIT consultado (se dan de alta en el portal de AFIP, no vía esta API). Usado en `sales_point_number` de POST /invoices, GET /invoices y POST /voucher-verifications. **Auth:** Bearer token: API Client (client_credentials) o sesión de usuario (JWT). **Parámetros:** | Nombre | En | Tipo | Obligatorio | Descripción | |---|---|---|---|---| | `represented_cuit_id` | query | string | no | Solo para un contador o usuario en autoservicio que factura por varios clientes — elige con cuál credential consultar el catálogo. Un usuario final nunca lo manda: se resuelve solo contra su propio certificado, o contra el certificado de la plataforma si no tiene uno propio. | **Respuesta 200** ```json [ { "number": 1, "emission_type": "CAE", "blocked": false, "deactivated_at": null }, { "number": 2, "emission_type": "CAEA", "blocked": false, "deactivated_at": null } ] ``` **Errores:** | Status | code | Cuándo | |---|---|---| | 400 | `40001009` | El usuario no tiene un CUIT configurado | | 400 | | Campos faltantes/mal tipados (class-validator) | | 401 | `40101037` | Falta el header Authorization con un Bearer token | | 401 | `40101038` | El token es inválido o expiró | | 401 | `40101039` | No se encontró la clave pública para verificar el token (kid) | | 401 | `40101040` | No se pudieron obtener las claves públicas para verificar el token | | 401 | `40101042` | El Api Client es inválido o fue revocado | | 403 | `40301008` | La credential asociada fue revocada | | 404 | `40401007` | RepresentedCuit no encontrado | | 503 | `50301010` | No hay ninguna credential de plataforma activa configurada | ### GET /catalogs/conceptos **Conceptos** Productos/servicios/ambos. Esta API declara el concepto directo como `concept_type` (PRODUCTS/SERVICES/PRODUCTS_AND_SERVICES) en POST /invoices, sin pasar por este catálogo — expuesto solo de referencia. **Auth:** Bearer token: API Client (client_credentials) o sesión de usuario (JWT). **Parámetros:** | Nombre | En | Tipo | Obligatorio | Descripción | |---|---|---|---|---| | `represented_cuit_id` | query | string | no | Solo para un contador o usuario en autoservicio que factura por varios clientes — elige con cuál credential consultar el catálogo. Un usuario final nunca lo manda: se resuelve solo contra su propio certificado, o contra el certificado de la plataforma si no tiene uno propio. | **Respuesta 200** ```json [ { "id": 1, "description": "Productos", "start_date": "2010-01-01", "end_date": null }, { "id": 2, "description": "Servicios", "start_date": "2010-01-01", "end_date": null }, { "id": 3, "description": "Productos y Servicios", "start_date": "2010-01-01", "end_date": null } ] ``` **Errores:** | Status | code | Cuándo | |---|---|---| | 400 | `40001009` | El usuario no tiene un CUIT configurado | | 400 | | Campos faltantes/mal tipados (class-validator) | | 401 | `40101037` | Falta el header Authorization con un Bearer token | | 401 | `40101038` | El token es inválido o expiró | | 401 | `40101039` | No se encontró la clave pública para verificar el token (kid) | | 401 | `40101040` | No se pudieron obtener las claves públicas para verificar el token | | 401 | `40101042` | El Api Client es inválido o fue revocado | | 403 | `40301008` | La credential asociada fue revocada | | 404 | `40401007` | RepresentedCuit no encontrado | | 503 | `50301010` | No hay ninguna credential de plataforma activa configurada | ### GET /catalogs/tipos-tributo **Tipos de tributo** Usado en `tax_items[].tax_type_id` de POST /invoices. **Auth:** Bearer token: API Client (client_credentials) o sesión de usuario (JWT). **Parámetros:** | Nombre | En | Tipo | Obligatorio | Descripción | |---|---|---|---|---| | `represented_cuit_id` | query | string | no | Solo para un contador o usuario en autoservicio que factura por varios clientes — elige con cuál credential consultar el catálogo. Un usuario final nunca lo manda: se resuelve solo contra su propio certificado, o contra el certificado de la plataforma si no tiene uno propio. | **Respuesta 200** ```json [ { "id": 1, "description": "Impuestos Nacionales", "start_date": "2010-01-01", "end_date": null }, { "id": 3, "description": "Impuestos Municipales", "start_date": "2010-01-01", "end_date": null }, { "id": 99, "description": "Otro", "start_date": "2010-01-01", "end_date": null } ] ``` **Errores:** | Status | code | Cuándo | |---|---|---| | 400 | `40001009` | El usuario no tiene un CUIT configurado | | 400 | | Campos faltantes/mal tipados (class-validator) | | 401 | `40101037` | Falta el header Authorization con un Bearer token | | 401 | `40101038` | El token es inválido o expiró | | 401 | `40101039` | No se encontró la clave pública para verificar el token (kid) | | 401 | `40101040` | No se pudieron obtener las claves públicas para verificar el token | | 401 | `40101042` | El Api Client es inválido o fue revocado | | 403 | `40301008` | La credential asociada fue revocada | | 404 | `40401007` | RepresentedCuit no encontrado | | 503 | `50301010` | No hay ninguna credential de plataforma activa configurada | ### GET /catalogs/condiciones-iva-receptor **Condición frente al IVA del receptor** Vigente desde RG 5616. Usado en `receptor.tax_condition` de POST /invoices. Complementa GET /catalogs/tipos-responsable (esa es la tabla legado de mantenimiento manual; esta siempre está actualizada). **Auth:** Bearer token: API Client (client_credentials) o sesión de usuario (JWT). **Parámetros:** | Nombre | En | Tipo | Obligatorio | Descripción | |---|---|---|---|---| | `represented_cuit_id` | query | string | no | Solo para un contador o usuario en autoservicio que factura por varios clientes — elige con cuál credential consultar el catálogo. Un usuario final nunca lo manda: se resuelve solo contra su propio certificado, o contra el certificado de la plataforma si no tiene uno propio. | **Respuesta 200** ```json [ { "id": 1, "description": "IVA Responsable Inscripto", "start_date": "2022-01-01", "end_date": null }, { "id": 5, "description": "Consumidor Final", "start_date": "2022-01-01", "end_date": null }, { "id": 6, "description": "Responsable Monotributo", "start_date": "2022-01-01", "end_date": null } ] ``` **Errores:** | Status | code | Cuándo | |---|---|---| | 400 | `40001009` | El usuario no tiene un CUIT configurado | | 400 | | Campos faltantes/mal tipados (class-validator) | | 401 | `40101037` | Falta el header Authorization con un Bearer token | | 401 | `40101038` | El token es inválido o expiró | | 401 | `40101039` | No se encontró la clave pública para verificar el token (kid) | | 401 | `40101040` | No se pudieron obtener las claves públicas para verificar el token | | 401 | `40101042` | El Api Client es inválido o fue revocado | | 403 | `40301008` | La credential asociada fue revocada | | 404 | `40401007` | RepresentedCuit no encontrado | | 503 | `50301010` | No hay ninguna credential de plataforma activa configurada | ### GET /catalogs/campos-opcionales **Campos opcionales del comprobante** Campos opcionales que ARCA admite en un comprobante. Sin uso directo en esta API todavía — expuesto de referencia para quien necesite mandar uno que esta API no modela explícitamente. **Auth:** Bearer token: API Client (client_credentials) o sesión de usuario (JWT). **Parámetros:** | Nombre | En | Tipo | Obligatorio | Descripción | |---|---|---|---|---| | `represented_cuit_id` | query | string | no | Solo para un contador o usuario en autoservicio que factura por varios clientes — elige con cuál credential consultar el catálogo. Un usuario final nunca lo manda: se resuelve solo contra su propio certificado, o contra el certificado de la plataforma si no tiene uno propio. | **Respuesta 200** ```json [ { "id": "17", "description": "Fecha Vto Servicio", "start_date": "2010-01-01", "end_date": null }, { "id": "22", "description": "CBU", "start_date": "2010-01-01", "end_date": null } ] ``` **Errores:** | Status | code | Cuándo | |---|---|---| | 400 | `40001009` | El usuario no tiene un CUIT configurado | | 400 | | Campos faltantes/mal tipados (class-validator) | | 401 | `40101037` | Falta el header Authorization con un Bearer token | | 401 | `40101038` | El token es inválido o expiró | | 401 | `40101039` | No se encontró la clave pública para verificar el token (kid) | | 401 | `40101040` | No se pudieron obtener las claves públicas para verificar el token | | 401 | `40101042` | El Api Client es inválido o fue revocado | | 403 | `40301008` | La credential asociada fue revocada | | 404 | `40401007` | RepresentedCuit no encontrado | | 503 | `50301010` | No hay ninguna credential de plataforma activa configurada | ### GET /catalogs/paises **Países** Usado en `receptor.country_code` y `export.destination_country_code`/`export.permits[].destination_country_code` de POST /invoices (exportación/turismo). **Auth:** Bearer token: API Client (client_credentials) o sesión de usuario (JWT). **Parámetros:** | Nombre | En | Tipo | Obligatorio | Descripción | |---|---|---|---|---| | `represented_cuit_id` | query | string | no | Solo para un contador o usuario en autoservicio que factura por varios clientes — elige con cuál credential consultar el catálogo. Un usuario final nunca lo manda: se resuelve solo contra su propio certificado, o contra el certificado de la plataforma si no tiene uno propio. | **Respuesta 200** ```json [ { "id": 200, "description": "ESTADOS UNIDOS", "start_date": "2010-01-01", "end_date": null }, { "id": 203, "description": "BRASIL", "start_date": "2010-01-01", "end_date": null } ] ``` **Errores:** | Status | code | Cuándo | |---|---|---| | 400 | `40001009` | El usuario no tiene un CUIT configurado | | 400 | | Campos faltantes/mal tipados (class-validator) | | 401 | `40101037` | Falta el header Authorization con un Bearer token | | 401 | `40101038` | El token es inválido o expiró | | 401 | `40101039` | No se encontró la clave pública para verificar el token (kid) | | 401 | `40101040` | No se pudieron obtener las claves públicas para verificar el token | | 401 | `40101042` | El Api Client es inválido o fue revocado | | 403 | `40301008` | La credential asociada fue revocada | | 404 | `40401007` | RepresentedCuit no encontrado | | 503 | `50301010` | No hay ninguna credential de plataforma activa configurada | ## padron Consulta pública y de solo lectura del padrón de ARCA (razón social, domicilio, condición frente al IVA) para previsualizar un CUIT antes de guardarlo en algún lado. ### GET /padron/{cuit} **Consulta en vivo el padrón de ARCA para previsualizar los datos de un contribuyente** Público dentro de la plataforma: no está scoped al usuario autenticado, cualquiera puede consultar cualquier CUIT/CUIL. 404 si ARCA no reconoce ese CUIT como un contribuyente real. **Auth:** Bearer token: API Client (client_credentials) o sesión de usuario (JWT). **Parámetros:** | Nombre | En | Tipo | Obligatorio | Descripción | |---|---|---|---|---| | `cuit` | path | string | sí | CUIT o CUIL a consultar (11 dígitos, sin guiones). | **Respuesta 200** **Errores:** | Status | code | Cuándo | |---|---|---| | 401 | `40101037` | Falta el header Authorization con un Bearer token | | 401 | `40101038` | El token es inválido o expiró | | 401 | `40101039` | No se encontró la clave pública para verificar el token (kid) | | 401 | `40101040` | No se pudieron obtener las claves públicas para verificar el token | | 401 | `40101042` | El Api Client es inválido o fue revocado | | 404 | `40401036` | ARCA no devolvió constancia para este CUIT | ## errors Catálogo completo de errores que puede devolver esta API — pensado para generar documentación aparte o un diccionario de errores del lado del cliente. Sin autenticación, es metadata estática del contrato. ### GET /errors **Catálogo completo de errores que puede devolver esta API** Todo `ApiError` declarado en el servicio (ver common/errors/api-error.ts), con su `code` compuesto ya resuelto contra el API_NUMBER real de este deployment. Orden estable por `id` ascendente. **Auth:** Sin autenticación. **Respuesta 200** # Acceso programático ## api-clients Credenciales OAuth 2.0 (client_credentials) para acceso programático — integraciones server-to-server y agentes. Siempre se gestionan con un token de sesión de usuario, nunca con otro API Client. ### GET /api-clients **Lista los API Clients del usuario (nunca expone el client_secret, solo metadata + client_id)** **Auth:** Bearer token: sesión de usuario (JWT). **Parámetros:** | Nombre | En | Tipo | Obligatorio | Descripción | |---|---|---|---|---| | `page` | query | number | no | Número de página, arranca en 1. Default: 1. (mín. 1) | | `size` | query | number | no | Cantidad de resultados por página. Default 20, máximo 100 (valores mayores se recortan a 100, nunca rechazan la request). (mín. 1) | **Respuesta 200**: Resultado paginado. **Errores:** | Status | code | Cuándo | |---|---|---| | 400 | | Campos faltantes/mal tipados (class-validator) | | 401 | `40101037` | Falta el header Authorization con un Bearer token | | 401 | `40101038` | El token es inválido o expiró | | 401 | `40101039` | No se encontró la clave pública para verificar el token (kid) | | 401 | `40101040` | No se pudieron obtener las claves públicas para verificar el token | ### POST /api-clients **Crea un API Client (credenciales OAuth 2.0 client_credentials)** Para integraciones server-to-server y agentes, no para uso humano interactivo. Requiere un token de sesión de usuario (no puede crearse con otro API Client). Devuelve client_id + metadata — el client_secret NO viaja acá, se pide aparte con GET /api-clients/{id}/secret. **Auth:** Bearer token: sesión de usuario (JWT). **Body (JSON):** | Campo | Tipo | Obligatorio | Descripción | |---|---|---|---| | `label` | string | sí | Nombre descriptivo para identificar este API Client (ej. "CI/CD", "Zapier"). | **Respuesta 201** **Errores:** | Status | code | Cuándo | |---|---|---| | 400 | | Campos faltantes/mal tipados (class-validator) | | 401 | `40101037` | Falta el header Authorization con un Bearer token | | 401 | `40101038` | El token es inválido o expiró | | 401 | `40101039` | No se encontró la clave pública para verificar el token (kid) | | 401 | `40101040` | No se pudieron obtener las claves públicas para verificar el token | | 500 | `50001047` | No se pudo completar la operación sobre el API Client — reintentá más tarde | ### GET /api-clients/{id}/secret **Devuelve el client_secret de un API Client propio** A demanda, no "se ve una sola vez": se puede volver a pedir cuantas veces haga falta. Requiere un token de sesión de usuario (no puede pedirse con otro API Client). **Auth:** Bearer token: sesión de usuario (JWT). **Parámetros:** | Nombre | En | Tipo | Obligatorio | Descripción | |---|---|---|---|---| | `id` | path | string | sí | Id del API Client. | **Respuesta 200** **Errores:** | Status | code | Cuándo | |---|---|---| | 401 | `40101037` | Falta el header Authorization con un Bearer token | | 401 | `40101038` | El token es inválido o expiró | | 401 | `40101039` | No se encontró la clave pública para verificar el token (kid) | | 401 | `40101040` | No se pudieron obtener las claves públicas para verificar el token | | 404 | `40401035` | Api Client no encontrado | | 500 | `50001047` | No se pudo completar la operación sobre el API Client — reintentá más tarde | ### DELETE /api-clients/{id} **Revoca un API Client — deja de servir para autenticar** **Auth:** Bearer token: sesión de usuario (JWT). **Parámetros:** | Nombre | En | Tipo | Obligatorio | Descripción | |---|---|---|---|---| | `id` | path | string | sí | Id del API Client a revocar. | **Respuesta 204** **Errores:** | Status | code | Cuándo | |---|---|---| | 401 | `40101037` | Falta el header Authorization con un Bearer token | | 401 | `40101038` | El token es inválido o expiró | | 401 | `40101039` | No se encontró la clave pública para verificar el token (kid) | | 401 | `40101040` | No se pudieron obtener las claves públicas para verificar el token | | 404 | `40401035` | Api Client no encontrado | | 500 | `50001047` | No se pudo completar la operación sobre el API Client — reintentá más tarde | # Operación ## health Healthcheck sin autenticación. ### GET /health **Healthcheck sin autenticación** **Auth:** Sin autenticación. **Respuesta 200**