File bytes are authoritative. APIs and /v are mirror surfaces for transport, automation, and audit.
Developers
Integration contracts for sealing any file as an original and deterministic verification.
Seal from canonical endpoints, preserve file bytes, and validate with stable protocol fields. This surface is designed for production workflows where audit outcomes must stay portable across API, offline, and optional public /v verification surfaces.
Developers can also add Login with Receiz ID (via the Sign in with Receiz button) using standard OpenID Connect. Users get passkey-first sign-in with magic-link fallback, automatic Receiz account provisioning, and same-identity convergence across passkey + email. Your app gets delegated record/seal/verify actions, live wallet balance reads, and wallet transfers through user-approved access tokens.
Core verification flows remain account-optional: Record, Seal, and Verify all run offline, and trusted portability is enforced by signature v4 policy checks.
Use /api/receiz for original PNG files, or /api/document-seal to seal documents, media, binaries, and folder package components.
Store returned file bytes without transformation. Verdicts are computed against the file itself, not screenshots or re-rendered copies.
Call /api/document-verify (or /api/verify for coordinate checks) and cross-check with the offline reference runtime for deterministic convergence. Trusted verdicts require Receiz signature v4 verification (payload hash + root-key policy + device cert-chain + cert validity window), real g16 proof format, and anchor coherence. The response envelope keys remain stable across success and failure statuses.
Use this profile when record/seal must run fully offline and still verify globally across devices with the same trusted verdicts.
- Use the Receiz trusted signing profile for offline record/seal outputs.
- Trusted v4 signatures bind payload hash to a device certificate chain rooted in pinned trust keys.
- Trusted signing material is local to each device; no account sign-in is required.
- Record/seal runs fully offline without account access.
- Reconnect only for optional trust-policy distribution updates.
- Verification remains public and does not require account sign-in.
- Trusted verifier bundles can be rotated by policy; verification itself stays offline-capable without account access.
- For fully offline environments, distribute verifier bundles with current trusted keys included.
- Server-side key rotation or revocation becomes visible on the next key refresh.
- Passkey-first sign-in with built-in magic-link fallback under one identity.
- Automatic account provisioning for first-time users; no registration form requirement.
- Stable OIDC subject (`sub`) per user, so passkey and email never fork identity mapping.
- No password database, reset flows, or credential-storage liability in your app.
- Standard OIDC/OAuth integration surface with PKCE and existing auth middleware.
- User-approved delegated scopes for `receiz:record`, `receiz:seal`, `receiz:verify`, `receiz:wallet.read`, and `receiz:wallet.transfer` actions.
- Receiz-side account/profile readiness from first successful sign-in.
One user, one subject, every login method.
When users switch between passkey and email magic-link, they keep the same Receiz identity and your app keeps the same mapped user record. New users are created automatically at first successful login.
Key your local user model by OIDC sub. Store email as mutable profile data.
- No existing account required. First successful login auto-creates the Receiz account.
- Passkey-only onboarding is supported. Email can be linked later without changing identity.
- Email-first users can add passkey later and still resolve to the same OIDC subject (`sub`).
- Passkey-first users can add email later and use magic-link as an additional sign-in path.
- Magic-link fallback is available when passkey is unavailable on the current browser/device.
- Users arriving from send-receiz email links are signed in automatically; new users are provisioned in-flow.
- Your app should key users by `sub` and treat email as mutable profile data.
Yes. Passkey-only onboarding is fully supported.
Yes. Both methods converge to one identity and one `sub` mapping.
No. Receiz login is passwordless.
On first successful callback by OIDC `sub`, then upsert on every return login.
curl -X POST https://receiz.com/api/document-seal \ -F "file=@invoice.pdf" \ -F "visualStamp=0"
// Seal any file while preserving original format semantics.
const sealForm = new FormData();
sealForm.append("file", fileInput.files[0]);
const sealed = await fetch("https://receiz.com/api/document-seal", {
method: "POST",
body: sealForm,
});
const sealedBlob = await sealed.blob();
const verifyPath = sealed.headers.get("x-receiz-verify-path");
// Verify deterministic result from the sealed artifact bytes.
const verifyForm = new FormData();
verifyForm.append("file", new File([sealedBlob], "artifact.receized"));
const verify = await fetch("https://receiz.com/api/document-verify", {
method: "POST",
body: verifyForm,
});
const result = await verify.json();
if (!verify.ok) {
throw new Error(result.errors?.[0] ?? "document_verify_failed");
}
// Canonical response keys are always present:
// ok, kind, errors, warnings, bundle, anchor, package
const { ok, kind, errors, warnings, bundle, anchor, package: pkg } = result;
console.log(ok ? "verified" : "rejected", kind, verifyPath, {
errors,
warnings,
hasBundle: Boolean(bundle),
hasAnchor: Boolean(anchor),
packageItems: pkg?.itemCount ?? 0,
});Drop this in as-is to launch passwordless Receiz sign-in. No password tables, reset flows, or credential lifecycle work.
<!-- Sign in with Receiz button -->
<button id="receiz-signin" type="button" class="receiz-signin-trigger">
<img src="https://receiz.com/sign-in-with-receiz.svg" alt="Sign in with Receiz" width="240" height="44" />
</button>
<style>
.receiz-signin-trigger {
border: 0;
background: transparent;
padding: 0;
line-height: 0;
cursor: pointer;
}
.receiz-signin-trigger img {
display: block;
width: 240px;
height: 44px;
}
</style>
<script type="module">
// Sign with Receiz guarantees:
// - Passkey is default auth ceremony
// - Email magic-link is fallback or optional login method
// - New users are auto-provisioned on first successful sign-in
// - Passkey + email converge to the same Receiz identity (same OIDC sub)
const RECEIZ_ISSUER = "https://receiz.com";
const RECEIZ_CLIENT_ID = "YOUR_RECEIZ_CLIENT_ID";
const REDIRECT_URI = "https://yourapp.com/auth/receiz/callback";
const SCOPE =
"openid profile email offline_access receiz:record receiz:seal receiz:verify receiz:wallet.read receiz:wallet.transfer";
const b64u = (bytes) =>
btoa(String.fromCharCode(...bytes)).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
async function createPkce() {
const verifierBytes = crypto.getRandomValues(new Uint8Array(32));
const verifier = b64u(verifierBytes);
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(verifier));
const challenge = b64u(new Uint8Array(digest));
return { verifier, challenge };
}
async function startReceizLogin(loginHintEmail) {
const discovery = await fetch(`${RECEIZ_ISSUER}/.well-known/openid-configuration`).then((r) => r.json());
const { verifier, challenge } = await createPkce();
const state = crypto.randomUUID();
const nonce = crypto.randomUUID();
sessionStorage.setItem("receiz:pkce_verifier", verifier);
sessionStorage.setItem("receiz:state", state);
sessionStorage.setItem("receiz:nonce", nonce);
const auth = new URL(discovery.authorization_endpoint);
auth.searchParams.set("response_type", "code");
auth.searchParams.set("client_id", RECEIZ_CLIENT_ID);
auth.searchParams.set("redirect_uri", REDIRECT_URI);
auth.searchParams.set("scope", SCOPE);
auth.searchParams.set("state", state);
auth.searchParams.set("nonce", nonce);
auth.searchParams.set("code_challenge", challenge);
auth.searchParams.set("code_challenge_method", "S256");
if (loginHintEmail) auth.searchParams.set("login_hint", loginHintEmail.trim().toLowerCase());
location.assign(auth.toString());
}
document.getElementById("receiz-signin")
?.addEventListener("click", () => startReceizLogin());
</script>// /api/auth/receiz/callback (Node/Express example)
import express from "express";
import fetch from "node-fetch";
const app = express();
const RECEIZ_ISSUER = "https://receiz.com";
const CLIENT_ID = process.env.RECEIZ_CLIENT_ID!;
const CLIENT_SECRET = process.env.RECEIZ_CLIENT_SECRET!;
const REDIRECT_URI = "https://yourapp.com/auth/receiz/callback";
app.get("/auth/receiz/callback", async (req, res) => {
const code = String(req.query.code || "");
const state = String(req.query.state || "");
const expectedState = req.session.receizState;
const verifier = req.session.receizPkceVerifier;
if (!code || !state || !verifier || state !== expectedState) {
return res.status(400).send("receiz_oauth_state_invalid");
}
const discovery = await fetch(`${RECEIZ_ISSUER}/.well-known/openid-configuration`).then((r) => r.json());
const tokenRes = await fetch(discovery.token_endpoint, {
method: "POST",
headers: { "content-type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "authorization_code",
code,
redirect_uri: REDIRECT_URI,
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
code_verifier: verifier,
}),
});
const token = await tokenRes.json();
if (!tokenRes.ok || !token.access_token) {
return res.status(401).send(token.error || "receiz_token_exchange_failed");
}
const userInfoRes = await fetch(discovery.userinfo_endpoint, {
headers: { authorization: `Bearer ${token.access_token}` },
});
const profile = await userInfoRes.json();
if (!userInfoRes.ok || !profile.sub) {
return res.status(401).send("receiz_userinfo_failed");
}
// Upsert your app user by stable OIDC subject. Store receiz_sub permanently.
// First login should create your local app user automatically if missing.
// Receiz guarantees passkey and email converge to one receiz_sub per user.
const appUser = await db.users.upsertByReceizSub({
receiz_sub: profile.sub,
email: profile.email || null,
name: profile.name || null,
raw_profile: profile,
});
req.session.userId = appUser.id;
req.session.receizAccessToken = token.access_token;
req.session.receizRefreshToken = token.refresh_token || null;
return res.redirect("/app");
});- Sign users in with passkey-first auth and magic-link fallback using pure OIDC + PKCE.
- Provision Receiz accounts automatically on first successful login, including passkey-only onboarding.
- Let users add email later and keep passkey + magic-link routed to the same Receiz identity.
- Avoid password storage, password reset flows, and account-auth security debt in your app.
- Read identity claims with standard scopes: openid profile email offline_access.
- Request delegated action scopes: receiz:record, receiz:seal, receiz:verify, receiz:wallet.read, receiz:wallet.transfer.
- Call delegated APIs on behalf of signed-in users: /api/connect/record, /api/connect/seal, /api/connect/verify, /api/connect/wallet/me, /api/connect/transfers.
- Open /developers/connect and create an OIDC client.
- Add your exact app callback URL(s), for example https://app.example.com/auth/receiz/callback (not receiz.com), and copy the generated client ID + secret.
- Request action scopes as needed: receiz:record, receiz:seal, receiz:verify, receiz:wallet.read, receiz:wallet.transfer.
- Implement Authorization Code flow with PKCE against discovery metadata.
- Call delegated action APIs with Authorization: Bearer <access_token>.
Keep callback URLs exact (scheme, host, path). Use HTTPS in production and localhost only for local development.
Identity, receipts, and delegated actions in one login.
- No password hashing, reset funnels, credential stuffing defense, or auth-table churn.
- Passkey and email both converge to one Receiz identity per user.
- First login auto-creates the Receiz account; returning login always resolves the same subject.
- Users can approve delegated actions with explicit OIDC scopes.
- Your app can issue verified record/seal/verify operations and run wallet reads/transfers with user-approved tokens.
- Compatible with standard OAuth/OIDC client libraries and callback middleware.