This quickstart is the first IAH integration path for web app developers. It keeps the app-side work small: redirect the user to IAH-hosted verification, handle the return URL, verify the result on the server, and receive webhook updates.
IAH owns the developer verification product surface. Cubid remains the infrastructure behind the proof, disclosure, policy, and identity runtime.
What You Build
- A "Verify with IAH" button in your app.
- A return URL that receives the user after hosted verification.
- A server verification check that confirms the result before granting access.
- A webhook receiver for asynchronous verification updates.
Environment
Use placeholders until your app credentials are issued:
IAH_APP_ID=iah_app_123
IAH_API_KEY=iah_live_replace_me
IAH_VERIFY_BASE_URL=https://i-am-human.app/verify/start
IAH_RETURN_URL=http://localhost:3000/iah/callback
IAH_WEBHOOK_SECRET=whsec_replace_me
Never expose IAH_API_KEY or IAH_WEBHOOK_SECRET to browser code.
Client Redirect
Build the hosted verification URL on the server when possible. For local prototypes, the browser can assemble a redirect with public values only:
import { createHostedVerificationUrl } from "@iah/browser";
const url = createHostedVerificationUrl({
appId: "iah_app_123",
returnUrl: "http://localhost:3000/iah/callback",
state: crypto.randomUUID(),
});
window.location.href = url;
The state value should be stored in your session before redirecting and checked when the user returns.
Return URL
IAH returns the user to your return_url with a verification reference and the original state. The current hosted UX scaffold is available at /verify/start and redirects to:
GET /iah/callback?verification_id=ver_123&state=state_123
Your app should:
- confirm
statematches the value stored before redirect - send
verification_idto your server - have the server check verification status with IAH before unlocking access
Server Verification Check
The server check is intentionally IAH-first. The app should not need to understand Cubid OIDC, custody, chains, or selective-disclosure internals.
import { createIAHCoreClient } from "@iah/core";
const iah = createIAHCoreClient({
apiKey: process.env.IAH_API_KEY,
appId: process.env.IAH_APP_ID,
});
const result = await iah.checkVerification({ verificationId });
if (result.data?.verified === true) {
// Grant access to the protected feature.
}
Expected response shape:
{
"data": {
"verification_id": "ver_123",
"verified": true,
"subject": "app_scoped_subject_123",
"completed_at": "2026-05-15T00:00:00.000Z"
}
}
Webhook Receiver
Use webhooks for asynchronous updates or delayed proof completion:
{
"type": "verification.completed",
"data": {
"verification_id": "ver_123",
"verified": true,
"subject": "app_scoped_subject_123"
}
}
The receiver should verify the webhook signature with IAH_WEBHOOK_SECRET before trusting the payload. The local example in examples/hosted-redirect/ includes the expected route shape.
Advanced Cubid Notes
Most apps should not need these for the first integration. When your product needs deeper identity infrastructure, use Cubid paths for Sign in with Cubid, app-scoped selective disclosure, passkey-required verification, custom proof policies, and chain or custody features.