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
| Status | Meaning |
|---|---|
in_review | Created and paid for. ByteVerse is reviewing it. Nobody can take the Growth Action yet. |
active | Approved and live. People are completing the Growth Action and ByteVerse is verifying their work. |
paused | You paused it. Budget stays reserved and no new people can start. |
completed | Every target action has been verified. Nothing more will be spent. |
cancelled | You 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.
| Formula | Example (instagram-follow, 70 per action) | |
|---|---|---|
| Target actions | floor(budget / price_per_action) | floor(5000 / 70) = 71 |
| Service fee | round(budget × 5%) | 250 |
| Total charged | budget + service_fee | 5,250 from your wallet |
| Spent so far | verified_actions × price_per_action | 30 × 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
| Action | Allowed when | Money |
|---|---|---|
| Pause | active | Budget stays reserved |
| Resume | paused | No change |
| Cancel | in_review, active, paused | in_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.
GET /v1/campaigns/{id}/submissionslists them newest first. Filter withstatus(pending,approved,rejected) and page withlimitandstarting_after.GET /v1/submissions/{id}/evidencereturns links to the proof screenshots whenevidence_availableistrue.
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
| Limit | Default | Notes |
|---|---|---|
| Budget per campaign | ₦500,000 | Per-account, can be raised on request |
| Campaigns per rolling 24 hours | 50 | Per-account, can be raised on request |
| Minimum budget | ₦1,000 or 20× the price per action, whichever is higher | Shown as min_budget in the catalog |
| Growth Action types per campaign | 1 | Create one campaign per Growth Action type |
