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.
| Header | Value |
|---|---|
Authorization | Bearer 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
| Field | Type | Required | Description |
|---|---|---|---|
to | string | Yes | Destination number in E.164 format, e.g. +14155550123. |
objective | string | Yes | What the AI should accomplish on the call. Write it as a clear goal in plain text. |
context | string | No | Background information the AI should know before the call. Not shared with the recipient. |
fields | array | No | Structured fields to extract from the transcript. Up to 20 items. Each item: { name, type?, description? }. type is one of string (default), boolean, or number. |
webhookUrl | string | No | HTTPS URL to receive signed webhook events during and after the call. See Webhooks. |
callerId | string | No | Caller ID override. Must be a number on your account (E.164). Defaults to the number set in your Developers dashboard. |
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
{
"callId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"status": "queued"
}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": [
{ "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": {
"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
| Field | Type | Description |
|---|---|---|
id | string | The call ID. |
status | string | Current status. See lifecycle below. |
to | string | Destination number (E.164). |
objective | string | The objective you supplied. |
summary | string | null | Plain-English summary of the call. Set on completed. |
transcript | string | null | Full transcript of the conversation. |
extracted | object | null | Extracted field values keyed by field name. |
recordingUrl | string | null | Authenticated URL to the call recording. |
startedAt | string | null | ISO 8601 timestamp when the call was answered. |
endedAt | string | null | ISO 8601 timestamp when the call ended. |
durationSec | number | null | Call duration in seconds. |
error | string | null | Error token on failed calls. |
{
"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"status": "completed",
"to": "+14155550123",
"objective": "Book a table for 4 at 7 PM Friday.",
"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 in the array has the same shape as GET /v1/agent/calls/{id}.
| Query param | Type | Default | Description |
|---|---|---|---|
limit | number | 20 | Maximum number of calls to return. Max 100. |
{
"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.
| Status | Terminal | Meaning |
|---|---|---|
queued | No | Accepted; call not yet dialing. |
dialing | No | Dialing the destination. |
in_progress | No | Call is live; AI is speaking. |
completed | Yes | Call finished normally. Summary and extracted fields available. |
no_answer | Yes | Destination did not pick up. |
voicemail | Yes | Reached voicemail. |
declined | Yes | Call rejected by recipient. |
failed | Yes | Technical 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 returns400 caller_id_not_owned.
Error reference
| HTTP status | Error token | Meaning |
|---|---|---|
| 400 | to_required | The to field is missing. |
| 400 | objective_required | The objective field is missing. |
| 400 | invalid_number | The destination is not a valid E.164 number. |
| 400 | destination_blocked | The destination is blocked (premium, IRSF, or unsupported country). |
| 400 | caller_id_not_owned | The callerId is not a number on your account. |
| 400 | invalid_fields | The fields array is malformed or exceeds the 20-item limit. |
| 400 | invalid_context | The context field is the wrong type (must be a string). |
| 400 | invalid_caller_id | The callerId field is the wrong type (must be a string in E.164 format). |
| 400 | invalid_webhook_url | The webhookUrl is not a valid HTTPS URL. |
| 401 | unauthorized | Missing or invalid API key. |
| 429 | rate_limited | Too many calls per minute. Retry after a short wait. |
| 429 | over_minute_cap | Outbound usage limit reached for this billing period. |
| 502 | placement_failed | Carrier or trunk issue. Retry shortly. |
| 503 | store_error | Temporary service error. Retry shortly. |
Error responses have the shape { "error": "token" }.