Guides
Conversation Receipts, in ten minutes
A court already held a company to what its chatbot invented (Air Canada, 2024), and EU AI Act Art. 50 chatbot transparency applies from August 2, 2026. This guide wires the answer: every message hashed locally, every commitment gated, and a receipt both sides can verify.
1. Install (proof works with no account)
npm i @fidacy/session
Hashing, digests, export and the tamper check run offline. Anchoring and the gate need a free API key (assess:write) from app.fidacy.com.
2. Wire the chain
import { createSession } from "@fidacy/session";
const session = createSession({ kind: "conversation", label: "case-4711" });
// your chat loop: one line per message (content never leaves your infra)
session.add("user", userMessage);
session.add("assistant", botReply);Using the Vercel AI SDK? Zero lines per message instead:
import { wrapLanguageModel } from "ai";
import { createSession, sessionMiddleware } from "@fidacy/session";
const session = createSession({ kind: "conversation", label: ticketId });
const model = wrapLanguageModel({
model: openai("gpt-5"),
middleware: sessionMiddleware(session), // every prompt + reply lands in the chain
});3. Gate what the bot can commit to
const verdict = await session.gate(mandate, {
apiKey: process.env.FIDACY_ENGINE_API_KEY!,
kind: "message_send",
});
if (!verdict.allowed) refusePolitely(); // deny-by-default: review is NOT a yesThe verdict is recorded into the chain, so the anchored receipt itself proves the gate ran. On any engine error the call throws and your bot refuses: fail closed, never fail open.
Mandate gallery
The mandate is a plain object describing the commitment. Common shapes:
Refund (kind message_send):
{ type: "refund", amount: 900, currency: "USD", case: "case-4711" }Quote above a threshold (kind message_send):
{ type: "quote", amount: 12500, currency: "EUR", product: "policy-home", case: "q-2231" }Claim approval (kind claim_document):
{ type: "claim_approval", amount: 4300, currency: "USD", claim: "clm-8891" }Document send (kind message_send):
{ type: "document_send", document: "policy-terms-v3.pdf", recipient: "customer", case: "case-4711" }4. Close with the receipt
const receipt = await session.anchor({ apiKey: process.env.FIDACY_ENGINE_API_KEY! });
customerLink = session.verifyUrl(); // no account, no key neededYour customer verifies at fidacy.com/verify with no account: paste the link, the digest, or the exported transcript (recomputed in the browser, nothing uploads). Changing one character of one message breaks the match.