Read and update general store settings, and manage the checkout consent checkboxes shown to customers. Sensitive configuration – API key hashes, Stripe credentials, and deploy config – is never exposed by these endpoints. Appearance, socials, brand kit, and loyalty have their own endpoints.

## Get Settings

```
GET /api/v1/settings
```

Returns general store settings. The `subdomain`, `domain`, `environment`, and email-domain fields are read-only – they cannot be changed through this endpoint.

```bash
curl -H "Authorization: Bearer your_api_key" \
  https://your-store.yns.store/api/v1/settings
```

### Response

```json
{
  "name": "Acme Goods",
  "description": "Handmade home essentials",
  "currency": "USD",
  "subdomain": "acme",
  "domain": "shop.acme.com",
  "environment": "live",
  "emailDomain": "acme.com",
  "emailDomainVerified": true,
  "published": true,
  "defaultLanguage": "en-US",
  "enabledLanguages": { "en-US": true },
  "enabledCurrencies": ["USD"],
  "notificationEmail": "team@acme.com",
  "contactRoutingEmails": { "sales": "sales@acme.com", "support": "help@acme.com" },
  "outboundEmail": "hello@acme.com",
  "aiInstructions": null,
  "omnibus": false,
  "taxBehavior": "exclusive",
  "logo": null,
  "welcomeOffer": null,
  "newsletterPopup": null,
  "newsletter": null,
  "stockHold": null,
  "invoiceSettings": null,
  "emailTracking": null,
  "blogSettings": null,
  "bookings": null,
  "cartRecommendations": null,
  "checkoutAppearance": null,
  "storeChat": null
}
```

---

## Update Settings

```
PATCH /api/v1/settings
```

Partially updates general store settings. Only the fields you send are changed; read-only fields are ignored if included. Returns the full, updated settings object.

### Grouped settings merge rule

Every grouped setting (the object fields below) follows one rule: send only the fields you are changing; `null` on a field clears that field; `null` on the entire group removes the whole group. A write never requires a read first.

### Request Body – Top-level Fields

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `name` | `string` | No | Public store name (min 1 char) |
| `description` | `string \| null` | No | Short store description |
| `published` | `boolean` | No | Whether the storefront is live |
| `currency` | `string` | No | Base currency (ISO code). Changing it re-denominates existing prices – a product at 29.99 becomes 29.99 of the new currency. Re-price the catalog afterwards. |
| `defaultLanguage` | `string` | No | Default storefront locale (e.g. `en-US`) – must be enabled in `enabledLanguages` |
| `enabledLanguages` | `object` | No | Per-locale toggle map, e.g. `{ "en-US": true }` |
| `enabledCurrencies` | `string[]` | No | ISO currency codes customers can shop in besides the base currency |
| `notificationEmail` | `string \| null` | No | Where store notifications are sent |
| `contactRoutingEmails` | `object \| null` | No | Maps form routing keys to recipients, e.g. `{ "sales": "sales@acme.com" }`. Max 20 entries. Pass `null` to remove. |
| `outboundEmail` | `string \| null` | No | From-address for customer emails (empty string allowed) |
| `aiInstructions` | `string \| null` | No | Custom instructions for AI features (max 20,000 chars) |
| `omnibus` | `boolean \| null` | No | EU Omnibus price-history compliance |
| `taxBehavior` | `string` | No | How prices are presented to shoppers: `"inclusive"` (shoppers see gross prices, tax included) or `"exclusive"` (shoppers see net prices, tax added at checkout). Prices are always stored net — see [Prices and tax](/docs/api-reference/products#prices-and-tax). |
| `logo` | `object \| null` | No | Storefront logo. Pass `null` to remove. |
| `logo.imageUrl` | `string` | Yes (within `logo`) | Logo image URL. External URLs are re-hosted on your store CDN. |
| `logo.width` | `number \| null` | No | Logo width in pixels |
| `logo.height` | `number \| null` | No | Logo height in pixels |

### Request Body – welcomeOffer

Newsletter welcome discount: automatically email a discount code to every new subscriber. Pass `null` to remove the group.

| Field | Type | Description |
|-------|------|-------------|
| `enabled` | `boolean` | Whether the welcome offer is active |
| `mode` | `string` | `"shared"` uses an existing coupon; `"unique"` (paid plans) generates a single-use code per subscriber |
| `sharedCouponId` | `string \| null` | UUID of an existing coupon. Required when `mode` is `"shared"` and `enabled` is `true`. |
| `uniquePrefix` | `string \| null` | Prefix for generated codes (A–Z, 0–9, hyphen; max 20 chars) |
| `discountType` | `string` | `"percentage"` or `"fixed"` |
| `discountValue` | `number \| null` | Discount amount. Required when `mode` is `"unique"`. |
| `expiryDays` | `number` | Days until the code expires (1–365) |
| `reminderDaysBefore` | `number \| null` | Send an expiry reminder this many days before (1–30); `null` sends none |

### Request Body – newsletterPopup

Newsletter popup copy, image, and timing. Only renders when the `newsletterPopup` module is enabled via `PATCH /api/v1/modules`. Pass `null` to remove the group.

| Field | Type | Description |
|-------|------|-------------|
| `delaySeconds` | `number` | Seconds on the page before the popup opens (0–300) |
| `heading` | `string \| null` | Popup headline (max 100 chars) |
| `subheading` | `string \| null` | Supporting line below the headline (max 200 chars) |
| `ctaText` | `string \| null` | Subscribe button label (max 50 chars) |
| `teaserText` | `string \| null` | Label on the tab that reopens the popup after dismissal (max 50 chars) |
| `imageUrl` | `string \| null` | Image shown beside the form. External URLs are re-hosted on your store CDN. |

### Request Body – newsletter

Newsletter signup mechanics.

| Field | Type | Description |
|-------|------|-------------|
| `doubleOptIn` | `boolean` | Require email confirmation before counting as subscribed. While on, the welcome offer is held back until they confirm. |

### Request Body – stockHold

Cart stock reservations at checkout, so two shoppers cannot buy the last unit.

| Field | Type | Description |
|-------|------|-------------|
| `enabled` | `boolean` | Reserve stock once the shopper reaches checkout |
| `holdMinutes` | `number` | How long the reservation lasts (5–60 minutes) |

### Request Body – invoiceSettings

Invoicing for orders. Pass `null` to remove the group.

| Field | Type | Description |
|-------|------|-------------|
| `enabled` | `boolean \| null` | Issue invoices for orders |
| `template` | `string` | Invoice template identifier (max 100 chars) |
| `cadence` | `string` | Invoice numbering cycle: `"monthly"` or `"yearly"` |
| `notes` | `string \| null` | Footer note printed on every invoice (max 2,000 chars) |

### Request Body – emailTracking

Open and click tracking on outgoing store emails.

| Field | Type | Description |
|-------|------|-------------|
| `trackOpens` | `boolean` | Embed a tracking pixel in outgoing store emails |
| `trackClicks` | `boolean` | Rewrite links in outgoing store emails to count clicks |

### Request Body – blogSettings

House style and model strength for AI blog generation. Requires the `blog` module.

| Field | Type | Description |
|-------|------|-------------|
| `systemPrompt` | `string \| null` | Rules prepended to every AI blog generation (max 20,000 chars) |
| `modelTier` | `string` | Model strength: `"regular"`, `"extra"`, or `"max"` |

### Request Body – bookings

Defaults and rules for the Bookings module. Individual slots are managed at [/api/v1/booking-slots](/docs/api-reference/booking-slots). Requires the `bookings` module.

| Field | Type | Description |
|-------|------|-------------|
| `defaultSlotDuration` | `number` | Default slot length in minutes |
| `bufferTime` | `number` | Minutes held between consecutive slots |
| `advanceBookingDays` | `number` | How far ahead shoppers may book |
| `cancellationHours` | `number` | Cancellation cut-off before a slot starts |
| `autoConfirm` | `boolean` | Confirm bookings without merchant review |
| `requireApproval` | `boolean` | Hold every booking for merchant approval |
| `sendReminders` | `boolean` | Email attendees before their slot |
| `reminderHours` | `number` | How long before the slot the reminder goes out |

### Request Body – cartRecommendations

Cart recommendation display. Requires the `cartRecommendations` module.

| Field | Type | Description |
|-------|------|-------------|
| `layout` | `string` | Where recommendations render: `"inline"` or `"sidebar"` |

### Request Body – checkoutAppearance

Branding of the YNS-hosted checkout page. This is checkout only – the rest of the storefront's look is the storefront's own code.

| Field | Type | Description |
|-------|------|-------------|
| `summaryPosition` | `string \| null` | Desktop side for the order summary: `"left"` or `"right"` |
| `summaryBackground` | `string \| null` | Summary panel fill: `"tint"`, `"plain"`, or `"custom"` |
| `summaryBackgroundColor` | `string \| null` | Used when `summaryBackground` is `"custom"` (max 32 chars) |
| `cornerRadius` | `string \| null` | Corner rounding: `"none"`, `"small"`, `"medium"`, `"large"` |
| `payButtonLabel` | `string \| null` | Pay button wording: `"pay"`, `"completeOrder"`, `"placeOrder"` |
| `showHeader` | `boolean \| null` | Show the store header on the checkout page |
| `showPolicyLinks` | `boolean \| null` | Show the Terms / Privacy / Refund / Contact footer bar |

### Request Body – storeChat

Shopper-facing AI assistant: its persona, its knowledge, and its spend caps. Requires the `storeChat` module on a plan that grants it.

| Field | Type | Description |
|-------|------|-------------|
| `assistantName` | `string \| null` | Display name of the assistant (max 60 chars) |
| `greeting` | `string \| null` | First message the assistant opens with (max 300 chars) |
| `suggestedQuestions` | `string[] \| null` | Up to 4 starter prompts (120 chars each) |
| `knowledge` | `string \| null` | Merchant-authored Markdown facts injected into the prompt. Never storefront-visible. (max 10,000 chars) |
| `monthlyAllowance` | `number` | Monthly credit budget (0–1,000,000) |
| `merchantReserve` | `number` | Credits held back for the merchant (0–1,000,000) |
| `visitorDailyTurns` | `number` | Per-visitor daily message cap (1–500) |

### Example

```bash
curl -X PATCH \
  -H "Authorization: Bearer your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "description": "Now shipping worldwide",
    "published": true,
    "stockHold": { "enabled": true, "holdMinutes": 15 },
    "checkoutAppearance": { "cornerRadius": "medium" }
  }' \
  https://your-store.yns.store/api/v1/settings
```

### Response (200)

Returns the full settings object with the updated values.

### Errors

| Status | Condition |
|--------|-----------|
| `400` | `sharedCouponId` does not reference an existing coupon in this store (shared mode) |
| `400` | `discountValue` is missing when using unique mode |
| `400` | Unique welcome codes require a paid plan |
| `400` | Logo or newsletter popup image URL is unreachable |
| `400` | `defaultLanguage` is not enabled in `enabledLanguages` |
| `422` | `taxBehavior` set to `inclusive` while the Stripe Tax module is enabled — Stripe Tax only works on tax-exclusive prices |

---

## List Checkout Consents

```
GET /api/v1/settings/checkout-consents
```

Returns the consent checkboxes shown at checkout, ordered by their position. Each consent includes both the stored TipTap `content` document and a rendered `contentHtml` string.

```bash
curl -H "Authorization: Bearer your_api_key" \
  https://your-store.yns.store/api/v1/settings/checkout-consents
```

### Response

```json
{
  "data": [
    {
      "id": "0191abc0-1234-7def-8000-000000000001",
      "required": true,
      "type": "general",
      "content": {
        "type": "doc",
        "content": [
          {
            "type": "paragraph",
            "content": [{ "type": "text", "text": "I accept the terms of service." }]
          }
        ]
      },
      "contentHtml": "<p>I accept the terms of service.</p>"
    }
  ],
  "meta": {
    "count": 1
  }
}
```

---

## Create Checkout Consent

```
POST /api/v1/settings/checkout-consents
```

Adds a consent checkbox to checkout. Pass `content` as a plain string (auto-wrapped into a single paragraph) or a TipTap JSON document (`type: "doc"`) for rich text such as links. Invalid TipTap documents are rejected with a `400`.

### Request Body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `content` | `string \| object` | Yes | Consent text: plain string or a TipTap JSON document (`type: "doc"`) |
| `required` | `boolean` | No | Whether the consent must be accepted to complete checkout (default: `false`) |
| `type` | `"general" \| "newsletter"` | No | `newsletter` consents auto-subscribe the customer when accepted (default: `general`) |

```bash
curl -X POST \
  -H "Authorization: Bearer your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"content": "Subscribe me to the newsletter", "type": "newsletter"}' \
  https://your-store.yns.store/api/v1/settings/checkout-consents
```

### Response (201)

```json
{
  "id": "0191abc0-1234-7def-8000-000000000002",
  "required": false,
  "type": "newsletter",
  "content": {
    "type": "doc",
    "content": [
      {
        "type": "paragraph",
        "content": [{ "type": "text", "text": "Subscribe me to the newsletter" }]
      }
    ]
  },
  "contentHtml": "<p>Subscribe me to the newsletter</p>"
}
```

---

## Get Checkout Consent

```
GET /api/v1/settings/checkout-consents/:id
```

Returns a single consent by ID. Returns `404` if the consent does not exist in this store.

```bash
curl -H "Authorization: Bearer your_api_key" \
  https://your-store.yns.store/api/v1/settings/checkout-consents/0191abc0-1234-7def-8000-000000000001
```

---

## Update Checkout Consent

```
PATCH /api/v1/settings/checkout-consents/:id
```

Updates an existing consent. Send only the fields you want to change. Use `position` to reorder a consent relative to its neighbors.

### Request Body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `content` | `string \| object` | No | Plain string or TipTap JSON document (`type: "doc"`) |
| `required` | `boolean` | No | Whether the consent must be accepted to complete checkout |
| `type` | `"general" \| "newsletter"` | No | Consent type |
| `position` | `string` | No | LexoRank ordering key – set between two neighbors' positions (from the list endpoint) to reorder |

```bash
curl -X PATCH \
  -H "Authorization: Bearer your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"required": true}' \
  https://your-store.yns.store/api/v1/settings/checkout-consents/0191abc0-1234-7def-8000-000000000001
```

### Response (200)

Returns the updated consent with `content` and `contentHtml`.

---

## Delete Checkout Consent

```
DELETE /api/v1/settings/checkout-consents/:id
```

Removes a consent from checkout. Returns `404` if it does not exist.

```bash
curl -X DELETE \
  -H "Authorization: Bearer your_api_key" \
  https://your-store.yns.store/api/v1/settings/checkout-consents/0191abc0-1234-7def-8000-000000000001
```

### Response (200)

```json
{
  "ok": true,
  "deleted": 1
}
```