Building an App
Build an embedded app that runs inside Hint. Start with a minimal workflow, then add deeper surfaces and automation.
Marketplace apps are embedded experiences that run inside the Hint UI. You host the app, own the UI and APIs, and Hint handles:
- secure authentication and session handoff
- surfacing your app inside Hint at specific entry points
- passing user, practice, and patient context into your app
- navigation patterns via the JS SDK
Foundation: build an integration
At their foundation, all apps require an integration. The first step in building an App is Building an Integration.
Quickstart: build a minimal app
This tutorial gets you to a working embedded app that:
- accepts the Marketplace handshake from Hint and returns a
session_key - renders a page inside an iframe
- loads the JS SDK and shows practice/user/patient context
Prerequisites
- You can activate your integration for a practice (see Integration Activation).
- You can host two URLs:
- a handshake URL (server endpoint)
- an iframe URL (web page)
Step 1: Handle the Marketplace handshake (server)
Create a handshake endpoint. Hint will POST a signed payload to your handshake URL with the user, practice, integration, installation, product, and a short-lived access token for making Hint API requests as that user session.
- Verify
X-Hint-Signatureexactly as described in Webhooks Security. - Create a
session_key(your session identifier) and persist any session state you need. - Return
session_key. Hint will append it when loading your iframe URL.
Example payload:
{
"timestamp": "2025-10-30T14:20:19.316986-03:00",
"user": {
"id": "user-BtAH8TTXCo6P",
"email": "[email protected]",
"first_name": "Dev",
"last_name": "User",
"name": "Dev User",
"partner_roles": ["admin"]
},
"practice": {
"id": "pra-cmVPOcw7hcIs",
"name": "Example Practice"
},
"integration": {
"id": "int-MicbNQhj6hsv"
},
"installation": {
"id": "inst-9kZ2pQ4mXb1T"
},
"product": {
"id": "ppro-7hLm3nR8sVwK",
"name": "Example App",
"slug": "example-app"
},
"access_token": "practice-scoped-access-token",
"access_context": "standard",
"browser_allow_list": []
}user.partner_roles tells you which roles the user has in your app, and access_context (standard or platform_support) tells you whether this is a regular practice user or a Hint platform-support session. Both are covered in Roles & Access Context - handling platform_support sessions is a required capability.
Example Express endpoint:
app.post("/handshake", (req, res) => {
const signature = req.header("X-Hint-Signature");
verifyHintSignature({ signature, body: req.body }); // reuse your webhook verifier
const sessionKey = createSessionKey(req.body); // your logic
res.json({ session_key: sessionKey });
});Step 2: Build your iframe page (web)
Once the handshake succeeds, Hint loads your iframe URL and appends session_key as a query parameter.
At minimum:
- load the SDK
- call
HintSDK.init()when ready - read context (
HintSDK.user,HintSDK.currentPatient, etc.) - allow Hint to frame the page (see below)
Allow Hint to frame your page
Hint does not frame your page directly. The Hint UI frames an embed page served from https://api.hint.com, and that page frames your iframe URL. Browsers check every ancestor in that chain against your page's frame-ancestors policy, so your policy must list the API origin as well as the Hint UI origins.
Send this header on every page your app loads inside Hint:
Content-Security-Policy: frame-ancestors https://api.hint.com https://app.hint.com https://core.hint.com
- Sandbox and live use the same origins.
- Remove any
X-Frame-Optionsheader. Many frameworks setX-Frame-Options: SAMEORIGINby default (Rails, Django, and Express withhelmet), and it blocks the frame even whenframe-ancestorsallows it. - If your page already sends a
Content-Security-Policywith aframe-ancestorsdirective, add these origins to it. When a page sends several policies, every one of them must allow the frame.
If the origins are missing, the surface shows the browser's "refused to connect" page, and the browser console reports a frame-ancestors violation naming the origin that was not allowed.
Minimal example:
<script src="https://api.sandbox.hint.com/hint-sdk.js"></script>
<script>
const params = new URLSearchParams(window.location.search);
const sessionKey = params.get("session_key"); // your session identifier
function render() {
const user = HintSDK.user;
const patient = HintSDK.currentPatient;
document.getElementById("session").textContent = sessionKey || "(missing)";
document.getElementById("user").textContent = user ? user.email : "(missing)";
document.getElementById("patient").textContent = patient ? patient.name : "(none selected)";
}
HintSDK.init(() => render());
</script>
<div style="font-family: ui-sans-serif, system-ui; padding: 16px">
<h2>Inventory app (quickstart)</h2>
<p><b>session_key</b>: <span id="session"></span></p>
<p><b>user</b>: <span id="user"></span></p>
<p><b>patient</b>: <span id="patient"></span></p>
<button onclick="HintSDK.close()">Close</button>
</div>Next: make it a real workflow
Once the quickstart works:
- Show recommendations in context for the
HintSDK.currentPatientyour surface was opened with. - Store your own records keyed by practice and patient IDs.
- Use the
access_tokenfrom the handshake session to call Hint APIs and record actions with a user audit trail.
Deep dives
Updated about 13 hours ago

