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

# Webhooks

> Los webhooks todavía no están disponibles. Cómo mantener tus sistemas al día consultando la API periódicamente dentro de tu límite de uso.

<Warning>
  muveya no envía webhooks por ahora. No hay ninguna operación para registrar una URL, no se envían eventos a tus sistemas y no hay un secreto de firma que administrar. Para seguir los cambios, consulta la API periódicamente como se describe en esta página.
</Warning>

Si tu integración necesita notificaciones automáticas, escribe a [team@muveya.com](mailto:team@muveya.com) y describe los eventos que necesitas. Cuando los webhooks estén disponibles, se anunciarán en [Novedades](/docs/es/changelog).

## Qué consultar

| Para saber cuándo | Consulta | Scope | Cómo detectar el cambio |
| - | - | - | - |
| Se crea un pedido nuevo | `GET /v1/orders` (todas las páginas) | `orders:read` | Un `orderId` que no habías visto. Los pedidos nuevos aparecen al final, porque la lista va del más antiguo al más reciente. |
| Un pedido cambia de estado | `GET /v1/orders?status=...` o `GET /v1/orders/{orderId}` | `orders:read` | Un `status` distinto. La `version` del pedido también aumenta cada vez que cambia su estado. |
| Una entrega avanza | `GET /v1/fulfillments?status=...` | `fulfillment:read` | Un `status` distinto o un `updatedAt` posterior. |
| Eventos de custodia de un pedido | `GET /v1/fulfillments/{orderId}` | `fulfillment:read` | Entradas nuevas en `movements`, o la aparición de `delivery`, `receipt` o `closure`. |
| Cambian las existencias de una caja | `GET /v1/inventory/boxes/{boxId}/balance` o `.../movements` | `inventory:read` | `onHand` o `reserved` distintos, o un `movementId` nuevo. |
| Cambian los niveles de existencias de una bodega | `GET /v1/inventory/balances?warehouseId=...` | `inventory:read` | `onHand`, `reserved` o `available` distintos por `boxId`. |
| Algo requiere atención | `GET /v1/analytics/exceptions` | `analytics:read` | Un `total` distinto, o una excepción cuyo `drillDownId` no habías visto. |
| Una exportación está lista | `GET /v1/analytics/exports/{exportId}` | `analytics:read` | `status` pasa a `ready` o `failed`. |

## Planifica tu presupuesto de consultas

Cada clave tiene **120 solicitudes cada 60 segundos**, compartidas por todas las operaciones (consulta [Límites de uso](/docs/es/api-reference/rate-limits)). Un presupuesto para una integración podría verse así:

| Proceso | Frecuencia | Solicitudes por ejecución |
| - | - | - |
| Excepciones operativas | Cada minuto | 1 |
| Pedidos que esperan aprobación: `GET /v1/orders?status=pending_approval&limit=200` | Cada 5 minutos | 1 por cada 200 pedidos que coinciden |
| Entregas en tránsito: `GET /v1/fulfillments?status=dispatched&limit=200` | Cada 5 minutos | 1 por cada 200 entregas que coinciden |
| Recorrido completo de pedidos: `GET /v1/orders?limit=200` | Cada hora | 1 por cada 200 pedidos |
| Sedes, bodegas y catálogo | Una vez al día | 4 |

Deja margen para reintentos y para consultas manuales con la misma clave, o da a cada proceso su propia clave.

## Buenas prácticas

* **Filtra por estado** para leer solo los pedidos o entregas que estás esperando, en lugar de recorrer todo.
* **Recuerda lo que viste.** Guarda cada `orderId` con su último `status` y `version`, y cada registro de entrega con su último `updatedAt`. Actúa solo cuando cambien.
* **Espera ver un cambio más de una vez, o con retraso.** Haz que tu procesamiento sea idempotente: procesar dos veces el mismo estado no debe causar problemas.
* **Lee las listas completas con `limit=200`** y sigue `nextCursor` hasta el final (consulta [Paginación](/docs/es/api-reference/pagination)).
* **No superpongas ejecuciones.** Inicia la siguiente consulta solo cuando la anterior haya terminado.
* **Espera ante un `429`.** Aguarda lo que indica `Retry-After` antes de la siguiente solicitud.

## Ejemplo: detectar cambios de estado de pedidos

<CodeGroup>
  ```javascript JavaScript theme={null}
  const BASE_URL = "https://api.muveya.com";
  const headers = { Authorization: `Bearer ${process.env.MUVEYA_API_KEY}` };
  const seen = new Map(); // orderId -> version; keep it in your database

  async function pollOrders(status) {
    let cursor;
    do {
      const url = new URL("/v1/orders", BASE_URL);
      url.searchParams.set("limit", "200");
      url.searchParams.set("status", status);
      if (cursor) url.searchParams.set("cursor", cursor);

      const response = await fetch(url, { headers });
      if (response.status === 429) {
        const wait = Number(response.headers.get("retry-after") ?? "1");
        await new Promise((resolve) => setTimeout(resolve, wait * 1000));
        continue;
      }
      if (!response.ok) throw new Error(`HTTP ${response.status}`);
      const page = await response.json();

      for (const order of page.data) {
        if (seen.get(order.orderId) !== order.version) {
          seen.set(order.orderId, order.version);
          console.log(`Order #${order.number} is ${order.status}`);
        }
      }
      cursor = page.hasMore ? page.nextCursor : undefined;
    } while (cursor);
  }

  await pollOrders("pending_approval");
  ```

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

  BASE_URL = "https://api.muveya.com"
  session = requests.Session()
  session.headers["Authorization"] = f"Bearer {os.environ['MUVEYA_API_KEY']}"
  seen = {}  # orderId -> version; keep it in your database

  def poll_orders(status):
      cursor = None
      while True:
          params = {"limit": 200, "status": status}
          if cursor:
              params["cursor"] = cursor
          response = session.get(f"{BASE_URL}/v1/orders", params=params, timeout=30)
          if response.status_code == 429:
              time.sleep(int(response.headers.get("Retry-After", "1")))
              continue
          response.raise_for_status()
          page = response.json()
          for order in page["data"]:
              if seen.get(order["orderId"]) != order["version"]:
                  seen[order["orderId"]] = order["version"]
                  print(f"Order #{order['number']} is {order['status']}")
          if not page["hasMore"]:
              return
          cursor = page["nextCursor"]

  poll_orders("pending_approval")
  ```
</CodeGroup>

## Páginas relacionadas

* [Paginación](/docs/es/api-reference/pagination)
* [Límites de uso](/docs/es/api-reference/rate-limits)
* [Scopes](/docs/es/api-reference/scopes)
* [Resumen de entregas](/docs/es/deliveries/overview)


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