Creator campaign webhooks
Receive an event when a creator joins, submits a post, gets approved or rejected, becomes payable or gets paid.
Creator campaign events follow a campaign from the first creator joining to the last payout.
Subscribe with POST /v1/webhooks/settings and the event names below (first event).
Delivery, retries and signatures are the same for every event (how webhooks behave).
Events
| Event | Description |
|---|---|
campaign_submission.received | A creator submitted a post and its tracking window opened. |
campaign_submission.flagged | A submission gained a fraud flag it did not carry before. |
campaign_submission.pending_review | A submission is ready for your review. |
campaign_submission.approved | You approved a submission. |
campaign_submission.rejected | You rejected a submission. |
creator_campaign.budget_exhausted | Earnings reached the campaign budget. |
creator_campaign.closed | A campaign closed. |
campaign_payout.payable | Held earnings became payable. |
campaign_payout.settled | A payout was recorded. |
campaign_payout.failed | A treasury payout did not reach the creator. |
campaign_participant.joined | A creator joined a campaign. |
campaign_participant.suspended | You suspended a participant. |
campaign_participant.removed | You removed a participant. |
How it behaves
- Every event is sent on the campaign's profile. A webhook or API key scoped to that profile receives them.
- The payloads carry no connected account id.
campaign.id,participant.idandcreator.ididentify what changed. - Every event belongs to the
creator_campaignsresource group. A restricted API key needs that group to subscribe. - Amounts are USD cents. A payout event's
payableCentsis the balance after the event, not a delta. - Retries and redeliveries keep the event
id. Dedupe on it before you pay anyone.
campaign_submission.received
A creator submitted a post to a campaign. Zernio resolved it, opened its tracking window and took the first view sample.
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Webhook payload for campaign_submission.received, campaign_submission.flagged,
campaign_submission.pending_review, campaign_submission.approved and
campaign_submission.rejected. received fires for every accepted submission;
flagged fires when a submission gains a flag it did not carry before (at submission or
on a later sync), and submission.flags lists every flag it carries; pending_review
fires when the submission becomes ready for the operator's review; approved and
rejected fire on the operator's decision.
Response Body
Example Requests
Webhook payload for campaign_submission.received, campaign_submission.flagged,
campaign_submission.pending_review, campaign_submission.approved and
campaign_submission.rejected. received fires for every accepted submission;
flagged fires when a submission gains a flag it did not carry before (at submission or
on a later sync), and submission.flags lists every flag it carries; pending_review
fires when the submission becomes ready for the operator's review; approved and
rejected fire on the operator's decision.
/campaign_submission.receivedcampaign_submission.flagged
A submission gained a new flag, such as duplicate_post, view_spike or low_authenticity. submission.flags lists every flag it carries. Zernio never rejects on its own; the decision is yours.
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Webhook payload for campaign_submission.received, campaign_submission.flagged,
campaign_submission.pending_review, campaign_submission.approved and
campaign_submission.rejected. received fires for every accepted submission;
flagged fires when a submission gains a flag it did not carry before (at submission or
on a later sync), and submission.flags lists every flag it carries; pending_review
fires when the submission becomes ready for the operator's review; approved and
rejected fire on the operator's decision.
Response Body
Example Requests
Webhook payload for campaign_submission.received, campaign_submission.flagged,
campaign_submission.pending_review, campaign_submission.approved and
campaign_submission.rejected. received fires for every accepted submission;
flagged fires when a submission gains a flag it did not carry before (at submission or
on a later sync), and submission.flags lists every flag it carries; pending_review
fires when the submission becomes ready for the operator's review; approved and
rejected fire on the operator's decision.
/campaign_submission.flaggedcampaign_submission.pending_review
A submission is ready for review. A CPM submission reached minViewsToEarn, a pool submission reached minViewsToQualify, and per post submissions arrive here after their first accrual.
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Webhook payload for campaign_submission.received, campaign_submission.flagged,
campaign_submission.pending_review, campaign_submission.approved and
campaign_submission.rejected. received fires for every accepted submission;
flagged fires when a submission gains a flag it did not carry before (at submission or
on a later sync), and submission.flags lists every flag it carries; pending_review
fires when the submission becomes ready for the operator's review; approved and
rejected fire on the operator's decision.
Response Body
Example Requests
Webhook payload for campaign_submission.received, campaign_submission.flagged,
campaign_submission.pending_review, campaign_submission.approved and
campaign_submission.rejected. received fires for every accepted submission;
flagged fires when a submission gains a flag it did not carry before (at submission or
on a later sync), and submission.flags lists every flag it carries; pending_review
fires when the submission becomes ready for the operator's review; approved and
rejected fire on the operator's decision.
/campaign_submission.pending_reviewcampaign_submission.approved
You approved a submission. What it earned so far moves to owed with the campaign hold. Later earnings post straight to owed.
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Webhook payload for campaign_submission.received, campaign_submission.flagged,
campaign_submission.pending_review, campaign_submission.approved and
campaign_submission.rejected. received fires for every accepted submission;
flagged fires when a submission gains a flag it did not carry before (at submission or
on a later sync), and submission.flags lists every flag it carries; pending_review
fires when the submission becomes ready for the operator's review; approved and
rejected fire on the operator's decision.
Response Body
Example Requests
Webhook payload for campaign_submission.received, campaign_submission.flagged,
campaign_submission.pending_review, campaign_submission.approved and
campaign_submission.rejected. received fires for every accepted submission;
flagged fires when a submission gains a flag it did not carry before (at submission or
on a later sync), and submission.flags lists every flag it carries; pending_review
fires when the submission becomes ready for the operator's review; approved and
rejected fire on the operator's decision.
/campaign_submission.approvedcampaign_submission.rejected
You rejected a submission. Its tracking window is cancelled and its unsettled earnings go back to the campaign budget.
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Webhook payload for campaign_submission.received, campaign_submission.flagged,
campaign_submission.pending_review, campaign_submission.approved and
campaign_submission.rejected. received fires for every accepted submission;
flagged fires when a submission gains a flag it did not carry before (at submission or
on a later sync), and submission.flags lists every flag it carries; pending_review
fires when the submission becomes ready for the operator's review; approved and
rejected fire on the operator's decision.
Response Body
Example Requests
Webhook payload for campaign_submission.received, campaign_submission.flagged,
campaign_submission.pending_review, campaign_submission.approved and
campaign_submission.rejected. received fires for every accepted submission;
flagged fires when a submission gains a flag it did not carry before (at submission or
on a later sync), and submission.flags lists every flag it carries; pending_review
fires when the submission becomes ready for the operator's review; approved and
rejected fire on the operator's decision.
/campaign_submission.rejectedcreator_campaign.budget_exhausted
The campaign earnings reached its budget. Nothing more accrues until budget is freed. It fires once per campaign.
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Webhook payload for creator_campaign.budget_exhausted and creator_campaign.closed.
budget_exhausted fires once, when the campaign's earnings first reach
campaign.budgetCents, and carries campaign.budgetExhaustedAt. closed fires once,
when the campaign closes, and carries campaign.closedAt.
Response Body
Example Requests
Webhook payload for creator_campaign.budget_exhausted and creator_campaign.closed.
budget_exhausted fires once, when the campaign's earnings first reach
campaign.budgetCents, and carries campaign.budgetExhaustedAt. closed fires once,
when the campaign closes, and carries campaign.closedAt.
/creator_campaign.budget_exhaustedcreator_campaign.closed
A campaign closed, because you closed it or because endsAt passed and every tracking window had its final read. A pool campaign is split among participants at this moment.
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Webhook payload for creator_campaign.budget_exhausted and creator_campaign.closed.
budget_exhausted fires once, when the campaign's earnings first reach
campaign.budgetCents, and carries campaign.budgetExhaustedAt. closed fires once,
when the campaign closes, and carries campaign.closedAt.
Response Body
Example Requests
Webhook payload for creator_campaign.budget_exhausted and creator_campaign.closed.
budget_exhausted fires once, when the campaign's earnings first reach
campaign.budgetCents, and carries campaign.budgetExhaustedAt. closed fires once,
when the campaign closes, and carries campaign.closedAt.
/creator_campaign.closedcampaign_payout.payable
A participant's held earnings matured. payout.payableCents is the participant's whole payable balance, the amount to pay when you pay from your own rails.
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Webhook payload for campaign_payout.payable and campaign_payout.settled.
payable fires when a participant's held earnings mature; payout.payableCents is the
participant's whole payable balance, so a partner paying from its own rails pays that
amount and records it with POST /v1/creator-campaigns/{campaignId}/participants/{participantId}/settle.
settled fires when a settlement is recorded and carries payout.settlement.
failed fires when a treasury payout is refused, unconfirmed or reversed; the amount returns to payable (minus anything a rejection claws back) and payout.failure says why.
Response Body
Example Requests
Webhook payload for campaign_payout.payable and campaign_payout.settled.
payable fires when a participant's held earnings mature; payout.payableCents is the
participant's whole payable balance, so a partner paying from its own rails pays that
amount and records it with POST /v1/creator-campaigns/{campaignId}/participants/{participantId}/settle.
settled fires when a settlement is recorded and carries payout.settlement.
failed fires when a treasury payout is refused, unconfirmed or reversed; the amount returns to payable (minus anything a rejection claws back) and payout.failure says why.
/campaign_payout.payablecampaign_payout.settled
A payout to a participant was recorded. payout.settlement holds the amount and the reference. recorded is true when the money moved outside Zernio.
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Webhook payload for campaign_payout.payable and campaign_payout.settled.
payable fires when a participant's held earnings mature; payout.payableCents is the
participant's whole payable balance, so a partner paying from its own rails pays that
amount and records it with POST /v1/creator-campaigns/{campaignId}/participants/{participantId}/settle.
settled fires when a settlement is recorded and carries payout.settlement.
failed fires when a treasury payout is refused, unconfirmed or reversed; the amount returns to payable (minus anything a rejection claws back) and payout.failure says why.
Response Body
Example Requests
Webhook payload for campaign_payout.payable and campaign_payout.settled.
payable fires when a participant's held earnings mature; payout.payableCents is the
participant's whole payable balance, so a partner paying from its own rails pays that
amount and records it with POST /v1/creator-campaigns/{campaignId}/participants/{participantId}/settle.
settled fires when a settlement is recorded and carries payout.settlement.
failed fires when a treasury payout is refused, unconfirmed or reversed; the amount returns to payable (minus anything a rejection claws back) and payout.failure says why.
/campaign_payout.settledcampaign_payout.failed
A payout through your Zernio balance did not reach the creator. Stripe refused it, it stayed unconfirmed for 24 hours, or it was reversed. The amount is payable again.
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Webhook payload for campaign_payout.payable and campaign_payout.settled.
payable fires when a participant's held earnings mature; payout.payableCents is the
participant's whole payable balance, so a partner paying from its own rails pays that
amount and records it with POST /v1/creator-campaigns/{campaignId}/participants/{participantId}/settle.
settled fires when a settlement is recorded and carries payout.settlement.
failed fires when a treasury payout is refused, unconfirmed or reversed; the amount returns to payable (minus anything a rejection claws back) and payout.failure says why.
Response Body
Example Requests
Webhook payload for campaign_payout.payable and campaign_payout.settled.
payable fires when a participant's held earnings mature; payout.payableCents is the
participant's whole payable balance, so a partner paying from its own rails pays that
amount and records it with POST /v1/creator-campaigns/{campaignId}/participants/{participantId}/settle.
settled fires when a settlement is recorded and carries payout.settlement.
failed fires when a treasury payout is refused, unconfirmed or reversed; the amount returns to payable (minus anything a rejection claws back) and payout.failure says why.
/campaign_payout.failedcampaign_participant.joined
A creator joined, by opening their invite link or by joining from the open link. It fires once per participant, and participant.joinedAt says when.
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Webhook payload for campaign_participant.joined, campaign_participant.suspended and
campaign_participant.removed. participant.status is the status after the change.
participant.joinedAt is null for a participant removed before they ever joined.
Response Body
Example Requests
Webhook payload for campaign_participant.joined, campaign_participant.suspended and
campaign_participant.removed. participant.status is the status after the change.
participant.joinedAt is null for a participant removed before they ever joined.
/campaign_participant.joinedcampaign_participant.suspended
You suspended a participant. They can no longer connect accounts or submit posts to this campaign. Posts they already submitted keep earning until you reject them.
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Webhook payload for campaign_participant.joined, campaign_participant.suspended and
campaign_participant.removed. participant.status is the status after the change.
participant.joinedAt is null for a participant removed before they ever joined.
Response Body
Example Requests
Webhook payload for campaign_participant.joined, campaign_participant.suspended and
campaign_participant.removed. participant.status is the status after the change.
participant.joinedAt is null for a participant removed before they ever joined.
/campaign_participant.suspendedcampaign_participant.removed
You removed a participant from the campaign. Removing a participant twice fires the event once.
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Webhook payload for campaign_participant.joined, campaign_participant.suspended and
campaign_participant.removed. participant.status is the status after the change.
participant.joinedAt is null for a participant removed before they ever joined.
Response Body
Example Requests
Webhook payload for campaign_participant.joined, campaign_participant.suspended and
campaign_participant.removed. participant.status is the status after the change.
participant.joinedAt is null for a participant removed before they ever joined.
/campaign_participant.removedRelated
Analytics webhooks
Receive a cursor each time an account's analytics sync completes, then read every changed post in one call to the delta feed.
Ads webhooks
Events for an ads account's first sync and stalls, Meta Lead Gen leads, ad reviews and delivery issues, exhausted budgets, triggered ad rules, and ad account and ad status changes.