VerifymeDocs
Widget

Overview

A hosted, branded code-entry UI — embed it, then redeem the proof on your server.

The widget is the fastest safe integration: Verifyme renders the code form in an iframe, sends and resends the code, and hands your page a single-use proof. Your server redeems the proof to learn who was verified.

1. Your server  ── createSession (API key) ──▶ Verifyme   → widget_token
2. Your page    ── <VerifyWidget token> ─────▶ iframe      → proof
3. Your server  ── redeem(proof) ────────────▶ Verifyme   → verified ✓

Setup

In Console → Widget → Setup, add every site origin that will show the widget, e.g. https://app.example.com (exact, no wildcards). Test keys also accept http://localhost:*.

Create a session (server)

app/api/verify/start/route.ts
import { Verifyme } from "@cubis/verifyme"

const verifyme = Verifyme.fromEnv()

export async function POST(req: Request) {
  const user = await requireUser(req) // your auth
  const { phone } = await req.json()

  const session = await verifyme.widget.createSession({
    target: phone,
    origin: "https://app.example.com", // must be allowlisted
    client_reference: user.id, // comes back on redeem
  })

  await saveForUser(user.id, session.session_id) // keep it server-side
  return Response.json({ token: session.widget_token }) // only this goes to the page
}

Options: provider (sms default, email, whatsapp, telegram), length (4–8, default 6), timeout (10–300 s, default 300), template, callback_url. The widget token lives 15 minutes.

Show the widget (browser)

import { VerifyWidget } from "@cubis/verifyme/react"

<VerifyWidget token={token} onSuccess={({ proof }) => complete(proof)} />

More frameworks, modals and options: Frameworks. Add frame-src https://verifyme.cubis.tech to your Content-Security-Policy.

Redeem the proof (server)

app/api/verify/complete/route.ts
export async function POST(req: Request) {
  const user = await requireUser(req)
  const { proof } = await req.json()

  const v = await verifyme.widget.redeem(proof) // throws if invalid, used or expired

  // The proof must belong to *this* user's session.
  if (v.session_id !== (await loadForUser(user.id)) || v.client_reference !== user.id) {
    return Response.json({ verified: false }, { status: 403 })
  }
  await markPhoneVerified(user.id, v.target)
  return Response.json({ verified: true })
}

A proof is single-use, valid 5 minutes, and bound to your business and mode.

The browser's onSuccess is only a hint

Anyone can call your page's success handler. Only a proof redeemed by your server means verified.

Limits

5 wrong codes burn a code; 15 checks per session; up to 3 resends, 30 s apart. A refused resend (throttle, outage) does not use one up.

Events and errors

onError / verifyme-error receive { code, message }:

codeMeaningWhat to do
invalid_codeWrong digits, attempts remainNothing — the widget asks again
code_spentThat code is used upThe widget offers a resend
session_expired · session_invalid · too_many_checksSession overCreate a new session
rate_limitedThrottledWait and retry
load_timeout · load_failedFrame or script did not loadCheck CSP frame-src / network

On this page