Incentive API reference →

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.

MethodReturnsDelivered to
settleRebates(programId)SettlementRun: the records createdKafka settlement-topic and the legacy webhook
settleCpa(programId)SettlementRun: the records createdKafka settlement-topic
settleRewards(programId)SettlementResult: a status messageKafka (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

settlementId
String
The record id. Send it back as externalRefId when you report the outcome. This is the id the CPA update requires.
settlementRunId
String
The settlement run the record belongs to.
crmRefer
String
The record's event id, the same value as settlementEventId in the settlement event.
ibId
String
FYNXT id of the IB or affiliate being paid.
amount
String
Amount to pay, as a decimal string, in currency.
currency
String
Settlement currency.
settlementStatus
String
Record status at creation, normally Pending.
tierId
String
The program tier the rewards were settled under.
Nothing to settle is not an error. When a program has no unsettled rewards, 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"
  }
]

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.

MethodSettlesIdentify each record by
updateRebateSettlementsRebate payoutsexternalRefId: the event's settlementRecordId, or its settlementEventId
updateCpaSettlementsCPA payoutsexternalRefId: the numeric settlement record id only
updateRewardSettlementsReward payoutssettlementRecordId, 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

externalRefId
String
required
Rebate and CPA only. The id of the FYNXT settlement record this payout settles, echoed from the settlement event. For CPA it must be the numeric record id; any other value is ignored.
settlementRecordId
Long
required*
Reward only. The settlementRecordId from the reward settlement event. *Or leave it null and set settlementEventId instead.
settlementEventId
String
optional
Reward only. The event's settlementEventId, used to find the record when settlementRecordId is null. Build the payout with the RewardPayout constructor to set it.
status
PayoutStatus
required
SUCCESS or FAILED. Payout.success(...) and Payout.failed(...) set it for you.
crmReferenceId
String
optional
Your CRM's own payout or transaction reference. Stored on the record for reconciliation.
failureReason
String
optional
Why the payout failed. Shown to staff in FYNXT. Ignored on a success.
processedAt
Instant
optional
When your CRM processed the payout. The factory methods set it to now.
batchReferenceId
String
optional
On SettlementUpdate / RewardSettlementUpdate: your CRM's payout batch id, stored on every record in the call.

How FYNXT applies an update

Who can call these. Settlement updates move money, so only your tenant's CRM key (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".