MNEMOCAST

Developer guide

For developers · updated with the service

Create your Mnemocast system-to-system account

Put Mnemocast's video meetings inside your own product. Your server talks to Mnemocast's API with a key; your app shows the call with LiveKit's SDK. Your people never need a Mnemocast account, and they stay in your organisation on Mnemocast, apart from everyone else's.

How it fits together:

Every call below goes to https://api.mnemocast.com with your key:

Authorization: Bearer mk_…
Content-Type: application/json

Every refusal is JSON, {"error": "<code>", "reason": "<what to do>"}, with the HTTP status. Codes are stable; reasons are for people. Another organisation's ids answer 404, never 403.

1. Create the account

  1. Sign in at app.mnemocast.com/developers with an account that belongs to your organisation (a shared work address, not one person's own), so it outlives any one person.

  2. Under Make a developer account, give it your organisation's name and, optionally, a link name: 1–40 lowercase letters, digits and hyphens, unique across Mnemocast, such as acme. It appears in join links (app.mnemocast.com/r/acme/<room>) and cannot be changed later; left empty, it is made from the name. Choose Make it.

  3. Copy the key now. It is shown once, as mk_<id>_<secret>. Put it in your server's secret store (AWS Secrets Manager, Google Secret Manager, Vault, or your host's encrypted settings). Never in your app, a repository, a chat or a log. Anyone with it acts as your whole organisation.

  4. Check it works:

    curl https://api.mnemocast.com/v1/tenant -H "Authorization: Bearer $MNEMOCAST_KEY"

    It answers with your organisation's id, name and slug.

You start on Sandbox: no card, small limits, enough to build and test (see pricing). Upgrade to Growth in the console when you go live; your card is entered on Stripe's page, never Mnemocast's. The console also makes new keys, revokes old ones (they stop at once) and shows your usage this month with its cost so far. To change keys without downtime: make a new one, deploy it, then revoke the old.

2. Your people

Tell Mnemocast about each person before they join, under your own id for them (letters, digits, _ and -):

curl -X PUT https://api.mnemocast.com/v1/users/athlete-1042 \
  -H "Authorization: Bearer $MNEMOCAST_KEY" -H "Content-Type: application/json" \
  -d '{"display_name": "Ada Obi", "age_band": "13_17"}'

3. Rooms

A room is a lasting place with its own rules: a mentor's room, a team's room, a clinic. Make one and keep its id:

curl -X POST https://api.mnemocast.com/v1/rooms \
  -H "Authorization: Bearer $MNEMOCAST_KEY" -H "Content-Type: application/json" \
  -d '{"name": "Coach Sam: 1:1 sessions", "alias": "coach-sam", "allows_minors": true}'

The settings you are most likely to want:

Setting Default What it does
alias none A name for the room within your organisation, for links and lookups (GET /v1/rooms/by-alias/{alias}).
allows_minors false Lets people aged 13 to 17 join.
agent_policy forbidden host_only or any_participant lets people bring AI agents. Leave it forbidden unless your app shows everyone which agents are in the call (part two).
capacity 50 Up to 1,000 people.
join_policy tenant_users members_only lets in only people you add with PUT /v1/rooms/{id}/members/{user}.
join_muted false Apps start with the microphone off.
max_meeting_minutes none A shorter time limit than Mnemocast's own (8 hours, which a host can extend to 12 and 16).

A host can change some of these later with PATCH /v1/rooms/{id}. Members and their roles: PUT /v1/rooms/{id}/members/{user} with {"role": "host"}, "presenter" or "attendee".

4. A call

Start it. One meeting at a time per room: if one is already under way, you get that one back, so two people pressing Start end up in the same call.

curl -X POST https://api.mnemocast.com/v1/rooms/$ROOM_ID/sessions -H "Authorization: Bearer $MNEMOCAST_KEY"

It answers with the session's id.

Let a person in. Your server asks for their pass, one per person per device:

curl -X POST https://api.mnemocast.com/v1/sessions/$SESSION_ID/join \
  -H "Authorization: Bearer $MNEMOCAST_KEY" -H "Content-Type: application/json" \
  -d '{"user": "athlete-1042", "role": "attendee", "device_id": "c1f0a9e2"}'

Connect in your app with LiveKit's open-source SDK and the two values:

// Web: npm install livekit-client
import { Room } from 'livekit-client';
const room = new Room({ adaptiveStream: true, dynacast: true });
await room.connect(url, token);
await room.localParticipant.setMicrophoneEnabled(!start_muted);
await room.localParticipant.setCameraEnabled(true);

On React Native, use @livekit/react-native and @livekit/react-native-webrtc (call registerGlobals() once at start-up), then the same Room code. It needs a development build (Expo Go cannot run it). Ask for a new pass from your server if the connection is lost for good.

End it with POST /v1/sessions/{id}/end, or let it end on its own: five minutes after the last person leaves, or at its time limit.

Before you launch, point your people at app.mnemocast.com/network: it checks that their network lets calls through (company firewalls and school Wi-Fi sometimes do not). Their IT team will want the network requirements: HTTPS to *.mnemocast.com, media over UDP with fallbacks over TCP, and media.mnemocast.com exempt from TLS inspection.

5. Booked meetings (optional)

If your product books calls ahead, Mnemocast can keep the diary: POST /v1/rooms/{id}/meetings with a title, starts_at (an instant, such as 2026-11-04T17:00:00Z), time_zone (such as America/New_York, where a repeating meeting keeps its clock time), duration_minutes, an optional recurrence, and invitees ({"user": "<your id>"} or {"email": "…"}). A booked meeting never starts itself: a host starts it, from ten minutes before. Change or cancel it with PATCH and DELETE /v1/meetings/{id}.

6. Be told what happened

Give Mnemocast an https address on your server:

curl -X POST https://api.mnemocast.com/v1/webhooks \
  -H "Authorization: Bearer $MNEMOCAST_KEY" -H "Content-Type: application/json" \
  -d '{"url": "https://api.example.com/mnemocast/events"}'

The answer includes a secret (whsec_…), shown once: keep it with your key. Mnemocast then POSTs each event to you, within about a minute:

Event When
session.started The first person connected.
participant.joined A person's first device connected.
participant.left Their last device went, with reason: left, dropped or removed. A drop of under 30 seconds is never sent.
session.ended The meeting ended, with the attendance (below).
meeting.scheduled, .updated, .cancelled, .starting Booked meetings; starting is ten minutes before.

Each delivery is {"id": "evt_…", "type": "<event>", "created_at": "…", "data": {…}}. Session events carry data.session, data.seq (to put them in order) and data.occurred_at; people are named by your ids. Pass "events": [...] to receive only some.

Check every delivery is from Mnemocast before you act on it. The Mnemocast-Signature header is t=<unix time>,v1=<hex>: an HMAC-SHA256 of "<t>.<the raw body>", keyed with your secret exactly as shown:

import { createHmac, timingSafeEqual } from 'node:crypto';

export function fromMnemocast(secret, header, rawBody) {
  const { t, v1 } = Object.fromEntries(header.split(',').map((p) => p.split('=')));
  if (!t || !v1 || Math.abs(Date.now() / 1000 - Number(t)) > 300) return false; // five minutes, against replays
  const want = createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
  return want.length === v1.length && timingSafeEqual(Buffer.from(want), Buffer.from(v1));
}

Answer with any 2xx quickly, then do the work. Anything else is tried again after 1, 5, 30, 120, 360 and 1,440 minutes; the same event keeps its id, so ignore one you have already handled. POST /v1/webhooks/{id}/test sends a test, and GET /v1/webhooks/{id}/deliveries shows what was sent and how you answered.

Attendance, for paying people by the time a call lasted or keeping records: GET /v1/sessions/{id}/attendance gives, for each person, when they first joined and last left, connected_seconds (with at least one device connected), device_seconds (summed across devices: what you are billed for) and the intervals. It is the same summary session.ended carries, and the way to recover it if that delivery never reached you.

7. Or let your people use Mnemocast's own apps

Instead of building the call into your app, you can send people to Mnemocast's web and phone apps with a room's link (app.mnemocast.com/r/<your slug>/<alias>). They then sign in with your sign-in service, so they arrive as your people: register it once with

curl -X POST https://api.mnemocast.com/v1/identity-providers \
  -H "Authorization: Bearer $MNEMOCAST_KEY" -H "Content-Type: application/json" \
  -d '{"issuer": "https://login.example.com", "audience": "<your client id>"}'

Your provider must be OpenID Connect. The person's id comes from the sub claim (or external_id_claim), matched to the ids you use in PUT /v1/users; their age band from a mnemocast_age_band claim (or age_band_claim), or from the user you created. Someone with neither cannot sign in. A sign-in service belongs to one organisation on Mnemocast.

8. Usage and limits

9. What you are responsible for

Questions about your integration: hello@mnemocast.com.

Bring your own agent to Mnemocast

Most people want their AI assistant, not this. To bring ChatGPT, Claude, Copilot or another assistant you already use, open My agents → Your AI assistant → Connect an assistant at app.mnemocast.com: no address, nothing to run. This guide is for developers who build and host an agent themselves.

A personal agent is an AI agent of your own, running wherever you choose, that you can bring into your Mnemocast meetings. Everyone in the meeting sees it is yours, who runs it, and what you say it keeps. It gets the meeting as text (never audio or video) and answers only when you ask it.

The working example is agent.mjs (about 90 lines, Node.js 20, with its package.json): npm install, then node agent.mjs.

1. What your agent must do

Mnemocast connects to your agent over a WebSocket. Your agent:

  1. Checks the hello. Each connection carries Authorization: Bearer <JWT>, signed by Mnemocast (ES256). Verify it against https://api.mnemocast.com/.well-known/jwks.json with issuer https://api.mnemocast.com, audience mnemocast-agent, and type mnemocast-hello+jwt. Refuse anything else. The claims say who it is for: sponsor (you), role (bot_listener scribe or bot_speaker orator), participant_id, session_id.
  2. Passes the address check. When you add it, and whenever you press "Check again", Mnemocast connects with a hello whose claims include purpose: "verify", then sends {"type":"verify","nonce":"…"}. Answer {"type":"verified","nonce":"…"} (the same nonce) within 10 seconds.
  3. Listens. In a meeting it is sent:
    • {"type":"transcript","speaker","text","start","end"}: what was said (people who objected to agents are never in it);
    • {"type":"chat","from","text","at"}: what was written;
    • {"type":"instructions","text"}: your standing instructions;
    • participant_joined, participant_left, participant_excluded …
  4. Answers when asked. When you address it by name, it is sent {"type":"prompt","prompt_id","from","via","text","can_speak","private"}. Reply once with {"type":"post_chat","prompt_id","text"}. If it is an orator and can_speak is true, it may speak instead: {"type":"speak","prompt_id","text","end":true}. A private question is answered in writing and Mnemocast shows the answer to you alone. To speak at all, declare "It can answer aloud" when you add it; a text-only agent is never made an orator. A personal agent may be voiced 1,000 characters at a time, 5,000 a meeting and 20,000 a day; past that, speak is refused with voice_limit_reached and you answer in chat. Anything unprompted, or a second reply, is refused.
  5. May propose an action, for you to confirm (ADR 0034). Each prompt also carries "can_propose" and "sponsor_time":{"now_local","time_zone"} (your local time, to read "Thursday at 2" by). Where can_propose is true, it may send, once for that prompt, before or with its reply: {"type":"propose","prompt_id","proposal":{"kind":"meeting","title","starts_local","duration_minutes","invitees":["names"]}} or {"kind":"reminder","at_local","text"}, with local times as YYYY-MM-DDTHH:MM. Mnemocast answers {"type":"proposed","prompt_id","proposal_id","summary"}, or an error saying why not (the room does not allow it, three already waiting, a name nobody in the meeting has). "invitees":["everyone"] invites everyone in the meeting. Nothing happens until you confirm it in Mnemocast; a meeting is yours, and anyone else is only invited: each is asked to accept, by a card in the meeting and a notification. Say in the reply that it is waiting for you. A host may ask your agent too, unless you turn off Let hosts propose actions on it.

2. Where it must run: its address

The address is wherever you host the agent: Mnemocast connects to it, so it must be reachable from the internet. For example wss://agent.example.com/mnemocast on your own server, or a tunnel to your computer while you test.

3. Adding it

At app.mnemocast.com, open My agents → Your own agent (for developers) → Add an agent: its name, who runs it, its address, and whether it keeps or trains on what it hears. Mnemocast checks it answers, then it appears in your Bring an agent list, marked Yours, in rooms that allow personal agents (all the rooms you make in Mnemocast do).

3a. Or: the AI assistant you already use

You do not need to run anything to bring ChatGPT, Claude, Copilot or another assistant that supports MCP. Under My agents → Your AI assistant, choose Connect an assistant, follow its steps with the address https://api.mnemocast.com/mcp, and allow it. It then appears in Bring an agent marked MCP, and you ask it about the meeting in the assistant itself. An assistant's own web address (chatgpt.com, claude.ai…) is not an agent address: it does not speak this protocol, and the check would fail.

4. What you are responsible for

What your agent does with what it hears is up to you, and you are responsible for it, including your declarations: the room is told what you say it keeps, and that Mnemocast has not checked it.