Marketing Offer Webhooks Guide
Marketing Offer Webhooks Guide
Marketing offer webhooks deliver pre-calculated funding offers for your merchants directly to your systems. This guide covers the webhook payload structure, processing requirements, and integration patterns for both Advance-level and Account-level offers.
Overview
Liberis generates marketing offers daily for eligible merchants. These offers include Business Cash Advances (BCA), Pay with Liberis, and Flex Capital upgrades. The webhook delivers a complete snapshot of all current offers for each merchant.
Who Receives Webhooks
Marketing offer webhooks are sent to partners who have registered a webhook endpoint and have merchants with revenue data in the Liberis system.
Delivery Cadence
Webhooks are delivered daily for each eligible merchant. The daily delivery serves several purposes:
- Input data changes daily - Revenue data, payment history, and other merchant metrics are updated continuously
- Offers are validated daily - Each day, offers are re-evaluated against current data to ensure accuracy
- Time affects calculations - The passage of time changes offer parameters like estimated repayment timelines
Reliability signal
The daily cadence helps distinguish between "no webhook received due to delivery failure" and "offer unchanged". If you haven't received a webhook within your expected daily window, investigate potential delivery issues rather than assuming offers are unchanged.
Understanding Marketing Offers
Marketing offers are categorised by their offer level, which determines what type of product or action they represent.
Offer Levels
| Level | Description |
|---|---|
ADVANCE | Individual funding products the merchant can apply for |
ACCOUNT | Account-level changes that affect the merchant's overall relationship with Liberis |
Advance Offer Types
| Type | Description |
|---|---|
BCA_INITIAL | First-time Business Cash Advance for new customers |
BCA_RENEWAL | Renewal advance for merchants who have previously completed an advance |
PAY_WITH_LIBERIS | Partner-funded advance product with different fee structures |
Account Offer Types
| Type | Description |
|---|---|
FLEX_UPGRADE | Upgrade to a Flex Capital facility, providing a pre-authorised limit for instant advances |
FLEX_RENEWAL | Renewal of an existing Flex Capital facility with updated terms |
Approval Statuses
Each offer includes an approvalStatus indicating how much verification is required:
| Status | Description |
|---|---|
SPECULATIVE | Preliminary offer based on limited data; full application required |
ELIGIBLE | Merchant meets eligibility criteria; standard application process applies |
PRE_APPROVED | Merchant has been pre-approved; streamlined process with minimal additional checks |
NO_DECISION_NEEDED | Offer can be actioned immediately without further approval |
Webhook Payload Structure
Envelope Structure
Every webhook follows a standard envelope format:
{
"eventId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"eventDate": "2025-02-01T12:01:02-05:00",
"type": "liberis.capitalAccounts.v3.marketingOfferCreated",
"data": { ... }
}| Field | Type | Description |
|---|---|---|
eventId | UUID | Unique identifier for this webhook event |
eventDate | ISO 8601 | Timestamp when the event was generated |
type | String | Always liberis.capitalAccounts.v3.marketingOfferCreated |
data | Object | The marketing offer payload |
Data Object Fields
| Field | Type | Description |
|---|---|---|
partnerClientIds | Array | Your identifiers for this merchant (may include multiple if merchant has multiple locations) |
customerHasCapitalAccount | Boolean | Whether the merchant already has a Liberis capital account provisioned |
doNotMarket | Boolean | If true, suppress all marketing to this merchant |
campaignSegment | String | Optional segment identifier for targeted marketing campaigns |
marketingOffers | Array | List of available offers |
Marketing Offers Array
Each item in the marketingOffers array contains:
| Field | Type | Description |
|---|---|---|
weightedPriority | Integer | Display priority (lower numbers = higher priority) |
offerId | String | Unique identifier for this specific offer |
offerLevel | String | Either ADVANCE or ACCOUNT |
expiry | ISO 8601 | When this offer expires (UTC) |
offer | Object | Offer details (structure varies by offer level) |
Advance Offer Schema
Advance-level offers (offerLevel: "ADVANCE") include detailed pricing:
{
"weightedPriority": 1,
"offerId": "adv123456",
"offerLevel": "ADVANCE",
"expiry": "2025-06-01T00:00:00Z",
"offer": {
"type": "BCA_INITIAL",
"amount": {
"currency": "USD",
"amount": 10000.00
},
"merchantFee": {
"currency": "USD",
"amount": 1000.00
},
"partnerFee": {
"currency": "USD",
"amount": 0.00
},
"merchantTotal": {
"currency": "USD",
"amount": 11000.00
},
"partnerTotal": {
"currency": "USD",
"amount": 0.00
},
"estimatedTimeToPay": 120,
"splitPercentage": 15,
"balanceBeforeAdvance": {
"currency": "USD",
"amount": 2000.00
},
"balanceAfterAdvance": {
"currency": "USD",
"amount": 13000.00
},
"limits": {
"min": {
"currency": "USD",
"amount": 5000.00
},
"max": {
"currency": "USD",
"amount": 15000.00
}
},
"approvalStatus": "ELIGIBLE"
}
}| Field | Description |
|---|---|
amount | Representative funding amount for this offer |
merchantFee | Fee charged to the merchant |
partnerFee | Fee charged to or shared with the partner |
merchantTotal | Total amount the merchant will repay |
partnerTotal | Total partner contribution (for Pay with Liberis) |
estimatedTimeToPay | Expected duration in days to repay the advance |
splitPercentage | Percentage of daily revenue applied to repayment |
balanceBeforeAdvance | Merchant's existing balance before this advance |
balanceAfterAdvance | Merchant's total balance if they take this advance |
limits.min / limits.max | Minimum and maximum funding amounts available |
Account Offer Schema
Account-level offers (offerLevel: "ACCOUNT") have a simpler structure:
{
"weightedPriority": 3,
"offerId": "acc789101",
"offerLevel": "ACCOUNT",
"expiry": "2025-07-15T00:00:00Z",
"offer": {
"type": "FLEX_UPGRADE",
"limits": {
"min": {
"currency": "USD",
"amount": 1000.00
},
"max": {
"currency": "USD",
"amount": 5000.00
}
},
"approvalStatus": "PRE_APPROVED"
}
}| Field | Description |
|---|---|
type | Either FLEX_UPGRADE or FLEX_RENEWAL |
limits.min / limits.max | Range of pre-authorised limit amounts available |
approvalStatus | Level of pre-approval for this account upgrade |
Processing Webhooks
State Snapshot Semantics
Critical
Each webhook represents a complete snapshot of current offers, not an incremental update. This design ensures your system always has the authoritative state.
When you receive a webhook:
- The
marketingOffersarray contains all current offers for this merchant - Any offer type not present in the array should be removed from your system
- Any offer type present should replace any existing offers of that type
Reconciliation Logic
Follow this algorithm when processing each webhook:
1. Receive webhook for merchant (identified by partnerClientIds)
2. Parse all offer types from the marketingOffers array
3. For each offer type present in the webhook:
- Replace any existing offers of that type with the new data
4. For each offer type previously stored but NOT in the webhook:
- Remove those offers from your system
5. Respond with 204 No Content
Example scenario:
Previous state for merchant ABC123:
- BCA_INITIAL offer (offerId: "adv001")
- FLEX_UPGRADE offer (offerId: "acc001")
New webhook contains:
- BCA_RENEWAL offer (offerId: "adv002")
- FLEX_UPGRADE offer (offerId: "acc002")
Result:
- Remove BCA_INITIAL (no longer present)
- Add BCA_RENEWAL (new type)
- Replace FLEX_UPGRADE (updated offer)
Idempotent Processing
Your webhook handler must be idempotent. You may receive the same webhook multiple times due to:
- Network retries
- System recovery processes
- Redelivery after transient failures
Use the eventId to detect duplicate deliveries if needed, but the state snapshot design means reprocessing the same webhook should produce the same result.
Response Requirements
You must respond with 204 No Content to acknowledge successful receipt.
HTTP/1.1 204 No ContentAny other response (including 2xx codes other than 204) may trigger retry behaviour. Non-2xx responses will definitely trigger retries with exponential backoff.
Handling Offer Validity
Expiry Dates
Every offer includes an expiry field with a UTC timestamp. You must check this before displaying offers:
if (offer.expiry < currentTime) {
// Do not display this offer
// Use GET endpoint to fetch fresh offers
}
Expired offers may still appear in webhooks during the processing window. Always validate expiry at display time.
doNotMarket Flag
When doNotMarket is true, you must suppress all marketing communications to this merchant. This typically indicates:
- The merchant's account is frozen
- There are compliance concerns
- The merchant has opted out of marketing
When this flag is set:
- The
marketingOffersarray will be empty - Do not display any cached offers
- Do not send marketing communications
Continuous Validation
Implement validation at multiple points:
- On webhook receipt - Store offers with their expiry dates
- On page render - Check expiry before displaying
- Background job (optional) - Periodically clean up expired offers
Integration Patterns
Webhook + GET Endpoint Pattern
For optimal reliability, combine webhook processing with on-demand retrieval:
sequenceDiagram
participant L as Liberis
participant Y as Your System
participant M as Merchant UI
L->>Y: Daily webhook with offers
Y->>Y: Store offers locally
M->>Y: Merchant views dashboard
Y->>Y: Check offer expiry
alt Offers valid
Y->>M: Display cached offers
else Offers expired or missing
Y->>L: GET /v3/marketing_offers/{partnerClientId}
L->>Y: Current offers
Y->>Y: Update local cache
Y->>M: Display fresh offers
end
Use the GET endpoint as a fallback when:
- Webhooks may have been missed
- Offers have expired
- Real-time accuracy is critical
GET /v3/marketing_offers/{partnerClientId}Display Priority
Use the weightedPriority field to order offers in your UI. Lower numbers indicate higher priority based on partnership goals.
offers.sort((a, b) => a.weightedPriority - b.weightedPriority)
Present the highest-priority offers most prominently while still giving merchants visibility into all available options.
Complete Payload Examples
Example: Advance and Account Offers
A merchant with both advance products and a Flex Capital upgrade available:
{
"eventId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"eventDate": "2025-02-01T12:01:02-05:00",
"type": "liberis.capitalAccounts.v3.marketingOfferCreated",
"data": {
"partnerClientIds": ["ABC123", "DEF456"],
"customerHasCapitalAccount": true,
"doNotMarket": false,
"campaignSegment": "high_value",
"marketingOffers": [
{
"weightedPriority": 1,
"offerId": "adv123456",
"offerLevel": "ADVANCE",
"expiry": "2025-06-01T00:00:00Z",
"offer": {
"type": "PAY_WITH_LIBERIS",
"amount": {
"currency": "USD",
"amount": 10000.00
},
"merchantFee": {
"currency": "USD",
"amount": 0.00
},
"partnerFee": {
"currency": "USD",
"amount": 0.00
},
"merchantTotal": {
"currency": "USD",
"amount": 10000.00
},
"partnerTotal": {
"currency": "USD",
"amount": 100.00
},
"estimatedTimeToPay": 120,
"splitPercentage": 15,
"balanceBeforeAdvance": {
"currency": "USD",
"amount": 2000.00
},
"balanceAfterAdvance": {
"currency": "USD",
"amount": 12000.00
},
"limits": {
"min": {
"currency": "USD",
"amount": 5000.00
},
"max": {
"currency": "USD",
"amount": 15000.00
}
},
"approvalStatus": "ELIGIBLE"
}
},
{
"weightedPriority": 2,
"offerId": "adv123457",
"offerLevel": "ADVANCE",
"expiry": "2025-06-01T00:00:00Z",
"offer": {
"type": "BCA_INITIAL",
"amount": {
"currency": "USD",
"amount": 10000.00
},
"merchantFee": {
"currency": "USD",
"amount": 1000.00
},
"partnerFee": {
"currency": "USD",
"amount": 0.00
},
"merchantTotal": {
"currency": "USD",
"amount": 11000.00
},
"partnerTotal": {
"currency": "USD",
"amount": 0.00
},
"estimatedTimeToPay": 120,
"splitPercentage": 15,
"balanceBeforeAdvance": {
"currency": "USD",
"amount": 2000.00
},
"balanceAfterAdvance": {
"currency": "USD",
"amount": 13000.00
},
"limits": {
"min": {
"currency": "USD",
"amount": 5000.00
},
"max": {
"currency": "USD",
"amount": 15000.00
}
},
"approvalStatus": "ELIGIBLE"
}
},
{
"weightedPriority": 3,
"offerId": "acc789101",
"offerLevel": "ACCOUNT",
"expiry": "2025-07-15T00:00:00Z",
"offer": {
"type": "FLEX_UPGRADE",
"limits": {
"min": {
"currency": "USD",
"amount": 1000.00
},
"max": {
"currency": "USD",
"amount": 5000.00
}
},
"approvalStatus": "PRE_APPROVED"
}
}
]
}
}Example: Advance Offers Only
A merchant eligible for advance products but not account-level changes:
{
"eventId": "4fb96f74-6828-4673-c4gd-3d074g77bgb7",
"eventDate": "2025-02-01T12:01:02-05:00",
"type": "liberis.capitalAccounts.v3.marketingOfferCreated",
"data": {
"partnerClientIds": ["MER789"],
"customerHasCapitalAccount": true,
"doNotMarket": false,
"campaignSegment": "renewal",
"marketingOffers": [
{
"weightedPriority": 1,
"offerId": "adv789012",
"offerLevel": "ADVANCE",
"expiry": "2025-05-15T00:00:00Z",
"offer": {
"type": "BCA_RENEWAL",
"amount": {
"currency": "USD",
"amount": 15000.00
},
"merchantFee": {
"currency": "USD",
"amount": 1350.00
},
"partnerFee": {
"currency": "USD",
"amount": 0.00
},
"merchantTotal": {
"currency": "USD",
"amount": 16350.00
},
"partnerTotal": {
"currency": "USD",
"amount": 0.00
},
"estimatedTimeToPay": 90,
"splitPercentage": 20,
"balanceBeforeAdvance": {
"currency": "USD",
"amount": 500.00
},
"balanceAfterAdvance": {
"currency": "USD",
"amount": 16850.00
},
"limits": {
"min": {
"currency": "USD",
"amount": 10000.00
},
"max": {
"currency": "USD",
"amount": 25000.00
}
},
"approvalStatus": "PRE_APPROVED"
}
}
]
}
}Example: doNotMarket Scenario
A merchant whose account is frozen:
{
"eventId": "5gc07g85-7939-5784-d5he-4e185h88chc8",
"eventDate": "2025-02-01T12:01:02-05:00",
"type": "liberis.capitalAccounts.v3.marketingOfferCreated",
"data": {
"partnerClientIds": ["FRZ001"],
"customerHasCapitalAccount": true,
"doNotMarket": true,
"campaignSegment": null,
"marketingOffers": []
}
}When you receive this payload:
- Remove all stored offers for this merchant
- Suppress any marketing communications
- Do not display funding options in your UI
Related Resources
- Flex Capital Integration Guide - Detailed guide for implementing Flex Capital upgrades
- Marketing Offers API Reference - Webhook API specification
- GET Marketing Offers Endpoint - On-demand offer retrieval
Updated 3 days ago