Knowledge & Ingestion API

A knowledge base (KB) is the grounding an AI Employee searches at run time. The Knowledge & Ingestion API is where you build that grounding: register a connection to an external source, create a KB under an AI Employee, upload or sync documents into it, and organize those documents with taxonomies and tags. The ingestion service is mounted at /api/v1/ingestion, so every path below is that prefix plus the path shown.

All requests require a JWT or tenant API key. Examples use https://your-tenant.ema.co as the host.

Knowledge bases belong to an AI Employee. Every KB is scoped to an AI Employee, so most paths begin with /ai-employees/{ai_employee_id}/knowledge-bases. The ai_employee_id is the same UUID as the workflow that powers the AI Employee. A request that names an AI Employee you are not a member of returns 403.

Connections

A connection holds the credentials for one external source — SharePoint, Google Drive, Confluence, Box, ServiceNow, or the platform's own local-file store. You create a connection once, then bind one or more knowledge bases to it.

  • List: GET /api/v1/ingestion/connections.
  • Create: POST /api/v1/ingestion/connections.
  • Get / update / delete: GET, PUT, DELETE /api/v1/ingestion/connections/{id}.
POST https://your-tenant.ema.co/api/v1/ingestion/connections
Authorization: Bearer eyJhbGciOiJSUzI1Ni...
Content-Type: application/json

{
  "name": "Engineering Confluence",
  "description": "Shared engineering spaces",
  "connector_type": "confluence",
  "connector_config": "{\"base_url\":\"https://acme.atlassian.net\",\"api_token\":\"...\"}"
}

connector_type is one of local_file, sharepoint, google_drive, confluence, box, the-dot, or servicenow. connector_config is a raw JSON string carrying the connector's credentials. The response is the created Connection with its id, status, and created_at.

OAuth2 connectors. Confluence, Box, SharePoint, and Google Drive support an OAuth2 authorization flow at GET /api/v1/ingestion/connections/{connector}/oauth2/authorize, with the provider redirecting back to the matching /oauth2/callback. Check availability first with GET /api/v1/ingestion/connections/{connector}/oauth2/available. The callback routes are public (the provider, not your credential, calls them).

Knowledge bases

A knowledge base groups documents under one AI Employee. A KB can be backed by a connection (synced from an external source) or be a local-file KB with no backing connection.

  • List: GET /api/v1/ingestion/ai-employees/{ai_employee_id}/knowledge-bases.
  • Create: POST /api/v1/ingestion/ai-employees/{ai_employee_id}/knowledge-bases.
  • Get / update / delete: GET, PUT, DELETE /api/v1/ingestion/ai-employees/{ai_employee_id}/knowledge-bases/{id}.
POST https://your-tenant.ema.co/api/v1/ingestion/ai-employees/3f7a.../knowledge-bases
Authorization: Bearer eyJhbGciOiJSUzI1Ni...
Content-Type: application/json

{
  "name": "Support runbooks",
  "description": "Internal runbooks for tier-1 support",
  "connection_id": "8c4d...",
  "intent": "search"
}

Omit connection_id (or send null) to create a local-file KB. intent selects the ingestion pipeline:

  • search (default) — chunks and embeds documents for retrieval. This is the right choice for a KB an AI Employee searches.
  • extraction — masks the full extracted markdown once and skips chunking, embedding, and auto-tagging. Use it for workflow file uploads that an LLM consumes directly rather than searching.

For a Confluence connection, include connector_options with at least one space_keys entry or root_page_ids entry to scope the sync.

The KnowledgeBase response carries a document_counts object (processed, failed, processing, pending, last_sync_failed), an orphaned flag (true if the backing connection was deleted), and last_synced_at.

Documents

Documents live inside a KB. You upload them directly, or let a sync pull them from the connection.

  • List: GET /api/v1/ingestion/ai-employees/{ai_employee_id}/knowledge-bases/{id}/documents — paginated, with optional filters:
    • search — case-insensitive substring match on the document title.
    • statuspending, processing, processed, failed, or deleted.
    • folder_path — exact-match folder filter (e.g. /ENG/Getting Started/).
    • tag_node_id — repeatable; AND semantics, so the document must carry every named tag.
    • sync_log_id — restrict to the documents touched by one sync run.
    The response is a flat array of Document objects; the total count, page, and page size come back in the X-Total-Count, X-Page, and X-Page-Size response headers.
  • Folder tree: GET .../knowledge-bases/{id}/folder-tree returns the full folder hierarchy (paths and counts only, never paginated).
  • Folder children: GET .../knowledge-bases/{id}/folder-children?path=/ returns the paginated direct folders and files at a path.
  • Get / delete a document: GET, DELETE /api/v1/ingestion/documents/{id}.
  • Download: GET /api/v1/ingestion/documents/{id}/file returns the original file bytes; GET /api/v1/ingestion/documents/{id}/image returns a rendered image where vision ingestion produced one.
  • Retry a failure: POST .../knowledge-bases/{id}/documents/{document_id}/retry resets a failed but retriable document to pending.

Upload a file

POST /api/v1/ingestion/ai-employees/{ai_employee_id}/knowledge-bases/{id}/upload takes a multipart/form-data body with a single file part.

POST https://your-tenant.ema.co/api/v1/ingestion/ai-employees/3f7a.../knowledge-bases/9b2c.../upload
Authorization: Bearer eyJhbGciOiJSUzI1Ni...
Content-Type: multipart/form-data; boundary=----boundary

------boundary
Content-Disposition: form-data; name="file"; filename="runbook.pdf"
Content-Type: application/pdf

<binary file bytes>
------boundary--

The response is 201 Created with the new document's ID, chunk count, and status:

{
  "document_id": "a1b2...",
  "chunk_count": 42,
  "status": "active",
  "message": "File uploaded and processed"
}

To upload many files under one sync log, start a batch with POST .../knowledge-bases/{id}/upload-batch (returns a batch_id), upload each file, then close it with POST .../knowledge-bases/{id}/upload-batch/{batchId}/complete.

Sync

For KBs backed by an external connector, sync pulls files from the source, diffs them against what is already stored, and enqueues new or changed documents.

  • Trigger now: POST /api/v1/ingestion/ai-employees/{ai_employee_id}/knowledge-bases/{id}/sync. Only works for connector-backed KBs (not local_file); a sync already in progress returns 409.
  • Sync config: POST, GET, PUT, DELETE .../knowledge-bases/{id}/sync-config. The config sets schedule_type (interval or manual) and, for interval schedules, interval_minutes.
  • Sync logs: GET .../knowledge-bases/{id}/sync-logs lists past sync runs; GET /api/v1/ingestion/sync-logs/{id} fetches one.

Taxonomies and tags

A taxonomy is a tree of tag nodes you define per AI Employee, then apply to documents to organize and filter them.

  • Taxonomies: GET, POST /api/v1/ingestion/ai-employees/{ai_employee_id}/taxonomies, plus GET, PUT, DELETE .../taxonomies/{taxonomy_id}.
  • Nodes: GET, POST .../taxonomies/{taxonomy_id}/nodes; rename with PUT and remove a node and its descendants with DELETE .../taxonomies/{taxonomy_id}/nodes/{node_id}.
  • Tag a document: POST .../knowledge-bases/{id}/documents/{doc_id}/tags stores the chosen node and all of its ancestors. List a document's tags with the matching GET, and remove a single tag with DELETE .../tags/{node_id}.

Auto-tagging

POST /api/v1/ingestion/ai-employees/{ai_employee_id}/taxonomies/{taxonomy_id}/auto-tag runs LLM-based tagging across the documents in the knowledge bases you name. To make a taxonomy the default for a KB's new documents, set it with PUT .../knowledge-bases/{id}/auto-tag-config (and read the current setting with the matching GET).

What's next

  • AI Employee API — create the AI Employee that owns these knowledge bases.
  • Integrations API — manage connections and Tools as installed integrations.
  • Workflow API — run the workflow that searches these knowledge bases.

Last updated: Jul 3, 2026