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.

  1. Verify X-Hint-Signature exactly as described in Webhooks Security.
  2. Create a session_key (your session identifier) and persist any session state you need.
  3. 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-Options header. Many frameworks set X-Frame-Options: SAMEORIGIN by default (Rails, Django, and Express with helmet), and it blocks the frame even when frame-ancestors allows it.
  • If your page already sends a Content-Security-Policy with a frame-ancestors directive, 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.currentPatient your surface was opened with.
  • Store your own records keyed by practice and patient IDs.
  • Use the access_token from the handshake session to call Hint APIs and record actions with a user audit trail.

Deep dives


What’s Next

Did this page help you?