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

# Sync an ERP

> The recommended loop for orders, stock and invoices.

This is the pattern we recommend for any ERP or accounting system. It is safe to run again after a crash, never loses an order, and never double-books one.

> [!NOTE]
>
> BrandGate also has ready-made connectors for several ERPs, set up by the brand in **Settings → ERP**. Use the API when you build the integration yourself or when you are an ERP vendor connecting all your customers.

## Scopes you need

`orders:read`, `orders:write`, `inventory:write`, and optionally `products:read`, `customers:read`, `invoices:read` and `webhooks:write`.

## 1. Orders into the ERP

**1. Listen**

Create a webhook endpoint for `order.created` and `order.updated`. When an event arrives, put its `data.id` on your own queue and answer `200` straight away.

**2. Fetch**

For each id, call `GET /v1/orders/{id}`. Draft orders never reach the API. Check `status`: an order can be `cancelled` after you created it.

**3. Create in the ERP**

If `external_reference` is `null`, create the order in your ERP. Match customers on `customer.id`, which never changes, and products on `lines[].sku`.

**4. Acknowledge**

Tell BrandGate the ERP's order number:

```bash theme={null}
curl https://api.brandgate.dev/v1/orders/$ORDER_ID/acknowledge \
  -H "Authorization: Bearer $BRANDGATE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"external_number":"SO-20931"}'
```

Repeating the call with the same number is safe and changes nothing. A **different** number for an order that is already acknowledged returns `409 already_acknowledged`, which protects you from creating the same order twice after a retry.

## 2. Catch up

Run a sweep every 15 minutes, and after any downtime, so nothing depends on a webhook arriving:

```bash theme={null}
curl "https://api.brandgate.dev/v1/orders?updated_since=2026-10-20T08:00:00Z&limit=100" \
  -H "Authorization: Bearer $BRANDGATE_API_KEY"
```

Follow `next_cursor` until `has_more` is `false` (see [pagination](/pagination)), handle each order as in step 1, and store the largest `updated_at` you saw as the next `updated_since`. Subtract a minute when you store it; a few repeats are harmless because acknowledging is idempotent.

## 3. Stock from the ERP

The ERP is the source of truth for stock. Send absolute levels, up to 500 SKUs per request:

```bash theme={null}
curl https://api.brandgate.dev/v1/inventory \
  -H "Authorization: Bearer $BRANDGATE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"items":[{"sku":"TSHIRT-M","quantity":140},{"sku":"TSHIRT-L","quantity":0}]}'
```

```json theme={null}
{ "object": "inventory_update", "updated": 1, "unmatched_skus": ["TSHIRT-L"] }
```

BrandGate overwrites its stock, records every change in its stock ledger with the reason `api`, and updates the product status the same way as any other stock change (`out_of_stock` at 0, `low_stock` at five times the product's minimum order or below). `unmatched_skus` lists SKUs that do not exist in BrandGate, so you can fix your mapping. Send changed SKUs every few minutes and a full list once a night.

Only one system can own stock. If the company already imports stock from an ERP connected inside BrandGate, the call answers `409 inventory_managed_by_erp` and changes nothing; the company turns that import off under Settings → ERP.

## 4. Invoices and payments

Read invoices with `GET /v1/invoices?updated_since=…`, or listen for `invoice.created` and `invoice.updated`. Amounts are decimal strings in the invoice `currency`; `ocr_reference` is the payment reference.

## Checklist before go-live

* Your receiver verifies `BrandGate-Signature` and rejects old timestamps.
* You skip events whose `BrandGate-Event-Id` you already handled.
* The catch-up sweep runs even when webhooks work.
* You back off on `429` using `Retry-After`.
* The API key is stored in a secret manager and has only the scopes above.


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