{
  "info": {
    "_postman_id": "8795e248-0c84-5084-8042-79856084900b",
    "name": "ConvoOK API",
    "description": "# ConvoOK API v1 (1.43.0)\n\nEvery request a workspace **API key** can make, ready to send.\n\n## Start in a minute\n\n1. In ConvoOK: **Developers -> API keys -> New API key**. Pick a ready-made key\n   (for example *Chat with my agents* or *Knowledge for my own agent*) and copy it.\n   It is shown once.\n2. In Postman: import this collection **and** `convook-api.postman_environment.json`,\n   choose the *ConvoOK API* environment, and paste the key into `api_key`.\n3. Send **Agents -> List the agents this key can use**. Its answer fills `agent_id`,\n   so **Chat with an agent** works next with nothing to copy. Creating or listing\n   sources and documents fills `source_id` and `document_id` the same way.\n\n## Good to know\n\n- The key is sent as `X-API-Key`. It names its own workspace; there is no workspace header.\n- Each request says the **key permission** it needs. A key without it answers `403`.\n- Creates and uploads send a fresh `Idempotency-Key`; send the same one again to retry safely.\n- Times in responses are UTC (`YYYY-MM-DD HH:MM:SS`) with no zone marker.\n- Built from the same OpenAPI file as the Developer Guide. Download it again after an\n  update: it is rebuilt with every change to the API.",
    "version": "1.43.0",
    "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"
      }
    ]
  },
  "item": [
    {
      "name": "Agents - chat with your agents",
      "item": [
        {
          "id": "0bf8a668-3d60-580f-9044-340179d62469",
          "name": "The workspace's agents, and which of them can answer",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/agents",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "agents"
              ]
            },
            "description": "The workspace's agents, and which of them can answer.\n\nAuthenticated by an **API key**, not a user token: send it as\n`X-API-Key: vok_...` or `Authorization: Bearer vok_...`. The key names\nits own workspace, so there is no workspace header or switch.\n\n**Key permission:** `agents.view`\n\nOpenAPI operation: `machineListAgents`"
          },
          "response": [],
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// Remember what this answered, so the next request can use it.",
                  "if (pm.response.code < 300) {",
                  "    const body = pm.response.json();",
                  "    const data = body && body.data !== undefined ? body.data : body;",
                  "    const agent_id = data.items && data.items[0] && data.items[0].id;",
                  "    if (agent_id) { pm.collectionVariables.set('agent_id', agent_id); }",
                  "}"
                ]
              }
            }
          ]
        },
        {
          "id": "b405adf4-628c-5630-8cd0-376f0d56a25a",
          "name": "Ask an agent, from your own backend",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/agents/:publicId/chat",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "agents",
                ":publicId",
                "chat"
              ],
              "variable": [
                {
                  "key": "publicId",
                  "value": "{{agent_id}}",
                  "description": ""
                }
              ]
            },
            "description": "Ask an agent, from your own backend. Persists a conversation.\n\nRuns the agent's LIVE version and writes a real conversation — this is\na customer interaction, and it appears in the Inbox.\n\nSend the returned `conversation_id` back to continue the same thread.\nThere is deliberately **no `history` field**: the transcript is read\nfrom the database, so a caller cannot hand the model assistant turns it\nnever said.\n\nThe retrieved passage TEXT is never returned. Citations carry titles\nand scores; `/knowledge/search` is the endpoint for passage text, and\nit is gated on `knowledge.inspect`.\n\nRead `ok`, not the status code: a 200 with `ok: false` means the turn\nran and produced no answer, and `message` says why.\n\n**While a person holds the conversation** (someone took it over in the\nInbox), the AI does not answer: the message is passed to the person,\nthe response has `held_by_person: true` and an empty `answer`, and no\nmodel is called or metered. The person's reply arrives as a\n`message.created` webhook with `author: person`; fetch it from\n`GET /v1/conversations/{id}/messages`.\n\n**Forms (spec 23)** run here exactly as in the widget: the agent may\nask for details, and a completed form sends `form.completed`; read the\nvalues from `GET /v1/conversations/{id}/details`.\n\n**Key permission:** `agents.test`\n\nOpenAPI operation: `machineChat`",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"message\": \"Hello! What are your opening hours?\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [],
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// Remember what this answered, so the next request can use it.",
                  "if (pm.response.code < 300) {",
                  "    const body = pm.response.json();",
                  "    const data = body && body.data !== undefined ? body.data : body;",
                  "    const conversation_id = data.conversation_id;",
                  "    if (conversation_id) { pm.collectionVariables.set('conversation_id', conversation_id); }",
                  "    const message_id = data.message_id;",
                  "    if (message_id) { pm.collectionVariables.set('message_id', message_id); }",
                  "}"
                ]
              }
            }
          ]
        }
      ]
    },
    {
      "name": "Conversations - read what was said",
      "item": [
        {
          "id": "4ee29af9-c567-5d17-8e4a-7e47b03ca339",
          "name": "A conversation's state — what a webhook's `conversation_id` points at",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/conversations/:publicId",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "conversations",
                ":publicId"
              ],
              "variable": [
                {
                  "key": "publicId",
                  "value": "{{conversation_id}}",
                  "description": ""
                }
              ]
            },
            "description": "A conversation's state — what a webhook's `conversation_id` points at.\n\nAny conversation in the key's workspace except playground tests.\nAnother workspace's id is 404, the same as one that does not exist.\n\n**Key permission:** `conversations.view-all`\n\nOpenAPI operation: `machineConversation`"
          },
          "response": []
        },
        {
          "id": "39896727-bd20-57ea-81bb-7ed8d7cdb626",
          "name": "Messages after a message id, oldest first",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/conversations/:publicId/messages",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "conversations",
                ":publicId",
                "messages"
              ],
              "query": [
                {
                  "key": "after",
                  "value": "string",
                  "description": "",
                  "disabled": true
                },
                {
                  "key": "limit",
                  "value": "50",
                  "description": "",
                  "disabled": true
                }
              ],
              "variable": [
                {
                  "key": "publicId",
                  "value": "{{conversation_id}}",
                  "description": ""
                }
              ]
            },
            "description": "Messages after a message id, oldest first.\n\nNeeds BOTH `conversations.view-all` and `conversations.read-body`.\n\nForward-only: keep the last `id` you showed and send it as `after`.\n`next_after` is what to send next time, unchanged when nothing new\narrived. An `after` from another conversation is a 422, not a silent\nrestart from the top.\n\nA person on the team is `author: person` — never a name. A redacted\nmessage has `redacted: true` and `text: null`.\n\n**Key permission:** `conversations.view-all`, `conversations.read-body`\n\nOpenAPI operation: `machineConversationMessages`"
          },
          "response": []
        },
        {
          "id": "d49c3f71-86ce-5326-bc24-f2b56869662b",
          "name": "The summary and labels an analysis gave the conversation",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/conversations/:publicId/insights",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "conversations",
                ":publicId",
                "insights"
              ],
              "variable": [
                {
                  "key": "publicId",
                  "value": "{{conversation_id}}",
                  "description": ""
                }
              ]
            },
            "description": "The summary and labels an analysis gave the conversation (spec 24).\n\nWhat a `conversation.analysed` webhook points at. Needs BOTH\n`conversations.view-all` and `conversations.read-body`: a summary restates\nthe customer. `value` is typed (a number is a number); `values` is the same\nkeyed by label.\n\n**Key permission:** `conversations.view-all`, `conversations.read-body`\n\nOpenAPI operation: `machineConversationInsights`"
          },
          "response": []
        },
        {
          "id": "5ab42a6c-ff94-55d9-934e-af3e4aa68acf",
          "name": "What the conversation's forms collected",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/conversations/:publicId/details",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "conversations",
                ":publicId",
                "details"
              ],
              "variable": [
                {
                  "key": "publicId",
                  "value": "{{conversation_id}}",
                  "description": ""
                }
              ]
            },
            "description": "What the conversation's forms collected (spec 22).\n\nWhat a `form.completed` webhook points at. Needs BOTH\n`conversations.view-all` and `conversations.read-body`: a customer's\ndetails are personal, like their words.\n\n`value` is typed -- a number is a number, yes/no a boolean, a date\n`YYYY-MM-DD`, a phone `+63...` -- and `display` is how the Inbox shows\nit. `values` is the same keyed by field. `source` says who gave a value\n(`person` never carries a name). A group with `status: loose` holds\nvalues set outside any form.\n\n**Key permission:** `conversations.view-all`, `conversations.read-body`\n\nOpenAPI operation: `machineConversationDetails`"
          },
          "response": []
        },
        {
          "id": "9531b820-7fd3-515e-be29-1424f9ab71bd",
          "name": "Relay your user's thumbs-up or thumbs-down on an AI answer",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/messages/:publicId/feedback",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "messages",
                ":publicId",
                "feedback"
              ],
              "variable": [
                {
                  "key": "publicId",
                  "value": "{{message_id}}",
                  "description": ""
                }
              ]
            },
            "description": "Relay your user's thumbs-up or thumbs-down on an AI answer.\n\nOnly an answer in an `api` conversation of this key's workspace --\na widget visitor's conversation is 404 here. One rating per answer:\nsending again changes it. A thumbs-down may file the answer for the\nworkspace's review (spec 19). Spends nothing.\n\n**Key permission:** `agents.test`\n\nOpenAPI operation: `machineMessageFeedback`",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"conversation_id\": \"{{conversation_id}}\",\n  \"rating\": 1\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "Tickets",
      "item": [
        {
          "id": "f2ef1132-ca24-5543-9621-bc6eda2f4521",
          "name": "A conversation by its ticket number (spec 62) — what a customer reads out",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/tickets/:ticket",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "tickets",
                ":ticket"
              ],
              "variable": [
                {
                  "key": "ticket",
                  "value": "{{ticket}}",
                  "description": ""
                }
              ]
            },
            "description": "A conversation by its ticket number (spec 62) — what a customer reads out.\n\n`T-000123`, `t-123`, `CS-123`: the number with any prefix. The same\nanswer as `GET /v1/conversations/{id}`. Another workspace's ticket, or\none that does not exist, is 404.\n\n**Key permission:** `conversations.view-all`\n\nOpenAPI operation: `machineTicket`"
          },
          "response": []
        }
      ]
    },
    {
      "name": "Records",
      "item": [
        {
          "id": "81a63e69-4dd3-5d58-a997-0848081440c2",
          "name": "CRM records, newest first -- to sync your own system from",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/records",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "records"
              ],
              "query": [
                {
                  "key": "sheet",
                  "value": "string",
                  "description": "The sheet's key, as in the CRM designer.",
                  "disabled": true
                },
                {
                  "key": "status",
                  "value": "open",
                  "description": "",
                  "disabled": true
                },
                {
                  "key": "disposition",
                  "value": "string",
                  "description": "",
                  "disabled": true
                },
                {
                  "key": "updated_after",
                  "value": "2026-10-02 09:00:00",
                  "description": "",
                  "disabled": true
                },
                {
                  "key": "page",
                  "value": "1",
                  "description": "",
                  "disabled": true
                },
                {
                  "key": "per_page",
                  "value": "1",
                  "description": "",
                  "disabled": true
                }
              ]
            },
            "description": "CRM records, newest first -- to sync your own system from (spec 45, D3).\n\nEveryone's records: a key is assigned nothing, so the scope is\n`records.view-all`. Ask with `updated_after` set to when you last asked\nto fetch only what changed. A list has no answers; read one record for\nthose.\n\n**Key permission:** `records.view-all`\n\nOpenAPI operation: `machineListRecords`"
          },
          "response": [],
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// Remember what this answered, so the next request can use it.",
                  "if (pm.response.code < 300) {",
                  "    const body = pm.response.json();",
                  "    const data = body && body.data !== undefined ? body.data : body;",
                  "    const record_id = data.items && data.items[0] && data.items[0].id;",
                  "    if (record_id) { pm.collectionVariables.set('record_id', record_id); }",
                  "}"
                ]
              }
            }
          ]
        },
        {
          "id": "b4c1733f-80ac-5868-9b9c-d2ee7efafed4",
          "name": "One CRM record with its answers -- what record.submitted points at",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/records/:publicId",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "records",
                ":publicId"
              ],
              "variable": [
                {
                  "key": "publicId",
                  "value": "{{record_id}}",
                  "description": ""
                }
              ]
            },
            "description": "One CRM record with its answers -- what record.submitted points at (spec 45, D3).\n\n`values` is keyed by field; `fields[]` carries each field's label, type,\ntyped `value` and `display` (as the console shows it). The answers are\nyour customer's details: keep the key on your server.\n\n**Key permission:** `records.view-all`\n\nOpenAPI operation: `machineShowRecord`"
          },
          "response": []
        }
      ]
    },
    {
      "name": "Identity",
      "item": [
        {
          "id": "63281fd8-a6c2-5967-a68d-2fc27cd01ea5",
          "name": "Who tapped this link in the chat?",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/identity/check-link",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "identity",
                "check-link"
              ]
            },
            "description": "Who tapped this link in the chat?\n\nA customer who is signed in can be given a link carrying a\n`{{identity.token}}` -- a token that is valid for 15 minutes and\nreadable only by ConvoOK. Your own page swaps it here for the account\nit belongs to, authenticated by your API key, so a token copied out of\na browser is worth nothing on its own.\n\nIt is refused when the token was not made by us, has expired, or the\ncustomer has signed out since.\n\n**Key permission:** `conversations.view-all`\n\nOpenAPI operation: `checkIdentityLink`",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"token\": \"string\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "id": "1d08662e-2176-54b3-8dc3-4735b28f781c",
          "name": "A one-time link that brings a logged-in customer into the chat already signed in",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/identity/start-link",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "identity",
                "start-link"
              ]
            },
            "description": "A one-time link that brings a logged-in customer into the chat already signed in.\n\nCall this **from your server**, while it renders a page for a customer\nwho is logged in on your website — never from a browser, where the key\nwould be on show. Put the `token` in whatever carries them into a chat:\n\n| Where | How |\n| --- | --- |\n| Facebook Messenger | `https://m.me/<your page>?ref=<token>` |\n| Telegram | `https://t.me/<your bot>?start=<token>` |\n| Your website chat | a `data-convook-identity=\"<token>\"` attribute on the widget's own script tag |\n| Your hosted chat page | `<hosted page link>?signed_in=<token>` |\n\nThe first conversation to present it is signed in as `account_id`, with\nthe name and fields you gave, for `hours` (24 by default) — exactly as\nif your own system had confirmed a code in the chat. It is then spent:\na forwarded link, a bookmark or a screenshot signs nobody else in, and\nit expires after 15 minutes whether or not it was used. A link that does\nnot work is ignored silently; the customer simply stays a guest.\n\nInstagram is not in the table: Instagram accounts connected with\nInstagram Login have no link that starts a chat with a parameter.\nSign Instagram customers in with a code, in an automation.\n\nOnly its SHA-256 is stored. `contacts.sign-in` is a dangerous scope —\nwhoever holds it can say who a chat is — so a key made for this should\nhold nothing else.\n\n**Key permission:** `contacts.sign-in`\n\nOpenAPI operation: `startIdentityLink`",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"account_id\": \"M-1001\",\n  \"name\": \"Maria Santos\",\n  \"fields\": {\n    \"plan\": \"gold\"\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "Knowledge - search, read and keep in sync",
      "item": [
        {
          "id": "40c8c1b1-c1b9-5968-be23-0d4bcc1e9d62",
          "name": "The passages that best answer a query, with everything needed to cite them",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/knowledge/retrieve",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "knowledge",
                "retrieve"
              ]
            },
            "description": "The passages that best answer a query, with everything needed to cite them (spec 33).\n\nFor the customer's own agents and software: the same knowledge base the\nworkspace's ConvoOK agents answer from. Needs `knowledge.retrieve` — the\npassage text is behind that scope and no other.\n\n**Spends one of the plan's `knowledge_retrievals`** per call that\nsearched, whatever it found. Nothing is spent when the request is\nrefused or when search is unavailable.\n\n`tags` is a name to a value or a list of values, ANDed, matched against\neach passage's effective tags (its source's, with its document's own\nmerged over them). An empty value is refused: it would match every\nclient. A key serving several clients must send the client's tags.\n\nThe query is kept in the workspace's question log (30 days).\n\n**Key permission:** `knowledge.retrieve`\n\nOpenAPI operation: `machineKnowledgeRetrieve`",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"query\": \"What are your opening hours?\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "id": "dcff6336-baf9-5b4d-86eb-d5decec8415a",
          "name": "The workspace's knowledge sources, oldest first, by cursor",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/knowledge/sources",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "knowledge",
                "sources"
              ],
              "query": [
                {
                  "key": "after",
                  "value": "string",
                  "description": "",
                  "disabled": true
                },
                {
                  "key": "limit",
                  "value": "50",
                  "description": "",
                  "disabled": true
                }
              ]
            },
            "description": "The workspace's knowledge sources, oldest first, by cursor (spec 33).\n\nNever a source's settings (`config`) — a crawl address can carry\ncredentials. Send the last `id` you handled as `after`; `next_after` is\nunchanged when nothing new arrived. A deleted source still works as a\ncursor.\n\n**Key permission:** `knowledge.view`\n\nOpenAPI operation: `machineKnowledgeSources`"
          },
          "response": [],
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// Remember what this answered, so the next request can use it.",
                  "if (pm.response.code < 300) {",
                  "    const body = pm.response.json();",
                  "    const data = body && body.data !== undefined ? body.data : body;",
                  "    const source_id = data.items && data.items[0] && data.items[0].id;",
                  "    if (source_id) { pm.collectionVariables.set('source_id', source_id); }",
                  "}"
                ]
              }
            }
          ]
        },
        {
          "id": "b007b156-f671-5854-867d-772cf54f3be6",
          "name": "Create a knowledge source",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{$guid}}",
                "description": "A new one for every attempt; send the SAME one to retry safely."
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/knowledge/sources",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "knowledge",
                "sources"
              ]
            },
            "description": "Create a knowledge source (spec 33, KA2).\n\n`type` is `text` or `file` -- the two the product processes. Tags need\nthe plan's \"tag knowledge by client\". Audited with the key.\n\n**Key permission:** `knowledge.create`\n\nOpenAPI operation: `machineKnowledgeCreateSource`",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Product FAQ\",\n  \"type\": \"text\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [],
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// Remember what this answered, so the next request can use it.",
                  "if (pm.response.code < 300) {",
                  "    const body = pm.response.json();",
                  "    const data = body && body.data !== undefined ? body.data : body;",
                  "    const source_id = data.id;",
                  "    if (source_id) { pm.collectionVariables.set('source_id', source_id); }",
                  "}"
                ]
              }
            }
          ]
        },
        {
          "id": "9c46fb8d-71c3-5627-a57d-8da8c09e05e9",
          "name": "One knowledge source",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/knowledge/sources/:publicId",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "knowledge",
                "sources",
                ":publicId"
              ],
              "variable": [
                {
                  "key": "publicId",
                  "value": "{{source_id}}",
                  "description": ""
                }
              ]
            },
            "description": "One knowledge source.\n\n**Key permission:** `knowledge.view`\n\nOpenAPI operation: `machineKnowledgeSource`"
          },
          "response": []
        },
        {
          "id": "0cb97dd1-8e53-5546-a47b-a5db184783cf",
          "name": "Rename a source or change its tags",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/knowledge/sources/:publicId",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "knowledge",
                "sources",
                ":publicId"
              ],
              "variable": [
                {
                  "key": "publicId",
                  "value": "{{source_id}}",
                  "description": ""
                }
              ]
            },
            "description": "Rename a source or change its tags.\n\n`tags` replaces the source's tags (`null` or `{}` clears them; an empty\nvalue removes that one tag). Every passage is re-tagged; nothing is\nre-embedded. If the search index cannot be updated, nothing changes (503).\n\n**Key permission:** `knowledge.update`\n\nOpenAPI operation: `machineKnowledgeUpdateSource`",
            "body": {
              "mode": "raw",
              "raw": "{}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "id": "761d46cc-730d-540b-bd78-b28fd1b62013",
          "name": "Delete a source with its documents, passages, vectors and stored files",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/knowledge/sources/:publicId",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "knowledge",
                "sources",
                ":publicId"
              ],
              "variable": [
                {
                  "key": "publicId",
                  "value": "{{source_id}}",
                  "description": ""
                }
              ]
            },
            "description": "Delete a source with its documents, passages, vectors and stored files.\n\nSends one `knowledge.source.deleted` webhook, not one per document. A repeat is 404.\n\n**Key permission:** `knowledge.delete`\n\nOpenAPI operation: `machineKnowledgeDeleteSource`"
          },
          "response": []
        },
        {
          "id": "17f1c65d-6c99-54ba-91e7-af2c20026d9e",
          "name": "A source's documents, oldest first, by cursor",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/knowledge/sources/:publicId/documents",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "knowledge",
                "sources",
                ":publicId",
                "documents"
              ],
              "query": [
                {
                  "key": "after",
                  "value": "string",
                  "description": "",
                  "disabled": true
                },
                {
                  "key": "limit",
                  "value": "50",
                  "description": "",
                  "disabled": true
                },
                {
                  "key": "status",
                  "value": "indexed",
                  "description": "",
                  "disabled": true
                },
                {
                  "key": "updated_since",
                  "value": "2026-09-17 00:00:00",
                  "description": "",
                  "disabled": true
                }
              ],
              "variable": [
                {
                  "key": "publicId",
                  "value": "{{source_id}}",
                  "description": ""
                }
              ]
            },
            "description": "A source's documents, oldest first, by cursor.\n\n`updated_since` (UTC, `YYYY-MM-DD HH:MM:SS`) returns what changed, for a\ncustomer keeping a copy. Deleted documents are not listed. Never the\nstorage path, the content hash or `last_error`.\n\n**Key permission:** `knowledge.view`\n\nOpenAPI operation: `machineKnowledgeDocuments`"
          },
          "response": [],
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// Remember what this answered, so the next request can use it.",
                  "if (pm.response.code < 300) {",
                  "    const body = pm.response.json();",
                  "    const data = body && body.data !== undefined ? body.data : body;",
                  "    const document_id = data.items && data.items[0] && data.items[0].id;",
                  "    if (document_id) { pm.collectionVariables.set('document_id', document_id); }",
                  "}"
                ]
              }
            }
          ]
        },
        {
          "id": "00286c74-8ad6-5762-a0b9-54c459957b33",
          "name": "One knowledge document",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/knowledge/documents/:publicId",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "knowledge",
                "documents",
                ":publicId"
              ],
              "variable": [
                {
                  "key": "publicId",
                  "value": "{{document_id}}",
                  "description": ""
                }
              ]
            },
            "description": "One knowledge document.\n\n**Key permission:** `knowledge.view`\n\nOpenAPI operation: `machineKnowledgeDocument`"
          },
          "response": []
        },
        {
          "id": "7a645495-c35c-541e-a74e-b56429466e1b",
          "name": "Set a document's own tags",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/knowledge/documents/:publicId",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "knowledge",
                "documents",
                ":publicId"
              ],
              "variable": [
                {
                  "key": "publicId",
                  "value": "{{document_id}}",
                  "description": ""
                }
              ]
            },
            "description": "Set a document's own tags.\n\nMerged over its source's tags on every passage. `tags` is required; `null` clears them.\n\n**Key permission:** `knowledge.update`\n\nOpenAPI operation: `machineKnowledgeTagDocument`",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"tags\": {}\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "id": "821de212-97d9-529a-be33-012f21df0575",
          "name": "Delete a document with its passages, vectors and stored file",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/knowledge/documents/:publicId",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "knowledge",
                "documents",
                ":publicId"
              ],
              "variable": [
                {
                  "key": "publicId",
                  "value": "{{document_id}}",
                  "description": ""
                }
              ]
            },
            "description": "Delete a document with its passages, vectors and stored file.\n\nSends `knowledge.document.deleted`. A repeat is 404.\n\n**Key permission:** `knowledge.delete`\n\nOpenAPI operation: `machineKnowledgeDeleteDocument`"
          },
          "response": []
        },
        {
          "id": "6a54b336-7877-53b2-a779-4cb45fc1d4f3",
          "name": "A document's indexed passages, in order",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/knowledge/documents/:publicId/chunks",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "knowledge",
                "documents",
                ":publicId",
                "chunks"
              ],
              "query": [
                {
                  "key": "from",
                  "value": "0",
                  "description": "",
                  "disabled": true
                },
                {
                  "key": "limit",
                  "value": "50",
                  "description": "",
                  "disabled": true
                }
              ],
              "variable": [
                {
                  "key": "publicId",
                  "value": "{{document_id}}",
                  "description": ""
                }
              ]
            },
            "description": "A document's indexed passages, in order.\n\nThe text around a retrieved passage (ask `from` its `chunk_index`\nminus one), or a copy for your own index. Needs `knowledge.retrieve`.\nNot a search: spends no retrievals.\n\n**Key permission:** `knowledge.retrieve`\n\nOpenAPI operation: `machineKnowledgeChunks`"
          },
          "response": []
        },
        {
          "id": "2e337509-c04c-5187-9622-13c3ae8fa738",
          "name": "Add text to a source and index it now",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{$guid}}",
                "description": "A new one for every attempt; send the SAME one to retry safely."
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/knowledge/sources/:publicId/text",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "knowledge",
                "sources",
                ":publicId",
                "text"
              ],
              "variable": [
                {
                  "key": "publicId",
                  "value": "{{source_id}}",
                  "description": ""
                }
              ]
            },
            "description": "Add text to a source and index it now.\n\nSpends the plan's knowledge pages (about one per 500 words) when it\nindexes; the same text again is `unchanged` and spends nothing. A text\nrefused for the plan stays as a waiting document: send it again once\nthere is room. `status` is `indexed`, `unchanged`, `pending` (search is\nnot set up yet), `skipped` (no usable text) or `failed`.\n\n**Key permission:** `knowledge.sync`\n\nOpenAPI operation: `machineKnowledgeAddText`",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"content\": \"We are open Monday to Friday, 9am to 6pm, and Saturday until noon.\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "id": "89646857-0ad5-583f-b584-fa6a4c4b0df4",
          "name": "Upload files to a source (PDF, DOCX, TXT, Markdown)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{$guid}}",
                "description": "A new one for every attempt; send the SAME one to retry safely."
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/knowledge/sources/:publicId/files",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "knowledge",
                "sources",
                ":publicId",
                "files"
              ],
              "variable": [
                {
                  "key": "publicId",
                  "value": "{{source_id}}",
                  "description": ""
                }
              ]
            },
            "description": "Upload files to a source (PDF, DOCX, TXT, Markdown).\n\nEach file is judged on its own, by its bytes. Accepted files are\nstored and queued for extraction; follow `knowledge.document.indexed` or\n`knowledge.document.failed`. A file already in the source is not stored\nagain (`duplicate: true`). When no file is accepted the answer is not 202.\n\n**Key permission:** `knowledge.sync`\n\nOpenAPI operation: `machineKnowledgeUploadFiles`",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "files[]",
                  "type": "file",
                  "description": "",
                  "disabled": false,
                  "src": []
                },
                {
                  "key": "tags",
                  "type": "text",
                  "description": "A JSON object, e.g. {\"corporate_id\": \"CORP-1001\"}",
                  "disabled": true,
                  "value": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "id": "c898e860-9a5f-5046-8c66-de0c31b56c97",
          "name": "Put an uploaded file back on the extraction queue",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/knowledge/documents/:publicId/retry",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "knowledge",
                "documents",
                ":publicId",
                "retry"
              ],
              "variable": [
                {
                  "key": "publicId",
                  "value": "{{document_id}}",
                  "description": ""
                }
              ]
            },
            "description": "Put an uploaded file back on the extraction queue.\n\n**Key permission:** `knowledge.sync`\n\nOpenAPI operation: `machineKnowledgeRetryDocument`"
          },
          "response": []
        },
        {
          "id": "a3c503a4-087a-5544-b23d-258988d538c6",
          "name": "Tag names in use, with their values",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/knowledge/tags",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "knowledge",
                "tags"
              ]
            },
            "description": "Tag names in use, with their values.\n\n**Key permission:** `knowledge.view`\n\nOpenAPI operation: `machineKnowledgeTags`"
          },
          "response": []
        }
      ]
    }
  ],
  "variable": [
    {
      "key": "base_url",
      "value": "https://api.convook.com/api",
      "type": "string",
      "description": "The API. Your environment's value wins over this one."
    },
    {
      "key": "agent_id",
      "value": "",
      "type": "string",
      "description": "Filled in by a request that returns it, or paste an id."
    },
    {
      "key": "conversation_id",
      "value": "",
      "type": "string",
      "description": "Filled in by a request that returns it, or paste an id."
    },
    {
      "key": "document_id",
      "value": "",
      "type": "string",
      "description": "Filled in by a request that returns it, or paste an id."
    },
    {
      "key": "message_id",
      "value": "",
      "type": "string",
      "description": "Filled in by a request that returns it, or paste an id."
    },
    {
      "key": "record_id",
      "value": "",
      "type": "string",
      "description": "Filled in by a request that returns it, or paste an id."
    },
    {
      "key": "source_id",
      "value": "",
      "type": "string",
      "description": "Filled in by a request that returns it, or paste an id."
    },
    {
      "key": "ticket",
      "value": "",
      "type": "string",
      "description": "Filled in by a request that returns it, or paste an id."
    }
  ]
}
