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

> Webhooks are not available yet. How to keep your systems up to date by polling the API within your rate limit.

<Warning>
  muveya does not send webhooks today. There is no operation to register a URL, no event is pushed to your systems and there is no signing secret to manage. To follow changes, poll the API as described on this page.
</Warning>

If your integration needs push notifications, write to [team@muveya.com](mailto:team@muveya.com) and describe the events you need. When webhooks become available they will be announced in the [changelog](/docs/en/changelog).

## What to poll

| To find out when | Poll | Scope | How to detect the change |
| - | - | - | - |
| A new order is created | `GET /v1/orders` (every page) | `orders:read` | An `orderId` you have not seen. New orders appear at the end, because the list runs oldest first. |
| An order changes status | `GET /v1/orders?status=...` or `GET /v1/orders/{orderId}` | `orders:read` | A different `status`. The order `version` also grows every time its status changes. |
| A delivery moves forward | `GET /v1/fulfillments?status=...` | `fulfillment:read` | A different `status` or a later `updatedAt`. |
| Custody events of one order | `GET /v1/fulfillments/{orderId}` | `fulfillment:read` | New entries in `movements`, or the appearance of `delivery`, `receipt` or `closure`. |
| Stock of one box changes | `GET /v1/inventory/boxes/{boxId}/balance` or `.../movements` | `inventory:read` | Different `onHand` or `reserved`, or a new `movementId`. |
| Stock levels of a warehouse change | `GET /v1/inventory/balances?warehouseId=...` | `inventory:read` | Different `onHand`, `reserved` or `available` per `boxId`. |
| Something needs attention | `GET /v1/analytics/exceptions` | `analytics:read` | A different `total`, or an exception whose `drillDownId` you have not seen. |
| A briefing export is ready | `GET /v1/analytics/exports/{exportId}` | `analytics:read` | `status` becomes `ready` or `failed`. |

## Plan your polling budget

Each key has **120 requests per 60 seconds**, shared by every operation (see [Rate limits](/docs/en/api-reference/rate-limits)). A budget for one integration could look like this:

| Job | Frequency | Requests per run |
| - | - | - |
| Operational exceptions | Every minute | 1 |
| Orders waiting for approval: `GET /v1/orders?status=pending_approval&limit=200` | Every 5 minutes | 1 per 200 matching orders |
| Deliveries in transit: `GET /v1/fulfillments?status=dispatched&limit=200` | Every 5 minutes | 1 per 200 matching deliveries |
| Full order scan: `GET /v1/orders?limit=200` | Every hour | 1 per 200 orders |
| Locations, warehouses and catalog | Once a day | 4 |

Keep a margin for retries and for manual checks with the same key, or give each job its own key.

## Good practices

* **Filter by status** to read only the orders or deliveries you are waiting for, instead of scanning everything.
* **Remember what you saw.** Store `orderId` with its last `status` and `version`, and fulfillments with their last `updatedAt`. Act only when they change.
* **Expect to see a change more than once, or late.** Make your processing idempotent: handling the same status twice must be harmless.
* **Read whole lists with `limit=200`** and follow `nextCursor` to the end (see [Pagination](/docs/en/api-reference/pagination)).
* **Do not overlap runs.** Start the next poll only when the previous one has finished.
* **Back off on `429`.** Wait for `Retry-After` before the next request.

## Example: detect order status changes

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

## Related pages

* [Pagination](/docs/en/api-reference/pagination)
* [Rate limits](/docs/en/api-reference/rate-limits)
* [Scopes](/docs/en/api-reference/scopes)
* [Deliveries overview](/docs/en/deliveries/overview)


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