Campaigns

Save repeatable targeting, bind GTM context, and run asynchronous audience cycles without granting delivery authority.

A Campaign is the project-scoped organizing object for repeatable Audience targeting. It saves an AudienceInput, records cycle history, and can bind the brand, offer, Playbook, message-template revision, and dossier specification used by a caller's external GTM pipeline. Blackpearl does not enroll recipients, schedule sequences, or send messages.

Create and find campaigns#

Operation create_campaign_v1_campaigns_post creates a campaign without starting a run. The request uses the same typed targeting fields as direct Audience creation.

CAMPAIGN=$(curl --fail-with-body --silent --show-error \
  --request POST "$BLACKPEARL_API_BASE_URL/v1/campaigns" \
  --header "Authorization: Bearer $BLACKPEARL_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "key": "anz-revops-2026q3",
    "name": "ANZ RevOps leaders",
    "objective": "Find revenue operations leaders at B2B SaaS companies in Australia and New Zealand.",
    "target_size": 100,
    "model": "default"
  }')

CAMPAIGN_ID=$(printf '%s' "$CAMPAIGN" | jq -er '.id')

Campaign keys are optional and stable within the project. Repeating a create with the same normalized key and targeting input returns the existing campaign; changed targeting input under that key returns campaign_key_exists. Operation list_campaigns_v1_campaigns_get lists non-archived campaigns newest first and accepts limit plus an opaque cursor. Operation get_campaign_v1_campaigns__campaign_id__get reads one campaign.

Bind the GTM graph#

Operation update_campaign_v1_campaigns__campaign_id__patch updates only supplied fields. Use it to change name, key, objective, lifecycle status, scalar metadata, or these optional relations: brand_profile_id, offer_id, playbook_job_id, message_template_revision_id, and company_dossier_specification_id.

curl --fail-with-body --silent --show-error \
  --request PATCH "$BLACKPEARL_API_BASE_URL/v1/campaigns/$CAMPAIGN_ID" \
  --header "Authorization: Bearer $BLACKPEARL_API_KEY" \
  --header "Content-Type: application/json" \
  --data "{
    \"status\": \"active\",
    \"brand_profile_id\": \"$BRAND_ID\",
    \"offer_id\": \"$OFFER_ID\"
  }"

Omit a relation to preserve it, send an empty string to clear it, or send an ID to rebind it. The complete merged relation set is validated. In particular, an offer must belong to the bound brand. Archived or out-of-scope relations fail closed.

Lifecycle transitions are draft to active or archived; active to paused, completed, or archived; paused to active, completed, or archived; and completed to archived. Archived is terminal. These states describe your pipeline, never Blackpearl delivery activity.

Run cycles#

Operation run_campaign_cycle_v1_campaigns__campaign_id__cycles_post starts one asynchronous Audience cycle. Supply Idempotency-Key and retain it until the Job ID is stored. Only one queued or running cycle is allowed per campaign.

JOB=$(curl --fail-with-body --silent --show-error \
  --request POST "$BLACKPEARL_API_BASE_URL/v1/campaigns/$CAMPAIGN_ID/cycles" \
  --header "Authorization: Bearer $BLACKPEARL_API_KEY" \
  --header "Idempotency-Key: cycle-20260717-anz-revops")

Poll the returned Job as described in Jobs and resumable polling. Operation list_campaign_cycles_v1_campaigns__campaign_id__cycles_get returns the most recent non-expired cycle Jobs in descending creation order. Each retained Job remains readable for 30 days.

Use Outcomes to record external results against campaign_id. A Campaign organizes targeting and attribution; it is not proof of consent and grants no outbound authority.