curl --request POST \
--url https://api.instaview.sk/agents/composer/{sessionId}/messages \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"message": "Also ask about relocation, and make the whole call Slovak."
}
'import requests
url = "https://api.instaview.sk/agents/composer/{sessionId}/messages"
payload = { "message": "Also ask about relocation, and make the whole call Slovak." }
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({message: 'Also ask about relocation, and make the whole call Slovak.'})
};
fetch('https://api.instaview.sk/agents/composer/{sessionId}/messages', 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.instaview.sk/agents/composer/{sessionId}/messages",
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([
'message' => 'Also ask about relocation, and make the whole call Slovak.'
]),
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.instaview.sk/agents/composer/{sessionId}/messages"
payload := strings.NewReader("{\n \"message\": \"Also ask about relocation, and make the whole call Slovak.\"\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.instaview.sk/agents/composer/{sessionId}/messages")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"message\": \"Also ask about relocation, and make the whole call Slovak.\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.instaview.sk/agents/composer/{sessionId}/messages")
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 \"message\": \"Also ask about relocation, and make the whole call Slovak.\"\n}"
response = http.request(request)
puts response.read_body{
"sessionId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"status": "processing",
"expiresAt": "2023-11-07T05:31:56Z"
}Refine a Composed Agent
Applies one plain-language change to the session’s agent. Returns 202; poll GET /agents/composer/{sessionId} for the updated preview. One turn runs at a time: sending another while one is in flight is a 409. Still creates nothing.
curl --request POST \
--url https://api.instaview.sk/agents/composer/{sessionId}/messages \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"message": "Also ask about relocation, and make the whole call Slovak."
}
'import requests
url = "https://api.instaview.sk/agents/composer/{sessionId}/messages"
payload = { "message": "Also ask about relocation, and make the whole call Slovak." }
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({message: 'Also ask about relocation, and make the whole call Slovak.'})
};
fetch('https://api.instaview.sk/agents/composer/{sessionId}/messages', 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.instaview.sk/agents/composer/{sessionId}/messages",
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([
'message' => 'Also ask about relocation, and make the whole call Slovak.'
]),
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.instaview.sk/agents/composer/{sessionId}/messages"
payload := strings.NewReader("{\n \"message\": \"Also ask about relocation, and make the whole call Slovak.\"\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.instaview.sk/agents/composer/{sessionId}/messages")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"message\": \"Also ask about relocation, and make the whole call Slovak.\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.instaview.sk/agents/composer/{sessionId}/messages")
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 \"message\": \"Also ask about relocation, and make the whole call Slovak.\"\n}"
response = http.request(request)
puts response.read_body{
"sessionId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"status": "processing",
"expiresAt": "2023-11-07T05:31:56Z"
}POST /agents/composer/9f8c1d2e-4b7a-4c3d-9e1f-2a5b6c7d8e90/messages
{ "message": "Also ask about relocation, and make the whole call Slovak." }
{
"sessionId": "9f8c1d2e-4b7a-4c3d-9e1f-2a5b6c7d8e90",
"status": "processing",
"expiresAt": "2026-09-17T10:15:00.000Z"
}
GET /agents/composer/{sessionId} for the updated preview, or send a webhookId when you start the session and be told instead.
POST /agents.One turn at a time
A session applies one instruction at a time. Send another while one is still running and you get a409 immediately, without spending anything — wait for the session to leave processing and send it again.
The session keeps showing the previous agent throughout. A refinement in flight does not take away the preview you already had: you can still read it, and still create it.
You do not send the agent back
The session holds the agent, so a follow-up carries only your instruction. There is nothing to keep in sync on your side, and no way to accidentally send back a stale version of the conversation. You can refine as many times as you like within the session’s 30 days.What a message can do
Anything the visual builder can do to a custom agent — the composer and the in-app copilot are the same thing behind different doors:| You say | What changes |
|---|---|
| ”Also ask about relocation” | A question is added to the right part of the conversation |
| ”Make it Slovak” | The language, and every message and prompt rewritten in it |
| ”Drop the salary question” | That question is removed |
| ”Be more direct, less chatty” | The agent’s communication style |
| ”Score the Go question as required” | How the call is scored afterwards |
| ”Call it something shorter” | The agent’s name |
| ”Transfer strong candidates to +421 900 123 456” | A handoff step (PHONE agents only) |
name when you started the session,
it stays pinned — the composer will not quietly relabel your agent while doing something
else.A rejected change leaves the agent alone
Every turn is validated the same way the create route validates a flow. If a change would break the conversation — emptying a required message, leaving a conditional with one branch — the session comes backfailed with the reason, and the agent is untouched: no half-applied edit, and the preview you had before is still there to read, to create, or to try a different instruction against.
A rejected change is not free, though: the instruction reached the model before it was found to be unapplicable, so it counts against your rate limit like any other turn. Only a 409 — refused before any work started — costs nothing.
{
"sessionId": "9f8c1d2e-4b7a-4c3d-9e1f-2a5b6c7d8e90",
"status": "failed",
"agent": { "…": "the agent your last successful turn left" },
"error": {
"code": "COMPOSER_INVALID_RESULT",
"message": "Could not apply that change without breaking the conversation."
},
"expiresAt": "2026-09-17T10:15:00.000Z"
}
Errors
These are the ways the request can fail. A change that fails after it has been accepted is not an error here — it is afailed session, described above.
| Status | When |
|---|---|
400 | message missing, empty, or over 4,000 characters |
404 | No live session with that id belongs to your company — including one that expired |
409 | A turn is already running on this session, or the session has no agent yet because its first turn has not finished (or failed) |
429 | Composer rate limit reached (10 per minute, 120 per hour) |
404.
They are deliberately indistinguishable.Authorizations
API key for authentication using Bearer scheme
Path Parameters
The composer session id returned by POST /agents/composer.
Query Parameters
Required for ATS API keys to specify which company to access. Ignored for standard company API keys.
Body
What to change, in plain language.
4000"Also ask about relocation, and make the whole call Slovak."
Response
The turn was accepted and is running. Poll GET /agents/composer/{sessionId} for the result.
The session to poll, to send follow-up messages to, and to create the agent from.
Always processing here: the turn has been accepted and is running.
processing When this session is deleted if you never create the agent. Sessions live 30 days.