Skip to content
Contents
  1. Native apps
  2. Errors and reconnection
  3. API keys and security
  4. Requirements on the VERIAX instance
  5. Version and licence
  6. FAQ

@veriax/sdk · v0.0.5

VERIAX JavaScript SDK

@veriax/sdk lets any JavaScript application, in the browser or in a native app, use VERIAX agents. Your application can hold a conversation with an agent, let it use its tools, ask it for structured output built from your knowledge, send it files and get back what it produces.

Your server keeps the API key and asks VERIAX for short-lived tokens; your application works only with those tokens. A token can act in one of three ways:

  • as an application account you create for the integration, for example to serve anonymous visitors;
  • as your own VERIAX user, for an integration that works on your behalf;
  • as each of your users, when your application signs its users in and VERIAX knows them by their email.

Choose how your application signs in explains the three cases.

Getting started

This tutorial goes from an empty project to a working conversation. You need a VERIAX account and at least one agent.

1. Create an API key

Every integration starts from an API key. Where you create it depends on who the tokens will act as; for a first test, use your own user:

  1. In VERIAX, open your Profile.
  2. In the Access credentials card, open the API keys tab and choose Create API key.
  3. Give the key a name and an expiry date, then create it.
  4. Copy the key: it is shown only once.

For the other two cases see Choose how your application signs in. Keep the key on your server, in an environment variable or a secret manager.

2. Install the SDK

npm install @veriax/sdk

The package is ESM only and has no runtime dependencies.

3. Issue tokens from your server

Add an endpoint to your server that asks VERIAX for a token and returns it to your application. VERIAX answers 201 {"token", "expires_at", "expires_in"}: return that body as is, with Cache-Control: no-store.

# Django: settings.VERIAX_URL and settings.VERIAX_API_KEY come from the environment
import requests
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse
from django.views.decorators.http import require_POST


@require_POST
@login_required
def veriax_token(request):
    answer = requests.post(
        f"{settings.VERIAX_URL}/api/v1/ai/get-token/",
        headers={"Authorization": f"Api-Key {settings.VERIAX_API_KEY}"},
        timeout=8,
    )
    response = HttpResponse(answer.content, status=answer.status_code, content_type="application/json")
    response["Cache-Control"] = "no-store"
    return response

4. Choose an agent and send a message

getAssistants() lists the agents the token can use, with their id, name and description. Let the user pick one, or choose it in your code, then open a conversation with it:

import { VeriaxClient } from '@veriax/sdk'

const client = new VeriaxClient({
  baseUrl: 'https://veriax.example.com',
  tokenUrl: '/api/veriax-token',
})

const picker = document.querySelector<HTMLSelectElement>('#agent')!
const agents = await client.getAssistants()
picker.replaceChildren(...agents.map((agent) => new Option(agent.name, String(agent.id))))

async function start(agentId: number) {
  const thread = await client.createThread({ assistant: agentId })
  const chat = client.chat(thread)
  const { text } = await chat.send('Hello').result
  console.log(text)
}

picker.onchange = () => start(Number(picker.value))

createThread opens a conversation with the agent, chat connects to it and send returns the reply. In the steps below, agentId is the agent you chose.

5. Show the reply as it arrives

Replies stream in. Render the text received so far on every delta, then show the sources the agent cited:

import { renderMarkdown } from '@veriax/sdk/markdown'

const out = document.querySelector<HTMLElement>('#answer')!
const reply = chat.send('What does our returns policy say?', {
  onDelta: (_delta, text) => out.replaceChildren(renderMarkdown(text)),
})
const { citations } = await reply.result

chat.stop() ends a reply early; it resolves with stopped: true.

6. Keep the conversation

The SDK remembers the last conversation. On the next load, resume it instead of creating a new one:

const thread = client.lastThread() ?? (await client.createThread({ assistant: agentId }))
const chat = client.chat(thread)
const history = await chat.getMessages()

By default the token and the last conversation live in sessionStorage. See Token and storage for the other options.

7. Going further

  • Tools. There is nothing to set up: an agent uses its tools, retrievers and abilities on its own, when the request needs them. If your interface lets the user point the agent at specific tools, as the tools menu of the VERIAX chat does, client.getTools(agentId) returns that menu and send(text, { selectedTools }) asks the agent to use the chosen ones for that message.
  • Files. client.uploadArtifact(chat.threadToken, file) adds a file to the conversation; the agent reads it if it has an ability that reads files. chat.getArtifacts() lists the files, and client.downloadArtifact(id, chat.threadToken) downloads one, including what the agent produced.
  • Structured input and output. When an agent declares input_schema, send the object as JSON text: chat.send(JSON.stringify(value)). When it declares structured_output, the reply text is JSON in that shape. Both schemas come with getAssistants() and createThread().

Choose how your application signs in

All three cases use the same SDK code. What changes is the API key and what your server sends to get-token/.

An application account

Use it when the people using your application have no VERIAX user, for example the visitors of a public site. Every token acts as the same account, which reaches only what you share with it.

  1. In Settings > Users, choose New user and set User type to Application.
  2. Share with the account the agents it may use, and the knowledge bases those agents read.
  3. On the account's page, in the Access credentials card, create an API key.

VERIAX refuses to share with an application account an agent that could act with the account's identity, for example through connectors or MCP tools: the sharing window lists what is in the way. Each conversation can be reached only with its own thread token, so visitors never see each other's conversations.

Your own user

Use it for an integration that works on your behalf, such as an internal tool or a script. Create the key in your Profile, as in step 1. Tokens act as you: your agents and your conversations.

Each of your users

Use it when your application signs its users in, for example an ERP or an intranet, and the same people have a VERIAX user with the same email. Each user then works with their own agents and conversations.

  1. Create an application account and its API key, as above.
  2. A superuser opens the account's page, turns on Open the chat as any user in the Access credentials card and saves the page.
  3. Your server adds the email of the user it signed in:
@require_POST
@login_required
def veriax_token(request):
    answer = requests.post(
        f"{settings.VERIAX_URL}/api/v1/ai/get-token/",
        headers={"Authorization": f"Api-Key {settings.VERIAX_API_KEY}"},
        json={"email": request.user.email},
        timeout=8,
    )
    response = HttpResponse(answer.content, status=answer.status_code, content_type="application/json")
    response["Cache-Control"] = "no-store"
    return response

Take the email from your own session, never from the request. VERIAX compares it ignoring case; an email that matches no active user answers 404 unknown_user. A user's profile may differ from user: read it with client.getProfiles() and pass it in the profile option.

Native apps

The SDK runs wherever fetch, WebSocket and AbortSignal.timeout exist, including React Native and WebViews. Two things differ from the browser:

  • Storage. Without sessionStorage the SDK keeps the token in memory. To keep the conversation across restarts, pass a synchronous storage object (getItem, setItem, removeItem) in the storage option.
  • Your token endpoint. An app has no site cookies: authenticate the call with your own login, for example with the headers option of tokenUrl.
import { tokenUrl } from '@veriax/sdk/helpers'

const client = new VeriaxClient({
  baseUrl: 'https://veriax.example.com',
  getToken: tokenUrl('https://api.example.com/veriax-token', {
    headers: () => ({ Authorization: `Bearer ${session.accessToken}` }),
  }),
})

Never ship the API key inside the app: an installed app can be decompiled.

Errors and reconnection

The SDK recovers on its own from most problems:

  • Network drops. The chat reconnects with an increasing delay. If a reply was cut off, the SDK reads the conversation again: a reply that finished meanwhile arrives with resynced: true, and a question that never arrived is sent again.
  • Expired tokens. At 80% of a token's lifetime, and after any 401, the SDK asks your endpoint for a new token and goes on.

Everything else arrives as a VeriaxError with a code; see Error codes. Reply errors reject reply.result, connection errors arrive on chat.on('error'), and REST errors reject the promise of the method you called.

API reference

Entry points

ImportContents
@veriax/sdkVeriaxClient, ChatSession, Reply, VeriaxError and the types (Thread, Assistant, Citation, TokenResponse, ...)
@veriax/sdk/helperstokenUrl, handleError and the TokenUrlOptions type
@veriax/sdk/markdownrenderMarkdown

VeriaxClient

new VeriaxClient(options):

OptionDefaultNotes
baseUrlrequiredThe VERIAX instance. Must be https://; http:// only towards localhost
tokenUrl or getTokenone is requiredtokenUrl: your token endpoint, called with POST. getToken: your own function returning {token, expires_at, expires_in}
profileuserThe VERIAX profile in the API paths
storage'session'Where to keep the token and the last conversation: see Token and storage
keydefaultStorage key suffix, to run several clients on the same page
fetch, WebSocketglobalsAlternative implementations
timeoutMs10 sTimeout of the calls to VERIAX
MethodWhat it does
getAssistants()The agents the token can open a conversation on, with input_schema and structured_output
createThread({ assistant, name? })Opens a conversation. Returns { id, token, name, assistant, input_schema, structured_output, date_create, date_last_modify }
getThread(threadToken)The conversation of that thread token
revokeThread(threadToken)Closes the conversation for every token
chat(thread, options?)Connects to the conversation and returns its ChatSession
lastThread(){ token } of the last conversation opened with chat(), if any
getTools(assistantId)The agent's tool menu: { tools: [{ name, display_name, icon, priority, visible }], total_count }
getProfiles()The profiles of the token's user: [{ value, label }]
uploadArtifact(threadToken, file, filename?)Adds a file to the conversation
downloadArtifact(id, threadToken), downloadPreview(id, threadToken)A file, or its preview, as a Blob
tokens.clear()Forgets the token and the last conversation, for example on logout
dispose()Stops the token timer and the visibility listener

ChatSession

Options of chat(): backoffBaseMs (1 s), backoffMaxMs (30 s), maxReconnectAttempts (10, Infinity for no limit), WebSocket.

MemberWhat it does
send(content, { onDelta?, selectedTools? })Sends a message and returns a Reply. A second send during a reply throws busy
stop()Stops the reply in progress
getMessages()The conversation history: human and ai messages with their citations
getArtifacts()The conversation's files
connect()Opens the connection; send does it by itself
close()Closes the connection and stops reconnecting
stateidle, connecting, open, reconnecting or closed
replyingWhether a reply sent by this session is in progress
threadTokenThe conversation's thread token
on(event, fn)Adds a listener and returns the function that removes it
EventWhen it fires
readyThe connection is ready, after every connection and reconnection. { threadToken, replying }
stateThe connection state changes
sourcesA reply cited sources: { threadToken, citations }
human-messageA message was sent to the same conversation from elsewhere, for example another tab
stream-finishedA reply not sent by this session finished
frameEvery message received from the server
errorA connection error

Reply

MemberWhat it holds
for await (const delta of reply)The text deltas, as they arrive
reply.textThe text received so far
reply.resultPromise of { text, stopped, resynced, messageId, citations? }

Token and storage

storageEffect
'session'sessionStorage: survives a reload of the tab
'local'localStorage: shared between tabs and kept after closing them
'memory'Nothing is kept: every load gets a new token and starts a new conversation
an object { getItem, setItem, removeItem }Your own synchronous storage

The storage key is veriax.session.<key>. If storage is unavailable, the SDK works in memory. With 'local', a tokens.clear() in one tab signs every tab out.

renderMarkdown

renderMarkdown(text, doc?) returns a DocumentFragment built with DOM APIs only, so text from the model is never parsed as HTML. In Node, pass a document as the second argument. It supports paragraphs, bold, italic, inline code, code blocks, one level of bulleted and numbered lists, and http:, https: and mailto: links, opened in a new tab. Headings, tables, images and raw HTML are shown as text.

Error codes

Every error is a VeriaxError with code, recoverable, message (technical, in English, not for users), and when present status, serverCode, closeCode and serverMessage (text meant for users, such as a usage-limit message).

codeWhenRecoverable
session_unavailableYour endpoint gives no valid token, or VERIAX refuses the token twiceno
thread_closedThe conversation was closed with revokeThreadno
assistant_not_allowedThe token cannot use that agentno
not_foundUnknown conversation or fileno
forbiddenAction not allowedno
invalid_inputEmpty or too long message (4000 characters by default), file too large (10 MiB by default), malformed requestin chat yes, on REST no
handshake_refusedThe connection to the conversation was refused twiceyes
limit_reachedA usage limit of the agent was reached; see serverMessageno
rate_limitedToo many requests, for example more than 10 messages per minute in one conversationyes
unavailableThe VERIAX licence is not activeno
connection_lostNetwork, timeout or server error, after the reconnection attemptsyes
model_errorError of the model or of the agentyes
action_not_allowedThe server refused a socket actionno
busysend while a reply is in progressno
closedclose() was called, or tokens.clear() during a token requestdepends

Socket close codes

CloseWhat the SDK does
Handshake refusedGets a new token and tries again; a second refusal gives handshake_refused
4401 (token expired, conversation closed, key revoked)Reconnects at once with the current token; if refused again, thread_closed
1000 after a limit or error messageFatal: limit_reached or the error received
Other codes, or no ready within 15 sReconnects with a delay of 1, 2, 4… up to 30 s, at most maxReconnectAttempts times, then connection_lost

API keys and security

  • Keep the key on the server. Never in browser code, an app, a repository, URLs or logs.
  • One key per integration, with an expiry. You can then revoke one without stopping the others. Revoking a key ends its tokens at once.
  • Protect your token endpoint. Whoever reaches it gets a token. Serve it over HTTPS, require your own login where your application has one, limit requests per user or per IP, and do not open it to other origins with CORS.
  • Treat the thread token as a secret. It gives access to the conversation: keep it out of URLs, logs and analytics.
  • Display replies safely. Use renderMarkdown or plain text, never innerHTML, and do not embed files from a conversation in the page: they come from users or from the model.
  • Tell your users. Conversations and files are stored in VERIAX under the account the token acts as.

Requirements on the VERIAX instance

For whoever runs the instance:

  • API keys switched on (THX_AI_API_KEYS_ENABLED) and chat tokens switched on (THX_AI_CHAT_SESSIONS_ENABLED, on by default).
  • The authentication classes ApiKeyAuthentication, then ChatSessionAuthentication, before SessionAuthentication, and ApiKeyOrSessionAuthStack for the WebSockets.
  • django-cors-headers installed, without CORS_ALLOW_CREDENTIALS: the SDK never sends cookies.

Version and licence

This page documents @veriax/sdk 0.0.5. The SDK is released under the MIT licence.

FAQ

Does the API key ever reach the browser or the app?

No. The key stays on your server, which uses it to ask VERIAX for a short-lived token and passes only the token on. The SDK works with the token and never sees the key.

Does the SDK draw an interface?

No. It gives you data and events, and you build the interface. To display replies there is renderMarkdown, which builds DOM nodes without using innerHTML.

Does it work in React Native?

Yes. The SDK needs fetch, WebSocket and AbortSignal.timeout. Without sessionStorage it keeps the token in memory; pass a synchronous storage object to keep it across restarts.

How long does a token last?

30 minutes by default. Before it expires the SDK asks your server for a new one, and conversations stay reachable with every new token.