Skip to content

Quickstart

Edition 3.1.1 Last verified 2026-07-19

Non-normative. This page is an informative orientation and onboarding aid. It adds no contract, defines no requirement, and is excluded from requirement extraction. The pseudocode below is illustrative; the binding contracts are the cited specification sections, requirement clauses, and machine registries. Where anything here appears to disagree with those canonical sources, they govern.

UIAF is an implementation specification: you build it into your platform. A conforming deployment is one that follows the specificationโ€™s requirements. The minimal conforming slice below is the smallest build that still qualifies, and the reading order at the end covers everything beyond it.

The smallest conforming deployment is a same-origin cookie route (create), a consent-gated automatic session, a same-origin relay, and one payload that passes the shared reference validator. A relay is your own server route that receives payloads from the browser and forwards them to your endpoint. Recovery, cross-domain handoff, conversion/identify, and retry-drain refinements layer on top of this skeleton without changing it.

Only the server creates UIDs (user identifiers). The route contract is closed: create is exactly {}, recover is exactly { "uiaf_recovery": "<credential>" }, and any UID anywhere in the body is a 400. The status map is exhaustive: 200 (issuance with Set-Cookie, or no-set without), 400, 403, 429, 5xx. No other status exists. See Endpoint Schema (UIAF-04-AUX-003, UIAF-04-AUX-008).

// PSEUDOCODE โ€” minimal create-capable cookie route (recover adds one closed body variant)
route POST /api/uiaf/cookie:
// Ingress authority: Origin is authoritative; Sec-Fetch-Site only corroborates.
if originInvalid(request) OR secFetchSite(request) == "cross-site": return 403 // single observable denial
if contentType(request) != "application/json": return 400 // strict JSON only
var body = parseJsonStrict(readBody(request)) // duplicate-member-rejecting; non-object/parse failure => 400
if body != {}: return 400 // create body is EXACTLY {}; any member โ€” incl. any UID โ€” => 400
if rateLimited(request): return 429 // per-source rate limit
// An already-valid browser-held cookie is AUTHORITATIVE: no Set-Cookie, no replace, no refresh.
var inbound = readValidUidCookie(request) // validated against the one canonical UID regex; malformed => absent
if inbound != null:
return json(200, { "uid": inbound, "recovery": currentBoundCredentialOrNull(inbound) }) // NO Set-Cookie
// Create: the base persistence predicate governs Set-Cookie (resolved consent + analytics allowed + policy).
var consent = readServerConsentState(request) // the server's own authoritative consent/policy state
if not basePersistencePredicate(consent, UIAF_CONFIG): return 403 // no Set-Cookie
var uid = mintUid() // CSPRNG UUIDv4 + "." + unix seconds; canonical form; SERVER-MINTED ONLY
var credential = mintRecoveryCredential(uid) // base64url 22-128 chars, >=128 CSPRNG bits, server-bound to uid
persist(uid, credential) // binding, expiry, revocation/rotation state; zero raw UID in logs
response.setHeader("Set-Cookie",
"uiaf_uid=" + uid + "; Path=/; Secure; SameSite=Lax; Max-Age=34560000") // + configured Domain if subdomain-sharing
response.setHeader("Cache-Control", "no-store")
return json(200, { "uid": uid, "recovery": credential }) // EXACTLY these members; uid is candidate-only until the client re-read

2. Client โ€” identity, session, one automatic session

Section titled โ€œ2. Client โ€” identity, session, one automatic sessionโ€

Three rules carry the whole flow: the client never mints a UID and never puts one in a request body; every response UID is candidate-only until the mandatory post-response cookie re-read; and behavior branches on the effective consent vector, never a tier number (Identity Management, Implementation Guide; UIAF-02-COOKIE-001/002, UIAF-09-ID-001, UIAF-09-CONSENT-001, UIAF-09-WAKE-001).

// PSEUDOCODE โ€” first eligible document: identity -> session -> one automatic session
async function minimalClientSession():
// 1. Consent is the first authority (CMP-over-GCM; every signal unknown fails closed to denied).
var consent = parseConsent(document, UIAF_CONFIG) // REAL API (document)
if not automaticSessionAuthorized(consent): return // status resolved/not_applicable AND effective.analytics_storage allowed
// AND documented analytics purpose โ€” else: no persist, no send, no session state
// 2. Identity: the browser-held uiaf_uid cookie is the ONLY authority.
var cookieUid = readValidUidCookie() // canonical-regex validated read of the browser-held cookie
var isNew = false
if cookieUid == null:
// No cookie: closed create call. (Full implementations wrap this boundary in the origin-wide
// uiaf-identity-issuance Web Lock so at most one tab calls the route โ€” spec 09.)
var resp = await fetch("/api/uiaf/cookie", { // REAL API (fetch)
method: "POST", headers: { "Content-Type": "application/json" }, body: "{}" })
if resp.status != 200: return // 400/403/429/5xx: identity indeterminate; bounded retry, never a tight loop
var issuance = parseJsonStrict(await resp.text())// exactly { uid, recovery }
cookieUid = readValidUidCookie() // MANDATORY authoritative post-response re-read
if cookieUid == null: return // unreadable => indeterminate: no persistence write, no send
if issuance.recovery != null:
mirrorGovernedPair(cookieUid, issuance.recovery) // atomic { uiaf_uid, uiaf_recovery } script-storage pair
isNew = true
var identity = { uid: cookieUid, session_id: null, session_seq: null, session_start: false,
is_new: isNew, resolution_method: "cookie", confidence: "high" }
// 3. Session ownership: AWAIT the allocator once per document; identity resolution never manufactures it.
var owned = await ensureSessionOwnership() // uiaf-session-alloc:<session_id> Web Lock, 250 ms deadline;
// proven { session_id, owned: true } reused for the document lifetime;
// coordination exhaustion degrades to owned: false (null-session emission)
// 4. Attribution from trusted navigation evidence (click IDs only when effective.ad_storage is allowed).
var attribution = captureAttribution(location.href, navigationDescriptor()) // REAL API (location.href)
// 5. Re-read BOTH authorities immediately before freeze/send.
consent = parseConsent(document, UIAF_CONFIG)
if not automaticSessionAuthorized(consent): return
if not authoritativeCookieMatches(identity): return // cookie diverged/disappeared => rebuild or drop; NEVER send
// 6. Sequence, reasons, one immutable freeze.
if owned.owned:
identity.session_id = owned.session_id
var seq = allocateSeqSync(owned.session_id) // reads uiaf_session_state.next_seq, persists the increment, NO async yield
identity.session_seq = seq
identity.session_start = (seq == 0)
var reasons = [] // exactly the coherent tuple โ€” no other vocabulary
if identity.session_start: reasons.push("session_open")
if identity.is_new: reasons.push("identity_created")
if attribution.is_new_touch: reasons.push("attribution_touch")
var frozen = assembleAndFreeze("session", identity, attribution, consent, reasons)
// event_id = fresh UUIDv4; RFC 8785 (JCS) canonical bytes, <= 32768 bytes, frozen ONCE
// 7. Same-origin relay send. The frozen bytes are the unit of retry โ€” never re-serialized.
var result = await postRelay(frozen) // 2xx => acknowledged; 4xx/unexpected final 3xx => terminal drop;
// 5xx/network => durably enqueue the byte-identical body under the same event_id
if result.acknowledged OR result.durablyEnqueued:
writeDirtyBaseline(jcsProjection(identity, attribution, consent)) // canonical projection STRING, never a hash

The relay is the recommended delivery path. It lives on a deployment-chosen, neutral first-party path. Avoid tracker-shaped names: collect, _ga, _fbp, _gcl, track, analytics. Its validation pipeline runs in exactly this order (Implementation Guide ยงDelivery; UIAF-04-EP-007):

// PSEUDOCODE โ€” same-origin relay route
route POST <deployment-chosen neutral path>:
if contentLength(request) > 40960: return 413 // hard pre-buffering ceiling โ€” before any parsing
if not sameOriginChecked(request): return 403 // Origin/Sec-Fetch-Site + CSRF + per-source rate limit
var raw = readBody(request)
// 1. Raw-byte preconditions FIRST: fatal UTF-8 decode, <= 32768 bytes, duplicate-member rejection,
// JSON parse, schema/enum validation, RFC 8785 canonical-JCS byte equality โ€” before any object is trusted.
// 2. Own-only canonicalization: force _meta.emitter = "client" for browser submissions
// (a public/browser assertion of "server" is rejected).
// 3. Semantic/reference validation of the canonicalized object (the shared reference validator).
var payload = relayPipeline(raw) // any failure => 4xx terminal; nothing partially processed
forward(payload, downstreamAuth) // server-added authentication; the browser holds no secrets
return 204 // 2xx acknowledgement

This is what the flow above emits on a first-ever visit from a Google Ads click with full consent. It is copied byte-for-byte from the committed golden fixture docs-site/schema-tools/fixtures/valid/session-identity-created.json, which the shared reference validator accepts as-is:

{
"event": "session",
"event_id": "3f6b1c2a-9d4e-4f7a-8b2c-1e5a7c9d3f04",
"timestamp": "2026-07-19T14:30:00.000Z",
"identity": {
"uid": "f81d4fae-7dec-4ec9-a765-00a0c91e6bf6.1647291600",
"session_id": "a3b8c9d0-1234-4678-9abc-def012345678",
"session_seq": 0,
"session_start": true,
"is_new": true,
"resolution_method": "cookie",
"confidence": "high"
},
"consent": {
"signals": {
"analytics_storage": "granted",
"ad_storage": "granted",
"ad_user_data": "granted",
"ad_personalization": "granted"
},
"status": "resolved",
"effective": {
"analytics_storage": "allowed",
"ad_storage": "allowed",
"ad_user_data": "allowed",
"ad_personalization": "allowed"
},
"gpc": {
"detected": false,
"applicable": false
},
"source": "cmp_cookiebot",
"state_updated_at": 1750000000,
"tier": 1
},
"attribution": {
"first_touch": {
"touch_id": "a3b8c9d0-1234-4678-9abc-def012345678",
"source": "google",
"medium": "cpc",
"campaign": "spring_sale",
"term": null,
"content": null,
"click_ids": {
"gclid": {
"value": "Cj0KCQjw84anAbCd",
"captured_at": 1750000000,
"expires_at": 1790000000
}
},
"referrer": "google.com",
"landing_url": "example.com/products/shoes",
"timestamp": 1750000000,
"custom": {}
},
"last_touch": {
"touch_id": "a3b8c9d0-1234-4678-9abc-def012345678",
"source": "google",
"medium": "cpc",
"campaign": "spring_sale",
"term": null,
"content": null,
"click_ids": {
"gclid": {
"value": "Cj0KCQjw84anAbCd",
"captured_at": 1750000000,
"expires_at": 1790000000
}
},
"referrer": "google.com",
"landing_url": "example.com/products/shoes",
"timestamp": 1750000000,
"custom": {}
},
"count": 1,
"is_new_touch": true
},
"page": {
"url": "https://example.com/products/shoes",
"path": "/products/shoes",
"referrer": "https://google.com/",
"title": "Shoes"
},
"client": {
"user_agent": "Mozilla/5.0",
"language": "en-US",
"viewport": "1920x1080",
"screen": "1920x1080"
},
"event_data": {
"reasons": [
"session_open",
"identity_created",
"attribution_touch"
]
},
"_meta": {
"uiaf_version": "3.1.1",
"data_quality": "full",
"attribution_completeness": "full",
"emitter": "client"
}
}

Why each group has exactly this shape:

Field pathWhy this value
event, event_id, timestampOne of exactly three data-plane events (session/conversion/identify); a fresh UUIDv4 per logical event, immutable across retries; RFC 3339 UTC with milliseconds
identity.uidServer-minted canonical form <uuidv4>.<unix-seconds>, adopted from the authoritative cookie re-read โ€” never client-generated
identity.session_seq, session_startFirst allocation of the owned session โ‡’ 0 โ‡’ session_start: true
identity.is_new, resolution_method, confidenceThe coherence tuple (UIAF-04-ID-001): is_new: true โ‡’ non-null uid and resolution_method: "cookie"; fresh issuance โ‡’ confidence: "high"
consentResolved via cmp_cookiebot; all four effective values allowed โ‡’ derived tier: 1 (serialized shorthand only โ€” nothing branches on it); state_updated_at is the selected recordโ€™s material-change time; consent_record_id is omitted here, never serialized as null
attributionOne touch: first_touch == last_touch, count: 1, is_new_touch: true; gclid captured because ad_storage is allowed, with captured_at/expires_at; landing_url is host + path, no query string
page, clientRequest context; url/path consistent, referrer the cross-site navigation referrer
event_data.reasonsExactly the coherent tuple for this state (UIAF-04-REASON-001): session_open (seq 0), identity_created (is_new), attribution_touch (is_new_touch) โ€” no other vocabulary
_metauiaf_version: "3.1.1"; data_quality: "full" (all four effective values allowed, nothing stripped); attribution_completeness: "full" (click IDs present โ‡’ full); emitter: "client" (browser submission โ€” the relay forces this value)
  • Cookie route: closed create/recover bodies, exhaustive 200/400/403/429/5xx status map, valid-cookie no-set rule. See Endpoint Schema (UIAF-04-AUX-003, UIAF-04-AUX-008).
  • Identity: server-minted UID only, candidate-only responses, mandatory re-reads. See Identity Management (UIAF-02-COOKIE-001/002) and the Implementation Guide (UIAF-09-ID-001).
  • Consent: every persist and send is gated on the effective vector; pending sends nothing. See Consent Integration, UIAF-09-CONSENT-001, UIAF-09-WAKE-001.
  • Delivery: one freeze; retries byte-identical under the same event_id; relay 413 pre-parse ceiling and exact pipeline order. See UIAF-04-RETRY-002, UIAF-04-EP-007.
  • Verification: validate every emission against the payload schema and the shared reference validator, then run the Test Scenarios and the Conformance policy.

Beyond the slice, the shortest useful path through the full material:

  1. Understand the shape: Introduction. The website (client plus server integration) resolves identity, captures attribution, and delivers payloads. The endpoint validates, deduplicates, and orders. Exactly three data-plane events exist: session, conversion, identify, in the event registry.
  2. Wire identity: Identity Management. The server alone issues the identity cookie. The client re-reads the browser-held cookie as the authority after every endpoint response. Creation is consent- and policy-gated.
  3. Capture attribution: Attribution Capture. The normalization pipeline, the touch boundary from trusted navigation evidence, bounded touchpoints. While consent is pending, only the closed three-key pending context (source, medium, campaign) exists, in current-document memory only.
  4. Assemble and send payloads: Endpoint Schema. Validate everything you emit against the canonical payload schema. Freeze bytes before sending; retries resend the identical frozen body under the same event_id.
  5. Apply the consent overlay: Consent Integration. Observed signals, lifecycle, and effective permissions gate every storage write and every send. The default pending state sends nothing.
  6. Harden and verify: Data Handling and the Implementation Guide, then the Test Scenarios and the Conformance policy. The FAQ covers the questions that come up in practice.