# Widget API overview

```
/api/v1/{organisation}/widgets/{widget}/…
```

`{widget}` is the widget's public id. The key you paste into the embed snippet looks like `{organisation}/{widget}`. That public key is safe to put in the browser; it is not a secret.

## Endpoints

| Method | Path | Purpose | Uses answers |
|---|---|---|---|
| `GET` | `/config` | Appearance and behaviour settings | No |
| `POST` | `/search` | Search your content | No |
| `POST` | `/chat` | AI answer (optional streaming) | Yes |
| `POST` | `/summarize` | Short AI summary of top results | Yes |
| `POST` | `/events` | Feedback and click analytics | No |
| `*` | `/conversations…` | History for signed-in visitors | No |

See [Authentication](/docs/authentication) for domain, secret key, and API key rules.

## Config

```
GET /widgets/{widget}/config
```

Responses may be cached for up to five minutes.

```json
{
  "type": "chatbot",
  "name": "Help",
  "variant": "bubble",
  "accent_light": "#18181b",
  "accent_dark": "#fafafa",
  "theme": "auto",
  "branding": true,
  "summary": false,
  "speech_input": false,
  "placeholder": "",
  "greeting": "",
  "prompts": [],
  "avatar": null,
  "debounce": 1000,
  "default_language": "en",
  "suggest_follow_ups": false
}
```

`prompts` is chat-only: short suggested questions (max 6) rendered as chips on an empty thread. `suggest_follow_ups` is chat-only: after an answer, leftover related pages can appear as follow-up questions. New chat widgets store this as `true`. A widget that never set the key reports `false`.

`branding` reflects your plan. On plans that show the Datalumo badge, clients cannot turn it off by ignoring this field.

## Session ids

Search and chat accept an optional `session_id`. If you omit it, Datalumo creates one and returns it. Reuse the same id from search through clicks and chat so analytics stay connected for that visit.

## Search

```
POST /api/v1/{organisation}/widgets/{widget}/search
```

**Auth:** widget domain, widget secret, or organisation API key with `search`  
**Widget type:** search or chatbot

Search across the sources linked to the widget. Search does not use answer credits.

### Body

| Field | Type | Required | Description |
|---|---|---|---|
| `query` | string | yes | Max 1024 characters |
| `filters` | object | no | Optional filters |
| `sort` | string | no | Optional sort |
| `limit` | int | no | 1-50, default `10` |
| `session_id` | string | no | Analytics session; created for you if omitted |

### Response

```json
{
  "data": [
    {
      "id": "0194b…",
      "external_id": "wp-42",
      "name": "Hello world",
      "url": "https://example.com/hello-world#section",
      "snippet": "…matching text…",
      "score": 0.87,
      "meta": null,
      "thumbnail": "https://example.com/hello-world.jpg",
      "highlights": ["hello", "world"]
    }
  ],
  "session_id": "…"
}
```

`url` is the link you can show as a citation (page URL, with a section anchor when available). `highlights` lists the query words found in `snippet`, without small words like "for" or "the", so you can bold them.

### Example

```bash
curl -sS -X POST \
  -H "Authorization: Bearer $WIDGET_SECRET" \
  -H "Content-Type: application/json" \
  -d '{"query": "how does pricing work", "limit": 5}' \
  "https://datalumo.app/api/v1/{organisation}/widgets/{widget}/search"
```

## Chat

```
POST /api/v1/{organisation}/widgets/{widget}/chat
```

**Auth:** widget domain, widget secret, or organisation API key with `chat`  
**Widget type:** chatbot  
**Billing:** uses one answer from your plan. When you have no answers left, the API returns **402** with plain search hits in `data` (same shape as the search endpoint). No chat model is called. The official embed renders those results in the bubble.

### Body

| Field | Type | Required | Description |
|---|---|---|---|
| `query` | string | yes | Visitor message, max 2048 characters |
| `conversation_id` | string | no | Continue an existing conversation |
| `session_id` | string | no | Analytics session |
| `browser_language` | string | no | Language hint from the visitor's browser |
| `context` | object | no | Up to 10 string keys with simple values (for example page URL or title) |
| `user` | object | no | `{ "id": "…", "hash": "…" }` signed visitor identity |
| `stream` | bool | no | Set `true` for a streamed response instead of one JSON payload |

### JSON response (`stream` false or omitted)

```json
{
  "data": {
    "text": "…answer in markdown…",
    "conversation_id": "…",
    "answer_id": "…",
    "sources": [
      {
        "id": "0194b…",
        "external_id": "wp-42",
        "name": "Hello world",
        "url": "https://example.com/hello-world",
        "snippet": "…",
        "score": 0.91,
        "meta": null
      }
    ],
    "actions": [],
    "suggestions": []
  },
  "session_id": "…"
}
```

### Streaming (`"stream": true`)

The response is a stream of server-sent events (`text/event-stream`):

| Event | Data | When |
|---|---|---|
| `status` | `{ "step": "searching", "query": "…" }` | Looking up your knowledge sources |
| `status` | `{ "step": "web_searching" }` | Searching allowlisted websites (chat action) |
| `status` | `{ "step": "sending" }` | Calling a configured webhook (chat action) |
| `status` | `{ "step": "offering" }` | Proposing a page action the visitor will confirm |
| `token` | `{ "text": "…" }` | Next piece of the answer |
| `done` | `{ "conversation_id", "session_id", "answer_id", "sources": […], "actions": […], "suggestions": […] }` | Finished |
| `error` | `{ "message": "…" }` | Something failed mid-stream |

### Identity and conversation ownership

- With a valid `user.id` and `user.hash`, the conversation belongs to that verified visitor.
- Continuing a verified conversation without the same proof returns **403**.
- A bad hash returns **422**.

See [Conversations](/docs/conversations) for listing history, reading transcripts, keeping conversations, and deletion.

`suggestions` is two or three follow-up questions from leftover retrieval. Empty when none fit or the widget turned them off. The official embed shows them under the reply actions and hides the list after the visitor sends again.

Chatbots can also call optional [chat actions](/docs/chat-actions) you configure in the dashboard (allowlisted web search, signed webhooks, and page actions).

## When to use the embed instead

The official embed already handles streaming, conversation storage, visitor identity, and falling back to search when answers run out. Prefer it in the browser: see [Install](/docs/sdk) for mount options, headless mode, and the analytics beacon, or the [chat](/docs/chat) and [search](/docs/search) product guides. Call these endpoints directly when you build a custom client or need server-side access.

Optional chatbot [actions](/docs/chat-actions) (allowlisted web search, signed webhooks, and page actions) are configured in the dashboard, not on this config endpoint.
