ByteVerseDevelopers
Get API keys

Guides

Campaigns

A campaign asks real people to take one action for your brand, such as following your Instagram page, and you pay only for actions ByteVerse verifies.

Lifecycle

in_review active paused completed cancelled ByteVerse approvesall actions verifiedpauseresumecancel (full refund)cancel (unspent budget refunded)
StatusMeaning
in_reviewCreated and paid for. ByteVerse is reviewing it. Nobody can take the Growth Action yet.
activeApproved and live. People are completing the Growth Action and ByteVerse is verifying their work.
pausedYou paused it. Budget stays reserved and no new people can start.
completedEvery target action has been verified. Nothing more will be spent.
cancelledYou cancelled it and the refund was credited to your wallet.

How pricing works

Every Growth Action type has a price per verified action (see GET /v1/task_types). You choose a whole-naira budget; ByteVerse works out how many actions it buys.

FormulaExample (instagram-follow, 70 per action)
Target actionsfloor(budget / price_per_action)floor(5000 / 70) = 71
Service feeround(budget × 5%)250
Total chargedbudget + service_fee5,250 from your wallet
Spent so farverified_actions × price_per_action30 × 70 = 2,100

Always call POST /v1/quotes first if you want to show a price to your own customers. The quote uses exactly the same maths as creation.

Creating a campaign

curl -X POST "https://api.byteverseafrica.com/v1/campaigns?status=active&limit=10" \
  -H "Authorization: Bearer $BYTEVERSE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "name": "Sallah Weekend Ankara Sale",
    "task_type": "instagram-follow",
    "budget": 5000,
    "destination_url": "https://instagram.com/arewathreads",
    "targeting": {
      "locations": [
        "Lagos",
        "Kano"
      ],
      "gender": "all"
    },
    "external_reference": "order_10482",
    "metadata": {
      "order_id": "10482"
    }
  }'
const res = await fetch("https://api.byteverseafrica.com/v1/campaigns?status=active&limit=10", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.BYTEVERSE_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": crypto.randomUUID(),
  },
  body: JSON.stringify({
    "name": "Sallah Weekend Ankara Sale",
    "task_type": "instagram-follow",
    "budget": 5000,
    "destination_url": "https://instagram.com/arewathreads",
    "targeting": {
      "locations": [
        "Lagos",
        "Kano"
      ],
      "gender": "all"
    },
    "external_reference": "order_10482",
    "metadata": {
      "order_id": "10482"
    }
  }),
});
const data = await res.json();
import os, uuid, requests

res = requests.post(
    "https://api.byteverseafrica.com/v1/campaigns?status=active&limit=10",
    headers={"Authorization": f"Bearer {os.environ['BYTEVERSE_API_KEY']}", "Idempotency-Key": str(uuid.uuid4())},
    json={
        "name": "Sallah Weekend Ankara Sale",
        "task_type": "instagram-follow",
        "budget": 5000,
        "destination_url": "https://instagram.com/arewathreads",
        "targeting": {
            "locations": ["Lagos", "Kano"],
            "gender": "all",
        },
        "external_reference": "order_10482",
        "metadata": {
            "order_id": "10482",
        },
    },
    timeout=30,
)
data = res.json()
<?php
$ch = curl_init("https://api.byteverseafrica.com/v1/campaigns?status=active&limit=10");
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => "POST",
  CURLOPT_HTTPHEADER => ["Authorization: Bearer " . getenv("BYTEVERSE_API_KEY"), "Content-Type: application/json", "Idempotency-Key: " . bin2hex(random_bytes(16))],
  CURLOPT_POSTFIELDS => json_encode([
    "name" => "Sallah Weekend Ankara Sale",
    "task_type" => "instagram-follow",
    "budget" => 5000,
    "destination_url" => "https://instagram.com/arewathreads",
    "targeting" => [
      "locations" => ["Lagos", "Kano"],
      "gender" => "all",
    ],
    "external_reference" => "order_10482",
    "metadata" => [
      "order_id" => "10482",
    ],
  ]),
]);
$data = json_decode(curl_exec($ch), true);

Creation is atomic: validation, the wallet charge and the campaign itself succeed or fail together. If your wallet is short you get 402 insufficient_funds and nothing is created or charged.

Always send an Idempotency-Key. It is required on this endpoint. If the network drops after you send the request, retry with the same key and body and you will get the original campaign back, never a second charge. See Idempotency.

Targeting

By default a campaign reaches people across Nigeria. Narrow it with targeting.locations (state names exactly as listed in the field reference, plus FCT) and targeting.gender.

Review and going live

Each live campaign is reviewed by the ByteVerse team before it runs. When it is approved the status becomes active and a campaign.activated event is sent. The tracking_url on a live campaign opens a public progress page you can share with your own customer.

Pausing, resuming and cancelling

ActionAllowed whenMoney
PauseactiveBudget stays reserved
ResumepausedNo change
Cancelin_review, active, pausedin_review: full refund including the service fee. Running: unspent budget refunded (slots not yet verified or awaiting review). The service fee is kept.

Refunds are credited to your wallet immediately and appear in GET /v1/wallet/transactions. The cancelled campaign's refund_amount shows exactly what came back.

Your own references

Set external_reference to your order or customer id and look the campaign up later with GET /v1/campaigns?external_reference=.... It must be unique per environment, which also protects you from accidentally creating the same campaign twice. metadata stores up to 20 key/value pairs that come back on every read.

Reviewing submissions

Each action a creator submits is a submission. You can list them for a campaign and, if you need to double-check one, fetch its proof. Both endpoints are anonymous: you see the id, action type, status and timing, never a username, email or avatar.

Proof links are sensitive. Fetch them when you need them, do not publish them and do not keep them long term. Proof can be removed after review, so a later request may return 404 not_found.

To be told as it happens instead of polling, subscribe to submission.approved and submission.rejected (live campaigns only). See Webhooks. Keys need the submissions:read and evidence:read scopes.

Limits

LimitDefaultNotes
Budget per campaign₦500,000Per-account, can be raised on request
Campaigns per rolling 24 hours50Per-account, can be raised on request
Minimum budget₦1,000 or 20× the price per action, whichever is higherShown as min_budget in the catalog
Growth Action types per campaign1Create one campaign per Growth Action type