Cookbook
Build a generation integration
If your product generates advice with an LLM on behalf of regulated firms, you can attest each generation into that firm's Bedrock ledger. Integrate once, and every firm you serve gets a complete, tamper-evident record of how their advice was produced.
Who this is for
This guide is for tool builders: paraplanning platforms, suitability-report generators, adviser copilots, and any product that calls an LLM to draft financial advice on behalf of regulated firms. If you are a firm attesting your own in-house generation, you do not need any of this: use your own API key directly as shown in Attestation.
The model
A generation belongs to the firm that gave the advice, not to your product. So every attestation you send is written into that firm's ledger. You attest on the firm's behalf, and Bedrock attributes the record to the firm and the named adviser, never to you.
Connecting a firm
The connection uses an OAuth-style authorisation grant, the same pattern Bedrock's own CRM integrations use. Your product is registered once as a Bedrock connected app; each firm then authorises it individually.
- Register your product in the Bedrock developer portal. You receive a
client_idandclient_secret. - Send the firm to Bedrock's authorisation screen, requesting the
generations:writescope. A firm admin approves the connection. - Bedrock returns an authorisation code to your callback. Exchange it for a per-firm refresh token bound to your app and that firm.
- Exchange the refresh token for short-lived access tokens as needed. Each token is write-only and can only append generations to that firm's ledger.
The firm can revoke your product at any time from their Bedrock settings, which invalidates the grant immediately without affecting any other credential.
Least privilege
Credentials issued through the grant carry only the generations:write scope. They cannot read ledger records, issue certificates, manage webhooks, rotate keys, or see any other firm's data.
Attesting on a firm's behalf
Initialise the SDK with the firm's access token. The adviser is who Bedrock records as the actor on the ledger record, so it must be the individual responsible for the advice.
import { Bedrock } from '@bedrockgovernance/attest';
// One client per connected firm, using that firm's access token.
const bedrock = new Bedrock({
accessToken: await tokenStore.getFirmToken(firmId),
});
await bedrock.attest({
clientReference,
correlationId, // minted once per drafting session, reused at submission
adviser: { name: adviser.name, fcaRef: adviser.fcaRef },
model: { provider: 'anthropic', name: 'claude-opus-4-8' },
systemPrompt,
messages,
retrievedContext,
output: { content: draft, finishReason: 'stop' },
});Because you serve many firms from one codebase, wire the attestation in once at your generation boundary and pass the firm context through. If you use the AI SDK, the middleware does this for you. Resolve the firm token and adviser per request:
import { wrapLanguageModel } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';
import { attestMiddleware } from '@bedrockgovernance/attest/ai-sdk';
function modelForRequest({ firmId, clientReference, correlationId, adviser }) {
const bedrock = new Bedrock({ accessToken: tokenStore.getFirmToken(firmId) });
return wrapLanguageModel({
model: anthropic('claude-opus-4-8'),
middleware: attestMiddleware({ bedrock, clientReference, correlationId, adviser }),
});
}Linking to the reviewed document
A piece of advice is usually several generations: a draft, a regenerated section, a revision. Mint one correlationId when the drafting session begins and pass it on every attest()call, so those records form one set in the firm's ledger.
Carry the same correlationId through to submission. When the firm submits the resulting document for review, through your product, a CRM integration, or the API, the submission passes the correlationId and Bedrock binds the whole generation set to the review job and seals it. The set is complete at submission.
Bedrock also stamps an outputHash on every generation, and compares the submitted document against them. A match to the final generation confirms the model's output was used verbatim; a mismatch is recorded as a human edit after generation. Order within the set is optional: set supersedes to name the generation a revision replaces, or leave it and rely on ledger order.
Checklist
- Register once as a connected app; request only
generations:write. - Store per-firm refresh tokens, never raw firm API keys.
- Identify the responsible adviser when you have them, so the record names an individual rather than the firm credential.
- Mint one
correlationIdper drafting session, tag every generation with it, and pass it again at submission. - Send prompts, context, and output verbatim, so Bedrock hashes them into the chain.
- Use an idempotency key so retries do not duplicate records.