Website SDK reference
Every function, property and option of the website loader's GraineAgent, grouped by what you use it for: putting the agent on a page, identity and memory, the chat, booking a call with the widget's sheet, scheduled calls, screen context and actions, and errors.
Everything the website loader, https://www.graine.ai/embed/v1.js, puts on window.GraineAgent, grouped by what you use it for. Each function comes with what it is for, its signature, its parameters, what it returns, the errors it can raise and an example. The loader is served, not installed: the script at /embed/v1.js is always the current v1, so a website has nothing to upgrade, and GraineAgent.version reports the build it is running (currently "1.2.0"). The same API across every platform, topic by topic, is the SDK reference; the widget in full is Embed on your website.
Requirements, install, permissions and initialisation
| Loader | https://www.graine.ai/embed/v1.js — always the current v1, nothing to upgrade; plain JavaScript, no build step, no dependencies |
|---|---|
| Requires | The page's host in the agent's Allowed domains (Agent → Embed & Widgets). A site with a Content-Security-Policy allows https://www.graine.ai in script-src, connect-src and frame-src: the loader fetches from that address and shows the widget in a frame from it. |
| Microphone | The widget's frame asks for the microphone (allow="microphone") when the visitor starts a voice conversation, so a Permissions-Policy must not deny it to that frame. The browser shows its own prompt; nothing to add. |
| Storage | The loader keeps this browser's install ID and the visitor's memory choice in localStorage; where storage is refused, they last for the page's life. |
HTML — before </body>, on every page the agent belongs on
The widget is a launcher button and a panel in a frame of its own, so your page's CSS cannot change it and its code cannot touch your page. It draws the agent's design from the Garage and loads its own typeface. Everything the page can call — identify, setScreen, defineAction, scheduleCall, openBooking — is safe before load(): it waits until the agent is on the page.
Install and the first answer
Put the agent on the page, know when it is there, and take it off again.
GraineAgent.load
Call it once on every page the agent belongs on, to put the launcher and the panel on the page.
It checks the publishable key with Graine before it draws anything, so a misconfigured agent never shows a launcher that opens onto an error. Called again with the same agent — a route re-rendering — it is the same load and returns the same promise. Called with another agent, it takes the one on the page off first (its frame, launcher and conversation) and puts the new one on; the visitor stays identified. Requests made meanwhile wait for the new agent.
The options it reads:
| Option | Type | Default | Meaning |
|---|---|---|---|
agentId | string | required | Which agent to show: the website agent's ID from Agent → Embed & Widgets → Install. |
publishableKey | string | required | The agent's publishable key (pk_live_…), from the same place. It identifies the agent and is safe in page source. |
variables | object | none | Prompt variables for this page's conversations, by name — up to 12, about 1 KB in all. The visitor's traits from identify() are added to them. |
mode | "both" | "voice" | "chat" | "both" | Which conversations the widget offers: a chat and a voice conversation with a switch between them, or only one. |
chatSize | "compact" | "large" | "full" | the design's | How big the panel opens on this page, over the agent's Window size in the Garage. open({ size }) overrides it for one open. |
accent | string (#RRGGBB) | "#318CE7" | The launcher's colour when the agent has no accent of its own in the dashboard. |
position | "bottom-right" | "bottom-left" | "bottom-right" | Which corner the launcher sits in when the dashboard does not say. |
launcherLabel | string | "Chat with us" | What a screen reader announces for the launcher (and the words on a pill-shaped launcher) when the dashboard does not say. |
| Parameter | Type | Default | Meaning |
|---|---|---|---|
options | object | required | The options above. agentId and publishableKey are required; everything else is optional. |
Returns — Promise<boolean> — true once the agent is on the page; false when it could not start (the browser console says why) or another load() came first. Example value: true.
Errors — it never rejects. Missing agentId or publishableKey, a refused key or a refused page origin resolve false and log [Graine] … in the console; the visitors see nothing.
GraineAgent.destroy
Call it when the agent should leave the page — a route without it, or a sign-out screen.
Takes the agent off the page: its frame, its launcher and the conversation in it. load() puts one back, the same agent or another. What belongs to the page stays: who the visitor is, this browser's install ID, the memory choice, the screen and the page's actions.
Parameters — none.
Returns — nothing.
Errors — none.
GraineAgent
Use it to reach everything the loader offers; it exists as soon as the script runs.
window.GraineAgent
One object for the whole page. Every function on it exists before load() is called; what needs the agent waits until the agent is on the page. One agent is on the page at a time.
What to expect — The properties below are read-only. schedulingRules, bookingAvailable and installId are read each time you ask, so read them again after load() or identify() rather than keeping a copy.
| Property | Type | Meaning |
|---|---|---|
version | "1.2.0" | Read it to know which loader build the page is running — include it when you ask for support. |
switchesAgents | true | Read it to know that load() with another agent switches agents in place; a loader without it needs a page reload to change agent. |
installId | string | Read it for this browser's install ID — the same value getInstallId() returns. |
schedulingRules | object | Read it to know what the agent lets visitors book; see GraineAgent.schedulingRules. |
bookingAvailable | boolean | Read it to decide whether to offer your own “Call me” button; see GraineAgent.bookingAvailable. |
Its functions: load, destroy, identify, setMemory, getInstallId, memories, forgetMemory, newConversation, open, close, defineAction, removeAction, setScreen, addMaskRule, reportEvent, openBooking, scheduleCall, scheduledCalls, cancelScheduledCall — each with its parameters, what it returns, its errors and an example.
Identity, memory and conversations that continue
Tell the agent who the visitor is, so a returning visitor's conversation continues where they left it and the agent can remember them — or not, as they choose. Where the agent remembers its users, an identified visitor who comes back finds the widget's chat where they left it: the earlier messages under day separators, an earlier voice conversation as one “Voice call” row, and New conversation in the chat's header to start afresh.
GraineAgent.identify
Call it after your visitor signs in, and with null when they sign out.
The trait your agent's Who is the user? setting names (user_id unless you chose email, phone or a custom trait) is how Graine tells your visitors apart: it lets the agent remember a visitor and lets a booked call ring their app. Its value goes only to Graine and never becomes a prompt variable; every other trait is masked in the browser and reaches the agent as a prompt variable. A placeholder value ("guest", "0", an empty string, a masked value) identifies nobody.
It applies to the next conversation. null signs the visitor out of every agent they used on this page: this browser stops counting as one of their devices, and later conversations are anonymous. Identifying a different visitor signs the previous one out the same way. Safe before load().
| Parameter | Type | Default | Meaning |
|---|---|---|---|
traits | object | null | required | Who the visitor is, by trait name — { user_id, name, plan }. null signs out. |
options | { memory?: boolean } | none | { memory: false } keeps this visitor's conversations out of memory (neither remembered nor recalled); { memory: true } turns it back on. Left out, their last choice in this browser stands — on until they make one. |
Returns — nothing.
Errors — none. A sign-out that cannot reach Graine logs a warning in the console; the browser is released anyway after thirty days without use.
GraineAgent.setMemory
Call it when the visitor changes their memory choice in your privacy settings.
The same as identify() with the same traits and { memory: enabled }: the choice is kept in this browser for this visitor and applies to their next conversation.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
enabled | boolean | required | true to remember this visitor's conversations, false to stop. |
Returns — nothing.
Errors — none.
GraineAgent.getInstallId
Call it when you need this browser's install ID — to show in a support screen, for example.
A random ID made the first time the loader runs in this browser and kept in localStorage; where storage is refused, it lasts for the page's life. Graine counts a visitor's devices by it; the agent never sees it. The installId property returns the same value.
Parameters — none.
Returns — string — a UUID. Example value: "9b2e7c4a-1f3d-4e8a-b6c5-0d7f2a9e4b13".
Errors — none.
GraineAgent.memories
Call it to show the visitor what the agent remembers about them, on a privacy screen.
What the agent remembers about the identified visitor, newest first, at most 100, already redacted. Only from a browser that is one of the visitor's devices. It waits for load() to have the agent's settings.
Parameters — none.
Returns — Promise<Array<{ id, text, createdAt, updatedAt }>> — id and text strings; createdAt and updatedAt are Dates, or null when Graine did not say. Example value: [{ id: "m_31", text: "Prefers a call after 6 PM", createdAt: Date("2026-10-12T09:15:00Z"), updatedAt: null }].
Errors — rejects with a GraineError (Errors): identity_required ("Nobody is identified. Call GraineAgent.identify() with the visitor's id first.") when nobody is identified; device_limit from a browser that is not one of the visitor's devices; rate_limited, network, unavailable, invalid_key, origin_refused.
GraineAgent.forgetMemory
Call it when the visitor asks the agent to forget them.
Deletes everything the agent remembers about the identified visitor, and the conversation it kept for them. Only when it succeeds does the widget's chat clear; a refusal leaves it as it was.
Parameters — none.
Returns — Promise<number> — how many memories were deleted. Example value: 3.
Errors — rejects with a GraineError: identity_required, device_limit, rate_limited, network, unavailable, invalid_key, origin_refused.
GraineAgent.newConversation
Call it to give the visitor a fresh conversation instead of continuing the last one.
Graine lets go of the conversation it kept for this visitor and agent, and the widget's chat clears and opens fresh, with the welcome again. Memories the agent keeps about the visitor stay as they are. With nobody identified nothing was kept, so only the chat starts over. The chat's own New conversation button does the same.
Parameters — none.
Returns — Promise<void> — resolves once the chat has started over.
Errors — rejects with a GraineError (device_limit, rate_limited, network, unavailable, invalid_key, origin_refused); the chat is then as it was.
Chat
The widget's panel holds the chat (and, where offered, the voice conversation). Open it from your own buttons, at the size you want. The chat shows the visitor's booked calls at the top, with Book a call where booking is available — see Booking with the SDK's sheet.
GraineAgent.open
Call it from your own “Chat with us” or “Talk to us” button to open the panel.
Nothing happens before load() has put an agent on the page. On a computer, compact is the panel above the launcher and large and full open a bigger panel (420 wide, up to 760 tall); on a page narrower than 640 px, large is a sheet over most of the page and full the whole page, and the page behind stops scrolling while it is open. Escape, the panel's close control and a tap on the dimmed page close it.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
options | { mode?: "chat" | "call", size?: "compact" | "large" | "full" } | none | mode picks the tab the panel opens on, among those the agent offers. size is for this open only; it wins over load({ chatSize }) and the agent's Window size. |
Returns — nothing.
Errors — none.
GraineAgent.close
Call it to close the panel from your own code — after a task is done, for example.
Closes the panel. A booking sheet open in it closes too, without booking (openBooking() resolves null).
Parameters — none.
Returns — nothing.
Errors — none.
The voice bar
Not on this platform as a separate view: on a website, talking to the agent happens in the widget's panel, on its call tab — open({ mode: "call" }) opens it there, and load({ mode }) decides whether it is offered. The browser asks the visitor for the microphone when the conversation starts. Captions, the connection's state and the call's controls are drawn inside the panel; the loader has no API for them.
Several agents in one app
A page shows one agent at a time. A single-page app that wants a support agent in its help pages and another agent elsewhere calls load() again with the other agent on that route: the agent on the page is taken off first (its frame, launcher and conversation) and the new one is put on, and load() resolves true once it is there. GraineAgent.switchesAgents says the loader can do this. Who the visitor is, the install ID, the memory choices, the screen and the page's actions belong to the page and carry over; identify(null) signs the visitor out of every agent they used on the page. destroy() takes the agent off without another.
Screen context, actions and components
Tell the agent what the visitor is looking at, and let it act on your page through functions you write. The agent's components (product lists, choices, time slots) are drawn inside the widget; the page needs no code for them.
GraineAgent.setScreen
Call it whenever what the visitor can see changes, so the agent talks about the right thing.
context is { screen, title, journey, fields, error, data, availableActions }: a stable ID for the screen, its title, where it sits in a flow ({ name, step, of, next }), its fields ({ name, label, value, status, error, optional } — status is "empty", "filled", "invalid", "pending" or "locked"), a screen-level error, anything else the agent should know, and which of your actions work here. Report real values: card numbers, phone numbers, email addresses and similar are masked in the browser before anything leaves the page, and your own rules from addMaskRule() apply too. The first change goes at once and later ones at most every 400 ms; before the widget exists the latest screen is kept and sent when it does. null clears it.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
context | object | null | required | What the visitor can see now, as above. |
Returns — nothing.
Errors — none.
GraineAgent.defineAction
Call it to let the agent do one thing on your page — open a section, apply a filter, fill a form.
Declare the action on the agent in the dashboard too: a handler with no declaration is never offered, and a declared action this page does not define is refused with “This page does not implement "…".”. The agent can call only the functions you define here. handler(args) receives the agent's arguments and returns { status, message, data } — or a promise of it — where status is "ok" (the default when you leave it out), "refused" or "error", and message is what the agent is told. Answer within 6 seconds — the SDKs' deadline: after that the agent is told “The page did not finish "…" in time.” and a later answer is dropped. A handler that throws answers error with its message. Defining a name again replaces the handler. Safe before load().
| Parameter | Type | Default | Meaning |
|---|---|---|---|
name | string | required | The action's name, exactly as it is declared on the agent. |
handler | (args) => result | Promise<result> | required | Runs the action and says how it went: { status: "ok" | "refused" | "error", message, data }. Return ok only when it really happened. |
Returns — nothing.
Errors — none. A call without a name or a function logs [Graine] defineAction(name, handler) needs both. in the console.
GraineAgent.removeAction
Call it when an action stops making sense — a section unmounting, for example.
The agent stops being offered the action; a request for it afterwards is refused.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
name | string | required | The action's name, as it was defined. |
Returns — nothing.
Errors — none.
GraineAgent.addMaskRule
Call it once at start-up to mask a value shape only your site knows — an account number, say.
Adds a rule applied after the built-in ones (card numbers, government ID numbers, phone numbers, email addresses) to everything the screen context and identity carry, before it leaves the page. Every match is replaced, whether or not the pattern has the g flag.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
pattern | RegExp | required | What to mask. |
token | string | "[REDACTED]" | What replaces each match. |
Returns — nothing.
Errors — none. A pattern that is not a RegExp logs [Graine] addMaskRule needs a RegExp. in the console.
GraineAgent.reportEvent
Call it when something happens that the agent may want to speak up about — a payment failing, a form being rejected.
The event reaches the conversation that is live in the widget. With no agent on the page it is dropped, not kept.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
event | { name: string, detail?: string } | required | What happened, in a word (name), and a sentence about it (detail) for the agent. |
Returns — nothing.
Errors — none.
Calls the user places
Not on this platform: a website has no native calls. A visitor talks to the agent in the widget's panel (see The voice bar); phone-style calls are part of the mobile SDKs.
Calls Graine rings
Not on this platform: a browser cannot be rung. A call booked from a website — with openBooking() or scheduleCall() — rings the visitor's app, on a phone where they use your app and are identified with the same trait; the booking sheet says “We'll ring you in the app”.
Booking with the SDK's sheet
“Call me at…” with no screen of your own: the widget draws the booking sheet in the agent's design, and the call it books rings the visitor's app. Offer it from your own button with openBooking(), or let the visitor find it in the chat.
What the sheet shows. A header, “Book a call”, with “{call name} will call you” under it — the call name is the agent's (Calls & scheduling), else the chat's title, else the agent's heading. Day: a chip for each day from today to the furthest day the agent allows — “Today”, “Tomorrow”, then the weekday and date, such as “Thu 16 Oct” — scrolling sideways. Time: the browser's time field, held to the times the chosen day offers, on 5-minute marks, between earliest(now) (rounded up to the next 5 minutes) and latest(now); a time typed outside them moves to the nearest one the day has. Under it, the browser's time zone and the chosen time, such as “Asia/Kolkata · Tue 14 Oct, 6:00 PM”, and “We'll ring you in the app”. What's it about? (optional): one line, at most 120 characters. Then Book call, with a spinner while it books. Once booked: “Booked — {call name} will call you on {day}, {time}” and Done.
It opens over the widget's panel: up from the bottom when the panel is a sheet or fills the page (large or full on a page narrower than 640 px), otherwise as a centred card. Escape, the close control and a tap outside close it. The times offered move on as the clock does while it is open.
A refusal stays in the sheet as one line under the button, in these words:
| Code | The line |
|---|---|
too_soon | Pick a time at least N minutes from now |
too_far | Pick a time within N days |
outside_window | That time is outside the hours we can call — pick another |
too_many_pending | You already have N calls booked — cancel one first |
scheduling_off | Booking a call isn't available right now |
identity_required | Sign in to book a call |
| anything else | Couldn't book just now — try again |
N is the agent's own number — its minimum notice, its furthest day, its limit of calls — singular for one (“1 minute”, “1 day”, “1 call”). The sheet opens even when booking is not available, says why (the scheduling_off or identity_required line) and books nothing — so check bookingAvailable before offering it. After too_soon the offer moves on to what is bookable now.
In the chat. The widget's chat shows the visitor's booked calls first in the conversation — an Upcoming calls card with each call's time in the visitor's time zone, the call name and Cancel (“Cancelling…” while it goes, “Couldn’t cancel · tap to retry” if it fails), at most three, soonest first — and, where booking is available, Book a call under them, or alone in the card's place when nothing is booked. Neither shows when the agent's design hides the booked-calls card (Garage → Chat window → Booked calls). A booking made in the sheet refreshes the card; it is also read again when the chat opens and, after the agent replies, at most every 30 seconds.
GraineAgent.bookingAvailable
Read it to decide whether to show your own “Call me” button.
true when the agent lets visitors book calls (Calls & scheduling → users may book) and the visitor is identified with the trait the agent's Who is the user? setting names. A browser cannot be rung, so unlike on a phone it does not depend on Graine being able to ring this device: the booking rings the visitor's app. false until load() has the agent's settings. Read it again after identify().
Parameters — none.
Returns — boolean. Example value: true.
Errors — none.
GraineAgent.openBooking
Call it from your own “Call me” button to let the visitor book a call in the widget's sheet.
Opens the panel on the booking sheet (above) and resolves when the sheet closes. One at a time: a newer openBooking(), the panel closing or the agent leaving the page ends the one before, unbooked. Safe before load(): the sheet opens once the agent is on the page; if load() fails, it resolves null.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
options | { reason?: string } | none | reason fills What's it about?, trimmed to 120 characters; the visitor may change it. It is kept with the call for the agent and never put in the notification that rings it. |
Returns — Promise<GraineScheduledCall | null> — the call booked in the sheet (GraineScheduledCall (website)), or null when the visitor closed it without booking. Example value: { id: "6f1c2a9e-7d1b-4c47-9a6e-2f0d8b1c5e44", at: Date("2026-10-16T12:30:00Z"), status: "scheduled", source: "app", reason: "Talk through my order", isUpcoming: true, canCancel: true }.
Errors — it never rejects: a refusal stays in the sheet as its line.
The scheduled-calls list
Book calls from your own interface, list the visitor's upcoming calls and cancel one — the same calls the sheet and the chat's card show. Each call rings the visitor's app.
GraineAgent.schedulingRules
Read it to build your own booking screen: which times the agent accepts, and whether visitors may book at all.
The agent's rules from its settings (Calls & scheduling), read each time you ask. Before load() has the settings, booking is off and every number is its default.
| Field | Type | Meaning |
|---|---|---|
usersCanBook | boolean | Whether visitors may book calls at all; false until the settings load. |
minLeadMinutes | number | The least notice a booking needs, in minutes — 5 by default. |
maxDaysAhead | number | How far ahead a booking may be, in days — 14 by default. |
maxPendingPerUser | number | How many upcoming calls a visitor may have at once — 3 by default. |
ringTimeoutSeconds | number | How long a call Graine rings keeps ringing before it is missed, 20–60 seconds — 45 by default. |
callName | string | null | The name the agent's calls ring with, when the dashboard sets one. |
hostedRinging | boolean | Whether Graine rings your users' apps itself (the workspace uploaded its push credentials). |
platforms | Array<"ios" | "android"> | The phones Graine rings; empty means every one. |
earliest(now?) | (now?: Date) => Date | The soonest time a booking is accepted: now plus minLeadMinutes. now defaults to the current time. |
latest(now?) | (now?: Date) => Date | The latest time a booking is accepted: now plus maxDaysAhead days. |
Parameters — none.
Returns — an object as above. Example value: { usersCanBook: true, minLeadMinutes: 5, maxDaysAhead: 14, maxPendingPerUser: 3, ringTimeoutSeconds: 45, callName: "Acme", hostedRinging: true, platforms: [], earliest: ƒ, latest: ƒ }.
Errors — none.
GraineAgent.scheduleCall
Call it to book a call from your own booking screen.
Books a call from the agent that rings the identified visitor's app at at. The time is sent with the browser's time zone, in which the agent's calling hours are checked. reason is trimmed to 120 characters, kept with the call for the agent, and never put in the notification that rings it.
It waits for load() to have the agent's settings, then refuses on the page — before anything is sent — what the rules already settle, with the booking sheet's words — the same on every SDK: scheduling_off, “Booking a call isn't available right now”; too_soon, “Pick a time at least N minutes from now” (“1 minute” for one; more than 30 seconds before earliest(now)); too_far, “Pick a time within N days” (“1 day” for one). Graine then checks what only it knows: the calling hours, the calls already waiting, who the visitor is.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
at | Date | string | number | required | When the call should ring: a Date, an ISO-8601 time with its offset, or a timestamp in milliseconds. |
reason | string | none | What the call is about, at most 120 characters. |
Returns — Promise<GraineScheduledCall> — the booked call (GraineScheduledCall (website)), status scheduled. Example value: { id: "6f1c2a9e-7d1b-4c47-9a6e-2f0d8b1c5e44", at: Date("2026-10-16T12:30:00Z"), status: "scheduled", source: "app", reason: "Talk through my order", isUpcoming: true, canCancel: true }.
Errors — rejects with a GraineError. On the page: invalid_request (“at must be a Date, an ISO-8601 time or a timestamp.”) at once, then scheduling_off, too_soon, too_far as above. From Graine: outside_window, too_many_pending, identity_required, device_limit, rate_limited, network, unavailable. unknown (“The call was booked, but its details did not come back.”) when Graine booked it without describing it.
GraineAgent.scheduledCalls
Call it to list the visitor's upcoming calls in your own interface.
The calls booked for the identified visitor (or, with nobody identified, this browser) that are still to come, in Graine's order — soonest first.
Parameters — none.
Returns — Promise<GraineScheduledCall[]>. Example value: [{ id: "6f1c2a9e-7d1b-4c47-9a6e-2f0d8b1c5e44", at: Date("2026-10-16T12:30:00Z"), status: "scheduled", source: "agent", reason: null, isUpcoming: true, canCancel: true }].
Errors — rejects with a GraineError: network, rate_limited, unavailable, invalid_key, origin_refused.
GraineAgent.cancelScheduledCall
Call it when the visitor calls off one of their booked calls.
Cancels a call booked for this visitor or browser; one booked for anyone else is not_found.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
id | string | required | The call's id, from scheduleCall(), scheduledCalls() or openBooking(). |
Returns — Promise<string> — the call's status afterwards, "cancelled". Example value: "cancelled".
Errors — rejects with a GraineError: invalid_request (“Pass the id of the call to cancel.”) for an empty id, not_found, network, rate_limited, unavailable.
GraineScheduledCall (website)
Read it to show a booked call: when it rings, where it is, and whether it can still be cancelled.
{ id, at, status, source, reason, isUpcoming, canCancel }
A plain object, the same fields as on every SDK.
What to expect — Returned by scheduleCall() (status scheduled), scheduledCalls() and openBooking(). at is a moment: show it in the visitor's time zone. A status or source newer than the loader arrives as Graine's own word.
| Field | Type | Meaning |
|---|---|---|
id | string | Use it to cancel the call — Graine's ID for it, a UUID. |
at | Date | null | When the call rings; null only when Graine's time could not be read. |
status | string | Where the call is: scheduled, ringing, answered, completed, declined (nothing rings that visitor again for 6 hours), missed, failed, cancelled, expired (its time passed while it could not ring) or noDevice (no phone of the visitor's could be rung). |
source | string | null | Who booked it: agent (in a conversation), app (your site or app), dashboard (your team) or api; null when Graine does not say. |
reason | string | null | What it is about, as it was booked — at most 120 characters — or null. |
isUpcoming | boolean | Use it to keep a call in an upcoming list: true while it is scheduled or ringing. |
canCancel | boolean | Use it to show a Cancel control: true while it is scheduled, before it rings. |
Design
The widget draws the design the brand chose in the Garage — colours, typeface, corners, the launcher's shape and glyph, the chat window and the booking sheet — and follows the visitor's light or dark setting when the design says “auto”. Nothing to call. The only design inputs on the page are load()'s fallbacks — accent, position and launcherLabel, used where the dashboard sets nothing — and the panel's size (load({ chatSize }), open({ size })).
Going live
- Allowed domains. Add every host the widget runs on — production, staging — to the agent's Allowed domains; a page whose host is missing gets no launcher and a console message.
- Content-Security-Policy. Allow
https://www.graine.aiinscript-src,connect-srcandframe-src, and do not deny the microphone to the widget's frame. - The console. The loader logs its problems as
[Graine] …in the browser console and never shows them to visitors: a missingagentIdor key, a refused key or origin, a sign-out that could not be sent. - The version.
GraineAgent.versionsays which loader build the page runs; include it when you ask for support.
Events and listeners
Not on this platform: the loader has no event API. Its functions answer with promises, the agent reaches your page through the actions you define (defineAction), and everything the visitor sees is drawn inside the widget.
Errors
A function that asks Graine on the visitor's behalf rejects with an Error whose name is "GraineError", carrying a code. Branch on the code; show the message, which is written for the visitor and never carries the identifier, traits or anything personal.
GraineError
Read it in a catch to decide what to do about a refused request.
Error & { name: "GraineError", code, message, status, retryAfterSeconds }
What to expect — code is one of the codes below, or a newer one as Graine sent it. A refusal made on the page before anything was sent has status 0.
| Field | Type | Meaning |
|---|---|---|
code | string | Branch on it: the reason in one stable word, such as too_soon or network. |
message | string | Show it: a sentence fit for the visitor. |
status | number | Use it in logs: the HTTP status Graine answered with, or 0 when nothing was sent or nothing came back. |
retryAfterSeconds | number | null | Use it to wait before trying again: how long Graine asked, with rate_limited; otherwise null. |
| Code | HTTP | Means | Raised by | Fix |
|---|---|---|---|---|
invalid_key | 401 | The publishable key is not one, or belongs to no agent; also every request after a load() that failed | Every request to Graine | Copy the key again from Agent → Embed & Widgets → Install |
origin_refused | 403 | The page's host is missing from the agent's Allowed domains | Every request | Add the exact host |
rate_limited | 429 | Too many requests from this browser, key or address | Every request | Wait retryAfterSeconds, then retry |
invalid_request | 400 (0 on the page) | A field is missing or malformed — no usable at, no call id | scheduleCall, cancelScheduledCall | Fix the call |
identity_required | 400 (0 on the page) | Nobody is identified, or the identifier is a placeholder | memories, forgetMemory, bookings that need a visitor | identify() with the identifier first |
device_limit | 403 | This browser is not one of the visitor's allowed devices | memories, forgetMemory, newConversation, scheduleCall | Sign out on another device, raise Devices per user, or Reset devices for the visitor |
scheduling_off | 403 (0 on the page) | Visitors may not book calls from this agent | scheduleCall | Turn it on in Calls & scheduling, or hide booking when bookingAvailable is false |
too_soon | 400 (0 on the page) | Earlier than earliest(now) | scheduleCall | Offer times from earliest(now) |
too_far | 400 (0 on the page) | Later than latest(now) | scheduleCall | Offer times up to latest(now) |
outside_window | 400 | Outside the agent's calling hours, in the visitor's time zone | scheduleCall | Show the message (it names a time that works); offer another time |
too_many_pending | 409 | The visitor already has the most calls waiting | scheduleCall | Cancel one first |
not_found | 404 | No such booking for this visitor or browser | cancelScheduledCall | Read scheduledCalls() again |
unavailable | 503 (504 on a timeout) | Graine could not answer just now — including a booking refused because the agent is switched off or out of credit; also a request left waiting when the agent was taken off the page | Every request | Retry with backoff; check the agent is on and the workspace has credit |
network | 0 | No answer came back: offline, or the connection dropped | Every request | Retry when online |
unknown | other | A code newer than the loader (carrying its word), or an answer that could not be read | Every request | Log the code and message; show the message |
Rate limits and quotas
| Limit | Value | Where it applies |
|---|---|---|
| Bookings from one install | 10 an hour | scheduleCall, the booking sheet, and the agent's bookings in that install's conversations |
| Bookings in a workspace | 1,000 an hour | All in-app bookings |
| Calls waiting per visitor | 3 by default (1–10) | At most this many upcoming calls per user |
| Devices per visitor | 2 by default (1–5) | Devices per user; idle devices free their place after 30 days |
| Notice and horizon | 5 minutes (2–1440) · 14 days (1–30) | With at least this much notice · Booked at most this far ahead |
| Booking reason · variables | 120 characters · 12 variables, about 1 KB | Bookings and conversations |
| Memories listed | At most 100, newest first | memories() |
| Conversation kept | 50 messages, 7 days after the last | Conversations that continue |
| Requests per key and address, per minute | Starting a conversation 30 (and 300 per key); booking, listing, cancelling, memory list and sign-out 30 (300 per key); forgetting memory 10 (120 per key); listing memories also 30 per install | Answered rate_limited with Retry-After |
| Action handlers · component data | 6 seconds · 8 seconds | After that, the agent is told the action did not finish; a component shows its authored content |
What's new
- Book a call from your website.
GraineAgent.openBooking({ reason })opens the widget's booking sheet — the day, the time, the visitor's time zone, What's it about? (optional), Book call — in the agent's design, and resolves with the call booked there, ornullwhen the visitor closed it. At the booked time, Graine rings the visitor in your app. GraineAgent.bookingAvailable: the agent lets visitors book and the visitor is identified. Show your own “Call me” button only then.- Booked calls in the widget's chat. The chat shows the visitor's upcoming calls with Cancel, and Book a call where booking is available.
- The full list of changes is in the Changelog.
Every symbol, A to Z
Every public symbol of the Website SDK — 33 in all — by name, each linked to where this page documents it: a type or function first, then its members.
GraineAgent—addMaskRule·bookingAvailable·cancelScheduledCall·close·defineAction·destroy·forgetMemory·getInstallId·identify·installId·load·memories·newConversation·open·openBooking·removeAction·reportEvent·scheduleCall·scheduledCalls·schedulingRules·setMemory·setScreen·switchesAgents·versionGraineAgent.load—accent·agentId·chatSize·launcherLabel·mode·position·publishableKey·variables

