> For the complete documentation index, see [llms.txt](https://help.ringtonic.app/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://help.ringtonic.app/guides/api.md).

# API

API Keys let you access Ring Tonic data programmatically. Use them to build custom integrations, sync call data with your CRM, or power dashboards.

{% hint style="info" %}
API Keys are available on the **Agency plan** only.
{% endhint %}

***

### Creating an API Key

1. Go to **Settings** → **API Keys**
2. Enter a descriptive name (e.g., "SEO Utils Integration")
3. Choose the key's scopes — pick the **Read-only** preset, **Full access**, or toggle individual scopes yourself
4. *(Optional)* Set an **Expiration**: Never, 30, 90, or 365 days
5. Click **Create API Key**
6. Copy your key immediately — you won't see it again

<figure><img src="/files/gnPEZByluC4wqK6IMyqw" alt=""><figcaption></figcaption></figure>

{% hint style="warning" %}
**Store your key securely.** Treat it like a password. Anyone with your key can access your data.
{% endhint %}

{% hint style="info" %}
**Only grant the scopes an integration actually needs.** A dashboard that displays call data needs read scopes alone — giving it write scopes means a mistake in that integration can change or delete your records.
{% endhint %}

***

### Using Your API Key

Include the key in the `Authorization` header of all requests:

```bash
curl "https://ringtonic.app/api/v1/campaigns" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json"
```

***

### Managing Keys

| Action        | How                            |
| ------------- | ------------------------------ |
| View keys     | **Settings** → **API Keys**    |
| See last used | Check the "Last Used" column   |
| Revoke key    | Click the trash icon → Confirm |

{% hint style="danger" %}
**Revoking is permanent.** Any integrations using that key will immediately stop working.
{% endhint %}

***

### Available Endpoints

Full interactive documentation — every endpoint with request and response schemas, plus a built-in "Try It" console — lives here:

{% embed url="<https://ringtonic.app/docs/api>" %}

You can **read** campaigns, call logs (with recordings and transcriptions), contacts, conversions, tracking numbers, call flows, tags, webhook endpoints, form submissions, blocked numbers, products & services, and appointments.

***

### Creating and Changing Data

The API can now build and maintain your account, not just read from it. Onboarding a new client no longer has to be a session in the dashboard — it can be a script.

| What you can manage | Create | Update | Delete |
| ------------------- | ------ | ------ | ------ |
| Campaigns           | ✅      | ✅      | ✅      |
| Call flows          | ✅      | ✅      | ✅      |
| Tracking numbers    | ✅      | ✅      | ✅      |
| Tags                | ✅      | ✅      | ✅      |
| Products & services | ✅      | ✅      | ✅      |
| Webhook endpoints   | ✅      | ✅      | ✅      |
| Appointments        | ✅      | ✅      | —      |
| Blocked numbers     | ✅      | —      | ✅      |
| Call logs           | —      | ✅      | ✅      |
| Contacts            | —      | —      | ✅      |

A few actions go beyond simple create and update:

* **Attach or detach a call flow** on a campaign, to change how its calls are answered
* **Duplicate a call flow**, to reuse a working setup for another client
* **Rotate a webhook signing secret**, or **send a test event** to confirm your endpoint is reachable
* **Book, reschedule, cancel, or mark an appointment** completed or no-show — the same booking engine the AI agent and the dashboard use

{% hint style="warning" %}
**Deleting a call flow changes how calls are answered.** Every campaign using that flow is detached and falls back to its simple forwarding settings. Check which campaigns use a flow before removing it.
{% endhint %}

{% hint style="success" %}
**Call flows and phone numbers can now be managed end to end.** You can design a flow, publish it, and buy the number that answers it without opening the dashboard. Both are covered in their own sections below.
{% endhint %}

***

### Building Call Flows

A call flow is a set of steps — greet the caller, check business hours, ring a team, take a message — connected together. The API works with the same flows the [Call Flow Builder](/guides/call-flow-builder.md) produces, so you can design one by hand and then reproduce it for every client with a script.

Each flow has two copies: the **draft** you are editing, and the **published** version that is answering calls. Writing to a flow only ever changes the draft.

{% stepper %}
{% step %}

#### Create the flow

Create a flow with its starting steps, or with none at all to begin from a blank flow.
{% endstep %}

{% step %}

#### Edit the draft

Send the steps and the connections between them. Callers are unaffected — the published version keeps answering.
{% endstep %}

{% step %}

#### Publish

Publishing takes a snapshot of the draft and puts it into service for every campaign using that flow.
{% endstep %}

{% step %}

#### Roll back if needed

Rolling back restores an earlier version **into the draft**. Publish again to actually return that version to service.
{% endstep %}
{% endstepper %}

{% hint style="warning" %}
**A flow is written whole, not step by step.** There is no way to add or remove a single step: whatever you send becomes the draft, so a step you leave out is removed along with its connections.

Always read the flow first, change the parts you want, and send the whole thing back. Building the request from scratch for a small edit will delete the rest of the flow.
{% endhint %}

{% hint style="info" %}
**Drafts are checked as though you were publishing them.** The builder lets you save half-finished work because you can see it on the canvas; a script cannot. So an incomplete flow is refused straight away, naming the step and the setting at fault, rather than being accepted and failing later when you publish.
{% endhint %}

<details>

<summary>Working safely when more than one thing edits a flow</summary>

* Read, change, and write back as one operation, then publish
* Two publishes of the same flow at the same time will not both apply — one succeeds and the other is asked to try again
* A flow that contains AI-generated voice audio publishes in the background, so check its version list to confirm the new version went live
* If you send back a webhook step's hidden signing secret exactly as you received it, the stored secret is kept — so a read-and-write round trip never destroys your credentials

</details>

***

### Buying and Releasing Numbers

Onboarding a client can now include getting them a number. The API can search what is available, buy one, point it at a campaign, and give it back when the client leaves.

{% stepper %}
{% step %}

#### Search what is available

Search by country, and narrow it down by area code, the digits the number contains, whether it is local, toll-free or mobile, and whether it needs to handle calls, texts or both. Prices come back per number type, so a toll-free number is quoted at its own rate.
{% endstep %}

{% step %}

#### Buy a number

Buy a specific number from those results, or give the criteria and let Ring Tonic pick the first match. You can assign it to a campaign and give it a label in the same request.
{% endstep %}

{% step %}

#### Release it when you are done

Releasing gives the number back to the carrier and stops the monthly charge for it.
{% endstep %}
{% endstepper %}

{% hint style="danger" %}
**Releasing a number is permanent.** The number returns to the carrier's pool, and anyone can buy it next. Calls to it stop reaching you immediately, and existing marketing that still shows the number will ring a stranger. If you only want to stop using a number on a campaign, unassign it instead — it stays in your inventory.
{% endhint %}

{% hint style="info" %}
**Numbers you already own in Twilio are imported from the dashboard.** Go to **Phone Numbers** → **Import from Twilio** to bring existing numbers into a workspace; the API buys new ones rather than adopting numbers already on your account.
{% endhint %}

<details>

<summary>What happens if a purchase fails half way through</summary>

Buying a number happens at the carrier before Ring Tonic can record it, so the two steps cannot be completed as one.

1. If the carrier sells the number but Ring Tonic cannot record it, the purchase is undone — the number is given straight back rather than left on your bill with nothing in your account to show for it.
2. If a purchase takes you past your daily cap, that number is released again and the response tells you the cap was reached.
3. If a request times out and you are unsure whether it went through, retry it with the same **Idempotency-Key** and you will get the original answer rather than a second number.

</details>

***

### 📅 Booking Appointments

Ring Tonic can book and manage appointments from your own tools — a scheduler, a CRM, or a script — using the same booking engine the AI agent and the dashboard use. A booking made this way shows up in the [Appointments](/guides/appointments.md) hub, syncs to Google Calendar when it's connected, and is attributed back to the call it came from.

This needs the **appointments** write scope on the key. Booking for a brand-new caller — someone who isn't a contact yet — additionally needs the **contacts** write scope, because it creates a contact record.

{% stepper %}
{% step %}

#### Book an appointment

Give the time, how long it runs, and who it's for — either an existing contact or a new one (a name and phone number). Add a title, notes, and a location, and turn on a text or email confirmation and reminders if you want them.
{% endstep %}

{% step %}

#### Reschedule or edit

Change the time, the length, or any detail. Send only what changes — anything you leave out stays as it was.
{% endstep %}

{% step %}

#### Cancel

Cancel the appointment with an optional reason, and choose whether to let the customer know. A cancelled appointment stays on record; it is not deleted.
{% endstep %}

{% step %}

#### Mark the outcome

Mark it **completed** or **no-show** once it has happened, or re-open it to **scheduled** if it was cancelled by mistake.
{% endstep %}
{% endstepper %}

A booking request looks like this:

```bash
curl -X POST "https://ringtonic.app/api/v1/appointments" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Idempotency-Key: 9b1f2c84-0d6a-4e21-9f77-c3a2b6e40d15" \
  -H "Content-Type: application/json" \
  -d '{
    "contact_uuid": "8d2c7a10-4f19-4b83-9c2e-1a6f0b3d5e77",
    "start_at": "2026-08-20T18:00:00Z",
    "duration_minutes": 30,
    "title": "Roof inspection"
  }'
```

{% hint style="info" %}
**The start time is a full timestamp with a timezone.** Send an exact instant such as `2026-08-20T18:00:00Z` (the `Z` means UTC) or with an offset like `2026-08-20T14:00:00-04:00`. A time without a zone is refused, so there is never any doubt about which moment you mean.
{% endhint %}

{% hint style="warning" %}
**A clashing time warns, but does not block.** If the slot overlaps something already booked, the request comes back with a conflict you can act on. To book anyway, send it again with `ignore_conflicts` turned on — and, because it creates a record, a fresh Idempotency-Key.
{% endhint %}

{% hint style="info" %}
**Confirmations and reminders are off unless you ask for them.** Turn on a text or email confirmation per booking, and add reminder times. A text only sends if the workspace has a text-capable number and the contact has a mobile; an email only sends if the contact has an email address.
{% endhint %}

<details>

<summary>Working with Google Calendar</summary>

* With Google Calendar connected, a booking creates the calendar event, a reschedule moves it, and a cancel removes it
* Without it, appointments still work — they simply stay inside Ring Tonic
* Connecting Google Calendar later does not back-fill appointments booked while it was disconnected

</details>

***

### Safe Retries

If a request times out, you rarely know whether it succeeded. Send it again and you risk creating the same campaign twice.

To avoid that, include an **Idempotency-Key** header on any request that creates something. Choose a unique value per action — a UUID works well:

```bash
curl -X POST "https://ringtonic.app/api/v1/campaigns" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Idempotency-Key: 7f3c1e94-2b6a-4d5e-9a10-c8f2b7d41e63" \
  -H "Content-Type: application/json" \
  -d '{"name": "Spring Promo", "tracking_type": "static", "forward_to": "+14155550123"}'
```

Retry with the same key and you get the original response back instead of a second campaign. Use a new key for each genuinely new action.

***

### Limiting What the API Can Spend

Workspace owners can cap how many phone numbers the API is allowed to buy per day, so an integration with a bug can't run up a bill overnight.

{% stepper %}
{% step %}

#### Open your workspace settings

Go to **Settings** → **Workspace**. The **API Access** section appears near the bottom of the **Basic** tab.
{% endstep %}

{% step %}

#### Set the daily cap

Enter a number between 0 and 100. Setting it to **0** prevents the API from buying numbers at all.

<figure><img src="/files/gA4GaP3xpWvoUrh4bhCo" alt=""><figcaption><p>The API Access section in workspace settings, showing the daily purchase cap field</p></figcaption></figure>
{% endstep %}
{% endstepper %}

{% hint style="info" %}
Only the **workspace owner** can change this — team members with admin access cannot.

Numbers that were bought and later released still count toward the day's total, because they were paid for. The count resets at midnight in your workspace's timezone.
{% endhint %}

***

### Common Questions

<details>

<summary>How many API keys can I create?</summary>

Unlimited. Create separate keys for each integration so you can revoke them independently.

</details>

<details>

<summary>Do API keys expire?</summary>

By default, no — keys stay active until you revoke them. When creating a key you can optionally set it to expire after 30, 90, or 365 days, and it will stop working automatically once that period passes.

</details>

<details>

<summary>What happens if my key is compromised?</summary>

Revoke it immediately from **Settings** → **API Keys** and create a new one.

</details>

<details>

<summary>Can I stop an integration from deleting things?</summary>

Yes. Scopes are granted per key, and read and write are separate.

* Give a reporting dashboard only the **read** scopes it needs
* Give a provisioning script the **write** scopes for the resources it manages, and nothing else
* Create a separate key per integration, so you can revoke one without breaking the others

</details>

<details>

<summary>Can the API book an appointment for someone who isn't a contact yet?</summary>

Yes. Send the new person's name and phone number instead of an existing contact, and a contact record is created for them.

Because that creates a contact, the key needs the **contacts** write scope on top of the **appointments** write scope. A key with only appointments access can still book, but only for contacts that already exist.

</details>

<details>

<summary>Does deleting through the API also delete the call recordings?</summary>

Deleting a call log removes the call and its recording and transcription, exactly as deleting it in the dashboard does. This cannot be undone, so confirm you have exported anything you need first.

</details>

<details>

<summary>Why did my request come back with an error I did not expect?</summary>

Errors include a short machine-readable `type` and a human-readable explanation, so you can tell an invalid value apart from a missing permission or a record that belongs to another workspace.

Common causes:

1. **The key is missing a scope.** Check the key's scopes in **Settings** → **API Keys**.
2. **The record belongs to another workspace.** A key only ever sees the workspace it was created in.
3. **A value failed validation.** The response names the field and what was wrong with it.

</details>

<details>

<summary>I edited one step and the rest of my flow disappeared — why?</summary>

A flow is written whole. The request replaces the draft with exactly what you send, so any step missing from it is removed.

To change one step:

1. Read the flow first
2. Change that step in what you read
3. Send the whole thing back

Building the request from scratch, listing only the step you wanted to change, deletes everything else. If this has already happened, roll the flow back to the last published version and publish again.

</details>

<details>

<summary>Can I cap what the API is allowed to spend?</summary>

Yes — workspace owners set a daily limit on how many numbers the API may buy. See [Limiting What the API Can Spend](#limiting-what-the-api-can-spend) above.

* The default is 10 numbers per day
* Setting it to **0** stops the API buying numbers entirely
* Numbers bought and then released still count toward the day, because they were paid for

</details>

<details>

<summary>Can two integrations safely write at the same time?</summary>

Yes. Requests are handled independently and your workspace is never left half-updated by a failed call.

Two behaviours worth knowing:

* Actions that create something accept an **Idempotency-Key**, so retries after a timeout do not duplicate records
* Where two requests genuinely conflict — publishing the same call flow twice at once, for example — one succeeds and the other is told to try again, rather than both partially applying

</details>
