# Soosh API: Introduction

Base URL: `https://app.soosh.io/v1`

The Soosh API lets your own systems do what your team does in the app: send WhatsApp messages and templates, keep contacts in sync, run broadcasts, and hear about each change with webhooks.

## Authentication

Send a workspace key as `Authorization: Bearer soosh_live_…` on every request. [Create an API key](/guides/integrations/create-an-api-key/) in Settings.

A key can only do what its scopes allow. Anything else gets `403 scope_missing`. Cookies are ignored. Browsers get no CORS access to `/v1`, so call the API from your server, never from a web page.

## Requests and responses

| Topic | Rule |
|---|---|
| Format | JSON, except `POST /v1/media`, which takes `multipart/form-data`. |
| IDs | UUIDs. |
| Times | RFC 3339 in UTC, with milliseconds in responses. |
| Phone numbers | Send the number with its country code. Spaces, dashes and brackets are fine. You get it back as `+` and digits. |
| Unknown request fields | Refused with `400 invalid_request`. |
| New fields and values | Ignore a field you don't know. Where a field says more values may be added, handle one you don't know. |
| Versioning | v1 only gains things. A breaking change becomes `/v2`, with at least 6 months of overlap. |

## Pagination

A list returns `{"data": [...], "next_cursor": "…" | null}`. Pass `next_cursor` back as `cursor` to get the next page. A `null` cursor means you've reached the end. There's no total count.

| Parameter | Meaning |
|---|---|
| `limit` | 1 to 100 items. The default is 25. |
| `cursor` | The `next_cursor` of the previous page, unchanged. |
| `sort` | `created_at` (the default) lists newest first. `updated_at` lists oldest change first. |

A cursor that was changed, or made for a different `sort`, is `400 invalid_request`. Only the lists of contacts, messages and templates take `sort`.

To copy changes into your own system, list with `sort=updated_at`. A change shows up about 30 seconds after it is saved. Resume from the last `next_cursor`, or pass `updated_after` set to a minute before your last sync, and skip rows you already have.

## Retrying safely

Send an `Idempotency-Key` header on any `POST`, so a retry never repeats it. The key is 1 to 255 printable ASCII characters.

Soosh keeps the key for 24 hours. A retry must be the same request: the same method, path and body, from the same API key or one rotated from it.

A repeat of a finished request returns the stored response with `Idempotent-Replayed: true`. `Original-Request-ID` names the request that made it. The body is the first response, byte for byte, so an error body's `request_id` is the first request's.

This includes 4xx responses: a `window_closed` stays `window_closed` for 24 hours under that key. A `5xx` isn't stored, so a retry runs again.

The same key on a different request, or from another of your API keys, is `422 idempotency_mismatch`. A request still running under the key is `409 idempotency_in_progress`.

## Rate limits

Every authenticated response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset` for your workspace. `X-RateLimit-Reset` is the seconds until the limit resets. A `429` also carries `Retry-After`.

| Limit | Window |
|---|---|
| All requests | A minute |
| Messages sent | A second |
| Heavy requests | 10 a minute |

Your plan sets the first two limits (`api_requests_per_minute` and `api_messages_per_second`); heavy requests are `POST /v1/broadcasts`, `POST /v1/templates`, `POST /v1/media` and `GET /v1/usage`, counted together.

## Errors

Every error has the same body, with a `code` to act on. Codes never change meaning, though new ones may be added: handle one you don't know by its HTTP status.

Every response, including errors, has an `X-Request-ID` header: `req_` followed by 20 letters and digits. Quote it when you contact support. [Errors](/reference/api/errors/) lists every status and code.


## Areas

- [Messages](https://docs.soosh.io/reference/api/messages/): A message is one WhatsApp message in a chat, sent or received. Send messages, and read every one your workspace sent or received, whoever sent it: your team, the chatbot, a broadcast or your own code.
- [Contacts](https://docs.soosh.io/reference/api/contacts/): A contact is a person you talk to on WhatsApp. Create and update contacts, tag them, and keep your own fields on them.
- [Templates](https://docs.soosh.io/reference/api/templates/): A template is a message format WhatsApp reviews and approves, so you can send it outside the 24-hour window. Create one, then follow WhatsApp's review of it.
- [Media](https://docs.soosh.io/reference/api/media/): Media is a file in a message. Upload files to send, and download the file of any message that has one.
- [Broadcasts](https://docs.soosh.io/reference/api/broadcasts/): A broadcast is one template sent to many people, now or at a time you choose. The app calls a broadcast a campaign. Create one, then follow it to the end.
- [Chats](https://docs.soosh.io/reference/api/chats/): A chat is the messages you and a contact have exchanged, so these endpoints take the contact's ID. Assign a chat to an agent or a team, and close or reopen it.
- [Webhooks](https://docs.soosh.io/reference/api/webhooks/): A webhook endpoint is a URL that Soosh sends your workspace's events to. Create endpoints, change them and rotate their signing secrets.
- [Numbers](https://docs.soosh.io/reference/api/numbers/): A number is a WhatsApp number connected to your workspace. List them to find the `number_id` to send from.
- [Usage](https://docs.soosh.io/reference/api/usage/): Usage is your workspace's limits and what it has used of them. Read it to see how close you are to a limit.
- [Webhook events](https://docs.soosh.io/reference/api/webhook-events/)
- [Errors](https://docs.soosh.io/reference/api/errors/)