> ## Documentation Index
> Fetch the complete documentation index at: https://docs.khaime.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Upsert Email Template

> Create or update your business's custom content for a B2B email type.

# Upsert Email Template

Sets your business's custom subject/body for a B2B email type, replacing Khaime's default for all future sends of that type. Keyed by `(business_id, template_type)` — calling this again for the same `type` updates the existing override rather than creating a duplicate.

## Path Parameters

<ParamField path="type" type="string" required>
  One of the 11 legacy B2B type names — see [Get Default Template](/api-reference/b2b/get-default-email-template) for the full list.
</ParamField>

## Request Body

<ParamField body="name" type="string" required>
  Internal label for this template, max 100 characters. Not shown to customers.
</ParamField>

<ParamField body="subject" type="string" required>
  Email subject line, max 255 characters. Supports `{{variable}}` placeholders — see [Template Variables](#template-variables) below.
</ParamField>

<ParamField body="html_body" type="string" required>
  Full HTML email body. Supports the same `{{variable}}` placeholders as `subject`, plus conditional blocks (see below).
</ParamField>

<ParamField body="is_active" type="boolean" default="true">
  Set `false` to save a draft without it taking effect — Khaime's default is still used for sends while `is_active` is `false`.
</ParamField>

## Response

<Note>Returns `200` even when this call creates a new override for the first time — not `201`.</Note>

```json theme={null}
{
  "success": true,
  "message": "Email template saved successfully",
  "data": {
    "id": 12,
    "business_id": 1042,
    "template_type": "b2b_quote_sent",
    "name": "Custom Quote Ready Email",
    "subject": "Your quote from {{business_name}} is ready",
    "html_body": "<html>...</html>",
    "is_active": true
  }
}
```

Note `template_type` in the response is Khaime's **internal** mapped name (e.g. `b2b_quote_sent`), not the `quote_sent` short name you passed in the URL.

## Template Variables

Rendering is a plain string substitution, not a full template engine: `{{key}}` is replaced with the value if present, and left as **empty string** if that key has no value for this send (there's no error or fallback text — a typo in a variable name silently renders as nothing, it won't throw).

<ParamField body="{{#key}}...{{/key}}">
  Conditional block. The enclosed content is kept only if `key` has a truthy value for this send; otherwise the whole block (including its contents) is stripped. Use this for optional sections — e.g. only show a rejection reason when one exists.
</ParamField>

Only a subset of variables is populated per event — which ones depend on the template `type`. Passing a variable that isn't relevant to that event type just renders empty.

| Variable                                                                                               | Populated for                                                                                       | Description                                                                                                                    |
| ------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `business_name`, `business_email`, `business_phone`                                                    | all types                                                                                           | Your business's display name, email, phone.                                                                                    |
| `business_logo_url`, `business_website`, `store_link`                                                  | all types                                                                                           | From your storefront settings.                                                                                                 |
| `customer_name`, `customer_email`, `customer_phone`, `company_name`                                    | all types                                                                                           | From the RFQ/quote's customer info.                                                                                            |
| `order_number`, `order_date`, `order_status`, `order_link`                                             | `rfq_received`, `rfq_confirmation`                                                                  | The underlying order/RFQ record.                                                                                               |
| `requested_items`                                                                                      | `rfq_received`, `rfq_confirmation`                                                                  | Pre-rendered HTML table of requested line items.                                                                               |
| `rfq_link`                                                                                             | `rfq_received`, `rfq_confirmation`                                                                  | Link back to the RFQ.                                                                                                          |
| `shipping_address`, `shipping_name`, `shipping_phone`, `customer_po_number`                            | where a shipping address was supplied                                                               | Formatted multi-line address (`<br>`-joined), only present if the RFQ/order included delivery details.                         |
| `quote_number`, `quote_date`, `quote_link`                                                             | `quote_sent`, `quote_viewed`, `quote_accepted`, `quote_rejected`, `quote_expiring`, `quote_expired` | The quote being referenced.                                                                                                    |
| `currency`, `subtotal`, `discount`, `discount_reason`, `shipping`, `tax`, `total`                      | quote types                                                                                         | Pre-formatted currency strings (e.g. `"$1,520.00"`), not raw numbers.                                                          |
| `valid_until`, `payment_terms`, `shipping_terms`, `supplier_notes`                                     | quote types                                                                                         | From the quote record.                                                                                                         |
| `quote_items`                                                                                          | quote types                                                                                         | Pre-rendered HTML table of quote line items.                                                                                   |
| `rejection_reason`, `rejection_reason_label`, `rejection_note`                                         | `quote_rejected`                                                                                    | Only present if the quote was actually rejected with a reason — wrap usage in `{{#rejection_reason}}...{{/rejection_reason}}`. |
| `po_number`, `po_date`                                                                                 | `po_created`                                                                                        | Only present once a PO number exists on the order.                                                                             |
| `invoice_id`, `invoice_number`, `invoice_amount`, `invoice_due_date`, `invoice_status`, `invoice_link` | `invoice_created`, `payment_received`                                                               | From the invoice record.                                                                                                       |
| `dashboard_link`                                                                                       | all types                                                                                           | Link to your Khaime dashboard.                                                                                                 |

<Note>
  [Preview Email Template](/api-reference/b2b/preview-email-template) renders against a fixed set of sample values covering most of these keys — use it to sanity-check your placeholders before saving, but note its sample data doesn't cover every conditional field (e.g. `rejection_reason` isn't populated in preview mode).
</Note>

## Error Codes

| Status | Cause                                                                                                   |
| ------ | ------------------------------------------------------------------------------------------------------- |
| `422`  | `type` isn't a valid legacy B2B type, or `name`/`subject`/`html_body` missing or over their max length. |

<RequestExample>
  ```bash cURL theme={null}
  curl -X PUT https://api.khaime.com/api/v1/partner/email-templates/quote_sent \
    -H "X-API-Key: pk_sandbox_your_key" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Custom Quote Ready Email",
      "subject": "Your quote from {{business_name}} is ready",
      "html_body": "<html><body>Hi {{customer_name}}, your quote is ready.</body></html>",
      "is_active": true
    }'
  ```
</RequestExample>
