Documentation Index
Fetch the complete documentation index at: https://orbit-docs.devotel.io/llms.txt
Use this file to discover all available pages before exploring further.
Conversations API
Multi-channel conversation threads and threading state
Base path: /api/v1/conversations
Endpoint count: 26
List all conversations
GET /api/v1/conversations/
Unified omnichannel inbox view across SMS, WhatsApp, email, Instagram, Messenger, RCS, Viber, and LINE. Each conversation represents a unique contact + channel pair.
Sort field (prefix with - for DESC)
channel
string (enum: sms|whatsapp|email|rcs|viber|instagram|…)
—
status
string (enum: open|closed|pending|snoozed|active|archived|…)
—
Filter by assigned agent ID (legacy)
Search by contact name, phone, or email
Filter by tag (legacy, single)
Comma-separated list of channels
Comma-separated list of statuses (open,closed,pending,snoozed,…)
Comma-separated tag list (ANY match)
“me”, “unassigned”, or a user id
unread
string (enum: true|false)
—
has_agent
string (enum: true|false)
—
has_video_room
string (enum: true|false)
—
sentiment
string (enum: positive|neutral|negative)
—
mentioned_internal
string (enum: true|false)
—
JSON-encoded full filter AST (takes precedence)
curl -X GET "https://orbit-api.devotel.io/api/v1/conversations/" \
-H "X-API-Key: dv_live_sk_your_key_here"
import { Orbit } from '@devotel/orbit-sdk'
const orbit = new Orbit({
apiKey: process.env.ORBIT_API_KEY!,
})
const res = await fetch('https://orbit-api.devotel.io/api/v1/conversations/', {
method: 'GET',
headers: {
'X-API-Key': process.env.ORBIT_API_KEY!,
},
})
console.log(await res.json())
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
r = requests.get("https://orbit-api.devotel.io/api/v1/conversations/", headers=headers)
print(r.json())
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("GET", "https://orbit-api.devotel.io/api/v1/conversations/", nil)
req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
http.DefaultClient.Do(req)
}
require 'net/http'
require 'json'
uri = URI('https://orbit-api.devotel.io/api/v1/conversations/')
req = Net::HTTP::Get.new(uri)
req['X-API-Key'] = ENV['ORBIT_API_KEY']
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
puts res.body
<?php
$ch = curl_init('https://orbit-api.devotel.io/api/v1/conversations/');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-API-Key: ' . getenv('ORBIT_API_KEY'),
]);
echo curl_exec($ch);
Get a conversation
GET /api/v1/conversations/{id}
Returns details about a specific conversation including contact info, channel, status, and assignment.
curl -X GET "https://orbit-api.devotel.io/api/v1/conversations/{id}" \
-H "X-API-Key: dv_live_sk_your_key_here"
import { Orbit } from '@devotel/orbit-sdk'
const orbit = new Orbit({
apiKey: process.env.ORBIT_API_KEY!,
})
const res = await fetch('https://orbit-api.devotel.io/api/v1/conversations/{id}', {
method: 'GET',
headers: {
'X-API-Key': process.env.ORBIT_API_KEY!,
},
})
console.log(await res.json())
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
r = requests.get("https://orbit-api.devotel.io/api/v1/conversations/{id}", headers=headers)
print(r.json())
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("GET", "https://orbit-api.devotel.io/api/v1/conversations/{id}", nil)
req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
http.DefaultClient.Do(req)
}
require 'net/http'
require 'json'
uri = URI('https://orbit-api.devotel.io/api/v1/conversations/{id}')
req = Net::HTTP::Get.new(uri)
req['X-API-Key'] = ENV['ORBIT_API_KEY']
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
puts res.body
<?php
$ch = curl_init('https://orbit-api.devotel.io/api/v1/conversations/{id}');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-API-Key: ' . getenv('ORBIT_API_KEY'),
]);
echo curl_exec($ch);
Conversation activity feed
GET /api/v1/conversations/{id}/activity
Operator state-changes — assignments, status transitions (close/reopen/snooze), tag updates, merges, handoff actions, bulk operations. Cursor-paginated newest-first. Does NOT include chat messages (use /:id/messages) or internal notes (use /inbox/:id/internal-notes).
curl -X GET "https://orbit-api.devotel.io/api/v1/conversations/{id}/activity" \
-H "X-API-Key: dv_live_sk_your_key_here"
import { Orbit } from '@devotel/orbit-sdk'
const orbit = new Orbit({
apiKey: process.env.ORBIT_API_KEY!,
})
const res = await fetch('https://orbit-api.devotel.io/api/v1/conversations/{id}/activity', {
method: 'GET',
headers: {
'X-API-Key': process.env.ORBIT_API_KEY!,
},
})
console.log(await res.json())
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
r = requests.get("https://orbit-api.devotel.io/api/v1/conversations/{id}/activity", headers=headers)
print(r.json())
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("GET", "https://orbit-api.devotel.io/api/v1/conversations/{id}/activity", nil)
req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
http.DefaultClient.Do(req)
}
require 'net/http'
require 'json'
uri = URI('https://orbit-api.devotel.io/api/v1/conversations/{id}/activity')
req = Net::HTTP::Get.new(uri)
req['X-API-Key'] = ENV['ORBIT_API_KEY']
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
puts res.body
<?php
$ch = curl_init('https://orbit-api.devotel.io/api/v1/conversations/{id}/activity');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-API-Key: ' . getenv('ORBIT_API_KEY'),
]);
echo curl_exec($ch);
Read agent_active state for a conversation
GET /api/v1/conversations/{id}/agent/active
AGT-017: Returns . The agent-runtime calls this before running the executor to check if the conversation is paused for human-in-the-loop handling. Fails open (agent_active=true) on DB error.
curl -X GET "https://orbit-api.devotel.io/api/v1/conversations/{id}/agent/active" \
-H "X-API-Key: dv_live_sk_your_key_here"
import { Orbit } from '@devotel/orbit-sdk'
const orbit = new Orbit({
apiKey: process.env.ORBIT_API_KEY!,
})
const res = await fetch('https://orbit-api.devotel.io/api/v1/conversations/{id}/agent/active', {
method: 'GET',
headers: {
'X-API-Key': process.env.ORBIT_API_KEY!,
},
})
console.log(await res.json())
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
r = requests.get("https://orbit-api.devotel.io/api/v1/conversations/{id}/agent/active", headers=headers)
print(r.json())
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("GET", "https://orbit-api.devotel.io/api/v1/conversations/{id}/agent/active", nil)
req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
http.DefaultClient.Do(req)
}
require 'net/http'
require 'json'
uri = URI('https://orbit-api.devotel.io/api/v1/conversations/{id}/agent/active')
req = Net::HTTP::Get.new(uri)
req['X-API-Key'] = ENV['ORBIT_API_KEY']
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
puts res.body
<?php
$ch = curl_init('https://orbit-api.devotel.io/api/v1/conversations/{id}/agent/active');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-API-Key: ' . getenv('ORBIT_API_KEY'),
]);
echo curl_exec($ch);
Get the full handoff packet for an in-flight AI conversation
GET /api/v1/conversations/{id}/handoff/packet
Read-only operator-facing DTO that bundles everything the human taking over needs: executive summary, last-inbound sentiment, full tool-call timeline (what the bot tried), KB articles consulted, explicit open questions the bot is uncertain about, a suggested first reply, and the per-turn confidence trajectory. Derived from existing data — no schema change. Fails open on every slot (missing LLM, missing tool-call rows, missing messages all degrade individual fields to null/[], never 5xx the endpoint).
curl -X GET "https://orbit-api.devotel.io/api/v1/conversations/{id}/handoff/packet" \
-H "X-API-Key: dv_live_sk_your_key_here"
import { Orbit } from '@devotel/orbit-sdk'
const orbit = new Orbit({
apiKey: process.env.ORBIT_API_KEY!,
})
const res = await fetch('https://orbit-api.devotel.io/api/v1/conversations/{id}/handoff/packet', {
method: 'GET',
headers: {
'X-API-Key': process.env.ORBIT_API_KEY!,
},
})
console.log(await res.json())
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
r = requests.get("https://orbit-api.devotel.io/api/v1/conversations/{id}/handoff/packet", headers=headers)
print(r.json())
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("GET", "https://orbit-api.devotel.io/api/v1/conversations/{id}/handoff/packet", nil)
req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
http.DefaultClient.Do(req)
}
require 'net/http'
require 'json'
uri = URI('https://orbit-api.devotel.io/api/v1/conversations/{id}/handoff/packet')
req = Net::HTTP::Get.new(uri)
req['X-API-Key'] = ENV['ORBIT_API_KEY']
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
puts res.body
<?php
$ch = curl_init('https://orbit-api.devotel.io/api/v1/conversations/{id}/handoff/packet');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-API-Key: ' . getenv('ORBIT_API_KEY'),
]);
echo curl_exec($ch);
Get current queue position for an in-flight handoff
GET /api/v1/conversations/{id}/handoff/queue-position
Returns when the conversation is currently in the org’s handoff queue (Redis sorted set inbox_queue:<orgId>). Returns null position when the conversation has been picked up, was never enqueued, or Redis is unavailable. The customer-facing chat widget polls this endpoint to render ‘You are #N in queue, est wait Mm’.
curl -X GET "https://orbit-api.devotel.io/api/v1/conversations/{id}/handoff/queue-position" \
-H "X-API-Key: dv_live_sk_your_key_here"
import { Orbit } from '@devotel/orbit-sdk'
const orbit = new Orbit({
apiKey: process.env.ORBIT_API_KEY!,
})
const res = await fetch('https://orbit-api.devotel.io/api/v1/conversations/{id}/handoff/queue-position', {
method: 'GET',
headers: {
'X-API-Key': process.env.ORBIT_API_KEY!,
},
})
console.log(await res.json())
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
r = requests.get("https://orbit-api.devotel.io/api/v1/conversations/{id}/handoff/queue-position", headers=headers)
print(r.json())
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("GET", "https://orbit-api.devotel.io/api/v1/conversations/{id}/handoff/queue-position", nil)
req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
http.DefaultClient.Do(req)
}
require 'net/http'
require 'json'
uri = URI('https://orbit-api.devotel.io/api/v1/conversations/{id}/handoff/queue-position')
req = Net::HTTP::Get.new(uri)
req['X-API-Key'] = ENV['ORBIT_API_KEY']
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
puts res.body
<?php
$ch = curl_init('https://orbit-api.devotel.io/api/v1/conversations/{id}/handoff/queue-position');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-API-Key: ' . getenv('ORBIT_API_KEY'),
]);
echo curl_exec($ch);
List linked conversations
GET /api/v1/conversations/{id}/links
Return every conversation linked to this one along with a denormalised preview (channel, status, last_message_at, contact_name, last_message) and the link metadata (id, type, note, created_by, created_at). Directional link types are surfaced from the queried conversation’s perspective — e.g. if A is stored as parent_of B, then GET /A/links returns parent_of and GET /B/links returns child_of for the same row.
curl -X GET "https://orbit-api.devotel.io/api/v1/conversations/{id}/links" \
-H "X-API-Key: dv_live_sk_your_key_here"
import { Orbit } from '@devotel/orbit-sdk'
const orbit = new Orbit({
apiKey: process.env.ORBIT_API_KEY!,
})
const res = await fetch('https://orbit-api.devotel.io/api/v1/conversations/{id}/links', {
method: 'GET',
headers: {
'X-API-Key': process.env.ORBIT_API_KEY!,
},
})
console.log(await res.json())
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
r = requests.get("https://orbit-api.devotel.io/api/v1/conversations/{id}/links", headers=headers)
print(r.json())
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("GET", "https://orbit-api.devotel.io/api/v1/conversations/{id}/links", nil)
req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
http.DefaultClient.Do(req)
}
require 'net/http'
require 'json'
uri = URI('https://orbit-api.devotel.io/api/v1/conversations/{id}/links')
req = Net::HTTP::Get.new(uri)
req['X-API-Key'] = ENV['ORBIT_API_KEY']
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
puts res.body
<?php
$ch = curl_init('https://orbit-api.devotel.io/api/v1/conversations/{id}/links');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-API-Key: ' . getenv('ORBIT_API_KEY'),
]);
echo curl_exec($ch);
Get conversation messages
GET /api/v1/conversations/{id}/messages
Full thread of inbound + outbound messages for a conversation, paginated with cursor.
curl -X GET "https://orbit-api.devotel.io/api/v1/conversations/{id}/messages" \
-H "X-API-Key: dv_live_sk_your_key_here"
import { Orbit } from '@devotel/orbit-sdk'
const orbit = new Orbit({
apiKey: process.env.ORBIT_API_KEY!,
})
const res = await fetch('https://orbit-api.devotel.io/api/v1/conversations/{id}/messages', {
method: 'GET',
headers: {
'X-API-Key': process.env.ORBIT_API_KEY!,
},
})
console.log(await res.json())
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
r = requests.get("https://orbit-api.devotel.io/api/v1/conversations/{id}/messages", headers=headers)
print(r.json())
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("GET", "https://orbit-api.devotel.io/api/v1/conversations/{id}/messages", nil)
req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
http.DefaultClient.Do(req)
}
require 'net/http'
require 'json'
uri = URI('https://orbit-api.devotel.io/api/v1/conversations/{id}/messages')
req = Net::HTTP::Get.new(uri)
req['X-API-Key'] = ENV['ORBIT_API_KEY']
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
puts res.body
<?php
$ch = curl_init('https://orbit-api.devotel.io/api/v1/conversations/{id}/messages');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-API-Key: ' . getenv('ORBIT_API_KEY'),
]);
echo curl_exec($ch);
Per-message sentiment timeline
GET /api/v1/conversations/{id}/sentiment-timeline
Chronologically-ordered (oldest → newest) sentiment trajectory across the messages in this conversation. Returns [{message_id, ts, sentiment_score, sentiment_label}] for every analyzed message (sentiment_score IS NOT NULL). The FE renders this as a sparkline strip at the top of the conversation detail with per-message hover. Unanalyzed messages are skipped; scheduler-sentiment-backfill (5-min cadence in webhook-worker) populates the columns for messages with NULL sentiment_score. Empty array when no messages have been analyzed yet — FE renders an Analyzing… empty state.
curl -X GET "https://orbit-api.devotel.io/api/v1/conversations/{id}/sentiment-timeline" \
-H "X-API-Key: dv_live_sk_your_key_here"
import { Orbit } from '@devotel/orbit-sdk'
const orbit = new Orbit({
apiKey: process.env.ORBIT_API_KEY!,
})
const res = await fetch('https://orbit-api.devotel.io/api/v1/conversations/{id}/sentiment-timeline', {
method: 'GET',
headers: {
'X-API-Key': process.env.ORBIT_API_KEY!,
},
})
console.log(await res.json())
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
r = requests.get("https://orbit-api.devotel.io/api/v1/conversations/{id}/sentiment-timeline", headers=headers)
print(r.json())
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("GET", "https://orbit-api.devotel.io/api/v1/conversations/{id}/sentiment-timeline", nil)
req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
http.DefaultClient.Do(req)
}
require 'net/http'
require 'json'
uri = URI('https://orbit-api.devotel.io/api/v1/conversations/{id}/sentiment-timeline')
req = Net::HTTP::Get.new(uri)
req['X-API-Key'] = ENV['ORBIT_API_KEY']
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
puts res.body
<?php
$ch = curl_init('https://orbit-api.devotel.io/api/v1/conversations/{id}/sentiment-timeline');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-API-Key: ' . getenv('ORBIT_API_KEY'),
]);
echo curl_exec($ch);
GET /api/v1/conversations/tags/distinct
Returns the complete tag vocabulary in use across the tenant’s conversations table, with usage counts. Used by the inbox UI to populate the tag-chip rail and the advanced-filter tag dropdown. Tenant-scoped; caps at 200 tags; results cached server-side for 60s and invalidated on every tag update.
curl -X GET "https://orbit-api.devotel.io/api/v1/conversations/tags/distinct" \
-H "X-API-Key: dv_live_sk_your_key_here"
import { Orbit } from '@devotel/orbit-sdk'
const orbit = new Orbit({
apiKey: process.env.ORBIT_API_KEY!,
})
const res = await fetch('https://orbit-api.devotel.io/api/v1/conversations/tags/distinct', {
method: 'GET',
headers: {
'X-API-Key': process.env.ORBIT_API_KEY!,
},
})
console.log(await res.json())
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
r = requests.get("https://orbit-api.devotel.io/api/v1/conversations/tags/distinct", headers=headers)
print(r.json())
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("GET", "https://orbit-api.devotel.io/api/v1/conversations/tags/distinct", nil)
req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
http.DefaultClient.Do(req)
}
require 'net/http'
require 'json'
uri = URI('https://orbit-api.devotel.io/api/v1/conversations/tags/distinct')
req = Net::HTTP::Get.new(uri)
req['X-API-Key'] = ENV['ORBIT_API_KEY']
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
puts res.body
<?php
$ch = curl_init('https://orbit-api.devotel.io/api/v1/conversations/tags/distinct');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-API-Key: ' . getenv('ORBIT_API_KEY'),
]);
echo curl_exec($ch);
Resume AI agent after human-in-the-loop handoff
POST /api/v1/conversations/{id}/agent/resume
AGT-017: Flips agent_active=TRUE so the AI agent processes the next inbound message. Called from the inbox ‘Resume AI’ button when the conversation’s agent_active is false. Idempotent — already-active conversations return 200 with already_active=true.
curl -X POST "https://orbit-api.devotel.io/api/v1/conversations/{id}/agent/resume" \
-H "X-API-Key: dv_live_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{}'
import { Orbit } from '@devotel/orbit-sdk'
const orbit = new Orbit({
apiKey: process.env.ORBIT_API_KEY!,
})
const res = await fetch('https://orbit-api.devotel.io/api/v1/conversations/{id}/agent/resume', {
method: 'POST',
headers: {
'X-API-Key': process.env.ORBIT_API_KEY!,
'Content-Type': 'application/json',
},
body: JSON.stringify({}),
})
console.log(await res.json())
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
headers["Content-Type"] = "application/json"
r = requests.post("https://orbit-api.devotel.io/api/v1/conversations/{id}/agent/resume", headers=headers, json={})
print(r.json())
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("POST", "https://orbit-api.devotel.io/api/v1/conversations/{id}/agent/resume", bytes.NewBuffer([]byte(`{}`)))
req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
req.Header.Set("Content-Type", "application/json")
http.DefaultClient.Do(req)
}
require 'net/http'
require 'json'
uri = URI('https://orbit-api.devotel.io/api/v1/conversations/{id}/agent/resume')
req = Net::HTTP::Post.new(uri)
req['X-API-Key'] = ENV['ORBIT_API_KEY']
req['Content-Type'] = 'application/json'
req.body = {}.to_json
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
puts res.body
<?php
$ch = curl_init('https://orbit-api.devotel.io/api/v1/conversations/{id}/agent/resume');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-API-Key: ' . getenv('ORBIT_API_KEY'),
'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, <<<JSON
{}
JSON);
echo curl_exec($ch);
Assign conversation
POST /api/v1/conversations/{id}/assign
Assign a conversation to a team member by their user ID.
curl -X POST "https://orbit-api.devotel.io/api/v1/conversations/{id}/assign" \
-H "X-API-Key: dv_live_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{}'
import { Orbit } from '@devotel/orbit-sdk'
const orbit = new Orbit({
apiKey: process.env.ORBIT_API_KEY!,
})
const res = await fetch('https://orbit-api.devotel.io/api/v1/conversations/{id}/assign', {
method: 'POST',
headers: {
'X-API-Key': process.env.ORBIT_API_KEY!,
'Content-Type': 'application/json',
},
body: JSON.stringify({}),
})
console.log(await res.json())
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
headers["Content-Type"] = "application/json"
r = requests.post("https://orbit-api.devotel.io/api/v1/conversations/{id}/assign", headers=headers, json={})
print(r.json())
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("POST", "https://orbit-api.devotel.io/api/v1/conversations/{id}/assign", bytes.NewBuffer([]byte(`{}`)))
req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
req.Header.Set("Content-Type", "application/json")
http.DefaultClient.Do(req)
}
require 'net/http'
require 'json'
uri = URI('https://orbit-api.devotel.io/api/v1/conversations/{id}/assign')
req = Net::HTTP::Post.new(uri)
req['X-API-Key'] = ENV['ORBIT_API_KEY']
req['Content-Type'] = 'application/json'
req.body = {}.to_json
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
puts res.body
<?php
$ch = curl_init('https://orbit-api.devotel.io/api/v1/conversations/{id}/assign');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-API-Key: ' . getenv('ORBIT_API_KEY'),
'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, <<<JSON
{}
JSON);
echo curl_exec($ch);
Close conversation
POST /api/v1/conversations/{id}/close
Close/resolve a conversation. Closed conversations can be reopened.
curl -X POST "https://orbit-api.devotel.io/api/v1/conversations/{id}/close" \
-H "X-API-Key: dv_live_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{}'
import { Orbit } from '@devotel/orbit-sdk'
const orbit = new Orbit({
apiKey: process.env.ORBIT_API_KEY!,
})
const res = await fetch('https://orbit-api.devotel.io/api/v1/conversations/{id}/close', {
method: 'POST',
headers: {
'X-API-Key': process.env.ORBIT_API_KEY!,
'Content-Type': 'application/json',
},
body: JSON.stringify({}),
})
console.log(await res.json())
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
headers["Content-Type"] = "application/json"
r = requests.post("https://orbit-api.devotel.io/api/v1/conversations/{id}/close", headers=headers, json={})
print(r.json())
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("POST", "https://orbit-api.devotel.io/api/v1/conversations/{id}/close", bytes.NewBuffer([]byte(`{}`)))
req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
req.Header.Set("Content-Type", "application/json")
http.DefaultClient.Do(req)
}
require 'net/http'
require 'json'
uri = URI('https://orbit-api.devotel.io/api/v1/conversations/{id}/close')
req = Net::HTTP::Post.new(uri)
req['X-API-Key'] = ENV['ORBIT_API_KEY']
req['Content-Type'] = 'application/json'
req.body = {}.to_json
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
puts res.body
<?php
$ch = curl_init('https://orbit-api.devotel.io/api/v1/conversations/{id}/close');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-API-Key: ' . getenv('ORBIT_API_KEY'),
'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, <<<JSON
{}
JSON);
echo curl_exec($ch);
Agent Copilot — suggest replies + next-best-actions
POST /api/v1/conversations/{id}/copilot/suggest
Returns up to 3 suggested replies and up to 2 next-best-action recommendations for the human agent currently working the conversation. Reads the recent message thread, asks the platform LLM for a structured response. Optional lookback (default 25, max 50) controls how many recent messages are fed to the model.
How many recent messages to feed the LLM. Defaults to 25.
curl -X POST "https://orbit-api.devotel.io/api/v1/conversations/{id}/copilot/suggest" \
-H "X-API-Key: dv_live_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{}'
import { Orbit } from '@devotel/orbit-sdk'
const orbit = new Orbit({
apiKey: process.env.ORBIT_API_KEY!,
})
const res = await fetch('https://orbit-api.devotel.io/api/v1/conversations/{id}/copilot/suggest', {
method: 'POST',
headers: {
'X-API-Key': process.env.ORBIT_API_KEY!,
'Content-Type': 'application/json',
},
body: JSON.stringify({}),
})
console.log(await res.json())
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
headers["Content-Type"] = "application/json"
r = requests.post("https://orbit-api.devotel.io/api/v1/conversations/{id}/copilot/suggest", headers=headers, json={})
print(r.json())
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("POST", "https://orbit-api.devotel.io/api/v1/conversations/{id}/copilot/suggest", bytes.NewBuffer([]byte(`{}`)))
req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
req.Header.Set("Content-Type", "application/json")
http.DefaultClient.Do(req)
}
require 'net/http'
require 'json'
uri = URI('https://orbit-api.devotel.io/api/v1/conversations/{id}/copilot/suggest')
req = Net::HTTP::Post.new(uri)
req['X-API-Key'] = ENV['ORBIT_API_KEY']
req['Content-Type'] = 'application/json'
req.body = {}.to_json
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
puts res.body
<?php
$ch = curl_init('https://orbit-api.devotel.io/api/v1/conversations/{id}/copilot/suggest');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-API-Key: ' . getenv('ORBIT_API_KEY'),
'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, <<<JSON
{}
JSON);
echo curl_exec($ch);
Escalate conversation
POST /api/v1/conversations/{id}/escalate
Mark a conversation as escalated (stamps escalated_at + metadata.escalation), fan out a Slack notification + email to the org’s configured escalation recipients, write an audit row, and emit an inbox SSE event. Idempotent — re-escalating an already-escalated conversation returns the existing escalated_at without re-fanning out the notifications. Recipients resolve from organizations.settings.escalation.user_ids + .email_recipients (or the per-request target_user_ids override). notify_channels lets callers opt out of slack-only or email-only fan-out (default: both).
Free-text reason — surfaced in Slack + email body and stored on metadata.escalation.reason.
priority
string (enum: normal|high|urgent)
Escalation priority. Default ‘urgent’. Drives Slack button styling (danger vs primary) and email subject prefix.
Optional caller override of recipients. Each id is validated against the caller’s organizationId so a malicious or buggy caller cannot leak the notification to another tenant’s users. When omitted, falls back to organizations.settings.escalation.user_ids.
notify_channels
string (enum: slack|email)[]
Opt-out lever for individual notification channels. Default [‘slack’,‘email’] (both). Useful for tests / programmatic callers that only want one fan-out path.
curl -X POST "https://orbit-api.devotel.io/api/v1/conversations/{id}/escalate" \
-H "X-API-Key: dv_live_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{}'
import { Orbit } from '@devotel/orbit-sdk'
const orbit = new Orbit({
apiKey: process.env.ORBIT_API_KEY!,
})
const res = await fetch('https://orbit-api.devotel.io/api/v1/conversations/{id}/escalate', {
method: 'POST',
headers: {
'X-API-Key': process.env.ORBIT_API_KEY!,
'Content-Type': 'application/json',
},
body: JSON.stringify({}),
})
console.log(await res.json())
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
headers["Content-Type"] = "application/json"
r = requests.post("https://orbit-api.devotel.io/api/v1/conversations/{id}/escalate", headers=headers, json={})
print(r.json())
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("POST", "https://orbit-api.devotel.io/api/v1/conversations/{id}/escalate", bytes.NewBuffer([]byte(`{}`)))
req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
req.Header.Set("Content-Type", "application/json")
http.DefaultClient.Do(req)
}
require 'net/http'
require 'json'
uri = URI('https://orbit-api.devotel.io/api/v1/conversations/{id}/escalate')
req = Net::HTTP::Post.new(uri)
req['X-API-Key'] = ENV['ORBIT_API_KEY']
req['Content-Type'] = 'application/json'
req.body = {}.to_json
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
puts res.body
<?php
$ch = curl_init('https://orbit-api.devotel.io/api/v1/conversations/{id}/escalate');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-API-Key: ' . getenv('ORBIT_API_KEY'),
'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, <<<JSON
{}
JSON);
echo curl_exec($ch);
Handoff conversation from AI to a human
POST /api/v1/conversations/{id}/handoff
Distinct from /assign: clears any AI agent assignment, flips status to pending so the conversation re-enters the unassigned queue, and stamps metadata.needs_human + handoff_reason + target_queue + handoff_skill_tags + handoff_matched_operators so the receiving human picks up the context. Audit #HANDOFF-1 also persists a top-level handoff_brief (LLM-generated 2-3 sentence summary) and handoff_reason_category (machine-readable enum) on the conversation row, and audit #HANDOFF-2 enqueues the conversation in the org’s Redis sorted-set queue so the customer-facing widget can render queue position.
Short reason — surfaced to the receiving human (e.g. ‘asked for a manager’).
Optional queue to route to. Defaults to the tenant’s default queue.
reason_category
string (enum: cant_understand|policy_block|customer_request|tool_failed|escalation_threshold|manual)
Machine-readable handoff category for analytics. When omitted, inferred from the free-text reason.
Caller override for the skill set used to match operators. When omitted, derived from the conversation’s most recent AI agent’s skill_tags.
Caller-supplied 2-3 sentence summary. When omitted, the service generates one via the LLM gateway.
curl -X POST "https://orbit-api.devotel.io/api/v1/conversations/{id}/handoff" \
-H "X-API-Key: dv_live_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{}'
import { Orbit } from '@devotel/orbit-sdk'
const orbit = new Orbit({
apiKey: process.env.ORBIT_API_KEY!,
})
const res = await fetch('https://orbit-api.devotel.io/api/v1/conversations/{id}/handoff', {
method: 'POST',
headers: {
'X-API-Key': process.env.ORBIT_API_KEY!,
'Content-Type': 'application/json',
},
body: JSON.stringify({}),
})
console.log(await res.json())
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
headers["Content-Type"] = "application/json"
r = requests.post("https://orbit-api.devotel.io/api/v1/conversations/{id}/handoff", headers=headers, json={})
print(r.json())
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("POST", "https://orbit-api.devotel.io/api/v1/conversations/{id}/handoff", bytes.NewBuffer([]byte(`{}`)))
req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
req.Header.Set("Content-Type", "application/json")
http.DefaultClient.Do(req)
}
require 'net/http'
require 'json'
uri = URI('https://orbit-api.devotel.io/api/v1/conversations/{id}/handoff')
req = Net::HTTP::Post.new(uri)
req['X-API-Key'] = ENV['ORBIT_API_KEY']
req['Content-Type'] = 'application/json'
req.body = {}.to_json
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
puts res.body
<?php
$ch = curl_init('https://orbit-api.devotel.io/api/v1/conversations/{id}/handoff');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-API-Key: ' . getenv('ORBIT_API_KEY'),
'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, <<<JSON
{}
JSON);
echo curl_exec($ch);
Resolve AI handoff
POST /api/v1/conversations/{id}/handoff/resolve
Clear the AI-handoff flags (needs_human, handoff_reason, target_queue) from a conversation’s metadata once a human has engaged. Stamps handoff_resolved_at so the audit trail keeps a record.
curl -X POST "https://orbit-api.devotel.io/api/v1/conversations/{id}/handoff/resolve" \
-H "X-API-Key: dv_live_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{}'
import { Orbit } from '@devotel/orbit-sdk'
const orbit = new Orbit({
apiKey: process.env.ORBIT_API_KEY!,
})
const res = await fetch('https://orbit-api.devotel.io/api/v1/conversations/{id}/handoff/resolve', {
method: 'POST',
headers: {
'X-API-Key': process.env.ORBIT_API_KEY!,
'Content-Type': 'application/json',
},
body: JSON.stringify({}),
})
console.log(await res.json())
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
headers["Content-Type"] = "application/json"
r = requests.post("https://orbit-api.devotel.io/api/v1/conversations/{id}/handoff/resolve", headers=headers, json={})
print(r.json())
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("POST", "https://orbit-api.devotel.io/api/v1/conversations/{id}/handoff/resolve", bytes.NewBuffer([]byte(`{}`)))
req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
req.Header.Set("Content-Type", "application/json")
http.DefaultClient.Do(req)
}
require 'net/http'
require 'json'
uri = URI('https://orbit-api.devotel.io/api/v1/conversations/{id}/handoff/resolve')
req = Net::HTTP::Post.new(uri)
req['X-API-Key'] = ENV['ORBIT_API_KEY']
req['Content-Type'] = 'application/json'
req.body = {}.to_json
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
puts res.body
<?php
$ch = curl_init('https://orbit-api.devotel.io/api/v1/conversations/{id}/handoff/resolve');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-API-Key: ' . getenv('ORBIT_API_KEY'),
'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, <<<JSON
{}
JSON);
echo curl_exec($ch);
POST /api/v1/conversations/{id}/links
Create a soft, reversible cross-reference between this conversation and another without collapsing either timeline (distinct from merge). The link surfaces in both threads’ sidebars and can be removed at any time via DELETE /:id/links/:linkId. Supported link_type values: relates_to (default, symmetric), duplicate_of (symmetric), blocks/blocked_by (directional inverses), parent_of/child_of (directional inverses). Directional types respect the operator’s submitted order from the perspective of /:id.
link_type
string (enum: relates_to|duplicate_of|blocks|blocked_by|parent_of|child_of)
—
curl -X POST "https://orbit-api.devotel.io/api/v1/conversations/{id}/links" \
-H "X-API-Key: dv_live_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"target_conversation_id": "string"
}'
import { Orbit } from '@devotel/orbit-sdk'
const orbit = new Orbit({
apiKey: process.env.ORBIT_API_KEY!,
})
const res = await fetch('https://orbit-api.devotel.io/api/v1/conversations/{id}/links', {
method: 'POST',
headers: {
'X-API-Key': process.env.ORBIT_API_KEY!,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"target_conversation_id": "string"
}),
})
console.log(await res.json())
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
headers["Content-Type"] = "application/json"
r = requests.post("https://orbit-api.devotel.io/api/v1/conversations/{id}/links", headers=headers, json={
"target_conversation_id": "string"
})
print(r.json())
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("POST", "https://orbit-api.devotel.io/api/v1/conversations/{id}/links", bytes.NewBuffer([]byte(`{
"target_conversation_id": "string"
}`)))
req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
req.Header.Set("Content-Type", "application/json")
http.DefaultClient.Do(req)
}
require 'net/http'
require 'json'
uri = URI('https://orbit-api.devotel.io/api/v1/conversations/{id}/links')
req = Net::HTTP::Post.new(uri)
req['X-API-Key'] = ENV['ORBIT_API_KEY']
req['Content-Type'] = 'application/json'
req.body = {
"target_conversation_id": "string"
}.to_json
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
puts res.body
<?php
$ch = curl_init('https://orbit-api.devotel.io/api/v1/conversations/{id}/links');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-API-Key: ' . getenv('ORBIT_API_KEY'),
'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, <<<JSON
{
"target_conversation_id": "string"
}
JSON);
echo curl_exec($ch);
Merge conversations
POST /api/v1/conversations/{id}/merge
Fold one or more sibling conversation rows into this one. Source rows must belong to the same contact (matching contact_id, phone, or email). Sources are archived with metadata.merged_into pointing at the primary; the primary’s channels[] is unioned so the existing thread query loads messages from every channel.
curl -X POST "https://orbit-api.devotel.io/api/v1/conversations/{id}/merge" \
-H "X-API-Key: dv_live_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"source_conversation_ids": []
}'
import { Orbit } from '@devotel/orbit-sdk'
const orbit = new Orbit({
apiKey: process.env.ORBIT_API_KEY!,
})
const res = await fetch('https://orbit-api.devotel.io/api/v1/conversations/{id}/merge', {
method: 'POST',
headers: {
'X-API-Key': process.env.ORBIT_API_KEY!,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"source_conversation_ids": []
}),
})
console.log(await res.json())
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
headers["Content-Type"] = "application/json"
r = requests.post("https://orbit-api.devotel.io/api/v1/conversations/{id}/merge", headers=headers, json={
"source_conversation_ids": []
})
print(r.json())
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("POST", "https://orbit-api.devotel.io/api/v1/conversations/{id}/merge", bytes.NewBuffer([]byte(`{
"source_conversation_ids": []
}`)))
req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
req.Header.Set("Content-Type", "application/json")
http.DefaultClient.Do(req)
}
require 'net/http'
require 'json'
uri = URI('https://orbit-api.devotel.io/api/v1/conversations/{id}/merge')
req = Net::HTTP::Post.new(uri)
req['X-API-Key'] = ENV['ORBIT_API_KEY']
req['Content-Type'] = 'application/json'
req.body = {
"source_conversation_ids": []
}.to_json
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
puts res.body
<?php
$ch = curl_init('https://orbit-api.devotel.io/api/v1/conversations/{id}/merge');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-API-Key: ' . getenv('ORBIT_API_KEY'),
'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, <<<JSON
{
"source_conversation_ids": []
}
JSON);
echo curl_exec($ch);
Reopen conversation
POST /api/v1/conversations/{id}/reopen
Reopen a closed conversation so agents can continue the thread.
curl -X POST "https://orbit-api.devotel.io/api/v1/conversations/{id}/reopen" \
-H "X-API-Key: dv_live_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{}'
import { Orbit } from '@devotel/orbit-sdk'
const orbit = new Orbit({
apiKey: process.env.ORBIT_API_KEY!,
})
const res = await fetch('https://orbit-api.devotel.io/api/v1/conversations/{id}/reopen', {
method: 'POST',
headers: {
'X-API-Key': process.env.ORBIT_API_KEY!,
'Content-Type': 'application/json',
},
body: JSON.stringify({}),
})
console.log(await res.json())
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
headers["Content-Type"] = "application/json"
r = requests.post("https://orbit-api.devotel.io/api/v1/conversations/{id}/reopen", headers=headers, json={})
print(r.json())
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("POST", "https://orbit-api.devotel.io/api/v1/conversations/{id}/reopen", bytes.NewBuffer([]byte(`{}`)))
req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
req.Header.Set("Content-Type", "application/json")
http.DefaultClient.Do(req)
}
require 'net/http'
require 'json'
uri = URI('https://orbit-api.devotel.io/api/v1/conversations/{id}/reopen')
req = Net::HTTP::Post.new(uri)
req['X-API-Key'] = ENV['ORBIT_API_KEY']
req['Content-Type'] = 'application/json'
req.body = {}.to_json
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
puts res.body
<?php
$ch = curl_init('https://orbit-api.devotel.io/api/v1/conversations/{id}/reopen');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-API-Key: ' . getenv('ORBIT_API_KEY'),
'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, <<<JSON
{}
JSON);
echo curl_exec($ch);
Reply in a conversation
POST /api/v1/conversations/{id}/reply
Send a message back to the contact on the same channel. Delegates to the existing messaging router.
curl -X POST "https://orbit-api.devotel.io/api/v1/conversations/{id}/reply" \
-H "X-API-Key: dv_live_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{}'
import { Orbit } from '@devotel/orbit-sdk'
const orbit = new Orbit({
apiKey: process.env.ORBIT_API_KEY!,
})
const res = await fetch('https://orbit-api.devotel.io/api/v1/conversations/{id}/reply', {
method: 'POST',
headers: {
'X-API-Key': process.env.ORBIT_API_KEY!,
'Content-Type': 'application/json',
},
body: JSON.stringify({}),
})
console.log(await res.json())
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
headers["Content-Type"] = "application/json"
r = requests.post("https://orbit-api.devotel.io/api/v1/conversations/{id}/reply", headers=headers, json={})
print(r.json())
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("POST", "https://orbit-api.devotel.io/api/v1/conversations/{id}/reply", bytes.NewBuffer([]byte(`{}`)))
req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
req.Header.Set("Content-Type", "application/json")
http.DefaultClient.Do(req)
}
require 'net/http'
require 'json'
uri = URI('https://orbit-api.devotel.io/api/v1/conversations/{id}/reply')
req = Net::HTTP::Post.new(uri)
req['X-API-Key'] = ENV['ORBIT_API_KEY']
req['Content-Type'] = 'application/json'
req.body = {}.to_json
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
puts res.body
<?php
$ch = curl_init('https://orbit-api.devotel.io/api/v1/conversations/{id}/reply');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-API-Key: ' . getenv('ORBIT_API_KEY'),
'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, <<<JSON
{}
JSON);
echo curl_exec($ch);
Snooze conversation
POST /api/v1/conversations/{id}/snooze
Hide a conversation from the open inbox until the supplied ISO 8601 timestamp. The webhook-worker auto-reopens the row once the deadline passes.
Absolute ISO 8601 timestamp (must be in the future)
Optional human-readable label e.g. ‘wait for invoice approval’
curl -X POST "https://orbit-api.devotel.io/api/v1/conversations/{id}/snooze" \
-H "X-API-Key: dv_live_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"until": "1970-01-01T00:00:00.000Z"
}'
import { Orbit } from '@devotel/orbit-sdk'
const orbit = new Orbit({
apiKey: process.env.ORBIT_API_KEY!,
})
const res = await fetch('https://orbit-api.devotel.io/api/v1/conversations/{id}/snooze', {
method: 'POST',
headers: {
'X-API-Key': process.env.ORBIT_API_KEY!,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"until": "1970-01-01T00:00:00.000Z"
}),
})
console.log(await res.json())
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
headers["Content-Type"] = "application/json"
r = requests.post("https://orbit-api.devotel.io/api/v1/conversations/{id}/snooze", headers=headers, json={
"until": "1970-01-01T00:00:00.000Z"
})
print(r.json())
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("POST", "https://orbit-api.devotel.io/api/v1/conversations/{id}/snooze", bytes.NewBuffer([]byte(`{
"until": "1970-01-01T00:00:00.000Z"
}`)))
req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
req.Header.Set("Content-Type", "application/json")
http.DefaultClient.Do(req)
}
require 'net/http'
require 'json'
uri = URI('https://orbit-api.devotel.io/api/v1/conversations/{id}/snooze')
req = Net::HTTP::Post.new(uri)
req['X-API-Key'] = ENV['ORBIT_API_KEY']
req['Content-Type'] = 'application/json'
req.body = {
"until": "1970-01-01T00:00:00.000Z"
}.to_json
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
puts res.body
<?php
$ch = curl_init('https://orbit-api.devotel.io/api/v1/conversations/{id}/snooze');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-API-Key: ' . getenv('ORBIT_API_KEY'),
'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, <<<JSON
{
"until": "1970-01-01T00:00:00.000Z"
}
JSON);
echo curl_exec($ch);
Undo a recent conversation merge
POST /api/v1/conversations/{id}/unmerge
Reverse a recent mergeConversations call within a 30-minute window. Restores every source row archived into this primary using the pre_merge_snapshot stamped at merge-time; recomputes the primary’s channels[] from pre_merge_channels minus any sources still merged. Mirrors the contact-merge undo gate in /contacts/unmerge/:mergeId. Returns — expired counts sources outside the 30-min window so the UI can show ‘contact support for a manual revert’ when the window has fully expired.
curl -X POST "https://orbit-api.devotel.io/api/v1/conversations/{id}/unmerge" \
-H "X-API-Key: dv_live_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{}'
import { Orbit } from '@devotel/orbit-sdk'
const orbit = new Orbit({
apiKey: process.env.ORBIT_API_KEY!,
})
const res = await fetch('https://orbit-api.devotel.io/api/v1/conversations/{id}/unmerge', {
method: 'POST',
headers: {
'X-API-Key': process.env.ORBIT_API_KEY!,
'Content-Type': 'application/json',
},
body: JSON.stringify({}),
})
console.log(await res.json())
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
headers["Content-Type"] = "application/json"
r = requests.post("https://orbit-api.devotel.io/api/v1/conversations/{id}/unmerge", headers=headers, json={})
print(r.json())
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("POST", "https://orbit-api.devotel.io/api/v1/conversations/{id}/unmerge", bytes.NewBuffer([]byte(`{}`)))
req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
req.Header.Set("Content-Type", "application/json")
http.DefaultClient.Do(req)
}
require 'net/http'
require 'json'
uri = URI('https://orbit-api.devotel.io/api/v1/conversations/{id}/unmerge')
req = Net::HTTP::Post.new(uri)
req['X-API-Key'] = ENV['ORBIT_API_KEY']
req['Content-Type'] = 'application/json'
req.body = {}.to_json
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
puts res.body
<?php
$ch = curl_init('https://orbit-api.devotel.io/api/v1/conversations/{id}/unmerge');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-API-Key: ' . getenv('ORBIT_API_KEY'),
'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, <<<JSON
{}
JSON);
echo curl_exec($ch);
Bulk action
POST /api/v1/conversations/bulk
Apply the same action (close / reopen / assign / add_tags / remove_tags) to up to 200 conversations in a single request. Returns per-id success/failure so the UI can surface partial-success outcomes.
action
string (enum: close|reopen|assign|add_tags|remove_tags)
required
—
Required for action=assign. Null to unassign.
Required for action=add_tags / remove_tags.
curl -X POST "https://orbit-api.devotel.io/api/v1/conversations/bulk" \
-H "X-API-Key: dv_live_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"action": "close",
"conversation_ids": []
}'
import { Orbit } from '@devotel/orbit-sdk'
const orbit = new Orbit({
apiKey: process.env.ORBIT_API_KEY!,
})
const res = await fetch('https://orbit-api.devotel.io/api/v1/conversations/bulk', {
method: 'POST',
headers: {
'X-API-Key': process.env.ORBIT_API_KEY!,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"action": "close",
"conversation_ids": []
}),
})
console.log(await res.json())
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
headers["Content-Type"] = "application/json"
r = requests.post("https://orbit-api.devotel.io/api/v1/conversations/bulk", headers=headers, json={
"action": "close",
"conversation_ids": []
})
print(r.json())
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("POST", "https://orbit-api.devotel.io/api/v1/conversations/bulk", bytes.NewBuffer([]byte(`{
"action": "close",
"conversation_ids": []
}`)))
req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
req.Header.Set("Content-Type", "application/json")
http.DefaultClient.Do(req)
}
require 'net/http'
require 'json'
uri = URI('https://orbit-api.devotel.io/api/v1/conversations/bulk')
req = Net::HTTP::Post.new(uri)
req['X-API-Key'] = ENV['ORBIT_API_KEY']
req['Content-Type'] = 'application/json'
req.body = {
"action": "close",
"conversation_ids": []
}.to_json
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
puts res.body
<?php
$ch = curl_init('https://orbit-api.devotel.io/api/v1/conversations/bulk');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-API-Key: ' . getenv('ORBIT_API_KEY'),
'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, <<<JSON
{
"action": "close",
"conversation_ids": []
}
JSON);
echo curl_exec($ch);
PUT /api/v1/conversations/{id}/tags
Set the tags array on a conversation. Tags are normalized to lowercase, trimmed, max 20 chars each, max 10 per conversation.
curl -X PUT "https://orbit-api.devotel.io/api/v1/conversations/{id}/tags" \
-H "X-API-Key: dv_live_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{}'
import { Orbit } from '@devotel/orbit-sdk'
const orbit = new Orbit({
apiKey: process.env.ORBIT_API_KEY!,
})
const res = await fetch('https://orbit-api.devotel.io/api/v1/conversations/{id}/tags', {
method: 'PUT',
headers: {
'X-API-Key': process.env.ORBIT_API_KEY!,
'Content-Type': 'application/json',
},
body: JSON.stringify({}),
})
console.log(await res.json())
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
headers["Content-Type"] = "application/json"
r = requests.put("https://orbit-api.devotel.io/api/v1/conversations/{id}/tags", headers=headers, json={})
print(r.json())
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("PUT", "https://orbit-api.devotel.io/api/v1/conversations/{id}/tags", bytes.NewBuffer([]byte(`{}`)))
req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
req.Header.Set("Content-Type", "application/json")
http.DefaultClient.Do(req)
}
require 'net/http'
require 'json'
uri = URI('https://orbit-api.devotel.io/api/v1/conversations/{id}/tags')
req = Net::HTTP::Put.new(uri)
req['X-API-Key'] = ENV['ORBIT_API_KEY']
req['Content-Type'] = 'application/json'
req.body = {}.to_json
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
puts res.body
<?php
$ch = curl_init('https://orbit-api.devotel.io/api/v1/conversations/{id}/tags');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-API-Key: ' . getenv('ORBIT_API_KEY'),
'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, <<<JSON
{}
JSON);
echo curl_exec($ch);
DELETE /api/v1/conversations/{id}/links/{linkId}
Remove a conversation link by its link id (NOT by the other conversation’s id — the link id is returned from POST /:id/links and from GET /:id/links). Returns 404 when the link id doesn’t exist in this tenant so the FE can distinguish ‘no longer exists’ from ‘successfully removed’.
curl -X DELETE "https://orbit-api.devotel.io/api/v1/conversations/{id}/links/{linkId}" \
-H "X-API-Key: dv_live_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{}'
import { Orbit } from '@devotel/orbit-sdk'
const orbit = new Orbit({
apiKey: process.env.ORBIT_API_KEY!,
})
const res = await fetch('https://orbit-api.devotel.io/api/v1/conversations/{id}/links/{linkId}', {
method: 'DELETE',
headers: {
'X-API-Key': process.env.ORBIT_API_KEY!,
'Content-Type': 'application/json',
},
body: JSON.stringify({}),
})
console.log(await res.json())
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
headers["Content-Type"] = "application/json"
r = requests.delete("https://orbit-api.devotel.io/api/v1/conversations/{id}/links/{linkId}", headers=headers, json={})
print(r.json())
package main
import (
"bytes"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("DELETE", "https://orbit-api.devotel.io/api/v1/conversations/{id}/links/{linkId}", bytes.NewBuffer([]byte(`{}`)))
req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
req.Header.Set("Content-Type", "application/json")
http.DefaultClient.Do(req)
}
require 'net/http'
require 'json'
uri = URI('https://orbit-api.devotel.io/api/v1/conversations/{id}/links/{linkId}')
req = Net::HTTP::Delete.new(uri)
req['X-API-Key'] = ENV['ORBIT_API_KEY']
req['Content-Type'] = 'application/json'
req.body = {}.to_json
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
puts res.body
<?php
$ch = curl_init('https://orbit-api.devotel.io/api/v1/conversations/{id}/links/{linkId}');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-API-Key: ' . getenv('ORBIT_API_KEY'),
'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, <<<JSON
{}
JSON);
echo curl_exec($ch);