New: voice agent API + MCP·Place calls over REST or MCP →
By Your Side
Sign inBook a demoStart building

Documentation

API reference

Base URL: https://api.byourside.ai. All endpoints require Authorization: Bearer bys_ak_... (your agent key). Request and response bodies are JSON.

Authentication

Every request must include an Authorization header with your agent API key. Keys are created in your account under Account - Developers.

HeaderValue
AuthorizationBearer bys_ak_YOUR_KEY

Place a call

POST /v1/agent/calls

Validates the request, runs guardrails, creates a call record with status queued, and immediately returns a callId. The call runs asynchronously. Guardrail failures return a 4xx before any call is placed.

Request body

FieldTypeRequiredDescription
tostringYesDestination number in E.164 format, e.g. +14155550123.
objectivestringYesWhat the AI should accomplish on the call. Write it as a clear goal in plain text.
contextstringNoBackground information the AI should know before the call. Not shared with the recipient.
fieldsarrayNoStructured fields to extract from the transcript. Up to 20 items. Each item: { name, type?, description? }. type is one of string (default), boolean, or number.
webhookUrlstringNoHTTPS URL to receive signed webhook events during and after the call. See Webhooks.
callerIdstringNoCaller ID override. Must be a number on your account (E.164). Defaults to the number set in your Developers dashboard.
voicestringNoThe voice the AI uses on the call, e.g. Aoede, Kore, Puck. Defaults to the voice set on the key in the Developers dashboard, then the platform default.
languagestringNoThe language the AI speaks, set per call as a BCP-47 code (e.g. en-US, ru-RU), or auto. There is no per-key language default. If you omit it, By Your Side infers the language from your objective and context, falling back to matching whoever answers. The response reports the resolved language and a languageSource of explicit (you passed it), inferred (picked from your objective), or auto (mirrors the recipient). Auto-detection is best-effort and not always perfect, so pass language when it matters.
Place a call (request)
POST /v1/agent/calls
Authorization: Bearer bys_ak_YOUR_KEY
Content-Type: application/json

{
  "to":        "+14155550123",
  "objective": "Book a table for 4 at 7 PM Friday. Confirm the time and ask for their name.",
  "context":   "We have dined here before. Our preference is a window seat.",
  "fields": [
    { "name": "booked",         "type": "boolean", "description": "Did they confirm the booking?" },
    { "name": "confirmed_time", "type": "string",  "description": "The time they confirmed"       },
    { "name": "contact_name",   "type": "string",  "description": "Name given for the reservation"}
  ],
  "webhookUrl": "https://your-server.example.com/webhooks/bys",
  "callerId":   "+12134932550"
}

Response

Returns immediately with callId (use this to poll or match webhook events), status: "queued", and the resolved language, languageSource, and voice the call will use. Check languageSource to confirm whether the language was taken from your request, inferred from your objective, or set to auto.

Place a call (response)
{
  "callId":         "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "status":         "queued",
  "language":       "en-US",
  "languageSource": "inferred",
  "voice":          "Aoede"
}

Structured field extraction

Pass a fields array when placing the call. After the call ends, By Your Side extracts each field from the transcript and populates the extracted object in the call record and webhook payload. Fields not found in the conversation are returned as null.

fields - request
"fields": [
  { "name": "confirmed",  "type": "boolean" },
  { "name": "amount",     "type": "number",  "description": "Price quoted in USD" },
  { "name": "next_steps", "type": "string",  "description": "What the contact said they will do next" }
]
extracted - response
"extracted": {
  "confirmed":  true,
  "amount":     249,
  "next_steps": "They will email a signed contract by end of day Thursday."
}

Get a call

GET /v1/agent/calls/{id}

Returns the current state of a call. Calls are tenant-scoped: you can only fetch calls placed by your own key.

Response fields

FieldTypeDescription
idstringThe call ID.
statusstringCurrent status. See lifecycle below.
tostringDestination number (E.164).
objectivestringThe objective you supplied.
voicestringThe voice the AI used on the call.
languagestringThe language the AI spoke (BCP-47, e.g. en-US).
languageSourcestringHow the language was chosen: explicit (you passed it), inferred (picked from your objective), or auto (mirrored the recipient).
summarystring | nullPlain-English summary of the call. Set on completed.
transcriptstring | nullFull transcript of the conversation.
extractedobject | nullExtracted field values keyed by field name.
recordingUrlstring | nullAuthenticated URL to the call recording.
startedAtstring | nullISO 8601 timestamp when the call was answered.
endedAtstring | nullISO 8601 timestamp when the call ended.
durationSecnumber | nullCall duration in seconds.
errorstring | nullError token on failed calls.
Get a call (response)
{
  "id":          "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "status":      "completed",
  "to":          "+14155550123",
  "objective":   "Book a table for 4 at 7 PM Friday.",
  "voice":          "Aoede",
  "language":       "en-US",
  "languageSource": "inferred",
  "summary":     "The restaurant confirmed a table for 4 at 7 PM Friday in the name of Smith, window section.",
  "extracted": {
    "booked":         true,
    "confirmed_time": "7 PM Friday",
    "contact_name":   "Smith"
  },
  "transcript":   "...",
  "recordingUrl": "https://api.byourside.ai/v1/agent/calls/f47ac10b-58cc-4372-a567-0e02b2c3d479/recording",
  "startedAt":    "2026-06-17T14:03:12.000Z",
  "endedAt":      "2026-06-17T14:05:48.000Z",
  "durationSec":  156
}

List calls

GET /v1/agent/calls?limit=20

Returns recent calls for your account, most recent first. Each item is a summary: it includes id, to, objective, status, summary, startedAt, endedAt, and durationSec. Full details (transcript, extracted, recordingUrl) are on GET /v1/agent/calls/{id}.

Query paramTypeDefaultDescription
limitnumber20Maximum number of calls to return. Max 100.
List calls (response)
{
  "calls": [
    {
      "id":          "f47ac10b-58cc-4372-a567-0e02b2c3d479",
      "to":          "+14155550123",
      "objective":   "Book a table for 4 at 7 PM Friday.",
      "status":      "completed",
      "summary":     "Confirmed table for 4 at 7 PM Friday.",
      "startedAt":   "2026-06-17T14:03:12.000Z",
      "endedAt":     "2026-06-17T14:05:48.000Z",
      "durationSec": 156
    }
  ]
}

Status lifecycle

Statuses queued, dialing, and in_progress are non-terminal. Poll or use a webhook until you see one of the five terminal statuses.

StatusTerminalMeaning
queuedNoAccepted; call not yet dialing.
dialingNoDialing the destination.
in_progressNoCall is live; AI is speaking.
completedYesCall finished normally. Summary and extracted fields available.
no_answerYesDestination did not pick up.
voicemailYesReached voicemail.
declinedYesCall rejected by recipient.
failedYesTechnical failure. Retry if the issue is transient.

Guardrails

The following rules apply to every call. Violations are rejected at placement time with a 400 error; no call is placed or billed.

  • Allowed destinations: US, CA, GB, AU, NZ, and IL. Calls to other countries are rejected with destination_blocked.
  • Blocked number types: Premium-rate, satellite, and IRSF-listed numbers are always blocked, regardless of country.
  • Rate limit: A maximum number of calls per minute applies per API key. Excess requests return 429 rate_limited.
  • Usage cap: Outbound minutes are drawn from your plan allowance. Once the cap is reached, additional calls return 429 over_minute_cap.
  • Caller ID ownership: The callerId (or your account default) must be a number on your account. An unrecognized number returns 400 caller_id_not_owned.

Error reference

HTTP statusError tokenMeaning
400to_requiredThe to field is missing.
400objective_requiredThe objective field is missing.
400invalid_numberThe destination is not a valid E.164 number.
400destination_blockedThe destination is blocked (premium, IRSF, or unsupported country).
400caller_id_not_ownedThe callerId is not a number on your account.
400invalid_fieldsThe fields array is malformed or exceeds the 20-item limit.
400invalid_contextThe context field is the wrong type (must be a string).
400invalid_caller_idThe callerId field is the wrong type (must be a string in E.164 format).
400invalid_webhook_urlThe webhookUrl is not a valid HTTPS URL.
401unauthorizedMissing or invalid API key.
429rate_limitedToo many calls per minute. Retry after a short wait.
429over_minute_capOutbound usage limit reached for this billing period.
502placement_failedCarrier or trunk issue. Retry shortly.
503store_errorTemporary service error. Retry shortly.

Error responses have the shape { "error": "token" }.