# Platform overview

RelationsAI runs AI voice agents that hold phone conversations on your behalf. You design and publish agents in the RelationsAI console, then use the API to start calls and campaigns, read results and receive events. This page shows how the pieces connect so you know which part of your integration talks to what.

## The console and the API

The console and the API work on the same workspace. The console is where people set things up; the API is where your software does the day-to-day work.

| Task | Where |
| --- | --- |
| Build, configure, test and publish agents | Console **Availability: Console only** |
| Add phone numbers and assign them to agents | Console **Availability: Console only** |
| Members, roles, billing, balance, calling hours, consent policy, security settings | Console **Availability: Console only** |
| Create, rotate and revoke API keys | Console **Availability: Console only** |
| Read agents, check whether an agent can call, read phone numbers | API |
| Place calls and read their status, transcript, recording and analysis | API |
| Create, start, pause, resume and stop campaigns | API (or console) |
| Sync contacts, consent and the do-not-call list | API (or console) |
| Register webhook endpoints and inspect deliveries | API (or console) |

Agent authoring through the API is **Availability: Planned**. See [API availability](/getting-started/api-availability) for the full list.

## Lifecycle of a call

A call is asynchronous: the request that places it returns before anyone answers.

1. **Accepted.** `POST /v1/calls` checks the agent, your account, balance and spend limits, the do-not-call list, consent, calling hours and the per-number daily limit. If everything passes, it answers `202 Accepted` with the call in `queued` status. If anything fails, nothing is dialled and you get an error that says why.
2. **Dialled and connected.** The call moves to `in_progress` when the person answers. (A separate `ringing` status is not reported today; the workspace's `capabilities.call_ringing_status` says when it is.)
3. **Ended.** `status` becomes `ended` and `outcome` says how: `completed`, `no_response`, `no_answer`, `busy`, `failed` or `unknown`. RelationsAI sends a `call.ended` event. The outcome is known at this point but can still be corrected until the call settles.
4. **Settled.** After the call ends, the transcript, recording and analysis are produced and the charge is finalised, usually within a minute. When everything is final, `settled` becomes `true` and RelationsAI sends `call.settled`. Until then, `outcome`, `duration_seconds` and `cost` can still change.

Campaigns follow the same idea at a larger scale: you create one, add recipients, start it, and its status (`running`, `paused`, `stopped`, `completed`, `failed`) and progress change as it calls each recipient. See [Core concepts](/getting-started/core-concepts).

## Events and webhooks

Instead of polling, register a webhook endpoint: an HTTPS URL on your server. RelationsAI sends it a signed JSON event when something happens, such as `call.ended`, `call.settled`, `campaign.started` or `campaign.completed`. Deliveries are at least once and can arrive out of order, so your receiver verifies the signature, discards duplicates by event id and answers quickly. See [Webhooks](/fundamentals/webhooks) and [Receiving webhooks](/guides/receiving-webhooks).

## Workspaces and modes

Your organisation in RelationsAI is a **workspace**. Every key belongs to one workspace and one **mode**:

- A **test key** (`rai_test_...`) works in test mode: calls are simulated, nothing is dialled or charged, and every object has `"livemode": false`.
- A **live key** (`rai_live_...`) works in live mode: real calls to real people, charged to the workspace balance, with `"livemode": true`.

Calls, campaigns, contacts, do-not-call entries, webhook endpoints and events created in one mode are invisible in the other. Agents and phone numbers are shared configuration, readable in both. See [Environments](/getting-started/environments).

## Compliance built in

The same checks apply to calls from the console, campaigns and the API, in test mode as in live mode. They run when a call is placed, and for a campaign when it starts and when it resumes:

- **Do-not-call list.** A call to a number on the list is refused ([`dnc_listed`](/errors/dnc_listed)), and a campaign leaves listed numbers out when it starts. A campaign that is already running is not screened again: a number added to the list during a run can still be called by that run. To stop that, pause and resume the campaign (the resume is refused while a remaining recipient is listed) or stop it.
- **Consent.** When the workspace requires consent, a number that matches a contact without recorded consent is refused ([`consent_required`](/errors/consent_required)). A number with no contact record is not checked for consent.
- **Calling hours.** Calls outside the hours set in the console are refused ([`outside_calling_hours`](/errors/outside_calling_hours)).
- **Per-number daily limit.** One number is not called more often in 24 hours than the workspace allows ([`contact_daily_limit_reached`](/errors/contact_daily_limit_reached)).
- **AI disclosure.** An agent cannot be published unless its instructions tell the people it calls that they are speaking with an AI.

No API key or request option can switch these checks off. Do-not-call screening can be switched off for a single campaign only in the console, by a person; the API refuses to start or resume such a campaign ([`campaign_dnc_override_enabled`](/errors/campaign_dnc_override_enabled)), and the campaign's `dnc_screening` shows `disabled_in_console`. Your integration's job is to keep consent and the do-not-call list accurate; see [Consent and do-not-call](/guides/consent-and-dnc).

## Where to go next

- [Core concepts](/getting-started/core-concepts): every object and how they relate.
- [Quickstart](/getting-started/quickstart): the lifecycle above, in test mode.
