AGNTDocs

Assistants, messaging and calls

Create assistants, tie phone numbers and email addresses to users, and reach Prime over iMessage, SMS, WhatsApp, email, Slack, Teams, phone calls and live voice.

Assistants, messaging and calls

An assistant is the identity your users talk to: a name, a personality, a writing style, an email inbox and phone lines. Users reach it on the channels they already use. AGNT works out who is writing, opens or continues that person's chat, and Prime answers in the same thread. This guide follows Harborview as it gives Jane Rivera an assistant named Ava, connects Jane's phone, adds phone lines, and talks to Ava over the API, Slack and voice.

How it works

 Jane texts Ava's number (+14155550142) from +14155550187
        │
        ▼
 AGNT matches the sender ──► verified phone identifier → Jane Rivera
        │                    (no match → unknown-sender handling)
        ▼
 Jane's chat with Ava on iMessage ──► Prime runs a turn with Jane's memory,
        │                              tasks, connections and settings
        ▼
 Ava replies on the same channel, in the same thread
RecordDescription
AssistantThe identity: profile fields, an email address, phone lines (channels), a voice, and a scope that sets who can use it.
IdentifierA handle that belongs to one user: an email address, a phone number, or a sign-in account. A verified phone number or email address tells AGNT who sent an inbound message.
ChatOne conversation thread. Each 1:1 thread on a channel is its own chat. A user's tasks are shared across all of their chats.

A phone thread belongs to the person, not the number. One assistant number can serve every user of your app, because AGNT tells users apart by the verified number they text from. When a number moves to a different user, the new holder does not inherit the previous holder's chat, history or tasks.

Create an assistant

Create Ava on Jane's roster. The call is idempotent on name for that user, and the first assistant on an empty roster becomes the user's primary.

curl https://api.agnt.ai/users/6702b1e8f9a0b1c2d3e4f5a6/assistants \
  -X POST \
  -H "Authorization: Bearer $AGNT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Ava",
    "email": "[email protected]",
    "personality": "Warm, brief and precise. Confirms details before acting.",
    "writingStyle": { "professionalism": 2, "humor": 1, "emojiUse": 0 }
  }'
{
  "ok": true,
  "assistant": {
    "id": "6702b4c1d2e3f4a5b6c7d8e9",
    "account": "66f1a2b3c4d5e6f708192a3b",
    "organization": null,
    "team": null,
    "user": "6702b1e8f9a0b1c2d3e4f5a6",
    "scope": "user",
    "name": "Ava",
    "email": "[email protected]",
    "avatarUrl": null,
    "signature": "--\nAssistant to {{name}}",
    "personality": "Warm, brief and precise. Confirms details before acting.",
    "writingStyle": { "professionalism": 2, "humor": 1, "emojiUse": 0 },
    "voice": null,
    "status": "active",
    "talkToStrangers": true,
    "inboxPlatform": null,
    "channels": {
      "imessage": null,
      "sms": null,
      "whatsapp": null,
      "voice": { "status": "none" }
    },
    "isPrimary": true,
    "createdAt": "2026-11-02T16:24:11.000Z",
    "updatedAt": "2026-11-02T16:24:11.000Z"
  }
}

The response is 201 for a new assistant and 200 when Ava already exists on Jane's roster.

NameTypeRequiredDescription
namestringYesThe assistant's name.
emailstringNoThe assistant's own address, unique within your account. Generated when omitted.
personality, backstorystringNoFree text that shapes how the assistant writes and talks.
writingStyleobjectNoprofessionalism, humor and emojiUse, each 0 to 3, plus optional free text characterInspiration, externalCommunication and oneOnOneCommunication.
avatarstringNoImage URL. Returned as avatarUrl.
signature, signatureEditorModestringNoEmail signature, and visual or html.
gender, pronouns, countrystringNoPersona details. country (ISO 3166-1 alpha-2) defaults to the user's country and never changes the output language.
voiceobjectNo{ voiceId, provider } for calls. See Live voice.
metadataobjectNoYour own key-value data.

channels is read-only. Phone lines are added as described in Phone lines. inboxPlatform reports google or outlook when the assistant runs on a real Gmail or Outlook mailbox.

To check an address first, call GET /assistants/check-email?slug=ava, which returns { available, email }. GET /assistants/generate?name=Ava suggests free addresses.

With the TypeScript SDK:

import { AgntClient } from '@agnt-sdk/client';

const client = await AgntClient.create();

const assistants = await client.assistants.list();
await client.assistants.update('6702b4c1d2e3f4a5b6c7d8e9', {
  personality: 'Warm, brief and precise.',
});

Shared assistants

POST /assistants creates an assistant that several users share. Call it with an account-wide key and no X-User-Email.

NameTypeRequiredDescription
namestringYesThe assistant's name.
emailstringYesThe assistant's address, unique within your account.
scopestringNoaccount (default), organization, team or user. Sets who can see and hire the assistant.
organization, teamstringNoThe organization or team, for those scopes.
talkToStrangersbooleanNoWhether the assistant engages people who are neither users of your app nor known contacts. true by default.

It also accepts every profile field in the table above.

Rosters and the primary assistant

Every assistant a user can see is on their roster, and one roster member is their primary: the assistant that answers for that user by default. A user can have several assistants, each with its own identity and lines.

curl https://api.agnt.ai/users/6702b1e8f9a0b1c2d3e4f5a6/assistants/6702b4c1d2e3f4a5b6c7d8e9/primary \
  -X POST \
  -H "Authorization: Bearer $AGNT_API_KEY"
MethodPathDescription
GET/users/:userId/assistantsThe roster, with isPrimary on each assistant and primaryAssistantId.
POST/users/:userId/assistants/:assistantId/hireAdd an assistant the user can see to their roster.
POST/users/:userId/assistants/:assistantId/primaryMake an assistant the primary. Returns the updated user.
DELETE/users/:userId/assistants/:assistantIdRelease one of the user's own assistants.

Releasing an assistant deletes it and retires its email address. The call returns 409 for the primary, and for an assistant with open tasks unless you pass ?transfer=primary to move that work to the primary. Shared and account-wide assistants cannot be released (403).

Identifiers

AGNT matches inbound messages to users through identifiers. A phone number or secondary email resolves a sender only when it is verified. An unverified number is stored but identifies no one, so a text from it is handled as coming from an unknown sender.

Add Jane's mobile number as a verified identifier:

curl https://api.agnt.ai/identifiers \
  -X POST \
  -H "Authorization: Bearer $AGNT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "phone",
    "value": "+14155550187",
    "userId": "6702b1e8f9a0b1c2d3e4f5a6",
    "verified": true,
    "label": "mobile"
  }'

The response is 201 with the new identifier and the user it belongs to.

NameTypeRequiredDescription
typestringYesemail, phone, google, microsoft, apple, external_id, employee_id or custom.
valuestringYesThe handle. Unique within your account.
userIdstringNoThe user to add it to, with an account-wide key. Called as a user, it is added to that user.
verifiedbooleanNoWhether the handle is verified. Honored only from account-wide credentials.
labelstringNoYour label, such as mobile.

A verified phone number decides whose texts land in whose chat, and a verified email decides who signs in as whom. For that reason only account-wide credentials (the console or an account-wide API key) set verified. Set it only for numbers and addresses you have verified yourself. User-scoped callers verify through the hosted app's one-time-code flow, and a verified value they send is ignored.

Phone numbers are stored in E.164 form. A number without a leading + is parsed with the user's country as the default. An unparseable number returns 400 with invalid_phone. A value already held by any user in your account returns 400.

Phone identifiers also report what AGNT has learned about the number: iMessageCapable, whatsAppCapable, preferredTextPlatform, and messagingWindow (when the person last messaged the assistant, per channel).

List Jane's phone numbers as Jane:

curl "https://api.agnt.ai/identifiers?type=phone" \
  -H "Authorization: Bearer $AGNT_API_KEY" \
  -H "X-User-Email: [email protected]"
const jane = client.as('[email protected]');
const phones = await jane.identifiers.list({ type: 'phone' });
TypeUse
emailSign-in and secondary addresses. POST /identifiers/:identifierId/make-primary makes one the user's primary email.
phoneNumbers the user texts and calls from.
google, microsoft, appleSign-in and connected accounts.
external_id, employee_id, customYour own keys for the user.

POST /identifiers/:identifierId/link moves an identifier to another user and requires account-wide credentials. A user's last identifier and their primary email cannot be deleted.

Channels

Channel1:1GroupsSetup
iMessageYesYesAssistant phone line
SMSYesYesAssistant phone line
WhatsAppYesNoAssistant phone line
Email (AGNT-hosted inbox)YesThreads with several peopleAutomatic, at the assistant's email
Email (the user's Gmail or Outlook)YesThreads with several peopleThe user connects over OAuth
SlackDMsChannelsEach assistant is its own Slack app
Microsoft TeamsYesNoAn organization or team admin connects Teams
Phone callsInbound and outboundNoVoice line on the assistant's number
Live voiceIn your appNoA voice engine on the assistant

Phone lines

Request numbers for the user's primary assistant with POST /phone-line-requests, called as the user.

curl https://api.agnt.ai/phone-line-requests \
  -X POST \
  -H "Authorization: Bearer $AGNT_API_KEY" \
  -H "X-User-Email: [email protected]" \
  -H "Content-Type: application/json" \
  -d '{
    "requestedChannels": { "imessage": true, "sms": true, "voice": true },
    "notes": "415 area code"
  }'
{
  "ok": true,
  "request": {
    "id": "6702cc48f9a0b1c2d3e4f5a6",
    "assistant": "6702b4c1d2e3f4a5b6c7d8e9",
    "requestedChannels": { "imessage": true, "sms": true, "whatsapp": false, "voice": true },
    "notes": "415 area code",
    "status": "pending",
    "createdAt": "2026-11-02T16:27:03.000Z"
  }
}
NameTypeRequiredDescription
requestedChannelsobjectNoimessage, sms, whatsapp and voice, each a boolean that defaults to false.
notesstringNoPreferences for the numbers, such as an area code.

When the numbers are ready, the request's status becomes fulfilled and the numbers appear on the assistant under channels.imessage, channels.sms and channels.whatsapp. An assistant's voice line uses the same number as its iMessage line. channels.voice.status reports the voice line: none, provisioning, pending_a2p (waiting on US carrier registration), active or failed. A dismissed request carries a dismissalReason.

GET /phone-line-requests/mine returns the user's latest request, or "request": null. POST /phone-line-requests returns 400 when the user has no assistant or already has a pending request.

iMessage, SMS and WhatsApp

Jane texts +14155550142. AGNT matches her verified number, continues her chat with Ava, and Prime replies on the same channel.

In iMessage and SMS group threads, the assistant works for its owner. Anything it does on the owner's behalf follows the owner's autonomy settings, and the owner's private conversations stay private.

WhatsApp allows a business to message a person freely only within 24 hours of that person's last message. AGNT tracks this per person in messagingWindow.

Email

Every assistant has an AGNT-hosted inbox at its email address, and mail sent there reaches Prime like any other message. Inbound mail is dropped if it fails spam or virus checks, if DMARC fails for a domain that publishes p=reject, or if SPF and DKIM both fail. Prime treats inbound mail as untrusted content.

An assistant can run on a real Gmail or Outlook mailbox instead, which the user authorizes in the hosted app. inboxPlatform then reports google or outlook.

A user can also connect their own Gmail or Outlook over OAuth from the hosted app. Prime can then read, triage and draft in the user's mailbox and send as the user. Sending as the user is an outbound action and follows the user's autonomy settings.

Slack

Each assistant can be its own Slack app, with its own bot name and avatar. POST /assistants/:assistantId/slack-apps creates the app and returns a manifest to apply in Slack. Give Ava a Slack app in Harborview's workspace, called as Jane:

curl https://api.agnt.ai/assistants/6702b4c1d2e3f4a5b6c7d8e9/slack-apps \
  -X POST \
  -H "Authorization: Bearer $AGNT_API_KEY" \
  -H "X-User-Email: [email protected]" \
  -H "Content-Type: application/json" \
  -d '{ "kind": "workspace", "botDisplayName": "Ava" }'

The response is 201 with slackApp (status is pending_credentials until the app's Slack credentials are in place) and manifest (manifestJson, createAppUrl, eventsUrl and redirectUrls). Calling again for an existing app returns it with 200.

kindThe appCall withErrors
marketplaceYour listed app, for an account-wide assistantAn account key, no X-User-Email403 for any other caller. 400 not_system_assistant for an assistant that is not account-wide.
workspaceAn app internal to one Slack workspace, for a user's own or shared assistantThe user (X-User-Email), who must have the assistant and whose organization allows Slack403 user_required, 403 slack_not_allowed, 403 system_assistant_console_only for an account-wide assistant.

Users install the app with "Add to Slack" (GET /oauth/slack/assistant/authorize) and link their Slack identity with "Sign in with Slack" (GET /oauth/slack/assistant/link). GET /assistants and GET /assistants/:assistantId include a per-user slack status: configured, connected, and deep links to message the bot. Each workspace install has a kill switch and a setting for how the assistant treats Slack users who are not users of your app.

Microsoft Teams

An organization or team admin connects Microsoft Teams with GET /oauth/teams/authorize?target=team&targetId=6702ab52c3d4e5f6a7b8c9d0, or target=org for the whole organization. Your account needs a Teams app configured first.

Chats

A chat is one conversation thread between a user and an assistant. Messages on a channel land in that channel's chat automatically. Use the chats API to run an in-app conversation in your own product, or to read any of a user's threads. Every chats call is user-scoped.

Start a chat and get a reply

  1. Open a chat between Jane and Ava.

    curl https://api.agnt.ai/chats \
      -X POST \
      -H "Authorization: Bearer $AGNT_API_KEY" \
      -H "X-User-Email: [email protected]" \
      -H "Content-Type: application/json" \
      -d '{ "assistantId": "6702b4c1d2e3f4a5b6c7d8e9", "title": "Brightline renewal" }'
    
    {
      "ok": true,
      "chat": {
        "id": "6702c5d1e2f3a4b5c6d7e8f9",
        "title": "Brightline renewal",
        "status": "active",
        "platform": "chat",
        "user": "6702b1e8f9a0b1c2d3e4f5a6",
        "assistant": "6702b4c1d2e3f4a5b6c7d8e9",
        "messageCount": 0,
        "lastMessageAt": null,
        "createdForTaskId": null,
        "modelTier": "medium",
        "createdAt": "2026-11-02T16:30:12.000Z",
        "updatedAt": "2026-11-02T16:30:12.000Z"
      }
    }
    
  2. Send Jane's message and run Prime on it.

    curl https://api.agnt.ai/chats/6702c5d1e2f3a4b5c6d7e8f9/process \
      -X POST \
      -H "Authorization: Bearer $AGNT_API_KEY" \
      -H "X-User-Email: [email protected]" \
      -H "Content-Type: application/json" \
      -d '{ "message": { "content": "What is the status of the Brightline Freight renewal?" } }'
    
  3. Read the thread.

    curl "https://api.agnt.ai/chats/6702c5d1e2f3a4b5c6d7e8f9/messages?perPage=20" \
      -H "Authorization: Bearer $AGNT_API_KEY" \
      -H "X-User-Email: [email protected]"
    

POST /chats parameters:

NameTypeRequiredDescription
assistantIdstringYesOne of the user's assistants.
titlestringNoThe chat's title.
metadataobjectNoYour own key-value data.
modelTierstringNolow, medium, high or ultra. Defaults to the organization's chat tier, then the account's, then medium. See Prime.

POST /chats/:chatId/process parameters:

NameTypeRequiredDescription
messageobjectNoA user message to save and respond to: content (required), plus optional files ([{ fileId }] from the user's Files), referenceId and metadata.
triggeringActivityIdstringNoThe id of an already-saved message to respond to.

Omit the body to run on messages already saved. POST /chats/:chatId/messages saves a message without running Prime.

The response is a text/event-stream. The HTTP status is 200 even when the run ends in an error event.

EventData
status_updateProgress during the run.
interim_message{ message, activityId }, a message Prime sends before its final reply.
message{ ok, message }, the reply.
error{ error, code, status }.

Each assistant message in GET /chats/:chatId/messages carries a read-only delivery object for outbound messages: state is staged (waiting in an approval), held, provider_accepted (sent and accepted for delivery) or unknown (no confirmed delivery record), with the taskId that sent it and the cartId of its approval, if any.

List and manage chats

curl "https://api.agnt.ai/chats?platform=imessage,sms&perPage=20" \
  -H "Authorization: Bearer $AGNT_API_KEY" \
  -H "X-User-Email: [email protected]"
NameTypeDescription
platformstringOne channel or a comma-separated list: chat, email, slack, teams, sms, whatsapp, imessage, phone.
statusstringactive (default) or archived.
assistantIdstringOnly chats with this assistant.
qstringSearch.
userEmailstringWith an account key and no X-User-Email: only chats of this user.
page, perPageintegerPagination. perPage is at most 100.

GET /chats/:chatId returns one chat, DELETE /chats/:chatId deletes it, and GET /chats/:chatId/usage returns tokens per run and the credits charged for each run.

Outbound messages

Prime's replies to the user go out directly. Anything sent to someone else, such as an email to [email protected], a text, a Slack message or a call to a third party, is an outbound action and follows the user's autonomy settings. An approved action sends exactly the content that was shown. See Tasks and approvals.

People who are not users of your app are handled by the assistant's stranger settings: talkToStrangers, and the per-install setting on Slack. Unusual requests from them are held for the owner.

Phone calls

A call to the assistant's number is answered with one of its greetings. When the caller is a recognized user, Prime runs the conversation with that user's memory and tasks. Prime places outbound calls when the user asks: to the user, or to a third party to get something done. Outbound calls follow the user's autonomy settings, with separate settings for calling as the assistant and calling as the user. Every attempt records its status, duration, a result summary and a transcript.

curl "https://api.agnt.ai/voice/calls?limit=25" \
  -H "Authorization: Bearer $AGNT_API_KEY" \
  -H "X-User-Email: [email protected]"
{
  "ok": true,
  "calls": [
    {
      "source": "direct",
      "id": "6702cd59a0b1c2d3e4f5a6b7",
      "relationship": "user",
      "target": { "phoneNumber": "+14155550187", "displayName": "Jane Rivera" },
      "goal": "Brief Jane on the Brightline Freight renewal before the board meeting",
      "status": "completed",
      "disposition": null,
      "startedAt": "2026-11-17T17:00:04.000Z",
      "endedAt": "2026-11-17T17:03:12.000Z",
      "durationSeconds": 188,
      "resultSummary": "Jane approved sending the 2027 terms to Brightline legal.",
      "transcript": "Ava: Hi Jane, it's Ava calling about the Brightline renewal."
    }
  ]
}
FieldDescription
sourcedirect, or task for an attempt Prime made on a task (with taskId and taskTitle).
relationshipuser, internal (someone in the user's organization) or third_party.
statusDirect calls: calling, completed, failed, no_answer, voicemail. Task calls: pending, preparing, calling, in_progress, completed, failed, needs_input.

limit is at most 100.

Greetings

Each assistant has stored opening lines: inbound lines for answering, and outbound templates for calling someone by name. Each list holds 1 to 5 lines of up to 160 characters. Outbound templates contain exactly one {firstName}, and inbound lines contain none. Lines are generated from the assistant's personality, and lines you write are never overwritten by generation.

curl https://api.agnt.ai/assistants/6702b4c1d2e3f4a5b6c7d8e9/voice-greetings \
  -X PUT \
  -H "Authorization: Bearer $AGNT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "inbound": ["Hi, this is Ava with Harborview. How can I help?"],
    "outbound": ["Hi {firstName}, this is Ava from Harborview."]
  }'

GET /assistants/:assistantId/voice-greetings returns the lines, their source (generated or owner), generating while a generation runs, and the validation limits. POST /assistants/:assistantId/voice-greetings/regenerate takes { "scope": "both" | "inbound" | "outbound", "replaceOwnerEdits": false }, up to 5 times per hour per assistant.

Live voice

Users can talk to their assistant live in the hosted app and in your own apps. Each assistant uses its own engine and voice through voice: { provider, voiceId }. GET /voice/providers lists the engines your account has credentials for, each with its voices and default voice. available is true when an engine can take calls. Engines include live speech-to-speech engines that run on credentials you supply for your own cloud project. GET /voice/providers lists what is available. Voice models follow your account's model tier settings.

Start a session for Jane with Ava, scoped to the Brightline renewal task:

curl https://api.agnt.ai/voice/live/session \
  -X POST \
  -H "Authorization: Bearer $AGNT_API_KEY" \
  -H "X-User-Email: [email protected]" \
  -H "Content-Type: application/json" \
  -d '{ "assistantId": "6702b4c1d2e3f4a5b6c7d8e9", "taskId": "6702c3a9b8c7d6e5f4a3b2c1" }'
{
  "ok": true,
  "provider": "google-live",
  "sessionId": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4a6f18",
  "websocketUrl": "wss://voice.agnt.ai/live",
  "sessionToken": "lvs_9Qm2xT7rKc4bWz8pLf3nHd6vYa1sEj5u",
  "inputSampleRate": 16000,
  "outputSampleRate": 24000,
  "metadata": {
    "accountId": "66f1a2b3c4d5e6f708192a3b",
    "userId": "6702b1e8f9a0b1c2d3e4f5a6",
    "assistantId": "6702b4c1d2e3f4a5b6c7d8e9",
    "taskId": "6702c3a9b8c7d6e5f4a3b2c1"
  }
}
NameTypeRequiredDescription
assistantIdstringNoDefaults to the assistant of the chat or task.
chatIdstringNoScope the session to a chat.
taskIdstringNoScope the session to a task. With neither, a new chat is created.

The client connects to websocketUrl with the single-use sessionToken. POST /voice/live/cancel with { sessionId } cancels a session before it connects.

StatusCodeMeaning
404call_context_not_foundThe assistant, chat or task is not this user's.
409voice_engine_not_configured, voice_not_configuredThe assistant's voice engine is not set up.
429voice_session_rate_limitedToo many sessions in a short time.

Endpoints

MethodPathDescription
GET/assistantsAssistants visible to the caller, with per-user slack status
POST/assistantsCreate a shared assistant
GET PUT DELETE/assistants/:assistantIdRead, update or delete an assistant
GET/assistants/check-emailWhether an address is free (?slug=)
GET/assistants/generateName and address suggestions (?name=)
GET PUT/assistants/:assistantId/voice-greetingsRead or edit greeting lines
POST/assistants/:assistantId/voice-greetings/regenerateRegenerate greeting lines
GET POST/users/:userId/assistantsRoster, or create an assistant on it
POST/users/:userId/assistants/:assistantId/hireAdd a visible assistant to the roster
POST/users/:userId/assistants/:assistantId/primarySet the primary
DELETE/users/:userId/assistants/:assistantIdRelease an assistant
GET POST PUT DELETE/users/:userId/assistantThe user's primary assistant
GET POST/identifiersList (?type=&userId=) or create identifiers
GET PUT DELETE/identifiers/:identifierIdRead, update (verified, label, metadata) or delete
POST/identifiers/:identifierId/linkMove to another user
POST/identifiers/:identifierId/make-primaryMake an email the user's primary
POST/phone-line-requestsRequest phone lines
GET/phone-line-requests/mineThe user's latest request
POST/assistants/:assistantId/slack-appsCreate the assistant's Slack app
GET/oauth/slack/assistant/authorize"Add to Slack" URL
GET/oauth/slack/assistant/link"Sign in with Slack" URL
GET/slack/workspacesThe user's linked Slack workspaces
GET/oauth/teams/authorizeConnect Microsoft Teams (?target=&targetId=)
GET POST/chatsList or create chats
GET DELETE/chats/:chatIdRead or delete a chat
GET POST/chats/:chatId/messagesList or save messages
POST/chats/:chatId/processRun Prime and stream the reply
GET/chats/:chatId/usageTokens and credits per run
GET/voice/providersVoice engines and voices
POST/voice/live/sessionStart a live voice session
POST/voice/live/cancelCancel a session before it connects
GET/voice/callsThe user's call history

See also