Mindset AI

Docs / Get started / Build it

Create a session for your users

The one server-to-server call your backend makes before an agent can run on your page.

Before an agent can run on your page, your backend makes one call to us. That call is the whole server-side integration. Everything after it happens in the browser.

Your backend holds your organization's API key and never lets it near a browser. The browser holds a short-lived credential, scoped to one agent and one user, that your backend hands it.

The shape of it

  1. Your user loads a page that has an agent on it.
  2. Your frontend asks your backend for a session.
  3. Your backend calls us with your org API key and gets back an opaque credential.
  4. Your backend passes that credential to the browser.
  5. The SDK runs the agent with it.

There's no step 6. You don't parse the credential and you don't store it.

Before you start

You'll need an organization API key. These are issued in the Mindset AI console by an org admin, so if you don't have one, ask your Mindset AI administrator to create one for you.

Your key is bound to one Environment. A key for your test Environment can't create sessions in production, which is the behavior you want but does mean you'll need one key per Environment you're integrating.

Make the call

POST /api/v1/orgs/{orgSlug}/envs/{envSlug}/agent-sessions

Your organization and Environment are both in the path. Send your key on the x-api-key header, not on Authorization.

This route is server-to-server only and is never CORS-enabled, so it can't be called from a browser even by accident.

Request body

Field Type Required Notes
user.email string one of Must be email-shaped. Creates a shared identity the same person can also log into the web app with
user.externalId string one of An opaque ID of your own. Letters, numbers, underscores and hyphens only, 100 characters or fewer. Embed-only, never web-loginable
agent string yes The agent's handle, or its UUID. Copy it from the agent's Embed tab in the console
createUserIfNeeded boolean no When true, a user we haven't seen before is created. Defaults to false, which means an unknown user is rejected
attribution object no Your own tags for this session. Up to 16 keys, keys up to 64 characters, values up to 256 characters. See below

Send exactly one of user.email or user.externalId. Sending both, or neither, is rejected.

The body is strict. Any field not in this table is rejected rather than ignored, so a typo surfaces immediately instead of silently doing nothing.

Example

curl -sS -X POST \
  "https://<your-mindset-host>/api/v1/orgs/{orgSlug}/envs/{envSlug}/agent-sessions" \
  -H "x-api-key: $ORG_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "user": { "email": "alice@acme.com" },
    "agent": "support-bot",
    "createUserIfNeeded": true,
    "attribution": { "plan": "enterprise", "region": "emea" }
  }'

What you get back

A JSON object. Pass it to the browser exactly as you received it.

The credential is opaque on purpose. Its internal shape isn't part of the contract and will change, and the SDK always gets back the structure it expects. Reading its fields, rebuilding it, or storing it in a database are all things that will break on you.

One call does three things: it resolves or creates the user in your organization, confirms that user can reach that agent in that Environment, and returns the credential. Holding your org key and making this call is the authorization. The credential works for the named agent regardless of that agent's publication settings, as long as the agent has a published version to embed.

Choosing an identity

Use email when the person using your embedded agent is the same person who might log into Mindset AI directly. The same email is one identity across both, once they've proved they own the mailbox.

Use externalId when your users exist only inside your application, or when you'd rather we didn't hold their email at all. It's opaque, it's never web-loginable, and it never merges with an email identity.

We check the shape of an email, not whether it's real, so an address that can't receive mail will still work. What we won't accept is a value with no @ in it, because that could later collide with an external ID and make one person look like two.

Pick one and stay with it. Switching a user from one form to the other gives you two separate users with two separate conversation histories.

Handing the credential to the browser

Create a credential when your user opens the page, and use it straight away. A credential is meant to be consumed as soon as it's generated. It's single use, and it's scoped to one user and one agent.

Keep it in memory. Don't put it in local storage, don't put it in a cookie, and don't render it into server-side HTML that gets cached.

Your frontend gives the SDK a function that returns the credential, rather than the credential itself. That's what lets the SDK ask again when it needs to.

Two agents on one page means two calls and two independent sessions.

Keeping the session alive

You don't have to do anything here. The SDK handles it, and there's no renewal endpoint for you to call.

A session stays alive while the agent stays in place on the page. When it can't continue, because the tab closed or the page reloaded, the SDK asks your integration for a session again. Your backend does what it did the first time: create a new one and hand it over. Your endpoint stays a single create.

Ending someone's access

There are two ways to stop a user reaching an agent, and both are done in the Mindset AI console rather than through an API:

  • Remove the agent, which stops everyone reaching it.
  • Disable the user, which stops that person reaching anything.

There's nothing more granular than this today, and there's no per-session revocation call.

What the credential can and can't do

The credential is admitted on the run surface only: inference, tool execution, and run events. It's refused on every management and administrative surface.

That holds regardless of the user's role in your organization. An org admin's session credential still only runs one agent. There's deliberately no way to widen it.

There's no restriction on where the SDK and credentials can be used, so be careful how they're shared before they're used.

Your org API key is a different matter entirely. It's admin-grade. Keep it on your server, keep it out of logs, and rotate it through the console if you think it's been exposed. The create route is never CORS-enabled and we never accept an org key from a browser, so the only way it reaches one is if your own code puts it there.

Tracking usage with attribution

The attribution tags you send aren't just labels. Those values surface again through our observability, so you can see what users of your embedded agents are doing, broken down by whatever dimensions matter to you.

"attribution": { "plan": "enterprise", "region": "emea", "team": "support" }

We don't inspect what you put in them. Up to 16 keys, keys up to 64 characters, values up to 256 characters. These limits are enforced, and a request that breaks them is rejected rather than quietly truncated, so you never end up with a session carrying tags you think are there but aren't.

Errors to handle

What happened Status Code
A field in the body that isn't in the table above 400 validation_error
Both email and externalId, or neither 400 validation_error
An email value with no @ in it 400 validation_error
An externalId that breaks the format or length rule 400 validation_error
An unknown user, without createUserIfNeeded 404 not_found
The agent isn't one of your organization's agents 404 not_found
Your key's organization doesn't match the orgSlug 404 not_found
The envSlug isn't your key's Environment, or doesn't exist 404 not_found
Your key doesn't carry the scopes this route needs 404 not_found

The 404s are deliberate and they're all identical. A wrong organization, a wrong Environment, a wrong agent and an insufficient key all answer the same way, so nobody holding a key can map out which organizations or Environments exist by probing. It does mean a 404 during setup needs checking against all four, since the response won't narrow it down for you.

Things that catch people out

Query parameters are ignored, extra body fields are rejected. Addressing is entirely in the path. The asymmetry is deliberate but surprising: a stray query string does nothing, a stray body field is a 400.

The embed always runs the agent's current published config. Publish a new version and your users pick it up on their next turn. A turn already in flight finishes on the version it started with.

The credential is end-user tier no matter who the user is. There's no admin session credential.

createUserIfNeeded defaults to false. Most first integrations want it true, and the symptom of leaving it out is a 404 that looks like the agent is wrong.