Campaign & Slot Endpoints
List, create, fund campaigns and inspect their slots via the Automation Actions API
/api/automation/actions/campaignsRequired
Requires API key authentication (UGSK_xxx)
List, create, fund campaigns and inspect their slots via the Automation Actions 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 GET https://api.ugc.africa/api/automation/actions/campaigns \ -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 GET "https://api.ugc.africa/automation/actions/campaigns?status=ACTIVE" \
-H "Authorization: Bearer UGSK_live_..."All endpoints below are under /api/automation/actions and accept a self-serve API key (UGSK_), an Automation connection key (UGAU_), or a portal session JWT - see Server-Side API Overview.
List my campaigns
GET /api/automation/actions/campaigns
| Query Param | Type | Required | Description |
|---|---|---|---|
| status | string | No | Filter by CampaignStatus (e.g. DRAFT, ACTIVE, PAUSED, COMPLETED) |
Response: { "success": true, "data": [ Campaign, ... ] }
Create a campaign
POST /api/automation/actions/campaigns
Creates a DRAFT campaign. It must be funded separately (see below) before it can go ACTIVE.
| Field | Type | Required | Description |
|---|---|---|---|
| title | string | Yes | Max 160 chars |
| description | string | No | Full broadcaster brief, max 10,000 chars |
| campaignType | enum | No | STANDARD_VIDEO (default), MUSIC_PROMO, MUSIC_PROMO_PRERELEASE, FIXED_FEE, TEXT_DISTRIBUTION, PRODUCT_PLACEMENT, CLIP_CAMPAIGN |
| campaignMode | enum | No | Default BROADCAST |
| assets | array | No | CDN URLs plus metadata. There is no upload tool. May be empty on a DRAFT; required before go-live except TEXT_DISTRIBUTION. |
| payoutType | enum | Yes | FLAT, CPM_SOCIAL, or CPM_STREAMING (not CPM) |
| advertiserCpmRate | number | Yes | USD per 1,000 views (CPM types) or per post (FLAT). |
| flatPayoutTiers | array | No | FLAT only - see Performance-tiered flat payouts below |
| totalBudget | number | Yes | Total campaign budget, USD |
| maxSlots | number | No | Ignored for FLAT (server-computed); validated against a cap for CPM_SOCIAL and CPM_STREAMING |
| targetPlatforms | string[] | No | How channels are set. Platform slugs such as whatsapp_status and snapchat. Omit for all active platforms. |
| countryAllowlist | string[] | No | ISO country codes; empty = all eligible countries |
| minKycTier | number | No | 0-2, default 1 |
| minCreatorLevel | number | No | 1-4, default 1 |
| agencyId | string | No | Solution Partner id for commission attribution |
Response: { "success": true, "data": Campaign } (status DRAFT)
Performance-tiered flat payouts
On a FLAT campaign, flatPayoutTiers lets you pay less for a post that gets little verified reach, instead of the full flat rate on every approval. It's optional; omit it and every approved slot pays advertiserCpmRate in full (today's default behavior).
flatPayoutTiers is an array of 1-5 tiers, each a percentage of the campaign's own rate - not a fixed dollar amount, so the same tier table works no matter what rate you set:
"flatPayoutTiers": [
{ "minViews": 30, "payoutPercent": 33 },
{ "minViews": 100, "payoutPercent": 100 }
]| Field | Type | Description |
|---|---|---|
| minViews | integer | Verified view-count threshold for this tier, >= 0 |
| payoutPercent | number | Percent of advertiserCpmRate credited once minViews is met, in (0, 100] |
Rules: tiers must be sorted ascending by minViews with both minViews and payoutPercent strictly increasing, and the top tier (largest minViews) must have payoutPercent: 100 - this can only reduce payout for under-performing posts, never raise it above the advertised rate. A verified view count below the lowest tier's minViews pays 0%. Editable only while the campaign is DRAFT or PENDING_FUNDING; frozen once ACTIVE.
This field may be rejected on accounts where the feature isn't yet enabled. If so, the response is a plain 403 Forbidden - see Errors & Troubleshooting.
Once slots start getting approved, each GET .../slots response includes resolvedFlatPayoutPercent on the slot (the percent actually applied, null for untiered or CPM_SOCIAL / CPM_STREAMING campaigns), and the `earnings_credited` trigger tells you in real time whether a credit was full or reduced.
Get a campaign
GET /api/automation/actions/campaigns/:id
Returns the campaign only if it's owned by the authenticated user.
Fund a campaign
POST /api/automation/actions/campaigns/:id/fund
Initiates funding on the chosen rail. The campaign_funded trigger fires once payment settles, moving the campaign toward ACTIVE.
| Field | Type | Required | Description |
|---|---|---|---|
| method | enum | No | checkout (default, in-app card/bank/crypto/MoMo/wallet), payment_link (shareable URL), or wallet (immediate debit) |
| amount | number | No | USD amount; required for top-ups, defaults to totalBudget on first funding |
| currency | enum | No | Wallet currency when method=wallet; defaults to USD |
| promoCode | string | No | CAMPAIGN_CREDIT promo code |
Response returns a checkout URL (checkout / payment_link) or a wallet debit confirmation. See Advertiser Checkout for the funding rails in detail.
List slots for a campaign
GET /api/automation/actions/campaigns/:id/slots
Only the campaign owner may call this.
| Query Param | Type | Required | Description |
|---|---|---|---|
| status | string | No | Filter by SlotStatus |
| page | number | No | Default 1 |
| limit | number | No | Default 50, max 200 |
Response: { "success": true, "data": [ Slot, ... ], "meta": { "total", "page", "limit" } }
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?