Skip to main content

Ads campaigns API

Submit advertising campaigns with Stripe-powered checkout, manage campaign lifecycle (approve, reject, complete), and request Mux upload URLs for ad creative. Campaigns are billed via Stripe checkout and broadcast on baseFM according to their slot schedule.

Submit a campaign

POST /api/ads/campaigns
Submits a new advertising campaign and returns a Stripe checkout URL for payment. No authentication is required — advertisers do not need an Agentbot account. If the caller is an authenticated subscriber on a qualifying plan, a 50% discount is applied automatically.

Request body

FieldTypeRequiredDescription
advertiserNamestringYesName of the advertiser
advertiserEmailstringYesContact email (must contain @)
advertiserUrlstringNoAdvertiser website URL
contactHandlestringNoSocial or messaging handle for contact
titlestringYesCampaign title
descriptionstringNoCampaign description
categorystringNoContent category. One of: ai-tech, dj, music, events, promoter, underground, x-creator, general. Defaults to general.
slotTypestringNoAd slot type. One of: spot, feature, campaign. Defaults to spot. See slot types below.

Slot types

SlotLabelDescriptionBroadcastsBase price (GBP)
spot30-Second Spot30-second audio ad — 5 scheduled broadcasts over 1 week on baseFM5£49
feature60-Second Feature60-second audio ad — 15 scheduled broadcasts over 2 weeks on baseFM15£119
campaign4-Week Campaign60-second audio ad — 40 scheduled broadcasts over 4 weeks across baseFM and Agentbot40£299
Authenticated subscribers on solo, collective, label, or network plans with an active or trialing subscription receive a 50% discount on the base price.

Response

{
  "campaignId": "clxyz123abc",
  "checkoutUrl": "https://checkout.stripe.com/c/pay/cs_live_...",
  "slot": {
    "type": "spot",
    "label": "30-Second Spot",
    "description": "30-second audio ad — 5 scheduled broadcasts over 1 week on baseFM",
    "broadcasts": 5,
    "pence": 4900,
    "envPriceId": "AD_PRICE_SPOT"
  }
}
FieldTypeDescription
campaignIdstringUnique campaign identifier
checkoutUrlstringStripe checkout session URL. Redirect the advertiser here to complete payment.
slot.typestringSelected slot type
slot.labelstringSlot display name
slot.descriptionstringSlot description
slot.broadcastsnumberNumber of scheduled broadcasts included
slot.pencenumberSlot base price in GBP pence (before any subscriber discount)

Errors

CodeDescription
400Advertiser name required — missing advertiserName
400Valid email required — missing or invalid advertiserEmail
400Campaign title required — missing title
500Payments not configured — Stripe is not configured on the platform

Example

curl -X POST https://agentbot.sh/api/ads/campaigns \
  -H "Content-Type: application/json" \
  -d '{
    "advertiserName": "Acme Records",
    "advertiserEmail": "ads@acme.com",
    "title": "Summer Festival Promo",
    "category": "events",
    "slotType": "feature"
  }'

List campaigns

GET /api/ads/campaigns
Returns all campaigns, ordered by creation date (newest first). Requires admin session authentication.

Response

{
  "campaigns": [
    {
      "id": "clxyz123abc",
      "advertiser_name": "Acme Records",
      "advertiser_email": "ads@acme.com",
      "title": "Summer Festival Promo",
      "status": "paid",
      "slot_type": "feature",
      "scheduled_slots": 15,
      "broadcasts_done": 0,
      "amount_pence": 11900,
      "category": "events",
      "created_at": "2026-04-12T10:00:00.000Z"
    }
  ]
}

Campaign statuses

StatusDescription
pending_paymentCampaign submitted, awaiting Stripe checkout completion
paidPayment confirmed via Stripe webhook
approvedAdmin approved the campaign for broadcast
rejectedAdmin rejected the campaign
liveCurrently broadcasting
completeAll broadcasts finished or manually completed

Errors

CodeDescription
403Admin only — requires admin session

Update a campaign

PATCH /api/ads/campaigns/:id
Performs an action on a campaign. The action field in the request body determines the operation. Some actions are admin-only, while request_upload is available to advertisers after payment.

Path parameters

ParameterTypeDescription
idstringCampaign identifier

Actions

request_upload

Generates a Mux direct upload URL for the ad creative. Available to any caller after the campaign has been paid or approved. Each campaign can only have one upload. Request body:
{
  "action": "request_upload"
}
Response:
{
  "uploadUrl": "https://storage.googleapis.com/video-storage-us-east1-uploads/...",
  "uploadId": "upload_abc123"
}
FieldTypeDescription
uploadUrlstringPre-signed URL for direct file upload. The client PUTs the media file to this URL.
uploadIdstringMux upload identifier
Errors:
CodeDescription
402Payment required before uploading — campaign status is not paid or approved
404Not found — campaign does not exist
409Upload already created — a Mux upload URL was already generated for this campaign
500Mux not configured — Mux credentials are missing
502Failed to create upload — Mux API error

approve (admin only)

Approves a campaign for broadcast scheduling. Request body:
FieldTypeRequiredDescription
actionstringYesMust be approve
startsAtstringNoISO 8601 start date. Defaults to 24 hours from now.
notesstringNoAdmin notes
Response:
{
  "success": true,
  "status": "approved",
  "startsAt": "2026-04-13T10:00:00.000Z",
  "endsAt": "2026-04-27T10:00:00.000Z"
}
The endsAt date is calculated based on the slot type: 7 days for spot, 14 days for feature, and 28 days for campaign.

reject (admin only)

Rejects a campaign. Request body:
FieldTypeRequiredDescription
actionstringYesMust be reject
notesstringNoRejection reason
Response:
{
  "success": true,
  "status": "rejected"
}

complete (admin only)

Marks a campaign as complete. Request body:
{
  "action": "complete"
}
Response:
{
  "success": true,
  "status": "complete"
}

Errors (all admin actions)

CodeDescription
400Unknown action: <action> — unrecognized action value
403Admin only — requires admin session
404Not found — campaign does not exist

Example

Request an upload URL after payment:
curl -X PATCH https://agentbot.sh/api/ads/campaigns/clxyz123abc \
  -H "Content-Type: application/json" \
  -d '{ "action": "request_upload" }'
Approve a campaign as admin:
curl -X PATCH https://agentbot.sh/api/ads/campaigns/clxyz123abc \
  -H "Content-Type: application/json" \
  -H "Cookie: next-auth.session-token=YOUR_SESSION" \
  -d '{
    "action": "approve",
    "startsAt": "2026-04-15T09:00:00.000Z",
    "notes": "Approved for baseFM morning slot"
  }'