Smara API
A real-time conversational avatar you embed with one script tag, and that can operate your website using actions you taught it by clicking, not by writing code.
One tag
The widget, its knowledge and its skills load from a single script element. No build step, no npm.
Metered, not unlimited
Session seconds, synthesis characters and tool actions are counted per key, with concurrency caps.
Teach by demonstration
Name an action in plain English, click the control that performs it. The avatar replays it for visitors.
Survives redesigns
Each step stores a ranked locator ensemble and a semantic fingerprint, and repairs itself when markup changes.
Quick start
Create a key in the Studio, then paste one tag before
</body>.
<script src="https://www.smaralive.com/embed.js"
data-api-key="sk_live_…"
data-brand-name="Acme"
data-accent-color="#F97316"
data-position="bottom-right"
data-greeting="Hi, ask me anything about this page."></script>That tag is the whole install. It loads the skill runtime itself, so she can operate the page without you adding anything else, and the teach recorder is fetched only when you open your own page with a teach token, visitors never download it.
If you want her to talk but never click anything, turn the runtime off:
<script src="https://www.smaralive.com/embed.js"
data-api-key="sk_live_…"
data-actions="off"></script>Authentication
There are three credential types, deliberately separated so a leak of one is not a leak of everything.
| Credential | Prefix | Grants | Lifetime |
|---|---|---|---|
| API key | sk_live_ / sk_test_ |
Sessions, reading skills | Until revoked |
| Teach token | tk_ |
Authoring skills for one site. Cannot start sessions. | 1 hour |
| Operator token | n/a | Key management, usage across all sites | 1 hour |
Send an API key or teach token as a header, or as ?key= on WebSocket connections
where headers are impractical.
curl https://www.smaralive.com/v1/usage \
-H "X-Smara-Key: sk_live_…"Errors
Every failure returns the same envelope. Branch on code, never on the message, messages are written for humans and will change.
{
"error": {
"code": "quota_exceeded",
"message": "Monthly session seconds quota exhausted (3600 of 3600).",
"type": "rate_limit_error",
"details": { "used": 3600, "limit": 3600, "resets_in_sec": 419200 },
"request_id": "3ea9d20ff3b94c01"
}
}| Code | HTTP | Meaning |
|---|---|---|
missing_api_key | 401 | No key supplied |
invalid_api_key | 401 | Unknown or revoked |
api_key_disabled | 403 | Key exists but is switched off |
quota_exceeded | 429 | Period quota spent. Retry-After is set |
concurrency_exceeded | 429 | Too many simultaneous sessions |
trial_exhausted | 403 | Anonymous demo already used |
enforcement_unavailable | 503 | Usage could not be verified, so the request was refused rather than allowed |
skill_exists | 409 | An action with that name is already taught |
unresolved | 404 | A taught element no longer exists on the page |
SMARA_ENFORCEMENT=permissive to invert
that in development only.Embed attributes
| Attribute | Default | Notes |
|---|---|---|
data-api-key | n/a | Required. Without it the widget disables itself. |
data-brand-name | Smara AI | Shown in the panel header. |
data-accent-color | #6366f1 | Any CSS colour. |
data-position | bottom-right | bottom-left also supported. |
data-size | 64 | Collapsed bubble size in pixels. |
data-theme | dark | light also supported. |
data-greeting | empty | Silent text bubble on first load. |
data-fab-offset | 24 | Distance from the viewport edge. |
First impression
A common request is for the avatar to greet visitors out loud the moment the page opens. It is worth knowing why that mostly does not work before choosing it.
Browsers block audio until the visitor interacts with the page. Chrome, Safari and Firefox all enforce this. An avatar that tries to speak on load is silent for most people, and still spends synthesis quota generating audio nobody hears.
| Mode | Behaviour | Recommended |
|---|---|---|
badge | Silent text bubble plus a soft pulse ring. Noticed without interrupting, and unaffected by autoplay policy. | Yes, the default |
off | Sits quietly until clicked. | Low-friction pages |
voice | Attempts speech on load. | Only where arrival follows a click |
Voice and text
The widget opens in whichever mode the visitor last used, remembered per browser. Switching is non-destructive: the conversation is one thread, and moving between modes never clears it.
| Transition | What happens |
|---|---|
| Text → voice | Microphone permission is requested on the first attempt only. If denied, the widget stays in text mode and says so once rather than re-prompting. |
| Voice → text | Any in-flight speech stops immediately, the mouth returns to rest, and the transcript remains. |
| Interruption | Speaking while the avatar is talking cuts playback and starts listening, the same way people interrupt each other. |
| Tab hidden | Frames stop rendering and audio pauses, so a backgrounded tab does not burn session seconds. |
Browser events
The widget emits DOM events so you can react without polling.
window.addEventListener('smara:tool_call', e => {
// { action, args }, fired after a taught skill runs
console.log(e.detail.action, e.detail.args);
});
// Programmatic control
window.Smara.open();
window.Smara.send('What is your return policy?');
window.Smara.onAction((action, args) => { /* … */ });How skills work
A skill is recorded once by demonstration and replayed deterministically after that. The expensive reasoning happens at teach time, which runs once per site rather than once per visitor, that is what keeps runtime free and fast.
The model only ever chooses an action name from the taught list. It never receives selectors and never composes one from visitor input, so a visitor cannot talk the avatar into clicking something arbitrary.
What happens when your site changes
Each step stores a ranked ensemble of locators and a semantic fingerprint, and resolution walks tiers until one succeeds.
| Tier | Method | Typical cost | Triggered by |
|---|---|---|---|
-1 | Native platform API (Shopify, WooCommerce) | 0 ms, free | Known platform |
0 | Compiled locator | ~3 ms, free | Normal traffic |
1 | Local semantic rematch against the fingerprint | ~15 ms, free | Class names or copy changed |
2 | Server-side match, then written back | ~500 ms, once | Larger restructure |
3 | Decline and flag for re-teaching | n/a | Control genuinely removed |
Endpoints
Liveness plus which store and enforcement mode are active.
{ "status": "ok", "store": "RedisStore", "enforcement": "strict" }Call before opening a socket so the widget can show an accurate state instead of discovering refusal mid-connection. With a key it returns quota; without one it returns the anonymous demo allowance.
{ "mode": "api", "site_id": "acme",
"usage": { "session_seconds": 412.5, "tts_chars": 18320 },
"remaining": { "session_seconds": 3187.5 },
"max_concurrent": 3 }Current period usage against the key's limits.
The compiled plan the browser replays. Only enabled, non-broken skills are returned, one that stopped resolving is withheld until repaired rather than handed out to fail on every visitor.
Normally called by the recorder rather than by hand.
{
"action": "add_to_cart",
"description": "Add a product to the cart",
"steps": [{
"action": "click",
"locators": [
{ "strategy": "testid", "value": "add-to-cart" },
{ "strategy": "role_name", "value": "button|Add to Cart" }
],
"fingerprint": { "role": "button", "name": "Add to Cart" }
}],
"requires_confirmation": false
}
Actions named checkout, pay,
delete and similar are flagged for confirmation automatically, whatever this
field says.
Edit description, phrases, enabled state, or replace the steps. Supplying new steps resets the health counters, a re-taught skill starts clean.
Returns 204.
The browser reports whether a replay worked. Without this the system cannot know your site changed until someone complains; with it, a degraded skill surfaces the first time it misses.
{ "skill_id": "skl_…", "ok": false,
"error": "step 0 (click) could not be resolved" }Writes back a repaired locator, inserted at the front of the ensemble with the originals kept behind it in case a redesign is reverted.
Counts and a needs_attention list. This is what the Studio's
attention panel renders.
Operator
Exchanges the bootstrap secret for a one-hour operator token, so the Studio never holds a long-lived credential.
Key records without hashes. Digests never leave the server.
Creates a key and returns secret, the only time it exists
outside the caller's hands.
Mints a teach credential scoped to one site. The teach link is pasted into an address bar, so it must not carry a permanent key, this expires and cannot start sessions.