Skip to content
TriedDocs
Get started

Migrate from Retell

Point existing Retell API code at Tried, keep webhook verification working, and check the fields and paths that differ.

Tried's public API uses Retell's endpoint paths, JSON field names (snake_case) and object shapes for agents, Retell LLMs, conversation flows, calls, phone numbers, knowledge bases, batch calls, voices and chat. Most integrations move by changing the base URL and the API key. This page lists what to change and what to check.

What to change#

1. Base URL#

Send requests to https://api.tried.com.au instead of https://api.retellai.com. Paths stay the same, for example POST /create-agent, POST /v2/create-phone-call, GET /v2/get-call/:call_id.

2. API key#

Create a key under Settings, API Keys and send it as a bearer token:

curl https://api.tried.com.au/v2/list-agents \
  -H "Authorization: Bearer $TRIED_API_KEY"

Keys can be limited by scope and source IP. See Account.

3. Webhook signature#

Every webhook carries two headers with the same value:

Header Value
x-tried-signature v=<timestamp_ms>,d=<hex HMAC-SHA256>
x-retell-signature Same value, for code that already reads this header

The HMAC is computed over the raw request body followed by the timestamp, with your API key as the secret. Tried signs with the key marked Set as webhook key under Settings, API Keys, or the workspace's oldest key when none is marked. Reject requests whose timestamp is more than 5 minutes old.

If your handler verifies x-retell-signature with the v=...,d=... format, it keeps working once you give it the Tried webhook key. To verify it yourself:

import { createHmac, timingSafeEqual } from "node:crypto";

export function verify(rawBody: string, apiKey: string, header: string): boolean {
  const m = /^v=(\d+),d=([0-9a-f]+)$/.exec(header);
  if (!m) return false;
  const ts = Number(m[1]);
  if (Math.abs(Date.now() - ts) > 5 * 60_000) return false;
  const expected = createHmac("sha256", apiKey).update(rawBody + ts).digest();
  const given = Buffer.from(m[2], "hex");
  return expected.length === given.length && timingSafeEqual(expected, given);
}
import hashlib, hmac, re, time

def verify(raw_body: str, api_key: str, header: str) -> bool:
    m = re.fullmatch(r"v=(\d+),d=([0-9a-f]+)", header)
    if not m:
        return False
    ts = int(m.group(1))
    if abs(time.time() * 1000 - ts) > 5 * 60_000:
        return False
    expected = hmac.new(api_key.encode(), (raw_body + str(ts)).encode(), hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, m.group(2))

See Webhooks.

4. Web call front end#

Replace the browser client import with @tried/web-sdk. It exports RetellWebClient as an alias of TriedWebClient, with startCall, stopCall, mute and unmute and the same event names. Pass the access_token from POST /v3/create-web-call. See SDKs.

What stays the same#

Area Notes
Agents /create-agent, /get-agent/:agent_id, /list-agents and /v2/list-agents, /update-agent/:agent_id, /delete-agent/:agent_id, /publish-agent-version/:agent_id, /list-agent-versions/:agent_id, /create-agent-version/:agent_id
Engines /create-retell-llm and /create-conversation-flow with their get, list, update and delete endpoints. response_engine types retell-llm, conversation-flow, custom-llm
Calls /v2/create-phone-call, /v3/create-web-call (also /v2/create-web-call), /v2/register-phone-call, /v2/get-call/:call_id, /v3/list-calls and /v2/list-calls, /v2/update-call/:call_id, /update-live-call/:call_id, /v2/stop-call/:call_id, /v2/delete-call/:call_id
Call object call_id, call_status, transcript, transcript_object, transcript_with_tool_calls, call_analysis, latency, call_cost, disconnection_reason, retell_llm_dynamic_variables, collected_dynamic_variables
Dynamic variables {{name}} syntax, retell_llm_dynamic_variables on calls, unknown variables left as written
Webhook payload { "event": "...", "call": { ... } } for call_started, call_ended, call_analyzed, transcript_updated and the transfer events
Custom LLM The WebSocket protocol at {llm_websocket_url}/{call_id}
Custom functions Requests are signed with x-retell-signature (and x-tried-signature)

What to check#

Unknown fields are dropped#

Agent, Retell LLM and conversation flow bodies are parsed against Tried's schema. Fields the schema does not know are removed without an error and are not stored. After migrating, read the agent back with Get agent and compare it to what you sent.

Model names#

model on a Retell LLM, model_choice.model on a flow and post_call_analysis_model accept only these values:

Provider Models
OpenAI gpt-5.6-terra (default), gpt-5.6-luna, gpt-5.5, gpt-5.4-mini, gpt-4.1, gpt-4.1-mini
Anthropic claude-opus-5-5, claude-sonnet-5-5, claude-haiku-5-5, claude-sonnet-4-6, claude-haiku-4-5
Google gemini-3.5-flash, gemini-3-flash-preview, gemini-2.5-flash

Speech-to-speech: s2s_model accepts gpt-realtime and gpt-realtime-mini. Any other value returns a 400 error. See LLM options.

Voice IDs and voice models#

Use a voice_id returned by List voices: the built-in catalogue (for example 11labs-Adrian, cartesia-Katie, openai-Marin, deepgram-Thalia) plus voices your workspace cloned or added. voice_model accepts the values listed on Voice. Agents created without a voice get 11labs-Adrian on eleven_v4_turbo (Premium).

Defaults that may differ#

New agents take Tried's defaults for anything you leave out, including language: "en-AU", timezone: "Australia/Sydney", responsiveness: 1, interruption_sensitivity: 0.9, stt_mode: "fast", denoising_mode: "noise-cancellation", end_call_after_silence_ms: 600000 and max_call_duration_ms: 3600000. Set the fields you rely on explicitly.

Paths that differ#

Task Tried endpoint
Listen to a live call POST /v2/calls/:call_id/listen
Take over a live call POST /v2/calls/:call_id/take-over
Export requests GET /exports, POST /exports, GET /exports/:id/download
Contacts /create-contact, /get-contact/:id, /list-contacts, /update-contact/:id, /delete-contact/:id, /get-contact-by-phone/:phone_number
Playground completion POST /agent-playground-completion/:agent_id streams server-sent events in Tried's own shape

IDs#

IDs keep the familiar prefixes (agent_, llm_, conversation_flow_, call_, knowledge_base_, batch_call_) followed by 24 hex characters. Phone numbers are their own ID in E.164 format. Resources do not carry over: recreate agents, engines and knowledge bases in Tried and store the new IDs.

Errors#

Errors return { "status": <http status>, "message": "<text>" }.

Move an agent across#

The fastest path is to export the configuration from your existing system as JSON, then create the engine and the agent in Tried:

Create the engine#

Send the Retell LLM or conversation flow body to Create Retell LLM or Create conversation flow. Fix any model names first.

Create the agent#

Send the agent body to Create agent with response_engine pointing at the new engine ID.

Recreate knowledge bases#

Create each knowledge base with Create knowledge base and put the new IDs in knowledge_base_ids on the engine.

Move the numbers#

Connect your numbers on Twilio or your SIP trunk, or buy new ones, and assign the agent. See Telephony.

Test and publish#

Test in the dashboard, then publish. See Quickstart.

The dashboard Import button on the agents list takes a JSON file made by Export JSON (agent plus its LLM or flow), or a bare agent object with a response_engine. For a bare agent it creates an empty Retell LLM, so send engine bodies through the API instead.