> ## Documentation Index
> Fetch the complete documentation index at: https://paperlinkai.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Widget protocol (reference)

> The requests and events between the widget and the Paperlink AI API, for understanding only.

> [!WARNING]
>
> This page is for understanding what the widget does. It is not for building your own client. Turnstile is required, so server-to-server use is not possible. This is not a supported API, and it can change without notice.

The widget talks to `api.paperlinkai.com` with a small set of requests. This page shows the order of the requests and the shape of the data.

## Sequence

```mermaid theme={"dark"}
sequenceDiagram
  participant V as Visitor's browser
  participant A as api.paperlinkai.com
  V->>A: GET /v1/widget/config (X-API-Key)
  A-->>V: name, color, position, turnstileSiteKey...
  V->>A: frame GET /v1/widget/challenge?key&parent
  A-->>V: Turnstile page, postMessage token to parent
  V->>A: POST /v1/widget/sessions {turnstileToken}
  A-->>V: sessionId, token (1h), resumeToken (30d)
  V->>A: POST /sessions/:id/messages {content} (X-PL-Session)
  A-->>V: SSE meta, text..., followups, done
  V->>A: POST .../messages/:id/feedback {rating, reason}
  A-->>V: 204
```

## Endpoints

| Request | Headers | Purpose |
| - | - | - |
| `GET /v1/widget/config` | `X-API-Key` | Get the bot name, color, position and Turnstile site key. |
| `GET /v1/widget/challenge?key=&parent=` | none (the key is in the query) | The Turnstile page that the widget loads in a frame. It sends the token to `parent` with `postMessage`. `parent` must be on your allowed origins. |
| `POST /v1/widget/sessions` | `X-API-Key` | Start a session with a Turnstile token. |
| `POST /v1/widget/sessions/:id/refresh` | `X-API-Key`, `X-PL-Resume` | Trade a resume token for new tokens. |
| `GET /v1/widget/sessions/:id?limit=` | `X-API-Key`, `X-PL-Session` | Load the latest messages. |
| `POST /v1/widget/sessions/:id/messages` | `X-API-Key`, `X-PL-Session` | Send a question. The reply is a Server-Sent Events stream. |
| `POST /v1/widget/sessions/:id/messages/:messageId/feedback` | `X-API-Key`, `X-PL-Session` | Send a rating. |
| `POST /v1/widget/sessions/:id/contact` | `X-API-Key`, `X-PL-Session` | Send the conversation to your support email. |

## Message request

The body is `{ "content": "..." }`. The content has 1 to 2,000 characters after trimming.

The feedback body has a `rating` of `1` or `-1`. The optional `reason` is one of `wrong`, `outdated`, `not_what_i_asked` or `other`.

## Server-Sent Events

The stream sends events in this order: `meta`, then `text` many times, then `followups`, then `done`. An `error` event can come at any point. It ends the stream.

### meta

**sessionId** `string`

The conversation id.

**messageId** `string`

The id of the answer message.

**tier** `string`

`cache` or `rag`. The value `card` is reserved and is not sent today.

**sources** `object[]`

The source pages. Each item has `doc`, `page`, `documentId` and an optional `superseded` flag.

### text

**t** `string`

The next piece of the answer text.

### followups

**questions** `string[]`

The follow-up questions that the widget shows as chips.

### done

**messageId** `string`

The id of the answer message.

**citations** `object[]`

The cited pages. Each item has `doc` and `page`.

**tier** `string`

`cache` or `rag`. The value `card` is reserved and is not sent today.

**answered** `boolean`

`false` when the bot could not answer.

**fallback** `object`

The contact card data: `type` is `contact`, with `email` and `url`. It is present when the widget shows your contact card.

### error

**code** `string`

The error code. See the list below.

**message** `string`

A readable message.

**fallback** `object`

The contact card data, when the widget shows your contact card.

## Error codes

The `error` event uses these codes:

* `REPLY_LIMIT`
* `RATE_LIMITED`
* `MODEL_ERROR`
* `RETRIEVAL_ERROR`
* `INTERNAL`
* `SUBSCRIPTION_INACTIVE`

## HTTP error envelope

Requests that fail before the stream starts return this JSON. A 429 reply carries the header `retry-after: 60`.

```json theme={"dark"}
{
  "error": {
    "code": "ORIGIN_NOT_ALLOWED",
    "message": "This site is not allowed to use this widget. Add it to the bot's allowed origins.",
    "details": null,
    "requestId": "..."
  }
}
```

**error.code** `string`

A stable code in upper case, for example `ORIGIN_NOT_ALLOWED`.

**error.message** `string`

A readable message.

**error.details** `any`

Extra data. It is present for some errors only.

**error.requestId** `string`

The request id. Give it to support when you report a problem.

> **[Install the widget with a script tag](https://paperlinkai.com/docs/developers/install/script-tag.md)**
>
> Go back to the supported way to add the widget.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.