# Events

> The events Commerce Orbit sends to Orbit Exchange when products, stock, collections, orders and the MAM connection change, what each carries, and how they are delivered. The same events can go to your own systems as webhooks.

Source: https://docs.bifrostorbit.com/commerce/developers/webhooks-and-events/

When something changes in Commerce Orbit, such as a product's price or an order being paid, it records an event and sends it to Orbit Exchange, the hub that connects Orbit's products. Other satellites react to them there, and Orbit shows them under **Exchange**.

Events go to Orbit Exchange, where Orbit's products use them, and to the [webhooks](https://docs.bifrostorbit.com/commerce/developers/outgoing-webhooks/) you add for your own systems:

- **CMS Orbit** refreshes website pages that show a product or collection when it changes. See [Show your products on your website](https://docs.bifrostorbit.com/commerce/everyone/cms-orbit/).
- **Insights Orbit** can count `commerce.order.placed` as a goal with revenue. See [Orbit connections](https://docs.bifrostorbit.com/commerce/everyone/orbit-connections/).

To keep your own website current, read the [storefront API](https://docs.bifrostorbit.com/commerce/developers/storefront-api/) and cache its answers for a short time.

## The envelope

Every event has the same outer shape as events from Orbit's other satellites. Only `data` differs.

| Field | What it holds |
| --- | --- |
| `id` | A unique ID (UUID) for this event. Use it to spot duplicates. |
| `type` | The event name, such as `commerce.order.paid`. |
| `version` | The version of this event's `data` shape. It only goes up for a change that could break a receiver. All are `1` today. |
| `time` | When it happened, in UTC. |
| `source` | Always `commerce`. |
| `sequence` | A number that goes up with each event from your organisation. |
| `tenant.organisationId` | Your organisation. The other `tenant` fields are `null`. |
| `data.shopId` | The shop the event happened in, when your organisation has more than one. |
| `subject.resourceType` | `product`, `collection`, `order` or `mam`. |
| `subject.id` | The ID of what changed. |
| `actor.type` | `user` when someone on your team did it, `system` when a shopper, Stripe or Commerce Orbit did, such as a checkout, a payment or a MAM sync. |
| `actor.id` | The person's ID, or `null` for `system`. |
| `data` | The details below. |

## The events

| Event | When |
| --- | --- |
| `commerce.product.published` | A product becomes active. |
| `commerce.product.unpublished` | An active product becomes a draft, is archived or moves to the bin. |
| `commerce.product.changed` | An active product is saved, or MAM changes its price. |
| `commerce.stock.changed` | Stock changes: by hand, at checkout, when an unpaid order is cancelled, on a refund with restock, or from a MAM sync. |
| `commerce.collection.changed` | A collection is added, saved or moved to the bin. |
| `commerce.order.placed` | An order is paid by card or placed on invoice. |
| `commerce.order.paid` | An order is paid: by card at checkout, or an invoice marked as paid. A card order sends this right after `placed`. |
| `commerce.order.fulfilled` | Items on an order are marked as sent, in full or in part. |
| `commerce.order.refunded` | Money is refunded, from Commerce Orbit or in Stripe. |
| `commerce.order.cancelled` | Your team cancels an order. |
| `commerce.order.edited` | Your team changes the items, quantities or discount on a placed order. The data has the new total. |
| `commerce.return.requested` | A customer asks to return items, or your team starts a return. |
| `commerce.return.approved` | Your team approves a return. |
| `commerce.return.rejected` | Your team rejects a return. |
| `commerce.return.received` | The returned parcel arrives. `status` is `refunded` when it was refunded in the same step. |
| `commerce.mam.status` | The MAM connection is switched on or off, or its status changes between `ok`, `degraded`, `down` and `off`. |

### Product events

`data` has `productId`, `slug` and `title`:

```json
{ "productId": "6650f1c2a9e4b3d2c1f0e9b1", "slug": "tind-2-tent", "title": "Tind 2 tent" }
```

### Stock events

`data` has `productId`, `slug` and `variantIds`, the variants whose stock changed. Read the product to get the new numbers.

### Collection events

`data` has `collectionId`, `slug` and `title`.

### Order events

`data` has `orderId`, `reference`, `status` (`open` or `cancelled`), `paymentStatus`, `fulfilment`, `total`, `currency`, `lines` (how many lines) and `source` (where the cart came from, such as `https://northwilds.com`, or `null`):

```json
{
  "orderId": "6650f1c2a9e4b3d2c1f0eb42",
  "reference": "NW1042",
  "status": "open",
  "paymentStatus": "paid",
  "fulfilment": "unfulfilled",
  "total": 46700,
  "currency": "EUR",
  "lines": 3,
  "source": "https://northwilds.com"
}
```

Amounts are whole minor units: `46700` with `EUR` is €467.00. Events never carry customers' names, emails or addresses.

Orders cancelled by Commerce Orbit because the payment wasn't finished in time send no `commerce.order.cancelled`, as they were never placed.

### Return events

`data` has `returnId`, `reference` (such as `R1001`), `orderId`, `orderReference`, `status`, `items` (how many items), `refundAmount` (or `null`) and `currency`.

### MAM events

`data` has `status` and `message`, MAM's reason when something failed, or `null`.

## Delivery

Events are sent at least once. Each one waits in Commerce Orbit until Orbit Exchange accepts it. If Exchange can't be reached, Commerce Orbit tries again after 2 minutes, then waits twice as long each time, up to 12 hours between tries, for 12 tries in all. After that the event is no longer sent. Because a try can succeed even when the answer is lost, the same event can arrive twice: use `id` to spot it.
