Platform Payouts Guide
Oncade provides two mechanisms for platforms to send money to users through the Platform API:
- Distributions — Batch payouts to a known list of recipients by email.
- Campaigns — Reward programs where users link their accounts and earn payouts from defined events.
Key difference: Distributions are push-based (sender specifies recipients). Campaigns are pull-based (users join, then earn payouts from triggered events).
Distributions
Distributions let a platform pay a list of recipients in a single batch. Each recipient is identified by email. The system automatically determines the best delivery method per recipient.
Required scopes
distribution:create, distribution:read, lookup:emails,gift:create, distribution:funds:create, distribution:funds:read
All requests require the X-Target-Business-Id header and a grant from the target business.
Listing distributions
Retrieve existing distributions for a business with optional pagination and status filtering.
curl -s \
-H "Authorization: Bearer $PLATFORM_KEY" \
-H "X-Target-Business-Id: $BIZ_ID" \
"https://<host>/api/v1/platform/distributions?page=1&limit=20"Supports page, limit (max 100), and status query parameters. Requires scope: distribution:read.
Step 1: Preview routing with email lookup
Before creating a distribution you can check which recipients already have an Oncade wallet. This determines whether each person receives a direct transfer or a gift claim email.
curl -s -X POST \
-H "Authorization: Bearer $PLATFORM_KEY" \
-H "X-Target-Business-Id: $BIZ_ID" \
-H "Content-Type: application/json" \
-d '{"emails":["alice@example.com","bob@example.com"]}' \
https://<host>/api/v1/platform/lookup-emailshasAccount: true— Recipient will receive a direct transfer to their wallet.hasAccount: false— Recipient will receive a gift claim email with a link to collect funds.
Step 2: Create a distribution
There are three ways to specify recipients:
Percentage mode
Split a total amount by percentage across recipients.
curl -s -X POST \
-H "Authorization: Bearer $PLATFORM_KEY" \
-H "X-Target-Business-Id: $BIZ_ID" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"totalAmountCents": 10000,
"mode": "percentage",
"recipients": [
{"email":"alice@example.com","name":"Alice","percentage":60},
{"email":"bob@example.com","name":"Bob","percentage":40}
],
"memo": "Q1 revenue split"
}' \
https://<host>/api/v1/platform/distributionsFixed mode
Specify a dollar amount per recipient. The totalAmountCents field is ignored — the total is calculated from recipient amounts.
curl -s -X POST \
-H "Authorization: Bearer $PLATFORM_KEY" \
-H "X-Target-Business-Id: $BIZ_ID" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"mode": "fixed",
"totalAmountCents": 0,
"recipients": [
{"email":"alice@example.com","name":"Alice","amount":60.00},
{"email":"bob@example.com","name":"Bob","amount":40.00}
]
}' \
https://<host>/api/v1/platform/distributionsFrom a saved SplitTemplate
Reference a previously created split template instead of passing recipients inline.
curl -s -X POST \
-H "Authorization: Bearer $PLATFORM_KEY" \
-H "X-Target-Business-Id: $BIZ_ID" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"templateId":"<split_template_id>","totalAmountCents":10000,"memo":"Monthly payout"}' \
https://<host>/api/v1/platform/distributionsThe response includes line items showing the routing decision per recipient:
| Field | Meaning |
|---|---|
method: "direct_transfer" | Funds will go to their Oncade wallet |
method: "gift" | A gift will be created; recipient receives a claim email |
Distribution fees
Every distribution has two categories of fees applied on top of totalAmountCents:
| Type | Source | When applied |
|---|---|---|
| Oncade service fee | Auto-applied from the business's pricing configuration (or the global default) | Always — cannot be opted out of |
| Platform fees | Passed by your platform in the fees array at creation time | Only when included in the request |
The response always contains totalFeeCents (sum of all fee amounts) and a feeLineItems array detailing each fee. The total amount the business must fund is totalAmountCents + totalFeeCents.
Preview fees before creating
Call the fee-preview endpoint to see which Oncade service fee will be auto-applied before you create a distribution. Requires scope: distribution:read.
curl -s \
-H "Authorization: Bearer $PLATFORM_KEY" \
-H "X-Target-Business-Id: $BIZ_ID" \
"https://<host>/api/v1/platform/distributions/fee-preview"Returns a fees array. Each entry has label, percentage, flat (in cents), and feeType. If the array is empty, no service fee is configured for that business.
Fee calculation types
The feeType field controls how the flat component scales:
| feeType | Formula | Use case |
|---|---|---|
flat_rate_plus_percentage (default) | flat + (percentage × total) | Fixed overhead + percentage of the payout pool |
per_payout_plus_percentage | (flat × recipientCount) + (percentage × total) | Per-recipient fee + percentage of the payout pool |
Attaching platform fees to a distribution
Pass a fees array in the create request to add fees on top of the Oncade service fee. Each entry requires a recipientWallet — the address that receives this fee payment.
curl -s -X POST \
-H "Authorization: Bearer $PLATFORM_KEY" \
-H "X-Target-Business-Id: $BIZ_ID" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"totalAmountCents": 100000,
"mode": "percentage",
"recipients": [
{"email":"alice@example.com","name":"Alice","percentage":60},
{"email":"bob@example.com","name":"Bob","percentage":40}
],
"fees": [
{
"label": "Platform Revenue Share",
"percentage": 3,
"flat": 0,
"feeType": "flat_rate_plus_percentage",
"recipientWallet": "0xYourPlatformWallet"
}
]
}' \
https://<host>/api/v1/platform/distributionsHeads up: Fee amounts are calculated at creation time and stored on the distribution. The business owner must fund the full totalAmountCents + totalFeeCents amount. Fees are paid out in the same batch transfer as recipients.
Step 3: Fund and finalize
After creating the distribution, the platform handles the funding operations, then notifies Oncade to send claim emails to gift recipients.
curl -s -X POST \
-H "Authorization: Bearer $PLATFORM_KEY" \
-H "X-Target-Business-Id: $BIZ_ID" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"distributionId": "<distribution_id>",
"giftData": [{"giftId":"<gift_id>","claimCode":"<64_hex>"}],
"transactionHash": "0x..."
}' \
https://<host>/api/v1/platform/gifts/mark-fundedThis updates gift status to FUNDED and sends claim emails to each gift recipient.
SplitTemplate management
Platforms can pre-create reusable recipient lists to avoid passing recipients on every distribution.
Limitation: The sender must know all recipient emails ahead of time. There is no mechanism for a recipient to opt in to a distribution. If you need recipients to self-enroll, use Campaigns below.
Campaigns (Join-Style Payouts)
Campaigns solve the problem of paying users you don't know yet. Instead of specifying emails, you define events with payout amounts, and users link their accounts to become eligible.
Required scopes
campaign:create, campaign:read, campaign:update,campaign:delete, campaign:start, campaign:stop,campaign:events:create, campaign:users:link:create,campaign:users:link:read, campaign:users:link:remove,campaign:users:link:staticcampaign:funds:create, campaign:funds:read
Campaign lifecycle
A campaign moves through a defined set of states. Understanding the lifecycle helps you integrate the correct API calls at the right time.
Step 1: Create a campaign
Define the campaign with one or more events, each specifying a payout amount in USDC smallest units (6 decimals, so 5000000 = $5.00). Optionally include a webhookUrl to receive campaign-specific webhook events.
curl -s -X POST \
-H "Authorization: Bearer $PLATFORM_KEY" \
-H "X-Target-Business-Id: $BIZ_ID" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"name": "q1-rewards",
"publicName": "Q1 Reward Program",
"events": [
{"code":"signup_bonus","name":"Signup Bonus","payoutAmount":"5000000"},
{"code":"referral","name":"Referral Reward","payoutAmount":"10000000"}
]
}' \
https://<host>/api/v1/platform/campaignsManaging campaigns
After creation you can retrieve, update, or delete a campaign. Updates allow changing the name, events, payout message, and webhook configuration. Deletion is only allowed for campaigns in the created or stopped state.
| Endpoint | Scope | Description |
|---|---|---|
GET /v1/platform/campaigns | campaign:read | List all campaigns for the target business |
GET /v1/platform/campaigns/{id} | campaign:read | Get campaign details including status and balance |
PATCH /v1/platform/campaigns/{id} | campaign:update | Update name, events, payout message, or webhook URL |
DELETE /v1/platform/campaigns/{id} | campaign:delete | Delete a campaign (created or stopped only) |
curl -s \
-H "Authorization: Bearer $PLATFORM_KEY" \
https://<host>/api/v1/platform/campaigns/$CIDStep 2: Fund the campaign
Create a funding session that the business owner completes through the hosted UI. Sessions expire after 1 hour.
curl -s -X POST \
-H "Authorization: Bearer $PLATFORM_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
https://<host>/api/v1/platform/campaigns/$CID/fund-sessionsThe response includes a url the business owner opens to authenticate, choose how much to fund, and complete the transaction.
Fund session lifecycle
Each funding session moves through a simple state machine. Poll the session status to track progress, or cancel a session that is no longer needed.
| Endpoint | Scope | Description |
|---|---|---|
POST .../fund-sessions | campaign:funds:create | Create a funding session (idempotent by Idempotency-Key) |
GET .../fund-sessions/{handle} | campaign:funds:read | Poll session status (pending, completed, expired, cancelled) |
DELETE .../fund-sessions/{handle} | campaign:funds:create | Cancel a pending session so the URL can no longer be used |
curl -s \
-H "Authorization: Bearer $PLATFORM_KEY" \
https://<host>/api/v1/platform/campaigns/$CID/fund-sessions/$SESSION_HANDLEIdempotency: Creating a fund session with the same Idempotency-Key returns the existing pending session (200) instead of creating a duplicate. New sessions return 201 with a Location header pointing to the funding URL.
Step 3: Create link sessions (the "join" mechanic)
Instead of specifying emails, you generate link URLs that users visit to opt in. The email field is optional and pre-fills the login form.
curl -s -X POST \
-H "Authorization: Bearer $PLATFORM_KEY" \
-H "Content-Type: application/json" \
-d '{"email":"user@example.com"}' \
https://<host>/api/v1/platform/campaigns/$CID/link/users/initiateWhen a user visits the link URL:
- They authenticate (creating an Oncade account if needed)
- They approve linking their account to the campaign
- They receive a stable
userRefthat the platform uses to record events
Managing link sessions
After creating a link session, you can check its status or remove a user's link.
| Endpoint | Scope | Description |
|---|---|---|
POST .../link/users/initiate | campaign:users:link:create | Create a link session URL (idempotent by email or Idempotency-Key) |
GET .../users/link/details?session={key} | campaign:users:link:read | Get link session status, userRef, and prefilled email |
POST .../users/{userRef}/link/remove | campaign:users:link:remove | Unlink a user from the campaign by user_ref |
curl -s \
-H "Authorization: Bearer $PLATFORM_KEY" \
"https://<host>/api/v1/platform/campaigns/$CID/users/link/details?session=$SESSION_KEY"Alternative: Link invites. Link sessions require generating a unique URL per user. If you need a single shareable URL (for email blasts, QR codes, or social media), use link invites instead. Each visit to a link invite URL automatically creates a unique linking session. Manage invites via the campaign:users:link:static scope. See the Link Invite API reference for full details.
Step 4: Start the campaign and record events
Start the campaign with a PUT request, then record events as users perform actions. Each event triggers a payout to the linked user for the configured amount.
curl -s -X PUT \
-H "Authorization: Bearer $PLATFORM_KEY" \
https://<host>/api/v1/platform/campaigns/$CID/startThe userRef must belong to a user with an approved account link for this campaign. The system validates this before dispatching the event. Requires scope: campaign:events:create. You can also send one-time tips by including payoutAmount with userRef in the same request (no pre-defined event required). See the Platform API reference for the Record Campaign Event endpoint and the campaign tipping feature doc for details.
Step 5: Stop the campaign
When the campaign is finished, stop it with a PUT request. Remaining funds can be withdrawn by the business owner. A stopped campaign can be restarted later if needed.
curl -s -X PUT \
-H "Authorization: Bearer $PLATFORM_KEY" \
https://<host>/api/v1/platform/campaigns/$CID/stopCampaign webhooks
Campaign lifecycle events are delivered to your platform's configured webhook URL. If you also set a webhookUrl on the campaign itself, a webhookSecret is included in the payload so you can verify campaign-specific webhooks separately.
| Event | Trigger |
|---|---|
Platform.Campaign.Created | Campaign created via the API |
Platform.Campaign.Updated | Campaign updated via PATCH |
Platform.Campaign.Deleted | Campaign deleted |
Platform.Campaign.Started | Campaign activated via PUT /start |
Platform.Campaign.Stopped | Campaign deactivated via PUT /stop |
Platform.Campaign.Event.Recorded | Event recorded and payout triggered |
Account link webhooks
When users interact with link session URLs, these events are delivered to your webhook:
| Event | Trigger |
|---|---|
User.Account.Link.Started | Link session created via the API |
User.Account.Link.Succeeded | User authenticated and approved the link |
User.Account.Link.Failed | Link attempt failed |
User.Account.Link.Canceled | User canceled the linking flow |
User.Account.Link.Removed | User unlinked via POST .../link/users/remove |
See the Platform API Reference - Webhooks section for full payload examples and signature verification.
When to Use What
| Need | Distributions | Campaigns |
|---|---|---|
| Known recipients, one-time payout | Yes | — |
| Recurring payouts to same team | Yes (SplitTemplates) | — |
| Users self-enroll / join | — | Yes (link sessions) |
| Event-driven payouts | — | Yes |
| Recipients unknown ahead of time | — | Yes |
| Simple percentage/fixed splits | Yes | — |
Mimicking distributions with campaigns
If you want a "join to get paid" flow but with a simpler payout model, you can use a campaign with a single event:
- Create a campaign with a single event (e.g.,
"payout") with the desired amount. - Generate link session URLs and share them with intended recipients.
- As each user links, record the payout event once per linked user.
This creates a "claim your payout" flow where recipients opt in. The trade-off is that campaigns require upfront funding, whereas distributions are lighter-weight.
API Quick Reference
Distribution Endpoints
All distribution endpoints are under /v1/platform.
| Method | Path | Scope | Purpose |
|---|---|---|---|
GET | /distributions | distribution:read | List distributions (paginated) |
POST | /distributions | distribution:create | Create distribution |
GET | /distributions/fee-preview | distribution:read | Preview auto-applied Oncade service fee |
POST | /lookup-emails | lookup:emails | Preview recipient routing |
POST | /gifts/mark-funded | gift:create | Mark gifts funded & send claim emails |
POST | /gifts/bulk | gift:create | Create gift records in bulk |
POST | /distributions/{id}/fund-sessions | distribution:funds:create | Create hosted funding session for a distribution |
GET | /distributions/{id}/fund-sessions/{handle} | distribution:funds:read | Poll funding session status |
DELETE | /distributions/{id}/fund-sessions/{handle} | distribution:funds:create | Cancel a pending funding session |
Split Template Endpoints
All split template endpoints are under /v1/platform/split-templates.
| Method | Path | Scope | Purpose |
|---|---|---|---|
GET | /split-templates | splitTemplate:read | List templates for a business |
POST | /split-templates | splitTemplate:create | Create template |
GET | /split-templates/{id} | splitTemplate:read | Get template details |
PATCH | /split-templates/{id} | splitTemplate:update | Update template |
DELETE | /split-templates/{id} | splitTemplate:delete | Archive template |
Campaign Endpoints
All campaign endpoints are under /v1/platform/campaigns.
| Method | Path | Scope | Purpose |
|---|---|---|---|
| Core | |||
GET | /campaigns | campaign:read | List campaigns |
POST | /campaigns | campaign:create | Create campaign |
GET | /campaigns/{id} | campaign:read | Get campaign details |
PATCH | /campaigns/{id} | campaign:update | Update campaign |
DELETE | /campaigns/{id} | campaign:delete | Delete campaign |
| Lifecycle | |||
PUT | /campaigns/{id}/start | campaign:start | Activate campaign |
PUT | /campaigns/{id}/stop | campaign:stop | Deactivate campaign |
POST | /campaigns/{id}/events | campaign:events:create | Record event & trigger payout |
| Link Sessions | |||
POST | /campaigns/{id}/link/users/initiate | campaign:users:link:create | Create user link URL |
GET | /campaigns/{id}/users/link/details | campaign:users:link:read | Get link session status |
POST | /campaigns/{id}/users/{userRef}/link/remove | campaign:users:link:remove | Unlink user from campaign |
| Link Invites | |||
POST | /campaigns/{id}/link-invites | campaign:users:link:static | Create shareable invite code |
GET | /campaigns/{id}/link-invites | campaign:users:link:static | List invites |
PATCH | /campaigns/{id}/link-invites/{code} | campaign:users:link:static | Update invite |
DELETE | /campaigns/{id}/link-invites/{code} | campaign:users:link:static | Revoke invite |
| Fund Sessions | |||
POST | /campaigns/{id}/fund-sessions | campaign:funds:create | Create funding session |
GET | /campaigns/{id}/fund-sessions/{handle} | campaign:funds:read | Get funding session status |
DELETE | /campaigns/{id}/fund-sessions/{handle} | campaign:funds:create | Cancel pending funding session |
References
- Platform API Reference
- Campaigns Integration Guide (Campaign API key flow)
- Account Linking Guide