# Verified by Toni: live website integration

Updated September 20, 2026. Current guide: https://www.vbtoni.com/developers/docs

## Setup

Complete company onboarding and representative identity verification. In API Credentials, connect your website by publishing the supplied public meta tag in its homepage HTML head or the exact token at /.well-known/toni-verification.txt. Select Verify connection. No DNS or registrar login is required.

Only successful proof claims a website. A verified claim cannot belong to two workspaces; contact its owner or Toni support if already connected. Pending entries do not reserve it. Multiple apps in the same workspace can use a claim.

Select the verified website for new live credentials. Register the exact HTTPS callback URL on that host or its www alias; other subdomains need separate proof. Existing apps retain their keys and existing callback hosts. A new host needs a separate app.

Under Production, select Create credentials. Save the client secret in your server's secret manager when it is shown, then continue to Redirect URLs. Existing credentials have an explicit Add redirect URL or Edit redirect URLs button. If the one-time secret was missed, select Replace client secret and confirm: the old secret stops working immediately, while the client ID and callback settings remain unchanged. Update your server environment before retrying sign-in. Never paste the secret into an AI prompt.

Approved * is operational while company review is pending. It is not completed EIN or company verification. Website proof does not attest native app ownership.

The button and callback must be implemented in your application. Creating a key does not install Toni on your site.

Browser authorization starts at https://login.vbtoni.com/oauth/authorize. Toni's issuer, token endpoint, JWKS, and discovery document remain on https://www.vbtoni.com so existing server-side validation stays pinned to the same issuer.

## Approved button

### Managed appearance (recommended for new integrations)

In API Credentials, select your app in OAuth setup. Preview its Button and Hosted auth screen, then select **Publish appearance**. Settings are saved per client, so Live and Sandbox remain separate. Only a workspace owner, admin, or Toni superadmin can publish. Publishing changes appearance, never login/register intent, scopes, callbacks, identity requirements, or consent. Reviewed names/logos remain a separate branding approval process.

The **Copy AI instructions** tab supplies a one-time integration for that client and flow. After installation, future appearance changes use Publish without another tenant deployment. Existing static embeds remain supported but will not follow saved button selections until migrated.

- Read `GET https://www.vbtoni.com/api/brand/appearance/{client_id}`. This public, credential-free JSON uses CORS `*` and `Cache-Control: no-store`. Do not send cookies, client secrets, or bearer tokens.
- Validate `version: 1`, the exact `client_id`, `published: true`, a positive safe-integer `revision`, theme `light` or `dark`, and the approved catalog design/dimensions supplied in the app-specific AI instructions. Unpublished valid apps return `published: false`, revision `0`; use your existing approved fallback.
- For single buttons, `button` contains `design`, `width`, `height`, and the pinned `src`: `https://www.vbtoni.com/api/brand/appearance/{client_id}/button?action=signin&revision={revision}`. Validate the exact URL against the response's validated revision; do not accept arbitrary extra queries. Use `action=register` only for an existing registration action, keeping the revision. Render as an image, not inline SVG, and preserve the aspect ratio. Do not proxy through an image optimizer. The SVG also uses `no-store` and CORS `*`; use `crossOrigin="anonymous"` for managed images. Older static fallback URLs do not promise CORS, so keep their conventional image embedding.
- For existing two-action v2 controls, `pair.design` is only `framed` or `duo`. Update `data-toni-design`; preserve the two accessible links and hosted v2 stylesheet. Do not add a registration action to a login-only site.
- Fetch on load and focus, and optionally every 30 seconds while visible. Use a bounded timeout and response size, ignore stale responses, and cancel on unmount. Load the new versioned image URL on revision changes; changing only an element's key is insufficient because browsers may reuse their available-image cache. A stale image revision returns `409` so new artwork cannot render with old dimensions; retain the fallback and retry metadata on the next refresh. Allow Toni in `connect-src` and `img-src` CSP (also `style-src` for pairs).
- Invalid/deleted/inactive or disconnected apps return `404`; operational errors return `503`. On errors or invalid data retain a usable approved fallback and the original server-owned login link. An appearance error must never bypass or block authentication.
- Toni resolves the published auth theme from the validated client and callback. It takes priority over legacy `theme` query parameters. Do not hard-code a theme in a new managed integration. In-progress auth is not restarted by publishing.

Example published response:

```json
{"version":1,"client_id":"pk_live_example","revision":1,"published":true,"theme":"light","button":{"design":"centerPillCharcoal","width":392,"height":58,"src":"https://www.vbtoni.com/api/brand/appearance/pk_live_example/button?action=signin&revision=1"},"pair":{"design":"framed"}}
```

### Static embeds (existing integrations)

Choose the tenant flow first under Toni button design in API Credentials. Use Login only when Toni is only the account bridge at entry and identity verification happens later in your app. Use Sign in or register when your entry point should offer both returning-user sign-in and new Toni account creation. Copy the matching embed and AI instructions for that exact flow. The combined option uses two real links and hosted v2 styling; a hosted SVG by itself cannot provide two clickable actions. Login-only variants include mascot buttons in white, blue, black, gray and red, plus wider clean provider-style bars with the compact Toni mark on the left. Old blue, charcoal, white and soft artwork URLs and v1 embeds continue working for existing integrations. Do not use the legacy widget.js. Creating credentials does not install the control on your site.

## Authorization

The approved designs are Framed Pair (B) and Soft Duo (C). Both offer two separately framed full-row links: Sign in with Toni and Register with Toni. Borders are 1px; Soft Duo has white text on the blue registration row. In API Credentials choose the style and copy its complete embed and matching prompt. The two-row control uses https://www.vbtoni.com/api/brand/button-styles/v2 and the hosted white mascot. Permit https://www.vbtoni.com in style-src and img-src CSP. Do not inline or locally copy these assets. Compatible v2 styling updates refresh centrally after the five-minute asset cache window. No credentials or callback changes are needed. Old v1 embeds and the old artwork URLs remain supported; adopting the new two-row markup requires one embed replacement. Keep both partner-owned links, keyboard focus styling and visible labels. Never wrap the group in another link/button or flatten it into an image. With React, use className and put the stylesheet link in the page head once. Plain links remain usable if styling is unavailable. New markup or authentication changes can still require a deliberate integration update.

### Sign in and register

Choose Login only, Register, or Sign in or register in the button designer. You can place separate sign-in and register buttons using the same client ID and callback. The copied Register link uses `/api/auth/toni/start?intent=register`; your server must forward that intent as `screen_hint=signup` on the Toni authorization request. This opens email registration directly, without Toni's business plan picker. Only sign-in links omit the hint. Switching between sign-in and registration preserves the partner authorization request. Already signed-in users reuse their Toni identity.

Login only is authentication, not verification. After a valid callback, you may create an authenticated-but-unverified local session for onboarding. Do not unlock protected features that require identity or age until Toni returns server-validated claims that satisfy your requirements.

For apps without published appearance, the hosted Toni auth screen supports a presentation-only `theme=light` option. Add it to `https://login.vbtoni.com/oauth/authorize` for the light shell; otherwise the approved branding theme or default dark shell applies. Once appearance is published in Toni, that saved theme takes priority. This must never affect account matching, verification, or access decisions.

The hosted artwork's `action=register` or `action=signin` query changes only its label, not navigation. Update both the link and your server-side start route using the matching AI prompt. Never link directly to `/signin` and drop the OAuth context. Generate fresh state, nonce, and PKCE for every attempt. After validating the callback, identify users by issuer and subject, not email matching; new accounts on your own site must still complete your registration and terms requirements.

GET https://login.vbtoni.com/oauth/authorize with:
- client_id: your pk_live_ client ID
- redirect_uri: the exact registered callback
- response_type: code
- scope: openid id_verification (add email/profile only when needed)
- state: fresh cryptographically random value for this attempt
- nonce: fresh random value
- code_challenge: base64url SHA-256 of a fresh PKCE verifier
- code_challenge_method: S256

Store state, nonce, verifier, callback, and intended local account in your server-side session using a secure HttpOnly cookie. Do not treat browser storage as a universal fix for session or state errors.

## Callback and exchange

Handle error responses. Validate and consume state against that pending session before exchange. Reject mismatched or missing state.

POST https://www.vbtoni.com/api/oauth/token from your server with JSON:
```json
{
  "grant_type": "authorization_code",
  "client_id": "YOUR_CLIENT_ID",
  "client_secret": "YOUR_SERVER_ONLY_SECRET",
  "code": "CALLBACK_CODE",
  "redirect_uri": "EXACT_CALLBACK",
  "code_verifier": "SAVED_PKCE_VERIFIER"
}
```

Never put the client secret in frontend code or log secrets, codes, or tokens. Reject non-successful responses.

## Signed result

Read trusted configuration from https://www.vbtoni.com/.well-known/openid-configuration.
Validate the ID token with Toni's published JWKS, pinned issuer, your exact client ID as audience, RS256 signature, expiration, saved nonce, and type=id_token. Require verification_status=VERIFIED and evaluate required check results before granting access.

The sub claim is partner-app-scoped. Email and name are optional scope-dependent fields. Raw ID photos and biometric images are not shared. Existing results may require additional or recent checks; they are not permanent universal clearance.

Use the authorization callback and token exchange. There is no published partner webhook subscription API or Toni Node SDK webhook helper.

## Billing and acceptance

Business setup currently includes a $5 verification-credit purchase. Paid verification sessions start at $0.50, with optional checks priced a la carte. Payments are one-time, not subscriptions. The workspace funds checks it requests for its users. Eligible reuse has no new verification charge. Executed declined checks can still be charged.

Test denied consent, invalid state, reused or expired codes, callback mismatch, insufficient coverage, and revoked access. Production acceptance additionally requires a real payment, verification, and itemized billing reconciliation on the registered live site.
