ByteVerseDevelopers
Get API keys

Guides

Sandbox & testing

The sandbox is a complete copy of the API that spends play money and simulates results, so you can build and test everything without launching a real campaign.

What is different

Sandbox (bv_test_)Production (bv_live_)
Wallet5,000,000 NGN of play moneyYour real balance
Campaign idscmp_test_...cmp_...
ApprovalYou trigger it with simulateByteVerse reviews it
ResultsYou trigger them with simulateReal people, verified by ByteVerse
WebhooksSeparate test endpoints, real signed deliveriesSeparate live endpoints
Same rulesValidation, pricing, refunds, idempotency, rate limitsIdentical

Simulating a campaign

Use POST /v1/sandbox/campaigns/{id}/simulate with one of three events:

eventEffectFires
activatein_review to activecampaign.activated
progressAdds actions verified actions (about 10% extra are recorded as rejected)campaign.progress at each 25/50/75/100% crossed
completeVerifies all remaining actionsProgress milestones + campaign.completed
curl -X POST "https://api.byteverseafrica.com/v1/sandbox/campaigns/cmp_test_4b7e91d0a2c85f13/simulate" \
  -H "Authorization: Bearer $BYTEVERSE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "event": "complete"
  }'
const res = await fetch("https://api.byteverseafrica.com/v1/sandbox/campaigns/cmp_test_4b7e91d0a2c85f13/simulate", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.BYTEVERSE_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "event": "complete"
  }),
});
const data = await res.json();
import os, requests

res = requests.post(
    "https://api.byteverseafrica.com/v1/sandbox/campaigns/cmp_test_4b7e91d0a2c85f13/simulate",
    headers={"Authorization": f"Bearer {os.environ['BYTEVERSE_API_KEY']}"},
    json={
        "event": "complete",
    },
    timeout=30,
)
data = res.json()
<?php
$ch = curl_init("https://api.byteverseafrica.com/v1/sandbox/campaigns/cmp_test_4b7e91d0a2c85f13/simulate");
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => "POST",
  CURLOPT_HTTPHEADER => ["Authorization: Bearer " . getenv("BYTEVERSE_API_KEY"), "Content-Type: application/json"],
  CURLOPT_POSTFIELDS => json_encode([
    "event" => "complete",
  ]),
]);
$data = json_decode(curl_exec($ch), true);

Sandbox submissions

Test campaigns have submissions too, so you can build your review screen without a live campaign. As you simulate progress, GET /v1/campaigns/{id}/submissions returns matching sample submissions (approved, rejected and pending) with ids that start sub_test_. They contain no personal data because none exists. In the sandbox the evidence endpoint returns an empty urls list, and submission.* webhooks are not sent.

A good test plan

  1. Create a campaign and confirm the wallet dropped by total_charged.
  2. Create one with a budget below min_budget and confirm you handle budget_too_low.
  3. Retry a creation with the same Idempotency-Key and confirm only one campaign exists.
  4. Drain the wallet and confirm you handle 402 insufficient_funds.
  5. Simulate activate, progress and complete and confirm each webhook arrives and verifies.
  6. Return a 500 from your webhook endpoint once and watch the retry in the deliveries list.
  7. Cancel campaigns in in_review and active and check the refund amounts.
  8. Simulate progress, then list submissions and page through them with starting_after.

The repository ships a tests/smoke.sh script that runs the core flow against a test key.