> ## 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.

# Integraciones Externas

> API REST para conectar sistemas externos con FileXpress Hub — referencia completa de endpoints, parámetros y respuestas

Las integraciones externas permiten conectar sistemas de terceros (ERPs, tiendas en línea, aplicaciones propias) con FileXpress Hub mediante una API REST autenticada con credenciales propias.

## Crear una integración

<Steps>
  <Step title="Ir a Gestión → Integraciones → Nueva Integración" />

  <Step title="Ingresar el nombre descriptivo de la integración (mínimo 3 caracteres)" />

  <Step title="Seleccionar los endpoints permitidos">
    | Endpoint  | Datos accesibles               |
    | --------- | ------------------------------ |
    | Ventas    | Facturas y documentos de venta |
    | Gastos    | Compras y gastos registrados   |
    | Clientes  | Directorio de clientes         |
    | Productos | Catálogo de productos          |
  </Step>

  <Step title="Configurar el rate limit (solicitudes por minuto, máximo 1,000 — por defecto 100)" />

  <Step title="Guardar">
    El sistema genera el **API Key** y **API Secret**. Copiarlos antes de cerrar el modal — el Secret no se vuelve a mostrar completo.
  </Step>
</Steps>

<Warning>
  El API Secret solo se muestra una vez. Si se pierde, debe regenerarse desde el listado de integraciones (invalida el anterior).
</Warning>

## Gestionar credenciales

Desde el listado de integraciones:

* **Activar / Desactivar** sin eliminar la integración.
* **Regenerar API Key** — nuevo key, el anterior deja de funcionar.
* **Regenerar API Secret** — nuevo secret, el anterior deja de funcionar.

## Estadísticas y logs

* **Estadísticas**: volumen total, solicitudes exitosas, errores, tiempo promedio de respuesta, último uso, desglose por endpoint y errores recientes.
* **Logs**: historial paginado de cada llamada con estado HTTP, duración y cuerpo de respuesta.

***

## Autenticación

Todas las solicitudes deben incluir las dos cabeceras:

```http theme={null}
x-api-key: TU_API_KEY
x-api-secret: TU_API_SECRET
```

**URL base**: `https://api.filexpress.app/api/external`

| Código | Causa                                                                             |
| ------ | --------------------------------------------------------------------------------- |
| `401`  | Credenciales inválidas, desactivadas o cabeceras ausentes                         |
| `403`  | Endpoint no incluido en los permisos de la integración, o recurso de otro negocio |
| `404`  | Recurso no encontrado                                                             |
| `422`  | Error de validación — revisar `errors` en el cuerpo de la respuesta               |
| `429`  | Rate limit excedido                                                               |
| `500`  | Error interno — reportar con el log del request                                   |

## Obtener el `branch_id`

El `branch_id` es un UUID requerido en la mayoría de endpoints. Se obtiene desde la vista de soporte del panel de administración de FileXpress (**Support → Sucursales**), donde se listan todos los IDs de sucursales del negocio.

***

## Ventas

### Crear venta

```http theme={null}
POST /api/external/sales
```

<Warning>
  Este endpoint requiere que el sistema externo **pre-calcule los desgloses fiscales** por línea de producto y los totales globales. No los calcula automáticamente.
</Warning>

**Cuerpo de la solicitud**

```json theme={null}
{
  "branch_id": "uuid",
  "customer_id": "uuid",
  "document_type": "fc",
  "date": "2025-06-01T10:00:00",
  "observation": "Entrega en bodega central",
  "is_credit": false,
  "credit_days": 0,
  "payment_details": [
    {
      "code": "01",
      "payment_method": "Efectivo",
      "amount": 22.60,
      "reference": ""
    }
  ],
  "products": [
    {
      "id": "uuid-del-producto",
      "sku": "PRD-001",
      "name": "Producto A",
      "quantity": 2,
      "price_before_taxes": 10.00,
      "sale_not_suject": 0,
      "sale_exent": 0,
      "sale_taxed": 20.00,
      "subtotal": 20.00,
      "discount": 0,
      "type": "phisical"
    }
  ],
  "totals": {
    "netTotals": 20.00,
    "saleTotalNotSuject": 0,
    "saleTotalExent": 0,
    "saleTotalTax": 20.00,
    "totalTax": 2.60,
    "totalRetein": 0,
    "totalRent": 0,
    "freight": 0,
    "insurance": 0,
    "saleTotalToPayment": 22.60
  }
}
```

**Campos principales**

| Campo             | Tipo     | Requerido | Descripción                                                                                                                           |
| ----------------- | -------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `branch_id`       | uuid     | Sí        | Sucursal que emite la venta                                                                                                           |
| `customer_id`     | uuid     | Sí        | Cliente receptor                                                                                                                      |
| `document_type`   | string   | Sí        | `fc` (Factura Consumidor), `ccf` (Crédito Fiscal), `fex` (Exportación), `nc` (Nota Crédito), `nd` (Nota Débito), `nr` (Nota Remisión) |
| `date`            | datetime | No        | Fecha/hora de emisión (ISO 8601). Por defecto: ahora                                                                                  |
| `observation`     | string   | No        | Observaciones del documento                                                                                                           |
| `is_credit`       | boolean  | No        | Si la venta es al crédito (defecto `false`)                                                                                           |
| `credit_days`     | integer  | No        | Días de crédito (requiere `is_credit: true`)                                                                                          |
| `products`        | array    | Sí        | Líneas de detalle (mínimo 1)                                                                                                          |
| `payment_details` | array    | Sí        | Métodos de pago utilizados (mínimo 1)                                                                                                 |
| `totals`          | array    | Sí        | Totales globales del documento                                                                                                        |

**Estructura `products[]`**

| Campo                | Tipo   | Descripción                                                                 |
| -------------------- | ------ | --------------------------------------------------------------------------- |
| `id`                 | uuid   | ID del producto en FileXpress (opcional pero recomendado)                   |
| `sku`                | string | Código SKU del producto                                                     |
| `name`               | string | Nombre del producto tal como aparecerá en el documento                      |
| `quantity`           | number | Cantidad vendida                                                            |
| `price_before_taxes` | number | Precio unitario sin impuestos                                               |
| `sale_not_suject`    | number | Subtotal de ventas no sujetas de esta línea                                 |
| `sale_exent`         | number | Subtotal de ventas exentas de esta línea                                    |
| `sale_taxed`         | number | Subtotal de ventas gravadas de esta línea (`price_before_taxes × quantity`) |
| `subtotal`           | number | Total de la línea sin impuestos                                             |
| `discount`           | number | Descuento en dólares aplicado a la línea                                    |
| `type`               | string | `phisical` (bien) o `service`                                               |

**Estructura `payment_details[]`**

| Campo            | Tipo   | Descripción                                                      |
| ---------------- | ------ | ---------------------------------------------------------------- |
| `code`           | string | Código del método de pago — ver `GET /payment-methods`           |
| `payment_method` | string | Nombre descriptivo del método (ej: "Efectivo", "Tarjeta")        |
| `amount`         | number | Monto pagado con este método                                     |
| `reference`      | string | Referencia de transacción (número de cheque, autorización, etc.) |

**Estructura `totals`**

| Campo                | Tipo   | Descripción                                                                     |
| -------------------- | ------ | ------------------------------------------------------------------------------- |
| `netTotals`          | number | Suma de subtotales de todas las líneas antes de impuestos                       |
| `saleTotalNotSuject` | number | Total de ventas no sujetas                                                      |
| `saleTotalExent`     | number | Total de ventas exentas                                                         |
| `saleTotalTax`       | number | Total de ventas gravadas (base imponible)                                       |
| `totalTax`           | number | Monto total de IVA (`saleTotalTax × 0.13`)                                      |
| `totalRetein`        | number | Total de retenciones (0 si no aplica)                                           |
| `totalRent`          | number | Total de renta (0 si no aplica)                                                 |
| `freight`            | number | Flete (0 si no aplica)                                                          |
| `insurance`          | number | Seguro (0 si no aplica)                                                         |
| `saleTotalToPayment` | number | Total a pagar (`saleTotalTax + totalTax + saleTotalExent + saleTotalNotSuject`) |

**Respuesta exitosa `200`**

```json theme={null}
{
  "status": true,
  "data": {
    "sale_id": "uuid",
    "generation_code": "GC-2025-001",
    "control_number": "DTE-01-0001-000000001",
    "status": "processed",
    "issue_time": "2025-06-01T10:30:00Z",
    "stamp": "...",
    "total": 22.60
  }
}
```

***

### Obtener venta

```http theme={null}
GET /api/external/sales/{sale_id}
```

**Respuesta exitosa `200`**

```json theme={null}
{
  "status": true,
  "data": {
    "sale_id": "uuid",
    "generation_code": "GC-2025-001",
    "control_number": "DTE-01-0001-000000001",
    "document_type": "fc",
    "status": "processed",
    "issue_time": "2025-06-01T10:30:00Z",
    "stamp": "...",
    "payment_status": "paid",
    "is_credit": false,
    "customer": {
      "id": "uuid",
      "legal_name": "Razón Social S.A.",
      "customer_name": "Nombre Comercial"
    },
    "totals": {
      "sale_total": 22.60,
      "sale_to_payment": 22.60
    },
    "products": [
      {
        "name": "Producto A",
        "quantity": 2,
        "individual_price": 10.00,
        "subtotal": 20.00
      }
    ]
  }
}
```

***

## Gastos

### Crear gasto

```http theme={null}
POST /api/external/expenses
```

<Warning>
  Al igual que ventas, el sistema externo debe pre-calcular los desgloses fiscales por línea y los totales globales.
</Warning>

**Cuerpo de la solicitud**

```json theme={null}
{
  "branch_id": "uuid",
  "provider_id": "uuid",
  "document_type": "ccf",
  "date": "2025-06-01",
  "observation": "Compra de insumos",
  "is_credit": false,
  "credit_days": 0,
  "number_control": "DTE-03-...",
  "generation_code": null,
  "stamp": null,
  "products": [
    {
      "sku": "INS-001",
      "name": "Insumo A",
      "type": "goods",
      "quantity": 5,
      "price_before_taxes": 8.00,
      "expense_not_suject": 0,
      "expense_exent": 0,
      "expense_taxed": 40.00,
      "subtotal": 40.00
    }
  ],
  "totals": {
    "netTotals": 40.00,
    "charge_1": 0,
    "charge_2": 0,
    "expenseTotalTax": 40.00,
    "totalRent": 0,
    "totalTax": 5.20,
    "totalRetein": 0,
    "expenseTotalToPayment": 45.20
  }
}
```

**Campos principales**

| Campo             | Tipo    | Requerido | Descripción                                                       |
| ----------------- | ------- | --------- | ----------------------------------------------------------------- |
| `branch_id`       | uuid    | Sí        | Sucursal que registra el gasto                                    |
| `provider_id`     | uuid    | Sí        | Proveedor emisor del documento                                    |
| `document_type`   | string  | Sí        | `fse` (Factura Sujeto Excluido), `ccf` (Crédito Fiscal), `ticket` |
| `date`            | date    | Sí        | Fecha del documento (`YYYY-MM-DD`)                                |
| `is_credit`       | boolean | Sí        | Si es al crédito                                                  |
| `credit_days`     | integer | No        | Días de crédito                                                   |
| `number_control`  | string  | No        | Número de control del documento físico                            |
| `generation_code` | string  | No        | Código de generación del DTE                                      |
| `stamp`           | string  | No        | Sello electrónico                                                 |
| `products`        | array   | Sí        | Líneas de detalle                                                 |
| `totals`          | array   | Sí        | Totales globales                                                  |

**Estructura `products[]` para gastos**

| Campo                | Tipo   | Descripción                            |
| -------------------- | ------ | -------------------------------------- |
| `sku`                | string | Código del producto/servicio           |
| `name`               | string | Descripción de la línea                |
| `type`               | string | `goods` (bien) o `services` (servicio) |
| `quantity`           | number | Cantidad                               |
| `price_before_taxes` | number | Precio unitario sin impuestos          |
| `expense_not_suject` | number | Monto no sujeto de la línea            |
| `expense_exent`      | number | Monto exento de la línea               |
| `expense_taxed`      | number | Monto gravado de la línea              |
| `subtotal`           | number | Total de la línea                      |

**Estructura `totals` para gastos**

| Campo                   | Tipo   | Descripción                           |
| ----------------------- | ------ | ------------------------------------- |
| `netTotals`             | number | Suma de subtotales antes de impuestos |
| `charge_1`              | number | Cargo adicional 1 (0 si no aplica)    |
| `charge_2`              | number | Cargo adicional 2 (0 si no aplica)    |
| `expenseTotalTax`       | number | Base imponible gravada total          |
| `totalRent`             | number | Renta retenida (0 si no aplica)       |
| `totalTax`              | number | Monto total de IVA                    |
| `totalRetein`           | number | Retenciones (0 si no aplica)          |
| `expenseTotalToPayment` | number | Total a pagar                         |

**Respuesta exitosa `200`**

```json theme={null}
{
  "status": true,
  "data": {
    "expense_id": "uuid",
    "generation_code": null,
    "control_number": "DTE-03-...",
    "status": "processed",
    "issue_time": "2025-06-01T10:00:00Z",
    "total": 45.20
  }
}
```

***

### Obtener gasto

```http theme={null}
GET /api/external/expenses/{expense_id}
```

Devuelve el gasto con proveedor, líneas de detalle y totales (`expense_total`, `expense_to_payment`).

***

## Clientes

### Listar clientes de una sucursal

```http theme={null}
GET /api/external/customers/branch/{branch_id}
```

Devuelve todos los clientes activos de la sucursal (no paginado).

***

### Obtener cliente

```http theme={null}
GET /api/external/customers/{id}
```

***

### Crear cliente

```http theme={null}
POST /api/external/customers
```

**Cuerpo de la solicitud**

```json theme={null}
{
  "customer_name": "Juan Pérez",
  "legal_name": "Juan Antonio Pérez García",
  "register_number": "12345-6",
  "tax_number": "0614-000101-000-0",
  "document_type": "DUI",
  "document_number": "00000000-0",
  "address": "Col. San Benito, Av. La Revolución #123",
  "country_id": "uuid",
  "state_id": "uuid",
  "city_id": "uuid",
  "phone": "7777-1234",
  "email": "juan@example.com",
  "business_type": "non-business",
  "business_size": "",
  "activity_id": "uuid",
  "branch_id": "uuid",
  "is_exent": false,
  "is_public_institution": false
}
```

**Campos requeridos**: `customer_name`, `address`, `country_id`, `state_id`, `city_id`, `phone`, `email`, `business_type`, `branch_id`

| Campo                   | Valores válidos                                                                              |
| ----------------------- | -------------------------------------------------------------------------------------------- |
| `business_type`         | `non-business` (persona individual), `natural` (persona natural), `legal` (persona jurídica) |
| `document_type`         | Tipo de documento de identidad (DUI, pasaporte, carné de residente, etc.)                    |
| `is_exent`              | `true` = el sistema elimina el IVA al facturar a este cliente                                |
| `is_public_institution` | `true` = entidad gubernamental — restringe los tipos de DTE disponibles                      |

**Respuesta `200`**: objeto del cliente creado con `id`.

***

### Actualizar cliente

```http theme={null}
PUT /api/external/customers/{id}
```

Mismos campos requeridos que la creación. Acepta además: `legal_name`, `register_number`, `tax_number`, `document_type`, `document_number`, `activity_id`, `business_size`.

***

### Eliminar cliente

```http theme={null}
DELETE /api/external/customers/{id}
```

Borrado lógico (`trashed = true`). Responde `200` con confirmación.

***

## Productos

### Listar productos de una sucursal

```http theme={null}
GET /api/external/products/branch/{branch_id}
```

***

### Obtener producto

```http theme={null}
GET /api/external/products/{id}
```

Incluye `portions` y `components` si el producto tiene composición avanzada.

***

### Crear producto

```http theme={null}
POST /api/external/products
```

**Cuerpo de la solicitud**

```json theme={null}
{
  "name": "Refresco 350ml",
  "sku": "BEB-001",
  "price_before_taxes": 1.11,
  "cost": 0.70,
  "tax_method": "is_taxed",
  "composition_type": "simple",
  "product_type": "phisical",
  "product_category_id": "uuid",
  "product_measurement_id": "uuid",
  "branch_id": "uuid",
  "availability": 100,
  "limit_availability": 10
}
```

**Campos requeridos**: `name`, `price_before_taxes`, `product_category_id`, `product_measurement_id`, `product_type`, `tax_method`, `composition_type`, `branch_id`, `availability`, `limit_availability`

| Campo              | Valores válidos                                                         |
| ------------------ | ----------------------------------------------------------------------- |
| `tax_method`       | `is_taxed` (gravado), `is_exent` (exento), `is_not_subject` (no sujeto) |
| `composition_type` | `simple`, `with_portions`, `with_recipe`                                |
| `product_type`     | `phisical` (bien), `service` (servicio), `both`                         |

**Con porciones** (`composition_type: with_portions`) — campos adicionales:

```json theme={null}
{
  "base_unit_quantity": 1000,
  "base_unit": "ml",
  "portions": [
    {
      "portion_name": "Vaso 250ml",
      "portion_quantity": 250,
      "portion_unit": "ml",
      "portion_price": 0.44,
      "is_active": true
    }
  ]
}
```

**Con receta** (`composition_type: with_recipe`) — campos adicionales:

```json theme={null}
{
  "components": [
    {
      "component_product_id": "uuid-ingrediente",
      "quantity_needed": 2,
      "unit_type": "base"
    }
  ]
}
```

Al crear un producto físico con `availability > 0`, se registra automáticamente un movimiento de inventario de entrada.

***

### Actualizar producto

```http theme={null}
PUT /api/external/products/{id}
```

Acepta los mismos campos. Si cambia `availability`, se genera un movimiento de ajuste automático. Al cambiar `composition_type`, las porciones o componentes anteriores se eliminan y reemplazan.

***

### Eliminar producto

```http theme={null}
DELETE /api/external/products/{id}
```

Borrado lógico. Responde `200` con confirmación.

***

## Proveedores

### Listar proveedores de una sucursal

```http theme={null}
GET /api/external/providers/branch/{branch_id}
```

***

### Crear proveedor

```http theme={null}
POST /api/external/providers
```

El cuerpo debe enviarse dentro de una clave `data`:

```json theme={null}
{
  "data": {
    "provider_name": "Distribuidora XYZ",
    "legal_name": "Distribuidora XYZ S.A. de C.V.",
    "register_number": "54321-0",
    "tax_number": "0614-000000-000-0",
    "address": "San Salvador",
    "country_id": "uuid",
    "state_id": "uuid",
    "city_id": "uuid",
    "phone": "2222-3333",
    "email": "ventas@xyz.com",
    "business_type": "legal",
    "branch_id": "uuid"
  }
}
```

**Campos requeridos** (dentro de `data`): `provider_name`, `address`, `country_id`, `state_id`, `city_id`, `phone`, `email`, `business_type`, `branch_id`

***

### Actualizar proveedor

```http theme={null}
PUT /api/external/providers/{id}
```

Mismos campos que la creación (sin wrapper `data` en update).

***

### Eliminar proveedor

```http theme={null}
DELETE /api/external/providers/{id}
```

***

## Catálogos (solo lectura)

| Recurso                | Endpoint                                                              |
| ---------------------- | --------------------------------------------------------------------- |
| Países                 | `GET /countries`, `GET /countries/{id}`                               |
| Departamentos          | `GET /states`, `GET /states/{id}`, `GET /states/country/{country_id}` |
| Municipios             | `GET /cities`, `GET /cities/{id}`, `GET /cities/state/{state_id}`     |
| Categorías de producto | `GET /product-categories`, `GET /product-categories/{id}`             |
| Unidades de medida     | `GET /product-measurements`, `GET /product-measurements/{id}`         |
| Actividades económicas | `GET /activities`, `GET /activities/{id}`                             |
| Métodos de pago        | `GET /payment-methods`                                                |
| Incoterms              | `GET /incoterms`                                                      |
