Public integration guide / agent-readable

Implement email verification and XCIM consent.

Choose how visitors verify their address. With EVP, their browser obtains evidence from their email provider. With OpenID Connect, they sign in with a configured identity provider. Both paths lead to the consent screen published for your application.

Add EVP to your website

Start with a registered application, a published consent screen and an HTTPS return URL. Your developer needs access to the website backend and the domain’s DNS.

  1. Check your website’s eligibility.Read live capabilities. For vendor_origin_handoff, verify your return origin with the consent_presentation_delegate role and register that exact origin in the Chrome origin trial. Visitors need a participating browser and email provider.
  2. Prepare the form and session.Generate a single-use nonce on your server and render it with the native email form. When you know the address, create the session using the same nonce and the published-screen flag. See the session request and form markup.
  3. Send the evidence from your backend.On submission, check the saved preparation and the address. Relay the browser token to the session’s EVP submission URI. After verification, send the visitor to hosted_consent_uri to choose their preferences. For an empty token, offer the configured OIDC alternative when available.
  4. Check the first receipt and fallback.With a participating account, make a test choice and verify the signed receipt after returning to your site. Repeat with EVP unavailable to check your alternative sign-in. Follow the browser test steps.
Agent instruction: preserve this separation.

Do not treat an EVP token, OAuth access token, OAuth ID token, or successful login as consent. Do not invent an email-reply flow. Only the explicit action on the Emabled-hosted Consent Manifest can grant the requested purposes. Identity must happen once; Hosted Consent must not start a second provider login after the application already supplied valid identity evidence.

01 / Quick start

Choose the identity path, then create one XCIM session.

Use the capability endpoint before selecting a path. Choose the fallback supported by your site's provider.

Check availability
GET https://api.emabled.com/v1/capabilities
ProfileUse whenWhat the agent must do
evp-01Capability status is experimental and the verifier origin has a valid Origin Trial token.Render the email form on the verified origin and submit the native evidence from your backend.
oidc-01EVP is unavailable, unsupported, or the product requires OAuth today.Use the configured site provider or the Google relay. Keep the client secret on your backend.
BothYour site uses EVP with an active Google hosted relay.Send ["evp-01", "oidc-01"]; Emabled selects the available path without weakening consent.
Create an EVP-first session with OAuth fallback
POST https://api.emabled.com/v1/consent-sessions
Authorization: Bearer <vendor-token>
Idempotency-Key: <unique-request-key>
Content-Type: application/json

{
  "issuance_profile": "evp_hosted_consent",
  "requested_recipient": "person@example.com",
  "use_published_consent_screen": true,
  "manifest_id": "urn:xcim:manifest:your-product-v3",
  "return_uri": "https://app.example.com/xcim/complete",
  "evp_delivery_mode": "vendor_origin_handoff",
  "evp_nonce": "<32-byte base64url server challenge>",
  "identity_profiles": ["evp-01", "oidc-01"],
  "exchange_endpoint_id": "oxe_your_reviewed_endpoint"
}

Supply your registered values and keep the returned session_id and resolved_manifest_id on your backend. Verify the receipt against that version, including when someone edits the consent screen while a session is open.

Choose one presentation mode: omit evp_delivery_mode for an Emabled-hosted EVP form, or send vendor_origin_handoff when your reviewed HTTPS origin owns the Origin Trial token. EVP-only sessions send identity_profiles: ["evp-01"] and omit exchange_endpoint_id. For Google fallback within this EVP session, supply your reviewed and active exchange_endpoint_id. If your site runs its own Google, Apple, GitLab.com, Slack, Twitch or Yahoo callback, offer a separate OIDC-only session with its oidc_source_id when EVP is unavailable. Omit exchange_endpoint_id in that new session. Generate the EVP nonce as exactly 32 cryptographically random bytes encoded as unpadded base64url (43 characters); a UUID is not a valid substitute.

02 / Browser handoff

Redirect the browser; never simulate the hosted page.

Read hosted_consent_uri from the response and navigate the top-level browser to it. Emabled validates the supplied identity evidence, presents the frozen manifest, records the decision and redirects immediately. Receipt signing continues asynchronously.

YOUR BACKENDCreate sessionStore session ID and an opaque recovery ID.
→
EMABLEDHosted ConsentIdentity once, then explicit purposes.
→
YOUR RETURN URIResume immediatelyPoll status or process a webhook server-side.
The return URL is terminal and exact.

Register the complete HTTPS return URI in Emabled. It is where Emabled sends the top-level browser after the consent decision. It is not Google's OAuth callback, not an access-token receiver, and not the EVP issuance endpoint. Do not trust a registration ID, email, receipt ID or success flag supplied only in its query string; recover the pending operation from server-side state.

03 / EVP path

The Origin Trial token belongs to the page origin.

The token must be issued for the exact HTTPS origin that serves the email form. A token for partners.lmgram.com cannot activate EVP on consent.emabled.com. Chrome 154+ supports third-party Origin Trials subject to issuer same-site restrictions; an Emabled token cannot enable arbitrary vendor sites.

Emabled-hostedToken origin: https://consent.emabled.com

Use this only when live capabilities report delivery_modes.emabled_hosted as available and Emabled serves the EVP form.

Vendor-hostedToken origin: https://your-app.example

Use vendor_origin_handoff after that exact origin has an active consent_presentation_delegate domain binding. The raw token travels only from the browser to your backend and then to Emabled.

Current Origin Trial interaction. The complete native EVP form must be present in the initial HTML response. Install the one-time nonce before enabling the email field, then let the person type, paste or autocomplete the address and allow background verification to finish. The address must belong to a verified email provider signed in within that same Chrome profile; being signed in as a different account does not attest the typed address. A browser checkmark is useful feedback, but only server validation of the native EVT proves identity. Do not assume restored or programmatically assigned values have been verified; keep the original form and nonce throughout the interaction. Missing evidence is an availability result, not an identity rejection. Offer OAuth only when your reviewed OIDC exchange endpoint is active.

October 2026 update. The Origin Trial now runs through Chrome 160, with Android support from Chrome 154. Tokens from before the extension expire on November 17, 2026: renew in the Origin Trials panel and replace both response header and HTML meta values. Renewal does not deploy the token. Chrome 156+ returns the submitted email exactly; Emabled preserves canonical delivery addresses and only permits provider-reviewed case comparison. EVT validation supports EdDSA/Ed25519 for Ed25519, ES256 for P-256, and bounded key search when kid is absent. See the official verifier guide. A live browser/provider test remains required after deployment.

Required form fields on the verifier origin
<input name="email-address" type="email" autocomplete="email">
<input type="hidden" name="token"
       nonce="<evp_handoff.nonce>"
       autocomplete="email-verification-token">
Vendor backend relay
POST https://api.emabled.com/v1/consent-sessions/<session_id>/identity-evidence/evp
Authorization: Bearer <token with identity-evidence:submit>
Content-Type: application/json

{
  "nonce": "<evp_handoff.nonce>",
  "token": "<browser-populated EVP token>"
}
  1. Prepare before the email interaction.Create a short-lived, single-use preparation on your backend. Generate a 32-byte base64url nonce and an unrelated opaque preparation ID. Render the same nonce on the hidden EVP field before the person interacts with the email field; never generate or replace it in browser JavaScript.
  2. Keep one email control in one native form.The address requested by signup and the address verified by Chrome must be the same control. Do not ask for it twice, clone it, move it to a second screen, or inject the verifier form after page load.
  3. Bind without destroying browser evidence.Once the field is locally valid, bind the preparation to the normalized address and create the Emabled session using the already-rendered nonce. Changes to the address invalidate the preparation. A repeated request must be idempotent or replace only an expired preparation.
  4. Put the Origin Trial token on the same response as the form.Do not use a token issued for another host, subdomain, port, or protocol. The response must carry the public Origin-Trial header for that exact origin.
  5. Post the native form to your own backend.Submit natively so Chrome can populate the token. JavaScript must not read, log, persist or forward the raw token. Your backend consumes the preparation once, verifies the selected email, then relays the token to Emabled over the authenticated backchannel.
  6. Redirect to hosted_consent_uri after verification.Identity evidence only unlocks the frozen manifest. The person must still make the explicit XCIM purpose decision.
  7. Fall back when EVP is unavailable.Use the configured OAuth/OIDC path or your product's non-XCIM continuation policy. Never silently downgrade to email reply verification.

04 / OAuth path

Keep OAuth standard and keep secrets server-side.

For a site using Google, Apple, GitLab.com, Yahoo, Slack or Twitch, follow the site sign-in guide to submit a verified ID token for an XCIM session. Sites using the hosted Google callback can use the reviewed authorization-code relay below. Choose OIDC on its own, or configure the hosted Google relay as a fallback for EVP.

Do

  • Register Emabled's callback URI in the vendor OAuth client.
  • Validate exact state, nonce, PKCE, issuer, audience, time, and email policy.
  • Return only the provider-signed ID token through the signed backchannel.
  • Keep client secrets, access tokens, and refresh tokens in the vendor backend.

Do not

  • Send a native Google access token to Emabled.
  • Use a GIS popup callback as if it were an OAuth return URL.
  • Let a vendor endpoint mark identity or consent as verified.
  • Start a second login after the provider flow already returned.
Google Identity Services popup applications.

A GIS JavaScript popup callback is not a redirect URI. You may keep the popup for your application's own login, then pass the provider-signed ID token only through an Emabled-reviewed first-party or backchannel binding. If that binding is not provisioned, create or use a Web OAuth client that supports the Emabled authorization-code callback. Never send a Google access token to Emabled and never replace your internal token-receiving API endpoint with the XCIM return URI.

Callback vocabulary
Google OAuth callback  https://id.emabled.com/oidc/callback/google
XCIM return URI       https://your-app.example/xcim/complete
Hosted Consent       https://consent.emabled.com/s/<session_id>
Receipt query         https://api.emabled.com/v1/receipts/<receipt_id>

05 / Reference lifecycle

Use server state to join the whole flow.

The production-tested pattern separates the browser's navigation state from the server's security state. It survives refreshes, cross-origin redirects, delayed receipts and a return that happens after a local preparation deadline.

Vendor implementation outline
// 1. Initial page response
preparation = createSingleUsePreparation()
nonce = base64url(randomBytes(32))         // exactly 43 chars
renderNativeEvpForm(preparation.id, nonce) // form exists in initial HTML

// 2. Valid email selection / blur
bind(preparation.id, normalize(email))
session = emabled.createConsentSession({ requested_recipient: email, evp_nonce: nonce })
store(preparation.id, session.session_id, session.resolved_manifest_id, session.hosted_consent_uri)

// 3. Native form POST populated by Chrome
consumeOnce(preparation.id)
if (browserToken) emabled.submitEvpEvidence(session.id, nonce, browserToken)
// No EVP token: preserve identity_pending and use the configured OIDC path.
redirect(session.hosted_consent_uri)

// 4. Exact return URI
pending = recoverFromHttpOnlyCookieOrOpaqueRecoveryId()
poll(emabledSessionId)                     // same-origin BFF recommended
verifyReceiptAndCurrentPermission()
materializeAccountOrRequestedOperation()

Persist server-side

  • Opaque preparation/recovery ID.
  • Normalized email commitment and pending payload.
  • Emabled session ID and exact hosted URI.
  • Expiry, one-time consumption and terminal status.

Browser state

  • Secure HttpOnly same-origin cookie as the primary binding.
  • Opaque recovery ID in session storage only when refresh recovery is needed.
  • No raw EVP token, provider token, receipt JWS or email in query parameters.
  • Use a same-origin completion endpoint so secure cookies are sent reliably.

06 / Completion contract

Return immediately; verify the receipt server-side.

The browser return is a UX handoff and must not remain on Hosted Consent waiting for cryptography. Open the product shell immediately, then complete the security-sensitive operation only after the signed receipt is available and valid.

Read session status
GET https://api.emabled.com/v1/consent-sessions/<session_id>

// The decision can be terminal before receipt issuance finishes:
{
  "status": "accepted",
  "identity_verified": true,
  "receipt_id": null
}
StatusAgent action
accepted + no receiptTreat as receipt_pending. Keep the terminal decision recoverable, return the UI to the product and retry with bounded polling or a webhook. Do not expire an already accepted decision because local pending time elapsed.
accepted + receiptVerify the receipt JWS and current permission state, then materialize the account or protected operation idempotently.
identity_unavailable / identity_failedOffer OAuth fallback if configured or return to the product without claiming XCIM consent.
identity_mismatchDo not issue access or grant the requested purpose for the original address. Ask the user to choose the correct identity or cancel.
cancelled / expiredStop the XCIM branch. The product may continue only under its own independent policy.
Receipt verification requirements.

Verify Ed25519 against Emabled's current JWKS; bind receipt_id, application ID, manifest ID/hash, identity evidence and the expected permission; then query current revocation/permission state. An XCIM receipt may intentionally contain expires_at: null. Treat null as no receipt expiry and validate an expiry only when the field is present and non-null. Accept the serialization published by the receipt endpoint, including flattened JWS JSON when returned.

07 / Production troubleshooting

Diagnose the boundary that actually failed.

Do not restart the whole identity flow for every error. Preserve terminal decisions and use safe diagnostics that never log an email, nonce, token, receipt payload or browser evidence.

SymptomLikely cause and fix
No official Chrome checkmarkA missing visual indicator alone does not establish verification failure. Chrome may confirm verification separately; rely on validated native evidence, not the icon. If evidence is also missing, check the provider account in the active profile, form and nonce timing, contact-info verification settings and the exact-origin trial token. Current Chrome supports typing, pasting and autocomplete.
Checkmark appears but Emabled rejects evidenceStart a fresh preparation and complete its native POST within the advertised session lifetime. Emabled measures EVP freshness from challenge creation, allows the configured clock skew, and still validates issuer signature, exact origin, nonce, browser key binding, token expiry and a maximum evidence age of five minutes.
invalid_request creating the sessionConfirm a 32-byte unpadded base64url nonce (43 characters), reviewed source/domain binding, active manifest, exact return URI and enabled capability.
EVP succeeds but Hosted Consent does not openConfirm the native POST consumed the same preparation once, email and nonce match, and the vendor backend submitted evidence with the scoped identity-evidence:submit credential.
Decision button appears stuckQuery the session before asking the person to click again. The decision may already be terminal. Confirm the exact registered return origin is allowed by Hosted Consent's form-action CSP and let Emabled redirect directly.
receipt_pending lasts unexpectedlyKeep polling without discarding accepted consent. Check worker database grants, KMS permissions and operation-level gRPC status. Encryption for stored identity evidence and the asserted-recipient receipt projection must use independent ciphertexts with their own authenticated contexts.
Return says invalid registrationUse a same-origin completion/BFF endpoint, a secure HttpOnly cookie and an opaque recovery ID. Cross-origin fetches do not send the pending cookie unless explicitly configured and should not be the default.
Second login shows provider picker or consent againReuse the application's authenticated session and existing terminal XCIM state. Start a new provider flow only for a new/expired identity operation or a materially changed manifest.

08 / Live validation

Test EVP and OAuth/OIDC separately.

A successful EVP signup does not exercise the alternative provider login. Use an address without an existing vendor account for each completed signup; do not delete real accounts to make a test pass.

  1. EVP in a normal session.Start with the participating email provider signed in. Enter, paste or autocomplete the requested address in the prepared native form, allow Chrome to verify it, submit signup, make the separate Emabled consent decision and confirm the authenticated return to the product. A missing green checkmark alone is not a failure.
  2. OAuth/OIDC without a prior provider login.Start a separate browsing session with no active provider login, such as a fresh Incognito session. Submit signup without EVP evidence. Confirm that Emabled offers Continue with Google, sign in with the same requested address, record the separate consent decision and confirm the authenticated product return. If Chrome verifies the address before submission, that attempt is another EVP test, not OIDC.
Why EVP may not be offered in Incognito.

EVP requires an active participating-provider session before verification. Incognito does not inherit the normal window's Google login; existing Incognito windows share their private session. Missing EVP before signing in is expected in this setup, not evidence that typing is unsupported or the address is invalid. Continue through the reviewed OIDC path rather than weakening verification or changing browser privacy settings. See Chrome's EVP prerequisites and Incognito session behavior.

Validated on October 15, 2026. The LMGram Partner Console integration completed both paths in user-assisted live tests: Chrome EVP verification followed by consent and return, and an Incognito signup with Google OAuth/OIDC followed by consent and return. These are user-reported browser results, not unattended authentication or a direct audit of private account rows and receipts. Backend receipt verification remains mandatory; mismatch, cancellation, expiry, delayed receipts, repeated completion and revocation remain separate regression cases.

09 / Agent checklist

Definition of done.

A coding agent can use this list as the acceptance gate before opening a pull request or deployment.

Need a reviewed vendor setup?

Use the Emabled console to register sources, domains, OAuth bindings, return URLs, and exchange endpoints. For Origin Trial enablement on a custom vendor origin, contact support@emabled.com before deploying.