---
title: "Site settings"
description: "The Site's name, primary domain, locale, niche and timezone, and its knowledge profile: the brand voice, product description and audience the AI writes for."
canonical: "https://writavo.com/docs/api/site-settings"
last-updated: "2026-09-24"
---

# Site settings

The Site's name, primary domain, locale, niche and timezone, and its knowledge profile: the
brand voice, product description and audience the AI writes for.

Base URL: `https://api.writavo.com/v1`

### GET /site/settings

Read the Site settings

- **Operation id**: `getSiteSettings`
- **Scope**: `site:read`
- **Permission**: `none`
- **Rate limit class**: read
- **Plan feature**: `none`
- **Spends credits**: no
- **Publishable key may call it**: no

The Site's editable settings: name, primary domain, locale, niche and timezone, plus its status
and the live base URL of its blog. Read this before changing anything, and before a paid SEO scan:
most scans need `primary_domain`.

`status` is read only. A `suspended` Site was suspended by Writavo; a `paused` one by the
organisation. Neither can be changed through the API.

**Responses**

| Status | Data | Description |
|---|---|---|
| 200 | SiteSettings | The Site settings. |
| 401 | - | `INVALID_API_KEY`, `API_KEY_REVOKED` or `API_KEY_EXPIRED`. There is no key, or it is not usable. This response never distinguishes "no such key" from "wrong key", so a caller cannot probe for valid keys. |
| 403 | - | `INSUFFICIENT_SCOPE`. The key is valid but does not carry the scope this operation requires, or its creator's permissions no longer cover it. It is never returned for an object belonging to another Site: that case is always 404, so this response never reveals that something exists. For a key an AI agent holds, a 403 may also be `AGENT_ACCESS_DISABLED` (the organisation turned agent access off) or `APPROVAL_DENIED` (a person denied this action). Neither is about scope, and neither reveals anything about another Site. On the organisation, team and settings operations a 403 may also be `FORBIDDEN`: a change no scope allows, such as an AI agent changing the access of the person it acts for, anything to do with ownership, or a setting only a person may change. The message says what to do instead. |
| 429 | - | `RATE_LIMIT_EXCEEDED`. Back off and honour `Retry-After`. Limits are per key, per minute, by endpoint class. Writes are limited harder than reads because they cost more to serve, and pipeline runs hardest of all because they cost real money, as do the paid SEO scans and the other operations that do expensive work. The two device sign-in endpoints take no key, so they are limited per IP address instead. \| Class \| Endpoints \| Limit \| \|---\|---\|---\| \| read \| every GET \| 600 per minute \| \| write \| POST, PATCH and DELETE on content, taxonomy, keys, webhooks, the content plan, formats and prompts, settings, delivery, SEO curation, the team and billing \| 120 per minute \| \| upload \| `POST /media/upload-url` and `POST /seo/backlinks/import` \| 60 per minute \| \| pipeline \| `POST /pipeline/runs`, `POST /pipeline/articles/{id}/requeue`, `POST /delivery/cms/push` and the seven paid SEO scans \| 10 per minute \| \| auth \| `POST /auth/device` and `POST /auth/device/token`, per IP address \| 60 per minute \| Every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`, so you can slow down before you are refused rather than after. |
| 500 | - | `INTERNAL_ERROR`. Something failed on our side. The message is deliberately generic; the detail is in our logs against the `request_id` in the body, so quote it if you contact support. Safe to retry, and safer still with the same `Idempotency-Key`, which guarantees you do not create a second object if the first request actually succeeded. |
| 503 | - | `MAINTENANCE`. Writes are paused, either platform wide or for your Site. Reads usually keep working, and your published blog is served from cache and stays up. Retry after the window given in `Retry-After`. |

```bash
curl https://api.writavo.com/v1/site/settings \
  -H "Authorization: Bearer wv_sk_EXAMPLE0000000000000000000000000000"
```

### PATCH /site/settings

Change the Site settings

- **Operation id**: `updateSiteSettings`
- **Scope**: `site:write`
- **Permission**: `website.settings`
- **Rate limit class**: write
- **Plan feature**: `none`
- **Spends credits**: no
- **Publishable key may call it**: no

A partial update of the Site's name, primary domain, locale, niche or timezone. Send at least one
field; omitted fields are left alone, and `null` clears `primary_domain` or `niche`.

`primary_domain` is a bare hostname such as `example.com` (a scheme, path or port is stripped,
and anything that is not a valid public hostname is `422` on `primary_domain`). It is the domain
the SEO suite measures and the default for new tracked keywords; it does not change where the
blog is served, which is `POST /delivery/proxy` or `POST /delivery/custom-domain`.

`status`, the organisation and the creator are not settable: naming any of them is `422`.
Changing the locale or niche affects articles written from now on, not ones already written.

**Request body** (required)

| Field | Type | Required | Description |
|---|---|---|---|
| `name` | string | no |  |
| `primary_domain` | string | null | no | A bare hostname such as `example.com`, or null to clear it. |
| `locale_language` | string | no |  |
| `locale_country` | string | no |  |
| `niche` | string | null | no |  |
| `timezone` | string | no | `UTC`, or an IANA Area/Location name such as `Europe/London` or `America/Argentina/Buenos_Aires`. Single-segment names such as `GMT` or `EST` are refused; a name that is not a real time zone is `422`. |

**Responses**

| Status | Data | Description |
|---|---|---|
| 200 | SiteSettings | The updated settings. |
| 400 | - | `INVALID_REQUEST`. The request could not be parsed, or a parameter is not usable: bad JSON, an unknown query parameter value, or a missing required header. |
| 401 | - | `INVALID_API_KEY`, `API_KEY_REVOKED` or `API_KEY_EXPIRED`. There is no key, or it is not usable. This response never distinguishes "no such key" from "wrong key", so a caller cannot probe for valid keys. |
| 403 | - | `INSUFFICIENT_SCOPE`. The key is valid but does not carry the scope this operation requires, or its creator's permissions no longer cover it. It is never returned for an object belonging to another Site: that case is always 404, so this response never reveals that something exists. For a key an AI agent holds, a 403 may also be `AGENT_ACCESS_DISABLED` (the organisation turned agent access off) or `APPROVAL_DENIED` (a person denied this action). Neither is about scope, and neither reveals anything about another Site. On the organisation, team and settings operations a 403 may also be `FORBIDDEN`: a change no scope allows, such as an AI agent changing the access of the person it acts for, anything to do with ownership, or a setting only a person may change. The message says what to do instead. |
| 422 | - | `VALIDATION_FAILED`. The request parsed but the values are not acceptable. `error.fields` maps each offending field to a message you can put next to the input. Common causes: publishing without a title, slug or content; scheduling in the past; and sending `status` on a create or update, which is how the API refuses to let a client push content into the AI pipeline. |
| 429 | - | `RATE_LIMIT_EXCEEDED`. Back off and honour `Retry-After`. Limits are per key, per minute, by endpoint class. Writes are limited harder than reads because they cost more to serve, and pipeline runs hardest of all because they cost real money, as do the paid SEO scans and the other operations that do expensive work. The two device sign-in endpoints take no key, so they are limited per IP address instead. \| Class \| Endpoints \| Limit \| \|---\|---\|---\| \| read \| every GET \| 600 per minute \| \| write \| POST, PATCH and DELETE on content, taxonomy, keys, webhooks, the content plan, formats and prompts, settings, delivery, SEO curation, the team and billing \| 120 per minute \| \| upload \| `POST /media/upload-url` and `POST /seo/backlinks/import` \| 60 per minute \| \| pipeline \| `POST /pipeline/runs`, `POST /pipeline/articles/{id}/requeue`, `POST /delivery/cms/push` and the seven paid SEO scans \| 10 per minute \| \| auth \| `POST /auth/device` and `POST /auth/device/token`, per IP address \| 60 per minute \| Every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`, so you can slow down before you are refused rather than after. |
| 500 | - | `INTERNAL_ERROR`. Something failed on our side. The message is deliberately generic; the detail is in our logs against the `request_id` in the body, so quote it if you contact support. Safe to retry, and safer still with the same `Idempotency-Key`, which guarantees you do not create a second object if the first request actually succeeded. |
| 503 | - | `MAINTENANCE`. Writes are paused, either platform wide or for your Site. Reads usually keep working, and your published blog is served from cache and stays up. Retry after the window given in `Retry-After`. |

```bash
curl -X PATCH https://api.writavo.com/v1/site/settings \
  -H "Authorization: Bearer wv_sk_EXAMPLE0000000000000000000000000000" \
  -H "Content-Type: application/json" \
  -d '{}'
```

### GET /site/knowledge-profile

Read the knowledge profile

- **Operation id**: `getKnowledgeProfile`
- **Scope**: `site:read`
- **Permission**: `none`
- **Rate limit class**: read
- **Plan feature**: `none`
- **Spends credits**: no
- **Publishable key may call it**: no

What the AI knows about the business it writes for: the brand voice, the product description and
the target audience. Every generated article is written from this, so it is the single most
effective thing to get right before turning the pipeline on.

`competitors_seed` is the onboarding suggestion list and is read only; the competitors the SEO
suite tracks are `GET /seo/competitors`.

**Responses**

| Status | Data | Description |
|---|---|---|
| 200 | KnowledgeProfile | The knowledge profile. |
| 401 | - | `INVALID_API_KEY`, `API_KEY_REVOKED` or `API_KEY_EXPIRED`. There is no key, or it is not usable. This response never distinguishes "no such key" from "wrong key", so a caller cannot probe for valid keys. |
| 403 | - | `INSUFFICIENT_SCOPE`. The key is valid but does not carry the scope this operation requires, or its creator's permissions no longer cover it. It is never returned for an object belonging to another Site: that case is always 404, so this response never reveals that something exists. For a key an AI agent holds, a 403 may also be `AGENT_ACCESS_DISABLED` (the organisation turned agent access off) or `APPROVAL_DENIED` (a person denied this action). Neither is about scope, and neither reveals anything about another Site. On the organisation, team and settings operations a 403 may also be `FORBIDDEN`: a change no scope allows, such as an AI agent changing the access of the person it acts for, anything to do with ownership, or a setting only a person may change. The message says what to do instead. |
| 429 | - | `RATE_LIMIT_EXCEEDED`. Back off and honour `Retry-After`. Limits are per key, per minute, by endpoint class. Writes are limited harder than reads because they cost more to serve, and pipeline runs hardest of all because they cost real money, as do the paid SEO scans and the other operations that do expensive work. The two device sign-in endpoints take no key, so they are limited per IP address instead. \| Class \| Endpoints \| Limit \| \|---\|---\|---\| \| read \| every GET \| 600 per minute \| \| write \| POST, PATCH and DELETE on content, taxonomy, keys, webhooks, the content plan, formats and prompts, settings, delivery, SEO curation, the team and billing \| 120 per minute \| \| upload \| `POST /media/upload-url` and `POST /seo/backlinks/import` \| 60 per minute \| \| pipeline \| `POST /pipeline/runs`, `POST /pipeline/articles/{id}/requeue`, `POST /delivery/cms/push` and the seven paid SEO scans \| 10 per minute \| \| auth \| `POST /auth/device` and `POST /auth/device/token`, per IP address \| 60 per minute \| Every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`, so you can slow down before you are refused rather than after. |
| 500 | - | `INTERNAL_ERROR`. Something failed on our side. The message is deliberately generic; the detail is in our logs against the `request_id` in the body, so quote it if you contact support. Safe to retry, and safer still with the same `Idempotency-Key`, which guarantees you do not create a second object if the first request actually succeeded. |
| 503 | - | `MAINTENANCE`. Writes are paused, either platform wide or for your Site. Reads usually keep working, and your published blog is served from cache and stays up. Retry after the window given in `Retry-After`. |

```bash
curl https://api.writavo.com/v1/site/knowledge-profile \
  -H "Authorization: Bearer wv_sk_EXAMPLE0000000000000000000000000000"
```

### PATCH /site/knowledge-profile

Change the knowledge profile

- **Operation id**: `updateKnowledgeProfile`
- **Scope**: `site:write`
- **Permission**: `website.settings`
- **Rate limit class**: write
- **Plan feature**: `none`
- **Spends credits**: no
- **Publishable key may call it**: no

Change the brand voice, product description or target audience. Send at least one; `null`
clears a field. Each is plain text up to 4000 characters. Once edited the profile is marked as
written by a person (`is_auto_generated: false`) and the onboarding generator never overwrites
it.

Send `If-Match` with the `ETag` from your last read, so an edit a person made in the dashboard
in the meantime is not silently overwritten (`412 PRECONDITION_FAILED` if it was).

**Parameters**

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `If-Match` | header | string | no | The `ETag` from your last read of this object. If it has changed since then you get `412 PRECONDITION_FAILED` and your write is not applied, so two people editing the same article cannot silently overwrite each other. Optional in v1 for compatibility. Omitting it means last write wins. Send it. |

**Request body** (required)

| Field | Type | Required | Description |
|---|---|---|---|
| `brand_voice` | string | null | no |  |
| `product_description` | string | null | no |  |
| `target_audience` | string | null | no |  |

**Responses**

| Status | Data | Description |
|---|---|---|
| 200 | KnowledgeProfile | The updated profile. |
| 400 | - | `INVALID_REQUEST`. The request could not be parsed, or a parameter is not usable: bad JSON, an unknown query parameter value, or a missing required header. |
| 401 | - | `INVALID_API_KEY`, `API_KEY_REVOKED` or `API_KEY_EXPIRED`. There is no key, or it is not usable. This response never distinguishes "no such key" from "wrong key", so a caller cannot probe for valid keys. |
| 403 | - | `INSUFFICIENT_SCOPE`. The key is valid but does not carry the scope this operation requires, or its creator's permissions no longer cover it. It is never returned for an object belonging to another Site: that case is always 404, so this response never reveals that something exists. For a key an AI agent holds, a 403 may also be `AGENT_ACCESS_DISABLED` (the organisation turned agent access off) or `APPROVAL_DENIED` (a person denied this action). Neither is about scope, and neither reveals anything about another Site. On the organisation, team and settings operations a 403 may also be `FORBIDDEN`: a change no scope allows, such as an AI agent changing the access of the person it acts for, anything to do with ownership, or a setting only a person may change. The message says what to do instead. |
| 412 | - | `PRECONDITION_FAILED`. Your `If-Match` did not match the current version, meaning somebody edited the object since you read it. Nothing was written. Re-read, merge, and retry with the new `ETag`. |
| 422 | - | `VALIDATION_FAILED`. The request parsed but the values are not acceptable. `error.fields` maps each offending field to a message you can put next to the input. Common causes: publishing without a title, slug or content; scheduling in the past; and sending `status` on a create or update, which is how the API refuses to let a client push content into the AI pipeline. |
| 429 | - | `RATE_LIMIT_EXCEEDED`. Back off and honour `Retry-After`. Limits are per key, per minute, by endpoint class. Writes are limited harder than reads because they cost more to serve, and pipeline runs hardest of all because they cost real money, as do the paid SEO scans and the other operations that do expensive work. The two device sign-in endpoints take no key, so they are limited per IP address instead. \| Class \| Endpoints \| Limit \| \|---\|---\|---\| \| read \| every GET \| 600 per minute \| \| write \| POST, PATCH and DELETE on content, taxonomy, keys, webhooks, the content plan, formats and prompts, settings, delivery, SEO curation, the team and billing \| 120 per minute \| \| upload \| `POST /media/upload-url` and `POST /seo/backlinks/import` \| 60 per minute \| \| pipeline \| `POST /pipeline/runs`, `POST /pipeline/articles/{id}/requeue`, `POST /delivery/cms/push` and the seven paid SEO scans \| 10 per minute \| \| auth \| `POST /auth/device` and `POST /auth/device/token`, per IP address \| 60 per minute \| Every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`, so you can slow down before you are refused rather than after. |
| 500 | - | `INTERNAL_ERROR`. Something failed on our side. The message is deliberately generic; the detail is in our logs against the `request_id` in the body, so quote it if you contact support. Safe to retry, and safer still with the same `Idempotency-Key`, which guarantees you do not create a second object if the first request actually succeeded. |
| 503 | - | `MAINTENANCE`. Writes are paused, either platform wide or for your Site. Reads usually keep working, and your published blog is served from cache and stays up. Retry after the window given in `Retry-After`. |

```bash
curl -X PATCH https://api.writavo.com/v1/site/knowledge-profile \
  -H "Authorization: Bearer wv_sk_EXAMPLE0000000000000000000000000000" \
  -H 'If-Match: W/"1767225600000"' \
  -H "Content-Type: application/json" \
  -d '{}'
```
