Embedded API Events

The Shopfront Embedded API has several events that you can subscribe to in order to add functionality to the POS. This document describes the events and what is expected to be returned from them.

Any event can return a promise.

Some object tables may not include all fields, please refer to the Typescript types for all fields

READY

This listener fires when the connection between Shopfront and the Embedded API is established and is ready to use.

Arguments

  • None

Returns

  • None
Ready
application.addEventListener("READY", () => ({
    console.log("Integration Ready to Process");
}));

FORMAT_INTEGRATED_PRODUCT

This listener fires whenever a product with its type set to integrated is added to the sale

Arguments

Field Type Description
event FormatIntegratedProductEvent Contains the product that was added to the sale

Returns

Field Type Description
event FormatIntegratedProductEvent Contains the modified product

FormatIntegratedProductEvent

Field Type Description
product FormattedSaleProduct The modified product

FormattedSaleProduct

Field Type Description
uuid string The unique identifier of the product
type string The type assigned to the product
name string The product name
caseQuantity number The case quantity of the product
metaData Record<string, unknown> Extra data about the product
Format Integrated Product
application.addEventListener("FORMAT_INTEGRATED_PRODUCT", event => {
    const product = event.product;
    if (
        typeof product.additional === "object" &&
        product.additional !== null &&
        product.additional.my_integration === 1
    ) {
        return {
            product: {
                ...product,
                metaData: {
                    ...product.metaData,
                    customInfo: {
                        name: "myIntegration",
                    },
                },
            },
        };
    }
});

PAYMENT_METHODS_ENABLED

This allows you to both enable and disable payment methods on the finalise payment screen

Arguments

Field Type Description
methods Array<SellScreenPaymentMethod> An array of all current payment methods

Returns

  • Array<SellScreenPaymentMethod>:
Field Type Description
background_colour string The background colour of the payment method option
gateway_url string The URL that is navigated too when the method is selected
name string The name of the payment method
text_colour string The colour of the payment method's name
type string The type of payment method
uuid string The unique identifier of the payment method
Payment Methods Enabled
//Return a custom payment method
const getPaymentMethod = async () => {
    let customMethod: LocalPaymentMethod | undefined;
    const methods = await application.database.all<LocalPaymentMethod>("paymentMethods");

    for(let i = 0, l = methods.length; i < l; i++) {
        if (methods[i].type === "custom" || methods[i].gateway_url === "https://mycustom.url") {
            customMethod = methods[i];
        }
    }

    return customMethod;
};

application.addEventListener("PAYMENT_METHODS_ENABLED", async methods => {
    const method = await getPaymentMethod();

    if (method) {
        //Add the payment method
        methods.push({
            ...method,
            default_pay_exact: true,
            type             : "custom"
        });
    }

    return methods;
});

REGISTER_CHANGED

This fires anytime the user, outlet or register changes

Arguments

Field Type Description
event RegisterChangedEvent An object containing the current user, outlet and register id

Returns

  • None

RegisterChangedEvent

Field Type Description
register string The current register id
outlet string The current outlet id
user string The current user id
Register Changed
application.addEventListener("REGISTER_CHANGED", event => ({
    console.log(`New register id: ${event.register}`);
}));

REQUEST_BUTTONS

This allows you to add custom buttons to several pages within Shopfront.

Arguments

Field Type Description
location string The location that the button will be appearing (see below for locations)

Locations:

  • ORDER_VIEW
  • REGISTER_CLOSURE_VIEW
  • FULFILMENT_PAGE

Returns

Request Buttons
import { Button } from "@shopfront/bridge";

const orderViewButtons = () => {
    const button = new Button("Send to Integration", "https://example.com/icon.png");

    button.addEventListener("click", () => {
        // Perform action here
    });

    return [button];
};

application.addEventListener("REQUEST_BUTTONS", location => {
    switch(location) {
        case "ORDER_VIEW":
            return orderViewButtons();
    }

    return [];
});

REQUEST_CUSTOMER_LIST_OPTIONS

This allows you to add your own button to the Add Customer screen. Particularly useful if you wish to implement a way for Shopfront users to add custom loyalty customers to a sale

Arguments

  • None

Returns

  • Array<SellScreenCustomerListOption>:
Field Type Description
contents string The name of the new option
onClick function The function that is executed when it is clicked
Request Customer List Options
application.addEventListener("REQUEST_CUSTOMER_LIST_OPTIONS", () => {
    return [
        {
            contents: "Add Loyalty Customer",
            onClick : () => {
                console.log("Adding loyalty customer");
            }
        }
    ];
});

REQUEST_SALE_KEYS

This allows you to create custom Sale Keys for your integration. Users can then add them to their Sale Key layouts.

Arguments

  • None

Returns

  • Array<SaleKey>:
Field Type Description
supportedEvents Array<string> The supported events that can be assigned to the button
id string The id of the Sale Key
name string The name given to the Sale Key
Request Sale Keys
import { SaleKey } from "@shopfront/bridge";r

application.addEventListener("REQUEST_SALE_KEYS", () => ({
    const keys: SaleKey[] = []; 
    const saleKey = new SaleKey(
        "@onshopfront/example/newKey",
         "A New Sale Key"
    ); 

    saleKey.addEventListener("click", () => {
        console.log("Button clicked");
    };

    keys.push(saleKey);

    return keys;
});

REQUEST_SELL_SCREEN_OPTIONS

Listening to this event will allow you to add options to the sell screen actions (next to Sales Keys and Parked Sales).

Any URL specified must be using the HTTPS protocol.

Arguments

  • None

Returns

  • Array<SellScreenOption>:
Field Type Description
url string The URL to display in a frame when this option is selected
title string The button label of the option, this can either be plain text or any valid HTML (this will inherit style from the POS)
Request Sell Screen Options
application.addEventListener("REQUEST_SELL_SCREEN_OPTIONS", () => ({
    "url": "https://example.com",
    "title": "Example Option",
});

REQUEST_SETTINGS

This allows you to add a settings page for your integration. It will be added next to the Revoke button on the Integrations Settings page

If you provide a URL to your settings page it must be using the HTTPS protocol.

Arguments

  • None

Returns

  • Object:
Field Type Description
logo string? The URL to the logo you would like to display
description string? The description of what your integration does
url string? The URL to the settings page you wish to display
Request Settings
application.addEventListener("REQUEST_SETTINGS", () => ({
    logo: "https://example.com/logo-location.png",
    description: "Sends purchases to your accounting software",
    url: "https://example.com/shopfront-settings"
}));

REQUEST_TABLE_COLUMNS

Listening to this event will allow you to add additional columns to several tables within Shopfront.

Arguments

Field Type Description
location string The location that the table is from (see below for locations)
data object Information about the table to be added to (see below along with locations)

Locations:

  • ORDER_VIEW
Field Type Description
id string The ID of the order
from string The ID of who the order is from
to string The ID of who the order is to
status string The status of the order
type string The type of order
sentAt string | null The time the order was sent
products Array<object> The products in the order
products.id string The ID of the product
products.caseQuantity number The case quantity of the product
products.orderedQuantity number The number of items ordered
products.receivedQuantity number The number of items received
products.baseCost number The base cost for the product
products.fees number The fees for the product
products.freight number The freight for the product
products.paymentFees number The payment fees for the product
products.rebate number The rebate amount for the product
products.supplierCode string The supplier code for the product

Returns

  • null or Object:
Field Type Description
headers Array<object> The headers for the table
headers.label string The label for the header
headers.key string The key for the body and footer values
headers.weight number The weight of the column, the higher the value the further to the right the column is
body Array<Object<key, label>> Each element in the array is a row which contains an object map to provide the cell contents
footer Object<key, label> The value of the footer
Request Table Columns
application.addEventListener("REQUEST_TABLE_COLUMNS", (location, data) => {
    if(location === "ORDER_VIEW") {
        if(data.status === "SENT" && data.type === "ORDER") {
            return {
                headers: [{
                    label : "10 Or More",
                    key   : "more",
                    weight: 10, // Will be 10 from the left
                }],
                body: data.products.map(product => ({
                    more: product.orderedQuantity > 10 ? "Yes" : "No",
                })),
                footer: {
                    more: "",
                },
            }
        }
    }
    
    return null;
});

SALE_COMPLETE

This listener fires whenever a sale is finalised.

Arguments

Field Type Description
event SaleCompletedEvent An object containing an object of the finalised sale

Returns

  • None

SaleCompletedEvent

Field Type Description
sale CompletedSale An object containing details of the sale

CompletedSale

Field Type Description
products Array<CompletedSaleProduct> An array containing details of each product in the sale
customer false OR object If set, containers a uuid field with the customers id
payments Array<CompletedSalePayment> An array containing all the payment methods used in the sale
notes object An object containing both internal and sale notes
totals object An object containing information on the payment totals
registerId string The register that the sale was finalised on
userId string The user who finalised the sale
status string The status of the sale
createdAt string The date and time the sale was finalised
metaData Record<string, unknown> A record containing extra information about the sale

CompletedSaleProduct

Field Type Description
name string The product name
caseQuantity number The amount of products in a case
quantity number The quantity being sold in this transaction
prices object An object containing information on the different prices the product has
metaData Record<string, unknown> An object containing all custom information about Shopfront, this doesn't have any predefined format

CompletedSalePayment

Field Type Description
method string The method of the payment (e.g CASH)
type string The type of the payment method (e.g INTEGRATED)
status string The processing status of the payment method
amount number The amount that was paid for by this method
metadata string A string containing extra information about the sale
Sale Complete
application.addEventListener("SALE_COMPLETE", event => ({
    console.log("Sale was finalized");
}));

AUDIO_READY

This listener fires when the Embedded API is ready to receive Audio events.

Arguments

  • None

Returns

  • None
Audio Ready
application.addEventListener("AUDIO_READY", () => ({
    console.log("Audio Events Ready to Process");
}));

UI_PIPELINE

The UI_PIPELINE allows you to modify the content which is displayed in certain parts of the POS. Each application is passed the previous application’s response allowing you to continually modify and add on to the content to be displayed in Shopfront. If you’re listening to this event and don’t need to modify the content, it is highly recommended to return the content which was passed in originally, otherwise content from other applications may be lost.

Arguments

Field Type Description
existing Array<UIPipelineResponse> The current content in the UI Pipeline
context UIPipelineContext Information about the Shopfront context when this listener was fired

Returns

  • Array<UIPipelineResponse>:
Field Type Description
name string The name of the content
content string The raw html that is being inserted

UIPipelineContext

Field Type Description
location string The location when the listener was fired
trigger () => void A callback that will refresh the UI Pipeline when called
UI Pipeline
application.addEventListener("UI_PIPELINE", (existing, context) => ({
    if (context.location === "shopfront:sell:transaction:products:pre") {
        const content = `<p>Your new content</p>`
        existing.push({
            "My new content",
            content
        });
    }

    return existing;
});

SALE_PRE_FINISH_PIPELINE

The SALE_PRE_FINISH_PIPELINE allows you to execute code and modify the sale state after a sale has entered the completing stage, but before it is fully finished, you can use this to do things like activate gift cards or perform modifications to the sale.

If you don't want the sale to complete, you should return false which will then exit.

Data isn't verified after the state is returned from this event, so be careful to maintain the same shape of the object that was passed in.

This can be called multiple times for a single sale, other events in the pipeline may reject the sale which can lead to the sale being attempted again.

All types of sales will trigger this, so make sure you check the status of the sale before performing actions on it.

Arguments

Field Type Description
sale SaleState The current sale to be processed through the sale pipeline
context PreSalePipelineContext Information about the current state of Shopfront when this listener was fired

Returns

  • SaleState or false

PreSalePipelineContext

Field Type Description
user string | false The user who performed the sale
register string The register the sale was performed on, this can result in an empty string if it is unknown
Pre Sale Finish Pipeline
application.addEventListener("SALE_PRE_FINISH_PIPELINE", (sale, context) => {
    if(shouldFailSale(sale, context)) {
        return false;
    }

    return {
        ...sale,
        notes: {
            ...sale.notes,
            internal: `${sale.notes.internal}\nAdded an internal note`,
        },
    };
});

INTERNAL_PAGE_MESSAGE

This event occurs whenever one of your custom pages (such as a sell screen option, or the settings page) sends your application an internal message. You can reply to the message by using the provided reference's send method.

We expect you verify the contents of the message before processing it to ensure the message came from your application page.

Arguments

  • InternalPageMessageEvent:
Field Type Description
method string The method that this message is coming from, e.g. REQUEST_SELL_SCREEN_OPTIONS, REQUEST_SETTINGS
url string The URL that the message originated from, this will be the URL you originally provided
clientId string The client ID of the custom page that the message was sent from
message any The message your application has sent
reference Object A reference to the sender, you can use it to reply and send future messages to your page
reference.send (message: any) => void A function that takes a message to send to the custom page

Returns

  • None
Internal Page Message (Embedded Application)
application.addEventListener("INTERNAL_PAGE_MESSAGE", event => {
    if(event.url !== process.env.CUSTOM_PAGE_URL || event.clientId !== process.env.CLIENT_ID) {
        return;
    }

    if(typeof event.message !== "object") {
        return;
    }

    if(event.method === "REQUEST_SELL_SCREEN") {
        if(event.message.type === "PING") {
            // You can keep a copy of event.reference around if you want
            // to send more messages without having to send them from the
            // custom page first.
            event.reference.send({
                type: "PONG",
            });
        }
    }
});

INTERNAL_PAGE_MESSAGE (custom page side)

This event is separate from the embedded bridge, but it allows you to receive messages from your embedded application. Before you're able to receive a message, you must first send a message to your application (in order for the embedded bridge to obtain a reference to your page).

Communication occurs using the browser's built-in postMessage API.

To send a message you must perform a postMessage to the parent window (which is a Shopfront window for the Vendor that has integrated the application), the message must be an object with the following structure:

Field Type Description
type "INTERNAL_PAGE_MESSAGE" Must be the string "INTERNAL_PAGE_MESSAGE"
from string The URL that you are sending this from, it must match the original URL that your application was loaded on
clientId string Your application's client ID
message any Typically an object, this is the message to be sent to your application

Once the message has been sent, your embedded application will receive it using the INTERNAL_PAGE_MESSAGE event and can respond using the provided reference. To receive a reply you must listen for messages on the Window. Messages will be received as a MessageEvent with the following object as the data property:

Field Type Description
type "INTERNAL_PAGE_MESSAGE" Will be the string "INTERNAL_PAGE_MESSAGE"
url string Your settings URL that the message is for
clientId string Your application's client ID
message any Typically an object, this is the message that has been sent from your application
Internal Page Message (Custom Page)
window.addEventListener("message", event => {
    if(event.origin !== "https://<vendor>.onshopfront.com") {
        return;
    }

    if(typeof event.data !== "object") {
        return;
    }

    if(event.data.url !== process.env.CUSTOM_PAGE_URL || event.data.clientId !== process.env.CLIENT_ID) {
        return;
    }

    if(event.data.type !== "INTERNAL_PAGE_MESSAGE") {
        return;
    }

    if(typeof event.data.message !== "object") {
        return;
    }

    if(event.data.message.type === "PONG") {
        console.log("Communication Successful!");
    }
}, false);

window.parent.postMessage({
    type    : "INTERNAL_PAGE_MESSAGE",
    from    : process.env.CUSTOM_PAGE_URL,
    clientId: process.env.CLIENT_ID,
    message : {
        type: "PING",
    },
}, "https://<vendor>.onshopfront.com");

GIFT_CARD_CODE_CHECK

Listening to this event (GIFT_CARD_CODE_CHECK) allows you check the gift card code being used in Shopfront against the rules of your application.

If a conflict is found, a message should be returned from your application.

For example, it is used to verify that the Shopfront's in-built gift card code, entered on the sell screen, won't conflict with codes reserved by third-party integrations.

Arguments

Field Type Description
code string The gift card code to be checked.
message string | null A message containing the information/error returned by the application - null if no conflict

Returns

Field Type Description
code string The gift card code to be checked.
message string | null A message containing the information/error returned by the application - null if no conflict
Gift Card Code Check
application.addEventListener("GIFT_CARD_CODE_CHECK", async event => {
    const reservedRange = ["00001002", "00002002"]

    if (event.message !== null) {
        return event
    }

    const codeInt = parseInt(event.code);

    if (codeInt >= parseInt(reservedRange[0]) && codeInt <= parseInt(reservedRange[1])) {
        // Check to make sure the length of the cardNumber is within Card Ranges length
        const minLength = reservedRange[0].length;
        const maxLength = reservedRange[1].length;

        if (event.code.length >= minLength && event.code.length <= maxLength) {
            return {
                code   : event.code,
                message: `Your Application: Gift card code refused - ${event.code} is within a reserved range of values.`
            };
        }
    }

    return event;
});

Direct Events

Shopfront allows you to plug in to several internal events in order to react to changes made by the user. These events don't provide any parameters nor do they expect any return value.

The currently supported events are:

  • SALE_ADD_PRODUCT
  • SALE_REMOVE_PRODUCT
  • SALE_CHANGE_QUANTITY
  • SALE_UPDATE_PRODUCTS
  • SALE_ADD_CUSTOMER
  • SALE_REMOVE_CUSTOMER
  • SALE_RETRIEVE
  • SALE_CLEAR

If you wish to hook into any trigger not listed above, feel free to reach out to us.

Direct Events
application.addEventListener("SALE_PRODUCT_ADD", () => {
    // Do something...
});