Agent SDK
Agent SDK is for CRM and internal support workspaces that need to show Ringnity agent data without opening the full dashboard. It uses a short-lived agent token, not a public website key.
1. Create an agent token
Your backend calls Server API with a private Server API key. The response token is safe to pass to your internal CRM page for a short time.
curl -X POST "https://api.ringnity.com/api/server/sdk-token/agent" \
-H "Authorization: Bearer SERVER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"username": "agent.one",
"scopes": [
"agent:profile:read",
"agent:presence:read",
"agent:presence:write",
"agent:conversations:read",
"agent:conversations:write",
"agent:messages:read",
"agent:messages:write",
"agent:media:delete",
"agent:calls:read",
"agent:calls:write"
],
"expiresIn": 900
}'Token rules
- Keep the Server API key on your backend only.
- Agent token includes tenant, agent, role, department, and scopes.
- Default expiry is 900 seconds. Maximum expiry is 3600 seconds.
- Use scope
server:agent-token:writeon the Server API key.
2. Use the token in your CRM page
Minimal JavaScript flow
Your CRM page asks your own backend for an Agent SDK token, then uses the Agent SDK object model to read profile, set presence, open inbox, send replies, and mark messages as read.
const tokenResponse = await fetch('/api/ringnity/agent-token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ userId: currentUser.id })
});
const { token } = await tokenResponse.json();
const agent = await RingnityAgent.create({ token });
await agent.presence.set('online');
const inbox = await agent.conversations.list({
status: 'open',
limit: 10
});Read a conversation thread
const conversationId = inbox.conversations[0].conversationId;
const thread = await agent.conversations.messages(conversationId, {
limit: 50
});
await agent.conversations.reply(conversationId, {
body: 'Hi, how can I help?'
});
await agent.conversations.assign(conversationId, {
assignedAgentId: currentAgent.id,
reason: 'Accepted from CRM inbox'
});
await agent.media.deleteAttachment(conversationId, attachmentMessageId, {
reason: 'Removed by agent request'
});Typed client example
import { RingnityAgent } from '@ringnity/agent-sdk';
const agent = await RingnityAgent.create({ token });
await agent.presence.set('online');
const inbox = await agent.conversations.list({
status: 'open',
limit: 10
});
if (inbox.conversations[0]) {
const conversationId = inbox.conversations[0].conversationId;
const thread = await agent.conversations.messages(conversationId);
await agent.conversations.reply(conversationId, {
body: 'Hi, how can I help?'
});
const lastMessage = thread.messages.at(-1);
if (lastMessage) {
await agent.conversations.markRead(conversationId, lastMessage.messageId);
}
}3. Add call controls when your workspace handles calls
Incoming call popup
Your CRM owns the popup and buttons. The SDK handles the live connection, call setup, media permission, and cleanup.
import { RingnityAgent } from '@ringnity/agent-sdk';
const agent = await RingnityAgent.create({ token });
await agent.calls.goOnline();
agent.calls.onIncomingCall((call) => {
showIncomingCallPopup({
customerName: call.customer.name ?? 'Customer',
onAccept: async () => {
await call.accept();
showActiveCallPanel(call);
},
onReject: () => call.reject('Agent declined')
});
});Active call buttons
agent.calls.onIncomingCall((call) => {
call.attachView({
local: document.querySelector('#agent-video'),
remote: document.querySelector('#customer-video')
});
document.querySelector('#accept').onclick = () =>
call.accept({ video: call.type === 'video' });
document.querySelector('#mute').onclick = () => call.mute();
document.querySelector('#unmute').onclick = () => call.unmute();
document.querySelector('#hold').onclick = () => call.hold();
document.querySelector('#resume').onclick = () => call.resume();
document.querySelector('#transfer').onclick = () =>
call.transfer({ departmentId: selectedDepartmentId });
document.querySelector('#start-video').onclick = () => call.startVideo();
document.querySelector('#stop-video').onclick = () => call.stopVideo();
document.querySelector('#end').onclick = () => call.end();
});Full call playground
The repository includes a plain HTML CRM call page at sun_webrtc_sdk/examples/agent-call-playground. Use it to see the token endpoint, agent online button, incoming popup, active call panel, transfer input, and optional video containers in one place.
cd sun_webrtc_sdk/examples/agent-call-playground npm install --package-lock=false npm run dev # Then update TOKEN_ENDPOINT in index.html to your backend route: # /ringnity/agent-token # or, for the current demo customer backend: # https://sadavir.id/ringnity/agent-token
Agent SDK object model
Read the current agent, tenant, role, department, and scopes.
Read whether the agent is online, busy, offline, or in an active call.
Set the agent status from your CRM workspace.
Show open, closed, or department-filtered conversations.
Read the message thread for one conversation.
Send an agent reply or internal note.
Mark a message as read by this agent.
Accept, assign, or transfer a conversation inside tenant visibility rules.
Close a conversation and record the reason.
Soft-delete an attachment-bearing message so media removal stays auditable.
Make this agent available for Ringnity calls.
Mark the agent unavailable and close the live workspace connection.
Run your own UI when a customer call arrives.
Accept a customer call and let the SDK handle call setup.
Decline an incoming call.
End the active call and clean up local media.
Put an active call on hold.
Resume a held call.
Transfer the call to another department.
Control the local microphone.
Turn video on or off from your call UI.
Choose where optional video appears in your page.
Exact API reference
Agent SDK methods above are the recommended public surface. When you need exact request fields, response shapes, status codes, or auth schemes, use the Scalar API reference generated from OpenAPI.
Open Scalar API referenceWho can see a conversation?
- The conversation is assigned to the agent.
- The agent is an active participant in the conversation.
- The conversation is unassigned and belongs to the agent department.
- The conversation is unassigned and has no department.
- Supervisor or admin roles can read tenant conversations without the regular agent filter.
Next milestone
- Add typed return models for every stable backend response.
- Add custom CRM voice/video example with browser permission guidance.
- Add live call smoke checklist after the first custom workspace test.
Common errors
401 AGENT_SDK_TOKEN_MISSING 401 AGENT_SDK_TOKEN_INVALID 403 AGENT_SDK_SCOPE_DENIED 404 AGENT_SDK_CONVERSATION_NOT_FOUND 403 AGENT_SDK_CONVERSATION_WRITE_FORBIDDEN