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

# Movimientos de stock (ERP → POS)

> Aplicá ajustes tipados de stock por sucursal, idempotentes, con fan-out a las tablets.

## Qué es

Este endpoint permite que el ERP aplique un **delta** de stock por sucursal (recepción, transferencia, ajuste, devolución) sin pisar el saldo. Cada movimiento:

* se aplica de forma atómica sobre el stock de la sucursal;
* se propaga a las tablets de esa sucursal (para que el POS siga offline-first);
* emite un evento [`stock.changed`](https://docs.pos.terominds.com/api/catalogo-eventos) con `reason` prefijado `erp_`.

> [!WARNING]
>
> No uses el `PUT .../stock` absoluto para mover stock desde el ERP: pisa el saldo y no es idempotente. Este endpoint es el camino correcto.

## Dónde está

```
POST /api/integration/v1/locations/:locationId/stock-movements
```

Requiere una clave de alcance **escritura** y el encabezado `Idempotency-Key`.

```bash theme={null}
export TERO_API='https://pos-api.terominds.com/api'
export TERO_API_KEY='tero_live_aabbccdd_xxxxxxxx'
export TERO_LOCATION_ID='11111111-1111-4111-8111-111111111111'
export TERO_PRODUCT_ID='22222222-2222-4222-8222-222222222222'

curl -sS -X POST "$TERO_API/integration/v1/locations/$TERO_LOCATION_ID/stock-movements" \
  -H "Authorization: Bearer $TERO_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: receipt-2026-10-21-0001" \
  -d '{
    "product_id": "'"$TERO_PRODUCT_ID"'",
    "delta": 24,
    "reason": "receipt",
    "ref": "OC-4711",
    "note": "Recepción proveedor"
  }'
```

## Cuerpo

| Campo | Tipo | Obligatorio | Qué es |
| - | - | - | - |
| `product_id` | uuid | sí | Producto de la empresa. |
| `delta` | entero ≠ 0 | sí | Cuánto sumar (positivo) o restar (negativo). |
| `reason` | texto | sí | `receipt`, `transfer_in`, `transfer_out`, `adjustment`, `return_to_supplier` u `other`. |
| `ref` | texto | no | Referencia externa (orden de compra, remito). |
| `note` | texto | no | Nota libre. |

El stock nunca baja de cero: un delta negativo mayor al saldo deja el saldo en 0.

## Respuesta

```json theme={null}
{
  "ok": true,
  "location_id": "11111111-1111-4111-8111-111111111111",
  "product_id": "22222222-2222-4222-8222-222222222222",
  "delta": 24,
  "reason": "erp_receipt",
  "stock_quantity_after": 48,
  "ref": "OC-4711",
  "idempotent": false
}
```

`idempotent: true` indica que la respuesta se recuperó de una llamada previa con la misma clave (no se volvió a mover stock).

## Idempotencia

`Idempotency-Key` es obligatorio. La clave se guarda por empresa:

* **Misma clave + mismo cuerpo** → se devuelve la misma respuesta (`idempotent: true`), sin volver a aplicar el delta.
* **Misma clave + cuerpo distinto** → `409 idempotency_conflict`.
* **Clave nueva** → se aplica el movimiento.

Usá una clave estable y única por operación (por ejemplo `receipt-<orden>-<linea>`), no un valor aleatorio por reintento.

## Errores

| Código | `error` | Qué significa |
| - | - | - |
| 400 | `idempotency_key_required` | Falta el encabezado. |
| 400 | `invalid_delta` | `delta` es 0 o no es un entero. |
| 400 | `invalid_reason` | `reason` fuera del catálogo. |
| 400 | `composite_product_no_stock` | Los productos compuestos no llevan stock propio. |
| 404 | `location_not_found` | La sucursal no existe o es de otra empresa. |
| 404 | `product_not_found` | El producto no existe o está dado de baja. |
| 409 | `idempotency_conflict` | La clave ya se usó con otro cuerpo. |


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