curl --request POST \
--url https://api.neoagent.io/public-api/til-requests/{request_id}/decision \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"acting_end_user": "<string>",
"acting_end_user_name": "<string>",
"acting_end_user_tenant_id": "<string>",
"advance_rotation": false,
"client_id": "<string>",
"note": "<string>"
}
'import requests
url = "https://api.neoagent.io/public-api/til-requests/{request_id}/decision"
payload = {
"acting_end_user": "<string>",
"acting_end_user_name": "<string>",
"acting_end_user_tenant_id": "<string>",
"advance_rotation": False,
"client_id": "<string>",
"note": "<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({
acting_end_user: '<string>',
acting_end_user_name: '<string>',
acting_end_user_tenant_id: '<string>',
advance_rotation: false,
client_id: '<string>',
note: '<string>'
})
};
fetch('https://api.neoagent.io/public-api/til-requests/{request_id}/decision', 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/til-requests/{request_id}/decision",
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([
'acting_end_user' => '<string>',
'acting_end_user_name' => '<string>',
'acting_end_user_tenant_id' => '<string>',
'advance_rotation' => false,
'client_id' => '<string>',
'note' => '<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/til-requests/{request_id}/decision"
payload := strings.NewReader("{\n \"acting_end_user\": \"<string>\",\n \"acting_end_user_name\": \"<string>\",\n \"acting_end_user_tenant_id\": \"<string>\",\n \"advance_rotation\": false,\n \"client_id\": \"<string>\",\n \"note\": \"<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/til-requests/{request_id}/decision")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"acting_end_user\": \"<string>\",\n \"acting_end_user_name\": \"<string>\",\n \"acting_end_user_tenant_id\": \"<string>\",\n \"advance_rotation\": false,\n \"client_id\": \"<string>\",\n \"note\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.neoagent.io/public-api/til-requests/{request_id}/decision")
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 \"acting_end_user\": \"<string>\",\n \"acting_end_user_name\": \"<string>\",\n \"acting_end_user_tenant_id\": \"<string>\",\n \"advance_rotation\": false,\n \"client_id\": \"<string>\",\n \"note\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"data": {
"agent_autonomy_type": "CHAINED_ACTIONS",
"agent_id": 123,
"agent_name": "<string>",
"approvers": [
{
"user_id": "<string>",
"user_name": "<string>"
}
],
"comment": "<string>",
"consent_status": "PENDING",
"created_at": "2023-11-07T05:31:56Z",
"decided_by": {
"id": "<string>",
"name": "<string>"
},
"id": 123,
"requested_by": {
"id": "<string>",
"name": "<string>"
},
"requested_for": {},
"session_id": "<string>",
"ticket_id": "<string>",
"ticket_number": "<string>",
"topic": "TICKET",
"updated_at": "2023-11-07T05:31:56Z"
},
"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"
}
}Approve, reject, or request changes on a TIL request
Records the technician’s decision (409 if it was already resolved). Returns the updated request. approve authorizes the action; reject stops it; request_changes does neither — it records COMMENTED and unlocks no write. For a request raised by an agent run, that run resumes to revise its proposal and raise a fresh request. For a request raised in a chat with a conversational agent, the agent reads the note on the next message in that chat — the dashboard and the Teams bot send that message; a direct API caller must send one. request_changes needs a non-empty note and is accepted only on agent requests (400 otherwise). A platform service account may pass client_id to resolve a TIL in another tenant — but only when the acting technician is a recipient of the request, the request belongs to a conversational chat agent, or it belongs to that tenant’s Neo Support agent (any other agent → 403); any non-platform caller passing client_id is rejected with 403. For proactive Teams cards the trusted relay may instead supply acting_end_user_tenant_id with acting_end_user: the backend locates the request’s workspace and checks the full identity, named recipient, and current workspace eligibility. Non-platform callers cannot use this identity field.
curl --request POST \
--url https://api.neoagent.io/public-api/til-requests/{request_id}/decision \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"acting_end_user": "<string>",
"acting_end_user_name": "<string>",
"acting_end_user_tenant_id": "<string>",
"advance_rotation": false,
"client_id": "<string>",
"note": "<string>"
}
'import requests
url = "https://api.neoagent.io/public-api/til-requests/{request_id}/decision"
payload = {
"acting_end_user": "<string>",
"acting_end_user_name": "<string>",
"acting_end_user_tenant_id": "<string>",
"advance_rotation": False,
"client_id": "<string>",
"note": "<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({
acting_end_user: '<string>',
acting_end_user_name: '<string>',
acting_end_user_tenant_id: '<string>',
advance_rotation: false,
client_id: '<string>',
note: '<string>'
})
};
fetch('https://api.neoagent.io/public-api/til-requests/{request_id}/decision', 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/til-requests/{request_id}/decision",
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([
'acting_end_user' => '<string>',
'acting_end_user_name' => '<string>',
'acting_end_user_tenant_id' => '<string>',
'advance_rotation' => false,
'client_id' => '<string>',
'note' => '<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/til-requests/{request_id}/decision"
payload := strings.NewReader("{\n \"acting_end_user\": \"<string>\",\n \"acting_end_user_name\": \"<string>\",\n \"acting_end_user_tenant_id\": \"<string>\",\n \"advance_rotation\": false,\n \"client_id\": \"<string>\",\n \"note\": \"<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/til-requests/{request_id}/decision")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"acting_end_user\": \"<string>\",\n \"acting_end_user_name\": \"<string>\",\n \"acting_end_user_tenant_id\": \"<string>\",\n \"advance_rotation\": false,\n \"client_id\": \"<string>\",\n \"note\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.neoagent.io/public-api/til-requests/{request_id}/decision")
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 \"acting_end_user\": \"<string>\",\n \"acting_end_user_name\": \"<string>\",\n \"acting_end_user_tenant_id\": \"<string>\",\n \"advance_rotation\": false,\n \"client_id\": \"<string>\",\n \"note\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"data": {
"agent_autonomy_type": "CHAINED_ACTIONS",
"agent_id": 123,
"agent_name": "<string>",
"approvers": [
{
"user_id": "<string>",
"user_name": "<string>"
}
],
"comment": "<string>",
"consent_status": "PENDING",
"created_at": "2023-11-07T05:31:56Z",
"decided_by": {
"id": "<string>",
"name": "<string>"
},
"id": 123,
"requested_by": {
"id": "<string>",
"name": "<string>"
},
"requested_for": {},
"session_id": "<string>",
"ticket_id": "<string>",
"ticket_number": "<string>",
"topic": "TICKET",
"updated_at": "2023-11-07T05:31:56Z"
},
"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).
Path Parameters
Body
"approve" authorizes the pending action (with a note, the agent must honour it). "reject" stops it. "request_changes" does neither: the note is required and nothing is authorized. A request raised by an agent run resumes that run to revise its proposal and raise a fresh request; a request raised in a chat with a conversational agent is read on the next message in that chat, which the dashboard and Teams send for you and a direct API caller must send. Only agent (AGENTIC) requests accept request_changes.
approve, reject, request_changes The technician resolving the request, as their stable user id (M365 AAD object id). Honored only for a platform service account: it authorizes resolving a proactive (Teams) TIL when this technician is one of its recipients, or a conversational (chat) MSP agent's TIL, and records them as the approver. Ignored for other callers.
Display name of the resolving technician (acting_end_user), for the audit record. Honored only for a platform service account; falls back to the user id when absent.
For the trusted Teams relay only: the authenticated sender's Microsoft directory. For proactive cards, resolves the workspace from the request and verifies the recipient's current access. Must come from the Teams activity, never card data.
Ticket Dispatch only, and only with action "reject". False (the default) means do not auto-assign this ticket: Neo stops dispatching it and leaves it for manual assignment, which is what a rejection means everywhere else. True declines only the proposed technician and lets Neo offer the ticket to the next available one, excluding everyone who has already declined it. Ignored for every other request type.
Target tenant whose TIL request to resolve. Honored only for a platform service account; any other caller passing it gets 403. A platform caller may resolve a tenant's TIL when the acting technician (see acting_end_user) is one of its recipients, or when it belongs to that tenant's Neo Support agent. Omit to resolve in the caller's own tenant.
