## Create or update business

`POST https://whatsapp.paaltech.org/api/v1/businesses`

Creates the business, or updates it when the `external_id` already exists. Call it when
a customer signs up and whenever their name or contact details change. Safe to repeat.

Ability: `businesses.write`

### Body

| Name | Type | Required | Description |
|---|---|---|---|
| `external_id` | string | yes | Max 191 characters. Pattern `^[A-Za-z0-9._:-]+$` |
| `name` | string | yes | Max 255 characters |
| `email` | string | null | no | Max 255 characters. Format email |
| `phone` | string | null | no | Max 32 characters |
| `timezone` | string | no | IANA time zone (default Africa/Accra), used for quiet hours. Example `Africa/Accra` |
| `quiet_hours` | object | null | no | Non-urgent business-initiated messages wait until the end (local time of the contact, else the business). null switches them off. |
| `quiet_hours.start` | string | no | Pattern `^[0-2][0-9]:[0-5][0-9]$`. Example `22:00` |
| `quiet_hours.end` | string | no | Pattern `^[0-2][0-9]:[0-5][0-9]$`. Example `06:00` |
| `retention` | object | null | no | How long PaalChat keeps message content, notes and media files for this business (7-3650 days; null = the default, 365). |
| `retention.message_content_days` | integer | no |  |

### Request

```bash
curl --request POST \
  --url 'https://whatsapp.paaltech.org/api/v1/businesses' \
  --header "Authorization: Bearer $PAALCHAT_TOKEN" \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data '{
  "external_id": "st-augustines",
  "name": "St. Augustine'\''s College",
  "email": "info@augustines.edu.gh"
}'
```

### Responses

- **200** - Updated - the external_id already existed.
- **201** - Created.

```json
{
  "data": {
    "external_id": "st-augustines",
    "name": "St. Augustine's College",
    "email": "info@augustines.edu.gh",
    "phone": null,
    "status": "active",
    "whatsapp": {"status": "not_connected", "wabas": 0},
    "created_at": "2026-09-30T18:54:27+00:00"
  }
}
```

- **401** - unauthenticated - missing, invalid, revoked or expired token

```json
{
  "error": {"code": "unauthenticated", "message": "A valid product token is required."}
}
```

- **403** - missing_ability, product_suspended or product_token_required

```json
{
  "error": {
    "code": "missing_ability",
    "message": "This token does not have the ability this request needs."
  }
}
```

- **422** - validation_failed - fields are invalid; see errors.

```json
{
  "error": {
    "code": "validation_failed",
    "message": "The external id field format is invalid. (and 1 more error)"
  },
  "errors": {
    "external_id": ["The external id field format is invalid."],
    "name": ["The name field is required."]
  }
}
```

- **429** - rate_limited - over 300 requests/minute for this product

```json
{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Slow down and retry after the Retry-After header."
  }
}
```

