Settlement
Settlement is a round trip. Your CRM asks FYNXT to send a program's
unsettled rewards, which FYNXT delivers as settlement events over Kafka and, for legacy
systems, the settlement webhook. Once your CRM has paid them, it
reports each payout's outcome back. FynxtCrmClient
has a method for each step and each settlement type: rebate, CPA and reward.
Before you start
These methods are part of the same FynxtCrmClient used for CRM onboarding.
Copy the client and build it once as described in
Install, Authentication
and Initialize the client. The full source is in
Client source.
Sending unsettled rewards
A settle call collects every unsettled reward of one program, groups them into a new settlement run with one record per payee, and sends each record out for payment. Rewards already in a pending or confirmed record are not sent twice.
| Method | Returns | Delivered to |
|---|---|---|
settleRebates(programId) | SettlementRun: the records created | Kafka settlement-topic and the legacy webhook |
settleCpa(programId) | SettlementRun: the records created | Kafka settlement-topic |
settleRewards(programId) | SettlementResult: a status message | Kafka (reward settlement topic) |
They call these endpoints, with no request body:
POST /api/abstraction/crm/api/settlements/settle/{programId} // rebate
POST /api/abstraction/crm/cpa-scheme/settlement/settle/{programId} // CPA
POST /api/abstraction/crm/api/settlements/reward/settle/{programId} // reward
SettlementRun run = fynxt.settleRebates(1001);
for (SettlementRecord r : run.data()) {
payIb(r.ibId(), r.amount(), r.currency(), r.settlementId());
}
SettlementRecord fields
externalRefId when you report the outcome. This is the id the CPA update requires.settlementEventId in the settlement event.currency.Pending.settleRebates and settleCpa return an empty
data() list and settleRewards returns
"No unsettled rewards found".
Legacy webhook
Kafka is the source of truth for settlement events. For legacy systems that cannot consume
Kafka, settleRebates also posts the same events to a webhook, once the run is
committed. FYNXT configures the webhook URL and issues your tenant a webhook key; without a
key, nothing is posted. CPA and reward settlements are delivered on Kafka only.
POST {your webhook URL}
Content-Type: application/json
X-Tenant-Slug: {your tenant slug}
X-Api-Key: {your webhook key}
[
{
"settlementBatchId": "Settlement_1001_1793959306877",
"settlementRecordId": 1234,
"settlementEventId": "CRM-SET-1",
"settlementType": "Rebate",
"tenantId": "7",
"ibCrmAccountId": "EXT-IB-001",
"programId": 1001,
"programName": "Gold Rebate Program",
"settlementAmount": 1250.75,
"settlementStatus": "Pending",
"settlementCurrency": "USD",
"retryCount": 0,
"ibInternalId": 10,
"invoiceId": "INV-2026-0001",
"invoiceUrl": "https://example.com/invoices/INV-2026-0001.pdf"
}
]
- The body is a JSON array: every record of the run in one call.
- Answer with any 2xx. The response body is ignored.
- A non-2xx or timeout is retried a few times with backoff, then dropped. There is no
replay, so reconcile against Kafka, or against the
SettlementRunyoursettleRebatescall returned. - Use
settlementEventIdto ignore duplicates. - Pay each event, then report the outcome with
updateRebateSettlements, usingsettlementRecordIdasexternalRefId.
Settlement updates
When a settlement run completes, FYNXT sends one settlement event per payee to your CRM
(Kafka topic settlement-topic, or the settlement webhook). Your CRM makes the
payout, then reports each record's outcome back with one of these methods. FYNXT then
settles the underlying rewards, or puts them back for the next run if the payout failed.
| Method | Settles | Identify each record by |
|---|---|---|
updateRebateSettlements | Rebate payouts | externalRefId: the event's settlementRecordId, or its settlementEventId |
updateCpaSettlements | CPA payouts | externalRefId: the numeric settlement record id only |
updateRewardSettlements | Reward payouts | settlementRecordId, or settlementEventId when the record id is not set |
They call these endpoints:
POST /api/abstraction/crm/api/settlements/settlement-update // rebate
POST /api/abstraction/crm/cpa-scheme/settlement/settlement-update // CPA
POST /api/abstraction/crm/api/settlements/reward/settlement-update // reward
fynxt.updateRebateSettlements(new SettlementUpdate("CRM-BATCH-2026-11-06", List.of(
Payout.success("1234", "PAY-77812"),
Payout.failed("1235", "PAY-77813", "Beneficiary account closed"))));
Payout fields
settlementRecordId from the reward settlement event. *Or leave it null and set settlementEventId instead.settlementEventId, used to find the record when settlementRecordId is null. Build the payout with the RewardPayout constructor to set it.SUCCESS or FAILED. Payout.success(...) and Payout.failed(...) set it for you.SettlementUpdate / RewardSettlementUpdate: your CRM's payout batch id, stored on every record in the call.How FYNXT applies an update
- Success confirms the record and settles its rewards. For rebates this credits the IB's wallet and runs rebate sharing.
- Failed marks the record failed and returns its rewards to unsettled, so the next settlement run pays them again. Failed is final for that record: report the retry on the new record the next run creates.
- Only records still waiting for an outcome change. A record that is already confirmed or failed ignores later updates, so resending the same update is safe.
- Unknown ids are skipped, not rejected. The call still returns HTTP 200; check
result.data(), which reads"No matching settlement records"when nothing matched.
X-Tenant-Api-Key) or the legacy shared key is accepted. A broker staff login is refused.
Error handling
Settlement methods throw FynxtApiException on a non-2xx response or a network
failure, like every client method (see Error handling).
Retrying a settlement update is always safe: a record that already has an outcome ignores
the repeat.
An update that matches no record is not an error. It returns HTTP 200, so
check result.data() and alert when it reads
"No matching settlement records".