> ## Documentation Index
> Fetch the complete documentation index at: https://docs.filexpress.app/llms.txt
> Use this file to discover all available pages before exploring further.

# API v2: qué cambia

> Moneda extranjera, impuestos por id y errores con código estable en ventas y compras

La versión 2 de la API Externa agrega **multi-moneda** y **impuestos por id** a las ventas y compras, y unifica el formato de errores con un `code` estable que tu integración puede evaluar sin leer el texto del mensaje.

## URL base

```
https://api.filexpress.app/api/external/v2
```

Usa la **misma API Key** (`X-API-Key`) y los mismos permisos que v1, más el permiso nuevo `currencies`. La autenticación, el [límite de peticiones](/docs/api/introduccion.md#límite-de-peticiones) y el [alcance por negocio](/docs/api/introduccion.md#alcance-de-los-datos) son idénticos.

> [!NOTE]
>
> v2 **no reemplaza** a v1: solo expone los endpoints que cambian. Todo lo demás (clientes, proveedores, productos, inventario, RRHH, contabilidad, cuentas por cobrar y pagar, etc.) sigue únicamente en `/api/external/v1`. Una integración puede usar v1 y v2 a la vez con la misma API Key.

## Endpoints disponibles

| Endpoint | Permiso | Descripción |
| - | - | - |
| `GET /health` | Ninguno | Verifica la conexión. Responde `Working` sin autenticación. |
| `GET /currencies` | `currencies` | Moneda base y monedas activas del negocio. Ver [Monedas y tasas](/docs/api/v2/monedas-y-tasas.md#listar-monedas). |
| `GET /exchange-rates` | `currencies` | Tasas de cambio del negocio, de la más reciente a la más antigua. |
| `GET /exchange-rates/resolve` | `currencies` | La tasa que se aplicaría a un documento en una fecha. |
| `POST /sales` | `sales` | [Crear venta](/docs/api/v2/ventas.md#crear-venta) con moneda e impuestos por id. |
| `GET /sales/{id}` | `sales` | [Consultar venta](/docs/api/v2/ventas.md#consultar-venta) con su referencia de moneda. |
| `POST /expenses` | `expenses` | [Registrar compra](/docs/api/v2/compras.md#registrar-compra) con moneda. |
| `GET /expenses/{id}` | `expenses` | [Consultar compra](/docs/api/v2/compras.md#consultar-compra) con su referencia de moneda. |

El permiso `currencies` se asigna en **Integraciones externas**, igual que `sales` y `expenses`: una integración con acceso total (heredado) ya lo incluye; una con permisos seleccionados debe tenerlo marcado. Si llamas a un endpoint de monedas sin él, recibes `403` con `required_scope: "currencies"`.

## Qué cambia respecto a v1

| Tema | v1 | v2 |
| - | - | - |
| Moneda del documento | No acepta moneda: los montos son siempre de la moneda base del negocio. Ignora `currency_code` y `exchange_rate`. | Acepta `currency_code` y `exchange_rate` en ventas y compras. Los montos van en la moneda del documento. |
| `tax_ids` por línea | Opcional. | **Obligatorio** en cada línea de `products` (puede ir vacío: `[]`). |
| Impuestos en ventas | Si el monto enviado difiere del recalculado, queda como advertencia interna. | **Modo estricto**: un monto distinto al recalculado responde `422` con `code: "tax_invalid"`. |
| Respuesta de documentos | Sin datos de moneda. | Agrega `currency_code`, `exchange_rate`, `foreign_total` y `base_currency_code`. |
| Errores | `{ "status": false, "message", "errors"? }` | Agrega `code` estable: `{ "status": false, "code", "message", "errors"? }`. |

Dentro de v2 el resto del contrato (campos de la venta y la compra, totales, centros de costo, punto de venta, etc.) es el mismo que en v1: consulta [Ventas](/docs/api/facturacion/ventas.md) y [Compras](/docs/api/facturacion/compras.md) para los campos que no cambian.

## Cómo funciona la moneda

**1. El negocio tiene una moneda base**

Es la moneda en la que FileXpress guarda y contabiliza todo. Consúltala con [`GET /currencies`](/docs/api/v2/monedas-y-tasas#listar-monedas) (`base_currency_code`).

**2. Envías el documento en su moneda**

Pasas `currency_code` (por ejemplo `GTQ`) y todos los montos del documento (líneas, impuestos, pagos y totales) **en esa moneda**. Si omites `currency_code` o envías la moneda base, el documento se trata como en v1.

**3. El servidor convierte a la moneda base**

Aplica la tasa del documento (`exchange_rate` enviada, o la vigente a la fecha del documento), valida los impuestos y emite. El documento electrónico (DTE) sale en la **moneda legal del país**, siempre en moneda base.

La **tasa de cambio** se expresa como *unidades de moneda base por 1 unidad de moneda extranjera*. Con base USD y una tasa de `0.128` para GTQ, 1 GTQ = 0.128 USD y 1,000 GTQ = 128.00 USD.

> [!WARNING]
>
> La moneda debe estar **activa** para el negocio (configuración de multi-moneda en FileXpress) y, para documentos electrónicos, la moneda base del negocio debe ser la moneda legal del país de la sucursal. De lo contrario la API responde `422` con `currency_not_enabled` o `base_currency_not_legal`.

## Errores y códigos

Toda respuesta de error de v2 sigue este formato:

```json theme={null}
{
  "status": false,
  "code": "exchange_rate_not_found",
  "message": "No hay tasa de cambio para GTQ a la fecha 2026-10-21 ni anterior. Registre una tasa de cambio o envíe exchange_rate en el documento.",
  "errors": { "exchange_rate": "Sin tasa de cambio para GTQ al 2026-10-21." }
}
```

`errors` es opcional y su forma depende del error: un objeto `campo → mensaje(s)` en validación y monedas, o una lista de objetos `{ field, code, message, tax_id? }` en `tax_invalid`. Evalúa siempre `code`; el texto de `message` puede cambiar.

| `code` | HTTP | Cuándo |
| - | - | - |
| `currency_not_enabled` | `422` (`403` en `GET /currencies` y `/exchange-rates*`) | Multi-moneda no está habilitada para el negocio, la moneda no existe en el catálogo o no está activa para el negocio. |
| `exchange_rate_not_found` | `422` en documentos, `404` en `/exchange-rates/resolve` | No hay tasa a la fecha del documento ni anterior y no enviaste `exchange_rate`. |
| `exchange_rate_invalid` | `422` | `exchange_rate` no es numérica o es menor o igual que cero. |
| `base_currency_not_legal` | `422` | Documento **electrónico** en moneda extranjera y la moneda base del negocio no es la moneda legal del país. |
| `validation_error` | `422` | Falta o es inválido un campo de entrada (por ejemplo, `products.0.tax_ids` ausente). |
| `tax_invalid` | `422` | Los impuestos por id son inválidos o no coinciden con lo recalculado. Solo en ventas. Ver [Impuestos por id](/docs/api/v2/ventas.md#impuestos-por-id). |
| `unauthorized` | `401` | Falta `X-API-Key` o no es válida. |
| `forbidden` | `403` | La integración está desactivada o no tiene el permiso requerido. |
| `not_found` | `404` | Recurso inexistente. |
| `rate_limited` | `429` | Límite de peticiones excedido. |
| `error` | Otro | Cualquier otro error (por ejemplo `500`). |

> [!NOTE]
>
> Las respuestas que genera la propia capa de acceso **antes** de llegar al endpoint (API Key ausente o inválida, integración desactivada, permiso faltante, límite de peticiones) y el `404` de un recurso que pertenece a otro negocio (por ejemplo, `GET /sales/{id}` con un id ajeno) mantienen el formato `{ "status": false, "message": ... }` y **pueden no incluir `code`**. Si falta `code`, usa el código HTTP (`401`, `403`, `404`, `429`) como respaldo. El `403` por permiso faltante agrega `required_scope`.

## Siguientes pasos

> **[Monedas y tasas](/docs/api/v2/monedas-y-tasas.md)**
>
> Consulta monedas activas y tasas.

> **[Ventas](/docs/api/v2/ventas.md)**
>
> Emite ventas en moneda extranjera.

> **[Compras](/docs/api/v2/compras.md)**
>
> Registra compras en moneda extranjera.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.