API reference

Spectare API

Complete reference for the Spectare REST API: endpoints, authentication, component types, rate limits, and error codes.

Looking for setup instructions? Integration guide →

Authentication

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.ai

All endpoints accept and return JSON. Set Content-Type: application/json on all requests.

Endpoints

POST/api/qualify/{orgSlug}No auth

Classifies 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"
}
POST/api/assemble/componentsNo auth

Assembles 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
}
POST/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
}
POST/api/conversionNo auth

Records 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 }
GET/api/contentNo auth

Your 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"
}

Script tag HTML attributes

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:

title

Replaces the element text content with the atom title. Uses textContent, not innerHTML.

content

Sets innerHTML from the atom body HTML.

cta

Sets 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.

Conversion goals

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

signup

A visitor created an account. Value defaults to 1 (count). No revenue tracking.

lead

A visitor submitted a lead form or contact request. Value defaults to 1.

purchase

A paid transaction completed. Use valueSelector to read the order total from the page.

upgrade

An existing user upgraded their plan. Use valueSelector to read the new plan price.

download

A visitor downloaded a file or resource. Value defaults to 1.

pageview

A 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.

Component types

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.

hero

Above-fold banner area

HeroStatement, ImageCtaHero

body

Main content section

AtomCard, StatGrid, FeatureList, CodeBlock, ComparisonTable

proof

Social proof section

TestimonialCard

cta

Call-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: awareness
headlinestring

Primary headline written fresh for this visitor (2-10 words)

subheadingstring?

Supporting sentence below the headline

ImageCtaHerozone: heroaudiences: any · stages: consideration, decision
headlinestring

AI-generated headline overlaid on a full-bleed background image

subheadingstring?

Supporting sentence below the headline

ctaLabelstring

CTA button label

altstring?

Alt text for the background image

CtaStripzone: ctaaudiences: any · stages: consideration, decision
headlinestring

Short 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: any
atomIdnumber

ID 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, decision
stats{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, consideration
items{text, sub}[]

Feature items extracted from the atom, each with a short feature name and supporting detail

CodeBlockzone: bodyaudiences: developer · stages: consideration, decision
codestring

Code content extracted from the atom

langstring?

Language hint for syntax highlighting

captionstring?

Optional caption below the block

ComparisonTablezone: bodyaudiences: any · stages: consideration, decision
competitorstring

Competitor 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: consideration
quotestring

Testimonial text, copied exactly as authored

namestring

Author name

rolestring

Author 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".

Component rules and overrides

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.

enabledboolean

When 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.

guidancestring

Replaces 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="..." />

Rate limits

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.

PlanAI personalizations / moAtom libraryPrice
Free50 / mo50$0 for 14 days
Starter500 / mo100$19/month
Pro2,000 / mo500$79/month
EnterpriseUnlimitedUnlimitedCustom

Error codes

400

Bad request

Missing or invalid request body. Check that required fields are present and correctly typed.

401

Unauthorized

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.

402

Payment 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.

404

Not found

The orgSlug does not match a workspace, or the workspace has no published content to assemble.

429

Rate 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.

500

Server error

An unexpected error occurred. Retrying after a short delay usually resolves transient issues. If persistent, contact [email protected].

MCP server

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/mcp

Claude Desktop config

Add to claude_desktop_config.json under mcpServers:

"spectare": {
  "type": "streamable-http",
  "url": "https://spectare.ai/api/mcp"
}

Tools

get_spectare_overview

Returns 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_visitor

Classifies 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_pricing

Returns current plan names, qualify limits, atom limits, and prices.

Prompts

about_spectare

System 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.

Ready to integrate?

Free 14-day trial, no credit card required.