BluetickedDeveloper docs

Commerce

20 endpoints, each with examples that open in the console prefilled.

All resources
GET/api/v1/commerce/products/{id}/salescommerce:readTry in console

Product sales

What one product sold in the last 30 days: units, revenue, and the most recent order lines as charged.

  • Counts paid orders only; units and revenue_cents cover every such line in the window, and lines lists the 20 most recent.
  • Each line keeps the price it was charged at, never the product's price today.
  • A malformed UUID answers 400 validation_failed before resource access; a well-formed unknown UUID or one outside your workspace answers 404.
curl -X GET "https://app.blueticked.com/api/v1/commerce/products/{id}/sales" \
  -H "Authorization: Bearer $BLUETICKED_API_KEY"
Response
{
  "ok": true,
  "window_days": 30,
  "units": 3,
  "revenue_cents": 40700,
  "lines": [
    {
      "order_id": "0446a464-20d9-4fcb-9e27-2ed069386cf7",
      "reference": "BT-7Q2K9",
      "number": 1043,
      "paid_at": "2026-09-30T08:00:00.000Z",
      "quantity": 1,
      "variant_name": "Large",
      "unit_price_cents": 18900
    }
  ]
}
GET/api/v1/commerce/products/{id}/stock-movementscommerce:readTry in console

Product stock movements

Every change to one product's stock count and its options' counts, newest first: by how much, the count it left, why and who.

Query parameters
limitstring
beforestring
  • Every change to a stock count is recorded by the database itself, whichever way it was made: a paid order, an edit in the dashboard, an API call.
  • cause says why: paid order <reference>, hand edit, created, API update, created via API, or browser edit; unrecorded marks a change whose writer named no cause.
  • delta is null when stock tracking was switched on or off; resulting_count is null when the count is no longer tracked.
  • by is the team member's name, or null for a paid order, an API key or a removed member.
  • Pages of limit (at most 100, default 50): pass next_before as before for the next page; it is null on the last page.
  • A malformed UUID answers 400 validation_failed before resource access; a well-formed unknown UUID or one outside your workspace answers 404.
curl -X GET "https://app.blueticked.com/api/v1/commerce/products/{id}/stock-movements?limit=50&before=128" \
  -H "Authorization: Bearer $BLUETICKED_API_KEY"
Response
{
  "ok": true,
  "movements": [
    {
      "id": 131,
      "variant_id": null,
      "variant_name": null,
      "delta": 12,
      "resulting_count": 14,
      "cause": "hand edit",
      "by": "Thandi M",
      "created_at": "2026-10-14T06:00:00.000Z"
    },
    {
      "id": 130,
      "variant_id": "6e2e7b58-a902-4056-bf1a-1f1938419e9b",
      "variant_name": "Large",
      "delta": -1,
      "resulting_count": 5,
      "cause": "paid order BT-7Q2K9",
      "by": null,
      "created_at": "2026-10-13T18:20:00.000Z"
    }
  ],
  "next_before": 130
}
GET/api/v1/commerce/productscommerce:readTry in console

List products

List the shop's products with live stock levels, newest first (up to 200).

  • stock_quantity: null means stock is not tracked for that product, different from 0, which means sold out.
  • updated_at is when the product was last changed.
  • source is blueticked for products created here and meta_catalog for ones imported from a Meta Commerce Manager catalog (with source_ref holding the catalog retailer id).
  • Soft-deleted products never appear; deactivated ones do, with is_active: false.
curl -X GET "https://app.blueticked.com/api/v1/commerce/products" \
  -H "Authorization: Bearer $BLUETICKED_API_KEY"
Response
{
  "ok": true,
  "products": [
    {
      "compare_at_cents": null,
      "id": "289e49ce-dfbc-4347-9b4c-52f001f009c3",
      "name": "Fynbos gift hamper",
      "slug": "fynbos-gift-hamper",
      "description": "Local honey, rooibos and a proudly SA snack selection.",
      "price_cents": 19950,
      "currency": "ZAR",
      "image_url": "https://example.com/hamper.jpg",
      "images": [
        {
          "url": "https://example.com/hamper.jpg",
          "storage_path": null,
          "alt": "The hamper, wrapped in kraft paper",
          "sort": 0
        },
        {
          "url": "https://example.com/hamper-open.jpg",
          "storage_path": null,
          "alt": "The hamper opened, showing the honey and rooibos",
          "sort": 1
        }
      ],
      "sku": "HAMPER-01",
      "stock_quantity": 14,
      "source": "blueticked",
      "source_ref": null,
      "is_active": true,
      "created_at": "2026-07-20T10:00:00.000Z",
      "variants": [
        {
          "id": "d37404e6-905e-48f7-9c9f-39cb178ba224",
          "compare_at_cents": null,
          "name": "Large",
          "price_cents": 24950,
          "stock_quantity": 6,
          "is_active": true,
          "sort": 0
        }
      ],
      "updated_at": "2026-09-28T08:00:00.000Z"
    }
  ]
}
POST/api/v1/commerce/productscommerce:writeTry in console

Create a product

Add a product to the shop.

Body parameters
namerequiredstring
descriptionstring
pricenumber
skustring
stock_quantitynumber
image_urlstring
  • price is in rands and is stored as cents (price_cents), rounded to the nearest cent.
  • Omit stock_quantity to leave stock untracked; the storefront then never shows a sold-out state for it.
  • sku is unique per organization; a duplicate returns 409.
  • images is the product's gallery in order, up to 12; the first is the cover, shown on the shop shelf, and image_url always repeats it. Each image needs an https url and alt text (a short description, up to 250 characters). An image you uploaded with the upload call also sends its path, which must be your own business's file.
  • Send images or image_url (one cover image), never both. Blueticked never fetches an image link; it only shows it.
curl -X POST "https://app.blueticked.com/api/v1/commerce/products" \
  -H "Authorization: Bearer $BLUETICKED_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Fynbos gift hamper",
  "description": "Local honey, rooibos and a proudly SA snack selection.",
  "price": 199.5,
  "sku": "HAMPER-01",
  "stock_quantity": 14,
  "image_url": "https://example.com/hamper.jpg"
}'
Response
{
  "ok": true,
  "product": {
    "compare_at_cents": null,
    "id": "289e49ce-dfbc-4347-9b4c-52f001f009c3",
    "name": "Fynbos gift hamper",
    "slug": "fynbos-gift-hamper",
    "description": "Local honey, rooibos and a proudly SA snack selection.",
    "price_cents": 19950,
    "currency": "ZAR",
    "image_url": "https://example.com/hamper.jpg",
    "images": [
      {
        "url": "https://example.com/hamper.jpg",
        "storage_path": null,
        "alt": "The hamper, wrapped in kraft paper",
        "sort": 0
      },
      {
        "url": "https://example.com/hamper-open.jpg",
        "storage_path": null,
        "alt": "The hamper opened, showing the honey and rooibos",
        "sort": 1
      }
    ],
    "sku": "HAMPER-01",
    "stock_quantity": 14,
    "source": "blueticked",
    "source_ref": null,
    "is_active": true,
    "created_at": "2026-07-20T10:00:00.000Z",
    "variants": [
      {
        "id": "d37404e6-905e-48f7-9c9f-39cb178ba224",
        "compare_at_cents": null,
        "name": "Large",
        "price_cents": 24950,
        "stock_quantity": 6,
        "is_active": true,
        "sort": 0
      }
    ]
  }
}
POST/api/v1/commerce/products/mediacommerce:writeTry in console

Upload a product image

Upload a photo and get back a hosted URL to set as a product's image_url.

Body parameters
filerequiredfile
  • Send multipart/form-data with a single file part, not JSON.
  • PNG, JPEG, WebP and GIF only, up to 10MB. The file's actual bytes are checked against its declared type, so a renamed file is rejected.
  • The returned url is public and permanent. Add it to a product's images with its path (or set it as image_url) in a create or update call. Uploading alone does not attach it to anything.
  • Images belong to the business, not to one site, so the same upload can be used on every site, in WhatsApp and on a payment link.
  • Up to 500 images per business.
  • A PNG, JPEG or WebP larger than 2,000 pixels on its longest side is resized to 2,000 pixels before it is stored, in its own format (GIFs are stored as sent). A still PNG, JPEG or WebP is stored the right way up and without its metadata (EXIF, XMP, IPTC, comments), so a photo's location never reaches the public URL; one whose metadata cannot be removed is refused (400 validation_failed, reason invalid_file). Animations are stored as sent. size is the stored file in bytes, so it can differ from what you uploaded.
curl -X POST "https://app.blueticked.com/api/v1/commerce/products/media" \
  -H "Authorization: Bearer $BLUETICKED_API_KEY" \
  -F "file=@product.jpg;type=image/jpeg"
Response
{
  "ok": true,
  "media": {
    "url": "https://cdn.blueticked.com/product-media/org_1/9f2c.jpg",
    "path": "org_1/9f2c.jpg",
    "size": 284119,
    "mime": "image/jpeg"
  }
}
PATCH/api/v1/commerce/products/{id}commerce:writeTry in console

Update a product or adjust stock

Change any product field. This is the stock-sync call: set stock_quantity after a delivery arrives or a till sale happens elsewhere.

Body parameters
stock_quantitynumber
is_activeboolean
  • A malformed UUID answers 400 validation_failed before resource access; a well-formed unknown UUID or one outside your workspace answers 404.
  • Send only the fields you are changing; everything else is untouched.
  • Set stock_quantity: null to stop tracking stock, 0 to mark sold out.
  • images replaces the whole gallery, in the order you send it (an empty list removes every image). image_url alone replaces only the cover and keeps the rest; null removes the cover and the next image becomes it. Never send both.
  • Paid orders decrement stock automatically. Only PATCH for movements Blueticked cannot see (till sales, stock takes, returns to shelf).
curl -X PATCH "https://app.blueticked.com/api/v1/commerce/products/{id}" \
  -H "Authorization: Bearer $BLUETICKED_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "stock_quantity": 9,
  "is_active": true
}'
Response
{
  "ok": true,
  "product": {
    "compare_at_cents": null,
    "id": "289e49ce-dfbc-4347-9b4c-52f001f009c3",
    "name": "Fynbos gift hamper",
    "slug": "fynbos-gift-hamper",
    "description": "Local honey, rooibos and a proudly SA snack selection.",
    "price_cents": 19950,
    "currency": "ZAR",
    "image_url": "https://example.com/hamper.jpg",
    "images": [
      {
        "url": "https://example.com/hamper.jpg",
        "storage_path": null,
        "alt": "The hamper, wrapped in kraft paper",
        "sort": 0
      },
      {
        "url": "https://example.com/hamper-open.jpg",
        "storage_path": null,
        "alt": "The hamper opened, showing the honey and rooibos",
        "sort": 1
      }
    ],
    "sku": "HAMPER-01",
    "stock_quantity": 9,
    "source": "blueticked",
    "source_ref": null,
    "is_active": true,
    "created_at": "2026-07-20T10:00:00.000Z",
    "variants": [
      {
        "id": "d37404e6-905e-48f7-9c9f-39cb178ba224",
        "compare_at_cents": null,
        "name": "Large",
        "price_cents": 24950,
        "stock_quantity": 6,
        "is_active": true,
        "sort": 0
      }
    ]
  }
}
DELETE/api/v1/commerce/products/{id}commerce:writeTry in console

Remove a product

Soft-delete a product: it leaves the shop but stays behind old order lines.

  • A malformed UUID answers 400 validation_failed before resource access; a well-formed unknown UUID or one outside your workspace answers 404.
  • Order history keeps the product's name and price as they were when sold.
curl -X DELETE "https://app.blueticked.com/api/v1/commerce/products/{id}" \
  -H "Authorization: Bearer $BLUETICKED_API_KEY"
Response
{
  "ok": true
}
GET/api/v1/commerce/orderscommerce:readTry in console

List orders

The order book, newest first: what sold, to whom, and where each order stands.

Query parameters
statusstring
sincestring
limitstring
  • status filters to one of pending, paid, failed, abandoned, refunded; since (ISO 8601) returns orders created at or after that moment; limit caps the page (default 50, max 200).
  • q searches reference, customer name, customer email and description across the whole book. offset pages through results; total is the count matching the filters, so offset + limit < total means there is more.
  • unfulfilled=true returns paid orders whose goods have not gone out: the packing list, ready to poll. Add fulfilment=delivery or fulfilment=collection to split it by how the order leaves.
  • fulfilled_since (ISO 8601) returns paid orders sent or collected at or after that moment. order=oldest returns the longest-waiting order first (the default is newest).
  • contact_id (a contact's UUID) returns the orders linked to that contact: a buyer becomes a contact when their order is paid, so this lists one customer's orders even when they gave no email. A malformed id answers 400.
  • For accounting, paid and refunded are the only statuses that involve money having moved.
  • Each row is the same shape the commerce.order.* webhooks deliver. Store either, they never disagree.
curl -X GET "https://app.blueticked.com/api/v1/commerce/orders?status=paid&limit=50" \
  -H "Authorization: Bearer $BLUETICKED_API_KEY"
Response
{
  "ok": true,
  "orders": [
    {
      "id": "8b43c136-a5d3-4352-99d4-bcf43354c548",
      "reference": "BT-ORD-8H2K1",
      "number": 1043,
      "paystack_reference": "4099260516",
      "status": "paid",
      "amount_cents": 45900,
      "currency": "ZAR",
      "description": "2 items from Aloe Ridge Guest House",
      "customer": {
        "name": "Naledi Dlamini",
        "email": "naledi@example.com",
        "phone": "+27800000001",
        "sms_marketing_opt_in": false
      },
      "fulfilment": {
        "method": "delivery",
        "delivery_address": "12 Protea Rd\nNewlands\nCape Town\n7700",
        "delivery_zone": "Southern Suburbs",
        "delivery_fee_cents": 6000,
        "fulfilled_at": null,
        "ready_for_collection_at": null,
        "tracking_reference": null,
        "waybill_id": null,
        "note": null
      },
      "discount": {
        "code": "WINTER10",
        "amount_cents": 4590
      },
      "payment": {
        "provider": "paystack",
        "method": null
      },
      "items": [
        {
          "id": "258ab704-cb29-4858-a35d-724d150bc85b",
          "product_id": "289e49ce-dfbc-4347-9b4c-52f001f009c3",
          "variant_id": "2a85a3c1-a901-49bf-a2a9-71e9d719a994",
          "variant_name": "Large",
          "name": "Fynbos gift hamper",
          "unit_price_cents": 19950,
          "quantity": 2
        }
      ],
      "site_id": "2503846b-7ed1-42bb-9f5d-f26d5cfdf4a9",
      "paid_at": "2026-08-03T09:14:00.000Z",
      "refunded_at": null,
      "failure_reason": null,
      "link_message_id": null,
      "emails": {
        "confirmation": {
          "state": "sent",
          "at": "2026-08-03T09:15:02.000Z",
          "attempts": 1,
          "reason": null
        },
        "sale_notification": {
          "state": "sent",
          "at": "2026-08-03T09:15:03.000Z",
          "attempts": 1,
          "reason": null
        }
      },
      "created_at": "2026-08-03T09:12:41.000Z"
    }
  ],
  "total": 1
}
GET/api/v1/commerce/orders/overviewcommerce:readTry in console

Order totals

The order book's headline numbers (counts, revenue and last sale), computed over every order in one call.

  • Computed in the database over the whole book, never derived from a page of /orders, so it stays exact at any order count.
  • to_fulfil is paid orders with a fulfilment method chosen and nothing sent yet: the packing list's size.
  • refunded_cents sits beside revenue rather than being subtracted from it, so both figures reconcile against Paystack as-is.
  • The day's work, each the length of its /orders filter: to_send (paid, delivery, not sent: unfulfilled=true&fulfilment=delivery), to_collect (the same for collection), awaiting_payment (status=pending) and done_30d (sent or collected in the last 30 days: fulfilled_since).
  • paid_month_cents is the money paid since midnight on the 1st of this month, South African time, summed over every paid order.
curl -X GET "https://app.blueticked.com/api/v1/commerce/orders/overview" \
  -H "Authorization: Bearer $BLUETICKED_API_KEY"
Response
{
  "ok": true,
  "overview": {
    "total_orders": 10234,
    "to_fulfil": 3,
    "paid_orders": 9411,
    "refunded_orders": 87,
    "paid_revenue_cents": 184039550,
    "refunded_cents": 1220050,
    "last_sale_at": "2026-08-05T09:14:00.000Z",
    "to_send": 2,
    "to_collect": 1,
    "awaiting_payment": 4,
    "done_30d": 318,
    "paid_month_cents": 4128050
  }
}
GET/api/v1/commerce/orders/{id}commerce:readTry in console

Get one order

Fetch a single order in full: the re-fetch half of the webhook contract.

  • A malformed UUID answers 400 validation_failed before resource access; a well-formed unknown UUID or one outside your workspace answers 404.
  • {id} is the order's id (also the order_id on every commerce webhook payload).
  • number is the order's short number, sequential per business from 1001: the number its buyer and the dashboard see (#1043), and a search term for GET /api/v1/commerce/orders?q=1043. reference stays the payment reference and the key of the buyer's order page. number is null only for an order the numbering has not reached.
  • payment says how the order was paid: provider is paystack, payfast or manual (the shop marked it paid by hand in the dashboard, when the money came another way), and method is cash, eft, card_machine or other for a manual payment, null otherwise. Who marked it is never part of the API.
  • fulfilment.tracking_reference appears once a courier is booked; fulfilment.note flags a paid order that stock could not fully cover; fulfilment.ready_for_collection_at is when a collection order was marked ready for collection, null until then.
  • failure_reason says why an order was set aside, from a documented list that may grow: link_not_delivered means the order came from a flow's payment link whose message could not be delivered, so the customer never got the link (status abandoned). It is null otherwise, and once the order is paid.
  • link_message_id is the id of the message the order's payment link went out in (read it with GET /api/v1/messages/{id} for that message's own failure_reason); null for an order no payment link made.
  • customer.sms_marketing_opt_in is true when the buyer ticked "Send me reminders and offers by SMS" at a storefront checkout, with a cell number; it is false otherwise, and on orders placed before 16 Oct 2026, when the question was not asked. The yes is also recorded on the buyer's contact as an SMS opt-in (consent source checkout, naming the order), unless that number has opted out. A flow on commerce.order.abandoned sends its SMS only to a buyer who said yes, as a marketing message (it needs the opt-in and keeps to the SMS sending hours), and sends no WhatsApp; order updates need no yes.
  • emails says how each automatic order email went, by kind (confirmation and sale_notification when paid, ready_for_collection once a collection order is marked ready, shipped, tracking, refunded): state is sent, retrying (a send failed and it will be tried again), failed (it could not be sent after its last try) or skipped; at is when, attempts the sends tried so far, and reason: on a skip, one of no_buyer_email, no_owner_email, no_tracking_reference or reserved_address; on a failure, why its last try failed, one of message_rejected (the recipient address or the message was refused), sender_not_allowed (our sending setup was refused), provider_rate_limited, provider_unavailable, send_error (it failed on our side before an answer), send_unconfirmed (the email service never answered the last send, so it may have arrived: check with the buyer before sending it another way) or provider_error (any other answer); null otherwise (the lists may grow; anything else reads null). A kind appears once something was recorded for it. State only: no address or message content. On a webhook it is a snapshot at the moment of the event.
curl -X GET "https://app.blueticked.com/api/v1/commerce/orders/{id}" \
  -H "Authorization: Bearer $BLUETICKED_API_KEY"
Response
{
  "ok": true,
  "order": {
    "id": "8b43c136-a5d3-4352-99d4-bcf43354c548",
    "reference": "BT-ORD-8H2K1",
    "number": 1043,
    "paystack_reference": "4099260516",
    "status": "paid",
    "amount_cents": 45900,
    "currency": "ZAR",
    "description": "2 items from Aloe Ridge Guest House",
    "customer": {
      "name": "Naledi Dlamini",
      "email": "naledi@example.com",
      "phone": "+27800000001",
      "sms_marketing_opt_in": false
    },
    "fulfilment": {
      "method": "delivery",
      "delivery_address": "12 Protea Rd\nNewlands\nCape Town\n7700",
      "delivery_zone": "Southern Suburbs",
      "delivery_fee_cents": 6000,
      "fulfilled_at": null,
      "ready_for_collection_at": null,
      "tracking_reference": null,
      "waybill_id": null,
      "note": null
    },
    "discount": {
      "code": "WINTER10",
      "amount_cents": 4590
    },
    "payment": {
      "provider": "paystack",
      "method": null
    },
    "items": [
      {
        "id": "258ab704-cb29-4858-a35d-724d150bc85b",
        "product_id": "289e49ce-dfbc-4347-9b4c-52f001f009c3",
        "variant_id": "2a85a3c1-a901-49bf-a2a9-71e9d719a994",
        "variant_name": "Large",
        "name": "Fynbos gift hamper",
        "unit_price_cents": 19950,
        "quantity": 2
      }
    ],
    "site_id": "2503846b-7ed1-42bb-9f5d-f26d5cfdf4a9",
    "paid_at": "2026-08-03T09:14:00.000Z",
    "refunded_at": null,
    "failure_reason": null,
    "link_message_id": null,
    "emails": {
      "confirmation": {
        "state": "sent",
        "at": "2026-08-03T09:15:02.000Z",
        "attempts": 1,
        "reason": null
      },
      "sale_notification": {
        "state": "sent",
        "at": "2026-08-03T09:15:03.000Z",
        "attempts": 1,
        "reason": null
      }
    },
    "created_at": "2026-08-03T09:12:41.000Z"
  }
}
PATCH/api/v1/commerce/orders/{id}/fulfilmentcommerce:writeTry in console

Mark an order fulfilled

Mark a paid order handed over (or undo it): the call a warehouse or fulfilment system makes when goods leave.

Body parameters
fulfilledrequiredboolean
  • A malformed UUID answers 400 validation_failed before resource access; a well-formed unknown UUID or one outside your workspace answers 404.
  • Only a paid order can be marked fulfilled; fulfilled: false undoes a mistaken hand-over.
  • Marking fulfilled emits the commerce.order.fulfilled webhook event once in the order's life.
  • Marking an order that is already fulfilled answers 200 with its first fulfilled_at and already_fulfilled: true, and sends nothing again: no second webhook, email or flow. Retrying the call is safe.
  • Marking an order again after fulfilled: false records the new fulfilled_at and announces nothing: the customer already heard it shipped. A mark that loses to a concurrent undo answers 409 conflict.
  • Courier bookings through The Courier Guy mark the order fulfilled themselves. A courier booked for an order already marked fulfilled only sends the buyer the tracking number.
curl -X PATCH "https://app.blueticked.com/api/v1/commerce/orders/{id}/fulfilment" \
  -H "Authorization: Bearer $BLUETICKED_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "fulfilled": true
}'
Response
{
  "ok": true,
  "fulfilled_at": "2026-08-03T11:30:00.000Z",
  "already_fulfilled": false
}
POST/api/v1/commerce/orders/{id}/ready-for-collectioncommerce:writeTry in console

Mark an order ready for collection

Tell the buyer of a paid collection order that it is ready to collect: one email in the business's name, with the shop's collection note.

  • A malformed UUID answers 400 validation_failed before resource access; a well-formed unknown UUID or one outside your workspace answers 404.
  • Only a paid order with fulfilment.method collection that has not been collected can be marked; anything else answers 409 conflict with the reason. It takes no body.
  • The buyer is emailed once, as the ready_for_collection kind in the order's emails: subject "Your order #1043 is ready to collect", with the collection note from the shop's fulfilment settings read when it is sent. A failed send is tried again automatically. Editing the collection note afterwards does not send it again.
  • Marking an order that is already ready answers 200 with its first ready_for_collection_at and already_ready: true, and sends nothing again. Retrying the call is safe.
  • There is no undo. Mark the order fulfilled with PATCH /api/v1/commerce/orders/{id}/fulfilment when the buyer collects it; a refund ends it too.
curl -X POST "https://app.blueticked.com/api/v1/commerce/orders/{id}/ready-for-collection" \
  -H "Authorization: Bearer $BLUETICKED_API_KEY"
Response
{
  "ok": true,
  "ready_for_collection_at": "2026-08-03T10:15:00.000Z",
  "already_ready": false
}
GET/api/v1/commerce/fulfilmentcommerce:readTry in console

Get delivery & collection settings

How the shop currently offers collection and delivery, with fees.

  • settings: null means fulfilment has never been configured: checkout takes payment without asking how the order arrives.
curl -X GET "https://app.blueticked.com/api/v1/commerce/fulfilment" \
  -H "Authorization: Bearer $BLUETICKED_API_KEY"
Response
{
  "ok": true,
  "settings": {
    "vat_number": null,
    "offers_collection": true,
    "offers_delivery": true,
    "delivery_fee_cents": 6000,
    "delivery_free_over_cents": null,
    "collection_note": "Shop 23, Bellville Mall (Mon to Fri, 09:00 to 17:00)",
    "delivery_note": "Courier, 2 to 3 working days anywhere in SA"
  }
}
PUT/api/v1/commerce/fulfilmentcommerce:writeTry in console

Set delivery & collection settings

Configure collection and delivery for checkout: the settings an agent needs before a shop can sell.

Body parameters
offers_collectionboolean
offers_deliveryboolean
delivery_fee_randsnumber
delivery_free_over_randsnumber
collection_notestring
delivery_notestring
  • At least one of offers_collection / offers_delivery must be true.
  • Fees are given in rands and stored in cents; named delivery areas (configured in the dashboard) replace the flat fee entirely while any exist.
curl -X PUT "https://app.blueticked.com/api/v1/commerce/fulfilment" \
  -H "Authorization: Bearer $BLUETICKED_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "offers_collection": true,
  "offers_delivery": true,
  "delivery_fee_rands": 60,
  "delivery_free_over_rands": 500,
  "collection_note": "Shop 23, Bellville Mall (Mon to Fri, 09:00 to 17:00)",
  "delivery_note": "Courier, 2 to 3 working days anywhere in SA"
}'
Response
{
  "ok": true,
  "settings": {
    "vat_number": null,
    "offers_collection": true,
    "offers_delivery": true,
    "delivery_fee_cents": 6000,
    "delivery_free_over_cents": 50000,
    "collection_note": "Shop 23, Bellville Mall (Mon to Fri, 09:00 to 17:00)",
    "delivery_note": "Courier, 2 to 3 working days anywhere in SA"
  }
}
GET/api/v1/commerce/discountscommerce:readTry in console

List discount codes

Every discount code with its derived uses: paid, and still waiting for payment.

  • redemptions counts paid orders carrying the code. redemptions_waiting counts orders that still hold a use while they can be paid: pending, failed or abandoned, placed in the last 7 days. Checkout counts both against max_redemptions, so a capped code is used up when the two together reach it.
curl -X GET "https://app.blueticked.com/api/v1/commerce/discounts" \
  -H "Authorization: Bearer $BLUETICKED_API_KEY"
Response
{
  "ok": true,
  "codes": [
    {
      "amount_off_cents": null,
      "min_order_cents": null,
      "max_redemptions": null,
      "expires_at": null,
      "created_at": "2026-10-11T08:00:00.000Z",
      "id": "6b9d5a20-1c3d-44ef-b193-0ec9747016d9",
      "code": "WELCOME10",
      "percent_off": 10,
      "free_delivery": false,
      "is_active": true,
      "redemptions": 3,
      "redemptions_waiting": 1
    }
  ]
}
POST/api/v1/commerce/discountscommerce:writeTry in console

Create a discount code

Create a checkout discount: a percentage, a rand amount, or free delivery, exactly one of the three.

Body parameters
coderequiredstring
percent_offnumber
expires_atstring
max_redemptionsnumber
  • Pass exactly ONE of percent_off (1 to 100), amount_off_rands (R1 to R100 000) or free_delivery: true.
  • Codes are stored uppercase (2 to 40 letters, numbers or dashes) and normalised the same way at checkout.
  • A date-only expires_at like 2026-09-30 means through the end of that day. min_order_rands gates any kind.
curl -X POST "https://app.blueticked.com/api/v1/commerce/discounts" \
  -H "Authorization: Bearer $BLUETICKED_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "code": "WELCOME10",
  "percent_off": 10,
  "expires_at": "2026-09-30",
  "max_redemptions": 100
}'
Response
{
  "ok": true,
  "discount": {
    "amount_off_cents": null,
    "min_order_cents": null,
    "max_redemptions": null,
    "expires_at": null,
    "created_at": "2026-10-11T08:00:00.000Z",
    "id": "6b9d5a20-1c3d-44ef-b193-0ec9747016d9",
    "code": "WELCOME10",
    "percent_off": 10,
    "free_delivery": false,
    "is_active": true,
    "redemptions": 0
  }
}
PATCH/api/v1/commerce/discounts/{id}commerce:writeTry in console

Pause or resume a discount code

Stop a code redeeming (or turn it back on) without deleting it.

Body parameters
is_activerequiredboolean
  • A malformed UUID answers 400 validation_failed before resource access; a well-formed unknown UUID or one outside your workspace answers 404.
  • A paused code is refused at checkout immediately; history keeps its redemptions.
curl -X PATCH "https://app.blueticked.com/api/v1/commerce/discounts/{id}" \
  -H "Authorization: Bearer $BLUETICKED_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "is_active": false
}'
Response
{
  "ok": true,
  "is_active": false
}
DELETE/api/v1/commerce/discounts/{id}commerce:writeTry in console

Delete a discount code

Remove a code for good. It is safe because orders copy the code as text.

  • A malformed UUID answers 400 validation_failed before resource access; a well-formed unknown UUID or one outside your workspace answers 404.
  • Past orders keep their record of the code and the amount it took off.
curl -X DELETE "https://app.blueticked.com/api/v1/commerce/discounts/{id}" \
  -H "Authorization: Bearer $BLUETICKED_API_KEY"
Response
{
  "ok": true
}