> ## Documentation Index > Fetch the complete documentation index at: https://apidocs.document360.com/llms.txt > Use this file to discover all available pages before exploring further. # List Eddy AI answer feedback > Returns the feedback readers left on Eddy AI answers in one workspace and language, newest first by default. Each row carries everything needed to act on it: whether the reader liked or disliked the answer, the reasons and comment they gave, the triage status and assignee, and the question that was rated: when it was asked (`asked_at`), what was asked (`query`) and the answer Eddy AI gave, and who left the feedback (`asked_by`). `created_at` is when the feedback was left, usually later than `asked_at`. `conversation_count` and `discussion_count` say how long the `/conversations` and `/discussions` lists of the record are. **Who left the feedback:** `asked_by` follows the Portal's AI feedback list: the email the reader gave with "notify me about changes", or, for a signed-in team member, their account; null when the feedback carries no identity. On a project that anonymizes readers, team accounts stay named and anyone else has `-` as name and email, where the Portal shows "Anonymous". **Sources:** only feedback from the sources the Portal lists (`kb_site`, `kb_widget`, `public_api`, `freshdesk`, `chatbot`, `zendesk`) is returned, so the list and its `total_count` match the Portal's. **Scope:** `workspace_id` and `lang_code` are both required; one call lists one workspace and language. **Filters:** every filter is optional and they combine with AND. A filter that takes several values (`status`, `source`, `feedback_reasons`) matches any of them; repeat the key (`status=open&status=inProgress`) or separate the values with commas. Every enum-like value (`feedback_type`, `answer_status`, `source`, `status`, `feedback_reasons`, `sort_order`) is matched case-insensitively, so `DISLIKE` and `InProgress` work; a number is refused, not read as a position. `source` also accepts the camelCase spelling (`kbWidget`). Responses always use the spelling the schema shows. `search` matches queries that contain every word of the term, in any order and not necessarily next to each other, ignoring case; a word also matches longer words it starts (`bill` finds `billing`). A term matching more than 10,000 questions is refused with `422`, so use a more specific one. **Dates:** `start_date` / `end_date` bound when the feedback was left (both inclusive, either may be omitted). A date-only `end_date` (`2026-10-26`) includes that whole day (UTC). **Order and paging:** by when the feedback was left, newest first unless `sort_order=asc`; ties are broken by id, so the order is stable. Pass `next_cursor` back as `cursor` to fetch the next page, with the same filters and sort order. When both are sent, `cursor` wins and `page` is ignored. Treat the cursor as opaque: it only marks a position, so it never widens the workspace, language or filters of the call it is sent with. `page` reaches the first 10,000 records; use `cursor` beyond that. A page past the last record is `200` with an empty `data` and `has_more: false`, not an error. **Content access:** an answer can quote any article of the workspace, so the caller needs access to all content of the workspace and language. A caller limited to some categories or articles, or denied any, gets `403`. Requires the `ViewAIFeatures` permission (the one the Portal requires to read AI answer feedback; submitting feedback needs only `ViewArticles`), and a plan that includes Eddy AI search. ## OpenAPI ````json GET /v3/projects/{project_id}/workspaces/ai/feedback { "openapi": "3.0.1", "info": { "title": "Document360 Customer API", "description": "Document360 RESTful APIs let you integrate your documentation with your software — onboard readers, manage articles, automate publishing, and more.\n\nFull reference: [API Documentation](https://apidocs.document360.io/docs).\n\n## Authentication\nV3 authenticates with an **API key** — not the `api_token` used by V1/V2. Create one in the Document360 portal under **Settings → Knowledge base portal → API keys**, then click **Create API key**. (Projects that still have the older screen show two tabs; choose **Enhanced keys (v3)**, or pick **API v3** from the Create menu.)\n\nSend it in the `X-API-Key` header:\n\n```\nX-API-Key: d360_sk_...\n```\n\nThe plaintext key is shown **once**, at creation — only its hash is stored, so a lost key must be replaced rather than recovered. Each key carries a fixed portal role, content role and content-access scope, supports an optional expiry, and can be disabled or deleted at any time from the same screen. Treat it as an opaque string: it is a pre-shared secret, not an OAuth 2.0 access token, and must not be parsed.\n\nFor flows that act on behalf of a specific person, V3 also accepts an OAuth 2.0 bearer token carrying the `customerApi` scope.\n\nAPI access is part of your subscription plan. If your plan does not include it, every endpoint returns `403` with the error code `FEATURE_NOT_IN_LICENSE` — including for keys created while an earlier plan was active. Contact your Customer Success Manager or `support@document360.com` to enable it.\n\n> **Note on terminology:** V1/V2 call these *API tokens* and manage them under **Settings → Knowledge base portal → API tokens**. V3 keys are a separate credential with their own screen, roles and scope — a V1/V2 token will not authenticate a V3 request.\n\n## Rate Limits\nAll endpoints are rate-limited per API key per project, with separate limits for reads (`GET`/`HEAD`) and writes (`POST`/`PUT`/`PATCH`/`DELETE`). Default limits vary by plan. When rate-limited, responses include `Retry-After`, `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset` headers. Use exponential backoff with jitter for optimal retry behavior.", "termsOfService": "https://document360.com/terms", "contact": { "name": "Document360 Support", "url": "https://document360.io/contact-us/", "email": "support@document360.com" }, "license": { "name": "Document360 API Terms of Use", "url": "https://document360.com/terms" }, "version": "3.0.0" }, "servers": [ { "url": "https://apihub.document360.io", "description": "Document 360 API Hub" }, { "url": "https://apihub.us.document360.io", "description": "Document360 API Hub - US data center" }, { "url": "https://apihub.{private_hosting}.document360.io", "description": "Private hosting - Please provide the subdomain name.", "variables": { "private_hosting": { "default": "domain", "description": "Sub domain for private hosting" } } } ], "security": [ { "ApiKey": [] }, { "Bearer": [ "customerApi" ] } ], "tags": [ { "name": "Projects", "description": "List and manage knowledge base projects. Export and import project documentation, and manage project-level site customization such as custom CSS and JavaScript." }, { "name": "Projects > Import & export", "description": "Start a project export or import and poll the resulting background operation for completion." }, { "name": "Workspaces", "description": "Manage workspaces and access workspace-scoped categories and articles." }, { "name": "Workspaces > AI", "description": "Query the workspace with Eddy AI, submit feedback on AI answers, and read that feedback with its conversation." }, { "name": "Articles", "description": "Create, read, update, and delete knowledge base articles and their settings, including bulk operations." }, { "name": "Articles > Publishing & workflow", "description": "Publish and unpublish articles (single and bulk) and update the article workflow status." }, { "name": "Articles > Versions", "description": "List, fork, and delete article versions." }, { "name": "Articles > Attachments", "description": "Manage the files attached to an article — list, upload, and delete article attachments." }, { "name": "Articles > Content drafts", "description": "(Internal) Stage AI-generated article content with an Accept/Reject review lifecycle — used by Eddy Copilot to preview content before committing to the live article body." }, { "name": "Articles > Labels", "description": "Read and set the labels associated with an article." }, { "name": "Articles > Comments", "description": "Read and create the comment threads authors and reviewers leave on an article's content, and reply to them. Each comment id matches the `data-comment-id` attribute of the corresponding marker in the raw article content, so adding a text-anchored comment takes two calls: update the content with the marker, then create the thread with the same id." }, { "name": "Categories", "description": "Manage the category hierarchy that organizes articles. Create, update, and delete categories and their settings, including bulk operations." }, { "name": "Categories > Page category", "description": "Read and update the content body of page-type categories, including bulk content updates." }, { "name": "Categories > Publishing & workflow", "description": "Publish and unpublish categories (single and bulk) and update the category workflow status." }, { "name": "Categories > Versions", "description": "List, fork, and delete category versions." }, { "name": "Categories > Labels", "description": "Read and set the labels associated with a category." }, { "name": "Categories > Comments", "description": "Read and create the comment threads authors and reviewers leave on a page category's content, and reply to them. Only page-type categories carry content, so other category types return `404`. Adding a text-anchored comment takes two calls: update the content with the marker, then create the thread with the same id." }, { "name": "ArticleTemplates", "description": "Manage reusable article templates — per-language article skeletons that authors and AI tools (such as Eddy Copilot) can apply when starting a new article.", "x-displayName": "Article templates" }, { "name": "Content reuse > Glossaries", "description": "Manage glossary entries — terminology definitions referenced inside articles via merge codes (`{{glossary.name}}`) and rendered as tooltips on the public KB." }, { "name": "Content reuse > Snippets", "description": "Manage reusable snippets — markdown or WYSIWYG content fragments referenced across articles via merge codes (`{{snippet.name}}`)." }, { "name": "Content reuse > Variables", "description": "Manage reusable variables — short text values referenced across articles via merge codes (`{{variable.name}}`)." }, { "name": "AiWriterStyleGuides", "description": "Retrieve AI writer style guides configured for the project — voice / tone instructions Eddy Copilot follows when generating article content. Used to let users pick a style before kicking off generation.", "x-displayName": "AI writer style guides" }, { "name": "RedirectRules", "description": "Manage article-redirection rules — 301/302 redirects from old article URLs to new locations after renames or restructures.", "x-displayName": "Redirect rules" }, { "name": "Tags", "description": "Manage tags — labels used to classify articles, categories, and files for filtering and discovery." }, { "name": "Labels", "description": "Manage labels shown on the article list and category tree. (Associating labels with a specific article or category lives under Articles and Categories.)" }, { "name": "MirroredArticles", "description": "List, read and create mirrored articles — read-only copies of a source article placed in other categories or workspaces of the project, including bulk mirroring." }, { "name": "CustomFields", "description": "Retrieve the custom field definitions configured for the project. Custom field values are read and written through the article and category settings endpoints.", "x-displayName": "Custom fields" }, { "name": "ApiReferences", "description": "Import, resync, and publish OpenAPI/Swagger specifications for API reference documentation.", "x-displayName": "API references" }, { "name": "ApiReferences > Logs", "description": "Monitor API reference import logs and inspect individual log entries." }, { "name": "Drive", "description": "Search Drive and retrieve files associated with an article, and track Drive background tasks." }, { "name": "Drive > Folders", "description": "Create, read, update, and delete Drive folders." }, { "name": "Drive > Files", "description": "Upload, copy, move, delete, and tag Drive files." }, { "name": "Drive > Image drafts", "description": "(Internal) Stage uploaded or AI-generated images (committed to Drive and associated with a staged article draft) — used by Eddy Copilot." }, { "name": "Readers", "description": "Manage reader accounts, invitations, and access scopes for private documentation." }, { "name": "Readers > Groups", "description": "Create, read, update, and delete reader groups." }, { "name": "Users", "description": "Manage users, their portal and content roles, and consolidated (effective) permissions within a project." }, { "name": "Users > Groups", "description": "Manage user groups, their portal role, and consolidated (effective) group permissions." }, { "name": "Roles", "description": "Create, read, update, and delete portal and content roles, list the users assigned to a role, and retrieve the permission dependency matrix." }, { "name": "ContentAccess", "description": "Manage per-node content-access permissions — grant, allow, deny, and inheritance overrides for users and groups against specific versions, languages, categories, or articles, plus version/language site protection.", "x-displayName": "Content access" }, { "name": "ContentAccess > Eligibility", "description": "List the readers and groups eligible to be granted content access." }, { "name": "Languages", "description": "Retrieve configured languages for a workspace." }, { "name": "Translations", "description": "Query translation status of articles across languages." }, { "name": "Operations", "description": "Track asynchronous operations — long-running tasks (bulk updates, merges, exports) accepted with `202 Accepted` and an `Operation-Location` header. Poll until the operation reaches a terminal status, honouring the `Retry-After` header." }, { "name": "AgentAssist", "description": "Back end for the Document360 Zendesk app's per-agent mode: workspace, category, article, search and Eddy AI reads executed as the signed-in support agent, plus article and category creation attributed to them.", "x-displayName": "Agent assist (Zendesk)" }, { "name": "ZendeskAppAuth", "description": "(Internal) Per-agent Document360 sign-in for the Zendesk app: begin a sign-in, receive the Identity callback, claim the agent's session handle, and check or end that session.", "x-displayName": "Zendesk app sign-in" }, { "name": "ZendeskAppAttestation", "description": "(Internal) The Zendesk app's signed background page: verifies the app token Zendesk signs for the agent at the keyboard and hands the app a short-lived agent pass that lets a sign-in complete silently.", "x-displayName": "Zendesk app attestation" }, { "name": "ZendeskAppConnect", "description": "(Internal) Completes connecting a project's Zendesk account for silent agent sign-in: the OAuth callback Zendesk redirects the approving admin to.", "x-displayName": "Zendesk account connection" }, { "name": "Analytics > Articles", "description": "View article performance metrics including views, top articles, reader locations, and category breakdowns." }, { "name": "Analytics > Search", "description": "View search usage metrics including top keywords, no-result queries, and bounced searches." }, { "name": "Analytics > AI search", "description": "View AI-powered search metrics including usage trends, most-referenced articles, and trending topics." }, { "name": "FeedbackManagement", "description": "Manage reader feedback on articles and page categories — list it, read a record, page through its team discussions, and update its status (including closing it) and assignee to sync it with external issue trackers.", "x-displayName": "Feedback management" }, { "name": "Analytics > Feedback", "description": "View reader feedback trends across articles, including top-performing articles and feedback keywords." } ], "externalDocs": { "description": "Document360 API Documentation", "url": "https://apidocs.document360.io/docs" }, "x-tagGroups": [ { "name": "Workspace", "tags": [ "Projects", "Projects > Import & export", "Workspaces", "Workspaces > AI" ] }, { "name": "Content", "tags": [ "Articles", "Articles > Publishing & workflow", "Articles > Versions", "Articles > Attachments", "Articles > Content drafts", "Articles > Labels", "Categories", "Categories > Page category", "Categories > Publishing & workflow", "Categories > Versions", "Categories > Labels", "ArticleTemplates", "AiWriterStyleGuides", "RedirectRules", "Tags", "Labels", "ApiReferences", "ApiReferences > Logs", "FeedbackManagement" ] }, { "name": "Content reuse", "tags": [ "Content reuse > Glossaries", "Content reuse > Snippets", "Content reuse > Variables" ] }, { "name": "Media", "tags": [ "Drive", "Drive > Folders", "Drive > Files", "Drive > Image drafts" ] }, { "name": "Access", "tags": [ "Readers", "Readers > Groups", "Users", "Users > Groups", "Roles", "ContentAccess", "ContentAccess > Eligibility" ] }, { "name": "Localization", "tags": [ "Languages", "Translations" ] }, { "name": "Operations", "tags": [ "Operations" ] }, { "name": "Integrations", "tags": [ "AgentAssist", "ZendeskAppAuth", "ZendeskAppAttestation", "ZendeskAppConnect" ] }, { "name": "Analytics", "tags": [ "Analytics > Articles", "Analytics > Search", "Analytics > AI search", "Analytics > Feedback" ] } ], "paths": { "/v3/projects/{project_id}/workspaces/ai/feedback": { "get": { "tags": [ "Workspaces > AI" ], "summary": "List Eddy AI answer feedback.", "description": "Returns the feedback readers left on Eddy AI answers in one workspace and language, newest first by default.\r\nEach row carries everything needed to act on it: whether the reader liked or disliked the answer, the reasons\r\nand comment they gave, the triage status and assignee, and the question that was rated: when it was asked\r\n(`asked_at`), what was asked (`query`) and the answer Eddy AI gave, and who left the feedback\r\n(`asked_by`).\r\n`created_at` is when the feedback was left, usually later than `asked_at`.\r\n`conversation_count` and `discussion_count` say how long the `/conversations` and\r\n`/discussions` lists of the record are.\r\n \r\n**Who left the feedback:** `asked_by` follows the Portal's AI feedback list: the email the reader gave\r\nwith \"notify me about changes\", or, for a signed-in team member, their account; null when the feedback carries\r\nno identity. On a project that anonymizes readers, team accounts stay named and anyone else has `-` as\r\nname and email, where the Portal shows \"Anonymous\".\r\n \r\n**Sources:** only feedback from the sources the Portal lists (`kb_site`, `kb_widget`,\r\n`public_api`, `freshdesk`, `chatbot`, `zendesk`) is returned, so the list and its\r\n`total_count` match the Portal's.\r\n \r\n**Scope:** `workspace_id` and `lang_code` are both required; one call lists one workspace and\r\nlanguage.\r\n \r\n**Filters:** every filter is optional and they combine with AND. A filter that takes several values\r\n(`status`, `source`, `feedback_reasons`) matches any of them; repeat the key\r\n(`status=open&status=inProgress`) or separate the values with commas. Every enum-like value\r\n(`feedback_type`, `answer_status`, `source`, `status`, `feedback_reasons`,\r\n`sort_order`) is matched case-insensitively, so `DISLIKE` and `InProgress` work; a number is\r\nrefused, not read as a position. `source` also accepts the camelCase spelling (`kbWidget`).\r\nResponses always use the spelling the schema shows.\r\n`search` matches queries that contain every word of the term, in any order and not necessarily next to\r\neach other, ignoring case; a word also matches longer words it starts (`bill` finds `billing`). A\r\nterm matching more than 10,000 questions is refused with `422`, so use a more specific one.\r\n \r\n**Dates:** `start_date` / `end_date` bound when the feedback was left (both inclusive, either may be\r\nomitted). A date-only `end_date` (`2026-10-26`) includes that whole day (UTC).\r\n \r\n**Order and paging:** by when the feedback was left, newest first unless `sort_order=asc`; ties are\r\nbroken by id, so the order is stable. Pass `next_cursor` back as `cursor` to fetch the next page,\r\nwith the same filters and sort order. When both are sent, `cursor` wins and `page` is ignored.\r\nTreat the cursor as opaque: it only marks a position, so it never widens the workspace, language or filters\r\nof the call it is sent with.\r\n`page` reaches the first 10,000 records; use `cursor` beyond that. A page past the last record is\r\n`200` with an empty `data` and `has_more: false`, not an error.\r\n \r\n**Content access:** an answer can quote any article of the workspace, so the caller needs access to all\r\ncontent of the workspace and language. A caller limited to some categories or articles, or denied any, gets\r\n`403`.\r\n \r\nRequires the `ViewAIFeatures` permission (the one the Portal requires to read AI answer feedback;\r\nsubmitting feedback needs only `ViewArticles`), and a plan that includes Eddy AI search.", "operationId": "listAiFeedback", "parameters": [ { "$ref": "#/components/parameters/ProjectId" }, { "name": "workspace_id", "in": "query", "description": "Identifier of the workspace to list feedback for.", "required": true, "schema": { "type": "string", "format": "uuid", "example": "9a3b4c5d-6e7f-8a9b-0c1d-2e3f4a5b6c7d" }, "example": "9a3b4c5d-6e7f-8a9b-0c1d-2e3f4a5b6c7d" }, { "name": "lang_code", "in": "query", "description": "Language code to list feedback for; must be a language of the workspace.", "required": true, "schema": { "pattern": "^[a-z]{2,3}(-[A-Za-z0-9]{2,8}){0,2}$", "type": "string", "example": "en" }, "example": "en" }, { "name": "feedback_type", "in": "query", "description": "Only feedback that liked (`like`) or disliked (`dislike`) the answer.", "schema": { "enum": [ "like", "dislike" ], "type": "string", "example": "like" }, "example": "like" }, { "name": "answer_status", "in": "query", "description": "Only feedback on questions Eddy AI answered (`answered`) or had no content to answer (`unanswered`). Both when omitted.", "schema": { "enum": [ "answered", "unanswered" ], "type": "string", "example": "answered" }, "example": "answered" }, { "name": "source", "in": "query", "description": "Only feedback left from these places: `kb_site`, `kb_widget`, `public_api`, `freshdesk`, `chatbot`, `zendesk`.", "schema": { "type": "array", "items": { "enum": [ "kb_site", "kb_widget", "public_api", "freshdesk", "chatbot", "zendesk" ], "type": "string", "example": "kb_site" }, "example": [ "kb_site" ] }, "example": [ "kb_site" ] }, { "name": "status", "in": "query", "description": "Only feedback in these triage statuses: `open`, `planned`, `inProgress`, `complete`, `closed`.", "schema": { "type": "array", "items": { "enum": [ "open", "planned", "inProgress", "complete", "closed" ], "type": "string", "example": "open" }, "example": [ "open" ] }, "example": [ "open" ] }, { "name": "assignee_id", "in": "query", "description": "Only feedback assigned to this team member.", "schema": { "type": "string", "format": "uuid", "example": "9a3b4c5d-6e7f-8a9b-0c1d-2e3f4a5b6c7d" }, "example": "9a3b4c5d-6e7f-8a9b-0c1d-2e3f4a5b6c7d" }, { "name": "feedback_reasons", "in": "query", "description": "Only feedback that gave any of these reasons: `need_more_information`, `difficult_to_understand`, `inaccurate_or_irrelevant`, `incorrect_source_reference`, `missing_information`, `dont_like_style`, `others`, `comments`.", "schema": { "type": "array", "items": { "enum": [ "need_more_information", "difficult_to_understand", "inaccurate_or_irrelevant", "incorrect_source_reference", "missing_information", "dont_like_style", "others", "comments" ], "type": "string", "example": "need_more_information" }, "example": [ "need_more_information" ] }, "example": [ "need_more_information" ] }, { "name": "search", "in": "query", "description": "Only feedback whose query contains all of these words, in any order (at most 256 characters).", "schema": { "type": "string", "example": "getting started" }, "example": "getting started" }, { "name": "start_date", "in": "query", "description": "Only feedback left at or after this time (ISO 8601; a value without an offset is UTC).", "schema": { "type": "string", "format": "date-time", "example": "2025-07-01T00:00:00Z" }, "example": "2025-07-01T00:00:00Z" }, { "name": "end_date", "in": "query", "description": "Only feedback left at or before this time (ISO 8601; a value without an offset is UTC; a date without a time includes that whole day); must not be before `start_date`.", "schema": { "type": "string", "format": "date-time", "example": "2025-08-15T23:59:59Z" }, "example": "2025-08-15T23:59:59Z" }, { "name": "sort_order", "in": "query", "description": "`desc` (newest first, the default) or `asc` (oldest first), by when the feedback was left.", "schema": { "enum": [ "asc", "desc" ], "type": "string", "default": "desc", "example": "desc" }, "example": "desc" }, { "$ref": "#/components/parameters/Page" }, { "$ref": "#/components/parameters/PageSize" }, { "name": "cursor", "in": "query", "description": "The `next_cursor` of the previous page.", "schema": { "type": "string", "example": "eyJpZCI6IjlhM2I0YzVkLTZlN2YtOGE5Yi0wYzFkLTJlM2Y0YTViNmM3ZCJ9" }, "example": "eyJpZCI6IjlhM2I0YzVkLTZlN2YtOGE5Yi0wYzFkLTJlM2Y0YTViNmM3ZCJ9" }, { "name": "include_total_count", "in": "query", "description": "Whether to return the total number of matching records in `pagination.total_count`.", "schema": { "type": "boolean", "default": false, "example": false }, "example": false } ], "responses": { "200": { "description": "Feedback retrieved successfully.", "headers": { "X-RateLimit-Limit": { "description": "The maximum number of requests allowed in the current time window for this request's bucket. Read requests (GET/HEAD) and write requests (POST/PUT/PATCH/DELETE) have independent limits, applied per caller (API key or user) per project.", "schema": { "type": "integer", "format": "int32" } }, "X-RateLimit-Remaining": { "description": "The number of requests remaining in the current time window. When this reaches 0, subsequent requests receive a 429 response.", "schema": { "type": "integer", "format": "int32" } } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AiFeedbackSummaryResponsePaginatedResponse" }, "examples": { "Eddy AI answer feedback": { "summary": "One page of feedback, newest first, with more to fetch: pass next_cursor back as cursor.", "value": { "data": [ { "feedback_id": "71a2b3c4-d5e6-4f7a-8b9c-0d1e2f3a4b5d", "feedback_type": "like", "answer_status": "answered", "comment": null, "feedback_reasons": [], "source": "kb_site", "status": "closed", "assignee_id": "2b1a0f9e-8d7c-4b6a-9f5e-4d3c2b1a0f9e", "workspace_id": "4d3c2b1a-0f9e-4d8c-b7a6-5f4e3d2c1b0a", "lang_code": "en", "asked_at": "2026-10-13T16:00:00Z", "asked_by": { "name": "Alex Kim", "email": "alex@example.com" }, "query": "Where are the API keys?", "answer": "You can generate API keys under Settings > API tokens.", "conversation_count": 1, "discussion_count": 0, "created_at": "2026-10-13T16:05:00Z" }, { "feedback_id": "71a2b3c4-d5e6-4f7a-8b9c-0d1e2f3a4b5c", "feedback_type": "dislike", "answer_status": "answered", "comment": "The steps were for the old UI.", "feedback_reasons": [ "incorrect_source_reference", "missing_information" ], "source": "kb_widget", "status": "open", "assignee_id": null, "workspace_id": "4d3c2b1a-0f9e-4d8c-b7a6-5f4e3d2c1b0a", "lang_code": "en", "asked_at": "2026-10-12T08:55:00Z", "asked_by": { "name": "Jordan Lee", "email": "jordan@example.com" }, "query": "How do I reset my password?", "answer": "Go to Settings > Security and choose Reset password.", "conversation_count": 2, "discussion_count": 1, "created_at": "2026-10-12T09:30:00Z" } ], "pagination": { "page": 1, "page_size": 2, "total_count": null, "has_more": true, "next_cursor": "NjM5MjczOTQyMDAwMDAwMDAwfDcxYTJiM2M0LWQ1ZTYtNGY3YS04YjljLTBkMWUyZjNhNGI1Yw" }, "success": true, "request_id": "req_abc123def456", "errors": null, "warnings": null } }, "No matching feedback": { "summary": "Nothing matches the filters: an empty page, not a 404.", "value": { "data": [], "pagination": { "page": 1, "page_size": 25, "total_count": 0, "has_more": false, "next_cursor": null }, "success": true, "request_id": "req_abc123def456", "errors": null, "warnings": null } } } } } }, "401": { "description": "Authentication token is missing or invalid.", "headers": { "WWW-Authenticate": { "description": "Indicates the authentication scheme required. Returns `Bearer` with optional `error` and `error_description` parameters per RFC 6750.", "schema": { "type": "string" } } }, "content": { "application/problem+json": { "schema": { "allOf": [ { "$ref": "#/components/schemas/V3ProblemDetails" }, { "type": "object", "properties": { "type": { "enum": [ "https://apidocs.document360.io/apidocs/errors/unauthorized" ], "type": "string", "description": "RFC 7807 error type URI. Always `https://apidocs.document360.io/apidocs/errors/unauthorized` for 401 responses." }, "status": { "enum": [ 401 ], "type": "integer", "description": "HTTP status code. Always `401` for this response.", "format": "int32" } } } ] }, "examples": { "Missing or invalid token": { "summary": "Authentication token is missing or invalid.", "value": { "type": "https://apidocs.document360.io/apidocs/errors/unauthorized", "title": "Unauthorized.", "status": 401, "detail": "The authentication token is missing or has expired.", "instance": null, "trace_id": "req_abc123def456", "errors": [ { "code": "UNAUTHORIZED", "message": "Bearer token is missing or invalid.", "field": null, "details": null } ], "warnings": null } } } } } }, "403": { "description": "Refused, with one of three error codes: `FEATURE_NOT_IN_LICENSE` (the plan does not include Eddy AI search), `PERMISSION_DENIED` (the caller lacks the `ViewAIFeatures` permission), or `FORBIDDEN` (the caller lacks access to all content of the workspace and language). An acting user with no access to the project at all gets `ACCESS_DENIED`.", "content": { "application/problem+json": { "schema": { "allOf": [ { "$ref": "#/components/schemas/V3ProblemDetails" }, { "type": "object", "properties": { "type": { "enum": [ "https://apidocs.document360.io/apidocs/errors/forbidden" ], "type": "string", "description": "RFC 7807 error type URI. Always `https://apidocs.document360.io/apidocs/errors/forbidden` for 403 responses." }, "status": { "enum": [ 403 ], "type": "integer", "description": "HTTP status code. Always `403` for this response.", "format": "int32" } } } ] }, "examples": { "Insufficient permissions": { "summary": "Insufficient permissions for this resource.", "value": { "type": "https://apidocs.document360.io/apidocs/errors/forbidden", "title": "Forbidden.", "status": 403, "detail": "You do not have permission to perform this action.", "instance": null, "trace_id": "req_abc123def456", "errors": [ { "code": "FORBIDDEN", "message": "Insufficient permissions for this project.", "field": null, "details": null } ], "warnings": null } } } } } }, "404": { "description": "Workspace not found.", "content": { "application/problem+json": { "schema": { "allOf": [ { "$ref": "#/components/schemas/V3ProblemDetails" }, { "type": "object", "properties": { "type": { "enum": [ "https://apidocs.document360.io/apidocs/errors/resource-not-found" ], "type": "string", "description": "RFC 7807 error type URI. Always `https://apidocs.document360.io/apidocs/errors/resource-not-found` for 404 responses." }, "status": { "enum": [ 404 ], "type": "integer", "description": "HTTP status code. Always `404` for this response.", "format": "int32" } } } ] }, "examples": { "Resource not found": { "summary": "The requested resource does not exist.", "value": { "type": "https://apidocs.document360.io/apidocs/errors/resource-not-found", "title": "Resource not found.", "status": 404, "detail": "The requested resource was not found.", "instance": null, "trace_id": "req_abc123def456", "errors": [ { "code": "RESOURCE_NOT_FOUND", "message": "Resource not found.", "field": null, "details": null } ], "warnings": null } } } } } }, "422": { "description": "Validation failed (missing scope, a workspace_id or assignee_id that is not a GUID, a lang_code whose language part is not lowercase or that is not a language of the workspace, an invalid filter value, a search matching more than 10,000 questions, date range, sort order, cursor or paging, or a page beyond the first 10,000 records). `errors[].field` names the parameter.", "content": { "application/problem+json": { "schema": { "allOf": [ { "$ref": "#/components/schemas/V3ProblemDetails" }, { "type": "object", "properties": { "type": { "enum": [ "https://apidocs.document360.io/apidocs/errors/validation-error" ], "type": "string", "description": "RFC 7807 error type URI. Always `https://apidocs.document360.io/apidocs/errors/validation-error` for 422 responses." }, "status": { "enum": [ 422 ], "type": "integer", "description": "HTTP status code. Always `422` for this response.", "format": "int32" } } } ] }, "examples": { "Validation failed": { "summary": "One or more request fields failed validation.", "value": { "type": "https://apidocs.document360.io/apidocs/errors/validation-error", "title": "Validation Error", "status": 422, "detail": "One or more fields failed validation.", "instance": null, "trace_id": "req_abc123def456", "errors": [ { "code": "VALIDATION_ERROR", "message": "The field is required.", "field": "name", "details": null } ], "warnings": null } } } } } }, "429": { "description": "Rate limit exceeded. Retry after the duration specified in the Retry-After header.", "headers": { "Retry-After": { "description": "Number of seconds to wait before retrying the request. Use exponential backoff with jitter for optimal retry behavior.", "schema": { "type": "integer", "format": "int32" } }, "X-RateLimit-Limit": { "description": "The maximum number of requests allowed in the current time window for this request's bucket. Read requests (GET/HEAD) and write requests (POST/PUT/PATCH/DELETE) have independent limits, applied per caller (API key or user) per project. Read limits are typically higher than write limits.", "schema": { "type": "integer", "format": "int32" } }, "X-RateLimit-Remaining": { "description": "The number of requests remaining in the current time window. When this reaches 0, subsequent requests will receive a 429 response.", "schema": { "type": "integer", "format": "int32" } }, "X-RateLimit-Reset": { "description": "Number of seconds until the current rate limit window resets.", "schema": { "type": "integer", "format": "int64" } } }, "content": { "application/problem+json": { "schema": { "allOf": [ { "$ref": "#/components/schemas/V3ProblemDetails" }, { "type": "object", "properties": { "type": { "enum": [ "https://apidocs.document360.io/apidocs/errors/too-many-requests" ], "type": "string", "description": "RFC 7807 error type URI. Always `https://apidocs.document360.io/apidocs/errors/too-many-requests` for 429 responses." }, "status": { "enum": [ 429 ], "type": "integer", "description": "HTTP status code. Always `429` for this response.", "format": "int32" } } } ] }, "examples": { "Rate limit exceeded": { "summary": "Rate limit exceeded.", "value": { "type": "https://apidocs.document360.io/apidocs/errors/too-many-requests", "title": "Too Many Requests.", "status": 429, "detail": "Rate limit exceeded. Retry after the duration specified in the Retry-After header.", "instance": null, "trace_id": "req_abc123def456", "errors": [ { "code": "TOO_MANY_REQUESTS", "message": "Rate limit exceeded. Retry after the duration specified in the Retry-After header.", "field": null, "details": null } ], "warnings": null } } } } } }, "500": { "description": "An unexpected server error occurred.", "content": { "application/problem+json": { "schema": { "allOf": [ { "$ref": "#/components/schemas/V3ProblemDetails" }, { "type": "object", "properties": { "type": { "enum": [ "https://apidocs.document360.io/apidocs/errors/internal-server-error" ], "type": "string", "description": "RFC 7807 error type URI. Always `https://apidocs.document360.io/apidocs/errors/internal-server-error` for 500 responses." }, "status": { "enum": [ 500 ], "type": "integer", "description": "HTTP status code. Always `500` for this response.", "format": "int32" } } } ] }, "examples": { "Unexpected server error": { "summary": "Unexpected server error.", "value": { "type": "https://apidocs.document360.io/apidocs/errors/internal-server-error", "title": "Internal Server Error.", "status": 500, "detail": "An unexpected error occurred. Please try again or contact support.", "instance": null, "trace_id": "req_abc123def456", "errors": [ { "code": "INTERNAL_SERVER_ERROR", "message": "An unexpected error occurred.", "field": null, "details": null } ], "warnings": null } } } } } } }, "security": [ { "ApiKey": [] }, { "Bearer": [ "customerApi" ] } ] } } }, "components": { "parameters": { "ProjectId": { "name": "project_id", "in": "path", "description": "The unique identifier of the project. Retrieve project IDs from `GET /v3/projects`.", "required": true, "schema": { "type": "string", "format": "uuid", "example": "9a3b4c5d-6e7f-8a9b-0c1d-2e3f4a5b6c7d" } }, "Page": { "name": "page", "in": "query", "description": "Page number (1-based). Defaults to 1.", "schema": { "minimum": 1, "type": "integer", "format": "int32", "default": 1 } }, "PageSize": { "name": "page_size", "in": "query", "description": "Number of results per page. Defaults to 25. Maximum 100.", "schema": { "maximum": 100, "minimum": 1, "type": "integer", "format": "int32", "default": 25 } } }, "schemas": { "AiFeedbackSummaryResponsePaginatedResponse": { "required": [ "data", "pagination", "request_id", "success" ], "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/AiFeedbackSummaryResponse" }, "description": "List of items for the current page." }, "pagination": { "allOf": [ { "$ref": "#/components/schemas/PaginationInfo" } ], "description": "Pagination metadata.", "readOnly": true }, "success": { "type": "boolean", "description": "Whether the API request was successful.", "readOnly": true }, "request_id": { "minLength": 1, "type": "string", "description": "Unique identifier for request tracing and correlation.", "readOnly": true }, "errors": { "type": "array", "items": { "$ref": "#/components/schemas/ApiError" }, "description": "List of errors if the request failed.", "nullable": true }, "warnings": { "type": "array", "items": { "$ref": "#/components/schemas/ApiWarning" }, "description": "List of non-fatal warnings from the request.", "nullable": true } }, "additionalProperties": false, "description": "Paginated API response containing a list of items." }, "V3ProblemDetails": { "required": [ "status", "title", "type" ], "type": "object", "properties": { "type": { "minLength": 1, "enum": [ "https://apidocs.document360.io/apidocs/errors/bad-request", "https://apidocs.document360.io/apidocs/errors/unauthorized", "https://apidocs.document360.io/apidocs/errors/forbidden", "https://apidocs.document360.io/apidocs/errors/resource-not-found", "https://apidocs.document360.io/apidocs/errors/conflict", "https://apidocs.document360.io/apidocs/errors/validation-error", "https://apidocs.document360.io/apidocs/errors/too-many-requests", "https://apidocs.document360.io/apidocs/errors/internal-server-error" ], "type": "string", "description": "URI reference identifying the error type (links to documentation)." }, "title": { "minLength": 1, "type": "string", "description": "Short human-readable summary of the error type." }, "status": { "enum": [ 400, 401, 403, 404, 409, 422, 429, 500 ], "type": "integer", "description": "HTTP status code.", "format": "int32" }, "detail": { "type": "string", "description": "Human-readable explanation specific to this occurrence.", "nullable": true }, "instance": { "type": "string", "description": "URI of the request that generated the error.", "nullable": true }, "trace_id": { "type": "string", "description": "Request trace identifier for correlation.", "nullable": true }, "errors": { "type": "array", "items": { "$ref": "#/components/schemas/ApiError" }, "description": "Structured list of specific errors (extension field).", "nullable": true }, "warnings": { "type": "array", "items": { "$ref": "#/components/schemas/ApiWarning" }, "description": "Non-fatal warnings (extension field).", "nullable": true } }, "additionalProperties": false, "description": "RFC 7807 Problem Details response for V3 API errors.\r\nContent-Type: application/problem+json" }, "AiFeedbackSummaryResponse": { "required": [ "conversation_count", "created_at", "discussion_count", "status" ], "type": "object", "properties": { "feedback_id": { "type": "string", "description": "Unique identifier of the feedback record.", "nullable": true, "readOnly": true, "example": "71a2b3c4-d5e6-4f7a-8b9c-0d1e2f3a4b5c" }, "feedback_type": { "enum": [ "like", "dislike" ], "type": "string", "allOf": [ { "$ref": "#/components/schemas/V3AiFeedbackType" } ], "description": "Whether the reader liked (`like`) or disliked (`dislike`) the answer.", "readOnly": true, "example": "dislike", "x-enumNames": [ "Like", "Dislike" ], "x-enum-varnames": [ "Like", "Dislike" ], "x-ms-enum": { "name": "V3AiFeedbackType", "modelAsString": true } }, "answer_status": { "enum": [ "answered", "unanswered" ], "type": "string", "allOf": [ { "$ref": "#/components/schemas/V3AiAnswerStatus" } ], "description": "Whether Eddy AI answered the question (`answered`) or had no content to answer from (`unanswered`).", "readOnly": true, "example": "answered", "x-enumNames": [ "Answered", "Unanswered" ], "x-enum-varnames": [ "Answered", "Unanswered" ], "x-ms-enum": { "name": "V3AiAnswerStatus", "modelAsString": true } }, "comment": { "type": "string", "description": "The comment the reader wrote with the feedback. Null when they wrote none.", "nullable": true, "readOnly": true, "example": "The steps were for the old UI." }, "feedback_reasons": { "type": "array", "items": { "type": "string" }, "description": "The reasons the reader picked. Zero or more of `need_more_information`,\r\n`difficult_to_understand`, `inaccurate_or_irrelevant`, `incorrect_source_reference`,\r\n`missing_information`, `dont_like_style`, `others` and `comments`. Empty when the\r\nreader picked none.", "nullable": true, "readOnly": true, "example": [ "incorrect_source_reference", "missing_information" ] }, "source": { "allOf": [ { "$ref": "#/components/schemas/V3AiFeedbackSource" } ], "description": "Where the reader asked the question and left the feedback. Null only for a source added after this API\r\nversion.", "nullable": true, "readOnly": true, "example": "kb_widget" }, "status": { "enum": [ "open", "planned", "inProgress", "complete", "closed" ], "type": "string", "allOf": [ { "$ref": "#/components/schemas/V3AiFeedbackStatus" } ], "description": "Current triage status of the feedback.", "readOnly": true, "example": "open", "x-enumNames": [ "Open", "Planned", "InProgress", "Complete", "Closed" ], "x-enum-varnames": [ "Open", "Planned", "InProgress", "Complete", "Closed" ], "x-ms-enum": { "name": "V3AiFeedbackStatus", "modelAsString": true } }, "assignee_id": { "type": "string", "description": "Identifier of the team member the feedback is assigned to. Null when unassigned.", "nullable": true, "readOnly": true, "example": "2b1a0f9e-8d7c-4b6a-9f5e-4d3c2b1a0f9e" }, "workspace_id": { "type": "string", "description": "Identifier of the workspace the question was asked in.", "nullable": true, "readOnly": true, "example": "4d3c2b1a-0f9e-4d8c-b7a6-5f4e3d2c1b0a" }, "lang_code": { "type": "string", "description": "Language code the question was asked in.", "nullable": true, "readOnly": true, "example": "en" }, "asked_at": { "type": "string", "description": "When the rated question was asked (UTC). Distinct from `created_at`, when the feedback was left. Null\r\nwhen the conversation is no longer stored.", "format": "date-time", "nullable": true, "readOnly": true, "example": "2026-10-12T08:55:00Z" }, "asked_by": { "allOf": [ { "$ref": "#/components/schemas/AiFeedbackAskerResponse" } ], "description": "Who left the feedback, as the Portal's AI feedback list shows them: the email they gave with \"notify me about\r\nchanges\", or, for a signed-in team member, their account. Null when the feedback carries no identity. On a\r\nproject that anonymizes readers, team accounts stay named and anyone else has `-` as name and email\r\n(the Portal shows \"Anonymous\").", "nullable": true, "readOnly": true }, "query": { "type": "string", "description": "The question that was rated. Null when the conversation is no longer stored.", "nullable": true, "readOnly": true, "example": "How do I reset my password?" }, "answer": { "type": "string", "description": "The answer Eddy AI gave, as stored but without its citation markers (`[citation_N]`); it may contain\r\nHTML markup. Null when the conversation is no longer stored.", "nullable": true, "readOnly": true, "example": "Go to Settings > Security and choose Reset password." }, "conversation_count": { "type": "integer", "description": "Number of questions in the session the rated question belongs to (it included), at most 1,000; list them\r\nwith `GET .../ai/feedback/{feedback_id}/conversations`.", "format": "int32", "readOnly": true, "example": 2 }, "discussion_count": { "type": "integer", "description": "Number of internal team discussion entries on the feedback, including the notes added in the Portal; list\r\nthem with `GET .../ai/feedback/{feedback_id}/discussions`.", "format": "int32", "readOnly": true, "example": 1 }, "created_at": { "type": "string", "description": "When the reader left the feedback (UTC).", "format": "date-time", "readOnly": true, "example": "2026-10-12T09:30:00Z" } }, "additionalProperties": false, "description": "One reader's feedback on an Eddy AI answer, as returned by the list endpoint: everything an integration needs to\r\nact on the row, including the question that was rated, when and by whom it was asked, and the answer." }, "PaginationInfo": { "required": [ "has_more", "page", "page_size" ], "type": "object", "properties": { "page": { "type": "integer", "description": "Current page number (1-based). Returns 0 when using cursor-based pagination.", "format": "int32" }, "page_size": { "type": "integer", "description": "Number of items per page.", "format": "int32" }, "total_count": { "type": "integer", "description": "Total number of items across all pages. Only populated when `include_total_count=true` is specified in the request.", "format": "int64", "nullable": true }, "has_more": { "type": "boolean", "description": "Whether additional pages are available." }, "next_cursor": { "type": "string", "description": "Opaque cursor to retrieve the next page of results. Pass this value as the `cursor` query parameter. Null when there are no more pages.", "nullable": true } }, "additionalProperties": false, "description": "Metadata describing the current pagination state. Supports both offset-based\r\nand cursor-based pagination." }, "ApiError": { "required": [ "code", "message" ], "type": "object", "properties": { "code": { "minLength": 1, "enum": [ "ACCESS_DENIED", "ARCHIVED_CONTENT_READ_ONLY", "ARCHIVE_ALREADY_ARCHIVED", "ARCHIVE_NOT_ARCHIVED", "BAD_REQUEST", "CONFLICT", "FEATURE_NOT_IN_LICENSE", "FEEDBACK_CLOSED", "FORBIDDEN", "INTERNAL_SERVER_ERROR", "LICENSE_LIMIT_EXCEEDED", "OPERATION_FAILED", "PERMISSION_DENIED", "PREMIUM_FEATURE_NOT_IN_LICENSE", "RESOURCE_NOT_FOUND", "TOO_MANY_REQUESTS", "UNAUTHORIZED", "UNRECOGNIZED_FIELD", "UNRECOGNIZED_FIELDS", "VALIDATION_ERROR" ], "type": "string", "description": "Machine-readable error code (e.g. VALIDATION_ERROR, RESOURCE_NOT_FOUND)." }, "message": { "minLength": 1, "type": "string", "description": "Human-readable error message." }, "field": { "type": "string", "description": "The request field that caused the error, if applicable.", "nullable": true }, "details": { "type": "string", "description": "Additional context about the error.", "nullable": true } }, "additionalProperties": false, "description": "Represents an error returned by the API." }, "ApiWarning": { "required": [ "code", "message" ], "type": "object", "properties": { "code": { "minLength": 1, "type": "string", "description": "Machine-readable warning code." }, "message": { "minLength": 1, "type": "string", "description": "Human-readable warning message." } }, "additionalProperties": false, "description": "Represents a non-fatal warning from the API." }, "V3AiFeedbackType": { "enum": [ "like", "dislike" ], "type": "string", "description": "Whether the reader liked (`like`) or disliked (`dislike`) the Eddy AI answer.", "x-enumNames": [ "Like", "Dislike" ], "x-enum-varnames": [ "Like", "Dislike" ], "x-ms-enum": { "name": "V3AiFeedbackType", "modelAsString": true } }, "V3AiAnswerStatus": { "enum": [ "answered", "unanswered" ], "type": "string", "description": "Whether Eddy AI answered the question (`answered`) or found no content to answer it from\r\n(`unanswered`).", "x-enumNames": [ "Answered", "Unanswered" ], "x-enum-varnames": [ "Answered", "Unanswered" ], "x-ms-enum": { "name": "V3AiAnswerStatus", "modelAsString": true } }, "V3AiFeedbackSource": { "enum": [ "kb_site", "kb_widget", "public_api", "freshdesk", "chatbot", "zendesk" ], "type": "string", "description": "Where the reader asked the question and left the feedback.", "x-enumNames": [ "KbSite", "KbWidget", "PublicApi", "Freshdesk", "Chatbot", "Zendesk" ], "x-enum-varnames": [ "KbSite", "KbWidget", "PublicApi", "Freshdesk", "Chatbot", "Zendesk" ], "x-ms-enum": { "name": "V3AiFeedbackSource", "modelAsString": true } }, "V3AiFeedbackStatus": { "enum": [ "open", "planned", "inProgress", "complete", "closed" ], "type": "string", "description": "Triage status of an AI answer feedback record. `closed` is terminal: a closed feedback can no longer be\r\nmodified in the Portal.", "x-enumNames": [ "Open", "Planned", "InProgress", "Complete", "Closed" ], "x-enum-varnames": [ "Open", "Planned", "InProgress", "Complete", "Closed" ], "x-ms-enum": { "name": "V3AiFeedbackStatus", "modelAsString": true } }, "AiFeedbackAskerResponse": { "required": [ "email", "name" ], "type": "object", "properties": { "name": { "type": "string", "description": "Display name. `-` when anonymized; null when no account has this email or it no longer exists.", "nullable": true, "readOnly": true, "example": "Jordan Lee" }, "email": { "type": "string", "description": "Email address. `-` when anonymized.", "format": "email", "nullable": true, "readOnly": true, "example": "jordan@example.com" } }, "additionalProperties": false, "description": "The person who left an Eddy AI answer feedback." } }, "securitySchemes": { "ApiKey": { "type": "apiKey", "description": "API key for machine-to-machine integrations, webhooks, and CI/CD (recommended). This is a static, pre-shared secret - treat it as an opaque string and do not parse it; it is an API key, not an OAuth 2.0 access token. Create one in the portal under **Settings > Knowledge base portal > API keys** using the **Create API key** button - this is a different credential from the V1/V2 *API token*, which lives on its own screen and will not authenticate a V3 request. The plaintext key (format `d360_sk_...`) is shown only once, at creation. Send it in the `X-API-Key` header: `X-API-Key: d360_sk_...`. Each key carries a fixed portal role, content role, and content-access scope, supports an optional expiry, and can be disabled or deleted at any time from the portal.", "name": "X-API-Key", "in": "header" }, "Bearer": { "type": "oauth2", "description": "All V3 endpoints require a Bearer token. Generate tokens in the Document360 portal under **Settings > API Tokens**. Tokens are project-scoped, require the `customerApi` scope, and do not expire by default. Tokens can be revoked at any time from the portal. Include the token in every request: `Authorization: Bearer `. Alternatively, use the Authorize button below to sign in via OAuth2 Authorization Code flow with PKCE.", "flows": { "authorizationCode": { "authorizationUrl": "https://identity.document360.io/connect/authorize", "tokenUrl": "https://identity.document360.io/connect/token", "scopes": { "openid": "OpenID Connect", "profile": "User profile", "email": "User email", "customerApi": "Document360 Customer API" }, "x-usePkce": { "disableManualConfiguration": true, "hideClientSecretInput": true } } }, "x-d360-clientId": "apiHubWebClient" } } } } ````