The operational silo of third-party quotation apps
Most Shopify merchants who need a 'quote instead of cart' workflow install an app from the Shopify App Store. These apps replace the 'Add to Cart' button with a 'Request a Quote' button that triggers an embedded form. On the surface, the store gains quotation capability in ten minutes. Behind the scenes, the integration creates a permanent operational silo.
First, standard quote apps store the submitted requests on external servers. The merchant's sales team must log into a separate SaaS dashboard to review inquiries, manually verify stock levels, and copy-paste details into external spreadsheets or ERP systems. When a quote is approved, the app either generates a generic email link with a custom payment token or requires staff to manually construct a draft order in Shopify.
Second, third-party quote scripts inject significant client-side overhead. Because they must inspect the DOM to locate and replace theme cart buttons across hundreds of different theme architectures, they ship heavy polling scripts and render-blocking stylesheets. For stores operating bespoke configurators, such as the BE O Lifestyle product customizer, these generic overlays fight with custom canvas elements and break the responsive presentation.
Third, customer identity is severed. If the buyer is an authenticated B2B buyer belonging to an approved corporate account, the third-party app rarely respects Shopify's native B2B company structures. The quote arrives as an anonymous email submission, forcing account managers to perform manual identity resolution before knowing what commercial terms apply.
Why standard carts cannot handle negotiated wholesale
Consumer ecommerce assumes three axioms: unit prices are fixed by catalog variants, payment is rendered immediately at checkout, and the transaction is final upon payment authorisation. Every step of Shopify's standard online store cart pipeline is optimised for this flow.
B2B wholesale and branded corporate merchandise violate all three axioms:
Tiered volume pricing with bespoke decoration: A single bottle variant might retail for €24.00, drop to €18.50 at one hundred units, and reach €15.00 at two hundred and fifty units. If the client adds multiple print colours or bespoke packaging, those fees must either modify the unit price or appear as discrete line items. As we documented in our analysis of checkout calculation extensibility, attempting to manipulate arbitrary line items inside standard checkout triggers strict platform validation.
Non-immediate settlement: Corporate purchase orders require formal quotation sign-off, credit verification, and invoice terms (such as Net 30 or Net 60). Directing a buyer to a standard consumer checkout that demands a corporate credit card causes immediate drop-off on orders valued at five or six figures.
Manufacturing lead times and proof approvals: Before production begins, the merchant's atelier or production team must generate a digital proof, verify vector artwork resolution, and confirm delivery schedules. A standard Shopify order flags as 'Unfulfilled' immediately, triggering automated fulfilment alerts and inventory commitments before the artwork is even approved.
Treating wholesale inquiries as immediate online cart checkouts is a category error. The transaction must start as an open draft.
Intercepting the submission: from PDP to GraphQL draftOrderCreate
The correct technical boundary for a quotation workflow is a lightweight serverless endpoint that receives structured JSON from the storefront and translates it directly into a native Shopify Draft Order via the Admin GraphQL API (draftOrderCreate).
When the customer submits their quotation request on the product page, client-side JavaScript intercepts the standard cart submission, uploads any attached vector artwork to a secure bucket, and sends the structured configuration payload to the serverless backend. The backend executes draftOrderCreate.
The result in the Shopify Admin is immediate and native: A draft order document appears in the merchant's standard Drafts list with the tag b2b-quote; all lines carry their exact variant identifiers, ensuring stock availability and warehouse SKU mapping remain intact; custom attributes preserve the vector logo URL, imprint colours, and decoration instructions; and sales staff can review the draft, adjust freight, and trigger Shopify's native draftOrderInvoiceSend mutation to email an authentic checkout URL.
export interface VolumePriceBreak {
minQuantity: number;
discountedUnitPrice: number;
}
export interface QuoteLineInput {
merchandiseId: string;
title: string;
sku: string;
quantity: number;
baseUnitPrice: number;
minQuantity?: number;
volumeBreaks?: VolumePriceBreak[];
customAttributes?: Record<string, string>;
requiresShipping?: boolean;
}
export interface QuoteRequestPayload {
customer: {
email: string;
companyName?: string;
vatNumber?: string;
companyId?: string;
companyLocationId?: string;
taxExempt?: boolean;
};
lines: QuoteLineInput[];
requestedPaymentTerms?: "NET_15" | "NET_30" | "NET_60" | "DUE_ON_RECEIPT";
generalNote?: string;
currencyCode?: string;
}
export function buildB2BDraftOrderInput(payload: QuoteRequestPayload) {
const lineItems = payload.lines.map((line) => {
// Resolve volume discount tier
const sorted = [...(line.volumeBreaks ?? [])].sort((a, b) => b.minQuantity - a.minQuantity);
const tier = sorted.find((t) => line.quantity >= t.minQuantity);
const unitPrice = tier && tier.discountedUnitPrice < line.baseUnitPrice
? tier.discountedUnitPrice
: line.baseUnitPrice;
const discountAmount = Math.max(0, line.baseUnitPrice - unitPrice);
const percent = Math.round((discountAmount / line.baseUnitPrice) * 10000) / 100;
const customAttributes: Array<{ key: string; value: string }> = [];
if (line.customAttributes) {
for (const [key, value] of Object.entries(line.customAttributes)) {
if (value && String(value).trim()) {
customAttributes.push({ key: key.trim(), value: String(value).trim() });
}
}
}
return {
...(line.merchandiseId ? { variantId: line.merchandiseId } : {}),
title: line.title,
sku: line.sku,
quantity: line.quantity,
originalUnitPrice: unitPrice.toFixed(2),
...(percent > 0
? {
appliedDiscount: {
title: `Volume Break (${line.quantity} units)`,
value: percent,
valueType: "PERCENTAGE" as const,
amount: (discountAmount * line.quantity).toFixed(2),
},
}
: {}),
customAttributes,
requiresShipping: line.requiresShipping ?? true,
};
});
return {
email: payload.customer.email,
...(payload.customer.companyId
? {
purchasingEntity: {
purchasingCompany: {
companyId: payload.customer.companyId,
companyLocationId: payload.customer.companyLocationId,
},
},
}
: {}),
taxExempt: payload.customer.taxExempt ?? false,
currencyCode: payload.currencyCode ?? "EUR",
lineItems,
tags: ["b2b-quote", "quote-pending-review"],
customAttributes: [
{ key: "_source", value: "Storefront B2B Quote Engine" },
...(payload.requestedPaymentTerms
? [{ key: "_requestedPaymentTerms", value: payload.requestedPaymentTerms }]
: []),
],
};
}Preserving artwork assets and bespoke specifications in custom attributes
In corporate merchandise and bespoke production, the primary friction point is artwork handling. When buyers upload logos through third-party quote forms, the file links frequently expire, get buried in email threads, or arrive in unsupported raster formats.
In our work on supplier portal architecture and production artwork automation, we established the principle that production metadata must attach directly to the commerce document. On a Shopify Draft Order, each line item accepts an array of customAttributes.
By prefixing operational attributes with an underscore (_artworkUrl, _imprintColor, _pantoneCodes, _decorationMethod), atelier technicians see the exact production parameters inside the Shopify Admin order view and receive the artwork URL directly on printing tickets, while Liquid storefront themes omit them from generic customer receipts.
When the draft order converts to a paid order upon invoice completion, Shopify cascades all customAttributes from the draft line item to the final Order line item automatically.
Calculating volume price breaks and tax exemptions deterministically
Wholesale pricing cannot rely on client-side math submitted from the browser. An unscrupulous buyer could modify DOM data attributes to claim a 500-unit tier discount while requesting only twenty units.
The quotation worker must validate every line deterministically against pre-configured business rules: Minimum Order Quantity (MOQ) enforcement aborts if the requested quantity falls below the threshold; volume discount resolution sorts configured price breaks in descending order to apply the highest qualifying tier; and tax exemption handling sets taxExempt: true for cross-border B2B transactions with validated VAT registrations.
On Shopify Plus stores utilizing native B2B features, the draft order binds directly to the customer's purchasingCompany record, ensuring the resulting order respects company credit limits, payment terms templates, and designated shipping locations.
What a native draft order quote pipeline cannot do
While native draft orders solve the operational disconnect created by third-party apps, the pattern has deliberate platform boundaries:
No real-time inventory hard lock: Creating a draft order does not decrement available storefront inventory. While reserveInventoryUntil places a temporary hold, open quotes must not hold scarce stock indefinitely without an automated expiry worker.
Storefront API cannot create draft orders directly: Because draft orders permit arbitrary price and discount overrides, draftOrderCreate is restricted to authenticated Admin API credentials and requires a secure serverless intermediary.
Checkout customisation boundaries: Once the merchant sends the invoice, the customer completes payment through native Shopify checkout, where custom terms checkboxes require Plus Checkout Extensibility.
Multi-currency limitations: The server worker must establish the draft order in the customer's presentment currency, calculating line prices explicitly in that currency.
When this needs an engineer
If your store sells standard retail products with occasional bulk inquiries and you receive fewer than three quote requests a week, an off-the-shelf quote app or a simple Shopify Form connected to an email inbox is sufficient. The manual effort of transposing three requests into draft orders is lower than the engineering cost of maintaining a custom quotation pipeline.
It requires dedicated engineering when products are truly bespoke and continuously dimensioned; when wholesale orders require multi-tier volume pricing, proof approvals, and Net 30/60 invoicing; and when quotation requests must integrate natively with Shopify Plus B2B company accounts, location catalogs, and ERP order pipelines, as demonstrated in our B2B & supplier operations collections and Shopify B2B development services.
Send us the store and the symptom.
