---
title: Build an event
description: Create an event, group it, add lines, and read the result back.
icon: calendar-plus
---

An event is built in three writes: the event itself, its groups, then its
lines. Groups come before lines only because a line usually wants to name the
group it belongs to.

1. **Create the event**

    Events use the same bulk shape as products. Omit `eventId` to create; `name`
    and `priceListId` are required.

    ```bash
    curl -X POST "https://app.bloomcount.com/api/v1/events" \
      -H "Authorization: Bearer bloomcount_..." \
      -H "Content-Type: application/json" \
      -d '{
        "items": [
          {
            "name": "Smith & Jones Wedding",
            "priceListId": "pl_...",
            "status": "quoting",
            "ceremonyStart": 1781424000000,
            "venueAddress": "The Manor Hotel, Bath",
            "numberOfGuests": 80,
            "numberOfTables": 10
          }
        ]
      }'
    ```

    The price list sets the rates the event's lines are priced from. Materials and
    hire can come from their own lists via `materialsPriceListId` and
    `hirePriceListId`.

2. **Add the groups**

    Groups are the sections of a quote: a bouquet, the table centres, the church.
    A row without a `groupId` creates one.

    ```bash
    curl -X POST "https://app.bloomcount.com/api/v1/events/evt_.../groups" \
      -H "Authorization: Bearer bloomcount_..." \
      -H "Content-Type: application/json" \
      -d '{
        "groups": [
          { "name": "Bridal bouquet", "order": 0 },
          { "name": "Table centres", "order": 1, "quantityMode": "group", "groupQuantity": 10 }
        ]
      }'
    ```

    `quantityMode` is where the leverage is. Under `open`, each line's quantity
    stands alone. Under `group`, every line in the group is multiplied by
    `groupQuantity`, so one table centre described once covers all ten tables.

3. **Add the lines**

    A line names a variant, a quantity and usually a group.

    ```bash
    curl -X POST "https://app.bloomcount.com/api/v1/events/evt_.../items" \
      -H "Authorization: Bearer bloomcount_..." \
      -H "Content-Type: application/json" \
      -d '{
        "items": [
          { "variantId": "var_...", "quantity": 12, "groupId": "evtgrp_...", "supplierId": "sup_..." },
          { "variantId": "var_...", "quantity": 3,  "groupId": "evtgrp_..." }
        ]
      }'
    ```

    Rules worth knowing:

    - **`supplierId` is optional but meaningful.** It decides which supplier's
      price the line is frozen at. Omit it and the event's own default applies.
    - **A line's price is snapshotted when it is written.** Changing the line's
      supplier later re-resolves it; changing the price list afterwards does not
      rewrite lines that already exist.
    - **`groupId` has three meanings.** Omit it to leave a line where it is, name a
      group to move it there, and pass `null` to take it out of every group.

4. **Read it back**

    ```bash
    curl "https://app.bloomcount.com/api/v1/events/evt_..." \
      -H "Authorization: Bearer bloomcount_..."
    ```

    The response nests the whole structure: each group with its `quantityMode` and
    `groupQuantity`, and each line with the variant and product it points at, its
    `quantity`, and its `effectiveQuantity` — the quantity after the group
    multiplier, which is what actually gets ordered.

    ```json
    {
      "eventId": "evt_...",
      "name": "Smith & Jones Wedding",
      "groups": [
        {
          "groupId": "evtgrp_...",
          "name": "Table centres",
          "quantityMode": "group",
          "groupQuantity": 10,
          "items": [
            {
              "itemId": "evti_...",
              "variantId": "var_...",
              "productName": "Pink Mondial",
              "variantName": "50cm",
              "quantity": 3,
              "effectiveQuantity": 30,
              "mode": "consumable"
            }
          ]
        }
      ]
    }
    ```

## Changing and removing

Both contents endpoints are upserts. A row carrying an id updates that row; a
row carrying an id and `remove: true` deletes it.

```json
{
  "items": [
    { "itemId": "evti_...", "quantity": 5 },
    { "itemId": "evti_...", "remove": true }
  ]
}
```

Removing a group takes its lines with it unless you say otherwise. Pass
`moveItemsTo` with another group's id to keep them:

```json
{ "groups": [{ "groupId": "evtgrp_...", "remove": true, "moveItemsTo": "evtgrp_..." }] }
```

## Reading the money

Ask for pricing and the read carries it at three levels: each line's cost and
customer price, each group's subtotal, and the event's own totals.

```bash
curl "https://app.bloomcount.com/api/v1/events/evt_8h2k4m9p1x7c3v5b/?fields=totals" \
  -H "Authorization: Bearer bloomcount_..."
```

Three things to know before you reconcile anything against these numbers.

Cost and waste do not overlap. Cost covers the units a line consumes, waste
cost covers what pack rounding leaves over, and cost of goods is the two added
together. Adding all three double-counts the waste.

Waste is reported per line. The app's own waste figure pools packs across lines
that share a variant, so a busy event can differ slightly between the two.

Money is unrounded. Round at the point you present or invoice it, not before.

## Next

Hire lines are added through the same endpoint but behave differently enough to
be worth their own page: see [taking a hire booking](/guides/hire-bookings).
