Campaign API

Get Started

The Campaign API handles the full lifecycle of advertising campaigns, including creation, status transitions, budget reservation, and pricing calculations. Campaigns support three pricing models — flat-rate, CPM (cost per mille), and CPC (cost per click) — each with its own budget handling logic.

Managing Campaigns

All campaign operations go through the Campaign_Manager singleton:

use WBAM_Pro\Modules\Campaigns\Campaign_Manager;

$manager = Campaign_Manager::get_instance();

// Create campaign
$campaign_id = $manager->create( array(
    'advertiser_id' => $advertiser_id,
    'name'          => 'Summer Sale',
    'budget'        => 500.00,
    'pricing_model' => 'cpm', // cpm, cpc, flat
    'cpm_rate'      => 2.50,
    'start_date'    => '2024-06-01',
    'end_date'      => '2024-08-31',
    'status'        => 'active',
) );

// Get campaign
$campaign = $manager->get( $campaign_id );

// Pause campaign
$manager->pause( $campaign_id, 'manual' );

// Resume campaign
$manager->resume( $campaign_id );

// Get campaign stats
$stats = $manager->get_stats( $campaign_id );
// Returns: impressions, clicks, ctr, spent, remaining_budget

Pricing Calculator

Use the Pricing_Calculator to estimate and compute campaign costs:

use WBAM_Pro\Modules\Campaigns\Pricing_Calculator;

$calculator = Pricing_Calculator::get_instance();

// Calculate CPM cost
$cost = $calculator->calculate_cpm( $impressions, $cpm_rate );

// Calculate CPC cost
$cost = $calculator->calculate_cpc( $clicks, $cpc_rate );

// Estimate campaign cost
$estimate = $calculator->estimate( array(
    'pricing_model' => 'cpm',
    'rate'          => 2.50,
    'impressions'   => 100000,
    'duration_days' => 30,
) );

Budget Reservation System

CPM and CPC campaigns with a budget greater than zero use a pre-funded reservation system. When a campaign is activated, the full budget amount is reserved (debited) from the advertiser’s wallet. When the campaign completes or is cancelled, any unspent budget is refunded.

How It Works

  • Activation: update_status('active') triggers the reservation. The create() method alone does not reserve funds.
  • Pause: The reservation is held during pause. Resuming a paused campaign does not re-reserve funds.
  • Completion/Cancellation: Unspent budget is automatically refunded to the advertiser’s wallet.
  • Budget changes: Use adjust_campaign_reservation() to handle budget modifications on active campaigns.
  • Flat-rate campaigns: Charged via charge_for_package() — no reservation needed.
  • Unlimited budget (budget = 0): Billed hourly via Billing_Manager.

Budget Calculation

  • CPM: price_per_unit * (impressions_limit / 1000)
  • CPC: price_per_unit * clicks_limit
  • Flat: The package price field is the total cost.

Status Transitions

Campaign status changes are enforced via Campaign::is_valid_transition(). The allowed transitions are:

From Allowed Transitions
draft pending, active, cancelled
pending active, cancelled
active paused, completed, expired, cancelled
paused active, completed, cancelled
completed (terminal state — no transitions)
expired (terminal state — no transitions)
cancelled (terminal state — no transitions)

Return Conventions

  • create() and update() return Campaign|WP_Error.
  • update_status(), delete(), activate(), pause(), complete(), and cancel() return true|WP_Error.
  • Always check results with is_wp_error($result) since WP_Error is truthy in PHP.

Code Example: Budget Alert Integration

add_action( 'wbam_pro_campaign_budget_exhausted', function( $campaign_id, $spent, $budget ) {
    $campaign = wbam_pro_get_campaign( $campaign_id );
    $advertiser = wbam_pro_get_advertiser( $campaign['advertiser_id'] );

    // Send Slack notification
    wp_remote_post( 'https://hooks.slack.com/services/xxx', array(
        'body' => wp_json_encode( array(
            'text' => sprintf(
                'Campaign "%s" for %s has exhausted its budget ($%s)',
                $campaign['name'],
                $advertiser['company_name'],
                number_format( $budget, 2 )
            ),
        ) ),
        'headers' => array( 'Content-Type' => 'application/json' ),
    ) );
}, 10, 3 );
Last updated: March 4, 2026