Skip to main content

Setting Up

Getting Started with Integration

After you are given partnerId and partnerToken for Sandbox environment, you are ready to start integrating HAPI Elements into your application.


When your integration is complete with Sandbox environment, we will do some checks mentioned in Releasing to Production to test & verify that your integration, then you will be given different partnerId and partnerToken for the Production environment then you will need to switch the URLs from Sandbox Environment to Production Environment.

partnerToken security best practices

caution

partnerToken is used to authenticate your users by generating a JWT token, and it should never be exposed in your frontend application. You need to store partnerToken securely in your backend; we suggest storing it in environment variables of your backend code. Your frontend application then can make a request to your own backend to authenticate the user.

Sandbox Environment

This environment, has the same code and database structure as Production in both HAPI Backend and HAPI Elements but the databases itself is different so that the test data is isolated on Sandbox environment and does not get carried over to the Production environment.


The VONQ Taxonomy data such as Job Functions, Locations, Industries are exactly the same as Production environment so that if you have code that works with id of such taxonomies, when you switch to Production environment, your code will work as is.


Please note that the data you create/manipulate in Sandbox environment, will not get carried to Production environment as the data on Sandbox environment is merely for testing.

Sandbox Environment URLs

  • API URL to generate the JWT token on your backend:
    • https://marketplace-sandbox.api.vonq.com
  • HAPI Elements URL to load Elements via injector.js
    • https://elements-sandbox.hapi.vonq.com

Production Environment

For this environment, you should have been given partnerId and partnerToken that is different than Sandbox environment, you will use these with the below URLs:

  • API URL to generate the JWT token on your backend:
    • https://marketplace.api.vonq.com
  • HAPI Elements URL to load Elements via injector.js:
    • https://elements.hapi.vonq.com
caution

It is really important that you keep partnerToken safe somewhere in your environment variables. partnerToken should never leak to the public!

note

Sandbox environment is a testing environment thus any money related transaction and any campaign ordered is not actually effective. Money related transactions (such as when topping-up the Wallet) are done using a test credit card and the end user does not receive an invoice.


On Sandbox environment, the campaigns ordered are not sent to the job boards as most job boards do not allow posting "test" campaigns therefore no real campaign is created and the contract's credits are not deducted.

Production Environment

After we do the checks mentioned in Releasing to Production and confirmed that your integration is working, we will ask you to provide us a hash (string or object) to be used as part of JWT HS-256 token generation; then give you new partnerId and partnerToken.


You will then change your partnerId and partnerToken in your application as well as the URLs from Sandbox to Production URLs.

Production Environment URLs

  • API URL to generate the JWT token on your backend:
    • https://marketplace.api.vonq.com
  • HAPI Elements URL to load Elements via loader.js:
    • https://elements.hapi.vonq.com

Making a request to authenticate your users

You need the following requisites:

  • an API endpoint on your backend to authenticate your users with HAPI Backend
  • an API request made from your frontend to your backend to the auth endpoint (optional if you are utilizing sessions)
caution

Keep in mind that partnerToken should never be exposed in your frontend application, please refer to partnerToken security best practices section for more info.


A request needs to be made on your backend to generate a JWT token for your user. An example is given below for Typescript application using axios package.


Loading...


The clientToken should be sent as a response so frontend code can pass them to the injector script to load HAPI Elements.

info

We recommend getting a new token per user session; session referring to user logging in or reopening their browser, and your application has just been freshly mounted.

Note about JWT Expiry

The JWT token expires every hour; thus, you might need to get a new JWT token each time you want to load HAPI Elements. You can adjust your business logic to refresh the token as needed. HAPI Elements does an automatic token refresh request every 5 minutes after it has been loaded and updates the token internally.

Listening to JWT Token Refresh

To be aware of when token has been refreshed (so that for example you can adjust your Cookies or LocalStorage) you can add an event listener as such:

window.hapiApi.onChangeJWTToken((newToken: string, oldToken: string) => {
// do some logic with the new token like saving to Cookies or LocalStorage
})

Refreshing the JWT Token Manually

To refresh the token manually, you can call the below function:

const tokenResponse = await window.hapiApi.refreshJWTToken()
// New token after refresh
// Please note that, if you added a listener callback via onChangeJWTToken
// your callback will also run
console.log('HAPI Elements JWT Refresh Token Response:', tokenResponse?.token)

Changing Auto JWT Refresh Token Interval in Seconds

By default, HAPI Elements will auto-refresh the JWT token every 5 minutes. To change this timer, you can do:

const intervalInSeconds = 60 * 30 //refresh every thirty minutes
window.hapiApi.setJWTRefreshIntervalInSeconds(intervalInSeconds)

Please note that you cannot specify more than an hour for JWT refresh, trying to set for example 60 * 61 (every 61 minutes) will result in an error saying "Number must be less than 3600".

Validating the JWT Token

You can also check if your token is still valid by making an API request as such:


Loading...

Or you can also check if token is valid, after HAPI Elements has been loaded by using the API submodule:

const isValid = await window.hapiApi.validateJWTToken()
console.log('Is HAPI Elements JWT Token Valid?:', isValid)

Understanding what clientId refers to

Tenant Based

Your application may have "tenants" in which there are sub-users with different sets of permissions. clientId refers to the "tenant" so that all the sub-users of the tenant can share the same set of data.


So all the users of a tenant must have the same shared clientId.


To learn how to manage access control within Elements Widgets, please look at Implementing Access Control.

User Based

If your application is not tenant-based, meaning that you are directly working with recruiters doing the job posting, then the clientId refers to the "user".

Understanding multi tenancy

When loading HAPI Elements, the clientId passed is for data separation purposes however you may in some cases want to run conditional logic based on user / tenant type. You are able to do that after you successfully integrate a widget to your page. This method works for every variable/function HAPI Elements provides. Here is how it would look like:

const currentTenant = "tenant-b"
const idOfTenantA = "tenant-a"
const idOfTenantB = "tenant-b"

const themeMap = {
tenantA: {
//...theme object
},
tenantB: {
//...theme object
}
}

// or payment methods example
const enabledPaymentMethodsMap = {
tenantA: [
window.hapi.orderJourney.utils.paymentMethodKeys.purchaseOrder,
],
tenantB: [
window.hapi.orderJourney.utils.paymentMethodKeys.wallet,
window.hapi.orderJourney.utils.paymentMethodKeys.directCharge
],
}

const resetSettings = () => {
window.hapi.theming.state.theme = themeMap[currentTenant]
window.hapi.orderJourney.state.paymentMethodsEnabled = enabledPaymentMethodsMap[currentTenant]
}

const onAppLoad = () => {
resetSettings()
}

const onTenantChange = () => {
resetSettings()
}

Including the HAPI Elements Script

note

Make sure to include all of HAPI Elements integration related code below at top-level of your application so that these only run once per application load.

Let's first create a setup function that will be used to set up credentials before HAPI Elements is loaded.

const onLoadHapiElementsInjector = (clientToken: string) => {
window.hapiInjector.setConfig("clientToken", clientToken)
window.hapiInjector.inject()
}

Then add an event listener so that we can invoke the above function when the script we will add in the following step, has loaded successfully:

const clientToken = "the JWT token you generated via your backend"

window.addEventListener(
"hapi:load:injector",
() => onLoadHapiElementsInjector(clientToken)
)

Then include injector.js script in your HTML:

Loading...

Injector will take care of loading HAPI Elements script for you. When HAPI Elements loads, it will fire another event, you can then start using HAPI Elements Javascript API:

const onLoadHapiElements = () => {
console.log('[HAPI] Elements has loaded', window.hapi)
}

window.addEventListener("hapi:load:script", onLoadHapiElements)

Alternatively you can also use window.hapiInjector.inject() function to detect the load of HAPI Elements, this way, you don't need to listen to hapi:load:script event:

const onLoadHapiElementsInjector = async (clientToken: string) => {
try {
window.hapiInjector.setConfig("clientToken", clientToken)
// Inject is a Promise that resolves when HAPI Elements script has successfully loaded
await window.hapiInjector.inject()
// You can now start using HAPI Elements Javascript API
console.log('[HAPI] Elements has loaded', window.hapi)
} catch (error) {
console.error(error)
}
}
Programmatic way of adding the script with POST request
note

injector.js under the hood prepares the config required by HAPI Elements script to load then via the inject function on window.hapiInjector, HAPI Elements script is injected into the DOM.


If you don't want to use injector.js, you can manually inject loader.js as follows:

Loading...

If your integration setup & authentication was successful, you should see the widget; otherwise, the widget will hide itself with a big red text explaining your token is invalid and post console errors in the developer tools of your browser.

A note about Content Security Policy (CSP)

In case you have CSP rules in your headers, you would need to add the following as part of the allowed list of hosts:

frame-src 'self' https://*.vonq-aws.com https://*.vonq.com
connect-src 'self' https://*.vonq-aws.com https://*.vonq.com

vonq-aws.com is used for development/testing environments of VONQ.


If you get the following CSP error;

Refused to execute inline script because it violates the following Content Security Policy directive: "script-src (...). Either the 'unsafe-inline' keyword, a hash ('sha256-vsEPtD4ipuOFerJfbP6WWYepfR9645C8YZOl5YYqaXo='), or a nonce ('nonce-...') is required to enable inline execution.

You should do the following:

  1. First create a nonce and whitelist it in your meta tags. You can use a hardcoded nonce or generate it every time you want to load HAPI Elements scripts. Modify your meta tags by replacing NONCE-YOU-GENERATED with the hardcoded one or the one you actually generated. The generated string can be anything random.
<!--
Assume that the nonce we generated is 'hapi-elements'
then meta tag would have 'nonce-hapi-elements' (including the single quotes)
-->
<meta
http-equiv="Content-Security-Policy"
content="script-src 'self' YOUR-OTHER-POLICIES 'nonce-NONCE-YOU-GENERATED'" />
  1. Add nonce attribute to the script tag with injector.js
<script
defer
type="module"
nonce="NONCE-YOU-GENERATED"
src="https://elements-environment-url.vonq.com/api/injector.js"
></script>

By doing this, you are whitelisting only the injector.js and loader.js scripts. When you call window.hapiInjector.inject(), it injects loader.js script to your DOM and that will reference the same nonce as the script tag with loader.js

Installing Typescript definitions from NPM to your Typescript application (Optional)

If you have a Typescript application and want to utilize the type definitions, you can do so:


To get started using the types, install the package as a dev dependency:

Using NPM:

npm install @vonq/hapi-elements-types --save-dev

Using Yarn:

yarn add -D @vonq/hapi-elements-types

All the types have been exported in a single file so you can get started using the types as such:

import {CampaignCreateForm} from "@vonq/hapi-elements-types";

const campaignObject: CampaignCreateForm = {} //Typescript will now warn about missing properties
note

If you are using React with TypeScript, you will most likely need to add typings for web components. Create a global.d.ts in the root folder of your project then include this file in the include array of tsconfig.json. Then place these inside global.d.ts:

import * as React from "react" //do not remove this

declare global {
namespace JSX {
interface IntrinsicElements {
[key: string]: any
}
}
}
note

If you get this error:

Module parse failed: Unexpected token (20:7)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
| import { ATSUserTokenResponse } from "../ats"

You may need to exclude HAPI Elements from ts-loader in webpack.config.jsas such:

{
//...rest of the webpack config
module: {
rules: [
{
test: /\.tsx?$/,
use: "ts-loader",
exclude: /node_modules\/(?!\@vonq\/hapi-elements-types).*/, //changed this so Elements types are not excluded
},
]
}

FAQs

Can we install HAPI Elements via a Javascript Package Manager instead of loading with injector.js?

We do not have an installable package and have no plans to release such a package. We architected HAPI Elements in a way that works for all frameworks, and we don't want to be in favour of ATSs using React versus any other framework; thus, the architecture with iframes inside web components and the SDK to manage the Elements application does an excellent job of providing the same developer experience.


The goal of HAPI Elements is to be as-maintenance free as possible. The autocomplete behaviour on the browser's developer tools, the optional Typescript definitions package and the Playground in the documentation should be enough to help you implement your business requirements.

We are getting 401 errors with a message saying ‘Authentication Required’ from the APIs; how can we resolve this?

There may be something wrong with your credentials and/or permissions. Please get in touch with your Partner Account Manager to get this investigated.