This page is for the engineers who will add HEIP to your site. In short: one script in your page, one call on your server.
Your first verdict in 30 minutes.
One script tag in your page. One call on your server. Start in observe mode, so nothing changes for your users until you are ready.
- Claude Code
- Cursor
- Codex
Paste this into any coding agent
Cursor, Claude Code, Codex, whatever you use. It adds the SDK, wires one protected action, and stops for your real keys.
The SDK and keys come with your HEIP account.
You're adding HEIP to this project. HEIP tells our server whether a real person is doing an important action. Get me to one verified action, nothing more.
1. Install the SDK: npm install heip-sdk (Node.js 18+). It comes with our HEIP account; if the install fails, stop and tell me.
2. Add HEIP_SITE_KEY= and HEIP_SECRET_KEY= to .env, and add .env to .gitignore. Read both from the environment. Never hardcode or print them.
3. Find the one form or button I most want to protect (ask me if it's unclear) and give the action a short name, like "checkout".
4. In the page, initialise the browser SDK with the site key. When that form is submitted, call HEIP.prepareAction("<action>") and send sessionId and actionNonce to the server with the request.
5. In the server route that handles it, before doing the work, create new HeipServer({ secretKey: process.env.HEIP_SECRET_KEY }) and call heip.verify({ sessionId, actionNonce, action }). If allow(result) is true, continue. If result.decision is "STEP_UP", return 401 and leave a TODO for the extra step. Otherwise return 403. verify throws if HEIP can't be reached: catch it and refuse the action. Log result.reasons and result.traceId.
6. Stop here and tell me to paste my keys into .env. Don't work around missing keys: no dummy values, no mocked responses.
7. When I confirm the keys are in, run the app, submit the form once, and show me the decision and mode from the server log.
Rules while you work:
- The site key may reach the browser. The secret key is server-only: never put it in client code, never log or print it.
- Call prepareAction when the protected form is submitted, not on page load. Its token works once and expires in seconds.
- Don't retry verify with the same token, and don't invent HEIP endpoints. The browser SDK handles its own calls.
- Treat any extra fields in the response as diagnostics: log them, don't branch on them.The integration, in four steps
Create a site
List the domains it runs on. You get a public site key and a private secret key.
Add the browser SDK
Load it in your page with the site key.
Get an action token
When the visitor presses a protected button, the page calls prepareAction.
Verify on your server
Your server calls verify with the secret key and reads the decision.
Then read the decision, log the reasons, and decide what to do.
The code
<script src="https://<sdk-host>/sdk.js" data-site-key="pk_live_…" async></script>
<script>
form.addEventListener("submit", async (e) => {
e.preventDefault();
const { sessionId, actionNonce } = await HEIP.prepareAction("checkout");
await fetch("/api/checkout", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ sessionId, actionNonce, ...formData }),
});
});
</script>With a bundler: import { init, prepareAction } from "heip-sdk";
import { HeipServer, allow } from "heip-sdk/server";
const heip = new HeipServer({ secretKey: process.env.HEIP_SECRET_KEY });
app.post("/api/checkout", async (req, res) => {
const { sessionId, actionNonce } = req.body;
const result = await heip.verify({ sessionId, actionNonce, action: "checkout" });
if (result.decision === "STEP_UP") return res.status(401).json({ next: "otp" });
if (!allow(result)) return res.status(403).end();
// ...commit the checkout
});curl -X POST https://<api-host>/v2/verify \
-H "X-HEIP-SECRET-KEY: $HEIP_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{"sessionId":"…","actionNonce":"…","action":"checkout"}'{
"success": true,
"decision": "ALLOW",
"reasons": [],
"mode": "enforce",
"traceId": "…"
}The browser SDK opens the session and sends what it needs by itself. You only call prepareAction in the page and verify on your server.
SDKs
| SDK | Runs on | Status |
|---|---|---|
| Browser, script tag or npm | Desktop and mobile browsers | Available |
| Server | Node.js 18+, ESM and CommonJS | Available |
| WebView | Works in principle, not yet certified | Planned |
| Native iOS and Android | Not yet | Planned |
| Other server languages | Use the HTTPS API | Available |
Policies, per action
You name your actions (login, post, withdraw, change_payout, agent_execute, and so on) and give each a policy. Your server then sends just the action name. HEIP evaluates; you decide what to do.
- Critical
- Withdrawals, payout changes, admin actions
- Fast
- Confirming trades or orders, quick publishing
- Standard
- Sign-up, posting, messaging
- Observe
- Reading and browsing: never blocks
Roll out safely
Observe
Switch the site to observe mode. HEIP records what it would do and never blocks.
Review
Look at decisions and reasons in the dashboard for a week of real traffic.
Enforce
One action at a time. Start with the action that hurts most when abused.
Built to be easy to check
- GET /v2/ping
- check your page's key and domain
- GET /v2/ping/server
- check your server's secret key
- localhost switch
- test locally without allowing localhost as a domain
- 2 secret keys per site
- rotate without downtime (the old key lives 24 h)
- X-HEIP-Reason
- tells you in the network tab why a browser call was refused
Trust has to move with the action.
When digital actions can be automated, trust must move with them.