Shopfront Embedded Bridge Fulfilment API

Shopfront's Embedded Bridge allows you to easily plug into Shopfront's fulfilment system so stores don't have to leave Shopfront to process orders and prevents manual processing errors.

This page describes the Embedded Bridge's Fulfilment API, for in-depth examples on how to use the API, check out the How To Guide.

Emitable Actions

The fulfilment API is made up a number of emitable actions that can be sent to Shopfront at any time.

RegisterIntent

Whilst optional, this should be sent to Shopfront as soon as your application is ready to opt-in to the Fulfilment API. This enabled the option on the sell screen to view the fulfilment page and display your application as connected.

Field Type Description
requireApproval boolean Whether sales waiting to process require approval before they're available to pack
RegisterIntent
import { Fulfilment, Bridge } from "@shopfront/bridge";

const application = Bridge.createApplication({ /* your options */ });

application.addEventListener("READY", async () => {
    await application.send(new Fulfilment.RegisterIntent({
        requireApproval: true,
    }));
});

Options

Update the fulfilment options previously provided on opt-in.

Field Type Description
requireApproval boolean Whether sales waiting to process require approval before they're available to pack
Options
import { Fulfilment } from "@shopfront/bridge";

application.send(new Fulfilment.Options({
    requireApproval: false,
}));

OrdersSync

Synchronise a number of orders with Shopfront, this is typically called on a schedule or when your application receives an event from your server.

Field Type Description
orders Array<OrderCreateDetails> The orders to send to Shopfront
merge boolean Whether the orders should be merged into the currently available orders, or if they should override the orders

Setting merge to true doesn't merge individual orders, it just prevents the removal of all previous orders for the integration. Updates to orders should use the OrderUpdate action.

OrderCreateDetails

Field Type Description
id string Your ID for the order
customer { name: string, phone: string } The customer who has performed the order
courier { icon?: string, name: string, phone: string }? (optional) The courier who will be picking up the order
comment string? (optional) Any comments for the order
totalPrice number The total price for the order
expiry (string | Date)? (optional) When the order will expire, this is purely a label and if a date is passed in it'll show the time relative to the date, otherwise it'll show the provided string
status OrderStatus? (optional) The status of the order
finalLabel string? (optional) The label to display as part of the final step in the UI
createdAt string The time the order was created

OrderStatus

One of:

  • PENDING_APPROVAL
  • WAITING_FOR_PACKING
  • PACKED
  • COLLECTED
  • COMPLETED
OrdersSync
import { Fulfilment } from "@shopfront/bridge";

// onOrderRetrievalQueue is a presumed way that you would retrieve orders from your API
onOrderRetrievalQueue(async allOrders => {
    await application.send(new Fulfilment.OrdersSync({
        orders: allOrders,
        merge : false,
    }));
});

OrderCreate

Create a single order to send to Shopfront's fulfilment system. This can be used instead of the OrdersSync action.

Field Type Description
order OrderCreateDetails The order to send to Shopfront
OrderCreate
import { Fulfilment } from "@shopfront/bridge";

// onNewOrder is a presumed way that you would receive a new order notification from your API
onNewOrder(async order => {
    await application.send(new Fulfilment.OrderCreate({
        order,
    }));
});

OrderUpdate

Update a single order that already exists within Shopfront's fulfilment system.

Field Type Description
order OrderSummaryDetails The order to send to Shopfront

OrderSummaryDetails

Field Type Description
id string Your ID for the order
customer { name: string, phone: string } The customer who has performed the order
courier { icon?: string, name: string, phone: string }? (optional) The courier who will be picking up the order
comment string? (optional) Any comments for the order
totalPrice number The total price for the order
expiry (string | Date)? (optional) When the order will expire, this is purely a label and if a date is passed in it'll show the time relative to the date, otherwise it'll show the provided string
status OrderStatus The status of the order
finalLabel string? (optional) The label to display as part of the final step in the UI
createdAt string The time the order was created
OrderUpdate
import { Fulfilment } from "@shopfront/bridge";

// onOrderUpdate is a presumed way that you would receive an updated order from your API
onOrderUpdate(async order => {
    await application.send(new Fulfilment.OrderUpdate({
        order,
    }));
});

OrderCancel

This cancels / removes an order within Shopfront's fulfilment system.

Field Type Description
id string The ID of the order to remove
OrderCancel
import { Fulfilment } from "@shopfront/bridge";

// onOrderCancel is a presumed way that you would receive a new order notification from your API
onOrderCancel(async id => {
    await application.send(new Fulfilment.OrderCancel({
        id,
    }));
});

Receivable Events

Along with registering and sending order updates, your application will also need to listen to a number of events and respond back to them with correctly formatted data.

Any event can optionally return a promise.

FULFILMENT_GET_ORDER

This event triggers when Shopfront requires the full details of a single order.

Arguments

Field Type Description
id string The ID of the order

Returns

Field Type Description
id string Your ID for the order
customer { name: string, phone: string } The customer who has performed the order
courier { icon?: string, name: string, phone: string }? (optional) The courier who will be picking up the order
comment string? (optional) Any comments for the order
totalPrice number The total price for the order
expiry (string | Date)? (optional) When the order will expire, this is purely a label and if a date is passed in it'll show the time relative to the date, otherwise it'll show the provided string
status OrderStatus The status of the order
finalLabel string? (optional) The label to display as part of the final step in the UI
createdAt string The time the order was created
items Array<OrderItem> The items on the order

OrderItem

Field Type Description
id string Your ID for the product
image string? (optional) The image to use for the product
name string Your name for the product
quantity number The number of packSizes sold
packSize number|PackSizeMatch The outer size which was sold (e.g. a six-pack would return this as 6)
price number The total price for the line
comment string? (optional) Any comment on the product to display
match OrderItemMatch? (optional) The matching for the product

PackSizeMatch

This allows you to specify a different quantity depending upon how the item was matched.

Field Type Description
id number? For matching via Shopfront ID
barcode ("auto"|number)? For matching matching via barcode (if auto is specified, it will use the barcode quantity)
mdbId number? For matching via MDB ID
supplierCode number? For matching via Supplier Code
defaultTo number When a match can't be determined or a field isn't specified which was used to match

OrderItemMatch

All fields are optional, for details on how Shopfront performs matching, see the product matching algorithm section.

Field Type Description
id string? Shopfront's ID for the product
barcodes Array<string>? The barcodes of the product, these don't have to correspond to the packSize
mdbId number? The master database reference for the product
supplier object? The supplier which this product belongs to
supplier.id string? Shopfront's ID for the supplier
supplier.mdbId number? The master database reference for the supplier
supplierCodes Array<string>? The supplier codes that belong to the product
FULFILMENT_GET_ORDER
application.addEventListener("FULFILMENT_GET_ORDER", id => {
    return retrieveFullOrderFromAPI(id);
});

FULFILMENT_VOID_ORDER

Triggers when Shopfront (typically on user interaction) voids / cancels an order. It's expected that you'll send this information back to your server to handle any potential refund process and remove the order from the list of orders available to sync to Shopfront.

Arguments

Field Type Description
id string The ID of the order

Returns

  • None
FULFILMENT_VOID_ORDER
application.addEventListener("FULFILMENT_VOID_ORDER", id => {
    return cancelOrderViaAPI(id);
});

FULFILMENT_PROCESS_ORDER

This occurs once the order has been processed and matched on Shopfront, moving to the PACKED stage. It's expected you'll transform and process the sale on Shopfront when ready (the sale is not automatically processed on Shopfront).

Arguments

Field Type Description
event FulfilmentProcessEvent The event that occurred

Returns

  • None

FulfilmentProcessEvent

Field Type Description
id string The ID of the order
sale Sale The sale from Shopfront
FULFILMENT_PROCESS_ORDER
application.addEventListener("FULFILMENT_PROCESS_ORDER", async (event) => {
    const id   = event.id;
    const sale = event.sale;

    await markSaleAsPackedAndSaveSale(id, sale);
});

FULFILMENT_ORDER_APPROVAL

This triggers when the order is approved or declined on Shopfront, this will only be called if you have enabled requireApproval when opting in to using the fulfilment system.

Arguments

Field Type Description
event FulfilmentApprovalEvent The event that occurred

Returns

  • None

FulfilmentApprovalEvent

Field Type Description
id string The ID of the order
approved boolean Whether the order was approved or not
FULFILMENT_ORDER_APPROVAL
application.addEventListener("FULFILMENT_ORDER_APPROVAL", async (event) => {
    if(!event.approved) {
        await cancelOrderViaAPI(event.id);
    } else {
        await approveOrder(event.id);
    }
});

FULFILMENT_ORDER_COLLECTED

Triggers when the order is marked as collected through Shopfront.

Arguments

Field Type Description
id string The ID of the order

Returns

  • None
FULFILMENT_ORDER_COLLECTED
import { Sales } from "@shopfront/bridge";

application.addEventListener("FULFILMENT_ORDER_COLLECTED", async id => {
    await markOrderAsCollected(id);
    const sale = await retrieveSavedShopfrontSale(id);

    sale.addPayment(
        new Sales.SalePayment("<< id of payment method >> ", sale.getSaleTotal())
    );

    await sale.create(application);
});

FULFILMENT_ORDER_COMPLETED

Triggers when the order is marked as completed within Shopfront

Arguments

Field Type Description
id string The ID of the order

Returns

  • None
FULFILMENT_ORDER_COMPLETED
application.addEventListener("FULFILMENT_ORDER_APPROVAL", async (id) => {
    await markOrderAsCompleted(id);
});

Product Matching Algorithm

When the FULFILMENT_GET_ORDER event is triggered, it expects an object back that contains a list of products, each of which can optionally contain an OrderItemMatch object.

It's suggested to provide as many details in OrderItemMatch as you can as it'll allow for the most accurate matching and reduce the chances of algorithm changes affecting your application in the future.

Shopfront then attempts to match using the following algorithm:

  1. Exact id match
    • If this matches, use the matching product
    • Otherwise continue
  2. Match via barcodes
    • If a single product matches, use the matching product
    • If no products match, continue
    • If multiple products match, continue using the matching products as the base result set
  3. Match via mdbId
    • If a single products matches, use the matching product
    • If no products match, continue
    • If multiple products match, continue using the matching products as the base result set
  4. Match via supplierCodes using the supplier.id as the supplier
    • If a single product matches, use the matching product
    • If no products match, continue
    • If multiple products match, continue
  5. Match via supplierCodes using the supplier.mdbId as the supplier
    • If a single product matches, use the matching product
    • If no products match, continue
    • If multiple products match, continue
  6. Stop matching and review existing products
    • If multiple products exist which have moved through each of the previous steps, select the first one
    • If multiple products exist from one of the previous steps (in order from 2-5), select the first one
    • If no products match, an automatic match isn't possible

Even is an automatic match is possible, the user can still elect to change which product matches to your provided product.