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

# API conventions

> Reference for the rules that every SiteGPT API v2 endpoint follows: base URL, response envelope, pagination, errors, and deletes.

These rules apply to every endpoint in SiteGPT API v2.

## Base URL

```text theme={null}
https://sitegpt.ai/api/v2
```

* Send and receive JSON. Set `Content-Type: application/json` on requests with a body.
* Send your API token in the `Authorization: Bearer` header. See [API v2 authentication](/docs/api-reference/v2/authentication.md).
* `GET /api/v2/health` needs no token. Use it to check that the API is reachable.
* The OpenAPI document is at `https://sitegpt.ai/api/v2/openapi.json`.

## Response envelope

Every response uses the same envelope.

Success:

```json theme={null}
{
  "ok": true,
  "data": { },
  "meta": {
    "requestId": "YOUR_REQUEST_ID"
  }
}
```

Error:

```json theme={null}
{
  "ok": false,
  "error": {
    "code": "TOKEN_SCOPE_NOT_ALLOWED",
    "message": "Token does not have the required scope",
    "hint": "..."
  },
  "meta": {
    "requestId": "YOUR_REQUEST_ID"
  }
}
```

| Field | Description |
| - | - |
| `ok` | `true` on success, `false` on error. |
| `data` | The result. Its shape depends on the endpoint. |
| `meta.requestId` | The ID of the request. Give it to support when you report a problem. If you send an `x-request-id` header with 1 to 64 letters, digits, `_`, or `-`, SiteGPT uses its value. |
| `meta.warnings` | Optional. Messages about the request that did not stop it, for example a deprecated credential. |
| `meta.nextCursor` | On list endpoints. The cursor for the next page. See [Pagination](#pagination). |
| `error.code` | A stable code in capital letters. Use it in your code. |
| `error.message` | A readable message. It can change. Do not match on it. |
| `error.hint` | Optional. A suggested next step. |
| `error.details` | Optional. More detail, for example the fields that failed validation, or `requiredScopes` for `TOKEN_SCOPE_NOT_ALLOWED`. |

> [!NOTE]
>
> The OpenAPI error schema does not list `error.hint`, but the API sends it. Allow extra fields in your error parser.

## Pagination

List endpoints that can return many items use cursor pagination.

| Query parameter | Description |
| - | - |
| `limit` | The maximum number of items to return. At least `1`. |
| `cursor` | The cursor from the previous page. Leave it out for the first page. |

The response has the cursor in two places:

* `meta.nextCursor`
* `data.pagination`, which has `limit`, `hasNextPage`, and `nextCursor`

`nextCursor` is `null` on the last page. A page can have fewer items than `limit`, or no items, and still have a `nextCursor`. This happens when a filter matches few items. Keep reading until `nextCursor` is `null`.

```bash theme={null}
# First page
curl "https://sitegpt.ai/api/v2/chatbots/YOUR_CHATBOT_ID/conversations?limit=50" \
  -H "Authorization: Bearer YOUR_API_TOKEN"

# Next page
curl "https://sitegpt.ai/api/v2/chatbots/YOUR_CHATBOT_ID/conversations?limit=50&cursor=YOUR_NEXT_CURSOR" \
  -H "Authorization: Bearer YOUR_API_TOKEN"
```

These endpoints use cursor pagination:

| Endpoint | Default `limit` | Maximum `limit` |
| - | - | - |
| Chatbots: `GET /chatbots` | 100 | 200 |
| Conversations: `GET /chatbots/{chatbotId}/conversations` | 50 | 100 |
| Messages in a conversation: `GET /chatbots/{chatbotId}/conversations/{threadId}/messages` | 100 | 500 |
| Leads: `GET /chatbots/{chatbotId}/leads` | 50 | 100 |
| Content documents: `GET /chatbots/{chatbotId}/documents` | 50 | 100 |
| Sync jobs: `GET /chatbots/{chatbotId}/ingest-jobs` | 50 | 100 |
| Documents of a connected source: `GET /chatbots/{chatbotId}/knowledge/sources/{connectionId}/documents` | 50 | 100 |
| Confluence spaces and pages: `GET /chatbots/{chatbotId}/knowledge/sources/confluence/spaces` and `.../confluence/pages` | 20 | 100 |

A `limit` outside the allowed range returns `400 VALIDATION_FAILED`. On `GET /chatbots`, a cursor that the API did not issue returns `400 INVALID_CURSOR`. Start again without a cursor.

`GET /chatbots` lists the chatbots that you were added to most recently first. For chatbots that you own, this is the order in which you created them, newest first.

## Errors

The HTTP status tells you the kind of error. `error.code` tells you the exact cause.

| Status | Meaning | Common codes |
| - | - | - |
| 400 | The request is not valid. | `INVALID_JSON`, `VALIDATION_FAILED`, `CONFIRMATION_REQUIRED` |
| 401 | The request has no valid credential. | `AUTHORIZATION_HEADER_REQUIRED`, `AUTHORIZATION_HEADER_NOT_VALID`, `TOKEN_NOT_VALID` |
| 403 | The credential cannot do this, or your plan does not include it. | `TOKEN_SCOPE_NOT_ALLOWED`, `PLAN_UPGRADE_REQUIRED`, `HUMAN_TAKEOVER`, `CHATBOT_LIMIT_REACHED`, `ACCOUNT_INACTIVE`, `ANALYTICS_LOCKED`, `WEBHOOKS_NOT_AVAILABLE`, `RATE_LIMITS_NOT_AVAILABLE`, `HIPAA_AI_ASSISTANTS_DISABLED`, `HIPAA_CONNECTORS_DISABLED` |
| 404 | The item does not exist, or the token cannot access it. Unknown API paths also return `404`. | `CHATBOT_NOT_FOUND`, `NOT_FOUND`, `DRAFT_NOT_FOUND` |
| 405 | The endpoint does not support this HTTP method. | `METHOD_NOT_ALLOWED` |
| 409 | The current state does not allow the action. | `CONVERSATION_CLOSED`, `REPLY_IN_CHANNEL`, `MEMBERS_QUOTA_EXCEEDED`, `MEMBER_ALREADY_EXISTS`, `MEMBER_INVITE_PENDING`, `INBOX_UNAVAILABLE`, `RECIPIENT_SUPPRESSED`, `DRAFT_ATTEMPT_PENDING` |
| 429 | A plan quota is used up, the conversation reached its message limit, or too many onboarding workspaces were started. | `CHATBOT_PAGES_LIMIT_REACHED`, `MESSAGE_LIMIT_REACHED`, `RATE_LIMITED` |
| 500 | An error on the SiteGPT side. | `INTERNAL_SERVER_ERROR` |
| 502 | SiteGPT could not send an email. For an invite, SiteGPT saved the change. For an email draft, the email was not sent: with `SEND_FAILED` the draft is kept, with `SEND_REFUSED` the email service refused it and the draft is closed. | `MEMBER_INVITE_EMAIL_FAILED`, `SEND_FAILED`, `SEND_REFUSED` |
| 503 | A check or a write could not finish. Try again in a moment. | `HIPAA_CHECK_UNAVAILABLE`, `REPLY_NOT_SENT` |

A `401` response includes a `WWW-Authenticate` header that points to the OAuth metadata at `https://sitegpt.ai/.well-known/oauth-protected-resource/api/v2`.

Some errors come with a `hint`. For example, `CHATBOT_LIMIT_REACHED` and `CHATBOT_PAGES_LIMIT_REACHED` suggest how to free space or where to see plans.

## Conversation state conflicts

A conversation can be resolved (closed), or a person on your team can take it over. The API checks these states at the moment it saves your change. If the state changes while your request runs, the new state wins. See [Conversations and handoff](/docs/concepts/conversations-and-handoff.md).

| Situation | Operation | Result |
| - | - | - |
| The conversation is resolved | Send a message (`POST .../conversations/{threadId}/messages`) | `409 CONVERSATION_CLOSED` |
| The conversation is resolved | Escalate (`POST .../conversations/{threadId}/escalate`) | `409 CONVERSATION_CLOSED` |
| The conversation is resolved | Release to AI (`POST .../conversations/{threadId}/switch-to-ai`) | `409 CONVERSATION_CLOSED` |
| The conversation is resolved | Take over (`POST .../conversations/{threadId}/take-over`) | `409 CONVERSATION_CLOSED` |
| The conversation is resolved | Reply as your team (`POST .../conversations/{threadId}/replies`) | `409 CONVERSATION_CLOSED` |
| The AI answers the conversation | Reply as your team | Success. The reply takes the conversation over first, and the visitor sees that a human agent joined. `tookOver` is `true` in the response. |
| The conversation is on Slack, Crisp, or another channel with its own agent inbox | Reply as your team | `409 REPLY_IN_CHANNEL`. SiteGPT saves nothing. Reply in that channel. |
| The conversation is on Messenger | Reply as your team | Success. SiteGPT sends the reply to the person in Messenger. If Messenger does not deliver it, `channelDelivery.delivered` is `false`, and the reply stays in the conversation. The `reason` is `outside_window` (Meta's time limit passed), `disconnected` (the Facebook Page is no longer connected), `hipaa_blocked`, or `send_failed`. See [Messenger](/docs/integrations/messenger.md#reply-from-the-dashboard). |
| The conversation is on email | Reply as your team | Success. SiteGPT emails the reply to the customer in the same thread. If the email cannot go out, `channelDelivery.delivered` is `false` with a `reason`, and the reply stays in the conversation. See [Email](/docs/integrations/email.md#reply-from-chat-history). |
| A person on your team has taken over | Send a message | Success. SiteGPT saves the visitor's message, but the AI does not answer. |
| A person on your team has taken over | Escalate | `403 HUMAN_TAKEOVER`. If the conversation is already escalated, the call succeeds. |
| A person on your team has taken over | Release to AI | Success. The AI answers the conversation again. |

The OpenAPI document marks each operation that can return `409`. This includes `POST /chatbots/{chatbotId}/messages` and `POST /chatbots/{chatbotId}/conversations`, which start a new conversation, and the member, persona, instruction, tag, and transfer operations that can return it.

## Deletes need confirmation

Delete endpoints that remove account or chatbot data need `confirm=true` in the query string. The endpoint pages show a `confirm` parameter where it applies.

Example:

```bash theme={null}
curl -X DELETE "https://sitegpt.ai/api/v2/chatbots/YOUR_CHATBOT_ID/leads/YOUR_LEAD_ID?confirm=true" \
  -H "Authorization: Bearer YOUR_API_TOKEN"
```

Without it, the API returns `400 CONFIRMATION_REQUIRED` and deletes nothing. Bulk deletes also need confirmation. Send `"confirm": true` in the request body of `POST /chatbots/{chatbotId}/documents/delete`, and with the `delete` action of `POST /chatbots/{chatbotId}/conversations/bulk` and `POST /chatbots/{chatbotId}/leads/actions`. The documents endpoint also accepts `confirm=true` in the query string.

## Rate limits and quotas

* There is no published per-request rate limit. Only `429 RATE_LIMITED` comes with a rate-limit header.
* `429 CHATBOT_PAGES_LIMIT_REACHED` means that a plan quota is used up, for example the pages quota for content. Do not retry it in a loop. Remove content or change your plan. See [Plans and limits](/docs/reference/plans-and-limits.md).
* `429 MESSAGE_LIMIT_REACHED` means that the conversation reached **Limit Messages Per Conversation**. The message is not saved, and the chatbot does not answer. The error message is "This conversation reached the message limit of the chatbot (Limit Messages Per Conversation). Start a new conversation." Send the message to a new conversation with `POST /chatbots/{chatbotId}/messages`. Replies from your team are not limited. See [Limit messages per conversation](/docs/guides/answers/keep-answers-on-topic.md#limit-messages-per-conversation).
* `429 RATE_LIMITED` comes from `POST /onboarding/agent/start` when too many onboarding workspaces were started from your network or in total. The message is, for example, "Too many onboarding workspaces were started. Try again in 3 minutes." The `Retry-After` header gives the seconds to wait. Wait that long, then try again. Do not start a new workspace for each attempt. Reuse the workspace that you have.

## Model names

API v2 uses the public model names `gpt-4.1` and `gpt-4.1-mini` in requests and responses.


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