Octulus indexes a website and serves an embeddable agent that answers questions from it. This page covers installation, widget options, the crawler, and the REST API.
Index your site on the demo page to get a workspace ID, then paste this before the closing </body> tag:
<script src="https://octulus.com/widget.js"
data-workspace="ws_your_workspace_id"
defer></script>
That's the whole installation. The widget renders inside a shadow root, so it can't inherit or leak CSS.
| Attribute | Default | Description |
|---|---|---|
data-workspace | — | Required. The workspace to answer from. |
data-accent | #5243E9 | Any CSS colour. Drives the bubble, header and buttons. |
data-title | Ask us anything | Header title inside the panel. |
data-greeting | Generic greeting | First message the visitor sees. |
data-position | right | right or left. |
data-open | false | Open the panel on page load. |
data-mount | — | CSS selector. Renders inline in that element instead of as a floating bubble. |
The widget exposes a small global once loaded:
Octulus.open(); // open the panel
Octulus.close(); // close it
Octulus.ask("What does this cost?"); // open and send a question
Octulus.reset(); // clear the conversation
Useful for wiring the agent to your own "Talk to us" button rather than the default bubble.
Octulus fetches your site with the user agent OctulusBot/1.0. It reads /sitemap.xml when present, otherwise follows same-origin links breadth-first to a depth of two. Scripts, styles, navigation and footers are stripped before indexing so boilerplate doesn't pollute answers.
Only public pages are indexed. Hosts resolving to private address ranges are rejected, so the crawler can't be pointed at internal infrastructure.
To exclude the crawler from parts of your site, disallow it in robots.txt:
User-agent: OctulusBot Disallow: /admin/
Each question is matched against your indexed content with BM25 ranking. The top passages — six by default — are passed to Claude Opus 5 along with a system prompt that forbids answering from anything else. Every claim gets a numbered citation resolving to the source page.
If retrieval returns nothing relevant, the agent says the site doesn't cover it and offers a human handoff. That turn is recorded as unanswered and its topic appears in your content gap report.
Base URL https://octulus.com/api. All requests and responses are JSON except the chat stream.
Start indexing a site. Returns a job ID immediately; crawling continues in the background.
curl -X POST https://octulus.com/api/index \
-H 'Content-Type: application/json' \
-d '{"url":"https://example.com"}'
{"jobId":"job_ab12cd34"}
Poll the job. status moves through crawling → indexing → ready, or lands on error.
{
"status": "ready",
"done": 18,
"total": 25,
"workspace": {
"id": "ws_xY9k2",
"name": "example.com",
"stats": { "pages": 18, "chunks": 142, "words": 24831 }
}
}
Ask a question. Responds with a Server-Sent Events stream: a meta frame carrying the conversation ID and matched sources, then delta frames of text, then done.
curl -N -X POST https://octulus.com/api/chat \
-H 'Content-Type: application/json' \
-d '{"workspaceId":"ws_xY9k2","message":"What does this cost?"}'
event: meta
data: {"conversationId":"cv_7fQ","sources":[{"n":1,"title":"Pricing","url":"..."}]}
event: delta
data: {"text":"Plans start at "}
event: done
data: {"conversationId":"cv_7fQ"}
Record a human-handoff request against a conversation.
curl -X POST https://octulus.com/api/lead \
-H 'Content-Type: application/json' \
-d '{"workspaceId":"ws_xY9k2","email":"[email protected]","note":"wants a demo"}'
Metrics, recent conversations, captured leads and ranked content gaps for a workspace.
Service status, active model and whether the Claude connection is configured.
| Endpoint | Limit |
|---|---|
POST /api/index | 4 per minute per IP |
POST /api/chat | 20 per minute per IP |
POST /api/lead | 10 per minute per IP |
Exceeding a limit returns 429 with a Retry-After header.
Email [email protected].