DenserAIDenserAI Docs
API

API Documentation

Use Denser.ai's API to query a chatbot programmatically, keep conversations per end user, and list them. Build custom chat UIs and connect your own apps.

Query a Denser chatbot from your own code, keep each of your users' conversations, and read them back. Before you start, generate an API key and copy your chatbot ID — see the REST API quick start.

EndpointWhat it does
POST /api/queryAsk the chatbot a question and get its answer, optionally as one of your users and within an ongoing conversation.
POST /api/v2/conversations/apiList the chatbot's conversations, optionally only one user's, with their messages.

Authentication

Each chatbot has its own API key, generated in its API channel (see the quick start). Regenerating the key immediately invalidates the old one.

  • /api/query takes the key in the key field of the JSON body.
  • /api/v2/conversations/api takes the key in the x-api-key header.

Keep the key on your server

Call the API from your backend, never from a browser. The key gives access to the chatbot and its conversations, and the userId you send is trusted because the key vouches for it.

Both endpoints stop working when the chatbot's API channel is turned off. Every answer from /api/query uses one query from your workspace's plan.

Query a chatbot

Endpoint

POST https://denser.ai/api/query

Send the body as Content-Type: application/json.

Request body

Prop

Type

Example request

curl -X POST "https://denser.ai/api/query" \
  -H "Content-Type: application/json" \
  -d '{
    "question": "What can you help me with?",
    "chatbotId": "11111111-1111-1111-1111-111111111111",
    "key": "00000000-0000-0000-0000-000000000000",
    "userId": "user_8842"
  }'

Response

A successful request returns HTTP 200 with:

Prop

Type

{
  "statusCode": "200",
  "answer": "I can help you with questions about our products, orders and returns...",
  "passages": [
    {
      "source": "https://example.com/help/returns",
      "text": "Returns are accepted within 30 days of delivery...",
      "score": 7.904721
    }
  ],
  "conversationId": "1791582688917-qzltde6lii",
  "messageId": "1791582688915-hq4r1oxjrz"
}

Errors

Some outcomes are reported in the body's statusCode with HTTP 200; others use the HTTP status.

HTTPstatusCode / bodyWhen
200"404", answer "Chatbot not found"The chatbot does not exist.
200"404", answer "Conversation not found"conversationId is not this chatbot's, or not the same userId's.
200"429", answer "Quota exceeded"The workspace has used all its queries.
400message lists the invalid fieldsA field is missing or invalid, such as a userId over 256 characters.
401"Invalid API Key"The key does not match the chatbot.
500"Internal Server Error"Anything else, including a chatbot whose API channel is turned off.
{
  "statusCode": 400,
  "error": "Bad Request",
  "message": ["question must be a string"]
}

End users and conversations

Pass userId to tie each question to one of your users, and conversationId to keep a conversation going. Both are optional: without them every call is a conversation of its own.

  1. Ask, with your user's ID. The response's conversationId and messageId identify the conversation and the answer.

    { "chatbotId": "…", "key": "…", "question": "What is your refund policy?", "userId": "user_8842" }
  2. Follow up in the same conversation by sending its conversationId with the same userId. Without context, the conversation's recent turns are the history, so a follow-up such as "Does that apply to sale items?" is understood.

    { "chatbotId": "…", "key": "…", "question": "Does that apply to sale items?", "userId": "user_8842", "conversationId": "1791582688917-qzltde6lii" }
  3. Record feedback on your side, such as thumbs up or down, keyed to the answer's messageId.

  4. List the user's conversations with List conversations and their userId.

Conversations made through the API also appear in the chatbot's Chat logs, with the userId as the client ID.

List conversations

Endpoint

POST https://denser.ai/api/v2/conversations/api

Send the API key in the x-api-key header and the filters as a JSON body. Up to 60 requests a minute per key.

Request body

Prop

Type

Example request

curl -X POST "https://denser.ai/api/v2/conversations/api" \
  -H "Content-Type: application/json" \
  -H "x-api-key: 00000000-0000-0000-0000-000000000000" \
  -d '{ "chatbotId": "11111111-1111-1111-1111-111111111111", "userId": "user_8842" }'

Response

{
  "success": true,
  "data": {
    "conversations": [
      {
        "id": "1791582688917-qzltde6lii",
        "title": "",
        "status": "active",
        "clientType": "api",
        "userId": "user_8842",
        "currentAgent": "bot",
        "createdAt": "2026-10-09T18:24:48.917Z",
        "updatedAt": "2026-10-09T18:26:05.403Z",
        "lastMessage": { "content": "…", "role": "assistant", "createdAt": "2026-10-09T18:26:07.120Z" },
        "messages": [
          { "id": "1791582688917-hihu5fongia", "role": "user", "content": "What is your refund policy?", "createdAt": "2026-10-09T18:24:48.917Z" },
          { "id": "1791582688915-hq4r1oxjrz", "role": "assistant", "content": "…", "createdAt": "2026-10-09T18:24:52.310Z" }
        ]
      }
    ],
    "pagination": { "nextCursor": null, "hasMore": false, "count": 1, "limit": 20 }
  }
}

Each conversation has userId when one was sent (or the website widget's client ID), and each message carries userFeedback when a visitor rated it in the widget. When pagination.hasMore is true, pass pagination.nextCursor back as cursor for the next page.

Errors

Errors have success: false and an error.code:

HTTPerror.codeWhen
400—A filter is invalid, such as a limit over 100.
401INVALID_API_KEYThe key does not match the chatbot.
401DATE_RANGE_EXCEEDEDcreatedAfter to createdBefore spans more than 90 days.
401REQUEST_FAILEDThe request could not be completed; error.message says why.
403CHANNEL_DISABLEDThe chatbot's API channel is turned off.
429—More than 60 requests in a minute.