Usage
Configuration
All of the embedded offer framework packages consume a standard config object for setting up the offer.
export interface Config {
/**
* Client ID part of your API key.
*/
client_id: string;
/**
* Theme options
*/
theme?: Theme;
/**
* A unique id for this offer. This value should be defined if there are
* multiple offers being presented on a single page. You can typically
* set this value to an order id, or an item id.
*/
unique_offer_id?: string;
/**
* Payment configuration
*/
payments?: PaymentElementConfig;
/**
* Customer details. If this customer is provided, it will override any customer
* defined in any specific product configuration.
*/
customer?: Customer;
/**
* Configuration / quote information for various products
*/
product_config?: ProductConfig;
}
Product Config
The product_config
field takes an object of product codes with an array of quote requests. These quote requests
always map to one of our individual quote request endpoints. As an example, to create a quote for Ticket Refund
protection, you would supply the product code that is part of the quote endpoint, and an array of quote objects. The
endpoint for quoting this product is /v1/quote/ticket-refund
with the product code being ticket-refund
product_config: {
"ticket-refund": [{
customer: {
email_address: "user@example.com",
state: "MN",
country: "US",
postal_code: "string",
first_name: "string",
middle_name: "string",
last_name: "string",
phone_number: "string",
street: "string",
city: "string"
},
metadata: {},
partner_id: "string",
policy_attributes: {
insurable_amount: 2500,
insurable_amount_currency: "USD",
event_start_date: "2024-07-29",
event_end_date: "2024-07-29"
},
}]
}
Metadata
The quote objects inside the product_config
field take a field called metadata. This is a dictionary
which can be used to supply additional data about the request.
product_config: {
"ticket-refund": [{
customer: {
email_address: "user@example.com",
state: "MN",
country: "US",
postal_code: "string",
first_name: "string",
middle_name: "string",
last_name: "string",
phone_number: "string",
street: "string",
city: "string"
},
metadata: {
context_name: "Dinger Baseball",
context_event: "Girls Baseball Summer 2024"
},
partner_id: "string",
policy_attributes: {
insurable_amount: 2500,
insurable_amount_currency: "USD",
event_start_date: "2024-07-29",
event_end_date: "2024-07-29"
},
}]
}
- JavaScript
- React
- Vue
You'll notice the reference for #offer
when instantiating the VerticalInsure
object below. This is
a document query selector and will be the element which we will mount the embedded offer.
<!-- Element where the offer will be mounted -->
<div id="offer"></div>
<script>
new VerticalInsure("#offer", {
client_id: "test_********************************",
product_config: {
"gap-medical": [{
"customer": {
"email_address: user@example.com",
"first_name: James",
"last_name: Doe",
"state: MN",
"postal_code: 55432"
},
"policy_attributes": {
"coverage_end_date": "YYYY-MM-DD",
"coverage_start_date": "YYYY-MM-DD",
"coverage_type": "SOCCER",
"covered_person": {
"birth_date": "YYYY-MM-DD",
"state": "MN",
"first_name": "John",
"last_name": "Doe",
"street": "NA"
}
}
}]
}
});
</script>
import { VerticalInsure } from '@vertical-insure/embedded-offer-react';
export default function App() {
const ref = useRef(null);
return (
<VerticalInsure
ref={ref}
config={{
client_id: "test_********************************",
product_config: {
"gap-medical": [{
"customer": {
"email_address: user@example.com",
"first_name": "James",
"last_name": "Doe",
"state": "MN",
"postal_code": "55432"
},
"policy_attributes": {
"coverage_end_date: YYYY-MM-DD",
"coverage_start_date: YYYY-MM-DD",
"coverage_type: SOCCER",
"covered_person": {
"birth_date: YYYY-MM-DD",
"state": "MN",
"first_name": "John",
"last_name": "Doe",
"street": "NA"
}
}
}]
}
}}
/>
)
}
<script lang="ts" setup>
import { ref } from 'vue'
import { VerticalInsure } from "@vertical-insure/embedded-offer-vue";
const config = ref({
client_id: "test_********************************",
product_config: {
"gap-medical": [{
"customer": {
"email_address": "user@example.com",
"first_name": "John",
"last_name": "Doe",
"state": "MN",
"postal_code": "55414"
},
"policy_attributes": {
"coverage_end_date": "2025-06-25",
"coverage_start_date": "2025-01-25",
"coverage_type": "SOCCER",
"covered_person": {
"birth_date": "2010-07-05",
"state": "MN",
"first_name": "John",
"last_name": "Doe",
"street": "NA"
}
}
}]
}
})
</script>
<template>
<div>
<VerticalInsure :config="config" />
</div>
</template>
Registering your domains
In order to use the embedded offer, you will need to add your domain in the Partner Portal (Developers -> Settings).
localhost
and 127.0.0.1
are automatically allowed for development purposes. If your domain isn't added, you will see console warnings letting you know.
Listening for Events
The embedded offer packages fire standard JavaScript CustomEvent for the following activities:
offer-ready
- Fired when the offer has finished loading and is ready for user interactionoffer-state-change
- Fired when the user interacts with the offer and changes their selections
You can listen to these events using the standard addEventListener approach.
// Listen for when the offer is ready
window.addEventListener("offer-ready", (e) => {
console.log("Offers available:", e.detail.offersAvailable);
});
// Listen for state changes when user interacts with the offer
window.addEventListener("offer-state-change", (e) => {
console.log("Offers:", JSON.stringify(e.detail.quotes));
});
The offer-ready
event is fired once when the offer has finished loading and is ready for user interaction. This is useful in case you want to adjust your page outside the embedded component.
The offer-state-change
event is fired upon user interaction with the embedded offer and returns the accepted quotes that the user has selected. This means the event could come back
with an empty array if no quote has been selected.
Our various packages also provide a standard way to subscribe to these events which provides an abstraction on top of the basic addEventListener
functionality.
- JavaScript
- React
- Vue
<!-- Element where the offer will be mounted -->
<div id="offer"></div>
<script>
new VerticalInsure("#offer", {
client_id: "test_********************************",
product_config: {
"gap-medical": [{
"customer": {
"email_address": "user@example.com",
"first_name": "James",
"last_name": "Doe",
"state": "MN",
"postal_code": "55432"
},
"policy_attributes": {
"coverage_end_date": "YYYY-MM-DD",
"coverage_start_date": "YYYY-MM-DD",
"coverage_type": "SOCCER",
"covered_person": {
"birth_date": "YYYY-MM-DD",
"state": "MN",
"first_name": "John",
"last_name": "Doe",
"street": "NA"
}
}
}]
}
},
function(offerState) {
console.log("Quotes:", JSON.stringify(offerState.quotes));
},
function(offerReady) {
console.log("Offer is ready for interaction");
console.log("Offers available:", offerReady.offersAvailable);
});
</script>
import { VerticalInsure } from '@vertical-insure/embedded-offer-react';
export default function App() {
const ref = useRef(null);
return (
<VerticalInsure
ref={ref}
onOfferReady={(offerReady) => {
console.log("Offers available:", offerReady.offersAvailable);
}}
onOfferStateChange={(offerState) => {
console.log("Quotes:", JSON.stringify(offerState.quotes));
}}
/>
)
}
<script lang="ts" setup>
import { ref } from 'vue'
import { VerticalInsure } from "@vertical-insure/embedded-offer-vue";
const config = ref({
client_id: "test_********************************",
product_config: {
"gap-medical": [{
"customer": {
"email_address": "user@example.com",
"first_name": "John",
"last_name": "Doe",
"state": "MN",
"postal_code": "55414"
},
"policy_attributes": {
"coverage_end_date": "2025-06-25",
"coverage_start_date": "2025-01-25",
"coverage_type": "SOCCER",
"covered_person": {
"birth_date": "2010-07-05",
"state": "MN",
"first_name": "John",
"last_name": "Doe",
"street": "NA"
}
}
}]
}
})
function offerReadyHandler(offerReady) {
console.log("Offers available:", offerReady.offersAvailable);
}
function offerStateChangeHandler(offerState) {
console.log("Quotes:", JSON.stringify(offerState.quotes));
}
</script>
<template>
<div>
<VerticalInsure
:config="config"
@offer-ready="offerReadyHandler"
@offer-state-change="offerStateChangeHandler"
/>
</div>
</template>
Actions
The embedded offer supports 3 different methods that can be used to interact with the offer in various ways.
- validate: Validates the form in the offer to determine if the user has completed selecting required choices.
- tokenizePaymentMethod: If payments are configured on the offer, tokenizes the payment method into a PCI-compliant token for transport to your backend.
- update: Updates the offer with new data for the quote.
With React and Vue, you don't need to explicitly perform update commands, as updating the input config update will sync these changes automatically.
- JavaScript
- React
- Vue
<div id="offer"></div>
<button id="validate-button">Validate</button>
<button id="tokenize-button">Tokenize Payment Method</button>
<button id="update-button">Update Quote</button>
<script>
const verticalInsure = new VerticalInsure("#offer", {
client_id: "test_********************************",
product_config: {
"gap-medical": [{
"customer": {
"email_address": "user@example.com",
"first_name": "James",
"last_name": "Doe",
"state": "MN",
"postal_code": "55432"
},
"policy_attributes": {
"coverage_end_date": "YYYY-MM-DD",
"coverage_start_date": "YYYY-MM-DD",
"coverage_type": "SOCCER",
"covered_person": {
"birth_date": "YYYY-MM-DD",
"state": "MN",
"first_name": "John",
"last_name": "Doe",
"street": "NA"
}
}
}]
}
},
function(offerState) {
console.log("Quotes:", JSON.stringify(offerState.quotes));
},
function(offerReady) {
console.log("Offers available:", offerReady.offersAvailable);
});
const validateButton = document.getElementById("validate-button");
validateButton.addEventListener("click", async (e) => {
e.preventDefault();
const status = await verticalInsure.validate();
console.log(status);
});
const tokenizeButton = document.getElementById("tokenize-button");
tokenizeButton.addEventListener("click", async (e) => {
e.preventDefault();
const token = await verticalInsure.tokenizePaymentMethod();
console.log(token);
});
const updateButton = document.getElementById("update-button");
updateButton.addEventListener("click",(e) => {
e.preventDefault();
verticalInsure.update({
"gap-medical": [{
"customer": {
"email_address": "user@example.com",
"first_name": "James",
"last_name": "Doe",
"state": "MN",
"postal_code": "55432"
},
"policy_attributes": {
"coverage_end_date": "YYYY-MM-DD",
"coverage_start_date": "YYYY-MM-DD",
"coverage_type": "SOCCER",
"covered_person": {
"birth_date": "YYYY-MM-DD",
"state": "MN",
"first_name": "John",
"last_name": "Doe",
"street": "NA"
}
}
}]
});
});
</script>
import { VerticalInsure } from '@vertical-insure/embedded-offer-react';
export default function App() {
const ref = useRef(null);
return (
<>
<VerticalInsure
ref={ref}
onOfferReady={(offerReady) => {
console.log("Offers available:", offerReady.offersAvailable);
}}
onOfferStateChange={(offerState) => {
console.log("Quotes:", JSON.stringify(offerState.quotes));
}}
/>
<button onClick={() => ref.current?.validate()}>Validate</button>
<button onClick={() => ref.current?.tokenizePaymentMethod()}>Tokenize Payment Method</button>
</>
)
}
<script lang="ts" setup>
import { ref, useTemplateRef } from 'vue'
import VerticalInsure from "./components/VerticalInsure.vue";
import { OfferStateChangeEvent } from '@vertical-insure/embedded-offer';
const config = ref({
client_id: "test_RGMDV4FV4BNK4TSPT7DOQVC3P9HKEXTQ",
product_config: {
"gap-medical": [{
"customer": {
"email_address": "user@example.com",
"first_name": "John",
"last_name": "Doe",
"state": "MN" as const,
"postal_code: 55414"
},
"policy_attributes": {
"coverage_end_date": "2025-06-25",
"coverage_start_date": "2025-02-25",
"coverage_type: SOCCER",
"covered_person": {
"birth_date": "2010-07-05",
"state": "MN",
"first_name": "John",
"last_name": "Doe",
"street": "NA"
}
}
}]
}
});
const viRef = useTemplateRef("verticalInsure");
function validate() {
viRef.value?.verticalInsure?.validate();
}
function tokenize() {
viRef.value?.verticalInsure?.tokenizePaymentMethod();
}
function readyHandler(offerReady) {
console.log("Offers available:", offerReady.offersAvailable);
}
function changeHandler(offerState: OfferStateChangeEvent) {
console.log("Quotes:", JSON.stringify(offerState.quotes));
}
</script>
<template>
<div>
<VerticalInsure
:config="config"
ref="verticalInsure"
@ready="readyHandler"
@change="changeHandler"
/>
<button @click="validate">Validate</button>
<button @click="tokenize">Tokenize</button>
</div>
</template>
Conversion Tracking
Conversion tracking is built into the offer without any action required from you.
If your flow requires an additional action to see an offer (for example a "Get Quote" button), please use VerticalInsure.trackOffer(client_id)
to obtain an offer_id
.
You can then pass the obtained offer_id
into any embedded offers you present via the config.tracking_offer_id
. Doing this will allow us to present and optimize an accurate conversion rate.
- HTML
- React
- Vue
<script>
// Track the offer when user clicks "Get Quote"
async function handleGetQuote() {
const trackingOfferId = await VerticalInsure.trackOffer('test_********************************');
// Initialize the embedded offer with tracking
new VerticalInsure("#offer", {
client_id: "test_********************************",
tracking_offer_id: trackingOfferId,
product_config: {
"registration-cancellation": [{
"customer": {
"email_address": "user@example.com",
"first_name": "John",
"last_name": "Doe",
"state": "MN",
"postal_code": "55414"
},
"policy_attributes": {
"event_end_date": "2025-06-25",
"event_start_date": "2025-01-25",
"insurable_amount": 50000,
"participant": {
"first_name": "John",
"last_name": "Doe"
}
}
}]
}
});
}
// Example button that triggers tracking
document.getElementById('get-quote-btn').addEventListener('click', handleGetQuote);
</script>
import { VerticalInsure } from '@vertical-insure/embedded-offer-react'
import { useVerticalInsureOfferTracking } from '@vertical-insure/embedded-offer-react'
export default function App() {
const { offerId } = useVerticalInsureOfferTracking('test_********************************');
return (
<VerticalInsure
onOfferStateChange={(offerState) => console.log(offerState)}
config={{
client_id: "test_********************************",
tracking_offer_id: offerId,
product_config: {
"registration-cancellation": [{
"customer": {
"email_address": "user@example.com",
"first_name": "John",
"last_name": "Doe",
"state": "MN",
"postal_code": "55414"
},
"policy_attributes": {
"event_end_date": "2025-06-25",
"event_start_date": "2025-01-25",
"insurable_amount": 50000,
"participant": {
"first_name": "John",
"last_name": "Doe"
}
}
}]
}
}}
/>
)
}
<script lang="ts" setup>
import { ref } from 'vue'
import { VerticalInsure } from "@vertical-insure/embedded-offer-vue";
import { useVerticalInsureOfferTracking } from "@vertical-insure/embedded-offer-vue";
const { offerId } = useVerticalInsureOfferTracking('test_********************************');
const config = ref({
client_id: "test_********************************",
tracking_offer_id: offerId,
product_config: {
"registration-cancellation": [{
"customer": {
"email_address": "user@example.com",
"first_name": "John",
"last_name": "Doe",
"state": "MN",
"postal_code": "55414"
},
"policy_attributes": {
"event_end_date": "2025-06-25",
"event_start_date": "2025-01-25",
"insurable_amount": 50000,
"participant": {
"first_name": "John",
"last_name": "Doe"
}
}
}]
}
})
</script>
<template>
<VerticalInsure :config="config" />
</template>
Theming
The standard config object accepts the following theme input
export interface Theme {
colors?: {
background?: string;
primary?: string;
primary_contrast?: string;
secondary?: string;
secondary_contrast?: string;
neutral?: string;
neutral_contrast?: string;
error?: string;
error_contrast?: string;
success?: string;
success_contrast?: string;
border?: string;
},
font_family?: string;
components?: {
border_radius?: string;
}
}
Fonts
The font_family
field can accept any Google font name. For example, the following config will use Roboto
as the standard font in the offer.
{
theme: {
font_family: 'Roboto'
}
}