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

# Endpoints y recursos

> Recursos y flujos para integrar compras, ventas, inventario, productos, contactos, reportes y configuración.

## Qué podés hacer

La API de La Pyme te permite leer datos operativos, crear operaciones comerciales y consultar reportes sin acceder directo a la base de datos.

Los casos principales son:

* Consultar clientes, proveedores, productos, depósitos, listas de precios, etiquetas y métodos de pago.
* Registrar compras, ventas, transferencias y movimientos manuales de stock.
* Aplicar ajustes masivos de costos o precios.
* Consultar reportes agrupados de ventas, compras, pagos e inventario.

## Primer request

Usá un endpoint de lectura chico para validar credenciales, scopes y formato de respuesta:

<CodeGroup>
  ```bash cURL theme={null}
  curl "https://api.lapyme.com.ar/api/v1/warehouses?limit=1" \
    -H "Authorization: Bearer YOUR_BEARER_HERE"
  ```

  ```typescript TypeScript theme={null}
  const response = await fetch("https://api.lapyme.com.ar/api/v1/warehouses?limit=1", {
    headers: {
      Authorization: `Bearer ${process.env.LAPYME_API_KEY}`,
    },
  });

  if (!response.ok) {
    throw new Error(await response.text());
  }

  const warehouses = await response.json();
  ```

  ```python Python theme={null}
  import os
  import requests

  response = requests.get(
      "https://api.lapyme.com.ar/api/v1/warehouses",
      params={"limit": 1},
      headers={"Authorization": f"Bearer {os.environ['LAPYME_API_KEY']}"},
      timeout=30,
  )
  response.raise_for_status()
  warehouses = response.json()
  ```
</CodeGroup>

<Info>
  Las solicitudes a la API aceptan `Authorization: Bearer ...` con API keys o tokens OAuth delegados emitidos para clientes públicos.
</Info>

<Info>
  La cuota de la API pública es por organización: `5000` solicitudes por hora y `120` solicitudes por minuto entre todas las credenciales de esa organización.
</Info>

## Convenciones

* Las respuestas usan `request_id` para trazabilidad y el status HTTP como señal de éxito o error.
* Los errores usan un envelope estructurado con `type`, `code`, `message`, `retryable` y `details`.
* Los objetos de recursos incluyen `object` como discriminador de solo lectura, por ejemplo `"object": "customer"`.
* Los listados devuelven `has_more` y `next_cursor`; usá ese valor como `cursor` para pedir la siguiente página.
* Las escrituras usan payloads de negocio planos: sin `mode`, sin `input`, sin `client` y sin `meta`.
* Las escrituras que pueden crear duplicados aceptan una clave de reintento en el header `Idempotency-Key`; cada endpoint indica si es obligatoria u opcional.
* Todos los importes monetarios se envían y devuelven en centavos.

## Paginación

Los listados devuelven un token para pedir la siguiente página:

* Enviá `limit` para controlar el tamaño de página. El máximo es `100` y el default es `50`.
* Si `has_more` es `true`, copiá `next_cursor` y envialo como `cursor` en la siguiente request.
* No mezcles un `cursor` con filtros distintos. Cuando cambian filtros o búsqueda, empezá sin `cursor`.
* `page` no está soportado.
* Los resultados se ordenan de forma estable por el criterio del endpoint; si necesitás reconstruir un export grande, guardá el último `next_cursor` procesado.

## Idempotencia

Las escrituras que pueden crear duplicados requieren o aceptan `Idempotency-Key`.

* Obligatoria: `POST /api/v1/purchases`, `POST /api/v1/sales`, `POST /api/v1/products`, `POST /api/v1/products/bulk-adjustments`, `POST /api/v1/stock-movements`, `POST /api/v1/purchase-orders` y `POST /api/v1/purchase-orders/{purchase_order_id}/receipts`.
* Opcional: `POST /api/v1/stock-transfers`.
* Reutilizá la misma key solo para reintentar la misma operación con el mismo payload.
* Si reutilizás una key con otro payload, la API responde `409 IDEMPOTENCY_CONFLICT`.
* Cuando una respuesta exitosa viene de un replay, el cuerpo incluye `idempotent_replay: true` dentro del recurso de resultado cuando aplica.
* Para timeouts de red o respuestas `5xx`, reintentá con la misma key. Para errores `4xx`, corregí el payload antes de enviar una key nueva.

### Respuesta de lectura

```json theme={null}
{
  "request_id": "req_123",
  "object": "list",
  "url": "/api/v1/suppliers",
  "data": [
    {
      "object": "supplier",
      "id": "550e8400-e29b-41d4-a716-446655440101",
      "name": "Supplier One"
    }
  ],
  "has_more": true,
  "next_cursor": "NEXT_CURSOR"
}
```

### Respuesta de error

```json theme={null}
{
  "request_id": "req_123",
  "error": {
    "type": "business_error",
    "code": "PRECONDITION_FAILED",
    "message": "The purchase could not be created because a business precondition failed.",
    "retryable": false,
    "details": [
      {
        "field": "items",
        "code": "INVENTORY",
        "message": "Insufficient stock. Current stock: -34, requested change: 1"
      }
    ]
  }
}
```

Cada ítem de `error.details` puede incluir:

* `field`: path lógico del campo o header relacionado.
* `code`: código estable para ese detalle.
* `message`: explicación legible para mostrar o registrar.

## Documentos de ventas y compras

Los detalles de ventas y compras informan la disponibilidad del documento sin
incluir bytes base64, rutas de Storage ni URLs firmadas durables:

```json theme={null}
{
  "status": "ready",
  "url": "/api/v1/purchases/550e8400-e29b-41d4-a716-446655440100/document"
}
```

* En ventas, el descriptor está en `document.file` y puede tener estado
  `ready`, `pending`, `failed` o `missing`.
* En compras, el descriptor está en `document` y usa `ready` o `missing`.
* Solo `ready` incluye una `url`. Esa ruta es estable, requiere
  `sales:read` o `purchases:read` y responde `302` hacia una URL firmada de
  cinco minutos. Configurá el cliente para seguir redirects.
* `pdf_path` en compras se conserva deprecado por compatibilidad de V1. No lo
  uses para descargar; migrá a `document.url`.

```bash theme={null}
curl --location \
  "https://api.lapyme.com.ar/api/v1/purchases/550e8400-e29b-41d4-a716-446655440100/document" \
  -H "Authorization: Bearer YOUR_BEARER_HERE" \
  --output comprobante
```

La `document.url` autenticada es el contrato para integraciones. El destino
firmado del redirect es efímero y no debe guardarse. Los links públicos opacos
`/comprobantes/d/{token}` enviados por email son credenciales de portador
separadas, y la ruta pública histórica usada por WhatsApp no forma parte del
contrato de la API.

## Flujo de creación de compra

1. Buscá el proveedor con `GET /api/v1/suppliers`.
2. Buscá productos con `GET /api/v1/products`.
3. Si enviás `products_received: true`, obtené el depósito con `GET /api/v1/warehouses`.
4. Enviá `POST /api/v1/purchases` con el payload plano y un `Idempotency-Key`.
5. Si la compra se crea, revisá `normalized_purchase`, `projected_effects` y `warnings` en la respuesta.
6. Obtené la compra creada con `GET /api/v1/purchases/{purchase_id}`.

<Info>
  `POST /api/v1/sales` sigue la misma regla: payload de negocio plano más un header `Idempotency-Key` obligatorio. Si querés guardar el ID de tu sistema en la venta, enviá además `integration_source` e `integration_id` en el cuerpo; `Idempotency-Key` solo deduplica reintentos y no se guarda como referencia externa visible. La respuesta devuelve la venta persistida, la venta normalizada, los efectos proyectados y los warnings.
</Info>

<Info>
  `POST /api/v1/stock-transfers` también usa un payload de negocio plano. `Idempotency-Key` es opcional: cuando se envía, el servidor puede deduplicar reintentos; cuando se omite, la operación sigue siendo válida pero no tiene protección automática contra repeticiones.
</Info>

## Flujo de aumento de costos por proveedor

1. Buscá el proveedor con `GET /api/v1/suppliers`.
2. Revisá los productos afectados con `GET /api/v1/products`.
3. Enviá `POST /api/v1/products/bulk-adjustments` con `target: "cost"`, el filtro `default_supplier_id` y un `Idempotency-Key`.
4. La capability de comercio actualiza costos, recalcula precios base cuando un producto usa pricing automático por margen y registra efectos de sincronización para canales conectados.

## Endpoints disponibles

### Contactos

* `GET /api/v1/customers`
* `POST /api/v1/customers`
* `GET /api/v1/customers/{customer_id}`
* `PUT /api/v1/customers/{customer_id}`
* `GET /api/v1/suppliers`
* `POST /api/v1/suppliers`
* `GET /api/v1/suppliers/{supplier_id}`
* `PUT /api/v1/suppliers/{supplier_id}`

### Productos y configuración

* `GET /api/v1/products`
* `POST /api/v1/products`
* `GET /api/v1/products/metafield-definitions`
* `GET /api/v1/products/{product_id}`
* `PUT /api/v1/products/{product_id}`
* `PATCH /api/v1/products/{product_id}/metafields`
* `POST /api/v1/products/bulk-adjustments`
* `GET /api/v1/categories`
* `POST /api/v1/categories`
* `GET /api/v1/categories/{category_id}`
* `PUT /api/v1/categories/{category_id}`
* `GET /api/v1/price-lists`
* `POST /api/v1/price-lists`
* `GET /api/v1/price-lists/{price_list_id}`
* `PUT /api/v1/price-lists/{price_list_id}`

Los campos personalizados de productos requieren Max o Enterprise. Descubrí primero las claves y validaciones con `GET /api/v1/products/metafield-definitions`, envialos al crear mediante `metafields: [{ "key": "MARCA", "value": "vicus" }]` y leelos en el detalle como `[{ "key": "marca", "value": "Vicus" }]`. Los valores son del grupo y se comparten entre variantes; el listado no los incluye.

Para cambios posteriores, `PATCH /api/v1/products/{product_id}/metafields` usa `entries`: un string asigna y `null` borra. No uses string vacío. `PUT /api/v1/products/{product_id}` rechaza `metafields`; las etiquetas se administran por su endpoint separado.

### Compras y ventas

* `GET /api/v1/purchases`
* `GET /api/v1/purchases/{purchase_id}`
* `GET /api/v1/purchases/{purchase_id}/document`
* `POST /api/v1/purchases`
* `GET /api/v1/sales`
* `POST /api/v1/sales`
* `GET /api/v1/sales/{sale_id}`
* `GET /api/v1/sales/{sale_id}/document`
* `GET /api/v1/purchase-orders`
* `POST /api/v1/purchase-orders`
* `GET /api/v1/purchase-orders/{purchase_order_id}`
* `POST /api/v1/purchase-orders/{purchase_order_id}/confirm`
* `POST /api/v1/purchase-orders/{purchase_order_id}/close`
* `POST /api/v1/purchase-orders/{purchase_order_id}/reopen`
* `POST /api/v1/purchase-orders/{purchase_order_id}/receipts`

### Inventario y ubicaciones

* `GET /api/v1/inventory`
* `GET /api/v1/inventory/movements`
* `POST /api/v1/stock-movements`
* `GET /api/v1/stock-transfers`
* `POST /api/v1/stock-transfers`
* `GET /api/v1/stock-transfers/{transfer_id}`
* `GET /api/v1/warehouses`
* `POST /api/v1/warehouses`
* `GET /api/v1/warehouses/{warehouse_id}`
* `PUT /api/v1/warehouses/{warehouse_id}`

### Etiquetas, cobranzas, medios de pago y reportes

* `GET /api/v1/tags`
* `POST /api/v1/tags`
* `PATCH /api/v1/tags/{tag_id}`
* `POST /api/v1/customers/tags/apply`
* `POST /api/v1/suppliers/tags/apply`
* `POST /api/v1/products/tags/apply`
* `POST /api/v1/sales/tags/apply`
* `GET /api/v1/customer-payments`
* `POST /api/v1/customer-payments`
* `GET /api/v1/customer-payments/{payment_id}`
* `POST /api/v1/customer-payments/{payment_id}/void`
* `GET /api/v1/supplier-payments`
* `POST /api/v1/supplier-payments`
* `GET /api/v1/supplier-payments/{payment_id}`
* `POST /api/v1/supplier-payments/{payment_id}/void`
* `GET /api/v1/payment-methods`
* `POST /api/v1/payment-methods`
* `GET /api/v1/payment-methods/{payment_method_id}`
* `PUT /api/v1/payment-methods/{payment_method_id}`
* `GET /api/v1/points-of-sale`
* `POST /api/v1/reports/query` - ver [Reportes](/api-reference/reports)

## Ejemplo de creación de producto con variantes

Para crear variantes, enviá `options` con entre uno y tres nombres y `variants` con las combinaciones vendibles. Cada variante debe incluir exactamente un valor para cada opción declarada. Los SKU y las combinaciones no pueden repetirse; el límite es de 250 variantes por request. Los importes están expresados en centavos.

```bash theme={null}
curl -X POST "https://api.lapyme.com.ar/api/v1/products" \
  -H "Authorization: Bearer YOUR_BEARER_HERE" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: shoe-matrix-2026-07-11-001" \
  -d '{
    "name": "Zapatilla clásica",
    "options": ["Material", "Color", "Talle"],
    "variants": [
      {
        "option_values": {
          "Material": "Cuero",
          "Color": "Negro",
          "Talle": "40"
        },
        "sku": "ZAP-CUE-NEG-40",
        "cost": 700000,
        "price": 1250000,
        "currency": "PES"
      },
      {
        "option_values": {
          "Material": "Lona",
          "Color": "Blanco",
          "Talle": "41"
        },
        "sku": "ZAP-LON-BLA-41",
        "cost": 650000,
        "price": 1150000,
        "currency": "PES"
      }
    ]
  }'
```

Para cargar stock inicial, agregá `warehouse_stocks` dentro de cada variante. Reutilizá la misma `Idempotency-Key` solamente para reintentar el mismo producto y la misma matriz.

## Ejemplo de creación de compra

```bash theme={null}
curl -X POST "https://api.lapyme.com.ar/api/v1/purchases" \
  -H "Authorization: Bearer YOUR_BEARER_HERE" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 2bb0c70a-8787-4186-9678-c8156206db16" \
  -d '{
    "supplier_id": "afafcbec-9d94-4174-8d5c-ec8d72780947",
    "voucher_type": 1,
    "supplier_invoice_number": "0001-00000001",
    "invoice_date": "2026-03-10",
    "currency": "PES",
    "exchange_rate": 1,
    "products_received": false,
    "items": [
      {
        "product_id": "9c692e8b-0f9a-4f7c-8b99-061a2eb188ae",
        "quantity": 1,
        "unit_cost": 8250
      }
    ]
  }'
```

## Ejemplo de aumento masivo de costos

```bash theme={null}
curl -X POST "https://api.lapyme.com.ar/api/v1/products/bulk-adjustments" \
  -H "Authorization: Bearer YOUR_BEARER_HERE" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: supplier-cost-increase-2026-04-30-001" \
  -d '{
    "target": "cost",
    "operation_type": "increase",
    "adjustment_type": "percentage",
    "adjustment_value": 5,
    "default_supplier_id": "550e8400-e29b-41d4-a716-446655440201",
    "selection": {
      "type": "all",
      "excluded_ids": []
    }
  }'
```

## Scopes requeridos

* `customers:read`
* `customers:write`
* `categories:read`
* `categories:write`
* `suppliers:read`
* `suppliers:write`
* `products:read`
* `products:write`
* `warehouses:read`
* `warehouses:write`
* `purchases:read`
* `purchases:write`
* `sales:read`
* `sales:write`
* `transfers:read`
* `transfers:write`
* `price_lists:read`
* `price_lists:write`
* `payment_methods:read`
* `payment_methods:write`
* `reports:read`

Cada endpoint está documentado en detalle en la referencia OpenAPI de esta sección.
