Reorders pickup-location options at checkout. Powered by Shopify's cart.delivery-options.transform.run Function target.
Three modes — pick the one that fits your situation. Modes are per-rule; you can have multiple rules with different modes and priorities running side-by-side.
| Mode | Configuration required | When it fires | Best for |
|---|---|---|---|
| Fixed order | Admin: pick + order your locations | Always | Flagship-store pinning, region-specific stores you want emphasized |
| Auto-match | None — zero config | Logged-in customers with saved default address | Multi-location retailers who already have a store in each major region |
| By cart attribute | Admin: pick match style. Storefront: write to _switchboard_pickup. |
Whenever your storefront sets the attribute | Custom logic — last-order pickup, B2B segments, ML-scored recommendations |
Pin a specific order of pickup locations for everyone. The merchant uses Up/Down/Remove buttons in the admin to build an ordered list. At checkout, those locations are sorted to the top in that exact order; locations not in the list stay where Shopify puts them.
When to use: you have a "we want THIS store first, everywhere" preference. Common for flagship stores, regional headquarters, or stores you're trying to promote.
Configuration:
No storefront code required.
Reads the logged-in customer's saved default address. Pins the pickup location whose own address matches by province (preferred) or country (fallback). Zero per-rule configuration — Switchboard fetches each pickup location's address from your Shopify Locations settings and computes the mapping automatically.
When to use: you have a store in each major region you serve, and you want the closest one to surface first for repeat / logged-in customers.
Configuration:
That's it. The admin shows a live preview of the auto-mapping based on your pickup locations' actual addresses:
Customer in MB → pin Winnipeg Customer in NB → pin New Brunswick Customer in ON → pin My Custom Location Customer in QC → pin Quebec
The mapping refreshes every time the merchant saves any rule (in case pickup locations get added or moved).
No storefront code required.
| Customer state | Geo fires? |
|---|---|
| Logged in, has saved default address | ✅ Yes — Shopify auto-fills the address |
| Logged in, no saved address | ❌ No (no signal) — rule silently steps aside |
| Anonymous guest | ❌ No (no signal) — rule silently steps aside |
When the rule doesn't fire, lower-priority rules still get a chance. So you can stack:
The first one that applies per delivery group wins.
Reads the cart attribute _switchboard_pickup. Its value can be:
Plus a match style chosen in the admin:
customer.last_order.line_items[].fulfillment.location.name)customer.tags, picks a different ordered list)Admin:
Storefront — easiest path:
// One location
Switchboard.setPickup("Quebec");
// Ordered list
Switchboard.setPickup(["Quebec", "Winnipeg", "New Brunswick"]);
// Clear
Switchboard.clearPickup();
Storefront — no SDK:
fetch('/cart/update.js', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
attributes: { _switchboard_pickup: 'Quebec' }
// or: { _switchboard_pickup: JSON.stringify(["Quebec","Winnipeg"]) }
})
});
See /docs/sdk for the full SDK reference and /docs/liquid for Liquid recipes.
| Value sent | Pickup location titles | Exact mode | Substring mode |
|---|---|---|---|
"Quebec" |
["Quebec", "Winnipeg"] |
✅ Pins Quebec | ✅ Pins Quebec |
"Quebec" |
["Quebec Warehouse", "Toronto"] |
❌ No match | ✅ Pins Quebec Warehouse |
"TX" |
["Houston, TX", "New York"] |
❌ No match | ✅ Pins Houston, TX |
"Dallas" |
["Dallas, TX Hub", "Houston, TX"] |
❌ No match | ✅ Pins Dallas, TX Hub |
Rule of thumb: if you send a full literal title, use Exact. If you send a region code / city / keyword that's a substring of the title, use Substring.
Send an array → those titles get sorted to the top in the order given. Titles not found in the cart's pickup options are silently skipped (no gaps left at the top).
| Cart attribute value | Pickup options (before) | Result (after) |
|---|---|---|
["Quebec","Winnipeg"] |
[A, B, Quebec, Winnipeg] |
[Quebec, Winnipeg, A, B] |
["Missing","Quebec","Also"] |
[A, B, Quebec] |
[Quebec, A, B] (missing items skipped) |
[] (empty array) |
unchanged | unchanged — rule doesn't apply, fallback rules get a turn |
Each rule has a numeric priority (default 100, lower wins). Per delivery group, the first applying rule wins. Once a rule applies, no further rules run for that group.
Typical setup:
The customer sees ONE rule's output. Picking sensible priorities prevents conflicts.
The function NEVER throws and NEVER blocks checkout. On any failure (malformed cart attribute JSON, missing pickup options, mismatched titles, etc.), the rule silently emits zero operations and Shopify's default pickup order is shown.
| Failure | Result |
|---|---|
| Malformed cart attribute JSON | Treated as plain title; if no match, no-op |
| Cart attribute set to a non-string, non-array value (e.g. object) | Rule does not apply |
| Title in array doesn't match any pickup option | That entry skipped; tight stack at top |
| All titles in array miss | No moves emitted |
| Customer not logged in (Auto-match) | Rule does not apply; lower-priority rules get a turn |
| No pickup locations enabled in the shop | No moves emitted (function has no targets) |
When the merchant saves a rule, Switchboard:
$app:switchboard.pickup_locations) so the function can read them for Auto-match.$app:switchboard.rules).At checkout, the function reads both metafields, plus the cart's _switchboard_pickup attribute. It emits deliveryOptionMove operations to reorder the pickup picker.
See extensions/switchboard-delivery-reorder/src/ in the repo for the function code.
deliveryOptionMove is the only operation type for this function target. You can move existing options around; you can't add or hide them.cart.deliveryGroups[].deliveryAddress is null for anonymous carts without a delivery address. Auto-match silently no-ops in that case.cart.token — pickup options expose handle and title only. Switchboard matches on the numeric location ID embedded in the handle, with a title-snapshot fallback.See SHOPIFY_APP_PLAYBOOK.md in the repo for the full list of Shopify Function constraints we hit while building this.