# Switchboard — v1

## Available Sections

Use `?section=<slug>` to fetch detailed docs for a single resource group.
Use `?section=overview` for authentication, filtering, and error handling reference.

### `account`
Verify your API credentials and view your account's organization and permissions.
Endpoints: `GET /v1/whoami`

### `billing`
Your organization's Switchboard invoices. Pay open invoices with a saved payment method.
Endpoints: `POST /v1/invoices/pay`

### `broadcasts`
Text messages sent to an audience (from a Phone List or Saved Search). List, create (Beta), send (Beta), schedule (Beta), preview (Beta), and export broadcasts, as well as view and export individual messages.
Endpoints: `GET /v1/broadcasts`, `POST /v1/broadcasts`, `POST /v1/broadcasts/export`, `GET /v1/broadcasts/{broadcast_id}`, `PATCH /v1/broadcasts/{broadcast_id}`, `POST /v1/broadcasts/{broadcast_id}/send`, `POST /v1/broadcasts/{broadcast_id}/send_sample`, `POST /v1/broadcasts/{broadcast_id}/schedule`, `POST /v1/broadcasts/{broadcast_id}/unschedule`, `POST /v1/broadcasts/{broadcast_id}/pause`, `POST /v1/broadcasts/{broadcast_id}/resume`, `POST /v1/broadcasts/{broadcast_id}/set_pause_threshold`, `GET /v1/broadcasts/{broadcast_id}/preview`, `GET /v1/broadcasts/{broadcast_id}/phone_messages`, `POST /v1/broadcasts/{broadcast_id}/phone_messages/export`

### `email-blasts`
Email messages sent to an audience (from an Email List or Saved Search). View email blasts, list their individual messages, and export blast data or message-level details.
Endpoints: `GET /v1/email_blasts`, `POST /v1/email_blasts/export`, `GET /v1/email_blasts/{blast_id}`, `GET /v1/email_blasts/{blast_id}/email_messages`, `POST /v1/email_blasts/{blast_id}/email_messages/export`

### `email-lists`
Named collections of emails, typically created by uploading a CSV file. Email lists can be used as audiences for email blasts. Create lists from CSV, view list details, manage labels, and export list members.
Endpoints: `POST /v1/email_lists`, `GET /v1/email_lists`, `GET /v1/email_lists/{email_list_id}`, `POST /v1/email_lists/{email_list_id}/export`, `POST /v1/email_lists/{email_list_id}/update_labels`

### `emails`
Individual email addresses in your organization. Each email has associated metadata (name, location, labels). List, create, update labels, and export emails.
Endpoints: `GET /v1/emails`, `POST /v1/emails`, `POST /v1/emails/update_labels`, `POST /v1/emails/export`, `POST /v1/emails/export_unsubscribes`

### `forms`
Basic and contribution forms. List, publish, unpublish, close, and archive forms, and view their status, URLs, labels, and configuration.
Endpoints: `GET /v1/forms`, `GET /v1/forms/{form_id}`, `POST /v1/forms/{form_id}/publish`, `POST /v1/forms/{form_id}/unpublish`, `POST /v1/forms/{form_id}/close`, `POST /v1/forms/{form_id}/archive`

### `inbox`
Inbound text messages received by your organization. Export inbox messages with optional filtering by reply status.
Endpoints: `POST /v1/inbox/export`

### `jobs`
Asynchronous operations such as CSV exports and list processing. When you trigger an export or create a list from CSV, a job is created. Poll the job endpoint to check status (PROCESSING → COMPLETED/ERROR) and retrieve result URLs.
Endpoints: `GET /v1/jobs`, `GET /v1/jobs/{job_id}`

### `labels`
Tags for organizing and segmenting phones and emails. Labels can be attached to phones or emails individually or in bulk. Create, list, view, and rename labels.
Endpoints: `GET /v1/labels`, `POST /v1/labels`, `GET /v1/labels/{label_id}`, `PATCH /v1/labels/{label_id}`

### `phone-lists`
Named collections of phones, typically created by uploading a CSV file. Phone lists can be used as broadcast audiences. Create lists from CSV, view list details, manage labels, and export list members.
Endpoints: `POST /v1/phone_lists`, `GET /v1/phone_lists`, `GET /v1/phone_lists/{phone_list_id}`, `PATCH /v1/phone_lists/{phone_list_id}`, `POST /v1/phone_lists/{phone_list_id}/export`, `POST /v1/phone_lists/{phone_list_id}/update_labels`

### `phones`
Individual phone numbers in your organization. Each phone has associated metadata (name, location, carrier, opt-in status, labels). List, create, update labels, export phones, and export opt-outs.
Endpoints: `GET /v1/phones`, `POST /v1/phones`, `GET /v1/phones/{phone_number}`, `GET /v1/phones/{phone_number}/messages`, `POST /v1/phones/{phone_number}/messages`, `POST /v1/phones/update_labels`, `POST /v1/phones/export`, `POST /v1/phones/export_opt_outs`

### `saved-searches`
Reusable dynamic queries over phones and emails. Define search criteria to select a set of phones or emails, then use the saved search as a broadcast audience or export its results.
Endpoints: `GET /v1/saved_searches`, `POST /v1/saved_searches`, `GET /v1/saved_searches/{saved_search_id}`, `POST /v1/saved_searches/{saved_search_id}/convert_to_list`, `POST /v1/saved_searches/{saved_search_id}/refresh`, `POST /v1/saved_searches/{saved_search_id}/export`

### `shortlink-domains`
Custom domains used to serve your organization's shortlinks (e.g. `swtch.to`). List and view shortlink domains.
Endpoints: `GET /v1/shortlink_domains`, `GET /v1/shortlink_domains/{shortlink_domain_id}`

### `sql-mirror-datasets`
BigQuery datasets that mirror your organization's Switchboard data for SQL-based analytics. List your organization's SQL mirror datasets.
Endpoints: `GET /v1/sql_mirror_datasets`

### `uploaded-media`
Images and media files for use in broadcasts (Beta). Upload media from a URL for attachment to broadcast messages.
Endpoints: `GET /v1/uploaded_media`, `POST /v1/uploaded_media`

### `form-responses`
Responses submitted through basic forms. Contribution form responses are not included.
Endpoints: `GET /v1/form_responses`, `GET /v1/form_responses/{response_id}`

### `payment-methods`
Saved payment methods available for invoice payment. Each method exposes its type, brand, and last four digits so it can be identified when paying an invoice programmatically. List and retrieve payment methods.
Endpoints: `GET /v1/payment_methods`, `GET /v1/payment_methods/{payment_method_id}`

### `uploads`
Direct file uploads via a presigned S3 POST (Beta). Create an upload to stage a file (e.g. a CSV) for use by async operations such as bulk label updates.
Endpoints: `POST /v1/uploads`


## Common Response Fields

All endpoints return these fields in every response:

- `errors`: array of objects


## Quick Start

Get up and running in three steps:

**1. Get your credentials** — Visit [Organization Settings](https://oneswitchboard.com/organization/settings?tab=api) or [Account Settings](https://oneswitchboard.com/account?tab=api) and generate API credentials. You'll receive an **Account ID** and **Secret Key**. Save the Secret Key securely — it cannot be retrieved again.

**2. Verify your credentials** — Call the `whoami` endpoint to confirm everything works:

```bash
curl -u YOUR_ACCOUNT_ID:YOUR_SECRET_KEY https://api.oneswitchboard.com/v1/whoami
```

A successful response returns your organization name and roles.

**3. Make your first data request** — List your phones:

```bash
curl -u YOUR_ACCOUNT_ID:YOUR_SECRET_KEY https://api.oneswitchboard.com/v1/phones
```

From here, explore the endpoints below to manage phones and emails, send broadcasts, run exports, and more.

## Key Concepts

Here are the core resources you'll work with:

- **Phones** — Individual phone numbers in your organization, with metadata like name, location, carrier, opt-in status, and labels.
- **Emails** — Individual email addresses in your organization, with metadata like name, location, and labels.
- **Broadcasts** — Text messages sent to an audience (from a Phone List or Saved Search). Create a broadcast, set a message and audience, then send or schedule it.
- **Email Blasts** — Email messages sent to an audience (from an Email List or Saved Search). The email equivalent of Broadcasts.
- **Phone Lists / Email Lists** — Named collections of phones or emails, typically created by uploading a CSV file. Used as audiences for broadcasts and email blasts.
- **Labels** — Tags you attach to phones and emails for segmentation and organization (e.g., "VIP", "Donor", "Volunteer").
- **Saved Searches** — Reusable dynamic queries that define a set of phones or emails matching specific criteria. Can be used as broadcast audiences or exported.
- **Jobs** — Asynchronous operations (exports, list processing). Many operations return a job URL you poll for results.
- **Uploaded Media** — Images and media files that can be attached to broadcasts (Beta).
- **Uploads**: Files (e.g. CSVs) staged via a presigned S3 POST, then referenced by async operations like bulk label updates (Beta).

## Feedback and Support

Switchboard's API is currently in Beta. For any feedback or questions, please email developers@oneswitchboard.com.

For a history of API changes, see the [API Changelog](https://swtch.help/api-changelog).

## Authentication

In order to access the API, you'll need to set up credentials within Switchboard.

There are two places to create API credentials:

- **Organization API Credentials** — Visit [Organization Settings](https://oneswitchboard.com/organization/settings?tab=api) to create a key scoped to a single organization.
- **Account API Credentials** — Visit [Account Settings](https://oneswitchboard.com/account?tab=api) to create a key with access to one or more organizations.

You will receive an Account ID and a Secret Key. Note that you will not be able to access the
Secret Key again, so be sure to save it somewhere secure.

This API uses HTTP Basic Authentication. When making requests, you must pass in that Account ID and
Secret Key as the Basic Auth username and password, respectively.

## Multi-Organization Keys

API keys can be granted access to multiple organizations. This is useful if you manage several organizations and want to use a single credential across them.

**Reading data** — List endpoints automatically return results from all organizations your key can access. Use the `organization_id` filter to narrow results to a specific organization (see [Filtering](#section/Filtering)).

**Creating resources** — When making a create/POST request with a multi-organization key, you must include the `organization_id` field in the request body to specify which organization the resource belongs to. Single-organization keys do not require this field.

**Checking your access** — Call `GET /v1/whoami` to see which organizations your key can access and what roles it has in each.

## Rate Limiting

All requests are subject to a global limit of 300 requests per minute across all endpoints. Some
endpoints have additional, lower limits; where those apply, they're documented in the endpoint's own
reference section.

When you exceed a limit, you'll get a `429 Too Many Requests` response (with the `rate_limited` error
code) and a `Retry-After` header telling you how long to wait before retrying. There are no
`X-RateLimit-*` headers, so pace your requests against the documented limits.

If these limits are a problem for your use case, please email developers@oneswitchboard.com.

## Concurrency Limits

Some endpoints start asynchronous work, such as exports and list creation. Beyond the per-minute
rate limits, these endpoints also cap how many requests of a given type you can have in progress at
once, scoped to your API key, so a backlog can't crowd out other callers. The cap clears as your
in-progress requests finish rather than on a fixed schedule, so poll your outstanding `job_url`s
rather than retrying on a timer.

### Export Jobs

Export creation endpoints share a rate limit of 60 requests per minute per API key and 20 per organization. They also run asynchronously: at most 20 exports may be in progress at once per API key and 20 per organization, after which further requests get a 429 until some complete.

### List Jobs

List creation endpoints share a rate limit of 20 requests per minute per API key and 8 per organization. They also run asynchronously: at most 20 list creations may be in progress at once per API key and 20 per organization (phone and email lists are counted separately), after which further requests get a 429 until some complete.

## Response Structure

Responses all follow this format:

```json
{ data: {}, errors: [] }
```

where `data` is the contents of a successful message and `errors` is the
content of a failed message. `data` will be `null` on error, and `errors` will
be an empty array on success.

There may be more than one error, each containing a `code` indicating a machine-readable
error code as well as a short `title` and longer `description` of the error in human terms.
Where applicable, `field_name` will be included to indicate which input field caused the error.

## Paginated Endpoints

APIs like `/v1/broadcasts` are paginated. You'll receive 20
entries and, if there are more entries after them, you'll get a URL in the
`next_page` field.

To get the next group of entries, run an HTTP GET request against that URL.
You'll keep getting `next_page` fields until there are no more entries, in which
case `next_page` will be `null`.

Page sizes are 20 entries per page. Page sizes are not adjustable. If you cannot
retrieve all your entries because of page and rate limiting restrictions, we recommend
using the [export endpoints](https://api.zeroswitchboard.com/v1/docs#section/Export-Endpoints) instead, though note that exports have their own
per-minute rate limit and a shared concurrency limit (see [Rate Limiting](#section/Rate-Limiting)).

## Export Endpoints

When you run an export, you start a Job that runs asynchronously on Switchboard's servers.
The response will return immediately and the data will contain a `job_url`.

Example:
```json
{
    "data": {
        "job_url": "https://..."
    },
    "errors": []
}
```

You can then poll this URL using an HTTP GET request periodically to check on the status of
the export. When the export is ready, the status will be `COMPLETED`, and you will get a
`result_url` that you can download.

```json
{
  "data": {
    "status": "COMPLETED",
    "result_url": "https://..."
  },
  "errors": []
}
```

The result URL is a signed S3 URL, and only available for five minutes from time of request.
New URLs can be generated by re-requesting the status of the Job.

### Polling Recommendations

We recommend polling the job URL with exponential backoff:

- **First poll**: after 5 seconds
- **Subsequent polls**: double the interval each time (10s, 20s, 40s) up to a maximum of 60 seconds
- **Typical completion time**: most exports finish within 1–2 minutes; large exports may take up to 10 minutes

Job statuses progress as follows: `PROCESSING` → `COMPLETED` or `ERROR`.

## DateTimes
All date times will be strings in ISO 8601 format yyyy-mm-ddThh:mm:ss±hhmm

## Filtering

Some list endpoints support filtering using via a `filter=` query param and a query syntax that allows you to narrow down results based on field values. The filter parameter accepts expressions that combine field comparisons with logical operators.

The specific fields available for filtering vary by endpoint and are documented in each endpoint's description.

**Important**: Filter expressions must be URL-encoded when included in query parameters, especially when they contain spaces, special characters, quotes, or operators.

### Basic Syntax

Filter expressions use the format `field operator value`, where:

- **field**: The name of the field to filter on
- **operator**: A comparison operator (see below)
- **value**: The value to compare against, enclosed in quotes for strings

### Operators

- `=` - equals
- `!=` - not equals
- `>` - greater than
- `<` - less than
- `>=` - greater than or equal
- `<=` - less than or equal
- `:` - contains (for text fields)

### Logical Operators

- `AND` - both conditions must be true
- `OR` - either condition can be true
- `NOT` - negates the following condition

### Examples

The following examples show filter expressions before URL encoding for clarity. Note: available filter fields vary by endpoint — check each endpoint's documentation for its supported fields.

```
// Filter by exact title match
?filter=title="My Broadcast"

// Filter by date range (after 2023-01-01)
?filter=created_at>"2023-01-01"

// Filter where time is not present
?filter=started_at=null

// Combine text search with date filter
?filter=name:"keyword" AND created_at<"2023-12-31"

// Exclude drafts
?filter=NOT status="draft"

// Complex query with grouping and multiple conditions
?filter=(title:"urgent" OR title:"important") AND created_at>"2023-01-01"

// Filter to a specific organization (for multi-organization keys)
?filter=organization_id="org_01jt0r4xhcefqs0rfkh906p7z0"

// Filter to one of several organizations (for multi-organization keys)
?filter=organization_id="org_01jt0r4xhcefqs0rfkh906p7z0" OR organization_id="org_01jv8k2m3npqr5st6uwx7yz9ab"
```

### URL Encoding

When making actual requests, these filter expressions must be URL-encoded. For example:

```
// Before encoding
?filter=title="My Broadcast"

// After URL encoding
?filter=title%3D%22My%20Broadcast%22

// Before encoding
?filter=(title:"urgent" OR title:"important") AND created_at>"2023-01-01"

// After URL encoding
?filter=%28title%3A%22urgent%22%20OR%20title%3A%22important%22%29%20AND%20created_at%3E%222023-01-01%22

// Before encoding
?filter=organization_id="org_01jt0r4xhcefqs0rfkh906p7z0" OR organization_id="org_01jv8k2m3npqr5st6uwx7yz9ab"

// After URL encoding
?filter=organization_id%3D%22org_01jt0r4xhcefqs0rfkh906p7z0%22%20OR%20organization_id%3D%22org_01jv8k2m3npqr5st6uwx7yz9ab%22
```

Most HTTP clients and libraries handle URL encoding automatically, but be sure to encode manually if constructing URLs by hand.

### Grouping

Use parentheses to group conditions and control operator precedence.

### Global Search

Some endpoints also support global search terms (without specifying a field) that will search across multiple relevant fields. For example: `"urgent message"` instead of `title:"urgent message"`.

### Filtering by Organization

Most list endpoints support filtering by `organization_id` to scope results to a specific organization. This is particularly useful with multi-organization keys, where results span all accessible organizations by default.

## Beta Headers

Some endpoints are in beta and require opt-in via the `switchboard-beta` header. Beta features
are experimental and may have breaking changes without notice.

To access beta endpoints, include the header in your requests:

```http
switchboard-beta: broadcasts-2025-01-07
```

To use multiple beta features in a single request, separate them with commas:

```http
switchboard-beta: feature1,feature2
```

Endpoints that require beta opt-in will return a `403 Forbidden` error with code `beta_feature_required`
if the appropriate header is not provided. If an invalid beta feature name is provided, you'll receive
a `400 Bad Request` error with code `beta_invalid_feature`.

Currently available beta features:
- `broadcasts-2025-01-07` - Create, send, and preview broadcasts via API

## Error Handling

All error responses follow this structure:

```json
{
  "data": null,
  "errors": [
    {
      "code": "bad_request",
      "title": "Invalid Request",
      "description": "The request was malformed or contained invalid parameters.",
      "field_name": "phone_number"
    }
  ]
}
```

There may be more than one error in the `errors` array. Each error contains:

- **`code`** — Machine-readable error code (see table below)
- **`title`** — Short human-readable summary
- **`description`** — Detailed explanation of what went wrong
- **`field_name`** — (optional) The specific input field that caused the error

### Error Codes

| Code | HTTP Status | Meaning |
|------|-------------|---------|
| `bad_request` | 400 | Invalid arguments or malformed request body |
| `unauthenticated` | 401 | Missing or invalid API credentials |
| `unauthorized` | 403 | Valid credentials but insufficient permissions for this action |
| `route_not_found` | 404 | The URL path does not match any API endpoint |
| `object_not_found` | 404 | The requested resource does not exist or you lack access |
| `beta_feature_required` | 403 | Endpoint requires a `switchboard-beta` header |
| `beta_invalid_feature` | 400 | The beta feature name in the header is not recognized |
| `internal_server_error` | 500 | Unexpected server error — contact support if persistent |

### Common Scenarios

- **401 Unauthenticated** — Double-check your Account ID and Secret Key. Ensure you're using HTTP Basic Auth, not Bearer tokens.
- **404 Object Not Found** — The ID may be wrong, or the resource belongs to a different organization. Verify the ID prefix matches the resource type (e.g., `bc_` for broadcasts, `lb_` for labels).
- **400 with `field_name`** — A specific input field failed validation. Check the `field_name` and `description` for details.
- **429 Rate Limited** — Wait the seconds given in the `Retry-After` header, then retry at a lower rate. See [Rate Limiting](#section/Rate-Limiting) for per-endpoint limits.


