Skip to main content

Client SDK

The JavaScript SDK is available as soon as you integrate your application with either the Web SDK or via Canvas Theme. It enables client-side automation with your Digital Twin—such as sending messages, listing favorites, and running assistant instructions.
To use the JavaScript SDK, your Digital Twin must be integrated into a web application using the Web SDK or injected into Canvas via Theme.

Integration Options

Integrate your Digital Twin in one of these ways to access the SDK:
  • Web SDK: Embed the Digital Twin directly in your web app by installing the Web SDK for easy setup, user management, and customization.
  • Canvas Theme: Inject the Digital Twin into Canvas using the Theme for direct access to SDK features within the Canvas LMS.
Both methods offer secure, flexible options to maximize your Digital Twin’s functionality. Select the approach that best fits your environment.

Global Window Object

When your Digital Twin is integrated, the pria object is injected directly onto the global window (DOM), making it the central access point for all Pria SDK API calls from anywhere in your application. This allows you to easily interact with Pria’s messaging, UI control, and event management methods without additional imports or setup—simply reference window.pria.
By being attached to the DOM, pria acts as your always-available gateway for secure and seamless integration with all Pria interfaces and backend services—enabling features like messaging, automation, and UI control from any context in your client-side code.

Messaging Technology

Communication between your web application and the Praxis AI Middleware via the Web SDK is powered by browser messaging, which securely enables cross-origin interactions between window objects, such as between a main application and an embedded iframe or pop-up. Requests are sent to the backend middleware server using the pria.send() API method, ensuring safe and reliable message delivery for seamless integration and robust application workflows.

Voice (Convo Mode) in Embedded Contexts

When your Digital Twin uses voice conversations (Convo Mode) inside an iframe you control — including LMS embeds such as Canvas — the host page’s iframe must delegate microphone and audio permissions, or the browser will hard-block voice regardless of what the user allows:
Without this, the in-app “Tap to start talking” screen still renders, but tapping surfaces a microphone-permission error instead of starting the conversation. If the Digital Twin has Start Immersive Mode by Default switched on, the embedded twin shows its “Tap to start talking” screen by itself once the SDK session is up. Anything your page asks for wins over that setting — the convoMode display option, or an explicit convo.start / convo.stop command. (In error messages, Convo Mode is abbreviated STS — speech-to-speech.)

Sending a Request

Request Message

To make a request, simply compose a Javascript object containing the command and arguments like below:

Available Commands

post is accepted as a legacy alias for message.post. New integrations should use message.post.

New or continued conversation (contextMode)

When your page picks an assistant, contextMode decides which conversation it lands in: Defaults when you leave it out: a convo.start with a selectedCourse continues that conversation. A convo.start without one, and assistant.select, continue the current conversation in the classic interface and start fresh in the 2026 interface. A message.post with an assistantId but no contextMode posts into the current conversation. Any value other than 'fresh' or 'continue' counts as left out.

Speaking into a running conversation

convo.post injects text into a voice conversation that is already running. It comes in two roles, and the difference matters. role: 'user' (the default) is the learner speaking. It is the exact equivalent of them saying it out loud and having it transcribed — your twin answers it like any other thing they said. role: 'system' is your page steering the twin: your instruction reaches the model, the twin says it in its own words, and nothing is recorded as if the learner had said it. Use it to react to what someone is doing on your page.
Two things must be true before role: 'system' works: an administrator has turned on Let the embedding page guide live conversations in the Digital Twin’s settings, and the command comes from the page that hosts the twin (not a different site or a tab you opened). If either is missing, the command comes back refused (HTTP 401). These turns cost credits like any other.
Set respond: false to give the twin context without making it answer. Support for that varies by provider, and where it cannot be honoured you get an explicit error rather than silence: A user message can be up to 4,000 characters, a system message up to 2,000.

Knowing when the twin is ready

Commands sent before the twin’s window is listening are held and replayed, in order, once it is — so you can send immediately after loading without retry loops. Up to 20 commands are held, each for at most 30 seconds; one that overflows or expires comes back as a pria-error. Pass queueUntilReady: false on a command you would rather have fail than wait. pria.isSessionReady() tells you whether a command sent right now is delivered rather than held. For voice specifically, the twin also emits convo.live once per call, when the conversation can actually accept convo.post — that is a different question from convo.ready, which only says the user is allowed to start one. When a call ends — the person hung up inside the panel, or the connection closed — the twin emits convo.ended once.

Confirm it’s you

When an admin account opens Pria from your page (for example a Canvas course page), the panel may first show Confirm it’s you and ask for a 6-digit code. While it waits:
  • commands already held fail at once, and new ones are refused, with a pria-error whose response.reason is 'auth_required' (other errors carry no reason);
  • pria.isAuthRequired() returns true;
  • the SDK calls your onAuthRequired display option once (see Confirm it’s you). The SDK does not open the panel by itself.
Nothing is replayed. Once the person confirms, the twin sends convo.ready again — send your commands then.

How a course-scoped post behaves

When a post (or message.post) carries selectedCourse, Pria switches the visible conversation to that course’s own thread before sending, so the question — and the reply — land there instead of wherever the conversation happened to be pointed before. A few behaviors to design around when wiring up course buttons or similar triggers:
  • Duplicates are dropped, not resent. An identical post (same text, course and assistant) sent again within 5 seconds of the first one — or while the first one is still waiting to go out — is dropped, and the page gets a pria-error event instead of a second reply. This covers both a double-click and a host-side retry.
  • A post waits for Pria to be ready. A post sent before the user is signed in is held — up to 16 seconds for sign-in, then up to 10 seconds more for the chat view — and sent once both are ready. If either wait times out, the post errors instead of hanging indefinitely.
  • Posts are handled one at a time. Pria works through held or queued posts in order; a later post never jumps ahead of, or interrupts, one that’s still being handled.
  • Trigger post from a plain button. A pria.send() call immediately followed by a form submit or a page navigation can lose a post that is still waiting to be delivered (for example while it’s held for sign-in). Use a plain button, not a submit button or a navigation handler.
  • Don’t retry within 5 seconds. Because an identical post sent within 5 seconds is dropped as a duplicate, a host-side retry issued right after a post will itself be dropped — wait past the 5-second window (or change the text) before retrying.
This course-switching and duplicate/ready behavior applies to post and message.post; other commands are unaffected.
Some accounts set a privacy policy that every user must read and accept before they can chat or talk with one of that account’s Digital Twins — see Managing Accounts — Privacy Policy Consent Form. Accounts that never set one never trigger any of this. While the current user has not yet accepted the account’s policy:
  • post, message.post, convo.post and convo.start are all refused with a pria-error whose content is exactly:
  • convo.ready reports authorized: false with content.reason === 'consent_required', and the Pria panel opens by itself so the user can read and accept the policy — you don’t need to open it yourself.
Once the user accepts, the Digital Twin sends convo.ready again, this time with authorized: true. A host that auto-starts voice on load should wait for this later convo.ready event rather than acting on the first one it sees, since the first convo.ready after login can still carry reason: 'consent_required'.

Choosing how the conversation opens

convo.start takes an optional intro:
On avatar twins (Anam, Cartesia) the line is spoken word for word. On OpenAI (Realtime and GPT-Live), Gemini and xAI the twin has no scripted opening line, so your text guides its greeting and it will paraphrase. ElevenLabs agents own their first message and ignore intro.

Pre-selecting an assistant (assistant.select)

assistant.select stages an assistant for the user’s next message — nothing is posted, no conversation is created, and the user sees no injected turn. The chat interface shows the assistant as the pending choice, and the user’s own first message is routed to it (from then on the conversation keeps that assistant automatically). The assistantId is validated against the Digital Twin’s assistant list; an unknown id returns an error and the twin stays on its default. Most integrations don’t need to send this command directly — pass the assistantId display option at load instead (see Assistant pre-select) and the SDK sends it for you at the right moment. If the user later picks a different assistant in the interface, their choice wins.

Sample Requests

Send a text message to your digital twin:

Error Handling

The pria.send() function may throw these errors:
  • “Connect Pria first, then retry.” - Pria is not properly initialized
  • “Function requires valid JSON.” - Invalid request format
Authorization failures (for example, requesting convo.start when voice mode is not enabled for the user) are not thrown by pria.send() — they come back asynchronously as an error message with isError: true. See Receiving Responses.

Receiving Responses

Subscribe to Responses

To receive responses from Pria, you must first subscribe:

Response Message

A response message from the Pria AI Middleware follows a clear and structured format to ensure consistent integration and robust client parsing. Each response includes standard fields that define its source, content, format, and compatibility.
Response Fields type The string constant "pria-response" (or "pria-error" for failures). Use as a filter or discriminator in your message handling logic — handle both values if you want to surface errors. response Top-level object that wraps all core response details from Pria AI Middleware. version Integer value indicating the version of the response structure. Used for ensuring compatibility with various client parsers.
Always check the version field to ensure your parser correctly supports all required fields and response shapes.

Sample Responses

See Object Definitions for a complete list of responses

UI Control Methods

The SDK provides several methods to control Pria’s visibility and display state.

Visibility Control

Control whether Pria is visible on the page at all. When hidden, both the button and UI panel are completely removed from view.

Display Control

Control whether the Pria UI panel is expanded or collapsed to the button.

Ready State

Check if Pria is fully initialized and ready to receive commands before sending messages:
Always check isReady() before sending commands to ensure the SDK is fully connected to the middleware.
Recommended Ready Check Pattern: When integrating Pria, use this pattern to safely check if the SDK is ready:

Convo Ready State

After the user authenticates, the Digital Twin sends a convo.ready event to the SDK indicating whether real-time voice (speech-to-speech) mode is authorized for the current user. The SDK captures this and exposes it via isConvoReady().
isConvoReady() returns false until the Digital Twin sends the convo.ready event after login. This happens automatically — you do not need to request it. If the account has an unaccepted privacy policy, authorized stays false and content.reason is 'consent_required' — see Privacy-policy consent above.
The convo.ready event: The convo.ready event is a proactive notification sent by the Digital Twin after the user logs in. It tells the embedding application whether real-time voice is available. You can listen for this event via pria.subscribe():
Full-screen voice mode example (hide UI while loading, show when voice is ready): This pattern keeps the Digital Twin hidden during initialization and only reveals it when voice mode is confirmed ready, providing a seamless voice-first experience:
For a simpler approach without custom subscriber logic, use the convoMode display option which handles auto-start automatically. See Convo Mode display option.

Live voice transcripts (convo.transcript)

When the Digital Twin allows it (Share voice transcripts with the embedding page in the twin’s settings) and your page asks for it, the twin sends the learner’s words and the avatar’s answers to your page live, as the voice conversation happens — for every voice provider. Ask for them with the transcripts display option (the SDK subscribes for you as soon as the twin is ready), or send the command yourself at any time:
Receive them through your subscriber:
  • final: false messages are live captions (at most a few per second); final: true carries the full text of the turn — replace, never stitch.
  • turnId links the learner’s turn and the avatar’s reply to it. A later message with the same turnId replaces the earlier one: on Anam twins a fast learner turn may arrive as reason: "eager", be reopened with reason: "resumed" if the learner keeps talking, then be confirmed with reason: "end_of_turn". interrupted means the avatar was cut off (barge-in or the call ended), error that the answer failed, intro the opening greeting, typed a message typed during the call, and empty that the twin produced no reply to an instruction your page sent with convo.post (its text is empty).
  • seq orders messages; confidence, startedAt and endedAt are filled when the speech recogniser reports them (null otherwise).
  • Transcripts are only ever sent to the page that hosts the Pria frame, at its own origin. They contain personal data: make sure your privacy notice covers them.

Cleanup and Destroy

Properly destroy and remove Pria when no longer needed:
The destroy() method performs complete cleanup:
  • Removes all UI elements (iframe, button, containers)
  • Removes injected CSS and OAuth scripts
  • Cleans up all event listeners (keyboard shortcuts, drag handlers, message handlers)
  • Clears internal state and subscriber callbacks
  • Removes window.pria and window.priasdk references
After calling destroy(), the SDK must be completely reloaded to use Pria again. All references to pria become invalid.
Destroy and Reload Pattern: If using the Web SDK wrapper (PriaIntegration), use these methods for clean lifecycle management:

UI Control Reference

Working with the SDK

Authentication & Security

The SDK uses the identity of the user currently connected to the digital twin. Ensure proper authentication is in place before making requests.

Requirements

  • Pria must be properly configured and fully connected
  • User must be authenticated with appropriate permissions
  • Valid session must be established

Content Format

Pria outputs content in Markdown format. Parse responses accordingly in your application.

Error Handling

It is best practice to wrap function calls in a try/catch block to avoid unhandled exceptions:

Response Processing

It is best practice to handle responses from Pria by looking at the response message type, isError, command, etc. so your handler covers every situation:

Cleanup

Consider removing the event handlers you have declared when subscribing to receive response messages before your application terminates. Pass the same function reference you subscribed with:

Troubleshooting

Common Issues

Debugging

The SDK logs its lifecycle to the browser console — watch for messages such as Pria SDK initialization started, PriaIntegration already loaded, and Failed to load Pria SDK while troubleshooting. Logging every message your subscriber receives is the quickest way to see exactly what the middleware returned:

Examples

See complete, runnable integration examples — the live SDK Playground and a full PriaTestHarness API class you can adapt for your own application.

JavaScript SDK Examples

The SDK Playground demo plus a complete PriaTestHarness class showing clean initialization, connection detection, and command handling (message.post, convo.start/convo.stop, assistants.list, and more).