Skip to main content

Knowledge API

The Knowledge API lets you manage your organization's knowledge content, the foundation of what the AI assistant knows.

List knowledge​

GET /knowledge/content
Authorization: Bearer <token>

Query parameters:

ParameterTypeDescription
domainstringFilter by domain
url_patternstringFilter by URL pattern
statusstringFilter by status: published, draft
pagenumberPage number (default: 1)
limitnumberItems per page (default: 20)

Response:

{
"data": [
{
"id": "uuid",
"title": "How to complete the registration form",
"content": "Step 1: Enter your full name...",
"domain": "app.example.com",
"url_pattern": "/register",
"status": "published",
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-01-20T14:00:00Z"
}
],
"pagination": {
"total": 42,
"page": 1,
"limit": 20
}
}

Create knowledge​

POST /knowledge/content
Authorization: Bearer <token>
Content-Type: application/json

{
"title": "How to complete the registration form",
"content": "Step 1: Enter your full name in the first field...",
"domain": "app.example.com",
"url_pattern": "/register",
"status": "draft"
}

Update knowledge​

PUT /knowledge/content/:id
Authorization: Bearer <token>
Content-Type: application/json

{
"content": "Updated content...",
"status": "published"
}

Delete knowledge​

DELETE /knowledge/content/:id
Authorization: Bearer <token>

Knowledge requests​

Users create requests when they can't find answers. Manage them via the API.

Two scopes apply to every route here and both are enforced on the server. The organization comes from the verified token, so a caller whose token resolves to no organization is refused rather than served an unfiltered list. Within an organization, an administrator sees the whole inbox and moderates it; anybody else sees and reads only the requests they filed themselves. Do not rely on a client-side filter for either: they are server rules now precisely because a client-side one is skipped by calling the endpoint directly.

Create a request​

POST /knowledge/requests
Authorization: Bearer <token>
Content-Type: application/json

{
"domain": "example.com",
"url": "https://example.com/form",
"page_title": "Contact Form",
"request_text": "How do I fill in this form?",
"language": "es"
}

Authentication is required. The asker is taken from the verified token, and a user_id in the body is ignored: the endpoint used to accept one while unauthenticated, which let anyone file a request into a customer's inbox under somebody else's name. That text later reaches the prompt an administrator runs to generate a suggestion, so its attribution has to be real.

List requests​

GET /knowledge/requests
Authorization: Bearer <token>

Filters: domain, url, status, start_date, end_date, app_id, and pagination through page, limit, sort_by, sort_order. A user_id filter is honoured for administrators only; every other caller is pinned to their own id whatever the parameter says.

Statuses​

pending, in_progress, resolved, rejected, harvested. The first four are what an administrator may set. harvested marks a row written by the harvest endpoint for page structure rather than a question somebody asked, and is never selectable.

Treat a status you do not recognise as unknown, never as pending. A client that fell back to pending is how resolved requests read "Pending" for months without anyone noticing.

Moderate a request​

PATCH /knowledge/requests/:id
Authorization: Bearer <token>
Content-Type: application/json

{
"status": "resolved",
"response_text": "Documented in the new article.",
"response_content_id": 77
}

Organization administrator only. Every field is optional and at least one is required. response_text is the free-text answer shown to the asker; response_content_id is the knowledge entry published in answer to the request, and null detaches it.

Moving to resolved or rejected stamps who resolved it and when, and only on the transition, so a second call on an already-finished request does not rewrite that. The same transition notifies the asker, in the language the request carries, with a link to the request in their My Account.

PDF Ingestion API​

Import knowledge base entries from a PDF document using AI-powered extraction.

Start ingestion​

Uploads a PDF and starts async background processing. Returns immediately with a batch_id.

POST /knowledge/ingest-pdf
Authorization: Bearer <token>
Content-Type: multipart/form-data

file=<pdf-binary>
app_id=1 # optional: associate entries with an application
domain=app.example.com # optional: associate entries with a domain

Response (202 Accepted):

{
"batch_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "processing",
"message": "PDF ingestion started. Poll /knowledge/ingestion-batches/:id for status."
}

Constraints:

  • File must be a PDF (application/pdf)
  • Maximum file size: 50 MB
  • Text must be extractable (image-only PDFs are not supported)

Poll batch status​

GET /knowledge/ingestion-batches/:batch_id
Authorization: Bearer <token>

Response:

{
"id": "550e8400-e29b-41d4-a716-446655440000",
"file_name": "user-manual.pdf",
"status": "completed",
"total_chunks": 47,
"processed_chunks": 47,
"total_entries": 132,
"error": null,
"entries": [
{
"id": 101,
"title": "Create an Organizational Unit",
"description": "Organizational units represent offices...",
"scope": "domain_global",
"is_active": false
}
],
"created_at": "2026-03-09T10:00:00Z",
"updated_at": "2026-03-09T10:03:00Z"
}

status values: processing | completed | failed

All extracted entries have is_active: false (draft) until approved.

List all batches​

GET /knowledge/ingestion-batches
Authorization: Bearer <token>

Approve entries​

Activates selected entries from a batch, making them visible to the AI assistant.

POST /knowledge/ingestion-batches/:batch_id/approve
Authorization: Bearer <token>
Content-Type: application/json

{
"entry_ids": [101, 102, 103]
}

Response:

{ "success": true, "approved": 3 }

Delete a batch​

Removes the batch record and all its unapproved draft entries.

DELETE /knowledge/ingestion-batches/:batch_id
Authorization: Bearer <token>

Already-approved entries (those activated via the approve endpoint) are not deleted.

Visibility-filtered page query​

Query knowledge for a specific page with visibility filtering based on authentication.

GET /knowledge/page?url=https://example.com/form

Authentication: Optional. Controls which visibility tiers are returned.

Auth methodHeaderContent returned
Nonen/apublic only
Extension userAuthorization: Bearer <jwt>public + authenticated
Backoffice adminAuthorization: Bearer <jwt> (admin role)public + authenticated + internal
Widget key (public)X-Widget-Key: wk_live_xxxpublic only
Widget key (authenticated)X-Widget-Key: wk_live_xxxpublic + authenticated

Query parameters:

ParameterTypeDescription
urlstringRequired. Full page URL
view_pathstringView path published by the client (e.g. from ViewStateBridge). When provided, the backend prefers view-scoped knowledge over URL pattern matching.
view_idnumberNumeric view ID. Alternative to view_path when the client knows it directly.

Pass the canonical view identifier for view-scoped knowledge. See the Views and detection rules guide.

Response (200):

{
"url": "https://example.com/form",
"status": "available",
"content": { "id": 1, "title": "Form Guide", "visibility": "public" },
"contents": [...],
"app": { "id": 1, "name": "My App", "isRegistered": true },
"domain": { "name": "example.com", "isRegistered": true },
"stats": { "appContentCount": 5, "domainContentCount": 2, "scopedContentCount": 1 }
}

Visibility levels​

Each knowledge entry has a visibility field:

LevelDescription
publicAccessible without authentication
authenticatedRequires a valid JWT or an authenticated widget key
internalOnly visible to backoffice users (admin/backoffice JWT roles)

Set visibility when creating or updating content:

POST /knowledge/content
Authorization: Bearer <token>
Content-Type: application/json

{
"title": "Internal troubleshooting guide",
"content": "...",
"domain": "app.example.com",
"visibility": "internal"
}

If visibility is omitted, it defaults to the app's default_knowledge_visibility setting (or public if not configured).