Intercom Fin + ContextDB
Fin answers from your help center. ContextDB remembers your customer.
Use Fin for knowledge answers, then put credits, plan changes, and account updates behind a backend that checks the customer identity, current account state, host authorization, and a durable memory decision.
evaluate_action, execute only the act
branch, and close the decision with report_execution.
recall_for_action is a compatibility retrieval helper, not
host authorization. Examples use the public Cloud client against
ContextDB Cloud. The local engine
uses pycontextdb. This independent guide has no vendor
partnership or endorsement.
The gated action
Check the customer's confirmed credit before paying it.
A customer writes "you said I'd get a credit." Maybe someone did, in
which case ContextDB can return act with evidence. It can
also return ask or abstain. The backend
still authenticates the Fin action, checks account authorization and
current state, and reports whether the host executed or skipped it.
# backend endpoint behind a Fin custom action from contextdb_cloud_client import CloudClient from fastapi import HTTPException, Request @app.post("/fin/actions/issue-credit") async def issue_credit(req: Request): action = await authenticate_fin_action(req) # verifies the signed host request customer = action.customer_id # canonical ID from authenticated context amount = parse_money(action.arguments["amount_label"]) account = await billing.get_account(customer) if not await billing.can_issue_credit( actor_id=action.actor_id, account=account, amount=amount ): raise HTTPException(status_code=403, detail="not authorized") async with CloudClient(BASE_URL, api_key=KEY) as cdb: decision = await cdb.evaluate_action( customer, f"credit of {amount} promised" ) if decision.outcome == "act": try: credit = await billing.issue_credit( customer, amount, expected_version=account.version ) except StaleAccountState: await cdb.report_execution( customer, decision.decision_id, "billing.issue_credit", "failed", idempotency_key=f"fin-receipt-{decision.decision_id}", error_code="current_state_changed", ) return {"outcome": "abstain", "say": "The account changed. I did not issue the credit."} await cdb.report_execution( customer, decision.decision_id, "billing.issue_credit", "succeeded", idempotency_key=f"fin-receipt-{decision.decision_id}", external_ref=credit.ref, ) return {"outcome": "issued", "reference": credit.ref} if decision.outcome == "ask": await cdb.report_execution( customer, decision.decision_id, "billing.issue_credit", "skipped", idempotency_key=f"fin-receipt-{decision.decision_id}", ) return {"outcome": "ask", "say": "Please confirm the promised credit with the team."} if decision.outcome == "abstain": await cdb.report_execution( customer, decision.decision_id, "billing.issue_credit", "skipped", idempotency_key=f"fin-receipt-{decision.decision_id}", ) return {"outcome": "abstain", "say": "I cannot issue this credit. I will escalate it."} raise RuntimeError("unknown ContextDB action outcome")
Writing memory
Save the important customer details when a conversation ends.
When a conversation resolves, store what should outlive it: the commitment a teammate made has third-party provenance, while the customer's own words are user-stated. Neither source label serves as the host's identity or authorization check.
@app.post("/fin/on-conversation-closed")
async def on_closed(req: Request):
convo = await authenticate_fin_webhook(req)
customer = convo.customer_id
async with CloudClient(BASE_URL, api_key=KEY) as cdb:
# a teammate's promise has third-party provenance
await cdb.remember(
customer,
"one-month service credit approved for the March outage",
source="third_party",
confidence=0.9,
action_relevant=True,
idempotency_key=f"fin-{convo.id}-credit",
)
# this question is user-stated, but it is not an authorization
await cdb.remember(
customer,
"asked whether annual billing gets a discount",
source="user_stated",
confidence=0.4,
idempotency_key=f"fin-{convo.id}-discount-question",
)
return {"ok": True}
Fin conversation order
Add memory at four points in a Fin conversation.
| Moment | Fin surface | ContextDB call |
|---|---|---|
| Conversation starts | Custom action: fetch context | recall → customer snapshot for Fin |
| Credit, refund, plan change | Custom action: gated mutation | evaluate_action → branch, then report_execution |
| Customer confirms in-thread | Custom action: confirm | confirm → record authenticated project-credential context |
| Conversation closes | Your webhook handler | remember with source and confidence |
Fin implementation
How customer memory fits around Fin.
Fin already has conversation history. Why add memory?
Conversation history is a transcript. Memory is a curated set of facts with trust metadata attached. The question at credit time is not "did someone mention a credit?" but "what does memory policy decide for this request?" ContextDB answers that question. Your backend still decides whether this actor may change the current account.
Does this work alongside human teammates in the inbox?
Yes. Teammates' commitments become sourced memories, and pending facts can be reviewed in the ContextDB console. An API or MCP confirmation records authenticated project-credential context against one scoped memory. It does not authenticate the end user. The customer host authenticates the end user and retains any end-user attestation. A Console confirmation records operator context. No confirmation proves objective truth. It does not replace your billing permissions.
What identity should I use for the customer?
Your canonical customer ID, the same one across chat, email, and phone. Resolve it from the authenticated Fin request or your own session mapping. Never accept the ContextDB partition from model arguments.
Build the Fin action gate with the SDK.
Use confirmed evidence before one credit or plan change.