OdyDocs

Knowledge

Read, write and search the workspace's knowledge base — folders, articles, website imports and the per-agent audience every AI reads from.

The knowledge base is what your AI knows about your business: articles organized in folders, shared by every AI agent in the workspace and by AI texting — the inbox's AI suggested replies and the agent's automatic replies (Business AI Texting), which retrieve the passages that answer each customer's text from this same base. The same data backs the Knowledge tab in the app and the MCP knowledge tools. Use this API to sync a CMS or help center into Ody, to keep a website import fresh on a schedule, or to check what one agent would answer from.

Nothing here needs a publish step. Writing an article re-indexes it for search and updates every published agent its audience admits, within a second or two.

#Concepts

  • Article — a Markdown body of up to 20,000 characters. Written through this API (kind: "text") or read from a website (kind: "url"). An article has an Available to AI switch (enabled) and an audience.
  • Folder — groups articles; nests up to three levels. A folder also has an audience, which applies to everything inside it.
  • Consumer — anything that reads knowledge, identified by a consumer key: agent:<agentId> for an AI agent, texting for AI texting on every number, texting:<e164> for AI texting on one specific number. GET /v1/api/knowledge lists the workspace's consumers (replies is a deprecated alias of texting).
  • Audience — all (every consumer, the default) or selected (only the consumer keys in consumers).

Two resolution rules decide who reads an article: a folder set to selected restricts everything inside it to its consumers (even an all article), and an article can narrow its folder's audience but never widen it. enabled: false hides the article from everyone.

#The article object

  • id uuid — Unique identifier.
  • title string — Shown in the app and used as the first line of every search chunk.
  • kind string — text (written) or url (website import).
  • content string | null — Markdown body. Omitted from list endpoints — fetch one article for it.
  • sourceUrl string | null — Website imports: the address that was read.
  • status string — ready, pending (import in progress) or failed.
  • pageCount integer | null — Website imports: pages read into content.
  • fetchedAt timestamp | null — Website imports: when the site was last read.
  • folderId uuid | null — Containing folder; null at the top level.
  • enabled boolean — The Available to AI switch.
  • audience all | selected — Who may read it.
  • consumers array of strings — Consumer keys granted when audience is selected; empty otherwise.
  • chars integer — Length of content.
  • indexedAt timestamp | null — When the search index last matched content; null until first indexed.
  • createdAt, updatedAt timestamp — ISO 8601.
The article object
{
  "id": "3f6d2a1e-8c4b-4f0e-9a7d-1b2c3d4e5f60",
  "title": "Cancellation policy",
  "kind": "text",
  "content": "## Cancellations\nFree up to 24 hours before…",
  "sourceUrl": null,
  "status": "ready",
  "pageCount": null,
  "fetchedAt": null,
  "folderId": "a1b2c3d4-0000-4000-8000-000000000001",
  "enabled": true,
  "audience": "selected",
  "consumers": ["agent:7c9e6679-7425-40de-944b-e07fc1f90ae7", "texting"],
  "chars": 412,
  "indexedAt": "2026-10-28T14:03:10.000Z",
  "createdAt": "2026-10-28T14:03:09.000Z",
  "updatedAt": "2026-10-28T14:03:09.000Z"
}

#The folder object

  • id uuid — Unique identifier.
  • parentId uuid | null — Parent folder; null at the top level.
  • name string — Folder name.
  • description string | null — One line describing what goes inside.
  • audience all | selected — Who may read everything inside.
  • consumers array of strings — Consumer keys granted when audience is selected.
  • position integer — Sort order among siblings.
  • articleCount integer — Articles directly inside (not in sub-folders).
  • createdAt, updatedAt timestamp — ISO 8601.

#The consumer object

  • key string — agent:<agentId> for an AI agent; texting for AI texting on every number (the inbox's AI suggested replies today, the automated texting agent next — same engine); texting:<e164> for AI texting on one of the workspace's numbers only (for example texting:+15125550100). Use it in consumers arrays and in search. replies is a deprecated alias of texting, still accepted on input.
  • kind string — agent or texting.
  • name string — The agent's name, "AI texting", or "AI texting · +1 (512) 555-0100".
  • channel string — voice for agents, text for texting.
  • live boolean — Agents only: published and switched on.
  • numberE164 string — Texting on one number: the line. Absent for every-number texting.
  • detail string — Texting on one number: the inbox name.

Per-number texting consumers are listed only when the workspace has more than one active number. When the texting AI answers on a line it reads every article granted to texting or to texting:<that line> — so an "every number" grant keeps working when you later narrow one article to one line. Folders restrict downward: a folder granted to texting:+15125550100 hides everything inside it from the other lines (and from the voice agents).

#Get the knowledge tree

GET/v1/api/knowledge

Returns every folder, every article (without content) and the consumers you can grant access to — everything the Knowledge tab renders, in one call.

Scope: knowledge:read

Returns { folders: Folder[], articles: ArticleSummary[], consumers: Consumer[] }.

curl "https://api.ody.co/v1/api/knowledge" \
  -H "Authorization: Bearer $ODY_API_KEY"

#Search knowledge

GET/v1/api/knowledge/search

Returns the passages that best answer q, with the article each comes from. The search is semantic (vector) when embeddings are configured and falls back to full-text otherwise. Pass consumer to see only what that AI can read — the audience rules and the enabled switch are applied — which is the quickest way to debug "why doesn't my agent know this".

Scope: knowledge:read

Query parameters

  • q string — Required. The question or phrase.
  • consumer string — Filter to one reader: agent:<agentId>, texting, or texting:<e164> (what the texting AI sees on that line). Without it, every enabled article is searched.
  • folderId uuid — Only articles inside this folder (and its sub-folders).
  • limit integer — 1–20, default 6.

Returns { hits: Hit[] } where each hit has articleId, title, folderId, kind, sourceUrl, chunkIndex, content (the passage) and score (0–1, higher is better).

curl "https://api.ody.co/v1/api/knowledge/search?q=cancellation%20fee&consumer=texting" \
  -H "Authorization: Bearer $ODY_API_KEY"
Response
{
  "hits": [
    {
      "articleId": "3f6d2a1e-8c4b-4f0e-9a7d-1b2c3d4e5f60",
      "title": "Cancellation policy",
      "folderId": "a1b2c3d4-0000-4000-8000-000000000001",
      "kind": "text",
      "sourceUrl": null,
      "chunkIndex": 0,
      "content": "Cancellation policy\n\nFree up to 24 hours before the appointment; $25 after that.",
      "score": 0.87
    }
  ]
}

#List articles

GET/v1/api/knowledge/articles

Returns every article without content, newest first. Pass folderId to list one folder's direct children.

Scope: knowledge:read

Query parameters

  • folderId uuid — Only articles directly inside this folder.

Returns { articles: ArticleSummary[] }.

curl "https://api.ody.co/v1/api/knowledge/articles?folderId=a1b2c3d4-0000-4000-8000-000000000001" \
  -H "Authorization: Bearer $ODY_API_KEY"

#Create an article

POST/v1/api/knowledge/articles

Writes a Markdown article. It is ready at once: indexed for search and pushed to every published agent its audience admits.

Scope: knowledge:write

Body

  • title string — Required. Up to 120 characters.
  • content string — Required. Markdown, up to 20,000 characters.
  • folderId uuid | null — Folder to put it in. Default: top level.
  • enabled boolean — Default true.
  • audience all | selected — Default all.
  • consumers array of strings — Consumer keys; required when audience is selected. Unknown keys are rejected with invalid_argument.

Returns { article: Article } with status 201.

curl -X POST "https://api.ody.co/v1/api/knowledge/articles" \
  -H "Authorization: Bearer $ODY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Cancellation policy",
    "content": "## Cancellations\nFree up to 24 hours before the appointment; $25 after that.",
    "folderId": "a1b2c3d4-0000-4000-8000-000000000001",
    "audience": "selected",
    "consumers": ["agent:7c9e6679-7425-40de-944b-e07fc1f90ae7", "texting"]
  }'

#Import a website

POST/v1/api/knowledge/articles/import

Reads a public website — up to 20 of its main pages, found via the sitemap and links — and organizes it into one article. The request blocks until the read finishes (typically 20–40 seconds; set your client timeout to at least 90 seconds). The article is created with enabled: true and audience: all; PATCH it afterwards to change either.

Scope: knowledge:write

Body

  • url string — Required. An http(s) address of a public site. Private and loopback addresses are rejected.
  • title string — Override the article title (default: the site's name).
  • folderId uuid | null — Folder to put it in.

Returns { article: Article } with status 201. Errors carry a reason field: invalid_url, private_address (400), or unreachable, blocked, no_content (409 failed_precondition).

curl -X POST "https://api.ody.co/v1/api/knowledge/articles/import" \
  -H "Authorization: Bearer $ODY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://www.yourbusiness.com", "folderId": null }'

#Retrieve an article

GET/v1/api/knowledge/articles/:id

Returns one article including its full content.

Scope: knowledge:read

Returns { article: Article }, or 404 not_found.

curl "https://api.ody.co/v1/api/knowledge/articles/3f6d2a1e-8c4b-4f0e-9a7d-1b2c3d4e5f60" \
  -H "Authorization: Bearer $ODY_API_KEY"

#Update an article

PATCH/v1/api/knowledge/articles/:id

Changes any of the article's fields. Send only what changes; at least one field is required. Changing content or title re-indexes the article; any change re-syncs published agents.

Scope: knowledge:write

Body

  • title string
  • content string — Markdown, up to 20,000 characters. Editing a website article's content is allowed; a later refresh replaces it.
  • folderId uuid | null — Move the article; null moves it to the top level.
  • enabled boolean — The Available to AI switch.
  • audience all | selected
  • consumers array of strings — Consumer keys when audience is selected.

Returns { article: Article }.

curl -X PATCH "https://api.ody.co/v1/api/knowledge/articles/3f6d2a1e-8c4b-4f0e-9a7d-1b2c3d4e5f60" \
  -H "Authorization: Bearer $ODY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "enabled": false }'

#Refresh a website article

POST/v1/api/knowledge/articles/:id/refresh

Re-reads a website article's sourceUrl in place — same id, folder, audience and enabled state; fresh content, pageCount and fetchedAt. Blocks like an import. Returns failed_precondition for articles that are not website imports.

Scope: knowledge:write

Returns { article: Article }.

curl -X POST "https://api.ody.co/v1/api/knowledge/articles/3f6d2a1e-8c4b-4f0e-9a7d-1b2c3d4e5f60/refresh" \
  -H "Authorization: Bearer $ODY_API_KEY"

#Delete an article

DELETE/v1/api/knowledge/articles/:id

Removes the article and its search chunks. Every AI stops using it immediately.

Scope: knowledge:write

Returns { deleted: true }, or 404 not_found.

curl -X DELETE "https://api.ody.co/v1/api/knowledge/articles/3f6d2a1e-8c4b-4f0e-9a7d-1b2c3d4e5f60" \
  -H "Authorization: Bearer $ODY_API_KEY"

#Create a folder

POST/v1/api/knowledge/folders

Scope: knowledge:write

Body

  • name string — Required. Up to 120 characters.
  • description string | null — Up to 500 characters.
  • parentId uuid | null — Nest inside another folder. Folders nest at most 3 levels deep (failed_precondition beyond that).
  • audience all | selected — Default all.
  • consumers array of strings — Consumer keys when audience is selected.

Returns { folder: Folder } with status 201.

curl -X POST "https://api.ody.co/v1/api/knowledge/folders" \
  -H "Authorization: Bearer $ODY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Sales line", "audience": "selected", "consumers": ["agent:7c9e6679-7425-40de-944b-e07fc1f90ae7"] }'

#Update a folder

PATCH/v1/api/knowledge/folders/:id

Rename, move (parentId), reorder (position) or change the audience. A folder cannot be moved into itself or one of its descendants.

Scope: knowledge:write

Body — any of name, description, parentId (null = top level), audience, consumers, position.

Returns { folder: Folder }.

curl -X PATCH "https://api.ody.co/v1/api/knowledge/folders/a1b2c3d4-0000-4000-8000-000000000001" \
  -H "Authorization: Bearer $ODY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Sales desk" }'

#Delete a folder

DELETE/v1/api/knowledge/folders/:id

By default the folder's articles and sub-folders move up to its parent — nothing is lost. Pass deleteContents=true to delete everything inside instead.

Scope: knowledge:write

Query parameters

  • deleteContents boolean — Delete the articles and sub-folders too. Default false.

Returns { deleted: true, movedArticles: number, deletedArticles: number }.

curl -X DELETE "https://api.ody.co/v1/api/knowledge/folders/a1b2c3d4-0000-4000-8000-000000000001" \
  -H "Authorization: Bearer $ODY_API_KEY"

#Recipes

Sync a help center. Create one folder per help-center category, then POST /knowledge/articles per article with folderId. Store the returned id next to your source id and PATCH on edits, DELETE on removals. Keep each article focused — the search index chunks by section and prefixes every chunk with the title.

Keep a website fresh. Import once, then call POST /knowledge/articles/:id/refresh from a nightly job. The article keeps its folder and audience.

Give each phone number its own facts. Create a folder per agent with audience: "selected" and that agent's key; put shared facts (hours, address) in a top-level all folder. Verify with GET /knowledge/search?q=…&consumer=agent:<id>.

  • Scopes — knowledge:read, knowledge:write.
  • AI agent — the agent's config, which inlines the knowledge it can read when published.
  • MCP tools › Knowledge — the same operations for assistants.
Last updated Mon Nov 02 2026 00:00:00 GMT+0000 (Coordinated Universal Time)Something wrong on this page? Tell us