Sales Agent API
Configure the AI sales agent, read its decision audit trail and active conversations, and process a conversation — api.misar.io/reach/api/sales-agent.
The sales agent is MisarReach's AI assistant for follow-up. It reads a conversation, decides what should happen next, and drafts the reply. Every decision is written to an audit trail before anything is sent.
Base URL: https://api.misar.io/reach/api · Auth: Authorization: Bearer mrk_…
Pro plan and above
Enabling the agent and processing a conversation both require a Pro-or-higher MisarReach plan. Reading configuration, actions, and conversations does not.
Scopes
Api-Key mrk_… and a bare mrk_… header value are accepted alongside Bearer. A key with the * scope satisfies every requirement below.
| Endpoint | Scope |
|---|---|
GET /api/sales-agent/config | sales_agent:read |
PATCH /api/sales-agent/config | sales_agent:write |
GET /api/sales-agent/actions | sales_agent:read |
GET /api/sales-agent/conversations | sales_agent:read |
POST /api/sales-agent/process | sales_agent:write |
Get configuration
/api/sales-agent/configReturns the caller's agent configuration. Takes no parameters.
Response fields
okbooleanAlways true on success.
config.user_idstringOwner of the configuration.
config.enabledbooleanWhether the agent runs at all. Defaults to false.
config.auto_sendbooleanWhether approved replies are sent without review. Defaults to false.
config.cal_linkstring | nullBooking link the agent offers when it decides to ask for a meeting.
config.offer_pricenumberOffer price in the smallest currency unit (paise). Defaults to 0.
config.offer_descriptionstring | nullWhat the agent is selling. Up to 1000 characters.
config.max_replies_per_daynumberDaily cap on auto-sent replies. Defaults to 20.
config.confidence_thresholdnumberDecisions below this confidence are flagged for a human. Defaults to 0.65.
config.updated_atstringISO-8601 last-modified time. Absent on the synthesised default.
curl https://api.misar.io/reach/api/sales-agent/config \
-H "Authorization: Bearer mrk_your_key_here"{
"ok": true,
"config": {
"user_id": "3f1a2b4c-5d6e-4f70-8a91-b2c3d4e5f607",
"enabled": true,
"auto_send": false,
"cal_link": "https://cal.com/jane/30min",
"offer_price": 1000000,
"offer_description": "Annual growth retainer",
"max_replies_per_day": 20,
"confidence_threshold": 0.65,
"updated_at": "2026-08-01T10:22:07.114Z"
}
}No row yet? You still get a config
If the agent has never been configured, the response is a synthesised default with enabled: false and no updated_at. It is not a 404.
Status codes
| Code | Meaning |
|---|---|
200 | Configuration returned |
401 | Not authenticated |
403 | Missing sales_agent:read |
500 | { "error": "Failed to load config" } |
Update configuration
/api/sales-agent/configPartial update — send only the fields you want to change. Unknown keys are ignored. An empty body is valid and simply touches updated_at.
Request body
enabledbooleanbodyTurn the agent on or off. Setting this to true triggers the Pro plan check.
auto_sendbooleanbodyWhether approved replies send without review.
cal_linkstring | nullbodyMust be a valid URL, at most 500 characters. null clears it.
offer_pricenumberbodyNon-negative integer in the smallest currency unit.
offer_descriptionstring | nullbodyUp to 1000 characters. null clears it.
max_replies_per_daynumberbodyInteger between 1 and 500.
confidence_thresholdnumberbodyBetween 0 and 1.
Response fields
okbooleantrue when the update was written. The updated configuration is not echoed back — re-read it with GET if you need it.
curl -X PATCH https://api.misar.io/reach/api/sales-agent/config \
-H "Authorization: Bearer mrk_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"enabled": true,
"confidence_threshold": 0.75,
"max_replies_per_day": 30,
"cal_link": "https://cal.com/jane/30min"
}'{ "ok": true }{
"success": false,
"error": "The AI Sales Agent is available on Pro and above. Upgrade to enable it.",
"feature": "ai_sales_agent",
"upgrade": true,
"upgrade_url": "/settings?tab=billing"
}Status codes
| Code | Meaning |
|---|---|
200 | Updated |
400 | Validation failed, or the body was not valid JSON. The message names the offending field. |
401 | Not authenticated |
403 | Missing sales_agent:write, or the plan gate rejected enabled: true |
500 | { "error": "Failed to update config" } |
The plan gate only guards enabling
It runs solely when the body sets enabled to true. A lower-tier account can still change other fields, and can always disable the agent.
List today's actions
/api/sales-agent/actionsThe agent's decision audit trail for today only, newest first, capped at 20 rows. There are no query parameters and no pagination.
Response fields
okbooleanAlways true.
actions[].idstringAction id.
actions[].actionstringOne of reply, ask_for_meeting, send_offer, close_deal, human_handoff.
actions[].reasonstring | nullWhy the agent chose this action.
actions[].confidencenumberBetween 0 and 1.
actions[].flagged_humanbooleanWhether the decision was routed to a human.
actions[].auto_sentbooleanWhether the reply was sent without review. Only auto-sent actions count against the daily cap.
actions[].conversation_idstringThe conversation the decision applies to.
actions[].created_atstringISO-8601 decision time.
statsobjecttotal (rows returned), deals (how many were close_deal), flagged (how many were flagged for a human).
curl https://api.misar.io/reach/api/sales-agent/actions \
-H "Authorization: Bearer mrk_your_key_here"{
"ok": true,
"actions": [
{
"id": "7b2c1a9d-4e5f-4061-9a72-c3d4e5f60718",
"action": "ask_for_meeting",
"reason": "Lead asked about availability twice",
"confidence": 0.82,
"flagged_human": false,
"auto_sent": false,
"conversation_id": "1a2b3c4d-5e6f-4071-8a92-b3c4d5e6f708",
"created_at": "2026-08-04T09:14:02.771Z"
}
],
"stats": { "total": 1, "deals": 0, "flagged": 0 }
}Today is the server's day, and the window is fixed
The cutoff is local midnight on the server, and the cap is a hard 20 rows. There is no date filter and no way to page further back — this is a live dashboard feed, not a history API. A read failure also returns 200, with an empty actions array and zeroed stats.
Status codes
| Code | Meaning |
|---|---|
200 | Returned |
401 | Not authenticated |
403 | Missing sales_agent:read |
List active conversations
/api/sales-agent/conversationsActive conversations for the caller, ordered by most recent message.
Query parameters
limitnumberquerydefault: 25Rows to return. Capped at 50. Pass a positive integer.
Response fields
okbooleanAlways true.
conversations[].idstringConversation id. Pass this to POST /api/sales-agent/process.
conversations[].lead_namestring | nullLead's display name.
conversations[].lead_emailstring | nullLead's email address.
conversations[].channelstringemail, whatsapp, sms, linkedin, instagram, facebook, twitter, or telegram.
conversations[].last_messagestring | nullMost recent message body.
conversations[].last_message_atstring | nullISO-8601 timestamp of the most recent message.
curl "https://api.misar.io/reach/api/sales-agent/conversations?limit=25" \
-H "Authorization: Bearer mrk_your_key_here"{
"ok": true,
"conversations": [
{
"id": "1a2b3c4d-5e6f-4071-8a92-b3c4d5e6f708",
"lead_name": "Sam Rivera",
"lead_email": "sam@example.com",
"channel": "email",
"last_message": "Could we do Thursday instead?",
"last_message_at": "2026-08-04T08:41:19.220Z"
}
]
}Status codes
| Code | Meaning |
|---|---|
200 | Returned |
401 | Not authenticated |
403 | Missing sales_agent:read |
500 | { "error": "Failed to load conversations" } |
Process a conversation
/api/sales-agent/processRuns the agent over one conversation: decide the next action, then draft the reply unless the decision was flagged for a human. The decision is written to the audit trail either way.
Request body
conversationIdstringbodyrequiredUUID of the conversation to process. Exactly one conversation per call.
Response fields
okbooleanAlways true on success.
decision.actionstringOne of reply, ask_for_meeting, send_offer, close_deal, human_handoff.
decision.reasonstringWhy the agent chose it.
decision.confidencenumberBetween 0 and 1.
replystring | nullThe drafted reply, or null when flaggedHuman is true.
flaggedHumanbooleantrue when the action was human_handoff or the confidence fell below the configured threshold.
todayCountnumberAuto-sent replies today, including this run.
dailyLimitnumberThe configured max_replies_per_day.
curl -X POST https://api.misar.io/reach/api/sales-agent/process \
-H "Authorization: Bearer mrk_your_key_here" \
-H "Content-Type: application/json" \
-d '{ "conversationId": "1a2b3c4d-5e6f-4071-8a92-b3c4d5e6f708" }'{
"ok": true,
"decision": {
"action": "ask_for_meeting",
"reason": "Lead asked about availability twice",
"confidence": 0.82
},
"reply": "Thursday works — here is my calendar: https://cal.com/jane/30min",
"flaggedHuman": false,
"todayCount": 4,
"dailyLimit": 20
}{
"ok": true,
"decision": {
"action": "human_handoff",
"reason": "Pricing objection outside configured offer",
"confidence": 0.41
},
"reply": null,
"flaggedHuman": true,
"todayCount": 4,
"dailyLimit": 20
}{ "error": "Daily reply limit reached", "limitReached": true }This endpoint never sends anything
The manual path always records auto_sent: false and returns the draft for you to send. Auto-sending happens only on the agent's own scheduled path, and only when auto_send is enabled. There is no queue mode — one conversationId per call.
Status codes
| Code | Meaning |
|---|---|
200 | Processed |
400 | conversationId missing or not a UUID, or the body was not valid JSON |
401 | Not authenticated |
403 | Missing sales_agent:write; the agent is disabled (Sales agent is not enabled); or the plan gate rejected the call |
404 | { "error": "Conversation not found" } — also returned when the conversation belongs to another user |
429 | { "error": "Daily reply limit reached", "limitReached": true } |
500 | { "error": "Failed to load messages" } |
Decision pipeline
Ownership and configuration
The conversation must belong to the caller and the agent must be enabled, otherwise the call stops at 404 or 403.
Daily cap
Auto-sent replies from today are counted. At or above max_replies_per_day, the call returns 429.
Decide
The last 20 messages are read in order and the agent picks an action with a confidence score.
Flag or draft
human_handoff, or any confidence below confidence_threshold, flags the decision and skips drafting. Otherwise the reply is generated.
Audit
The decision is written to the audit trail — visible immediately via GET /api/sales-agent/actions.
Rate limits
These endpoints carry no request rate limit. The only throttle is the per-day auto-send cap, and because manual process calls never auto-send, they do not consume it.
See Errors and Conversations.