{
  "info": {
    "name": "Zinevu API v1",
    "description": "The Zinevu public API: read your leads, offers, customers and catalogue, and write your price matrices and promotions.\n\nSETUP\n1. Mint a key in the portal under Settings -> Developers. The raw key is shown once.\n2. Set the collection variables `base_url` and `api_key`.\n\nEvery request here authenticates with `Authorization: Bearer {{api_key}}`.\n\nWRITES\nWrite requests need a write scope — `prices:write` or `promotions:write` — and neither is ever granted implicitly. The price writes and the promotion backfill all accept a dry run, which reports exactly what would change without changing it — start there. Send an `Idempotency-Key` header if your importer retries; the same key with the same body replays the first response instead of writing again.\n\nNeither writing a price nor changing a promotion ever reprices an offer that has already gone out.\n\nFull documentation: https://app.zinevu.com/developers",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "item": [
    {
      "name": "Account",
      "item": [
        {
          "name": "Who am I",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{api_key}}"
              },
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/me",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "me"
              ]
            },
            "description": "The dealer behind this key, and the scopes the key carries. The cheapest way to check a key works. Needs no scope."
          },
          "response": []
        }
      ]
    },
    {
      "name": "Leads",
      "item": [
        {
          "name": "List leads",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{api_key}}"
              },
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/leads?status=&source=&limit=25&cursor=&updated_since=2026-09-01T00:00:00Z",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "leads"
              ],
              "query": [
                {
                  "key": "status",
                  "value": "",
                  "description": "Filter by lead status.",
                  "disabled": true
                },
                {
                  "key": "source",
                  "value": "",
                  "description": "Filter by source, e.g. form or meta.",
                  "disabled": true
                },
                {
                  "key": "limit",
                  "value": "25",
                  "description": "Page size, 1-100.",
                  "disabled": true
                },
                {
                  "key": "cursor",
                  "value": "",
                  "description": "From meta.next_cursor of the previous page. Omit for the first page.",
                  "disabled": true
                },
                {
                  "key": "updated_since",
                  "value": "2026-09-01T00:00:00Z",
                  "description": "ISO-8601. Only records changed at or after this.",
                  "disabled": true
                }
              ]
            },
            "description": "Leads with their configuration answers and source. Cursor paginated: follow meta.next_cursor until it is null. Scope: leads:read."
          },
          "response": []
        },
        {
          "name": "Get one lead",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{api_key}}"
              },
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/leads/14868",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "leads",
                "14868"
              ]
            },
            "description": "One lead by id. Scope: leads:read."
          },
          "response": []
        }
      ]
    },
    {
      "name": "Offers",
      "item": [
        {
          "name": "List offers",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{api_key}}"
              },
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/offers?status=&limit=25&cursor=&updated_since=2026-09-01T00:00:00Z",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "offers"
              ],
              "query": [
                {
                  "key": "status",
                  "value": "",
                  "description": "Filter by offer status.",
                  "disabled": true
                },
                {
                  "key": "limit",
                  "value": "25",
                  "description": "Page size, 1-100.",
                  "disabled": true
                },
                {
                  "key": "cursor",
                  "value": "",
                  "description": "From meta.next_cursor of the previous page. Omit for the first page.",
                  "disabled": true
                },
                {
                  "key": "updated_since",
                  "value": "2026-09-01T00:00:00Z",
                  "description": "ISO-8601. Only records changed at or after this.",
                  "disabled": true
                }
              ]
            },
            "description": "Offers with their line items and totals. Scope: offers:read."
          },
          "response": []
        },
        {
          "name": "Get one offer",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{api_key}}"
              },
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/offers/14868",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "offers",
                "14868"
              ]
            },
            "description": "One offer by id. Scope: offers:read."
          },
          "response": []
        }
      ]
    },
    {
      "name": "Customers",
      "item": [
        {
          "name": "List customers",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{api_key}}"
              },
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/customers?email=&limit=25&cursor=&updated_since=2026-09-01T00:00:00Z",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "customers"
              ],
              "query": [
                {
                  "key": "email",
                  "value": "",
                  "description": "Exact e-mail match.",
                  "disabled": true
                },
                {
                  "key": "limit",
                  "value": "25",
                  "description": "Page size, 1-100.",
                  "disabled": true
                },
                {
                  "key": "cursor",
                  "value": "",
                  "description": "From meta.next_cursor of the previous page. Omit for the first page.",
                  "disabled": true
                },
                {
                  "key": "updated_since",
                  "value": "2026-09-01T00:00:00Z",
                  "description": "ISO-8601. Only records changed at or after this.",
                  "disabled": true
                }
              ]
            },
            "description": "Customer records and their addresses. Scope: customers:read."
          },
          "response": []
        },
        {
          "name": "Get one customer",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{api_key}}"
              },
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/customers/1",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "customers",
                "1"
              ]
            },
            "description": "One customer by id. Scope: customers:read."
          },
          "response": []
        }
      ]
    },
    {
      "name": "Catalogue (read)",
      "item": [
        {
          "name": "List price matrices",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{api_key}}"
              },
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/price-matrices?code=zv_ovk_poly&limit=25&cursor=&updated_since=2026-09-01T00:00:00Z",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "price-matrices"
              ],
              "query": [
                {
                  "key": "code",
                  "value": "zv_ovk_poly",
                  "description": "Exact code — returns just that grid.",
                  "disabled": true
                },
                {
                  "key": "limit",
                  "value": "25",
                  "description": "Page size, 1-100.",
                  "disabled": true
                },
                {
                  "key": "cursor",
                  "value": "",
                  "description": "From meta.next_cursor of the previous page. Omit for the first page.",
                  "disabled": true
                },
                {
                  "key": "updated_since",
                  "value": "2026-09-01T00:00:00Z",
                  "description": "ISO-8601. Only records changed at or after this.",
                  "disabled": true
                }
              ]
            },
            "description": "Your price grids — axes and dimensions only, no cells. `code` is the stable identifier a pricing rule names and a write addresses; `id` only orders the cursor. Scope: prices:read."
          },
          "response": []
        },
        {
          "name": "Get one price matrix",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{api_key}}"
              },
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/price-matrices/1",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "price-matrices",
                "1"
              ]
            },
            "description": "One grid, as a table (`grid`) and as a flat list (`cells`). All three matrix types share the `cells` shape: a list_1d grid reports a null column and a fixed one a single cell with no axes, so you never branch on matrix_type. Scope: prices:read."
          },
          "response": []
        },
        {
          "name": "List materials",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{api_key}}"
              },
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/materials?active_only=1&limit=25&cursor=&updated_since=2026-09-01T00:00:00Z",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "materials"
              ],
              "query": [
                {
                  "key": "active_only",
                  "value": "1",
                  "description": "Skip rows the dealer switched off.",
                  "disabled": true
                },
                {
                  "key": "limit",
                  "value": "25",
                  "description": "Page size, 1-100.",
                  "disabled": true
                },
                {
                  "key": "cursor",
                  "value": "",
                  "description": "From meta.next_cursor of the previous page. Omit for the first page.",
                  "disabled": true
                },
                {
                  "key": "updated_since",
                  "value": "2026-09-01T00:00:00Z",
                  "description": "ISO-8601. Only records changed at or after this.",
                  "disabled": true
                }
              ]
            },
            "description": "Your priced add-ons — an extra post, a concrete footing, hauling the old roof away. Flat per-unit items; the size-driven catalogue is /price-matrices. Scope: prices:read."
          },
          "response": []
        },
        {
          "name": "List offer items",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{api_key}}"
              },
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/offer-items?active_only=1&has_article_number=1&limit=25&cursor=&updated_since=2026-09-01T00:00:00Z",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "offer-items"
              ],
              "query": [
                {
                  "key": "active_only",
                  "value": "1",
                  "description": "Skip items the dealer switched off.",
                  "disabled": true
                },
                {
                  "key": "has_article_number",
                  "value": "1",
                  "description": "Only items that carry an article number.",
                  "disabled": true
                },
                {
                  "key": "limit",
                  "value": "25",
                  "description": "Page size, 1-100.",
                  "disabled": true
                },
                {
                  "key": "cursor",
                  "value": "",
                  "description": "From meta.next_cursor of the previous page. Omit for the first page.",
                  "disabled": true
                },
                {
                  "key": "updated_since",
                  "value": "2026-09-01T00:00:00Z",
                  "description": "ISO-8601. Only records changed at or after this.",
                  "disabled": true
                }
              ]
            },
            "description": "The rules under Settings -> Offer items, each with the article number it has in your ERP (e.g. Odoo's Internal Reference). Lines of GET /offers/{id} carry offer_item_id and article_number. Scope: prices:read."
          },
          "response": []
        },
        {
          "name": "Get offer item by article number",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{api_key}}"
              },
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/offer-items/vk-1001",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "offer-items",
                "vk-1001"
              ]
            },
            "description": "One offer item by its article number. Case does not matter; numbers are stored lowercase and unique per account. 404 when no item has it. Scope: prices:read."
          },
          "response": []
        }
      ]
    },
    {
      "name": "Prices (write)",
      "item": [
        {
          "name": "Upsert price matrices — DRY RUN",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{api_key}}"
              },
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/price-matrices?dry_run=1",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "price-matrices"
              ],
              "query": [
                {
                  "key": "dry_run",
                  "value": "1",
                  "description": "Validate and report; write nothing.",
                  "disabled": true
                }
              ]
            },
            "description": "Validates the whole payload and reports what would change. Writes nothing. Run this first: the dry run is the same validation as the real write with the last step skipped, so anything it accepts, the write accepts. Scope: prices:write.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"matrices\": [\n    {\n      \"code\": \"example_roof\",\n      \"name\": \"Example — polycarbonate roof\",\n      \"matrix_type\": \"grid_2d\",\n      \"row_axis\": \"diepte_cm\",\n      \"col_axis\": \"breedte_cm\",\n      \"row_values\": [\n        250,\n        300\n      ],\n      \"col_values\": [\n        300,\n        400\n      ],\n      \"prices\": [\n        [\n          1176,\n          1344\n        ],\n        [\n          1334,\n          1596\n        ]\n      ]\n    },\n    {\n      \"code\": \"example_montage\",\n      \"name\": \"Example — installation\",\n      \"matrix_type\": \"fixed\",\n      \"fixed_price\": 1250\n    }\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Upsert price matrices",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{api_key}}"
              },
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{$guid}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/price-matrices",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "price-matrices"
              ]
            },
            "description": "Creates or replaces whole grids, addressed by `code`. A code you have not sent creates a grid; one you have updates it — so re-running last night's import changes nothing and adds nothing.\n\nNothing is applied unless everything validates: one bad entry rejects the request with a list of every problem in it.\n\nThe Idempotency-Key header is optional. With it, a retry of the same body replays the first response (marked Idempotent-Replay: true) instead of writing again. Generate a fresh one per intent — Postman's {{$guid}} does that.\n\nScope: prices:write.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"matrices\": [\n    {\n      \"code\": \"example_roof\",\n      \"name\": \"Example — polycarbonate roof\",\n      \"matrix_type\": \"grid_2d\",\n      \"row_axis\": \"diepte_cm\",\n      \"col_axis\": \"breedte_cm\",\n      \"row_values\": [\n        250,\n        300\n      ],\n      \"col_values\": [\n        300,\n        400\n      ],\n      \"prices\": [\n        [\n          1176,\n          1344\n        ],\n        [\n          1334,\n          1596\n        ]\n      ]\n    },\n    {\n      \"code\": \"example_montage\",\n      \"name\": \"Example — installation\",\n      \"matrix_type\": \"fixed\",\n      \"fixed_price\": 1250\n    }\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Patch individual prices",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{api_key}}"
              },
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{$guid}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/price-matrices/cells",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "price-matrices",
                "cells"
              ]
            },
            "description": "Changes prices in place without resending a grid — the 'this size went up' path. A row or column value that is not already on the grid is an error, not an insertion: growing a grid changes what every pricing rule reading it can quote, so state the whole shape through the upsert instead.\n\nFor a list_1d grid, omit `col`. Scope: prices:write.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"updates\": [\n    {\n      \"code\": \"example_roof\",\n      \"cells\": [\n        {\n          \"row\": 300,\n          \"col\": 400,\n          \"price\": 1650\n        }\n      ]\n    }\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "Promotions",
      "item": [
        {
          "name": "List promotions",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{api_key}}"
              },
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/promotions?active_only=1&code=autumn2026&limit=25&cursor=&updated_since=2026-09-01T00:00:00Z",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "promotions"
              ],
              "query": [
                {
                  "key": "active_only",
                  "value": "1",
                  "description": "Only promotions running right now.",
                  "disabled": true
                },
                {
                  "key": "code",
                  "value": "autumn2026",
                  "description": "Filter by your own code.",
                  "disabled": true
                },
                {
                  "key": "limit",
                  "value": "25",
                  "description": "Page size, 1-100.",
                  "disabled": true
                },
                {
                  "key": "cursor",
                  "value": "",
                  "description": "From meta.next_cursor of the previous page. Omit for the first page.",
                  "disabled": true
                },
                {
                  "key": "updated_since",
                  "value": "2026-09-01T00:00:00Z",
                  "description": "ISO-8601. Only records changed at or after this.",
                  "disabled": true
                }
              ]
            },
            "description": "Your promotions, newest first. Two flags are computed for you: is_live (enabled and inside its window) and is_applied_to_new_offers (the one new offers actually get — several can be live at once, and the newest wins). Scope: promotions:read."
          },
          "response": []
        },
        {
          "name": "Get one promotion",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{api_key}}"
              },
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/promotions/41",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "promotions",
                "41"
              ]
            },
            "description": "One promotion, plus offers_count: how many offers are carrying its discount. Scope: promotions:read."
          },
          "response": []
        },
        {
          "name": "Start a promotion",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{api_key}}"
              },
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{$guid}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/promotions",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "promotions"
              ]
            },
            "description": "While it is live, every NEW offer is created carrying this rate and this wording. Offers that already exist are untouched — spreading it over the ones still open is the separate 'Apply to open offers' call.\n\nWindow edges are optional and either may be null, meaning open-ended. Send them with an offset: a timestamp without one is read as Dutch local time, the same rule the portal uses.\n\nThe response carries backfill_preview: how many open offers this could still be applied to.\n\nThe response also carries also_live: the other promotions running right now. Saving a live promotion displaces whichever was winning before (newest wins), so an empty list is the reassuring answer.\n\nScope: promotions:write.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Najaarsactie\",\n  \"code\": \"autumn2026\",\n  \"discount_pct\": 10,\n  \"description\": \"10% najaarskorting\",\n  \"valid_from\": \"2026-09-01T00:00:00+02:00\",\n  \"valid_until\": \"2026-09-30T23:59:59+02:00\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Change a promotion",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{api_key}}"
              },
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/promotions/41",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "promotions",
                "41"
              ]
            },
            "description": "Partial: send only what moves, and anything you leave out keeps its stored value. null is a real value and clears a field.\n\nChanging a promotion never re-prices an offer that carries it — the offer holds a snapshot of the terms it was created with.\n\nTo stop a promotion while keeping the record, send {\"is_active\": false}.\n\nThe response also carries also_live: the other promotions running right now. Saving a live promotion displaces whichever was winning before (newest wins), so an empty list is the reassuring answer.\n\nScope: promotions:write.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"discount_pct\": 15,\n  \"valid_until\": \"2026-10-31T23:59:59+01:00\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Apply to open offers — DRY RUN",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{api_key}}"
              },
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/promotions/41/apply",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "promotions",
                "41",
                "apply"
              ]
            },
            "description": "Counts what a real run would change, and changes nothing. eligible is what would be discounted; skipped_dealer_set is offers you priced by hand or whose promotion you removed; skipped_pinned is offers whose total you pinned to the cent. Scope: promotions:write.\n\nA promotion whose rate is 0 is refused with 422 nothing_to_apply: a zero discount is how we record a promotion you took OFF an offer by hand, so applying one would mark the whole batch as deliberately un-promoted.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"dry_run\": true\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Apply to open offers",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{api_key}}"
              },
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{$guid}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/promotions/41/apply",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "promotions",
                "41",
                "apply"
              ]
            },
            "description": "Applies the promotion to concept offers that were never sent. Never touches an offer already sent, one you priced by hand, or one whose total you pinned. Idempotent: run it twice and the second run applies to nothing. Scope: promotions:write.\n\nA promotion whose rate is 0 is refused with 422 nothing_to_apply: a zero discount is how we record a promotion you took OFF an offer by hand, so applying one would mark the whole batch as deliberately un-promoted.",
            "body": {
              "mode": "raw",
              "raw": "{}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "End a promotion",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{api_key}}"
              },
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/promotions/41?force=1",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "promotions",
                "41"
              ],
              "query": [
                {
                  "key": "force",
                  "value": "1",
                  "description": "Delete even though offers carry it.",
                  "disabled": true
                }
              ]
            },
            "description": "Deletes the promotion. Offers carrying it keep their discount and their wording — the customer was quoted that price — but what does not survive is knowing which promotion won them: after the delete they look like offers discounted by hand.\n\nSo a promotion that has reached offers is refused once, with 409 promotion_in_use naming the count. Repeat with ?force=1 to go ahead. One that nothing carries deletes straight away.\n\nTo stop a promotion but keep the record, PATCH it with {\"is_active\": false} instead. Scope: promotions:write."
          },
          "response": []
        }
      ]
    }
  ],
  "variable": [
    {
      "key": "base_url",
      "value": "https://api.zinevu.com/api/public/v1",
      "description": "The API root. Change this only if we have told you to."
    },
    {
      "key": "api_key",
      "value": "",
      "description": "Your key from Settings -> Developers. Shown once when minted; we store only a hash, so it cannot be shown again."
    }
  ]
}
