Cookbook
Integrate with an existing pipeline
Wire the platform into your back-office without rewriting it. The pattern is the same regardless of what your existing system looks like.
You already have a system that produces advice (Intelliflo, Iress XPLAN, an in-house Salesforce build, a Python notebook, or something else). The job is to add the platform without ripping that system out. There are three integration points.
1. The submission hook
Wherever your existing system finalises a piece of advice, add a single call to the API to submit it for review. This can be:
- A workflow step in your back-office BPM
- A database trigger watching the “advice ready” state
- A message-queue consumer picking up “advice.finalised” events
- A scheduled job submitting whatever was completed in the last hour
async function onAdviceFinalised(advice: Advice) {
// 1. Upload the finalised PDF directly to S3 via a presigned URL.
const documentKey = await uploadDocument(advice.documentBytes, `${advice.id}.pdf`);
// 2. Submit the uploaded document for review.
const { id: bedrockJobId } = await bedrock.submitAdvice({
documentKey,
documentType: 'SUITABILITY_REPORT',
clientReference: advice.id,
documentReference: advice.documentReference,
factFindSummary: {
riskProfile: advice.riskProfile,
investmentObjective: advice.investmentObjective,
investmentHorizon: advice.investmentHorizon,
capacityForLoss: advice.capacityForLoss,
existingHoldings: advice.existingHoldings,
vulnerabilityFlags: advice.vulnerabilityFlags ?? [],
annualIncome: advice.annualIncome,
netWorth: advice.netWorth,
},
// Only include aiContext when model identity is available
...(advice.modelProvider && advice.modelVersion
? {
aiContext: {
model: { provider: advice.modelProvider, version: advice.modelVersion },
inputs: advice.modelInputs,
outputs: advice.modelOutputs,
},
}
: {}),
});
await db.advice.update({ where: { id: advice.id }, data: { bedrockJobId } });
}See Submit an advice record for the full uploadDocument / submitAdvice implementation.
2. The webhook receiver
See Handle a webhook. This is the path that updates your customer record with the certificate URL.
3. The reconciliation job
Even with webhooks, you should reconcile periodically, say nightly, to catch any events you missed during downtime. List the firm's ledger records from your last successful run and apply anything you have not already seen. The ledger list endpoint accepts startDate/endDate filters and standard pagination.
async function reconcile() {
const lastSeenAt = (await db.config.get('bedrock.lastSeenAt')) ?? '1970-01-01T00:00:00.000Z';
let page = 1;
let highWatermark = lastSeenAt;
for (;;) {
const url = new URL('https://api.bedrockgovernance.com/v1/ledger/records');
url.searchParams.set('startDate', lastSeenAt);
url.searchParams.set('page', String(page));
url.searchParams.set('pageSize', '100');
const res = await fetch(url, {
headers: { 'X-Bedrock-Key': process.env.BEDROCK_API_KEY! },
});
const { data: records, pagination } = (await res.json()) as {
data: Array<{ id: string; sequenceNumber: number; eventType: string; timestamp: string }>;
pagination: { hasNext: boolean };
};
for (const record of records) {
// Idempotent, dedupe on sequenceNumber inside applyEntry.
await applyEntry(record);
if (record.timestamp > highWatermark) highWatermark = record.timestamp;
}
if (!pagination.hasNext) break;
page += 1;
}
await db.config.set('bedrock.lastSeenAt', highWatermark);
}What not to do
- Do not make submission to the platform synchronous with adviser submission. Queue it; treat the submission as fire-and-forget with a retry on failure.
- Do not rely solely on webhooks. They are at-least-once, but networks fail; reconcile.
- Do not skip reconciliation. The whole point of the platform is that the records exist independently of your system; if the two diverge, the ledger is the truth.