curl --request POST \
--url https://api.relayapp.im/v1/me/agents \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"handle": "scheduler",
"displayName": "<string>",
"tagline": "<string>",
"avatarUrl": "<string>",
"accentColor": "#0B75FF",
"openingMessage": {
"parts": [
{
"type": "text",
"text": "<string>",
"mention": "<string>",
"mention_range": [
1
],
"styles": [
{
"start": 1,
"length": 2,
"styles": []
}
]
}
]
}
}
'import requests
url = "https://api.relayapp.im/v1/me/agents"
payload = {
"handle": "scheduler",
"displayName": "<string>",
"tagline": "<string>",
"avatarUrl": "<string>",
"accentColor": "#0B75FF",
"openingMessage": { "parts": [
{
"type": "text",
"text": "<string>",
"mention": "<string>",
"mention_range": [1],
"styles": [
{
"start": 1,
"length": 2,
"styles": []
}
]
}
] }
}
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({
handle: 'scheduler',
displayName: '<string>',
tagline: '<string>',
avatarUrl: '<string>',
accentColor: '#0B75FF',
openingMessage: {
parts: [
{
type: 'text',
text: '<string>',
mention: '<string>',
mention_range: [1],
styles: [{start: 1, length: 2, styles: []}]
}
]
}
})
};
fetch('https://api.relayapp.im/v1/me/agents', 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.relayapp.im/v1/me/agents",
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([
'handle' => 'scheduler',
'displayName' => '<string>',
'tagline' => '<string>',
'avatarUrl' => '<string>',
'accentColor' => '#0B75FF',
'openingMessage' => [
'parts' => [
[
'type' => 'text',
'text' => '<string>',
'mention' => '<string>',
'mention_range' => [
1
],
'styles' => [
[
'start' => 1,
'length' => 2,
'styles' => [
]
]
]
]
]
]
]),
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.relayapp.im/v1/me/agents"
payload := strings.NewReader("{\n \"handle\": \"scheduler\",\n \"displayName\": \"<string>\",\n \"tagline\": \"<string>\",\n \"avatarUrl\": \"<string>\",\n \"accentColor\": \"#0B75FF\",\n \"openingMessage\": {\n \"parts\": [\n {\n \"type\": \"text\",\n \"text\": \"<string>\",\n \"mention\": \"<string>\",\n \"mention_range\": [\n 1\n ],\n \"styles\": [\n {\n \"start\": 1,\n \"length\": 2,\n \"styles\": []\n }\n ]\n }\n ]\n }\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.relayapp.im/v1/me/agents")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"handle\": \"scheduler\",\n \"displayName\": \"<string>\",\n \"tagline\": \"<string>\",\n \"avatarUrl\": \"<string>\",\n \"accentColor\": \"#0B75FF\",\n \"openingMessage\": {\n \"parts\": [\n {\n \"type\": \"text\",\n \"text\": \"<string>\",\n \"mention\": \"<string>\",\n \"mention_range\": [\n 1\n ],\n \"styles\": [\n {\n \"start\": 1,\n \"length\": 2,\n \"styles\": []\n }\n ]\n }\n ]\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.relayapp.im/v1/me/agents")
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 \"handle\": \"scheduler\",\n \"displayName\": \"<string>\",\n \"tagline\": \"<string>\",\n \"avatarUrl\": \"<string>\",\n \"accentColor\": \"#0B75FF\",\n \"openingMessage\": {\n \"parts\": [\n {\n \"type\": \"text\",\n \"text\": \"<string>\",\n \"mention\": \"<string>\",\n \"mention_range\": [\n 1\n ],\n \"styles\": [\n {\n \"start\": 1,\n \"length\": 2,\n \"styles\": []\n }\n ]\n }\n ]\n }\n}"
response = http.request(request)
puts response.read_body{
"agent": {
"id": "agt_01JZRELAY",
"handle": "relay",
"displayName": "Relay",
"tagline": "Your built-in Relay agent",
"visibility": "private",
"status": "active",
"createdAt": "2023-11-07T05:31:56Z",
"avatarUrl": "<string>",
"accentColor": "#0B75FF",
"installation": {
"id": "<string>",
"state": "installed",
"conversationId": "<string>",
"canRemove": true
},
"creator": {
"id": "org_01JZRELAY",
"name": "Relay",
"verified": true,
"kind": "person",
"display_name": "<string>",
"avatar_url": "<string>",
"domain": "relayapp.im",
"logo_url": "<string>",
"support_url": "<string>"
},
"preinstalled": true,
"removable": true
},
"token": "<string>",
"chat_id": "<string>"
}{
"error": {
"code": "invalid_request",
"message": "chat_id is required"
}
}{
"error": {
"code": "invalid_request",
"message": "chat_id is required"
}
}{
"error": {
"code": "invalid_request",
"message": "chat_id is required"
}
}{
"error": {
"code": "invalid_request",
"message": "chat_id is required"
}
}Create an agent and mint its token
Creates an agent owned by the signed-in person and returns its Agent Token. This is the only response that carries the token; store it now. A terminal bridge calls this once, with the session it received from the device flow, and then uses the token for everything else.
curl --request POST \
--url https://api.relayapp.im/v1/me/agents \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"handle": "scheduler",
"displayName": "<string>",
"tagline": "<string>",
"avatarUrl": "<string>",
"accentColor": "#0B75FF",
"openingMessage": {
"parts": [
{
"type": "text",
"text": "<string>",
"mention": "<string>",
"mention_range": [
1
],
"styles": [
{
"start": 1,
"length": 2,
"styles": []
}
]
}
]
}
}
'import requests
url = "https://api.relayapp.im/v1/me/agents"
payload = {
"handle": "scheduler",
"displayName": "<string>",
"tagline": "<string>",
"avatarUrl": "<string>",
"accentColor": "#0B75FF",
"openingMessage": { "parts": [
{
"type": "text",
"text": "<string>",
"mention": "<string>",
"mention_range": [1],
"styles": [
{
"start": 1,
"length": 2,
"styles": []
}
]
}
] }
}
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({
handle: 'scheduler',
displayName: '<string>',
tagline: '<string>',
avatarUrl: '<string>',
accentColor: '#0B75FF',
openingMessage: {
parts: [
{
type: 'text',
text: '<string>',
mention: '<string>',
mention_range: [1],
styles: [{start: 1, length: 2, styles: []}]
}
]
}
})
};
fetch('https://api.relayapp.im/v1/me/agents', 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.relayapp.im/v1/me/agents",
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([
'handle' => 'scheduler',
'displayName' => '<string>',
'tagline' => '<string>',
'avatarUrl' => '<string>',
'accentColor' => '#0B75FF',
'openingMessage' => [
'parts' => [
[
'type' => 'text',
'text' => '<string>',
'mention' => '<string>',
'mention_range' => [
1
],
'styles' => [
[
'start' => 1,
'length' => 2,
'styles' => [
]
]
]
]
]
]
]),
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.relayapp.im/v1/me/agents"
payload := strings.NewReader("{\n \"handle\": \"scheduler\",\n \"displayName\": \"<string>\",\n \"tagline\": \"<string>\",\n \"avatarUrl\": \"<string>\",\n \"accentColor\": \"#0B75FF\",\n \"openingMessage\": {\n \"parts\": [\n {\n \"type\": \"text\",\n \"text\": \"<string>\",\n \"mention\": \"<string>\",\n \"mention_range\": [\n 1\n ],\n \"styles\": [\n {\n \"start\": 1,\n \"length\": 2,\n \"styles\": []\n }\n ]\n }\n ]\n }\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.relayapp.im/v1/me/agents")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"handle\": \"scheduler\",\n \"displayName\": \"<string>\",\n \"tagline\": \"<string>\",\n \"avatarUrl\": \"<string>\",\n \"accentColor\": \"#0B75FF\",\n \"openingMessage\": {\n \"parts\": [\n {\n \"type\": \"text\",\n \"text\": \"<string>\",\n \"mention\": \"<string>\",\n \"mention_range\": [\n 1\n ],\n \"styles\": [\n {\n \"start\": 1,\n \"length\": 2,\n \"styles\": []\n }\n ]\n }\n ]\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.relayapp.im/v1/me/agents")
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 \"handle\": \"scheduler\",\n \"displayName\": \"<string>\",\n \"tagline\": \"<string>\",\n \"avatarUrl\": \"<string>\",\n \"accentColor\": \"#0B75FF\",\n \"openingMessage\": {\n \"parts\": [\n {\n \"type\": \"text\",\n \"text\": \"<string>\",\n \"mention\": \"<string>\",\n \"mention_range\": [\n 1\n ],\n \"styles\": [\n {\n \"start\": 1,\n \"length\": 2,\n \"styles\": []\n }\n ]\n }\n ]\n }\n}"
response = http.request(request)
puts response.read_body{
"agent": {
"id": "agt_01JZRELAY",
"handle": "relay",
"displayName": "Relay",
"tagline": "Your built-in Relay agent",
"visibility": "private",
"status": "active",
"createdAt": "2023-11-07T05:31:56Z",
"avatarUrl": "<string>",
"accentColor": "#0B75FF",
"installation": {
"id": "<string>",
"state": "installed",
"conversationId": "<string>",
"canRemove": true
},
"creator": {
"id": "org_01JZRELAY",
"name": "Relay",
"verified": true,
"kind": "person",
"display_name": "<string>",
"avatar_url": "<string>",
"domain": "relayapp.im",
"logo_url": "<string>",
"support_url": "<string>"
},
"preinstalled": true,
"removable": true
},
"token": "<string>",
"chat_id": "<string>"
}{
"error": {
"code": "invalid_request",
"message": "chat_id is required"
}
}{
"error": {
"code": "invalid_request",
"message": "chat_id is required"
}
}{
"error": {
"code": "invalid_request",
"message": "chat_id is required"
}
}{
"error": {
"code": "invalid_request",
"message": "chat_id is required"
}
}Authorizations
Better Auth user-session bearer used by the Relay app and by a paired bridge; never an Agent Token.
Body
Camel-cased, like the Agent projection it creates. Any field outside this list answers 422 rather than being ignored.
The agent's name, unique only among this creator's agents. Lowercased on write. Reserved names answer 409, and a dot answers 422: the dot joins a name to its owner and belongs to neither half.
^[a-z][a-z0-9_]{2,31}$"scheduler"
1 - 801202048^https://(?![^/?#]*@)"#0B75FF"
The message the agent sends when someone installs it, as a parts array. Null for no opening message.
Show child attributes
Show child attributes
Response
Agent created; store the token now.
Consumer-safe agent identity. Top-level keys are camelCase here and the nested creator object is snake_case, which is the shape the app reads; every other body in this API is snake_case throughout. Installation is present only on an installed-agent projection. A system prompt, provider, model, runtime, credential, or backend configuration is never included.
Show child attributes
Show child attributes
The Agent Token. Returned once and never again.
^rly_live_The owner's direct conversation with the new agent.
^cnv_
