Skip to main content

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.
cursor
string
Pagination cursor
limit
integer
sort
string
Sort field (prefix with - for DESC)
channel
string (enum: sms|whatsapp|email|rcs|viber|instagram|…)
status
string (enum: open|closed|pending|snoozed|active|archived|…)
assigned_agent
string
Filter by assigned agent ID (legacy)
Search by contact name, phone, or email
tag
string
Filter by tag (legacy, single)
channels
string
Comma-separated list of channels
statuses
string
Comma-separated list of statuses (open,closed,pending,snoozed,…)
tags
string
Comma-separated tag list (ANY match)
assignee
string
“me”, “unassigned”, or a user id
date_from
string
date_to
string
segment_id
string
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)
filters
string
JSON-encoded full filter AST (takes precedence)
cURL
curl -X GET "https://orbit-api.devotel.io/api/v1/conversations/" \
  -H "X-API-Key: dv_live_sk_your_key_here" 
Node.js
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())


Python
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())
Go
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)
}
Ruby
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
<?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.
id
string
required
cURL
curl -X GET "https://orbit-api.devotel.io/api/v1/conversations/{id}" \
  -H "X-API-Key: dv_live_sk_your_key_here" 
Node.js
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())


Python
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())
Go
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)
}
Ruby
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
<?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).
id
string
required
cursor
string
limit
integer
cURL
curl -X GET "https://orbit-api.devotel.io/api/v1/conversations/{id}/activity" \
  -H "X-API-Key: dv_live_sk_your_key_here" 
Node.js
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())


Python
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())
Go
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)
}
Ruby
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
<?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.
id
string
required
cURL
curl -X GET "https://orbit-api.devotel.io/api/v1/conversations/{id}/agent/active" \
  -H "X-API-Key: dv_live_sk_your_key_here" 
Node.js
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())


Python
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())
Go
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)
}
Ruby
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
<?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).
id
string
required
cURL
curl -X GET "https://orbit-api.devotel.io/api/v1/conversations/{id}/handoff/packet" \
  -H "X-API-Key: dv_live_sk_your_key_here" 
Node.js
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())


Python
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())
Go
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)
}
Ruby
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
<?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’.
id
string
required
cURL
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" 
Node.js
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())


Python
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())
Go
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)
}
Ruby
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
<?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.
id
string
required
cURL
curl -X GET "https://orbit-api.devotel.io/api/v1/conversations/{id}/links" \
  -H "X-API-Key: dv_live_sk_your_key_here" 
Node.js
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())


Python
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())
Go
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)
}
Ruby
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
<?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.
id
string
required
cursor
string
limit
integer
cURL
curl -X GET "https://orbit-api.devotel.io/api/v1/conversations/{id}/messages" \
  -H "X-API-Key: dv_live_sk_your_key_here" 
Node.js
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())


Python
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())
Go
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)
}
Ruby
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
<?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.
id
string
required
cURL
curl -X GET "https://orbit-api.devotel.io/api/v1/conversations/{id}/sentiment-timeline" \
  -H "X-API-Key: dv_live_sk_your_key_here" 
Node.js
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())


Python
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())
Go
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)
}
Ruby
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
<?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);

List distinct conversation tags for the tenant

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
curl -X GET "https://orbit-api.devotel.io/api/v1/conversations/tags/distinct" \
  -H "X-API-Key: dv_live_sk_your_key_here" 
Node.js
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())


Python
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())
Go
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)
}
Ruby
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
<?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.
id
string
required
cURL
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 '{}'
Node.js
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())
Python
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())
Go
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)
}
Ruby
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
<?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.
id
string
required
cURL
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 '{}'
Node.js
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())
Python
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())
Go
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)
}
Ruby
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
<?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.
id
string
required
cURL
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 '{}'
Node.js
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())
Python
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())
Go
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)
}
Ruby
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
<?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.
id
string
required
lookback
integer
How many recent messages to feed the LLM. Defaults to 25.
cURL
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 '{}'
Node.js
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())
Python
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())
Go
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)
}
Ruby
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
<?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).
id
string
required
reason
string
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.
target_user_ids
string[]
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
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 '{}'
Node.js
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())
Python
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())
Go
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)
}
Ruby
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
<?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.
id
string
required
reason
string
Short reason — surfaced to the receiving human (e.g. ‘asked for a manager’).
target_queue
string
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.
skill_tags
string[]
Caller override for the skill set used to match operators. When omitted, derived from the conversation’s most recent AI agent’s skill_tags.
brief
string
Caller-supplied 2-3 sentence summary. When omitted, the service generates one via the LLM gateway.
cURL
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 '{}'
Node.js
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())
Python
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())
Go
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)
}
Ruby
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
<?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.
id
string
required
cURL
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 '{}'
Node.js
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())
Python
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())
Go
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)
}
Ruby
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
<?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.
id
string
required
target_conversation_id
string
required
note
string
cURL
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"
}'
Node.js
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())
Python
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())
Go
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)
}
Ruby
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
<?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.
id
string
required
source_conversation_ids
string[]
required
cURL
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": []
}'
Node.js
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())
Python
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())
Go
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)
}
Ruby
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
<?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.
id
string
required
cURL
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 '{}'
Node.js
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())
Python
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())
Go
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)
}
Ruby
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
<?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.
id
string
required
cURL
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 '{}'
Node.js
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())
Python
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())
Go
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)
}
Ruby
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
<?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.
id
string
required
until
string
required
Absolute ISO 8601 timestamp (must be in the future)
reason
string
Optional human-readable label e.g. ‘wait for invoice approval’
cURL
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"
}'
Node.js
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())
Python
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())
Go
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)
}
Ruby
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
<?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.
id
string
required
cURL
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 '{}'
Node.js
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())
Python
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())
Go
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)
}
Ruby
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
<?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
conversation_ids
string[]
required
agent_id
string | null
Required for action=assign. Null to unassign.
tags
string[]
Required for action=add_tags / remove_tags.
cURL
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": []
}'
Node.js
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())
Python
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())
Go
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)
}
Ruby
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
<?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);

Update conversation tags

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.
id
string
required
cURL
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 '{}'
Node.js
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())
Python
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())
Go
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)
}
Ruby
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
<?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’.
id
string
required
cURL
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 '{}'
Node.js
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())
Python
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())
Go
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)
}
Ruby
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
<?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);