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:
| Parameter | Type | Description |
|---|---|---|
domain | string | Filter by domain |
url_pattern | string | Filter by URL pattern |
status | string | Filter by status: published, draft |
page | number | Page number (default: 1) |
limit | number | Items 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 method | Header | Content returned |
|---|---|---|
| None | n/a | public only |
| Extension user | Authorization: Bearer <jwt> | public + authenticated |
| Backoffice admin | Authorization: Bearer <jwt> (admin role) | public + authenticated + internal |
| Widget key (public) | X-Widget-Key: wk_live_xxx | public only |
| Widget key (authenticated) | X-Widget-Key: wk_live_xxx | public + authenticated |
Query parameters:
| Parameter | Type | Description |
|---|---|---|
url | string | Required. Full page URL |
view_path | string | View path published by the client (e.g. from ViewStateBridge). When provided, the backend prefers view-scoped knowledge over URL pattern matching. |
view_id | number | Numeric 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:
| Level | Description |
|---|---|
public | Accessible without authentication |
authenticated | Requires a valid JWT or an authenticated widget key |
internal | Only 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).