The Routable connector polls a Routable workspace and surfaces its settings accounts, companies (counterparties), payables, and receivables to the Connectivity service. It also initiates outbound payables on Routable in response to Formance CreateTransfer and CreatePayout workflows.
Prerequisites#
You need a Routable account with an API key dedicated to Formance. Decide upfront which team member will own payables initiated through Formance — Routable requires an acting_team_member on every payable creation. You can either set this per-connector via config, or per-request via metadata; see Initiating payouts and transfers.
Make sure to create an API key dedicated to Formance. Doing so will improve your auditability and security and will allow you to revoke access to Formance at any time if needed.
Installation#
curl -X POST $FORMANCE_API_URL/api/payments/connectors/routable \
-H "Content-Type: application/json" \
-d @config.jsonConfiguration fields#
| Field | Required | Default | Purpose |
|---|---|---|---|
apiKey | yes | — | Routable bearer token. Sent as Authorization: Bearer <apiKey> on every request. |
endpoint | no | https://api.routable.com | API root. Use https://api.sandbox.routable.com for the Routable sandbox. |
actingTeamMember | no | "" | Default Routable team member ID for payable creation. Optional at the connector level — callers can override per-request via the com.routable.spec/acting_team_member metadata key. If neither is set, payable creation fails with a clear validation error before any HTTP call. |
pollingPeriod | no | 30m | Sync cadence (minimum 20m). |
The connector validates the API key at install time with a tiny GET /v1/settings/accounts?page=1&page_size=1 probe — a bad key surfaces as an install-time error rather than as a FETCH_ACCOUNTS failure later.
Capabilities#
The connector supports the following capabilities:
FETCH_ACCOUNTS— internal settings accounts, polled viaGET /v1/settings/accountsFETCH_BALANCES— settings-accountavailable_amount, derived per-account viaGET /v1/settings/accounts/{id}FETCH_EXTERNAL_ACCOUNTS— companies (counterparties), viaGET /v1/companiesFETCH_PAYMENTS— payables (outbound) and receivables (inbound), viaGET /v1/payablesandGET /v1/receivablesCREATE_TRANSFERandCREATE_PAYOUT— both initiatePOST /v1/payables(transfers are payables with theTRANSFERpayment type)
Webhooks, bank-account creation, and reversals are not yet implemented — follow-up PRs upstream will add them.
Routable caps API throughput at roughly 1.5 payouts per second. The connector declares this via the PluginWithPayoutThrottle interface, so the platform routes CreatePayout and CreateTransfer workflows through a dedicated Temporal task queue throttled to that rate. See Operations → Connector reliability for the cross-cutting behaviour.
Asset model#
Routable returns asset codes as ISO currency strings (USD, EUR, KWD). The connector formats them as Formance assets via go-libs/v3/currency.FormatAsset — USD/2, EUR/2, KWD/3, and so on. Unsupported currencies are skipped (logged) without dropping the rest of the page.
Amounts are decimal strings on Routable's side and are converted to integer minor units with half-up rounding before being written to Connectivity Payments.
Status mapping#
Routable's payable statuses collapse to the standard Connectivity Payment statuses:
Routable status | Payment status |
|---|---|
draft, ready_to_send, pending, scheduled, initiated, processing, in_transit, awaiting_delivery | PENDING |
completed, paid, externally_paid, delivered | SUCCEEDED |
failed, returned, nsf | FAILED |
stopped, canceled, cancelled, voided | CANCELLED |
expired | EXPIRED |
| anything else (or empty) | UNKNOWN |
Comparison is case-insensitive.
The payment scheme is mapped from Routable's delivery_method: ach_* → ACH, everything else → OTHER.
Metadata keys#
All Routable-specific fields are namespaced under com.routable.spec/. The full list lives in the connector's MAPPINGS.md; the highlights:
Account metadata (internal accounts)#
object, type, is_valid, currency_code, plus type_details.account_type, type_details.bank_name, type_details.account_number, type_details.routing_number.
External-account metadata (companies)#
object, type, status, country_code, is_vendor, is_customer, is_archived, external_id, business_name, display_name, plus the structured registered_address.* keys.
Payment metadata#
For payables (PAYOUT) and receivables (PAYIN): type, delivery_method, status, external_id, memo, reference. Two correlation aliases also land on every Payment we initiated:
com.routable.spec/payment_initiation_reference— the originating PaymentInitiation reference (only present when Formance initiated the payable; payables created in Routable's UI don't carry it).com.routable.spec/payable_id— the Routable payable UUID (mirrorsPayment.Reference).
See Correlating an initiation with the synced payment for how to use these.
Initiating payouts and transfers#
CreateTransfer and CreatePayout both translate the Formance PSPPaymentInitiation into a Routable POST /v1/payables. Most fields are derived from the structured initiation; a few Routable-specific knobs are exposed as initiation metadata:
| Metadata key | Required | Default | Maps to | Purpose |
|---|---|---|---|---|
com.routable.spec/type | no | ach | type | Payable rail (ach, wire, check, international, external, vendor_choice). |
com.routable.spec/delivery_method | no | ach_standard | delivery_method | Specific delivery option (ach_standard, ach_same_day, wire, check, …). Must be compatible with type. |
com.routable.spec/acting_team_member | conditional | connector config | acting_team_member | Routable team member ID initiating the payable. Required at request time — from either the connector config or this key. |
com.routable.spec/external_id | no | "" | external_id | Caller-supplied external reference (idempotent lookup key on Routable's side). |
com.routable.spec/line_item_description | no | PSPPaymentInitiation.Description, then "Payment <reference>" | line_items[0].description | Description on the auto-generated single-line item. Routable v1 requires a non-empty value. |
The Formance PaymentInitiation.Reference is sent as the HTTP Idempotency-Key header — Routable returns the original payable on retries with the same key, which is the behaviour Formance's create-then-poll workflow relies on.
Async response handling#
Routable's POST /v1/payables answers in two shapes:
| Routable response | Behaviour |
|---|---|
202 Accepted (async) | Platform schedules PollPayoutStatus / PollTransferStatus against GET /v1/payables/{id}. The first successful poll links the PaymentInitiation to a Payment and ends the polling loop. |
201 Created with a terminal status (completed, failed, cancelled, expired) | Payment returned immediately; workflow ends. |
201 Created with a non-terminal status (pending, processing, …) | Polling round scheduled; first poll returns the Payment. |
Once linked, subsequent status transitions (PENDING → PROCESSING → SUCCEEDED) are picked up by the periodic FETCH_PAYMENTS schedule rather than by re-polling.
Correlating an initiation with the synced payment#
When you initiate a payable through Formance, two distinct rows land:
- A
PaymentInitiationkeyed by the reference you supplied (e.g.payout-acmecorp-20260506-172725). - A
PSPPaymentkeyed by Routable's payable UUID (e.g.652e0807-02ed-4546-848f-56babc66ec99).
They're linked at the engine level. To resolve one from the other:
Pagination and recovery#
Routable list endpoints are 1-indexed page+page_size (capped at 100). The connector checkpoints pagination state opaquely between cycles, so a worker crash mid-cycle resumes deterministically from the last persisted checkpoint — no row is double-billed or silently dropped.
The payments fetcher walks payables, then receivables, then advances its cycle watermark. The watermark stays immutable for the full duration of a cycle to avoid the page-2-tighter-than-page-1 race that would drop rows whose status_changed_at lands between page boundaries.
Routable's status_changed_at.gte filter is inclusive, so rows on the cycle boundary get re-emitted at every cycle start. The engine dedupes by PSPPayment.Reference, so this is wasted bandwidth but never a correctness problem.