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.

Point Fin's custom actions at your backend. Gate credits and plan changes with 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.