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.
- Check your website’s eligibility.Read live capabilities. For
vendor_origin_handoff, verify your return origin with theconsent_presentation_delegaterole and register that exact origin in the Chrome origin trial. Visitors need a participating browser and email provider. - 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.
- 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_urito choose their preferences. For an empty token, offer the configured OIDC alternative when available. - 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.
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.
GET https://api.emabled.com/v1/capabilities| Profile | Use when | What the agent must do |
|---|---|---|
evp-01 | Capability 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-01 | EVP 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. |
| Both | Your site uses EVP with an active Google hosted relay. | Send ["evp-01", "oidc-01"]; Emabled selects the available path without weakening consent. |
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.
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.
https://consent.emabled.comUse this only when live capabilities report delivery_modes.emabled_hosted as available and Emabled serves the EVP form.
https://your-app.exampleUse 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.
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.
<input name="email-address" type="email" autocomplete="email">
<input type="hidden" name="token"
nonce="<evp_handoff.nonce>"
autocomplete="email-verification-token">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>"
}- 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.
- 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.
- 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.
- 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-Trialheader for that exact origin. - 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.
- Redirect to
hosted_consent_uriafter verification.Identity evidence only unlocks the frozen manifest. The person must still make the explicit XCIM purpose decision. - 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.
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.
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.
// 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.
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
}| Status | Agent action |
|---|---|
accepted + no receipt | Treat 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 + receipt | Verify the receipt JWS and current permission state, then materialize the account or protected operation idempotently. |
identity_unavailable / identity_failed | Offer OAuth fallback if configured or return to the product without claiming XCIM consent. |
identity_mismatch | Do not issue access or grant the requested purpose for the original address. Ask the user to choose the correct identity or cancel. |
cancelled / expired | Stop the XCIM branch. The product may continue only under its own independent policy. |
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.
| Symptom | Likely cause and fix |
|---|---|
| No official Chrome checkmark | A 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 evidence | Start 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 session | Confirm 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 open | Confirm 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 stuck | Query 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 unexpectedly | Keep 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 registration | Use 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 again | Reuse 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.
- 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.
- 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.
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.
- Capabilities checked.Integration records whether
evp-01is available and retains a provisionedoidc-01path. - Application is registered.Application ID/key, HTTPS domain binding, multilingual manifest, exact return URI, pairwise key and any reviewed exchange endpoint are configured in Emabled.
- Identity is separate from consent.No token, login, webhook, or vendor API call directly grants a purpose, and Hosted Consent does not repeat identity already established.
- EVP page is native and early.The complete form, exact-origin token and 43-character nonce exist before email interaction; one email field is used throughout.
- State is server-bound.A one-time preparation joins recipient, nonce, Emabled session and pending operation; return query parameters are never authoritative.
- Return does not block.Hosted Consent redirects immediately and the product handles
receipt_pendingasynchronously without discarding a terminal decision. - Secrets stay server-side.No client secret, access token, refresh token, raw EVT, browser key or decrypted address enters public logs, receipts, frontend bundles or URLs.
- Receipt is verified.Backend verifies the signed receipt, application/manifest binding and current permission/revocation state; nullable expiry is handled correctly.
- Recovery is tested.Hard refresh, delayed receipt, duplicate click, expired preparation, lost browser storage and idempotent completion have explicit tests.
- Fallback is explicit.Unsupported EVP never becomes an email-reply flow or an unverified vendor assertion.
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.