Reference Omeka S module

REST API

Both endpoints accept a JSON or form-encoded POST body, return JSON, and are rate limited per client IP address: 10 requests per 60 seconds by default, shared between the two endpoints. The client IP is taken from the CF-Connecting-IP header, then X-Forwarded-For, then the remote address.

The search endpoint is public. The chat endpoint runs with the configuration of the place the chat lives, which the server looks up: a Chatbot page block, named by block_id, or the floating widget. The system prompt is never taken from the request.

POST /chatbot/api/chat

Request from a Chatbot page block:

{
  "question": "What Roman artefacts are in the collection?",
  "block_id": 42
}

Request from the floating widget (no block_id):

{
  "question": "What Roman artefacts are in the collection?",
  "site_id": 1,
  "site_slug": "my-site"
}
Field Required Description
question yes 1 to 1000 characters.
block_id for blocks The id of the Chatbot page block the chat is on. The server uses that block's System prompt override (or the module's default prompt), its Restrict to item sets list and its page's site, and enforces its Allow anonymous users option. The visitor must be able to see the block's page.
site_id no Widget requests only: use evidence only from resources belonging to this site. Omit to search across all sites. Ignored with block_id.
site_slug no Widget requests only: rewrite source URLs to /s/{site_slug}/.... Ignored with block_id.
item_set_ids no Widget requests only: array of item set ids. Only chunks of items in at least one of these sets, and of those items' media, are used as evidence; blog posts and site pages are excluded when this is sent. Non-positive and non-numeric values are ignored; an empty array means no restriction. Ignored with block_id, which uses the block's own list.

Requests without block_id use the module's default system prompt and are accepted only while Enable global chat widget on all public pages is on. A system_prompt field, sent by versions before 1.5.1, is ignored.

Response (200):

{
  "answer_html": "<p>The collection holds Roman pottery<sup><a href=\"#chatbot-source-1\" class=\"chatbot-citation\">[1]</a></sup> ...</p>",
  "sources": [
    {
      "citation_num": 1,
      "title": "Roman Pottery Shard",
      "url": "/s/my-site/item/42",
      "resource_type": "item",
      "resource_id": 42,
      "snippet": "Roman Pottery Shard\n\nA fragment of ..."
    }
  ]
}

answer_html is escaped paragraph HTML in which [N] markers have become links to #chatbot-source-N. sources contains only the evidence items the model cited, in citation order. resource_type is item, media, blog_post or site_page. Snippets are the first 200 characters of the matching chunk.

POST /chatbot/api/search

Request:

{
  "query": "agricultural buildings",
  "page": 1,
  "per_page": 10,
  "site_id": 1,
  "site_slug": "my-site",
  "item_set_ids": [3, 5]
}
Field Required Description
query yes 1 to 500 characters.
page no Default 1.
per_page no Default 10, maximum 50.
site_id, site_slug no As for chat.
item_set_ids no As for chat: only items in these sets and their media are returned; blog posts and site pages are excluded.

Response (200):

{
  "results": [
    {
      "resource_type": "item",
      "resource_id": 42,
      "title": "Threshing Barn",
      "url": "/s/my-site/item/42",
      "distance": 0.2134,
      "snippet": "Threshing Barn\n\nTimber-framed barn ..."
    }
  ],
  "total": 14,
  "page": 1,
  "per_page": 10
}

distance is the cosine distance, where 0 is identical. Results are one per resource URL, sorted by distance. total is the number of distinct resources among the fetched chunks (at most 100, or 150 with a site filter), not the size of the collection. Snippets are up to 300 characters.

Errors

Status Body Cause
400 {"error": "Question is required."} and similar Missing or over-length question or query.
403 {"error": "Please log in to use the chat."} Chat: the block does not allow anonymous use and the visitor is not logged in.
403 {"error": "Chat is not available."} Chat: block_id is not a Chatbot block, or is on a page the visitor may not see; or no block_id was sent while the widget is off.
405 {"error": "Method not allowed."} The request was not a POST.
429 {"error": "Rate limit exceeded. Please try again shortly."} The per-IP limit was hit.
503 {"error": "The chat service is temporarily unavailable. Please try again later."} (search: "Search is temporarily unavailable. Please try again later.") Turso unavailable or its index needs rebuilding, an OpenAI error, or another runtime failure. The underlying error is written to the module log (chat.error, search.error), never sent to the visitor.
500 {"error": "An unexpected error occurred. Please try again."} Anything else.

Example

curl -X POST https://example.org/chatbot/api/chat \
  -H 'Content-Type: application/json' \
  -d '{"question": "Who built the mill?", "block_id": 42}'
Log in for Support