Origin and action scoped
Beta keys and server credentials are issued for named origins, workflows, and actions. A bond for one submit should not unlock a different flow.
Pulse beta
Start with one high-risk action. The hosted gate handles the phone handoff, but your server remains the enforcement point before the action is accepted.
Hardened gate model
The public demo now follows the same boundary we expect partners to use: your backend creates short-lived Worker sessions, the browser can only render and ask for a phone bond, and the backend verifies the result before accepting the protected action.
Beta keys and server credentials are issued for named origins, workflows, and actions. A bond for one submit should not unlock a different flow.
The hosted gate owns the QR, phone handoff, status, help, and fail-closed UI. Your server still rejects missing, forged, stale, or wrong-action session ids.
For high-stakes flows, keep the completion capability server-side and close the bond only after your protected action durably succeeds.
Access path
Name the workflow and exact moment Pulse guards, such as credit application submit.
Send production and staging domains. The beta key only works from origins Kenshiki approves.
Your backend must mint the Pulse session, verify the returned receipt, enforce freshness and action scope, and complete the bond only after the action succeeds.
Agent-ready integration
Copy these files into your developer workflow. They define the beta API contract, browser embed point, and required server verification.
---
title: Pulse LLM Integration Prompt
description: Agent-ready instructions for integrating the Pulse beta embed into an existing customer form.
owner: product
status: beta
version: 1.3.0
lastReviewed: 2026-06-28
nextReview: 2026-09-28
---
# Prompt for LLM: integrate Kenshiki Pulse into an existing form
You are an implementation agent integrating Kenshiki Pulse into an existing customer-owned web form
and backend workflow.
Do not build a new application unless the customer explicitly asks for a standalone sample. Preserve
the customer's current form, validation, styling, analytics, accessibility behavior, CSRF handling,
and backend submit path while adding Pulse immediately before the protected action proceeds.
## What Pulse is
Pulse is not OTP, 2FA, push approval, GPS tracking, reCAPTCHA, or an identity-file score.
Pulse proves continuity of life from the applicant's carried phone: private presence, motion, device
physics, radios, and day rhythm that support a real human behind the application. The browser is not
the root of trust. The server is the enforcement point.
## Current production integration shape
Use the Worker-first, server-authoritative shape:
1. Browser loads `https://kenshiki-pulse-worker-production.pulsekenshikilabscom.workers.dev/v1/pulse.js`.
2. Browser asks the customer's backend for a Pulse session, for example `POST /api/pulse/sessions`.
3. Customer backend calls the Pulse Worker session endpoint with its server credential.
4. Customer backend stores `completion_secret` server-side and returns only `{ session }` to the browser.
5. Browser calls `window.KenshikiPulse.init(...)`.
6. Browser calls `pulse.requireBond({ session, action })`.
7. Browser includes `bonded_session_id` in the protected submit.
8. Customer backend fetches the Worker receipt, validates state/freshness/workflow/action/evidence, and only then processes the action.
9. Customer backend completes the bond after the action durably succeeds.
Do not route session creation through the Kenshiki Web/Vercel project. Do not rely on `VITE_*` or
host build env vars. A customer's runtime config is the publishable key, workflow, action, and their
own backend route.
## Customer must provide before implementation
Ask for these values if they are not already present:
- Framework and runtime: plain HTML, React, Next.js, Astro, Rails, Laravel, Django, etc.
- Existing form selector or component name.
- Existing submit endpoint and HTTP method.
- Existing client-side validation behavior that must remain intact.
- Existing server-side handler that processes the protected action.
- Protected workflow name, for example `credit_application`, `benefits_claim`, `account_recovery`,
`wire_transfer`, or `job_application`.
- Protected action name, for example `submit_application`, `submit_claim`, `recover_account`, or `approve_wire`.
- Pulse publishable key for the browser, for example `pk_beta_...`.
- Pulse server credential env var, for example `PULSE_SERVER_KEY`.
- Approved production and staging origins.
- What should happen if Pulse cannot bond: block, show retry, route to manual review, or save draft.
- Whether this workflow requires passport. Passport must not be globally mandatory for every Pulse user.
- Where to store the verified `bonded_session_id` and bounded verification summary for audit.
If the customer cannot provide an answer, make the safest assumption and clearly mark it in a short
integration note. Never bypass server verification.
## Required frontend shape
1. Keep the existing form.
2. Add a hidden `bonded_session_id` field.
3. Add a Pulse container near the protected submit area.
4. Reserve visible space for that container with CSS. Do not hide or rewrite the Pulse status.
5. Load the Worker-hosted `pulse.js` once.
6. Initialize `window.KenshikiPulse` with `publishableKey`, `workflow`, `containerId`, and
`sessionEndpoint`.
7. After normal client-side validation, call the customer's backend to create a Pulse session.
8. Call `pulse.requireBond({ session, action })`.
9. If bonded, write `result.sessionId` into the hidden field and continue the existing submit.
10. Do not call `bond.complete()` in the production shape. Completion belongs on the backend after
the protected action commits.
## Required backend shape
Add a route like `POST /api/pulse/sessions`:
```js
const PULSE_SESSION_ENDPOINT =
process.env.PULSE_SESSION_ENDPOINT ??
"https://kenshiki-pulse-worker-production.pulsekenshikilabscom.workers.dev/api/v1/sessions";
const PULSE_SERVER_KEY = process.env.PULSE_SERVER_KEY;
app.post("/api/pulse/sessions", async (req, res) => {
const workflow = req.body.workflow;
const action = req.body.action;
if (workflow !== "credit_application" || action !== "submit_application") {
return res.status(400).json({ error: "unsupported_pulse_scope" });
}
const pulseRes = await fetch(PULSE_SESSION_ENDPOINT, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${PULSE_SERVER_KEY}`,
},
body: JSON.stringify({ workflow, action }),
});
const session = await pulseRes.json();
if (!pulseRes.ok) return res.status(pulseRes.status).json(session);
await pulseSessionStore.save({
id: session.id,
workflow,
action,
expiresAt: session.expires_at,
completionSecret: session.completion_secret,
});
const { completion_secret: _completionSecret, ...publicSession } = session;
return res.status(201).json({ session: publicSession });
});
```
## Required trust-gate CSS
```css
#pulse-trust-gate,
[data-pulse-widget] {
display: block;
min-height: 168px;
max-width: 360px;
}
```
Do not render `qr_svg_url` as a bare image in production. Do not apply `display: none`,
`visibility: hidden`, `opacity: 0`, `pointer-events: none`, off-screen positioning, clipping, or
transform scaling to the Pulse gate or its children.
## Frontend code for a plain existing HTML form
Replace the key, workflow, action, and form id with the customer's values.
```html
<input type="hidden" name="bonded_session_id" />
<div id="pulse-trust-gate"></div>
<script src="https://kenshiki-pulse-worker-production.pulsekenshikilabscom.workers.dev/v1/pulse.js"></script>
<script>
const form = document.getElementById("application-form");
const pulse = window.KenshikiPulse.init({
publishableKey: "pk_beta_issued_by_kenshiki",
workflow: "credit_application",
containerId: "pulse-trust-gate",
sessionEndpoint:
"https://kenshiki-pulse-worker-production.pulsekenshikilabscom.workers.dev/api/v1/sessions",
});
async function createPulseSession() {
const response = await fetch("/api/pulse/sessions", {
method: "POST",
headers: { "Content-Type": "application/json" },
credentials: "same-origin",
body: JSON.stringify({ workflow: "credit_application", action: "submit_application" }),
});
if (!response.ok) throw new Error("pulse_session_create_failed");
return response.json();
}
form.addEventListener("submit", async (event) => {
if (!form.checkValidity()) return;
event.preventDefault();
const created = await createPulseSession();
const result = await pulse.requireBond({
session: created.session,
action: "submit_application",
context: { form_id: form.id || "application-form" },
});
if (result.state !== "bonded" || !result.sessionId) return;
form.elements.bonded_session_id.value = result.sessionId;
HTMLFormElement.prototype.submit.call(form);
});
</script>
```
## Backend verification code
Use this logic in the customer's existing protected-action handler before creating the application,
approving the action, moving money, opening the account, or sending the record to underwriting.
```ts
type PulseSession = {
id: string;
state: "pending" | "bonded" | "step_up_required" | "completed" | "killed" | "expired";
workflow: string | null;
action: string | null;
api_base_url?: string;
expires_at: string;
verification: null | {
device_possession: boolean;
local_auth: "verified" | "failed";
assertion_confidence?: number;
human_presence_confidence?: number;
assertion_band?: "strong" | "good" | "forming" | "limited" | "review";
continuity_score_long_lived?: number;
carrier?: {
number_verified: boolean;
sim_swap_recent: boolean;
device_status: "active" | "inactive" | "unknown";
};
sensor_continuity?: {
motion_present: boolean;
continuity_score: number;
};
passport?: {
tier?: "none" | "document" | "chip";
};
};
};
async function verifyPulseBond(input: {
bondedSessionId: string | undefined;
expectedWorkflow: string;
expectedAction: string;
requiresPassport?: boolean;
}): Promise<PulseSession> {
if (!input.bondedSessionId) throw new Error("missing_pulse_session");
const response = await fetch(
`${PULSE_SESSION_ENDPOINT}/${encodeURIComponent(input.bondedSessionId)}`,
{
cache: "no-store",
headers: { Authorization: `Bearer ${process.env.PULSE_SERVER_KEY}` },
},
);
const session = (await response.json()) as PulseSession;
if (!response.ok) throw new Error("pulse_lookup_failed");
if (session.state !== "bonded") throw new Error("unbonded_pulse_session");
if (session.workflow !== input.expectedWorkflow) throw new Error("wrong_pulse_workflow");
if (session.action !== input.expectedAction) throw new Error("wrong_pulse_action");
if (Date.parse(session.expires_at) <= Date.now()) throw new Error("expired_pulse_session");
const verification = session.verification;
if (!verification) throw new Error("missing_pulse_verification");
if (!verification.device_possession) throw new Error("device_not_possessed");
if (verification.local_auth !== "verified") throw new Error("local_auth_failed");
if ((verification.human_presence_confidence ?? verification.assertion_confidence ?? 0) < 0.55) {
throw new Error("weak_session_assertion");
}
if (verification.carrier?.sim_swap_recent) throw new Error("recent_sim_swap");
if ((verification.sensor_continuity?.continuity_score ?? 0) < 0.6) {
throw new Error("weak_continuity");
}
if (input.requiresPassport && verification.passport?.tier !== "chip") {
throw new Error("passport_chip_required");
}
return session;
}
```
## Complete the bond
After the protected action durably succeeds, complete the Pulse session with the stored
`completion_secret`.
```js
const stored = await pulseSessionStore.get(bondedSessionId);
await fetch(`${PULSE_SESSION_ENDPOINT}/${encodeURIComponent(bondedSessionId)}/form-completed`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PULSE_SERVER_KEY}`,
"X-Pulse-Completion-Secret": stored.completionSecret,
},
});
```
## UX and copy constraints
- Do not call Pulse a one-time code, 2FA, phone confirmation, push approval, CAPTCHA, or GPS check.
- Say Pulse proves continuity of life, presence, or existence behind the application.
- Desktop should show the Pulse QR inside the configured container.
- Mobile should show the Pulse handoff / Continue with Pulse path, not QR.
- Pulse owns the help link, reassurance copy, and status inside its container. Step 1
Request a Pulse beta key for exact origins you control. Kenshiki reviews production FI requests before activation.
credit_application.localhost is enabled only for the Kenshiki demo key, not partner keys.Request received. We will review origins, workflow, and license acknowledgement.
Something did not validate. Check origins, acknowledgements, and work email fields.