Embedding the white-label UI (v2)

What you get

You generate a short-lived token for one of your users on the server side, then point an <iframe> (or a browser window) at the Worklio application with that token. Worklio signs the user in and lands them exactly where you asked — on a whole page, or on a single self-contained piece of functionality.

There are two building blocks:

ConceptWhat it isHow you ask for it
PageA full application page (Dashboard, Team, Payroll, Employee detail…)page_name request property
Named flowA single embeddable widget — a form, a modal, a section — without any app chromeflow custom claim

Both are selected inside the signed token rather than as editable URL parameters. The page or flow is therefore not exposed as a separate query parameter, and changing the JWT payload invalidates its signature.

1. Get a client token

In the examples below, {AuthUrl} and {ApiUrl} are the authorization-server and API base URLs Worklio assigned for your environment.

Authenticate your application with the OAuth 2.0 client credentials flow at /connect/token, using the client_id and client_secret Worklio assigned to you and the user_token API scope.

curl --request POST \
     --url '{AuthUrl}/connect/token' \
     --header 'Content-Type: application/x-www-form-urlencoded' \
     --data 'grant_type=client_credentials' \
     --data 'client_id=<your-client-id>' \
     --data 'client_secret=<your-client-secret>' \
     --data 'scope=user_token'

The response contains an access_token — the client token. It identifies your application, not a person. Keep it on your server; never send it to a browser.

2. Exchange it for a user token

POST /token/user/ with the client token as the bearer. The response is the appUrl and the userToken you embed.

Request body

PropertyTypeRequiredDescription
emailstringyes*The Worklio user the token is issued for.
tokenstringyes*Alternative to email — the user's JWT from another trusted authentication server.
page_namestringnoWhich page to open. See Page names. Omit when you use a named flow.
modeintno1 = Browser, 2 = iFrame. Defaults to iFrame. See iFrame mode vs. browser mode.
roleAccessintnoWhich of the user's roles to use: null = highest available, 1 = System (Multi-Admin), 2 = Admin, 3 = Employee.
companyintnoFor users holding the same role at several companies, picks the role for that company. Applies to Admin and Employee roles.
customAppClaimsarrayno{ "type": …, "value": … } pairs carrying whatever the requested page or flow needs. See Custom claims.

* Send exactly one of email or token.

page (integer) is deprecated. v1 numeric page ids still work for backward compatibility, but they will be removed. New integrations must use page_name.

Response

{
  "email": "[email protected]",
  "appUrl": "https://your_app_domain",
  "userToken": "eyJhbGciOiJSUzI..."
}

The appUrl above mirrors the endpoint's documented response. Use the URL returned for your configured environment when constructing the iFrame source. The embedded application reads the token from the URL fragment, so the fragment is not included in the browser's initial HTTP request for the page.

Generate a fresh token for every embed. Flow tokens are short-lived and the embedded app deliberately cannot renew them silently; an expired token ends the session inside the frame.

3. Embed it

<iframe src="https://your_app_domain/flow#eyJhbGciOiJSUzI..."></iframe>

Server-side templating, e.g. Razor:

<iframe src="@Url.Content(appUrl + userToken)"></iframe>

iFrame mode vs. browser mode

iFrame mode (mode: 2, the default) — for embedding inside your own layout.

  • Navigation menu, logo and user menu are hidden.
  • The user is confined to the page or flow the token was issued for.
  • If they navigate deeper (for example from Team into an employee), a Back to starting page link (an ✕ on desktop) returns them to the entry point.
  • The application background is transparent so the frame blends into your page.
  • The token only works inside a frame. Opening an iFrame-mode URL as a top-level document fails with Unallowed Flow Context.

Browser mode (mode: 1) — for opening Worklio in a new window or tab.

  • The full interface is available, including menus; the user can move freely.
  • Works both standalone and inside a frame.
  • There is no Log Out; the user menu offers Close instead.

Showing the menu inside an iFrame

Add the menu custom claim to render the primary navigation inside an iFrame-mode embed. The menu appears, but the logo and the profile controls stay hidden, in the left panel and in the mobile header alike. Use it when you embed a large area and want the user to move around inside it.

{ "type": "menu", "value": true }

The claim is ignored in browser mode, where the full UI is always shown.

curl --request POST \
     --url '{ApiUrl}/token/user/' \
     --header 'Authorization: Bearer <client-token>' \
     --header 'Content-Type: application/json' \
     --data '{
       "email": "[email protected]",
       "page_name": "team",
       "roleAccess": 2,
       "customAppClaims": [
         { "type": "menu", "value": true }
       ]
     }'

Worked example — admin embed

Open one employee's detail for a company administrator, inside an iFrame, with no menu.

curl --request POST \
     --url '{ApiUrl}/token/user/' \
     --header 'Authorization: Bearer <client-token>' \
     --header 'Content-Type: application/json' \
     --data '{
       "email": "[email protected]",
       "page_name": "employee",
       "roleAccess": 2,
       "customAppClaims": [
         { "type": "employee-id", "value": 123 }
       ]
     }'
<iframe
  src="https://your_app_domain/flow#eyJhbGciOiJSUzI..."
  style="width: 100%; height: 900px; border: 0;"></iframe>

Issuing the same page to a Multi-Admin (roleAccess: 1) additionally needs company-id, so the application knows which company to enter:

{
  "email": "[email protected]",
  "page_name": "employee",
  "roleAccess": 1,
  "customAppClaims": [
    { "type": "company-id", "value": 42 },
    { "type": "employee-id", "value": 123 }
  ]
}

Worked example — employee embed

Open an employee's own pay statements in a new browser tab, with the full application available.

curl --request POST \
     --url '{ApiUrl}/token/user/' \
     --header 'Authorization: Bearer <client-token>' \
     --header 'Content-Type: application/json' \
     --data '{
       "email": "[email protected]",
       "page_name": "pay-statements",
       "roleAccess": 3,
       "mode": 1
     }'
window.open(appUrl + userToken, '_blank');

Page names

page_name is resolved against the role the token was issued for, so the same name can mean different things to a Multi-Admin, an Admin and an Employee (for example dashboard). The API accepts only page names enabled for the selected role; a name outside that list fails with 400 PageValidationError. After a token is issued, what the user can see and do within the page still depends on their role and on the products and permissions enabled for the company.

Multi-Admin role

page_namePage
adminsAdmins
companiesCompanies
dashboardDashboard
notificationsNotifications
payroll-historyPayroll History
payrollsPayrolls Overview
reportsReports
white-labelWhite Label

Admin role

Every Admin page can also be opened by a Multi-Admin, in which case company-id must be supplied.

page_namePageRequired claims
benefitsBenefits
calendarCompany Calendar
custom-fieldsCompany Custom HR Fields
dashboardDashboard
documentsCompany Documents
employeeTeam Member detailemployee-id
holiday-groupsCompany Holiday Groups
integrationsIntegrations
journal-entriesCompany Journal Entries
organizationCompany Organization
payrollPayroll
payroll-historyPayroll History
payroll-settingsCompany Payroll Settings
premium-atsPremium ATS
reportsReports
settingsCompany General Settings
surveysMy Surveys
teamTeam
team-attendanceTime and Attendance — Team
team-time-offTime Off — Team
time-off-requestsTime Off Requests
time-off-settingsTime Off Settings
verificationCompany Bank and Verification

Employee role

page_namePage
benefitsBenefits
bulletin-boardBulletin Board
calendarCompany Calendar
change-requestsChange Requests
dashboardDashboard
documentsDocuments
my-surveysMy Surveys
organizationCompany Organization
payPay
pay-statementsPay Statements
personalPersonal
surveysSurveys Assigned To Me
tax-formsTax Forms (W-2 and 1099)
tax-setupTax Setup
time-clockTime and Attendance — Time Clock
time-offMy Time Off
timesheetTime and Attendance — Timesheet

No role

For users who have no company role yet (for example during onboarding).

page_namePage
personalPersonal

One page, one purpose

Each page_name opens one focused area of Worklio. If users need several areas, embed each page where it belongs in your product, or enable Worklio's navigation menu with the menu custom claim. The menu lets users move between the pages available to their role and company without leaving the embed.


Named flows

A named flow renders one self-contained piece of functionality with no page layout around it — the right choice for slotting a Worklio form or modal into your own screen. Request it with the flow custom claim instead of page_name; if both are present, flow wins.

flowRequired claimsWhat it shows
custom-fields-employee-viewemployee-idEmployee's custom HR fields, read-only
custom-fields-employee-editemployee-idEdit modal for custom HR fields
custom-fields-employee-onboarding-phase1onboarding-idCustom HR fields, onboarding step 1
custom-fields-employee-onboarding-phase2company-id, ercuCustom HR fields, onboarding step 2
premium-atscompany-idPremium ATS entry / registration
premium-ats-settingscompany-idPremium ATS settings

Add company-id to any of these when the token is issued to a Multi-Admin.

curl --request POST \
     --url '{ApiUrl}/token/user/' \
     --header 'Authorization: Bearer <client-token>' \
     --header 'Content-Type: application/json' \
     --data '{
       "email": "[email protected]",
       "customAppClaims": [
         { "type": "flow", "value": "custom-fields-employee-view" },
         { "type": "company-id", "value": 1 },
         { "type": "employee-id", "value": 1 }
       ]
     }'

Named flows size themselves to their content and tell you their height — see iFrame events (postMessage).

Not for new integrations

Two flows have been superseded by pages that do the same job. They still ship and still work, so existing integrations need not change — but build new ones on the page. Both log a console warning when used.

FlowUse instead
custom-fields-companypage_name: custom-fields
company-verificationpage_name: verification

Public flows

Some flows are reached by people who are not signed in — a candidate finishing self-onboarding, for example. Those tokens are issued as anonymous tokens: the application skips sign-in entirely, loads no roles or capabilities, and renders only the named flow.

A public flow must therefore

  • use flow (a page-based token has no role to resolve pages against), and
  • carry rcu or ercu so the application can load the right white-label branding.

custom-fields-employee-onboarding-phase2 is the public flow in the table above: it identifies the person by ercu rather than by an employee id.


Custom claims

customAppClaims is a flat list of { "type": …, "value": … } pairs. Three of them steer the embed itself:

typeEffect
flowRenders that named flow instead of a page
menutrue shows the navigation menu in iFrame mode
rcu / ercuIdentifies the branding (and the person, in public flows)

Everything else is passed straight through to the requested page or flow as a property. Both spellings are accepted for the identifiers:

ClaimMeaning
company-id / companyIdCompany to enter. Required for a Multi-Admin opening company content.
employee-id / employeeIdEmployee the page or flow is about.
onboarding-id / onboardingIdOnboarding record for the onboarding flows.

Talking to the embedded application

The embedded application signals back to your host page with postMessage: a keepalive so you can extend your own session, a height report so you can size a named flow to its content, and commands telling you what the user did. Your host page can send commands the other way too.

These events are the same for v1 and v2 embeds, so they have their own page: iFrame events (postMessage).

Verifying your integration

Work through these before you ship. Start with the token because its claims determine which content and behavior the application loads.

1. Check the token says what you meant. Decode the userToken payload locally and confirm its claims match your request. Decoding lets you inspect the payload; it does not verify the token's signature.

ClaimWhat to check
wt_app_page_nameMatches the page_name you requested for a page embed.
app_flowMatches the named flow you requested when you use one.
app_modeIs browser for mode: 1 or iframe for mode: 2. If you omitted mode, this claim may be absent and the application defaults to iFrame mode.
app_menuIs true when you enabled the menu. When it is absent or false, the menu stays hidden in iFrame mode.
role, roleidIdentify the role selected by roleAccess and its specific role assignment. Anonymous flows do not have these claims.
app_<type>Matches each entry in customAppClaims; for example, company-id becomes app_company-id. Camel-case forms such as app_companyId are also accepted.

If these values differ from your request, correct the token request before loading the embed.

2. Confirm the embed renders for the role and company you will really use. The application resolves page names against the selected role, and the available routes still depend on that user's capabilities. A page that works for one test account may be unavailable to another, so test with an account that matches your production case.

3. Exercise the mode you will ship. In iFrame mode, confirm your container gives the frame enough room. Without the menu, also confirm that Back to starting page returns to the requested page after the user navigates deeper. With the menu, confirm that users can reach the intended areas. In browser mode, confirm the window you open is not stopped by a popup blocker.

4. Verify your message handling. Log the message events on the host page and confirm you receive only the messages your integration relies on: activity from any iFrame-mode embed, and resize or command from named flows that send them. Confirm that your event.origin and event.source checks accept messages from the intended frame and reject everything else.

5. Check token lifetime. The embedded application does not renew its token silently. Test the expired-token path and confirm your server can issue a fresh token and reload the embed when needed.

Ask Worklio for access to a test environment if you would rather work through all of this before touching production data.

Troubleshooting

MessageCause
Unallowed Flow ContextAn iFrame-mode token was opened as a top-level document. Issue the token with mode: 1 for that case.
400 PageValidationError from /token/user/The page_name is not on the list the API accepts for that role. Page names are validated per role, so a name valid for an Admin may not exist for an Employee.
The requested flow does not exist.Unknown flow name, or a page_name that has no page for the token's role.
You do not have access to the requested flow.The page exists, but the application did not register it for this user context. Check the selected role and user- or company-specific eligibility.
Access token is expiredFlow tokens are short-lived and are not renewed silently. Generate a new token for each embed.
The frame renders with a transparent backgroundExpected in iFrame mode — it is designed to blend with your page. Paint your own background behind it.

Configuration

Your client_id, client_secret and the URLs of the authorization server, the API and the application are issued by Worklio per environment. See How to get API access, and contact Worklio if anything is missing or wrong.



Did this page help you?