API Reference

The InformedAI API is a REST API that uses JSON request/response bodies and Server-Sent Events (SSE) for streaming AI responses.

Base URL:

https://api.informedai.app/api/v1

Authentication#

All authenticated endpoints require a Bearer token in the Authorization header:

curl -H "Authorization: Bearer wsk_your_key" \
  https://api.informedai.app/api/v1/document-types

Key Types#

InformedAI uses four key types, each scoped to specific operations:

| Prefix | Name | Scope | |--------|------|-------| | wsk_ | Workspace Secret Key | Full server-side access (document types, documents, sessions, knowledge library) | | pak_ | Public Access Key | Client-side widget authentication (domain-validated) | | ssk_ | Sync Secret Key | Data sync operations (one key per sync source) | | aak_ | Admin Access Key | Admin chatbot access (domain-validated) |

Keep wsk_ and ssk_ keys on your server. Only pak_ keys are safe for client-side use. Website agents and smart questionnaires use domain-based auth instead of API keys.

Document Types#

All document type endpoints require a wsk_ key.

Create Document Type#

POST /document-types
{
  "name": "blog_post",
  "displayName": "Blog Post",
  "schema": {
    "fields": {
      "title": {
        "type": "string",
        "label": "Title",
        "description": "The blog post title"
      },
      "body": {
        "type": "richtext",
        "label": "Body",
        "description": "Main content"
      }
    }
  }
}

Field types: string, number, boolean, richtext, array, image

Each field in the schema becomes a task the AI assistant can work on. The description tells the AI what kind of content to generate for that field.

List Document Types#

GET /document-types

Returns all document types in the workspace.

Get Document Type#

GET /document-types/:id

Returns a single document type with its schema and task configurations.

Update Document Type#

PUT /document-types/:id

Update the schema, display name, or task configs. Accepts partial updates.

{
  "displayName": "Updated Name",
  "schema": { "fields": { "..." : "..." } },
  "taskConfigs": {
    "title": {
      "enabled": true,
      "templateId": "template_abc",
      "quickActions": [
        { "label": "Write title", "prompt": "Write a compelling title" }
      ],
      "greeting": "Let me help you write a title."
    }
  }
}

Task config fields:

| Field | Type | Description | |-------|------|-------------| | enabled | boolean | Whether the AI can work on this field (default: true) | | templateId | string | Custom prompt template ID | | quickActions | array | One-click action buttons shown to the user | | greeting | string | Custom greeting when this task starts |

Discover Document Type (AI)#

POST /document-types/discover
{
  "query": "I need a schema for electrical bid proposals"
}

Uses AI to search the knowledge library and generate a suggested document type schema. Returns a JSON object with discovered fields.

Delete Document Type#

DELETE /document-types/:id

Returns 204 No Content.

Documents#

All document endpoints require a wsk_ key.

Create Document#

POST /documents
{
  "documentTypeId": "dt_abc123",
  "data": {}
}

List Documents#

GET /documents

Query parameters:

| Parameter | Type | Description | |-----------|------|-------------| | documentTypeId | string | Optional. Filter by document type |

Returns all documents in the workspace, optionally filtered by document type.

Get Document#

GET /documents/:id

Returns the document with its current data and associated document type.

Update Document Field#

PATCH /documents/:id/field
{
  "field": "title",
  "value": "My New Title"
}

Updates a single field and creates a version history entry.

Get Document History#

GET /documents/:id/history

Returns version history for the document, ordered by version descending.

Restore Document Version#

POST /documents/:id/restore/:version

Restores the document data to a specific version from history.

Delete Document#

DELETE /documents/:id

Returns 204 No Content.

Widget Sessions#

Widget endpoints require a Pool Access Key (pak_ prefix) in the Authorization header. The API also validates the request origin against your environment's allowed domains.

Create Session#

POST /widget/sessions
{
  "documentTypeId": "dt_abc123",
  "externalId": "your-record-id",
  "initialData": {
    "title": "Draft Title"
  }
}

| Field | Type | Description | |-------|------|-------------| | documentTypeId | string | Required. Document type to create a session for | | externalId | string | Optional. Your object's ID for idempotent document linking | | initialData | object | Optional. Current field values to sync |

When externalId is provided, the API returns the existing session if one already exists for that external ID, making the call idempotent.

Response:

{
  "session": { "id": "sess_abc", "status": "active", "..." : "..." },
  "document": { "id": "doc_abc", "data": {}, "..." : "..." },
  "documentType": { "id": "dt_abc", "name": "blog_post", "..." : "..." }
}

Get Session#

GET /widget/sessions/:id

Returns the session with its messages formatted for the widget.

Send Message (SSE)#

POST /widget/sessions/:id/message
{
  "message": "Write a compelling introduction",
  "currentFieldValues": { "title": "My Post" }
}

| Field | Type | Description | |-------|------|-------------| | message | string | Required. The user's message | | currentFieldValues | object | Optional. Current form values for context |

Returns a Server-Sent Events stream. See SSE Streaming below.

Send Quick Action#

POST /widget/sessions/:id/quick-action
{
  "action": "task_action:write_title",
  "currentFieldValues": {}
}

| Action format | Description | |---------------|-------------| | select_task:field_name | Switches the active task to the specified field | | task_action:action_id | Triggers a quick action defined in the task config |

Returns either a JSON response (for select_task) or an SSE stream (for task_action).

Apply Pending Value#

POST /widget/sessions/:id/apply

Applies the AI-suggested value to the document field. After applying, automatically advances to the next task that has a pending value.

Response:

{
  "session": { "..." : "..." },
  "appliedField": "title",
  "appliedValue": "10 Tips for Better Writing"
}

Skip Task#

POST /widget/sessions/:id/skip

Skips the current task and advances to the next field. If there are pending values on other tasks, auto-advances to the next task with a pending value.

Resume Session#

POST /widget/sessions/:id/resume

Resumes an abandoned or timed-out session. Preserves the full conversation history and document state.

Heartbeat#

POST /widget/sessions/:id/heartbeat

Sends a heartbeat to keep the session active. Updates the lastActivityAt timestamp used for session timeout tracking.

Response:

{ "success": true }

Get Analytics Run#

GET /widget/sessions/:id/analytics-runs/:runId

Returns the results of an analytics run performed during the session, including the structured breakdown and matched results.

End Session#

POST /widget/sessions/:id/end

Marks the session as ended. Ended sessions cannot be resumed.

Response:

{ "success": true }

Knowledge Library#

All library endpoints require a wsk_ key.

Add Document#

POST /library
{
  "name": "Product Guide",
  "content": "Full text content to ingest...",
  "category": "guides",
  "description": "Complete product documentation",
  "tags": ["product", "guide"],
  "metadata": { "version": "2.0" }
}

| Field | Type | Description | |-------|------|-------------| | name | string | Required. Document name | | content | string | Required. Full text content | | category | string | Optional. Category for organization | | description | string | Optional. Brief description | | tags | string[] | Optional. Searchable tags | | metadata | object | Optional. Custom metadata |

The content is automatically chunked, embedded (via Voyage AI), and indexed for semantic search. The AI assistant uses this knowledge when generating content.

List Documents#

GET /library

Query parameters:

| Parameter | Type | Description | |-----------|------|-------------| | category | string | Optional. Filter by category |

Returns all documents in the knowledge library.

Get Document#

GET /library/:id

Returns a single knowledge library document with its metadata.

Update Document#

PATCH /library/:id
{
  "name": "Updated Name",
  "category": "new-category",
  "description": "Updated description",
  "tags": ["updated"],
  "metadata": { "version": "3.0" }
}

Updates document metadata. All fields are optional.

Delete Document#

DELETE /library/:id

Deletes the document and asynchronously cleans up its knowledge chunks. Returns 204 No Content.

List Categories#

GET /library/categories

Returns distinct categories with document counts.

Get Library Stats#

GET /library/stats

Returns statistics about the knowledge library.

Response:

{
  "totalDocuments": 42,
  "totalCategories": 5
}

List Chunks#

GET /library/chunks

Query parameters:

| Parameter | Type | Description | |-----------|------|-------------| | sourceType | string | Required. Source type (e.g., kb_document, sync_category) | | sourceId | string | Required. Source ID |

Returns the knowledge chunks for a specific source.

Response:

{
  "chunks": [{ "id": "...", "content": "...", "..." : "..." }],
  "totalCount": 15
}

Get Document Chunks#

GET /library/:id/chunks

Returns the knowledge chunks for a specific knowledge library document.

Get Retrieval Logs#

GET /library/retrieval-logs

Query parameters:

| Parameter | Type | Description | |-----------|------|-------------| | sourceId | string | Required. Source ID to filter by | | limit | number | Optional. Max results (default: 50) |

Returns retrieval logs showing which chunks were retrieved for AI queries against a specific source.

Sync API#

The Sync API is used by the @informedai/sync SDK to push data from your backend into the InformedAI knowledge library. All sync endpoints require an ssk_ key.

See the Sync SDK documentation for the recommended way to use these endpoints.

Get Source#

GET /sync/source

Returns the sync source details, including categories and their stats. Also updates the heartbeat timestamp.

Register Category#

POST /sync/categories
{
  "name": "projects",
  "displayName": "Projects",
  "columns": ["title", "description", "tech_stack"]
}

Creates or updates a sync category. Categories organize your synced data by type.

List Categories#

GET /sync/categories

Returns all categories for this sync source.

Push Data#

POST /sync/categories/:category/data
{
  "records": [
    { "id": "proj_1", "title": "My Project", "description": "..." },
    { "id": "proj_2", "title": "Other Project", "description": "..." }
  ]
}

Push one or more records to a sync category. Records are cached and converted into knowledge chunks for RAG queries.

Response:

{
  "stored": 2,
  "skipped": 0,
  "category": "projects",
  "totalShards": 4,
  "totalChars": 1250,
  "totalRows": 2
}

Delete Record#

DELETE /sync/categories/:category/data/:externalId

Deletes a single record from the sync category. Returns 204 No Content.

Rebuild Category#

POST /sync/categories/:category/rebuild

Force rebuild knowledge chunks for a category. Useful after bulk changes or if data gets out of sync.

Poll Recovery Requests#

GET /sync/requests

Query parameters:

| Parameter | Type | Description | |-----------|------|-------------| | limit | number | Optional. Max requests to return |

Returns pending recovery requests. When the system needs data that's missing from cache, it creates recovery requests that the SDK polls for and resends.

Fulfill Recovery Request#

POST /sync/requests/:requestId/fulfill

Marks a recovery request as fulfilled after the data has been re-synced.

Heartbeat#

POST /sync/heartbeat

Sends a heartbeat to indicate the sync SDK is active and connected.

Admin Chatbot#

Send messages to your workspace's knowledge library chatbot. Requires an aak_ key. The API also validates the request origin against the admin agent's allowed domains.

Send Message (SSE)#

POST /admin-chatbot/message
Authorization: Bearer aak_your_key
{
  "message": "What products do we offer?",
  "history": [],
  "systemPrompt": "You are a helpful assistant that answers questions about our products."
}

| Field | Type | Description | |-------|------|-------------| | message | string | Required. The user's message | | history | array | Optional. Previous conversation messages ({ role, content }) | | systemPrompt | string | Optional. Custom system prompt for the chatbot |

Returns a Server-Sent Events stream. See SSE Streaming below.

Get Status#

GET /admin-chatbot/status

Returns whether the admin chatbot is configured for the workspace.

Response:

{
  "configured": true,
  "environmentId": "env_abc123"
}

Website Agent#

Send messages to a website agent. Uses domain-based authentication — no API key required, but the request origin must match the agent's allowed domains.

Send Message (SSE)#

POST /website-agent/:agentId/message
{
  "message": "How do I reset my password?",
  "history": []
}

| Field | Type | Description | |-------|------|-------------| | message | string | Required. The user's message | | history | array | Optional. Previous conversation messages ({ role, content }) |

Returns a Server-Sent Events stream.

Smart Questionnaire#

Guide users through step-by-step questionnaires that match answers against the knowledge base. Uses domain-based authentication — no API key required.

Get Steps#

GET /smart-questionnaire/:questionnaireId/steps

Returns the questionnaire step definitions (only enabled steps).

Response:

[
  {
    "name": "budget",
    "question": "What is your budget range?",
    "options": ["Under $500", "$500-$1000", "Over $1000"],
    "multiSelect": false,
    "answerKey": "budget"
  }
]

Submit Step Answer#

POST /smart-questionnaire/:questionnaireId/step/:stepName
{
  "message": "I need something waterproof",
  "history": []
}

Submit a free-text answer for a specific step. Option-based answers are handled client-side with zero API calls.

Response:

{
  "message": "Great, waterproof it is!",
  "answer": "waterproof",
  "action": "advance"
}

Match Answers#

POST /smart-questionnaire/:questionnaireId/match
{
  "answers": {
    "budget": { "value": "mid-range", "type": "option" },
    "requirements": { "value": "waterproof and durable", "type": "free-text" }
  }
}

Matches collected answers against the knowledge base and returns ranked results.

Get Results (SSE)#

POST /smart-questionnaire/:questionnaireId/results
{
  "matches": [],
  "message": "Tell me more about the first option",
  "history": []
}

Returns initial results from matched answers. When message and history are included, the response is an SSE stream for follow-up conversation about the results.

SSE Streaming#

Message and quick-action endpoints return a Server-Sent Events stream. Each event has a type field:

| Event Type | Description | |------------|-------------| | content | AI response text chunk | | session_update | Updated session state (task changes, pending values) | | done | Stream complete | | error | Error occurred during processing |

Streaming Endpoints#

The following endpoints return SSE streams:

  • POST /widget/sessions/:id/message
  • POST /widget/sessions/:id/quick-action (for task_action type)
  • POST /admin-chatbot/message
  • POST /website-agent/:agentId/message
  • POST /smart-questionnaire/:id/results (when message is included)

Example: Consuming the Stream#

const response = await fetch(`${API_URL}/widget/sessions/${id}/message`, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer pak_your_key',
  },
  body: JSON.stringify({
    message: 'Write a title',
    currentFieldValues: { body: 'Article content...' },
  }),
});
 
const reader = response.body!.getReader();
const decoder = new TextDecoder();
let buffer = '';
 
while (true) {
  const { done, value } = await reader.read();
  if (done) break;
 
  buffer += decoder.decode(value, { stream: true });
  const lines = buffer.split('\n');
  buffer = lines.pop()!;
 
  for (const line of lines) {
    if (line.startsWith('data: ')) {
      const event = JSON.parse(line.slice(6));
 
      switch (event.type) {
        case 'content':
          // Append AI text to your UI
          appendText(event.content);
          break;
        case 'session_update':
          // Update session state (new pending values, task changes)
          updateSession(event.session);
          break;
        case 'done':
          // Stream finished
          break;
        case 'error':
          console.error(event.error);
          break;
      }
    }
  }
}

The React SDK handles SSE parsing automatically. You only need to consume the stream directly if you're building a custom integration.

Error Responses#

All errors follow a consistent JSON format:

{
  "error": "Human-readable error message",
  "code": "ERROR_CODE",
  "status": 400
}

Common Error Codes#

| Status | Code | Description | |--------|------|-------------| | 400 | VALIDATION_ERROR | Invalid request body or parameters | | 401 | UNAUTHORIZED | Missing or invalid API key | | 403 | FORBIDDEN | Key lacks permission for this operation or domain not allowed | | 404 | NOT_FOUND | Resource does not exist | | 409 | CONFLICT | Resource already exists (duplicate externalId) | | 429 | RATE_LIMITED | Too many requests (billing limit exceeded) | | 500 | INTERNAL_ERROR | Server error |