curl --request POST \
--url https://api.neoagent.io/public-api/chat/sessions \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"agent_id": 123,
"bot_id": "<string>",
"channel": "TEAMS",
"client_id": "<string>",
"end_user_email": "<string>",
"inbound_tenant_id": "<string>",
"managed_agent_key": "NEO_SUPPORT",
"title": "<string>"
}
'import requests
url = "https://api.neoagent.io/public-api/chat/sessions"
payload = {
"agent_id": 123,
"bot_id": "<string>",
"channel": "TEAMS",
"client_id": "<string>",
"end_user_email": "<string>",
"inbound_tenant_id": "<string>",
"managed_agent_key": "NEO_SUPPORT",
"title": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
agent_id: 123,
bot_id: '<string>',
channel: 'TEAMS',
client_id: '<string>',
end_user_email: '<string>',
inbound_tenant_id: '<string>',
managed_agent_key: 'NEO_SUPPORT',
title: '<string>'
})
};
fetch('https://api.neoagent.io/public-api/chat/sessions', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.neoagent.io/public-api/chat/sessions",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'agent_id' => 123,
'bot_id' => '<string>',
'channel' => 'TEAMS',
'client_id' => '<string>',
'end_user_email' => '<string>',
'inbound_tenant_id' => '<string>',
'managed_agent_key' => 'NEO_SUPPORT',
'title' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.neoagent.io/public-api/chat/sessions"
payload := strings.NewReader("{\n \"agent_id\": 123,\n \"bot_id\": \"<string>\",\n \"channel\": \"TEAMS\",\n \"client_id\": \"<string>\",\n \"end_user_email\": \"<string>\",\n \"inbound_tenant_id\": \"<string>\",\n \"managed_agent_key\": \"NEO_SUPPORT\",\n \"title\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.neoagent.io/public-api/chat/sessions")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"agent_id\": 123,\n \"bot_id\": \"<string>\",\n \"channel\": \"TEAMS\",\n \"client_id\": \"<string>\",\n \"end_user_email\": \"<string>\",\n \"inbound_tenant_id\": \"<string>\",\n \"managed_agent_key\": \"NEO_SUPPORT\",\n \"title\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.neoagent.io/public-api/chat/sessions")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"agent_id\": 123,\n \"bot_id\": \"<string>\",\n \"channel\": \"TEAMS\",\n \"client_id\": \"<string>\",\n \"end_user_email\": \"<string>\",\n \"inbound_tenant_id\": \"<string>\",\n \"managed_agent_key\": \"NEO_SUPPORT\",\n \"title\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"data": {
"agent_id": 123,
"archived_at": "2023-11-07T05:31:56Z",
"channel": "<string>",
"client_id": "<string>",
"created_at": "2023-11-07T05:31:56Z",
"id": "<string>",
"title": "<string>",
"updated_at": "2023-11-07T05:31:56Z",
"audience": "MSP",
"idle_reset_minutes": 123,
"show_agent_progress_steps": true,
"trigger_mode": "<string>"
},
"meta": {
"request_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"timings_ms": {},
"pagination": {
"has_more": true,
"next_cursor": "<string>"
},
"warnings": [
"<string>"
]
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": {}
},
"meta": {
"request_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": {}
},
"meta": {
"request_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": {}
},
"meta": {
"request_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": {}
},
"meta": {
"request_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": {}
},
"meta": {
"request_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": {}
},
"meta": {
"request_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": {}
},
"meta": {
"request_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": {}
},
"meta": {
"request_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
}Create a chat session
Create a session against a CONVERSATIONAL agent. Returns the session resource; messages are appended via POST /chat/sessions/<id>/messages.
Select the agent with exactly one of agent_id (numeric) or managed_agent_key (a stable key for a Neo-managed agent, e.g. NEO_SUPPORT); the latter lets a caller target the agent without knowing its per-tenant id.
A platform service account may pass client_id to create the session for another tenant — used by the converged Neo Support relay to run each MSP technician’s turn as their own tenant. For a platform caller the agent is resolved from bot_id (the inbound Teams bot app id) + inbound_tenant_id (the message’s M365 tenant): a white-label bot with a channel installed for that tenant routes to the channel’s agent acting as the owning MSP (the acting tenant is then resolved server-side, overriding client_id, and returned on the resource); else the ACTIVE channel registered for that bot; else that tenant’s Neo Support agent. The caller is confined to those outcomes (never an arbitrary agent). Any non-platform caller passing client_id is rejected with 403, and a platform caller whose client_id names no active tenant with 400 tenant_not_active — end-user-relayable copy, because the relay passes the sender’s own M365 tenant here and anybody may install the marketplace bot from the Teams store.
A white-label session is always scoped to one end-company, resolved server-side from inbound_tenant_id. A tenant linked to several companies (branch offices) is narrowed to the company whose PSA contacts hold the caller-supplied end_user_email, then by deterministic tiebreaks. If the tenant isn’t linked to any company in the owning MSP’s dashboard, the create is refused with 409 end_company_not_mapped; the error message is end-user-relayable and tells the MSP admin what to link.
channel names the relay’s surface: TEAMS (the default) or SLACK. It is honored only for a platform caller; any other service-account or end-user-session caller’s session is TEAMS, and a dashboard caller’s is always a dashboard session. A platform caller with SLACK must pass bot_id (the Slack app id) and inbound_tenant_id (the Slack workspace id). The session routes to the channel whose Slack app is installed in that workspace, acting as its owning MSP, and is scoped to the company the workspace was linked to at install time. There is no Neo Support fallback for Slack: a workspace no active channel routes is refused with 409 white_label_not_connected, and an unlinked workspace with 409 end_company_not_mapped.
curl --request POST \
--url https://api.neoagent.io/public-api/chat/sessions \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"agent_id": 123,
"bot_id": "<string>",
"channel": "TEAMS",
"client_id": "<string>",
"end_user_email": "<string>",
"inbound_tenant_id": "<string>",
"managed_agent_key": "NEO_SUPPORT",
"title": "<string>"
}
'import requests
url = "https://api.neoagent.io/public-api/chat/sessions"
payload = {
"agent_id": 123,
"bot_id": "<string>",
"channel": "TEAMS",
"client_id": "<string>",
"end_user_email": "<string>",
"inbound_tenant_id": "<string>",
"managed_agent_key": "NEO_SUPPORT",
"title": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
agent_id: 123,
bot_id: '<string>',
channel: 'TEAMS',
client_id: '<string>',
end_user_email: '<string>',
inbound_tenant_id: '<string>',
managed_agent_key: 'NEO_SUPPORT',
title: '<string>'
})
};
fetch('https://api.neoagent.io/public-api/chat/sessions', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.neoagent.io/public-api/chat/sessions",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'agent_id' => 123,
'bot_id' => '<string>',
'channel' => 'TEAMS',
'client_id' => '<string>',
'end_user_email' => '<string>',
'inbound_tenant_id' => '<string>',
'managed_agent_key' => 'NEO_SUPPORT',
'title' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.neoagent.io/public-api/chat/sessions"
payload := strings.NewReader("{\n \"agent_id\": 123,\n \"bot_id\": \"<string>\",\n \"channel\": \"TEAMS\",\n \"client_id\": \"<string>\",\n \"end_user_email\": \"<string>\",\n \"inbound_tenant_id\": \"<string>\",\n \"managed_agent_key\": \"NEO_SUPPORT\",\n \"title\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.neoagent.io/public-api/chat/sessions")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"agent_id\": 123,\n \"bot_id\": \"<string>\",\n \"channel\": \"TEAMS\",\n \"client_id\": \"<string>\",\n \"end_user_email\": \"<string>\",\n \"inbound_tenant_id\": \"<string>\",\n \"managed_agent_key\": \"NEO_SUPPORT\",\n \"title\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.neoagent.io/public-api/chat/sessions")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"agent_id\": 123,\n \"bot_id\": \"<string>\",\n \"channel\": \"TEAMS\",\n \"client_id\": \"<string>\",\n \"end_user_email\": \"<string>\",\n \"inbound_tenant_id\": \"<string>\",\n \"managed_agent_key\": \"NEO_SUPPORT\",\n \"title\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"data": {
"agent_id": 123,
"archived_at": "2023-11-07T05:31:56Z",
"channel": "<string>",
"client_id": "<string>",
"created_at": "2023-11-07T05:31:56Z",
"id": "<string>",
"title": "<string>",
"updated_at": "2023-11-07T05:31:56Z",
"audience": "MSP",
"idle_reset_minutes": 123,
"show_agent_progress_steps": true,
"trigger_mode": "<string>"
},
"meta": {
"request_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"timings_ms": {},
"pagination": {
"has_more": true,
"next_cursor": "<string>"
},
"warnings": [
"<string>"
]
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": {}
},
"meta": {
"request_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": {}
},
"meta": {
"request_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": {}
},
"meta": {
"request_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": {}
},
"meta": {
"request_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": {}
},
"meta": {
"request_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": {}
},
"meta": {
"request_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": {}
},
"meta": {
"request_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": {}
},
"meta": {
"request_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
}Authorizations
Authorization: Bearer <token> where <token> is either a neo_sk_<env>_<secret> API key (service account) or a Microsoft Entra ID access token (dashboard user).
Body
Numeric id of the CONVERSATIONAL agent to run the session against. Provide exactly one of agent_id or managed_agent_key.
Inbound bot app id, forwarded by the platform relay: the Teams bot app id for channel=TEAMS, the Slack app id for channel=SLACK. The backend routes the session to the ACTIVE channel registered for that bot — else, on Teams only, the tenant's Neo Support agent. Honored only for a platform service account; ignored for other callers.
The messaging surface the session runs on: TEAMS (the default) or SLACK. For a platform caller with SLACK, bot_id carries the Slack app id and inbound_tenant_id the Slack workspace (team) id, and both are required: the session routes to the channel whose Slack app was installed in that workspace. Honored only for a platform service account; every other caller's session is TEAMS (a dashboard caller's is always a dashboard session).
TEAMS, SLACK Target tenant to create the session for. Honored only for a platform service account; the agent is resolved from bot_id (a matching channel, else that tenant's Neo Support agent). Any other caller passing it gets 403. Omit to use the caller's own tenant.
The speaker's verified email, forwarded by the platform relay at session create. Used only for white-label END_USER dispatch: when the inbound Microsoft 365 tenant is linked to several companies (branch offices), the session is scoped to the company whose PSA contacts hold this email. Honored only for a platform service account.
The inbound message's tenant, forwarded by the platform relay: the Microsoft 365 tenant id for channel=TEAMS, the Slack workspace (team) id for channel=SLACK. With bot_id, lets the backend route a white-label turn via the channel installed for that tenant — the acting tenant is then resolved server-side from the channel's owning MSP, overriding client_id. Honored only for a platform service account.
Target a Neo-managed agent by its stable key instead of a numeric id — the backend resolves the target tenant's row. Lets the converged Neo Support relay stay agent-id-agnostic across tenants. Provide exactly one of agent_id or managed_agent_key.
NEO_SUPPORT 