Quote Templates

Quote Templates control the layout and content of the quote emails sent to buyers and the PDF attached to them. B2B Edition ships with several built-in templates, and you can create custom templates to match your branding, tailor the product table for different customers or regions, translate every piece of text, and surface quote-module extra fields inside the document.

This feature is available for the Quotes 2.0 experience. The template you select is used for the quote emails buyers receive and for the PDFs attached to those emails.

Where to manage quote templates

Go to Settings > Quotes in your B2B Edition control panel to see the template list. Each row shows the template name, whether it is built-in or custom, and whether it is enabled.

  • Built-in legacy templates - the templates pre-loaded with B2B Edition (including Simple, Sky, and Dots from the legacy quotes experience). They cover common layouts and don’t need configuration. Built-in legacy templates can’t be renamed, disabled, or deleted, but you can set one as the default template.
  • Default template - the template used when a quote doesn’t specify one. You can make any enabled template the default, including a legacy one. The default template can’t be disabled or deleted until another template becomes the default.
  • Custom templates - templates you create. You can rename, enable, disable, and delete these freely, except that a custom template can’t be disabled or deleted while it is the default template.

To create a template, click Add template. Template names must be unique within the store and can be up to 200 characters long.

The template editor

Each template has four tabs:

TabWhat you edit
Product TableWhich columns appear in the product table and their per-locale headers
PhrasesText labels used in the template, translated per locale
CodeThe template’s HTML and default subject
PreviewA live preview of the rendered PDF

If your store has more than one storefront, choose the storefront first. Templates are assigned per storefront, so you can reuse one template across storefronts or give each storefront its own template.

Product Table tab

The product table renders the quote’s line items. You choose which columns appear, their order, and the header text for each column per locale.

ColumnDescription
ImageProduct image
ProductProduct name
SKUProduct SKU
OptionsSelected product options
QuantityQuantity ordered
PriceOriginal base price
Quoted PricePrice offered on the quote
DiscountDiscount percentage applied to the line
SubtotalExtended price before the quote discount
Quoted SubtotalExtended price after the quote discount
NotesLine item notes

New templates start with the default column set: Image, Product, SKU, Options, Quantity, Quoted Price, Quoted Subtotal, and Notes.

When discount visibility is turned off in your quote settings, the Price, Discount, and Subtotal columns are hidden from the PDF, even if they’re selected in the template. This setting applies to all quote templates.

Column headers are translatable. Select a locale, edit the header text, and buyers on that storefront see the translated header. Locale changes are cached for about five minutes.

Phrases tab

Phrases are the text labels used throughout the template, such as column captions, section titles, and messages. In the template code a phrase renders where you place {{ phrases.<phrase_name> }}, so changing a phrase value updates every place that references it.

New templates come with these phrases pre-populated:

Phrase nameDefault value
quote_numberQuote#:
date_createdDate created:
valid_untilValid until:
reference_numberReference number:
contact_titleContact
billing_address_titleBilling Address
shipping_address_titleShipping Address
notes_titleNotes
quote_details_titleQuote details
terms_titleTerms and conditions
original_totalOriginal total:
discountDiscount:
quoted_totalQuoted total:
shippingShipping:
taxTax:
totalTotal:
thank_youThank you for your business!
contact_messageIf you have any questions, please contact:
checkout_buttonCheckout
view_quote_detailView quote detail

You can add, edit, and delete phrase names. Phrase names must be unique within the template. Phrase names are merchant-defined, so any name works as long as the template references it with {{ phrases.<name> }}.

Use the locale selector to translate phrase values. Phrases stored under the default locale are the fallback when a buyer’s storefront language has no translation for that phrase.

Code tab

The Code tab holds the template’s HTML and its default subject. The default subject is used when a quote email is sent without an explicit subject, and each template keeps its own default subject.

Template content is HTML with mustache-style variable holders. The renderer supports these categories:

Fixed variables

Fixed variables are populated from the quote, company, store, or user data at render time. Variable names are lowercase snake_case. The B2B Ninja camel-case spellings, such as {{ legalTerms }}, are rejected; the supported forms are the snake_case names documented here.

Quote details

VariableDescription
{{ quote_number }}Quote number
{{ quote_title }}Quote title
{{ quote_amount }}Quote total amount
{{ quote_created_at }}Date the quote was created
{{ quote_expired_at }}Quote expiration date
{{ quote_contact_name }}Quote contact name
{{ quote_contact_email }}Quote contact email
{{ quote_logo }}Quote logo image URL, falling back to the store logo. Use it as an <img> src
{{ quote_notes }}Notes on the quote
{{ quote_details_url }}Link to the quote details page
{{ quote_reminder_days }}Days until the quote reminder
{{ reference_number }}Quote reference number
{{ order_number }}Order number, when the quote is converted

Quote totals

VariableDescription
{{ subtotal }}Line-item subtotal before the quote discount
{{ discount_price }}Quote discount amount
{{ tax_total }}Quote tax total
{{ shipping_total }}Quote shipping amount
{{ grand_total }}Quote grand total
{{ total_amount }}Quote total amount after tax and shipping

The totals above are bare amounts, such as 1,000.00. To show the currency symbol, use the formatted variant of each total. It places the store currency symbol on the correct side of the amount, and shows the amount without a symbol when the quote hides its prices. {{ formatted_shipping_total }} and {{ formatted_tax_total }} render TBD when the quote has no shipping method yet.

VariableDescription
{{ formatted_subtotal }}{{ subtotal }} with the currency symbol
{{ formatted_discount_price }}{{ discount_price }} with the currency symbol
{{ formatted_tax_total }}{{ tax_total }} with the currency symbol
{{ formatted_shipping_total }}{{ shipping_total }} with the currency symbol
{{ formatted_grand_total }}{{ grand_total }} with the currency symbol
{{ formatted_total_amount }}{{ total_amount }} with the currency symbol

Company and buyer contact

VariableDescription
{{ company_name }}Buyer company name
{{ company_phone }}Buyer company phone number
{{ customer_firstname }}Buyer first name
{{ customer_lastname }}Buyer last name
{{ customer_phone }}Buyer phone number
{{ customer_address }}Buyer address, line 1
{{ customer_address_2 }}Buyer address, line 2
{{ customer_city }}Buyer city
{{ customer_state }}Buyer state
{{ customer_zipcode }}Buyer postal code

Billing and shipping addresses

Each field of the quote’s billing and shipping address is available as {{ billing_address.<field> }} and {{ shipping_address.<field> }}. A field the quote doesn’t have renders empty.

FieldDescription
firstNameFirst name
lastNameLast name
addressAddress, line 1
apartmentAddress, line 2
cityCity
stateState
zipCodePostal code
countryCountry
phoneNumberPhone number

For example, {{ shipping_address.city }} renders the shipping city. Field names are case-sensitive, and other fields are rejected.

Store details

VariableDescription
{{ store_name }}Store name
{{ store_secure_url }}Storefront URL
{{ store_login_url }}Storefront login URL
{{ store_logo_url }}Store logo image URL
{{ store_email }}Store contact email
{{ store_phone }}Store phone number
{{ store_country }}Store country
{{ store_currency }}Store currency code
{{ store_currency_symbol }}Store currency symbol
{{ store_weight_units }}Store weight units
{{ store_street }}Store street address

People and shopping lists

VariableDescription
{{ submitter_name }} / {{ submitter_email }}Quote submitter
{{ approver_name }} / {{ approver_email }}Quote approver
{{ inviter_name }} / {{ inviter_email }}Company inviter
{{ user_name }} / {{ user_email }}Recipient’s user name and email
{{ shopping_list_name }}Shopping list name, when the quote originated from one
{{ shopping_list_url }}Link to the shopping list

Sales rep

VariableDescription
{{ sales_rep_name }}Sales rep assigned to the quote
{{ sales_rep_email }}Sales rep email
{{ sales_rep_phone }}Sales rep phone number

The sales rep variables come from the backend user assigned to the quote, and they render empty when the quote has no assigned sales rep. {{ sales_rep_name }} is the backend user’s username, which can be a handle or an email address rather than a display name. You can use these variables in the default subject as well as in template content. In the Preview tab, they render sample values: Sample Sales Rep, sales.rep@example.com, and 555-0300.

The base template footer lists the sales rep lines above the store email and phone line, so a quote without an assigned sales rep still shows the store contact details.

Legal terms and date

VariableDescription
{{ legal_terms }}Terms and conditions attached to the quote
{{ store_conditions }}Same value as {{ legal_terms }}, under the B2B Ninja parity name
{{ current_date }}Current date at render time

A few B2B Ninja parity names are accepted but render empty for now: {{ store_city }}, {{ store_state }}, {{ store_zipcode }}, {{ store_hours }}, and {{ store_fax }}. Names shared with other transactional-email contexts, such as the invoice_* variables, newuser_email, and password_reset_url, are also accepted by the editor but aren’t populated in quote documents.

The product table holder

{{ product_table }} renders the full product table configured on the Product Table tab. This is a single server-rendered holder: don’t try to loop over products or rebuild the table yourself in template code. To change what the table shows, edit the Product Table tab.

The checkout button holder

{{ checkout_button }} renders a styled Checkout link to the quote’s checkout page. Its label comes from the checkout_button phrase. The link renders only when checkout is allowed on the quote; otherwise the holder renders empty, so templates don’t need their own condition. It can only be used in template content, not in the default subject.

The base template includes a View quote detail link in its header, matching the link in the built-in legacy templates:

<a href="{{ quote_details_url }}" rel="noopener noreferrer">{{ phrases.view_quote_detail }}</a>

The link opens the quote on the storefront the quote belongs to. In the Preview tab, the sample link points to the storefront home page instead.

The link is ordinary template HTML, so you can change it on the Code tab:

  • To restyle it, add an inline style attribute, for example style="color: #3c64f4; font-weight: 600;". Inline styles follow the same template rules as the rest of the content.
  • To move it, cut the <a> element and paste it anywhere else in the template content.
  • To remove it, delete the <a> element.

The link text comes from the view_quote_detail phrase. To relabel or translate it, edit that phrase’s value per locale in the Phrases tab.

Extra-field variables

Quote-module extra fields become template variables. See Using extra fields as variables.

Template rules

The editor validates content before saving and blocks invalid templates:

  • Content must be HTML. Django template syntax, including {% ... %} statement tags and {# ... #} comments, isn’t allowed.
  • Only variables listed above, plus {{ phrases.<name> }}, {{ extra_fields.<name> }}, and the {{ billing_address.<field> }} / {{ shipping_address.<field> }} fields, are supported. Unknown variables are rejected.
  • {{ product_table }} and {{ checkout_button }} render HTML, so place them in template content rather than the default subject. The default subject rejects {{ checkout_button }}.
  • Link URLs support the https and mailto schemes only.
  • Inline style attributes are sanitized: properties that can load external resources (such as anything carrying a url()) are stripped.
  • Template content can be up to 100,000 characters, names up to 200 characters, and the default subject up to 1,300 characters.

Preview tab

The Preview tab renders the template with sample quote data so you can check layout, translations, and variable output before saving. Preview respects the selected storefront and locale.

You can also send the rendered PDF to an email address with Send test email to see exactly what a buyer receives.

Using extra fields as variables

Extra fields let you attach custom data to quotes and display that data inside the quote PDF.

  1. Go to Settings > Extra Fields and create extra fields for the Quote module.
  2. Supported value types are text, textarea, number, and dropdown.
  3. Reference a field’s value in the template as {{ extra_fields.<variable_name> }}.

The variable name is derived from the field name:

  • Lowercase the name and replace spaces and symbols with underscores (for example, “Delivery Window” becomes delivery_window).
  • If the name starts with a digit, the variable is prefixed with field_ (for example, “2Day Ship” becomes field_2day_ship).
  • If two fields slugify to the same name, later ones get a numeric suffix (_2, _3, and so on).

Use the Code tab’s variable list in the control panel to copy the exact variable name for each field; it reflects the slugified name.

Resources