Automation triggers (31)
REST-hook trigger names for the Automation subscribe API
/automation/webhooks/subscribeRequired
Requires API key authentication (UGSK_xxx)
REST-hook trigger names for the Automation subscribe API
Rate Limit
This endpoint is limited to 100 requests per 1 minute per API key.
Rate limit headers are included in all responses: X-RateLimit-Limit, X-RateLimit-Remaining
Credentials
Your API key is never stored and only used for this request
curl -X POST https://api.ugc.africa/automation/webhooks/subscribe \ -H "Authorization: Bearer YOUR_API_KEY"
SDK examples (coming soon)
// Official Node SDK - coming soon. Use REST (see Getting Started) or fetch() examples.
// Planned: npm install @UGC/node
Official @UGC/* client libraries are on the roadmap. Integrate with the REST API and webhooks today. SDK pages · Email notify
Success
{
"id": "..."
}curl -X POST https://api.ugc.africa/automation/webhooks/subscribe \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json"These strings are the `trigger` value for POST /automation/webhooks/subscribe. The list matches AUTOMATION_TRIGGER_LIST in api/src/modules/automation/constants/trigger-types.ts. The Developer Console webhook picker exposes 19 UGC-focused events (campaign, slot, earnings, withdrawals) - not billing/username or AI/storage.
Delivery payloads are JSON and are signed with `X-UGC-Signature` (hex HMAC-SHA256 of the raw JSON body using the hookSecret returned at subscribe time); each delivery also carries an X-UGC-Event-Id header for idempotent processing.
Campaign lifecycle
| Trigger | Fires when |
|---|---|
campaign_created | A DRAFT campaign is created |
campaign_funded | Funding settles (checkout, wallet, or payment link) |
campaign_approved | Moderation approves the campaign |
campaign_rejected | Moderation rejects the campaign |
campaign_activated | Campaign goes live and starts accepting slot reservations |
campaign_paused | Campaign is paused by the advertiser or platform |
campaign_completed | Campaign budget is exhausted or its end date passes |
Slot lifecycle
| Trigger | Fires when |
|---|---|
slot_reserved | A creator reserves a slot on a campaign |
slot_submitted | A creator submits a post URL for review |
slot_approved | A submitted slot is approved |
slot_rejected | A submitted slot is rejected |
slot_expired | A reserved slot's window lapses without submission |
slot_proof_required | Additional proof is requested before approval |
Earnings & withdrawals
| Trigger | Fires when |
|---|---|
earnings_credited | A slot approval credits earnings (fires once per approval that credits > 0; carries payoutPercent and flatPayoutTierApplied so you can tell a full-rate credit from a reduced one on FLAT campaigns with performance tiers - see Create a campaign) |
earnings_released | Cleared creator earnings move from pending balance into the spendable wallet |
withdrawal_initiated | A wallet or crypto cash-out request is on the books (not under manual review) |
withdrawal_review | Ops or policy holds the cash-out (high amount, float, pending approval, etc.) |
withdrawal_completed | The rail confirms funds sent |
withdrawal_failed | Terminal failure on a cash-out |
AI clip, credits & storage
| Trigger | Fires when |
|---|---|
ai_clip_job_succeeded | An AI clip job completes successfully |
ai_clip_job_failed | An AI clip job fails terminally |
ai_credit_balance_low | AI credit balance drops below the configured threshold |
storage_connection_reconnect_required | A linked Drive/Dropbox (or similar) grant must be reconnected before ingest/clip jobs can run |
Checkout, subscriptions & username
| Trigger | Fires when |
|---|---|
payment_intent_created | An advertiser checkout payment intent is created |
payment_paid | A checkout payment intent is paid |
subscription_created | A billing subscription starts |
subscription_cancelled | A billing subscription is cancelled |
subscription_payment_succeeded | A recurring subscription charge succeeds |
subscription_payment_failed | A recurring subscription charge fails |
username_purchased | A premium username is purchased |
username_claimed | A free username is claimed |
Payload shape
{
"id": "evt_...",
"trigger": "campaign_funded",
"timestamp": "2026-08-28T10:30:00Z",
"data": { "...": "..." }
}Exact data fields depend on the resource; treat unfamiliar fields as additive and non-breaking. See Actions for the Campaign/Slot shapes these triggers report on.
Common Errors
INVALID_CURRENCYInvalid Currency Code
The currency code provided is not supported.
{
"success": false,
"error": {
"code": "INVALID_CURRENCY",
"message": "Currency code \"XYZ\" is not supported",
"supportedCurrencies": [
"USD",
"EUR",
"GBP",
"GHS",
"NGN"
]
}
}Solution: Use a valid ISO 4217 currency code. See our list of supported currencies.
INSUFFICIENT_BALANCEInsufficient Wallet Balance
The wallet does not have enough balance to complete this transaction.
{
"success": false,
"error": {
"code": "INSUFFICIENT_BALANCE",
"message": "Insufficient balance in USD wallet",
"required": 5000,
"available": 2500
}
}Solution: Add funds to your wallet or use a different payment method.
UNAUTHORIZEDAuthentication Required
The request requires authentication but none was provided.
{
"success": false,
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid or expired authentication token"
}
}Solution: Include a valid API key or JWT token in the Authorization header.
RATE_LIMIT_EXCEEDEDRate Limit Exceeded
Too many requests have been made in a short period.
{
"success": false,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded. Try again in 60 seconds",
"retryAfter": 60
}
}Solution: Wait for the specified retry period before making another request. Consider implementing exponential backoff.
Did this solve your problem?