# Merchant Branding

Customize merchant branding and appearance (portal, hosted checkout, receipts).

All endpoints require `Authorization: Bearer <JWT>`. On the three `/branding` endpoints, `:merchantId` may be `me` to address the caller's own merchant (tenant admins without a merchant get the tenant-level branding). The `/logo` endpoints need the real merchant id.

> **Note:** merchant users (who lack the tenant-level `merchants.view` key) must use their merchant id for GET requests; `GET /merchants/me/branding` returns `403 FORBIDDEN` for them because the read check matches the path segment against their merchant id.

---

## Get Branding Settings

```http
GET /api/v1/merchants/:merchantId/branding
```

```bash
curl https://uat.auxcore.net/api/v1/merchants/8f3c2a1e-5b7d-4e9a-9c1f-2d6e8a4b7c30/branding \
  -H "Authorization: Bearer YOUR_TOKEN"
```

**Response:**

```json
{
  "success": true,
  "data": {
    "name": "ACME Corp",
    "tagline": "Quality since 1990",
    "logoUrl": "https://cdn.example.com/logo.png",
    "logoDarkUrl": null,
    "faviconUrl": null,
    "logoData": null,
    "hasLogo": false,
    "primaryColor": "#007bff",
    "primaryDarkColor": null,
    "primaryLightColor": null,
    "secondaryColor": null,
    "accentColor": "#28a745",
    "sidebarColor": null,
    "sidebarTextColor": null,
    "darkMode": false,
    "buttonRadius": "medium",
    "fontFamily": null,
    "headingFont": null,
    "customCss": null,
    "dynamicDescriptor": "ACME CORP",
    "receiptHeader": "Thank you for shopping with us",
    "receiptFooter": null,
    "vtConfig": null,
    "displaySaveCard": false,
    "address": "100 Main St",
    "city": "Austin",
    "state": "TX",
    "postalCode": "78701",
    "country": "US",
    "contactEmail": "contact@acme.example",
    "timezone": "Central",
    "checkoutPolicies": {}
  }
}
```

`name` is the merchant's Business Name (`companyName`); it is edited with `PUT /api/v1/merchants/:merchantId`, not here. `checkoutPolicies` carries the resolved policy links used by hosted checkout.

---

## Update Branding

```http
PUT /api/v1/merchants/:merchantId/branding
```

JSON body; only the fields you send are changed.

```bash
curl -X PUT https://uat.auxcore.net/api/v1/merchants/8f3c2a1e-5b7d-4e9a-9c1f-2d6e8a4b7c30/branding \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "logoUrl": "https://cdn.example.com/newlogo.png",
    "primaryColor": "#ff6600",
    "darkMode": true
  }'
```

**Accepted fields:** `tagline`, `logoUrl`, `logoDarkUrl`, `faviconUrl`, `logoData`, `primaryColor`, `primaryDarkColor`, `primaryLightColor`, `secondaryColor`, `accentColor`, `sidebarColor`, `sidebarTextColor`, `darkMode`, `buttonRadius`, `fontFamily`, `headingFont`, `customCss`, `dynamicDescriptor`, `receiptHeader`, `receiptFooter`. `name` is ignored.

**Response:** `200 OK` with the saved branding fields (same names as above, without `logoData`, address and policy fields).

---

## Upload Branding with Files

```http
POST /api/v1/merchants/:merchantId/branding/upload
```

`multipart/form-data` with optional file fields `logo`, `logoDark`, `favicon` and an optional `settings` field holding a JSON string of branding values (`tagline`, colors, `buttonRadius`, `fontFamily`, `headingFont`, `customCss`).

```bash
curl -X POST https://uat.auxcore.net/api/v1/merchants/8f3c2a1e-5b7d-4e9a-9c1f-2d6e8a4b7c30/branding/upload \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -F "logo=@logo.png" \
  -F 'settings={"primaryColor":"#ff6600"}'
```

- Files: JPEG, PNG, SVG or WebP, max 5 MB each, one file per field.
- Uploaded images are stored as `data:` URLs in `logoUrl` / `logoDarkUrl` / `faviconUrl` unless the platform is configured with external storage.

---

## Upload Logo Files

```http
POST /api/v1/merchants/:merchantId/logo/upload
```

Same file fields and limits as above, without `settings`. At least one file is required (`400 VALIDATION_ERROR` otherwise). Unlike `/branding/upload`, files are written to S3 (when configured) or to the service's upload directory and saved as a URL (`/uploads/...`), not as a `data:` URL. Returns `{ logoUrl, logoDarkUrl, faviconUrl, uploadedFiles }`.

---

## Set Logo by URL

```http
POST /api/v1/merchants/:merchantId/logo
```

```json
{ "logoUrl": "https://cdn.example.com/logo.png" }
```

`logoUrl` is required and must be a valid URL (`400 VALIDATION_ERROR` otherwise).

---

## Get Logo

```http
GET /api/v1/merchants/:merchantId/logo
```

Returns `{ "logoUrl": "...", "companyName": "ACME Corp" }`.

---

## Delete Logo

```http
DELETE /api/v1/merchants/:merchantId/logo
```

Intended to clear `logoUrl`; returns `{ "message": "Logo deleted successfully" }`. Known issue: the current implementation does not persist the cleared value, so the logo may remain. To remove a logo, send `"logoUrl": null` to `PUT /api/v1/merchants/:merchantId/branding`.

---

**Need help?** Contact support@auxcore.net
