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 yes

The 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 needed

Your 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.

Honest scope. Fidacy never sees, stores or judges the conversation content, only hashes and verdicts travel. This is integrity proof plus a commitment gate, not content moderation.