{
  "info": {
    "_postman_id": "b2f7e3a1-9c2d-4f8e-a1b6-shiprexcmsapi01",
    "name": "ShipRex CMS / API (v1)",
    "description": "The canonical API contract for the ShipRex Website CMS & API.\n\nTwo surfaces share this server:\n\n1. **Public API** (`/api/v1/*`) — consumed by the ShipRex marketing website and future integrators. Auth = **API key** sent as `x-api-key: {{api_key}}` (or `Authorization: Bearer {{api_key}}`). Each key has **scopes**: `kb`, `chat`, `contact`, `tickets`. Responses use the envelope `{ ok, data, meta? }` / `{ ok: false, error }`.\n2. **Admin portal** (`/admin/*`) — used by the ShipRex team. Auth = **session cookie** (login with email+password). It is server-rendered HTML; included here only so you can exercise login + a couple of actions with Postman's cookie jar. The admin portal is a separate realm and is NEVER reachable with an API key.\n\nSet variables in the environment `ShipRex_API.postman_environment.json` (base_url, api_key, admin_email, admin_password).\n\nKeep this collection in lockstep with the code: a PRP task that adds/changes an endpoint updates this file in the same commit.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "auth": {
    "type": "apikey",
    "apikey": [
      { "key": "key", "value": "x-api-key", "type": "string" },
      { "key": "value", "value": "{{api_key}}", "type": "string" },
      { "key": "in", "value": "header", "type": "string" }
    ]
  },
  "variable": [
    { "key": "base_url", "value": "http://localhost:3000", "type": "string" },
    { "key": "api_key", "value": "dev-website-key-change-me", "type": "string" },
    { "key": "admin_email", "value": "admin@shiprex.com", "type": "string" },
    { "key": "admin_password", "value": "ChangeMe123!", "type": "string" },
    { "key": "thread_id", "value": "", "type": "string" },
    { "key": "article_slug", "value": "what-is-shiprex", "type": "string" },
    { "key": "ticket_reference", "value": "", "type": "string" },
    { "key": "ticket_email", "value": "client@example.com", "type": "string" }
  ],
  "item": [
    {
      "name": "Meta (no auth)",
      "description": "Health and feature-flag probes. No API key required.",
      "auth": { "type": "noauth" },
      "item": [
        {
          "name": "Health check",
          "request": {
            "method": "GET",
            "header": [],
            "url": { "raw": "{{base_url}}/health", "host": ["{{base_url}}"], "path": ["health"] },
            "description": "Liveness + DB connectivity. 200 = DB up, 503 = DB down."
          },
          "response": [
            {
              "name": "OK",
              "originalRequest": { "method": "GET", "header": [], "url": { "raw": "{{base_url}}/health", "host": ["{{base_url}}"], "path": ["health"] } },
              "status": "OK",
              "code": 200,
              "header": [{ "key": "Content-Type", "value": "application/json" }],
              "body": "{\n  \"ok\": true,\n  \"db\": \"up\",\n  \"uptime\": 12.34\n}"
            }
          ]
        },
        {
          "name": "API status (feature flags)",
          "request": {
            "method": "GET",
            "header": [],
            "url": { "raw": "{{base_url}}/api/v1/status", "host": ["{{base_url}}"], "path": ["api", "v1", "status"] },
            "description": "Service identity + which features are configured (chatbot enabled? Brevo configured?)."
          },
          "response": [
            {
              "name": "OK",
              "originalRequest": { "method": "GET", "header": [], "url": { "raw": "{{base_url}}/api/v1/status", "host": ["{{base_url}}"], "path": ["api", "v1", "status"] } },
              "status": "OK",
              "code": 200,
              "header": [{ "key": "Content-Type", "value": "application/json" }],
              "body": "{\n  \"ok\": true,\n  \"data\": {\n    \"service\": \"shiprex-cms-api\",\n    \"version\": \"v1\",\n    \"features\": { \"knowledgeBase\": true, \"chatbot\": false, \"contact\": false, \"tickets\": true },\n    \"env\": \"production\"\n  }\n}"
            }
          ]
        }
      ]
    },
    {
      "name": "Knowledge Base",
      "description": "Public read-only access to published KB content. Scope: **kb**.\n\nUse cases: the marketing website's Help Center / FAQ pages, and the on-site search box.",
      "item": [
        {
          "name": "List categories",
          "request": {
            "method": "GET",
            "header": [],
            "url": { "raw": "{{base_url}}/api/v1/kb/categories", "host": ["{{base_url}}"], "path": ["api", "v1", "kb", "categories"] },
            "description": "Active KB categories ordered by sort_order."
          },
          "response": [
            {
              "name": "OK",
              "originalRequest": { "method": "GET", "header": [], "url": { "raw": "{{base_url}}/api/v1/kb/categories", "host": ["{{base_url}}"], "path": ["api", "v1", "kb", "categories"] } },
              "status": "OK",
              "code": 200,
              "header": [{ "key": "Content-Type", "value": "application/json" }],
              "body": "{\n  \"ok\": true,\n  \"data\": [\n    { \"id\": 1, \"name\": \"Getting Started\", \"slug\": \"getting-started\", \"description\": \"Introduction to ShipRex\", \"sort_order\": 0, \"is_active\": true }\n  ]\n}"
            }
          ]
        },
        {
          "name": "List published articles",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/kb/articles?search=&category=&page=1&limit=20",
              "host": ["{{base_url}}"],
              "path": ["api", "v1", "kb", "articles"],
              "query": [
                { "key": "search", "value": "", "description": "Optional keyword (title/body/tags)." },
                { "key": "category", "value": "", "description": "Optional category slug filter." },
                { "key": "page", "value": "1" },
                { "key": "limit", "value": "20", "description": "1–50." }
              ]
            },
            "description": "Paginated published articles. `meta` carries pagination."
          },
          "response": [
            {
              "name": "OK",
              "originalRequest": { "method": "GET", "header": [], "url": { "raw": "{{base_url}}/api/v1/kb/articles?page=1&limit=20", "host": ["{{base_url}}"], "path": ["api", "v1", "kb", "articles"], "query": [{ "key": "page", "value": "1" }, { "key": "limit", "value": "20" }] } },
              "status": "OK",
              "code": 200,
              "header": [{ "key": "Content-Type", "value": "application/json" }],
              "body": "{\n  \"ok\": true,\n  \"data\": [\n    { \"id\": 1, \"title\": \"What is ShipRex?\", \"slug\": \"what-is-shiprex\", \"excerpt\": \"A quick introduction to the ShipRex platform.\", \"tags\": \"intro,overview\", \"published_at\": \"2026-06-28T12:00:00.000Z\", \"category_id\": 1, \"category\": { \"id\": 1, \"name\": \"Getting Started\", \"slug\": \"getting-started\" } }\n  ],\n  \"meta\": { \"page\": 1, \"limit\": 20, \"total\": 1, \"pages\": 1 }\n}"
            }
          ]
        },
        {
          "name": "Get article by slug",
          "request": {
            "method": "GET",
            "header": [],
            "url": { "raw": "{{base_url}}/api/v1/kb/articles/{{article_slug}}", "host": ["{{base_url}}"], "path": ["api", "v1", "kb", "articles", "{{article_slug}}"] },
            "description": "Single published article incl. Markdown body. Increments view_count."
          },
          "response": []
        },
        {
          "name": "Missing API key (401)",
          "request": {
            "method": "GET",
            "header": [],
            "auth": { "type": "noauth" },
            "url": { "raw": "{{base_url}}/api/v1/kb/articles", "host": ["{{base_url}}"], "path": ["api", "v1", "kb", "articles"] },
            "description": "Demonstrates the auth failure shape when no key is sent."
          },
          "response": [
            {
              "name": "Unauthorized",
              "originalRequest": { "method": "GET", "header": [], "url": { "raw": "{{base_url}}/api/v1/kb/articles", "host": ["{{base_url}}"], "path": ["api", "v1", "kb", "articles"] } },
              "status": "Unauthorized",
              "code": 401,
              "header": [{ "key": "Content-Type", "value": "application/json" }],
              "body": "{\n  \"ok\": false,\n  \"error\": { \"code\": \"missing_api_key\", \"message\": \"API key is required\" }\n}"
            }
          ]
        }
      ]
    },
    {
      "name": "Chatbot (marketing)",
      "description": "Marketing chatbot on Gemini, grounded in the admin-editable KB. Scope: **chat**. Single JSON per turn (no streaming/socket.io).\n\nFlow: 1) POST /chat/threads (optional `locale`) → returns `thread_id` + a static bilingual greeting; store `thread_id` in localStorage (one thread per visitor). 2) POST /chat/threads/{id}/messages for each question → single JSON reply. 3) GET /chat/threads/{id} to restore.\n\nThe bot identifies as an AI, answers only ShipRex questions from the KB (marketing tone, no technical details), replies in the user's language, caps at 10 questions (then a static CTA), offers a callback on intent and creates a lead via a tool, and falls back gracefully on provider errors (a fallback does NOT consume a question).",
      "item": [
        {
          "name": "Create thread",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "try {",
                  "  const j = pm.response.json();",
                  "  if (j && j.ok && j.data && j.data.thread_id) pm.collectionVariables.set('thread_id', j.data.thread_id);",
                  "} catch (e) {}"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": { "mode": "raw", "raw": "{\n  \"locale\": \"en\"\n}" },
            "url": { "raw": "{{base_url}}/api/v1/chat/threads", "host": ["{{base_url}}"], "path": ["api", "v1", "chat", "threads"] },
            "description": "Opens a thread. Body `locale` is optional (en|ar). Returns `thread_id` + the static bilingual greeting."
          },
          "response": [
            {
              "name": "Created",
              "originalRequest": { "method": "POST", "header": [], "body": { "mode": "raw", "raw": "{ \"locale\": \"en\" }" }, "url": { "raw": "{{base_url}}/api/v1/chat/threads", "host": ["{{base_url}}"], "path": ["api", "v1", "chat", "threads"] } },
              "status": "Created",
              "code": 201,
              "header": [{ "key": "Content-Type", "value": "application/json" }],
              "body": "{\n  \"ok\": true,\n  \"data\": {\n    \"thread_id\": \"cv_562dd3ba6cfa83f1f26a41b4d0e5082e\",\n    \"messages\": [ { \"id\": 1, \"role\": \"assistant\", \"kind\": \"greeting\", \"content\": \"Hi! I'm ShipRex's AI assistant — not a human… / مرحباً! أنا المساعد الذكي لشيب‑رِكس…\" } ]\n  }\n}"
            }
          ]
        },
        {
          "name": "Send message",
          "request": {
            "method": "POST",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": { "mode": "raw", "raw": "{\n  \"message\": \"What does ShipRex do?\"\n}" },
            "url": { "raw": "{{base_url}}/api/v1/chat/threads/{{thread_id}}/messages", "host": ["{{base_url}}"], "path": ["api", "v1", "chat", "threads", "{{thread_id}}", "messages"] },
            "description": "Body `message` (required, 1–8000 chars; server caps at CHAT_MAX_INPUT_CHARS). Single JSON reply. Possible shapes: normal answer, `capped:true` (static CTA after 10), `fallback:true` (provider error — not counted), or a callback-tool turn with `tool` + `lead`."
          },
          "response": [
            {
              "name": "OK (normal answer)",
              "originalRequest": { "method": "POST", "header": [], "body": { "mode": "raw", "raw": "{ \"message\": \"What does ShipRex do?\" }" }, "url": { "raw": "{{base_url}}/api/v1/chat/threads/{{thread_id}}/messages", "host": ["{{base_url}}"], "path": ["api", "v1", "chat", "threads", "{{thread_id}}", "messages"] } },
              "status": "OK",
              "code": 200,
              "header": [{ "key": "Content-Type", "value": "application/json" }],
              "body": "{\n  \"ok\": true,\n  \"data\": {\n    \"thread_id\": \"cv_562dd…\",\n    \"reply\": \"ShipRex helps delivery companies onboard merchants, capture orders, price by zone, assign drivers, collect COD, and settle payouts…\",\n    \"messageId\": 3,\n    \"question_count\": 1,\n    \"remaining\": 9,\n    \"capped\": false\n  }\n}"
            },
            {
              "name": "OK (cap reached → static CTA)",
              "originalRequest": { "method": "POST", "header": [], "body": { "mode": "raw", "raw": "{ \"message\": \"one more\" }" }, "url": { "raw": "{{base_url}}/api/v1/chat/threads/{{thread_id}}/messages", "host": ["{{base_url}}"], "path": ["api", "v1", "chat", "threads", "{{thread_id}}", "messages"] } },
              "status": "OK",
              "code": 200,
              "header": [{ "key": "Content-Type", "value": "application/json" }],
              "body": "{\n  \"ok\": true,\n  \"data\": { \"thread_id\": \"cv_562dd…\", \"reply\": \"Thanks for the great chat! You've reached the limit… try the demo or leave your details. / شكراً…\", \"capped\": true, \"question_count\": 10, \"remaining\": 0 }\n}"
            },
            {
              "name": "OK (callback tool → lead created)",
              "originalRequest": { "method": "POST", "header": [], "body": { "mode": "raw", "raw": "{ \"message\": \"Yes please call me, I'm Ahmed, +20100..., ahmed@example.com\" }" }, "url": { "raw": "{{base_url}}/api/v1/chat/threads/{{thread_id}}/messages", "host": ["{{base_url}}"], "path": ["api", "v1", "chat", "threads", "{{thread_id}}", "messages"] } },
              "status": "OK",
              "code": 200,
              "header": [{ "key": "Content-Type", "value": "application/json" }],
              "body": "{\n  \"ok\": true,\n  \"data\": { \"thread_id\": \"cv_562dd…\", \"reply\": \"You're all set — a ShipRex Account Manager will reach out shortly.\", \"tool\": \"request_callback\", \"lead\": { \"reference\": \"#42\" }, \"question_count\": 1, \"remaining\": 9 }\n}"
            }
          ]
        },
        {
          "name": "Get thread (restore)",
          "request": {
            "method": "GET",
            "header": [],
            "url": { "raw": "{{base_url}}/api/v1/chat/threads/{{thread_id}}", "host": ["{{base_url}}"], "path": ["api", "v1", "chat", "threads", "{{thread_id}}"] },
            "description": "Restores the conversation (greeting + public text/cap/fallback messages). Used to rehydrate the widget from localStorage. The system prompt and KB are never returned. 404 if the thread id is unknown."
          },
          "response": [
            {
              "name": "OK",
              "originalRequest": { "method": "GET", "header": [], "url": { "raw": "{{base_url}}/api/v1/chat/threads/{{thread_id}}", "host": ["{{base_url}}"], "path": ["api", "v1", "chat", "threads", "{{thread_id}}"] } },
              "status": "OK",
              "code": 200,
              "header": [{ "key": "Content-Type", "value": "application/json" }],
              "body": "{\n  \"ok\": true,\n  \"data\": {\n    \"thread_id\": \"cv_562dd…\",\n    \"status\": \"active\",\n    \"question_count\": 1,\n    \"remaining\": 9,\n    \"capped\": false,\n    \"messages\": [ { \"id\": 1, \"role\": \"assistant\", \"kind\": \"greeting\", \"content\": \"Hi! …\" }, { \"id\": 2, \"role\": \"user\", \"kind\": \"text\", \"content\": \"What does ShipRex do?\" }, { \"id\": 3, \"role\": \"assistant\", \"kind\": \"text\", \"content\": \"ShipRex helps…\" } ]\n  }\n}"
            }
          ]
        }
      ]
    },
    {
      "name": "Contact / Email",
      "description": "Capture a \"call me back\" / contact lead. Scope: **contact**. Rate-limited (strict).\n\nUse case: the website contact / request-a-callback form. The lead is stored, synced to Brevo, acknowledged to the visitor, and notified to sales — all best-effort (email/sync failures never block capture). Include an empty `website` honeypot field from the form for basic bot defense.",
      "item": [
        {
          "name": "Submit contact request",
          "request": {
            "method": "POST",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Jane Doe\",\n  \"email\": \"jane@example.com\",\n  \"phone\": \"+201234567890\",\n  \"company\": \"Acme Co\",\n  \"reason\": \"callback\",\n  \"message\": \"Please call me about shipping rates.\",\n  \"preferred_time\": \"Tomorrow morning\",\n  \"source\": \"pricing-page\",\n  \"website\": \"\"\n}"
            },
            "url": { "raw": "{{base_url}}/api/v1/contact", "host": ["{{base_url}}"], "path": ["api", "v1", "contact"] },
            "description": "Body: `name`, `email` (required); `phone`, `company`, `reason` (callback|quote|general|partnership), `message`, `preferred_time`, `source` (optional); `website` must be empty (honeypot)."
          },
          "response": [
            {
              "name": "Created",
              "originalRequest": { "method": "POST", "header": [], "body": { "mode": "raw", "raw": "{ \"name\": \"Jane Doe\", \"email\": \"jane@example.com\", \"reason\": \"callback\" }" }, "url": { "raw": "{{base_url}}/api/v1/contact", "host": ["{{base_url}}"], "path": ["api", "v1", "contact"] } },
              "status": "Created",
              "code": 201,
              "header": [{ "key": "Content-Type", "value": "application/json" }],
              "body": "{\n  \"ok\": true,\n  \"data\": { \"id\": 1, \"status\": \"new\", \"message\": \"Thanks! We've received your request and will be in touch shortly.\" }\n}"
            },
            {
              "name": "Validation error (422)",
              "originalRequest": { "method": "POST", "header": [], "body": { "mode": "raw", "raw": "{ \"name\": \"X\", \"email\": \"not-an-email\" }" }, "url": { "raw": "{{base_url}}/api/v1/contact", "host": ["{{base_url}}"], "path": ["api", "v1", "contact"] } },
              "status": "Unprocessable Entity",
              "code": 422,
              "header": [{ "key": "Content-Type", "value": "application/json" }],
              "body": "{\n  \"ok\": false,\n  \"error\": {\n    \"code\": \"validation_error\",\n    \"message\": \"Validation failed\",\n    \"details\": [\n      { \"field\": \"name\", \"message\": \"String must contain at least 2 character(s)\" },\n      { \"field\": \"email\", \"message\": \"Invalid email\" }\n    ]\n  }\n}"
            }
          ]
        }
      ]
    },
    {
      "name": "Support Tickets",
      "description": "Client support tickets (bug reports / requests). Scope: **tickets**. Writes are rate-limited (strict).\n\nUse cases: the website's \"open a ticket\" form, a \"check ticket status\" lookup (by reference + email), and a customer reply. Internal admin notes are never returned here.",
      "item": [
        {
          "name": "Create ticket",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "try {",
                  "  const json = pm.response.json();",
                  "  if (json && json.ok && json.data && json.data.reference) {",
                  "    pm.collectionVariables.set('ticket_reference', json.data.reference);",
                  "  }",
                  "} catch (e) {}"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"requester_name\": \"Bob Client\",\n  \"requester_email\": \"{{ticket_email}}\",\n  \"subject\": \"App crashes on login\",\n  \"description\": \"The app crashes when I sign in on Android.\",\n  \"category\": \"bug\",\n  \"priority\": \"high\",\n  \"client_ref\": \"\",\n  \"meta\": { \"app_version\": \"1.2.3\", \"platform\": \"android\" }\n}"
            },
            "url": { "raw": "{{base_url}}/api/v1/tickets", "host": ["{{base_url}}"], "path": ["api", "v1", "tickets"] },
            "description": "Body: `requester_name`, `requester_email`, `subject`, `description` (required); `category` (bug|billing|shipment|account|feature|other), `priority` (low|normal|high|urgent), `client_ref`, `meta` (optional)."
          },
          "response": [
            {
              "name": "Created",
              "originalRequest": { "method": "POST", "header": [], "body": { "mode": "raw", "raw": "{ \"requester_name\": \"Bob\", \"requester_email\": \"client@example.com\", \"subject\": \"Hi\", \"description\": \"Testing\" }" }, "url": { "raw": "{{base_url}}/api/v1/tickets", "host": ["{{base_url}}"], "path": ["api", "v1", "tickets"] } },
              "status": "Created",
              "code": 201,
              "header": [{ "key": "Content-Type", "value": "application/json" }],
              "body": "{\n  \"ok\": true,\n  \"data\": { \"reference\": \"SRX-F0FB67\", \"status\": \"open\", \"message\": \"Your support ticket has been created.\" }\n}"
            }
          ]
        },
        {
          "name": "Lookup ticket status",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/api/v1/tickets/lookup?reference={{ticket_reference}}&email={{ticket_email}}",
              "host": ["{{base_url}}"],
              "path": ["api", "v1", "tickets", "lookup"],
              "query": [
                { "key": "reference", "value": "{{ticket_reference}}" },
                { "key": "email", "value": "{{ticket_email}}", "description": "Must match the requester email on the ticket." }
              ]
            },
            "description": "Returns ticket status + public replies (internal notes excluded). 404 if reference+email don't match."
          },
          "response": []
        },
        {
          "name": "Customer reply to ticket",
          "request": {
            "method": "POST",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"reference\": \"{{ticket_reference}}\",\n  \"email\": \"{{ticket_email}}\",\n  \"body\": \"Here is the extra detail you asked for.\"\n}"
            },
            "url": { "raw": "{{base_url}}/api/v1/tickets/reply", "host": ["{{base_url}}"], "path": ["api", "v1", "tickets", "reply"] },
            "description": "Adds a requester reply. If the ticket was waiting_customer, it flips back to open."
          },
          "response": []
        }
      ]
    },
    {
      "name": "Admin Portal (session — browser-first)",
      "description": "The admin portal is server-rendered HTML secured by a **session cookie**, not an API key. It is intended for browsers; these requests exist so you can drive login + a couple of actions in Postman using its cookie jar.\n\nFlow: run **Login** (form-urlencoded). Postman stores the `shiprex.sid` cookie for `{{base_url}}` automatically; subsequent admin requests reuse it. Forms use POST with `?_method=PUT|DELETE` for updates/deletes.\n\n⚠️ Never send an API key here, and never expose these routes through `/api`.",
      "auth": { "type": "noauth" },
      "item": [
        {
          "name": "Login",
          "request": {
            "method": "POST",
            "header": [{ "key": "Content-Type", "value": "application/x-www-form-urlencoded" }],
            "body": {
              "mode": "urlencoded",
              "urlencoded": [
                { "key": "email", "value": "{{admin_email}}", "type": "text" },
                { "key": "password", "value": "{{admin_password}}", "type": "text" }
              ]
            },
            "url": { "raw": "{{base_url}}/admin/login", "host": ["{{base_url}}"], "path": ["admin", "login"] },
            "description": "On success → 302 redirect to /admin and a `shiprex.sid` session cookie. On failure → 401 with the login page."
          },
          "response": []
        },
        {
          "name": "Dashboard (requires session)",
          "request": {
            "method": "GET",
            "header": [],
            "url": { "raw": "{{base_url}}/admin", "host": ["{{base_url}}"], "path": ["admin"] },
            "description": "Returns the dashboard HTML if logged in, else 302 → /admin/login."
          },
          "response": []
        },
        {
          "name": "Create API client (key) for a website",
          "request": {
            "method": "POST",
            "header": [{ "key": "Content-Type", "value": "application/x-www-form-urlencoded" }],
            "body": {
              "mode": "urlencoded",
              "urlencoded": [
                { "key": "name", "value": "ShipRex Marketing Website", "type": "text" },
                { "key": "scopes", "value": "kb", "type": "text" },
                { "key": "scopes", "value": "chat", "type": "text" },
                { "key": "scopes", "value": "contact", "type": "text" },
                { "key": "scopes", "value": "tickets", "type": "text" }
              ]
            },
            "url": { "raw": "{{base_url}}/admin/settings/api-clients", "host": ["{{base_url}}"], "path": ["admin", "settings", "api-clients"] },
            "description": "Generates a new API key (shown once on the resulting page). This is how integrators are provisioned — copy the key into the website's server-side env."
          },
          "response": []
        },
        {
          "name": "Logout",
          "request": {
            "method": "POST",
            "header": [],
            "url": { "raw": "{{base_url}}/admin/logout", "host": ["{{base_url}}"], "path": ["admin", "logout"] },
            "description": "Destroys the session and clears the cookie."
          },
          "response": []
        }
      ]
    }
  ]
}
