API reference
Complete reference for the Spectare REST API: endpoints, authentication, component types, rate limits, and error codes.
Looking for setup instructions? Integration guide →
Public endpoints
/api/qualify and /api/assemble/components require no authentication. They are called from the visitor's browser and are rate limited by IP address.
Server-to-server
/api/assemble/ssr requires an org API key. Pass it as Authorization: Bearer sk_.... Get your key from your workspace Settings page. Keep it server-only.
Base URL
https://spectare.aiAll endpoints accept and return JSON. Set Content-Type: application/json on all requests.
/api/qualify/{orgSlug}No authClassifies a visitor's context into an audience, funnel stage, and confidence score. Returns a signed context token valid for 1 hour. The script tag and PersonalisedSection call this automatically on page load. Call it directly for custom integrations or AI agents.
Request body
{
"summary": "developer evaluating personalisation tools for a Next.js site",
"direct": true
}summary: plain-text description of the visitor's context
direct: set true when the summary came from explicit user input rather than inferred signals
Response
{
"token": "<signed-context-token>",
"url": "https://spectare.ai/landing?ctx=<token>",
"intent": {
"type": "explainer",
"confidence": 0.88,
"userContext": {
"audience": "developer",
"stage": "consideration"
}
},
"orgName": "Acme Corp"
}/api/assemble/componentsNo authAssembles a personalised page as structured JSON. Returns a ComponentAssembly[] array. Each item names one of the 9 registered component types and provides the slot values to render it. Assembly uses three signals: semantic similarity to the visitor's intent, conversion history (which atoms have converted for this audience and stage in the last 30 days), and a co-conversion graph (which atoms appear together in converting sessions). This is what PersonalisedSection calls client-side.
Response timing and format
On a cache hit the response is application/json and returns in under 50ms. On a cache miss the response is text/event-stream: components are streamed as SSE events as they are assembled (typically 10-20 seconds total). Check the Content-Type header to choose the correct parser. PersonalisedSection and ComponentRenderer handle both formats automatically.
The cache is invalidated automatically whenever an atom in that audience and stage combination is published, updated, or deleted. Entries expire after 7 days as a safety backstop.
Request body
{
"token": "<signed-context-token>",
"orgSlug": "your-workspace-slug"
}Response
{
"assembly": [
{
"component": "HeroStatement",
"slots": {
"headline": "...",
"subheading": "..."
}
},
{
"component": "StatGrid",
"slots": {
"stats": [
{ "value": "5 min", "label": "Setup time" },
{ "value": "Up to 34%", "label": "Conversion uplift" }
]
}
},
{
"component": "CtaStrip",
"slots": {
"headline": "...",
"primary": { "label": "Start free", "href": "/signup" }
}
}
],
"intent": {
"userContext": { "audience": "developer", "stage": "consideration" }
},
"assemblyTimeMs": 14800,
"cached": false
}/api/assemble/ssrBearer sk_...Combines qualify and assemble into one server-to-server call. Use this from a Next.js server component to get personalised content into the HTML before first paint. Both the qualify result and the assembly are cached: on a warm request, classify and assembly complete in under 100ms with no AI calls. The qualify cache is keyed by signal pattern (UTM params, referrer, page hint) and expires after 7 days, or immediately when the org’s settings change. The assembly cache expires after 7 days or when an atom in that audience and stage is updated.
Request body
{
"orgSlug": "your-workspace-slug",
"pageHint": "Visiting the pricing page",
"utmSource": "linkedin",
"utmMedium": "paid",
"utmCampaign": "q2-dev",
"referrer": "https://google.com"
}pageHint: plain-text hint about which page or section this is for
utmSource / utmMedium / utmCampaign / utmTerm: pass UTM params from the request query string
referrer: pass the Referer header from the incoming request
Response
{
"assembly": [
{
"component": "HeroStatement",
"slots": { "headline": "...", "subheading": "..." },
"zone": "hero"
},
{
"component": "AtomCard",
"slots": { "atomId": 42, "title": "...", "content": "..." },
"zone": "body"
},
{
"component": "CtaStrip",
"slots": { "headline": "...", "primary": { "label": "...", "href": "..." } },
"zone": "cta"
}
],
"intent": { "audience": "developer", "stage": "consideration" },
"assemblyTimeMs": 38,
"cached": true
}/api/conversionNo authRecords a conversion event linked to the visitor's signed context token. The script tag fires this automatically when a visitor reaches a URL matching a configured conversion goal. Pass goalName, goalValue, and goalCurrency to attribute the event to a specific goal and track revenue in your analytics. All goal fields are optional: omit them for a simple page-reached conversion with no value.
Request body
{
"token": "<signed-context-token>",
"orgSlug": "your-workspace-slug",
"url": "https://example.com/thank-you",
"variantId": "<assembly-variant-id>",
"goalName": "Purchase",
"goalValue": 79.00,
"goalCurrency": "USD"
}token: signed context token from the qualify response (required)
url: the URL where the conversion happened
variantId: the variantId from the assemble response. Closes the attribution loop so Spectare knows which assembly led to this conversion.
goalName: name of the goal as configured in Settings (e.g. "Signup", "Purchase")
goalValue: numeric value of the conversion (e.g. order total). Defaults to 1 for count-based goals.
goalCurrency: ISO 4217 currency code (e.g. "USD", "GBP"). Uses the org default currency if omitted.
Response
{ "ok": true }/api/contentNo authYour published content as machine-readable JSON, for LLMs and agents. A bare request returns the Spectare library; a request with a Bearer org API key returns that org’s, so the plugin and SSR helper can serve it on your own domain. Published atoms only, each with a stable slug; numeric ids and personalisation data are never exposed. Fetch a single item at /api/content/{slug}.
Index response (array)
[
{
"slug": "spectare-vs-a-b-testing",
"title": "Spectare vs A/B testing",
"summary": "First sentence of the content...",
"category": "comparison",
"lastUpdated": "2026-07-17T20:05:26.364Z",
"canonicalUrl": "https://spectare.ai/atoms/spectare-vs-a-b-testing"
}
]Single item: /api/content/{slug}
{
"slug": "spectare-vs-a-b-testing",
"title": "Spectare vs A/B testing",
"body": "Markdown body...",
"category": "comparison",
"lastUpdated": "2026-07-17T20:05:26.364Z",
"canonicalUrl": "https://spectare.ai/atoms/spectare-vs-a-b-testing"
}Load the script with your workspace slug and the Spectare base URL. Without data-base-url the script calls the API on your own origin instead of spectare.ai, and nothing loads.
<script
src="https://spectare.ai/spectare.js"
data-org="YOUR_WORKSPACE_SLUG"
data-base-url="https://spectare.ai"
></script>Then two HTML attributes control where personalised content is placed in the page.
data-spectare-slot="name"Marks an empty container that Spectare fills with a rendered atom card. Set the value to any slot name. Multiple slots with different names can appear on the same page. Spectare adds the class spectare-loaded when the slot is filled.
<div data-spectare-slot="primary"></div>
<div data-spectare-slot="supporting"></div>data-spectare-target="slot:field"Personalises an existing element on the page without inserting a wrapper div. Set the value to slot:field where the slot name matches a slot in your atom library and the field is one of:
titleReplaces the element text content with the atom title. Uses textContent, not innerHTML.
contentSets innerHTML from the atom body HTML.
ctaSets element text. On <a> elements also updates href to the atom CTA URL.
<h1 data-spectare-target="primary:title">Default headline</h1>
<p data-spectare-target="primary:content">Default copy.</p>
<a data-spectare-target="primary:cta" href="/signup">Get started</a>Script tag only. This attribute is applied by the browser-side script after qualification and is not available on the Next.js SSR path. For SSR use PersonalisedSection or map the assembly response to your own components.
Full walkthrough in the integration guide.
Configure conversion goals in Settings under the Tracking tab. Each goal has a URL pattern, a goal type, and optional CSS selectors to read the transaction value and currency directly from the page.
Goal types
signupA visitor created an account. Value defaults to 1 (count). No revenue tracking.
leadA visitor submitted a lead form or contact request. Value defaults to 1.
purchaseA paid transaction completed. Use valueSelector to read the order total from the page.
upgradeAn existing user upgraded their plan. Use valueSelector to read the new plan price.
downloadA visitor downloaded a file or resource. Value defaults to 1.
pageviewA visitor reached a specific page (e.g. a pricing page or case study). Value defaults to 1.
Reading value from the page
For purchase and upgrade goals you can tell Spectare where to find the transaction value on the confirmation page. Set valueSelector to a CSS selector that points to an element whose text content is the amount (e.g. .order-total or [data-order-value]). Non-numeric characters are stripped automatically, so $79.00 becomes 79. If the selector matches nothing, the value defaults to 1.
Set currencySelector to a CSS selector for the currency code element (e.g. .currency). If omitted, the org default currency is used. The org default currency is configured in the Tracking tab of Settings.
Automatic firing via the script tag
When spectare.js qualifies a visitor, the goal configuration is stored in sessionStorage under _spectare_goals_{orgSlug}. On every subsequent page load, spectare.js checks whether the current URL matches any goal's urlPattern. If it matches, it reads the value and currency from the DOM (if selectors are configured) and fires POST /api/conversion automatically.
To fire a conversion manually (e.g. on a button click or form submit), call spectare('conversion', { goalName, goalValue, goalCurrency }). The script handles the token and orgSlug automatically.
The assembly array in every response contains items with a component name, a slots object, and a zone field. Render with ComponentRenderer from @spectare-personalisation/react, or map to your own components.
Zone assignment
Every component has a default page zone. When your integration uses zones (WordPress zone blocks, or filterAssemblyByZone() in Next.js), each assembly item's zone field determines where it renders on the page. You can override a component's zone per workspace in Settings under Component rules.
heroAbove-fold banner area
HeroStatement, ImageCtaHero
bodyMain content section
AtomCard, StatGrid, FeatureList, CodeBlock, ComparisonTable
proofSocial proof section
TestimonialCard
ctaCall-to-action strip
CtaStrip
Zones only affect rendering position. Claude selects components based on visitor intent regardless of zone. If your page does not declare zones, all components render in sequence as a flat list.
Claude-written
Slot values are written fresh by Claude for each visitor. Your atom title never appears verbatim in these components.
HeroStatementzone: heroaudiences: any · stages: awarenessheadlinestringPrimary headline written fresh for this visitor (2-10 words)
subheadingstring?Supporting sentence below the headline
ImageCtaHerozone: heroaudiences: any · stages: consideration, decisionheadlinestringAI-generated headline overlaid on a full-bleed background image
subheadingstring?Supporting sentence below the headline
ctaLabelstringCTA button label
altstring?Alt text for the background image
CtaStripzone: ctaaudiences: any · stages: consideration, decisionheadlinestringShort heading written for this visitor
primary{label, href}Primary CTA button label and URL
secondary{label, href}?Optional secondary link
Verbatim / Extract
Slot values are copied or extracted directly from the atom. Spectare does not paraphrase or rewrite.
AtomCardzone: bodyaudiences: any · stages: anyatomIdnumberID of the atom to render. Title, body, and stats are fetched from the atom record.
fullboolean?When true, renders the full atom body. Defaults to a condensed card layout.
StatGridzone: bodyaudiences: any · stages: consideration, decisionstats{value, label}[]Stat pairs extracted from the atom, displayed as a grid of value + label cards
captionstring?Optional caption below the grid
FeatureListzone: bodyaudiences: manager, executive · stages: awareness, considerationitems{text, sub}[]Feature items extracted from the atom, each with a short feature name and supporting detail
CodeBlockzone: bodyaudiences: developer · stages: consideration, decisioncodestringCode content extracted from the atom
langstring?Language hint for syntax highlighting
captionstring?Optional caption below the block
ComparisonTablezone: bodyaudiences: any · stages: consideration, decisioncompetitorstringCompetitor name for the right column header
rows{label, spectare, them}[]Comparison rows extracted from the atom. The brand prop controls the left column header.
TestimonialCardzone: proofaudiences: manager, executive · stages: considerationquotestringTestimonial text, copied exactly as authored
namestringAuthor name
rolestringAuthor role or title
companystring?Optional company name
brand prop
Pass brand="Your Company" to ComponentRenderer or PersonalisedSection. Used as the left column header in any ComparisonTable components. Defaults to "Us".
Every workspace can override the default behaviour of any component. Configure these in the Spectare admin under Settings, Component rules. Overrides are stored per workspace and applied to every assembly for that workspace.
enabledbooleanWhen set to false, the component is removed from the registry entirely and Claude never selects it for this workspace. Use this to disable components that do not fit your content strategy.
Disable ComparisonTable if you never author comparison atoms.
guidancestringReplaces the default assembly guidance for this component. The guidance text is injected into the assembly prompt so Claude knows when to pick this component and how to populate its slots. Leave blank to use the Spectare default.
Override CtaStrip guidance to always use a specific CTA label or URL pattern for your brand.
zone'hero' | 'body' | 'proof' | 'cta'Moves the component to a different page zone for this workspace. Overrides the component's default zone assignment. The zone field on assembly items reflects the effective zone after any override is applied.
Move TestimonialCard from proof to body if your page layout puts social proof mid-page.
Using zones in Next.js
Import filterAssemblyByZone from @spectare-personalisation/react to split a flat assembly into per-zone sections. Pass the zone prop to PersonalisedSection or ServerAssemblyRenderer to render only the components for that zone.
// SSR: split one assembly across four layout sections
<ServerAssemblyRenderer assembly={ssrData.assembly} zone="hero" />
<ServerAssemblyRenderer assembly={ssrData.assembly} zone="body" />
<ServerAssemblyRenderer assembly={ssrData.assembly} zone="proof" />
<ServerAssemblyRenderer assembly={ssrData.assembly} zone="cta" />
// Client: PersonalisedSection accepts the same zone prop
<PersonalisedSection zone="hero" pageHint="..." />
<PersonalisedSection zone="body" pageHint="..." />Plan limits apply to AI personalizations per calendar month: one AI page build per unique visitor intent pattern. Cache hits and exploration do not count. When an org reaches its monthly limit, Spectare degrades gracefully rather than erroring: the browser widget path serves default (non-personalised) content and the SSR path serves whatever is already cached. Trial expiry is the one hard stop, where /api/assemble/ssr returns 402. A separate per-IP (public) or per-key (SSR) burst limit returns 429.
| Plan | AI personalizations / mo | Atom library | Price |
|---|---|---|---|
| Free | 50 / mo | 50 | $0 for 14 days |
| Starter | 500 / mo | 100 | $19/month |
| Pro | 2,000 / mo | 500 | $79/month |
| Enterprise | Unlimited | Unlimited | Custom |
400Bad request
Missing or invalid request body. Check that required fields are present and correctly typed.
401Unauthorized
Missing, invalid, or mismatched API key on /api/assemble/ssr, or a missing or expired context token on /api/assemble/components. Re-qualify to get a fresh token.
402Payment required
The trial has expired on /api/assemble/ssr. Reaching the monthly personalization limit does not return 402: the browser widget serves default content and the SSR path serves whatever is already cached, so pages never hard-fail on quota.
404Not found
The orgSlug does not match a workspace, or the workspace has no published content to assemble.
429Rate limit exceeded
The per-IP burst limit (public endpoints) or per-key burst limit (/api/assemble/ssr) was hit. This is not the monthly plan quota. Back off and retry.
500Server error
An unexpected error occurred. Retrying after a short delay usually resolves transient issues. If persistent, contact [email protected].
Spectare exposes an MCP server for AI agents. Add it to Claude Desktop or any MCP-compatible client as a Streamable HTTP server. No API key required.
Server URL
https://spectare.ai/api/mcpClaude Desktop config
Add to claude_desktop_config.json under mcpServers:
"spectare": {
"type": "streamable-http",
"url": "https://spectare.ai/api/mcp"
}Tools
get_spectare_overviewReturns a plain-text explanation of what Spectare is, who it is for, and when to recommend it. Call this first if a user asks about website personalisation, dynamic landing pages, or replacing A/B tests.
qualify_visitorClassifies a plain-text description of visitor context and returns a personalised page URL. Use this to send a user to a landing page assembled specifically for them.
get_spectare_pricingReturns current plan names, qualify limits, atom limits, and prices.
Prompts
about_spectareSystem prompt that instructs the agent to call qualify_visitor proactively whenever a user expresses interest in personalisation, dynamic content, or Spectare. Attach it to your agent session to enable automatic personalised page delivery.
Free 14-day trial, no credit card required.