---
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.
