Developer Platform

Integrate TapSign the way Stripe-style docs should feel: direct, productized, and protocol-first.

Use this public guide to wire login approval, Guardian, and OTP flows into your product. Live SDKs are marked as live. Java, Go, Flutter, and React wrapper tracks are documented as wrapper contracts with REST fallback where the package has not shipped yet.

Universal Web SDKGuardian SDKOTP SDKREST API

Quickstart

Five steps to a real integration.

  • Create a TapSign client key, then provision a connector so server-side calls use Ed25519 proof-of-possession instead of a shared bearer secret.
  • Register at least one approver device before testing login approval, Guardian, or OTP verification.
  • Start the approval session from your backend or trusted browser helper and store the returned session identifier.
  • Poll status until a terminal result arrives, then grant the relying-party session or protected action only after approval.
  • Keep production rollout separate from sandbox by changing keys and callback URLs through environment configuration.

Start login approval over HTTP

bash
curl -X POST "https://your-app.example.com/api/tapsign/login/approval/start" \
  -H "Content-Type: application/json" \
  -d '{
    "client_key_id": "tk_live_workspace_example",
    "device_ref": "dev_public_example_1234",
    "login_request_id": "site-login-123",
    "request_context": "custom-site-login"
  }'

# same-origin backend route recommended:
# your server holds connector_id + connector_private_key_b64
# the browser never sees TapSign backend credentials

SDK Matrix

Choose the integration track that matches your stack.

Live now

@tapsign/universal-web-sdk

Universal Web SDK

Custom browser login and step-up UX

Starts login approval or privileged-action approval and polls status from the browser.

Live now

guardian_sdk/python + guardian_sdk/node

Guardian SDK

Server-side admin actions and protected route handlers

Python and Node helpers for intercepting high-risk actions and enforcing approval before execution.

Live now

otp_sdk/python + otp_sdk/node + otp_sdk/php

OTP SDK

Device-bound OTP guard flows for systems that keep their own OTP UX

Python, Node, and PHP packages for request/reveal/verify integrations where TapSign guards an OTP flow you already have; TapSign does not become the SMS or email sender in this track.

Wrapper contract

Uses Universal Web SDK contract today

React wrapper

React and Next.js teams

Thin wrapper over the Universal Web SDK so app teams can stay inside hooks/components instead of raw polling calls.

Wrapper contract

REST contract today, package shape documented here

Flutter wrapper

Flutter mobile teams

Wrapper contract around the same login approval and step-up flows, with REST fallback until the package is shipped.

Wrapper contract

REST contract today, Java facade shape documented here

Java SDK

Backend platforms, banks, and enterprise Java services

Server-side wrapper contract for login approval, Guardian, and OTP with REST fallback available immediately.

Wrapper contract

REST contract today, Go facade shape documented here

Go SDK

Go APIs, platform services, and infra tooling

Server-side wrapper contract for the same backend routes, designed for context-aware service integrations.

Agent Prompts

Copy one prompt, hand it to your coding agent, and keep the trust boundary intact.

These prompts are written for repo-aware coding agents and IDE assistants. They tell the agent which SDK track to use, what not to expose, and which env vars belong on the server only. The goal is practical integration, not generic prose.

Assistant

Best for longer implementation plans and careful refactors when you want the agent to explain each integration decision.

Track

Use this when

Custom web apps, admin portals, and browser flows that need login approval or step-up approval.

Server env only

  • TAPSIGN_API_BASE_URL
  • TAPSIGN_CLIENT_KEY_ID
  • TAPSIGN_CONNECTOR_ID
  • TAPSIGN_CONNECTOR_PRIVATE_KEY_B64

Claude prompt for Universal Web SDK

You are Claude. Integrate TapSign using the Universal Web SDK track (@tapsign/universal-web-sdk).

Goal:
Add browser login approval or privileged-action approval to a web app without exposing TapSign credentials to browser code.

Project constraints:
- Keep all TapSign credentials server-side.
- Never put connector private keys, shared secrets, or OpenBao/Vault secrets in browser, mobile JS, or untrusted client code.
- Use the live TapSign route contract and preserve the start -> poll -> terminal approval model.
- If the target app already has auth or OTP UI, keep that host UX and add TapSign at the approval boundary instead of rebuilding the host app.

Required server env vars:
- TAPSIGN_API_BASE_URL
- TAPSIGN_CLIENT_KEY_ID
- TAPSIGN_CONNECTOR_ID
- TAPSIGN_CONNECTOR_PRIVATE_KEY_B64

Implementation checklist:
- Keep TapSign credentials on the server only; the browser talks to a same-origin backend route.
- Use connector proof-of-possession for the server-side TapSign transport.
- Create the relying-party session only after terminal approval is `approved`.

Expected deliverables:
- production-ready integration code for this stack
- env/config wiring
- one local test path and one production note
- no placeholder secrets

Do not invent missing endpoints or downgrade to browser-held credentials. If a package is not shipped yet, use the documented REST contract while keeping the same method names and trust boundary.

Universal Web SDK

Browser-side login approval and privileged-action polling.

The Universal Web SDK is the live browser helper for custom sites. It starts approval sessions and polls terminal status, but your backend still decides when to create the real relying-party session.

JavaScript / browser

ts
import { createTapSignWebClient } from "@tapsign/universal-web-sdk";

const tapsign = createTapSignWebClient({
  apiBaseUrl: "/api/tapsign",
  clientKeyId: "tk_live_workspace_example",
});

const start = await tapsign.startLoginApproval({
  deviceRef: "dev_public_example_1234",
  loginRequestId: "checkout-admin-42",
  requestContext: "admin-login",
});

if (start.status === "pending") {
  const terminal = await tapsign.waitForLoginApprovalTerminal(start.session_id);
  if (terminal.status === "approved") {
    // ask your backend to create the real app session now
  }
}

React Wrapper

Thin React hooks over the same Universal Web contract.

Use this wrapper shape if your team wants hooks and components instead of directly managing polling state. The underlying live contract is still the Universal Web SDK plus your backend session logic.

React hook shape

tsx
import { useState } from "react";
import { createTapSignWebClient } from "@tapsign/universal-web-sdk";

const tapsign = createTapSignWebClient({
  apiBaseUrl: "/api/tapsign",
  clientKeyId: process.env.NEXT_PUBLIC_TAPSIGN_CLIENT_KEY_ID!,
});

export function useTapSignLoginApproval() {
  const [status, setStatus] = useState<"idle" | "pending" | "approved" | "denied">("idle");

  async function start(registeredDeviceRef: string, loginRequestId: string) {
    const session = await tapsign.startLoginApproval({
      deviceRef: registeredDeviceRef,
      loginRequestId,
      requestContext: "react-login",
    });

    if (session.status === "pending") {
      setStatus("pending");
      const terminal = await tapsign.waitForLoginApprovalTerminal(session.session_id);
      setStatus(terminal.status === "approved" ? "approved" : "denied");
      return terminal;
    }

    setStatus(session.status === "approved" ? "approved" : "denied");
    return session;
  }

  return { start, status };
}

Flutter Wrapper

Documented wrapper contract with REST fallback today.

Flutter teams can integrate immediately against the public API contract while the package shape stays stable. When the wrapper package lands, it should mirror this method naming and session model.

Flutter wrapper contract

dart
final tapsign = TapSignFlutter(
  apiBaseUrl: const String.fromEnvironment("TAPSIGN_API_BASE_URL"),
  clientKeyId: const String.fromEnvironment("TAPSIGN_CLIENT_KEY_ID"),
  connectorId: const String.fromEnvironment("TAPSIGN_CONNECTOR_ID"),
  connectorPrivateKeyB64: const String.fromEnvironment("TAPSIGN_CONNECTOR_PRIVATE_KEY_B64"),
);

final session = await tapsign.startLoginApproval(
  deviceRef: deviceRef,
  loginRequestId: "mobile-login-123",
  requestContext: "flutter-login",
);

final terminal = await tapsign.waitForLoginApprovalTerminal(session.sessionId);
if (terminal.status == "approved") {
  // continue the app sign-in flow
}

Java SDK

Server-side facade for banks and enterprise Java services.

The Java SDK is documented as the stable facade shape. Until the package ships, use the same backend routes directly from your HTTP client and keep the method naming shown here.

Java facade shape

java
TapSignClient client = TapSignClient.builder()
    .apiBaseUrl(System.getenv("TAPSIGN_API_BASE_URL"))
    .clientKeyId(System.getenv("TAPSIGN_CLIENT_KEY_ID"))
    .connectorId(System.getenv("TAPSIGN_CONNECTOR_ID"))
    .connectorPrivateKeyB64(System.getenv("TAPSIGN_CONNECTOR_PRIVATE_KEY_B64"))
    .build();

LoginApprovalStartResponse session = client.loginApproval().start(
    new LoginApprovalStartRequest(
        "tk_live_workspace_example",
        "dev_public_example_1234",
        "bank-console-login-99",
        "java-backend-login"
    )
);

LoginApprovalStatusResponse terminal =
    client.loginApproval().waitForTerminal(session.sessionId());

Go SDK

Context-aware service integration for Go APIs and platform tooling.

The Go SDK track follows the same session lifecycle and endpoint contract. Use REST now, then swap to the package without changing your approval model.

Go facade shape

go
client := tapsign.NewClient(tapsign.Config{
    APIBaseURL:  os.Getenv("TAPSIGN_API_BASE_URL"),
    ClientKeyID: os.Getenv("TAPSIGN_CLIENT_KEY_ID"),
    ConnectorID: os.Getenv("TAPSIGN_CONNECTOR_ID"),
    ConnectorPrivateKeyB64: os.Getenv("TAPSIGN_CONNECTOR_PRIVATE_KEY_B64"),
})

session, err := client.LoginApproval.Start(ctx, tapsign.LoginApprovalStartRequest{
    ClientKeyID:    "tk_live_workspace_example",
    DeviceRef:      "dev_public_example_1234",
    LoginRequestID: "ops-console-login-9",
    RequestContext: "go-backend-login",
})
if err != nil {
    return err
}

terminal, err := client.LoginApproval.WaitForTerminal(ctx, session.SessionID)
if err != nil {
    return err
}

Guardian SDK

Protect destructive admin or ops actions on the server side.

Guardian is live as Python and Node packages for backend handlers. Use it to intercept high-risk actions and wait for approval before your code touches the destructive path.

Guardian Node middleware

ts
import { createGuardianMiddleware } from "@tapsign/guardian-sdk";

const guardDeleteUser = createGuardianMiddleware({
  apiBaseUrl: process.env.TAPSIGN_API_BASE_URL,
  clientKeyId: process.env.TAPSIGN_CLIENT_KEY_ID,
  connectorId: process.env.TAPSIGN_CONNECTOR_ID,
  connectorPrivateKeyB64: process.env.TAPSIGN_CONNECTOR_PRIVATE_KEY_B64,
}, {
  deviceId: (req) => req.headers["x-tapsign-device-id"],
  operationKey: "destructive_admin_action",
  operationType: "admin",
  channel: "api",
});

app.post("/admin/delete-user",
  guardDeleteUser,
  async (req, res) => {
    await deleteUser(req.body.userId);
    res.json({ ok: true });
  }
);

OTP SDK

Protect an OTP step you already have. TapSign does not become the OTP sender here.

TapSign is not positioned here as a generic SMS or email OTP provider. This SDK is for systems that already have an OTP UX and want TapSign to block the reveal or redeem step until the approved device says yes. Your existing OTP provider can stay in place; TapSign adds the device-bound approval layer around it.

OTP SDK Python

python
from tapsign_otp import TapSignOtpClient

client = TapSignOtpClient(
    api_base_url="https://api.example.com",
    client_key_id="tk_live_workspace_example",
    connector_id="11111111-1111-1111-1111-111111111111",
    connector_private_key_b64="<base64-32-byte-ed25519-seed>",
)

request = client.request_otp(
    subject_ref="order-12345",
    channel="checkout",
    device_id="registered-device-id",
    request_context="high-risk-transfer",
)

verify = client.verify_otp(
    session_id=request.session_id,
    code="123456",
)

API Contract

Public routes that the SDKs and wrappers are built on.

RoutePurposeUsed by
POST /registerRegister a device public key for a client.Mobile/native onboarding and device linking.
POST /verifyVerify a signed challenge against a registered device.Core verification and trust checks.
POST /login/approval/startStart a login approval session.Universal Web SDK, React wrapper, custom sign-in flows.
GET /login/approval/statusPoll login approval status until terminal state.Browser and server orchestration layers.
POST /otp/sdk/requestStart a bound OTP request.OTP SDK packages and direct API clients.
POST /otp/sdk/verifyRedeem or verify the OTP decision.OTP SDK packages and direct API clients.
POST /guardian/interceptCreate a Guardian session for a privileged operation.Guardian SDK, gateway, proxy, and protected admin handlers.
GET /guardian/statusPoll Guardian status for allow, approval, deny, or expiry.Guardian SDK and gateway polling paths.
POST /guardian/approveSubmit signed approval from an authorized device.Approver mobile/device flows.

Go Live

Production notes that matter more than marketing language.

  • Keep browser code on same-origin routes only; backend integrations should prefer connector-signed proof-of-possession over `x-client-secret`.
  • Use sandbox keys and device registrations first, then swap environment variables for production cutover.
  • Put Cloudflare or your WAF in front of public HTTP endpoints only; it does not protect PostgreSQL directly.
  • Treat Firebase/APNs/MDM as optional notification transport. The trust anchor is still the device signature, not the push vendor.
  • Split runtime, migration, executor, and break-glass roles before enabling Guardian on destructive production paths.

Contact

Need rollout or integration help?

Use the docs for implementation detail, then use this contact path for pricing, rollout planning, enterprise delivery, or direct developer support.

Send a docs enquiry

Use this for integration questions, rollout support, and buyer follow-up.

Add NEXT_PUBLIC_WEB3FORMS_ACCESS_KEY during deployment to enable docs-form delivery.