Attests a KYB/KYC verdict CFX produced itself back to RouteFusion — RF's reliance model. UNDOCUMENTED publicly (see COMPLIANCE_EVALUATION_MUTATION) and a hard precondition for finalizeEntity, which is rejected with "compliance_evaluation is required before finalization" until this has run for the entity.
Registers a payout destination against a RouteFusion business entity and
returns its id — the beneficiary_id every createTransfer names, and the
value CFX stores as routeFusionBeneficiaryId on the payment instrument.
One call serves US wire, US ACH and SWIFT: RouteFusion's bank-identifier
arguments are polymorphic scalars, so the rails differ only in which of
account_number / routing_code / swift_bic are populated. There is no
per-rail split to mirror Victor's createUsWireCounterparty /
createAchCounterparty.
user_id is the org JWT's own account, so RouteFusion attributes the
beneficiary to the identity CFX acts as — matching the entity mutations.
Pulls funds from a registered incoming-transfer account. Moves no money on its own — two-phase, like the payout, and committed by finalizeIncomingTransfer.
This is the ONLY RouteFusion pull path that is safe to retry, and the id it
returns is why. createDirectDebitTransfer takes only
(direct_debit_account_id, entity_id, amount), is single-phase, and
returns a DirectDebitTransfer carrying no field to match on — so a retry
after a request that already succeeded re-debits the customer with nothing
to detect it by. This one is two-phase, which makes it a ticket, and the id
is its handle.
A caller MUST record this id before finalizing, and consult it with
getIncomingTransfer on every attempt. That ordering is the guard:
money cannot leave the customer's bank without the id being stored first,
so a retry always finds the ticket it already minted rather than opening a
second debit. executeRouteFusionPayout in @cfxlabsinc/withdrawal-services
is the worked example on the payout side.
reference is a reconciliation label. Do not build a lookup on it — see
getIncomingTransfer.
Registers the customer's external bank account as a source RouteFusion may debit, bound to the wallet the pulled funds land in. Returns its id.
The pull-side counterpart of createBusinessBeneficiary — and not interchangeable with it: a beneficiary is where money goes, this is where it comes from, so an instrument used in both directions needs both.
The individual counterpart of createBusinessBeneficiary.
Kept as a separate call rather than folded in behind a flag because the
two carry genuinely different KYC records — a natural person registered
through the business mutation would reach RouteFusion with their name in
business_name. Callers branch on the instrument's beneficiary type.
Creates an outbound payout. Moves no money on its own — RouteFusion's payout is two-phase and the transfer stays unfinalized until finalizeTransfer commits it.
There is no idempotency key on this mutation, and none is needed: the id
this returns IS the handle. A caller that records it before finalizing can
read the ticket back with getTransfer and re-accept it; a caller
that does not record it will mint a second ticket on every retry.
reference is a label for reconciliation, not a lookup key — RouteFusion
offers no way to query by it.
walletId is the pool debited — for CFX that is the wallet on the source
ledger account's RouteFusion virtual account, which IS the pool (RF has no
separate FBO account the way Victor does).
Registers a webhook subscription so RouteFusion pushes events for the given
resource category to url. Returns the subscription id.
headerKey/headerValue set a static header RouteFusion sends on every
webhook (e.g. a shared secret). Optional.
Removes a beneficiary. Returns whether RouteFusion confirmed it — null
is treated as a refusal, mirroring updateRepresentative, so a
caller cannot mistake "no answer" for "deleted".
Disables a previously-created webhook subscription.
RouteFusion's onboarding requirements are parameterized by country,
entity type, and business type — NOT a fixed set (the documented
[proof_of_registration, proof_of_ownership, bank_statement] trio is only
the US example response). Returns the OR-of-AND document sets: satisfy
every document in ANY ONE inner array to meet KYB requirements. Document
types the response returns that this client doesn't recognize are dropped
(defensive — an ill-formed set could never be satisfied anyway).
Commits a created pull. This is the call that actually moves money.
Commits a created transfer. This is the call that actually moves money.
Safe to repeat for a transfer already finalized — RouteFusion returns the same id — which is what lets the withdrawal activity recover from a crash between create and finalize.
Reads a beneficiary's lifecycle state and the rails RouteFusion says it can serve.
Returns null for an unknown id rather than throwing — unlike getWallet. The asymmetry is deliberate: a missing wallet under a VA we believe is ACTIVE is a state disagreement on the money path, while a missing beneficiary is the ordinary answer for an instrument that was never registered, which every caller here has to handle anyway.
What RouteFusion currently holds for the entity — the authority a drift sync diffs against (RF's own state, not a local snapshot).
Reads one pull by id, or null if RouteFusion does not know it.
THE canonical single-pull read, and the pull's whole idempotency guard — the mirror of getTransfer. A retry consults the id it recorded before finalizing: finish the ticket if it is still open, report it if the debit already went through.
It replaces an incomingTransfers(search_terms: reference) lookup, which
was the construction that turned out to be inert on the payout side —
search_terms there matches a transfer's own id rather than its
reference, so that guard never fired once. This endpoint has NOT been
probed, and the swap does not depend on one: addressing a ticket by the id
that minted it is exact where a fuzzy scan is not, and it reaches an
unfinalized ticket, which a listing does not.
The stakes are the mirror of the payout's. There, a missed guard pays a beneficiary twice; here it debits the customer's own bank twice, and the money has to be given back.
Organization-wide incoming transfers (credits), with the pagination
envelope retained — the sync needs items_total to compute its page list
up front, the way metcap's sync does. page is 0-indexed here;
RouteFusion's PaginationInput is 1-indexed, hence the +1.
No startDate/endDate: FilterInput — CONFIRMED AGAINST LIVE
INTROSPECTION AND A LIVE QUERY PROBE — is { key, values, value }, where
key addresses a literal DB column and values does exact/IN-list
matching, not a range (see INCOMING_TRANSFERS_PAGE_QUERY).
RouteFusion has no range/comparison operator on this endpoint at all, so a
date window here is a client-side concern: a caller that needs one must
filter transaction_date itself after fetching a page.
Reads one transfer by id, or null if RouteFusion does not know it.
THE canonical single-transfer read, and the only one that reaches a minted
but unaccepted ticket: the transfers listing carries transactions, so an
open ticket is absent from it under every value of includeNotFinalized,
and transfers(search_terms:) matches a transfer's own id rather than its
reference (both probed against the sandbox on 2026-09-01). RouteFusion
publishes no by-reference query at all.
That makes the id the whole idempotency story. Both callers turn on it: the
webhook consumer re-reads here because the event body's state may be stale,
and executeRouteFusionPayout re-reads the ticket it recorded before
finalizing, so a retry re-accepts that one instead of minting a second.
Organization-wide outbound transfers, from_date onward. page is
0-indexed for parity with VictorTransactionClient.getTransactions; the
offset is derived, so callers never compute it.
endDate is accepted for interface parity with
VictorTransactionClient.getTransactions, but is NOT sent to
RouteFusion: ListFilter — CONFIRMED AGAINST LIVE INTROSPECTION — has no
upper-bound date field (only from_date; see TRANSFERS_QUERY).
There is no server-side way to close the window, so a caller that needs
one must stop consuming pages once created_date passes endDate.
Reads a wallet's lifecycle state and balances — the two things a payout
gate needs, in one round trip. Used by the withdrawal quote path; see
lookupRouteFusionRoutingPredicates in @cfxlabsinc/withdrawal-services.
THROWS when RouteFusion returns no wallet for the id. A wallet missing on an id we hold against a VA we believe is ACTIVE is a state disagreement, not an "unknown balance": returning null would let a caller treat the pool as merely unmeasured and pass the route through, so it fails loudly and retryably instead.
Per-representative document requirements
(docs.routefusion.com/reference/representative-required-fields) — same
OR-of-AND documents shape as entityRequiredFields, but keyed
only by the representative's country + program (no entity/business
type). NOTE: for this query the document's type field is a purpose
category ("identity_verification") shared across many documents, not the
file_enum — the actual RouteFusionFileEnum value lives in enum (e.g.
"passport", "front_drivers_license"), so this narrows on doc.enum.
Applies a PARTIAL update to an existing beneficiary.
Only the keys the caller supplied are sent. That is load-bearing, not a micro-optimization: RouteFusion reads a present-but-null argument as "blank this field", so serializing every absent key would wipe live KYC data on a beneficiary whose bank name was all the caller meant to change. Same trap toRepresentativeVariables guards on the representative mutations.
currency and bank_country are not updatable — RF's mutation does not
accept them, so BusinessBeneficiaryUpdateInput omits them and
rebanking means creating a new beneficiary.
Partial update — send only the fields that changed.
The individual counterpart of updateBusinessBeneficiary, with the same partial semantics: only the keys the caller supplied are sent, because RouteFusion reads a present-but-null argument as "blank this field".
Push CFX-side edits to a representative RouteFusion already holds.
createRepresentative is the only place CFX ever sends a person's details
and it runs exactly once per person (stamped by
related_person.data.routeFusionRepresentativeId), so without this an
operator who supplies a missing tax number or postal code after onboarding
has started sees it land in CFX and never reach RouteFusion — and
finalizeEntity keeps refusing the submission for the very field they just
filled in. The entity has the same shape and the same fix
(updateBusinessEntity via syncPinnedEntity).
A true partial update: send only what the caller wants written.
Returns RouteFusion's bare Boolean. There is no accompanying message —
a rejection RouteFusion can articulate comes back as a GraphQL error (so it
THROWS, carrying the reason), and false is the residual "refused, no
reason given" case. A caller that needs to explain a false has nothing
beyond the fact of it.
Attaches a document to an entity or a representative via RouteFusion's
GraphQL multipart singleUpload mutation (Apollo Upload!, header
apollo-require-preflight: true) — NOT the JSON transport graphql
uses, since the file body can't be JSON-encoded. entity_id /
representative_id and file_enum are inlined into the query string
(RF's file_enum is an unquoted GraphQL enum literal, and its id argument
is a custom scalar that takes a string literal, so neither rides as a typed
$variable the way $file does). Both are therefore validated before
interpolation — file_enum against the known enum, the id against a strict
id charset — so a crafted value can never break out of the query. Exactly
one of entityId/representativeId is required.
The RouteFusion
UserAccountid this client acts as, decoded from the org JWT'sidclaim. Sent asuser_idon entity mutations.