@checkfu/sdk, with no private hook available to it that is not
available to you.
Review capability status before planning a
production integration. The public collaboration surface this guide uses is
covered by V-CHN-001, V-CHN-002, V-CHN-003, V-CHN-005, and V-CHN-015 in the
verification ledger; the customer-built journey itself is V-CHN-024 and
carries a recorded gap.
What your adapter owns, and what Checkfu owns
Your adapter owns everything provider-shaped. Checkfu owns everything the Session means.
Two consequences are worth stating before you write a line of code.
Checkfu never verifies your provider’s signature. It cannot: you own the
app registration and the signing secret. Verify the request before you call
any Checkfu operation, and reject it there. A forged payload that reaches
ingestConversation is a real message as far as the platform is concerned.
Delivery is best effort, and that is a deliberate posture. External
channels carry the grade their substrate provides. Checkfu does not claim
exactly-once provider delivery on any channel, rented or custom. What it does
guarantee is that two instances of your adapter cannot both post the same
answer — see One winner below.
The five calls, in the order a turn makes them
1. Pair the installation and its scopes, once
Before any traffic, pair the provider installation that stands for your provider tenant, create the surface scopes that stand for the places inside it —root, team, channel, personal — and place an agent on those scopes,
declaring its identity posture: autonomous (the agent acts as its own
service Principal) or delegated (it acts as the resolved person).
This is one-time setup and it is not channel-specific: it is the same flow
every installed agent uses, whoever built the surface.
Installations covers pairing proofs, the scope
hierarchy, and identity posture in full, and
Install an agent blueprint walks a
worked example.
What matters for the rest of this guide is the three ids that setup produces —
the provider installation, one surface scope, and the agent placement. They
are your adapter’s whole configuration, and every call below carries all
three.
2. Resolve the speaker to a Principal
Map the provider’s stable user id — never a display name, never an email — to a Checkfu Principal. Checkfu stamps the provider identity link from those stable identifiers itself and writes an audit record; your adapter cannot assert a link, which is exactly what makes multiplayer steering, approvals, and audit attribute correctly.external_user_id with your channel key. Two providers can hand
you the same opaque id, and a Principal is not a place to discover that.
3. Resolve the conversation binding
One canonical external conversation binds to one Checkfu Session. The conversation key is akind, a root, and a thread; resolve creates the
binding or returns the existing one, so your adapter never tracks which
threads it has seen.
4. Ingest exactly one provider event
ingestConversation appends one authored drive event to the bound live
Session. It is idempotent on external_event_id, so a provider redelivery
converges on the same disposition instead of driving the agent twice. Give it
the provider’s own message id, namespaced.
disposition — one of driven, observed, replayed,
or filtered — the bound session_id, and, for a driven turn, the exact
event_id and event_sequence of the authored event. observed is what the
authorless occurrences below settle as: the turn is recorded, no Run is
driven, and both coordinates are null. Treat anything other than driven as
“there is no reply coming for this delivery”, and never read a null coordinate
as a zero. Those coordinates are an exclusive
cursor. Read the Session log strictly after event_sequence and you will
never replay an earlier turn’s reply or stop at a historical settlement. Do not
substitute the Session’s latest sequence, and do not treat a missing sequence
as zero; a driven receipt without one is a failure, not a default.
Ingest with retries disabled. A provider redelivery is the retry path, and it
converges server-side on external_event_id. Your own retry loop just makes
duplicates more likely to race.
5. Read the answer from the event log, then post it once
Stream the Session event log from the exclusive cursor and derive the interaction result. Settlement is readable from the log — that is the whole point of the ledger — so never infer it from elapsed time, from the provider’s delivery telemetry, or from the fact that a Run appears to be over. Before you call the provider, claim the presentation.One winner, not exactly-once
The presentation fence is four operations and one rule: claim before you post, settle after.owner token is a finite lease, not a permanent grant. Two replicas racing the
same key produce exactly one winner, and the loser posts nothing.
Three rules the fence depends on:
- A lost outcome settles
indeterminate, never a blind retry. If your provider call times out, dies mid-post, or returns nothing you can read, settleindeterminate. Retrying a provider call whose outcome you do not know is how one answer becomes two. - A settle retry is a settle retry. Repeating
settlewith the samedispatch_tokenand outcome replays the committed fact. It never re-enters the provider. inspectConversationPresentationis how a restarted process finds out where it was, without exposing another replica’s owner token.
Fences your adapter must not cross
- Approvals never ride the channel. Render an approval as a plain link into the Checkfu-native approval surface, where the one-shot decision authority already lives behind Checkfu authentication. No channel carries authorization weight, and no provider’s interactive buttons settle anything.
- Provider delivery receipts are not settlement.
delivered,read, and the provider’s own status webhooks are telemetry about a transport. What the agent decided is in the Session event log and nowhere else. - Do not fabricate a person. An occurrence your provider authenticates but
cannot attribute — a deletion, a reaction, a system message — is ingested as
an authorless observation and comes back
observed, notdriven. Do not borrow the agent placement’s acting Principal to fill the field, and do not invent one. - Do not put message content in your coordination store. Locks, dedupe keys, and cursors are coordination. The Session is the transcript.
When the contract is not enough
If you find something the public operations cannot express, record it as a gap — open an issue naming the operation you needed and the journey it blocks. Do not route around it with a private hook, an undocumented endpoint, or a second transcript. That fence (D115) is the reason the public contract is worth building on: everything Checkfu’s own conversation host can do, your adapter can do, and the day that stops being true is a bug in the contract rather than a feature of being first-party.Next
- Custom channel adapter — a minimal reference adapter in one file.
- Conversation bindings — the binding and ingest semantics in full.
- Installations — installations, surface scopes, and identity posture.
- Handle an approval — the surface an approval link points at.