PAI Palooza Advertising API
PAI Palooza is AINative Studio's event advertising platform. Event organizers monetize their event pages by selling ad slots; sponsors purchase ad credits and run targeted campaigns. The API covers the complete advertising stack: sponsor registration, creative upload, campaign management, ad serving, impression/click tracking, and payouts.
Base path: https://api.ainative.studio/api/v1/paipalooza
Authentication: All endpoints require Authorization: Bearer <your_api_key> unless explicitly marked as public.
Sponsors
Sponsors are companies or individuals that purchase ad credits to run campaigns on event pages.
Create sponsor profile
POST /api/v1/paipalooza/sponsors
Register as a sponsor. Creates a Stripe customer account and initializes a credit balance of 0.
Request body
{
"user_id": "550e8400-e29b-41d4-a716-446655440000",
"company_name": "Acme AI Corp",
"industry": "Artificial Intelligence",
"website_url": "https://acmeai.example.com",
"contact_email": "ads@acmeai.example.com"
}
| Field | Type | Required | Description |
|---|---|---|---|
user_id | string (UUID) | Yes | Must match the authenticated user's ID |
company_name | string | Yes | Company or advertiser name |
industry | string | No | Industry category |
website_url | string | No | Company website |
contact_email | string | Yes | Contact email for billing and notifications |
Response 201 Created
{
"id": "sponsor_abc123",
"user_id": "550e8400-e29b-41d4-a716-446655440000",
"company_name": "Acme AI Corp",
"industry": "Artificial Intelligence",
"website_url": "https://acmeai.example.com",
"contact_email": "ads@acmeai.example.com",
"stripe_customer_id": "cus_abc123xyz",
"credit_balance": 0,
"auto_reload_enabled": false,
"auto_reload_threshold": null,
"auto_reload_amount": null,
"created_at": "2026-04-25T14:00:00Z"
}
Get sponsor profile
GET /api/v1/paipalooza/sponsors/{sponsor_id}
Retrieve sponsor profile including current credit balance.
Path parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
sponsor_id | string | Yes | Sponsor ID |
Response 200 OK
Returns a SponsorResponse object (same shape as create response).
Update sponsor profile
PATCH /api/v1/paipalooza/sponsors/{sponsor_id}
Update sponsor information. Only the sponsor owner or an admin can update.
Updatable fields: company_name, industry, website_url, contact_email, auto_reload_enabled, auto_reload_threshold, auto_reload_amount.
Purchase ad credits
POST /api/v1/paipalooza/sponsors/{sponsor_id}/purchase-credits
Purchase ad credits via Stripe. 1 credit = $1 USD.
The flow:
- This endpoint creates a Stripe payment intent and returns a
client_secret. - Your frontend completes the payment using Stripe.js.
- Credits are automatically added to the sponsor's balance via Stripe webhook.
Request body
{
"sponsor_id": "sponsor_abc123",
"amount": 500,
"payment_method_id": "pm_stripe_test_method"
}
| Field | Type | Required | Description |
|---|---|---|---|
sponsor_id | string | Yes | Must match the path parameter |
amount | integer | Yes | Number of credits to purchase (= USD amount) |
payment_method_id | string | Yes | Stripe payment method ID |
Response 200 OK
{
"payment_intent_id": "pi_abc123",
"client_secret": "pi_abc123_secret_xyz",
"amount": 500,
"status": "requires_confirmation"
}
Get ad credits balance
GET /api/v1/paipalooza/sponsors/{sponsor_id}/ad-credits
Get current credit balance and usage summary.
Response 200 OK
{
"sponsor_id": "sponsor_abc123",
"total_credits_purchased": 1500,
"credits_used": 342,
"remaining_balance": 1158,
"total_usd_spent": 342.00,
"auto_reload_enabled": false,
"auto_reload_threshold": null,
"low_balance_alert": false
}
Get usage breakdown by campaign
GET /api/v1/paipalooza/sponsors/{sponsor_id}/usage-breakdown
Get per-campaign credit usage for a given time period.
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
time_period | string | last_30_days | last_7_days, last_30_days, last_90_days, or all_time |
Response 200 OK
{
"sponsor_id": "sponsor_abc123",
"time_period": "last_30_days",
"campaigns": [
{
"campaign_id": "camp_abc123",
"campaign_name": "AI Conference Q1 2026",
"credits_used": 200,
"impressions": 10000,
"clicks": 320,
"ctr": 0.032,
"average_cpm": 20.00
}
]
}
Ad Creatives
Creatives are the ad assets (video, banner, or native) that run inside campaigns. All creatives start in pending_review status and must be approved before they can serve.
Creative statuses
| Status | Meaning |
|---|---|
pending_review | Awaiting admin approval |
approved | Active and available for serving |
rejected | Rejected by admin; cannot serve |
Upload video creative
POST /api/v1/paipalooza/ad-creatives/video
Upload a video ad. The pipeline transcodes the video to 720p and 1080p, extracts a thumbnail, and generates a VAST 4.2 XML document.
Content-Type: multipart/form-data
Form fields
| Field | Type | Required | Description |
|---|---|---|---|
video_file | file | Yes | MP4, MOV, or WebM; max 500 MB |
title | string | Yes | Creative title (3–200 characters) |
call_to_action | string | Yes | CTA text (e.g., Register Now, Learn More, Watch Now) |
destination_url | string | Yes | Click destination (must be HTTPS) |
description | string | No | Creative description (max 1000 characters) |
duration_seconds | integer | No | Video duration in seconds (5–120); auto-detected if omitted |
Response 201 Created
{
"id": "creative_def456",
"sponsor_id": "sponsor_abc123",
"type": "video",
"status": "pending_review",
"title": "Acme AI — Register Now",
"call_to_action": "Register Now",
"destination_url": "https://acmeai.example.com/register",
"duration_seconds": 30,
"video_url": "https://r2.example.com/videos/creative_def456_original.mp4",
"transcoded_urls": {
"720p": "https://r2.example.com/videos/creative_def456_720p.mp4",
"1080p": "https://r2.example.com/videos/creative_def456_1080p.mp4"
},
"thumbnail_url": "https://r2.example.com/thumbnails/creative_def456.jpg",
"vast_xml_url": "https://api.ainative.studio/api/v1/paipalooza/ad-creatives/creative_def456/vast.xml",
"vast_tracking_urls": {
"impression": "https://api.ainative.studio/api/v1/paipalooza/tracking/impression",
"start": "...",
"complete": "..."
},
"created_at": "2026-04-25T14:00:00Z"
}
Example
curl -X POST https://api.ainative.studio/api/v1/paipalooza/ad-creatives/video \
-H "Authorization: Bearer $API_KEY" \
-F "video_file=@/path/to/ad.mp4" \
-F "title=Acme AI — Register Now" \
-F "call_to_action=Register Now" \
-F "destination_url=https://acmeai.example.com/register"
with open("/path/to/ad.mp4", "rb") as f:
resp = requests.post(
"https://api.ainative.studio/api/v1/paipalooza/ad-creatives/video",
headers={"Authorization": f"Bearer {api_key}"},
data={
"title": "Acme AI — Register Now",
"call_to_action": "Register Now",
"destination_url": "https://acmeai.example.com/register",
},
files={"video_file": f}
)
creative = resp.json()
Upload banner creative
POST /api/v1/paipalooza/ad-creatives/banner
Upload a banner image. Dimensions must exactly match the declared IAB standard size.
Content-Type: multipart/form-data
IAB standard sizes
ad_size value | Dimensions | Common use |
|---|---|---|
300x250 | 300 × 250 | Medium Rectangle — inline content |
728x90 | 728 × 90 | Leaderboard — top of page |
320x50 | 320 × 50 | Mobile Banner — bottom sticky |
160x600 | 160 × 600 | Wide Skyscraper — sidebar |
970x250 | 970 × 250 | Billboard — homepage takeover |
Form fields
| Field | Type | Required | Description |
|---|---|---|---|
image_file | file | Yes | JPG, PNG, or GIF; max 5 MB |
title | string | Yes | Creative title (3–200 characters) |
call_to_action | string | Yes | CTA text |
destination_url | string | Yes | Click destination (HTTPS) |
ad_size | string | Yes | One of the IAB sizes above |
description | string | No | Creative description |
Response 201 Created
{
"id": "creative_banner789",
"sponsor_id": "sponsor_abc123",
"type": "banner",
"status": "pending_review",
"title": "Acme AI Leaderboard",
"ad_size": "728x90",
"width": 728,
"height": 90,
"banner_url": "https://r2.example.com/banners/creative_banner789.jpg",
"is_animated": false,
"destination_url": "https://acmeai.example.com/register",
"created_at": "2026-04-25T14:05:00Z"
}
Create native creative
POST /api/v1/paipalooza/ad-creatives/native
Create a text-based native ad that blends into content feeds. An optional image can be uploaded.
Content-Type: multipart/form-data
Form fields
| Field | Type | Required | Constraints |
|---|---|---|---|
title | string | Yes | 10–60 characters |
description | string | Yes | 30–200 characters |
call_to_action | string | Yes | Max 20 characters |
destination_url | string | Yes | HTTPS |
sponsor_name | string | No | Max 50 characters |
sponsor_logo_url | string | No | HTTPS URL |
image_file | file | No | JPG, PNG, or GIF; max 5 MB |
Response 201 Created
{
"id": "creative_native001",
"sponsor_id": "sponsor_abc123",
"type": "native",
"status": "pending_review",
"title": "Build smarter AI agents",
"description": "AINative Studio gives you the tools to ship AI-native apps in days, not months.",
"call_to_action": "Start Free Trial",
"destination_url": "https://ainative.studio/signup",
"sponsor_name": "AINative Studio",
"image_url": null,
"created_at": "2026-04-25T14:08:00Z"
}
List creatives
GET /api/v1/paipalooza/ad-creatives
List your creatives with optional filtering.
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
status | string | — | pending_review, approved, or rejected |
type | string | — | video, banner, or native |
skip | integer | 0 | Pagination offset |
limit | integer | 20 | Results per page |
Response 200 OK
{
"creatives": [
{
"id": "creative_def456",
"type": "video",
"status": "approved",
"title": "Acme AI — Register Now"
}
],
"total": 3,
"skip": 0,
"limit": 20
}
Get creative
GET /api/v1/paipalooza/ad-creatives/{creative_id}
Get full details for a specific creative. You must own the creative.
Update creative metadata
PUT /api/v1/paipalooza/ad-creatives/{creative_id}
Update title, description, CTA, or destination URL. Only pending_review creatives can be updated; approved or rejected creatives are locked.
Request body
{
"title": "Acme AI — Join Us Today",
"destination_url": "https://acmeai.example.com/join"
}
Delete creative
DELETE /api/v1/paipalooza/ad-creatives/{creative_id}
Delete a creative. Only pending_review creatives can be deleted. Returns 204 No Content.
Get VAST 4.2 XML
GET /api/v1/paipalooza/ad-creatives/{creative_id}/vast.xml
Public endpoint — no auth required. Serves an IAB VAST 4.2 compliant XML document for video ad players. Only works for approved creatives.
Response Content-Type: application/xml
<?xml version="1.0" encoding="UTF-8"?>
<VAST version="4.2">
<Ad id="creative_def456">
<InLine>
<AdTitle>Acme AI — Register Now</AdTitle>
<Creatives>
<Creative>
<Linear>
<Duration>00:00:30</Duration>
<MediaFiles>
<MediaFile type="video/mp4" width="1280" height="720">
https://r2.example.com/videos/creative_def456_720p.mp4
</MediaFile>
</MediaFiles>
<VideoClicks>
<ClickThrough>https://acmeai.example.com/register</ClickThrough>
</VideoClicks>
</Linear>
</Creative>
</Creatives>
</InLine>
</Ad>
</VAST>
Campaigns
Campaigns tie together a sponsor, one or more approved creatives, a budget, a date range, and optional targeting rules.
Campaign statuses
| Status | Description |
|---|---|
draft | Not yet serving; can be edited |
active | Serving ads; credits are being consumed |
paused | Temporarily stopped; budget is still reserved |
completed | End date reached or budget exhausted |
cancelled | Manually cancelled |
Create a campaign
POST /api/v1/paipalooza/campaigns/
Create a new campaign. New campaigns start in draft status.
Request body
{
"sponsor_id": "sponsor_abc123",
"name": "AI Conference Q1 2026",
"creative_ids": ["creative_def456"],
"budget_credits": 1000,
"start_date": "2026-05-01T00:00:00Z",
"end_date": "2026-06-30T23:59:59Z",
"targeting": {
"event_types": ["conference", "hackathon"],
"topics": ["AI", "machine learning"],
"locations": ["San Francisco", "New York"],
"min_attendees": 50,
"max_attendees": null
}
}
| Field | Type | Required | Description |
|---|---|---|---|
sponsor_id | string | Yes | Sponsor ID (must own the sponsor account) |
name | string | Yes | Campaign name |
creative_ids | array[string] | Yes | One or more approved creative IDs |
budget_credits | integer | Yes | Total credits to allocate (1 credit = $1) |
start_date | datetime | Yes | Campaign start (must be in the future) |
end_date | datetime | Yes | Campaign end (must be after start) |
targeting | object | No | Targeting rules — null means target all events |
targeting.event_types | array[string] | No | Match events by type |
targeting.topics | array[string] | No | Match events by topic via semantic similarity (threshold ≥ 0.7) |
targeting.locations | array[string] | No | Match events by location (case-insensitive) |
targeting.min_attendees | integer | No | Minimum attendee threshold |
targeting.max_attendees | integer | No | Maximum attendee threshold |
Response 201 Created
{
"id": "camp_abc123",
"sponsor_id": "sponsor_abc123",
"name": "AI Conference Q1 2026",
"status": "draft",
"creative_ids": ["creative_def456"],
"budget_credits": 1000,
"spent_credits": 0,
"remaining_credits": 1000,
"start_date": "2026-05-01T00:00:00Z",
"end_date": "2026-06-30T23:59:59Z",
"targeting": {
"event_types": ["conference", "hackathon"],
"topics": ["AI", "machine learning"],
"locations": ["San Francisco", "New York"]
},
"created_at": "2026-04-25T14:00:00Z"
}
Errors
| Code | Reason |
|---|---|
400 | Invalid dates, or start date is in the past |
402 | Insufficient credits |
403 | Creative does not belong to this sponsor |
404 | Sponsor or creative not found |
Example
curl -X POST https://api.ainative.studio/api/v1/paipalooza/campaigns/ \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"sponsor_id": "sponsor_abc123",
"name": "AI Conference Q1 2026",
"creative_ids": ["creative_def456"],
"budget_credits": 1000,
"start_date": "2026-05-01T00:00:00Z",
"end_date": "2026-06-30T23:59:59Z"
}'
resp = requests.post(
"https://api.ainative.studio/api/v1/paipalooza/campaigns/",
headers={"Authorization": f"Bearer {api_key}"},
json={
"sponsor_id": "sponsor_abc123",
"name": "AI Conference Q1 2026",
"creative_ids": ["creative_def456"],
"budget_credits": 1000,
"start_date": "2026-05-01T00:00:00Z",
"end_date": "2026-06-30T23:59:59Z",
}
)
campaign = resp.json()
Get campaign
GET /api/v1/paipalooza/campaigns/{campaign_id}
Retrieve full campaign details including budget tracking.
List campaigns
GET /api/v1/paipalooza/campaigns/
List campaigns with filtering, sorting, and pagination.
Query parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
sponsor_id | string | Yes | — | Filter by sponsor |
status | string | No | — | draft, active, paused, completed, or cancelled |
creative_id | string | No | — | Filter to campaigns using this creative |
event_type | string | No | — | Filter by event type targeting |
start_date_from | datetime | No | — | Campaigns starting on or after (ISO 8601) |
start_date_to | datetime | No | — | Campaigns starting on or before (ISO 8601) |
search | string | No | — | Search in campaign name |
sort_by | string | No | created_at | created_at, start_date, budget_credits, or name |
sort_order | string | No | desc | asc or desc |
offset | integer | No | 0 | Pagination offset |
limit | integer | No | 20 | Results per page (max 100) |
Response 200 OK
{
"campaigns": [...],
"total": 5,
"limit": 20,
"offset": 0,
"has_more": false
}
Update campaign
PATCH /api/v1/paipalooza/campaigns/{campaign_id}
Update a draft or paused campaign. Active and completed campaigns cannot be modified. You can increase budget_credits or extend end_date, but cannot shorten the campaign or change the start_date.
Activate campaign
POST /api/v1/paipalooza/campaigns/{campaign_id}/activate
Transition a draft campaign to active. The campaign will begin serving ads immediately based on its targeting configuration.
Requirements: Campaign must be in draft status; all creatives must still be approved.
Pause campaign
POST /api/v1/paipalooza/campaigns/{campaign_id}/pause
Stop a running campaign. No new impressions will be served, but the budget remains reserved.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
sponsor_id | string | Yes | Sponsor ID for authorization |
Resume campaign
POST /api/v1/paipalooza/campaigns/{campaign_id}/resume
Resume a paused campaign. Requires sufficient credits for the daily budget.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
sponsor_id | string | Yes | Sponsor ID for authorization |
Preview targeting
GET /api/v1/paipalooza/campaigns/{campaign_id}/preview-targeting
Before activating, preview which published events match the campaign's targeting criteria.
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 50 | Max matching events to return (max 100) |
Response 200 OK
{
"campaign_id": "camp_abc123",
"targeting": {
"event_types": ["conference"],
"topics": ["AI"]
},
"matching_events": [
{
"id": "evt_001",
"title": "AI Summit 2026",
"event_type": "conference"
}
],
"total_matched": 12,
"similarity_scores": {
"evt_001": 0.92
}
}
Campaign & Creative Analytics
Get campaign performance
GET /api/v1/paipalooza/campaigns/{campaign_id}/performance
Get comprehensive performance metrics for a campaign.
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
time_range | string | 7d | 24h, 7d, 30d, or custom |
start_date | datetime | — | Required when time_range=custom |
end_date | datetime | — | Required when time_range=custom |
include_creatives | boolean | true | Include per-creative breakdown |
include_events | boolean | true | Include per-event breakdown |
Response 200 OK
{
"campaign": {
"campaign_id": "camp_abc123",
"impressions": 50000,
"clicks": 1600,
"completions": 8500,
"ctr": 0.032,
"completion_rate": 0.17,
"total_spend": 100.00,
"average_cpm": 2.00,
"average_cpc": 0.063
},
"by_creative": [
{
"creative_id": "creative_def456",
"impressions": 50000,
"clicks": 1600,
"ctr": 0.032
}
],
"by_event": [
{
"event_id": "evt_001",
"impressions": 30000,
"revenue": 60.00,
"fill_rate": 0.85
}
],
"time_range": "7d",
"generated_at": "2026-04-25T15:00:00Z"
}
Export campaign performance as CSV
GET /api/v1/paipalooza/campaigns/{campaign_id}/performance/export
Export campaign performance as a CSV file. Accepts the same query parameters as the performance endpoint.
Response 200 OK
{
"filename": "campaign_camp_abc123_7d_2026-04-25.csv",
"content": "type,id,name,impressions,clicks,completions,ctr,completion_rate,spend\n...",
"row_count": 12,
"generated_at": "2026-04-25T15:00:00Z"
}
Get creative performance
GET /api/v1/paipalooza/campaigns/creative/{creative_id}/performance
Get detailed performance analytics for a specific creative across all campaigns.
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
time_range | string | 7d | 24h, 7d, 30d, or custom |
campaign_id | string | — | Scope to a specific campaign |
include_daily | boolean | true | Include daily time series |
include_by_campaign | boolean | true | Include breakdown by campaign |
Compare creatives
GET /api/v1/paipalooza/campaigns/creatives/compare
Compare up to 10 creatives side-by-side, ranked by CTR.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
creative_ids | array[string] | Yes | List of creative IDs to compare (max 10) |
time_range | string | No | 24h, 7d, 30d, or custom |
Response 200 OK
{
"creatives": [
{
"creative_id": "creative_def456",
"impressions": 50000,
"clicks": 1600,
"ctr": 0.032,
"rank": 1
},
{
"creative_id": "creative_banner789",
"impressions": 30000,
"clicks": 450,
"ctr": 0.015,
"rank": 2
}
],
"best_performing": "creative_def456",
"total_creatives": 2,
"time_range": "7d",
"generated_at": "2026-04-25T15:00:00Z"
}
Get event performance (organizer view)
GET /api/v1/paipalooza/campaigns/event/{event_id}/performance
Ad revenue analytics for a specific event — designed for event organizers to see how much their event is earning from ad placements.
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
time_range | string | 7d | 24h, 7d, 30d, or custom |
include_daily | boolean | true | Daily revenue time series |
include_sponsors | boolean | true | Top sponsors by spend |
Response 200 OK
{
"event": {
"event_id": "evt_001",
"impressions": 80000,
"unique_impressions": 65000,
"clicks": 2560,
"completions": 12800,
"ctr": 0.032,
"completion_rate": 0.16,
"total_revenue": 160.00,
"average_rpm": 2.00,
"fill_rate": 0.87
},
"daily_revenue": [
{ "date": "2026-04-19", "revenue": 22.40 },
{ "date": "2026-04-20", "revenue": 28.60 }
],
"top_sponsors": [
{ "sponsor_id": "sponsor_abc123", "spend": 100.00 }
],
"time_range": "7d",
"generated_at": "2026-04-25T15:00:00Z"
}
Compare events
GET /api/v1/paipalooza/campaigns/events/compare
Compare up to 10 events side-by-side, ranked by revenue.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
event_ids | array[string] | Yes | Event IDs to compare (max 10) |
time_range | string | No | 24h, 7d, 30d, or custom |
Ad Serving
Serve an ad
POST /api/v1/paipalooza/ads/serve
Public endpoint — no auth required. Called by the frontend ad widget when an event page loads. Returns the best-matched ad for the given placement, or 204 No Content if no eligible ads are available.
Rate limit: 10,000 requests/minute per IP.
Request body
{
"event_id": "evt_001",
"placement": "header",
"device_type": "desktop"
}
| Field | Type | Required | Description |
|---|---|---|---|
event_id | string | Yes | ID of the event page requesting an ad |
placement | string | Yes | Ad slot: header, sidebar, content, or video |
device_type | string | Yes | desktop, mobile, or tablet |
Response 200 OK — ad found
{
"ad_id": "ad_xyz789abc123",
"creative_id": "creative_def456",
"campaign_id": "camp_abc123",
"creative_type": "video",
"creative_data": {
"title": "AI Conference 2026",
"video_url": "https://r2.example.com/videos/creative_def456_720p.mp4",
"thumbnail_url": "https://r2.example.com/thumbnails/creative_def456.jpg",
"duration_seconds": 30,
"call_to_action": "Register Now",
"destination_url": "https://acmeai.example.com/register"
},
"tracking_urls": {
"impression_url": "https://api.ainative.studio/api/v1/paipalooza/tracking/impression",
"click_url": "https://api.ainative.studio/api/v1/paipalooza/tracking/click",
"completion_url": "https://api.ainative.studio/api/v1/paipalooza/tracking/completion"
}
}
Response 204 No Content
No eligible ads are available for this placement.
Example
// Frontend ad widget
const resp = await fetch("https://api.ainative.studio/api/v1/paipalooza/ads/serve", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
event_id: "evt_001",
placement: "header",
device_type: "desktop"
})
});
if (resp.status === 204) {
// No ad available — hide ad slot
return;
}
const ad = await resp.json();
// Render ad using ad.creative_data
// Fire impression beacon to ad.tracking_urls.impression_url
Tracking
All tracking endpoints are public — no authentication required. They are designed to be called by ad widgets and VAST-compatible video players.
Rate limit: 1,000 requests/minute per IP for all tracking endpoints.
Track impression
POST /api/v1/paipalooza/tracking/impression
Record that an ad was displayed. Deduplicates within 24 hours per authenticated user.
Credit costs per placement
| Placement | Cost per impression |
|---|---|
header | $0.02 |
sidebar | $0.01 |
content | $0.015 |
video | $0.03 |
footer | $0.005 |
Request body
{
"campaign_id": "camp_abc123",
"creative_id": "creative_def456",
"event_id": "evt_001",
"placement": "header",
"user_id": "550e8400-e29b-41d4-a716-446655440000"
}
| Field | Type | Required | Description |
|---|---|---|---|
campaign_id | string | Yes | Active campaign ID |
creative_id | string | Yes | Approved creative ID assigned to the campaign |
event_id | string | Yes | Event page where the ad was shown |
placement | string | Yes | header, sidebar, content, video, or footer |
user_id | string (UUID) | No | Authenticated viewer (for deduplication) |
Response 201 Created
{
"impression_id": "imp_abc123xyz",
"campaign_id": "camp_abc123",
"creative_id": "creative_def456",
"event_id": "evt_001",
"placement": "header",
"credits_deducted": 0.02,
"created_at": "2026-04-25T15:00:00Z"
}
Errors
| Code | Reason |
|---|---|
400 | Campaign inactive or creative unapproved |
404 | Campaign, creative, or event not found |
409 | Duplicate impression within 24h for this user |
Track click
POST /api/v1/paipalooza/tracking/click
Record that a user clicked an ad. Must reference a valid impression.
Request body
{
"impression_id": "imp_abc123xyz",
"campaign_id": "camp_abc123",
"creative_id": "creative_def456",
"destination_url": "https://acmeai.example.com/register"
}
| Field | Type | Required | Description |
|---|---|---|---|
impression_id | string | Yes | Impression to associate with this click |
campaign_id | string | Yes | Campaign ID |
creative_id | string | Yes | Creative ID |
destination_url | string | Yes | Click destination URL |
Response 201 Created
{
"click_id": "clk_def456abc",
"impression_id": "imp_abc123xyz",
"campaign_id": "camp_abc123",
"redirect_url": "https://acmeai.example.com/register",
"time_to_click_seconds": 12,
"created_at": "2026-04-25T15:00:12Z"
}
Notes
time_to_click_seconds: seconds elapsed between impression and click- Clicks within 30 seconds earn an engagement quality bonus
- Duplicate clicks for the same impression are rejected with
409
Track video completion
POST /api/v1/paipalooza/tracking/completion
Track video ad playback milestones. Only applies to video creatives.
Milestones
| Milestone | milestone value | Engagement score |
|---|---|---|
| Ad started | 0 | 0.0 |
| 25% watched | 25 | 0.25 |
| 50% watched | 50 | 0.50 |
| 75% watched | 75 | 0.75 |
| 100% watched | 100 | 1.0 |
Only 100% completion earns a $0.01 credit bonus.
Request body
{
"impression_id": "imp_abc123xyz",
"campaign_id": "camp_abc123",
"creative_id": "creative_def456",
"milestone": 100
}
| Field | Type | Required | Description |
|---|---|---|---|
impression_id | string | Yes | The originating impression |
campaign_id | string | Yes | Campaign ID |
creative_id | string | Yes | Must be a video creative |
milestone | integer | Yes | 0, 25, 50, 75, or 100 |
Response 201 Created
{
"completion_id": "cmp_ghi789jkl",
"impression_id": "imp_abc123xyz",
"campaign_id": "camp_abc123",
"milestone": 100,
"engagement_score": 1.0,
"credits_bonus": 0.01,
"created_at": "2026-04-25T15:00:30Z"
}
Errors
| Code | Reason |
|---|---|
400 | Creative is not video type, or campaign is inactive |
409 | Duplicate milestone for this impression |
Content Inventory
Event organizers define ad slot inventory that sponsors can browse and campaigns can fill.
Create inventory
POST /api/v1/paipalooza/content-inventory/events/{event_id}/inventory
Organizer auth required. Define available ad slots for an event.
Wait — this endpoint path is constructed from the router prefix. Based on the source, the path is:
POST /api/v1/paipalooza/content-inventory
With event_id as a query parameter.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
event_id | string | Yes | Event to attach inventory to |
Request body
{
"slot_type": "video_preroll",
"format": "video",
"total_slots": 10,
"floor_price": 5.00,
"suggested_price": 8.00
}
| Field | Type | Required | Description |
|---|---|---|---|
slot_type | string | Yes | video_preroll, banner_sidebar, email_header, content_native, or audio_preroll |
format | string | Yes | video, banner, native, or audio |
total_slots | integer | Yes | Number of available slots (must be > 0) |
floor_price | number | Yes | Minimum price per slot (USD, >= 0) |
suggested_price | number | Yes | Recommended price per slot (>= floor_price) |
Response 201 Created
{
"id": "inv_abc001",
"event_id": "evt_001",
"organizer_id": "organizer_xyz",
"slot_type": "video_preroll",
"format": "video",
"total_slots": 10,
"filled_slots": 0,
"available_slots": 10,
"floor_price": 5.00,
"suggested_price": 8.00,
"created_at": "2026-04-25T14:00:00Z"
}
Get inventory
GET /api/v1/paipalooza/content-inventory/{inventory_id}
Public endpoint. Get inventory details by ID.
List event inventory
GET /api/v1/paipalooza/content-inventory/events/{event_id}/inventory
Public endpoint. Browse available ad slots for an event — no auth required so sponsors can discover inventory before registering.
Path parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
event_id | string | Yes | Event ID |
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
format | string | — | Filter by format: video, banner, native, or audio |
sort_by | string | price | price or availability |
sort_order | string | asc | asc or desc |
limit | integer | 25 | Results per page (1–100) |
offset | integer | 0 | Pagination offset |
Response 200 OK
{
"items": [
{
"id": "inv_abc001",
"event_id": "evt_001",
"slot_type": "video_preroll",
"format": "video",
"total_slots": 10,
"filled_slots": 3,
"available_slots": 7,
"floor_price": 5.00,
"suggested_price": 8.00
}
],
"total": 4,
"has_more": false
}
Organizers
Event organizers register to monetize their events through ad revenue.
Create organizer
POST /api/v1/paipalooza/organizers
Register as an event organizer. Creates a Stripe Connect Express account and sets default revenue share to 60%.
Request body
{
"user_id": "550e8400-e29b-41d4-a716-446655440000",
"organization_name": "AI Events LLC",
"contact_email": "hello@aievents.example.com",
"contact_phone": "+14155551234"
}
| Field | Type | Required | Description |
|---|---|---|---|
user_id | string (UUID) | Yes | Must match authenticated user |
organization_name | string | Yes | Organization name |
contact_email | string | Yes | Used for Stripe Connect verification |
contact_phone | string | No | Contact phone number |
Response 201 Created
{
"id": "organizer_xyz",
"user_id": "550e8400-e29b-41d4-a716-446655440000",
"organization_name": "AI Events LLC",
"contact_email": "hello@aievents.example.com",
"stripe_connect_account_id": "acct_abc123",
"revenue_share_percentage": 60,
"status": "pending",
"total_revenue_earned": 0.00,
"total_payouts": 0.00,
"created_at": "2026-04-25T14:00:00Z"
}
Errors
| Code | Reason |
|---|---|
400 | User already has an organizer account |
403 | user_id does not match authenticated user |
Get organizer
GET /api/v1/paipalooza/organizers/{organizer_id}
Get organizer profile. Accessible only by the organizer themselves or an admin.
Update organizer
PATCH /api/v1/paipalooza/organizers/{organizer_id}
Update organization_name, contact_email, or contact_phone. Revenue share percentage can only be changed by an admin.
Get organizer revenue
GET /api/v1/paipalooza/organizers/{organizer_id}/revenue
View revenue metrics and payout status for an organizer.
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
days | integer | 7 | Time range in days (1–365) |
Response 200 OK
{
"organizer_id": "organizer_xyz",
"total_revenue_earned": 1500.00,
"total_payouts": 1000.00,
"pending_balance": 500.00,
"payout_eligible": true,
"payout_threshold": 100.00,
"revenue_by_event": [
{
"event_id": "evt_001",
"event_title": "AI Summit 2026",
"revenue": 800.00
}
],
"period_impressions": 400000,
"period_revenue": 350.00,
"period_days": 7
}
Payouts
Organizers receive 60% of ad revenue (configurable per organizer). Payouts require a minimum pending balance of $100 and a valid Stripe Connect account.
Check payout eligibility
GET /api/v1/paipalooza/payouts/eligibility/{organizer_id}
Check whether an organizer is eligible for a payout.
Response 200 OK
{
"organizer_id": "organizer_xyz",
"pending_balance": 500.00,
"payout_threshold": 100.00,
"eligible": true,
"reason": null
}
If not eligible:
{
"organizer_id": "organizer_xyz",
"pending_balance": 42.00,
"payout_threshold": 100.00,
"eligible": false,
"reason": "Pending balance $42.00 is below the $100.00 threshold"
}
Get payout history
GET /api/v1/paipalooza/payouts/history/{organizer_id}
Retrieve payout history, sorted most recent first.
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 100 | Maximum records to return (max 1000) |
Response 200 OK
[
{
"payout_id": "pay_abc001",
"amount": 500.00,
"status": "completed",
"stripe_payout_id": "po_stripe_123",
"created_at": "2026-04-20T10:00:00Z",
"completed_at": "2026-04-22T08:00:00Z",
"failure_reason": null
}
]
Process pending payouts (Admin only)
POST /api/v1/paipalooza/payouts/process
Admin only. Batch process all eligible organizer payouts. Intended to be called by a scheduled job.
Response 200 OK
{
"total_checked": 25,
"eligible_count": 8,
"successful_payouts": 7,
"failed_payouts": 1,
"total_amount_paid": 4200.00,
"errors": [
"Organizer organizer_zzz: Stripe Connect account not verified"
]
}
Manual payout (Admin only)
POST /api/v1/paipalooza/payouts/manual
Admin only. Trigger a payout for a specific organizer, optionally bypassing the $100 minimum threshold.
Request body
{
"organizer_id": "organizer_xyz",
"override_threshold": false
}
| Field | Type | Required | Description |
|---|---|---|---|
organizer_id | string | Yes | Organizer to pay out |
override_threshold | boolean | No | If true, bypass the $100 minimum balance requirement |
Response 201 Created
{
"payout_id": "pay_abc002",
"organizer_id": "organizer_xyz",
"stripe_payout_id": "po_stripe_456",
"amount": 500.00,
"status": "completed",
"created_at": "2026-04-25T15:00:00Z",
"completed_at": "2026-04-25T15:00:05Z"
}
Errors
| Code | Reason |
|---|---|
400 | Organizer has no pending balance |
403 | Admin privileges required |