Overview #

Gbemidebe is a cross-PSP micro-rewards engine. Customers pay a small entry fee at checkout for a chance to win their transaction amount back. As a partner, you connect your platform to Gbemidebe so that every payment can become a reward moment.

There are two ways to integrate:

Option A

API Integration

Your backend calls Gbemidebe APIs directly, server-to-server. No SDK on the client. Full control over the UI and flow.

Server-to-server Custom UI Any language
Option B

SDK Integration

Embed the Gbemidebe JS or mobile SDK in your checkout. The SDK handles the widget, opt-in flow, and API calls automatically.

Auto UI Web / RN / Flutter < 40 KB
i

When to use API integration: you already have a custom checkout UI, operate server-side, or need full control over the opt-in and fee display experience. When to use the SDK: you want the fastest path to production with the opt-in widget, fee display, and contact collection handled for you.

Base URLs

EnvironmentBase URL
Productionhttps://api.gbemidebe.ng
SandboxComing soon

Authentication #

All partner API endpoints require a Bearer API key passed in the Authorization header. Keys are scoped to an environment — sandbox keys only work against the sandbox base URL.

Authorization header http
Authorization: Bearer gbemidebe_test_sk_abc123...

Key formats

EnvironmentKey prefixNotes
Sandboxgbemidebe_test_Issued on request; no live money, deterministic draws.
Productiongbemidebe_live_Issued after merchant KYC is approved.
!

Keep your API key secret. Never expose it in client-side JavaScript, mobile app bundles, or public repositories. For SDK integrations, use the domain-locked public key instead — see the SDK guide.

Request a sandbox key

Email partnerships@gbemidebe.ng with your company name and use-case. Keys are issued within one business day during the private beta.


API Quick Start #

This end-to-end walkthrough shows the three API calls required to record a rewarded transaction, using cURL. Run these in sequence against the sandbox.

1

Init a session

Call POST /api/v1/sdk/init with your purchase context. Gbemidebe returns a session token, the entry fee for this cycle, and raffle metadata.

2

Record the entry

When the customer opts in and your payment is about to process, call POST /api/v1/sdk/entry with the session token and your transaction ID. This reserves a slot in the pot.

3

Confirm the payment

After your payment gateway returns a result, call POST /api/v1/sdk/entry/confirm with success or failed. This transitions the entry to its final state and, on success, adds it to the draw pool.

Step 1 — Init session

Step 1: Init curl
curl -X POST https://api.gbemidebe.ng/api/v1/sdk/init \
  -H "Authorization: Bearer gbemidebe_test_sk_abc123" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "country": "NG",
    "currency": "NGN",
    "purchase_amount": 50000
  }'

Step 2 — Record entry

Step 2: Entry curl
curl -X POST https://api.gbemidebe.ng/api/v1/sdk/entry \
  -H "Authorization: Bearer gbemidebe_test_sk_abc123" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "gbemidebe_session_token": "gbemidebe_sess_abc123...",
    "transaction_id": "TXN-20251201-0001",
    "flow_type": "psp_customer",
    "customer_token": "your-customer-id",
    "customer_ip": "41.58.100.200",
    "customer_user_agent": "Mozilla/5.0 (compatible)"
  }'

Step 3 — Confirm payment

Step 3: Confirm curl
curl -X POST https://api.gbemidebe.ng/api/v1/sdk/entry/confirm \
  -H "Authorization: Bearer gbemidebe_test_sk_abc123" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "transaction_id": "TXN-20251201-0001",
    "payment_status": "success",
    "failure_reason": null
  }'

1. Init Session #

Initializes a Gbemidebe session for a given transaction context. Returns the active entry fee, raffle cycle info, and a signed session token that must be passed to the entry endpoint.

POST https://api.gbemidebe.ng /api/v1/sdk/init
API Key
Base URL
Request Body
POST /api/v1/sdk/init

        

Request body

FieldTypeRequiredDescription
country string required ISO 3166-1 alpha-2 country code (e.g. NG, KE, ZA). Used for cycle resolution.
currency string required ISO 4217 currency code (e.g. NGN, KES, ZAR).
purchase_amount integer required Transaction amount in the smallest currency unit (kobo, cents, pence). E.g. 50000 = ₦500 NGN.

Response body (200)

FieldTypeDescription
statusstringAlways success on a 200.
data.gbemidebe_session_tokenstringSigned session token. Pass this verbatim to the entry endpoint. Expires after 15 minutes.
data.entry_feeintegerEntry fee amount in smallest currency unit. Display this to the customer before opt-in.
data.currencystringEchoes the requested currency.
data.raffle_modestringfirst_to_fill or pot_full_random.
data.compliance_textstringLocalized legal disclaimer text. Display this near the opt-in to satisfy regulatory requirements.
data.cycle_info.codestringHuman-readable cycle identifier (e.g. ng-daily).
data.cycle_info.target_amountintegerPot target amount in smallest currency unit.
data.cycle_info.entries_per_potintegerMaximum number of entries before the draw triggers.
data.expires_atstringISO 8601 timestamp. Session token is invalid after this time.

Code examples

init.sh bash
curl -X POST https://api.gbemidebe.ng/api/v1/sdk/init \
  -H "Authorization: Bearer gbemidebe_test_sk_abc123" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "country": "NG",
    "currency": "NGN",
    "purchase_amount": 50000
  }'

# Example response:
# {
#   "status": "success",
#   "data": {
#     "gbemidebe_session_token": "gbemidebe_sess_abc123...",
#     "entry_fee": 100,
#     "currency": "NGN",
#     "raffle_mode": "pot_full_random",
#     "compliance_text": "By participating...",
#     "cycle_info": { "code": "ng-daily", "target_amount": 1000, "entries_per_pot": 10 },
#     "expires_at": "2026-08-13T11:00:00Z"
#   }
# }
init.php php
use GuzzleHttp\Client;

$client = new Client([
    'base_uri' => 'https://api.gbemidebe.ng',
    'headers'  => [
        'Authorization' => 'Bearer ' . $apiKey,
        'Content-Type'  => 'application/json',
        'Accept'        => 'application/json',
    ],
]);

$response = $client->post('/api/v1/sdk/init', [
    'json' => [
        'country'         => 'NG',
        'currency'        => 'NGN',
        'purchase_amount' => 50000,
    ],
]);

$body = json_decode($response->getBody(), true);
$sessionToken = $body['data']['gbemidebe_session_token'];
$entryFee     = $body['data']['entry_fee'];

// Store $sessionToken in your session / checkout state
// Display $entryFee to the customer with the compliance_text
init.js javascript
const response = await fetch(
  'https://api.gbemidebe.ng/api/v1/sdk/init',
  {
    method:  'POST',
    headers: {
      'Authorization': `Bearer ${apiKey}`,
      'Content-Type':  'application/json',
      'Accept':        'application/json',
    },
    body: JSON.stringify({
      country:          'NG',
      currency:         'NGN',
      purchase_amount:  50000,
    }),
  }
);

const { data } = await response.json();
const { gbemidebe_session_token, entry_fee, compliance_text } = data;

// Persist session token; show entry_fee + compliance_text to user

2. Record Entry #

Records a pending raffle entry for a customer who has opted in. Call this before you charge the customer — the entry stays in pending state until you confirm it. Pending entries auto-expire after 30 minutes.

POST https://api.gbemidebe.ng /api/v1/sdk/entry
API Key
Base URL
Request Body
POST /api/v1/sdk/entry

        
i

The gbemidebe_session_token in the request body must be the exact value returned by Init Session. Sessions expire after 15 minutes; re-call init if the customer takes a long time at checkout.

Flow types

The flow_type field tells Gbemidebe how the customer is identified:

flow_typeWhen to useExtra fields required
psp_customer Customer has an account on your PSP platform. You can identify them by their internal user ID. customer_token (your internal ID)
non_psp_customer Guest checkout. Customer is not a registered user on your platform. contact_phone or contact_email

Request body

FieldTypeRequiredDescription
gbemidebe_session_token string required Session token from the Init Session response.
transaction_id string required Your unique transaction identifier. Acts as an idempotency key — calling twice with the same ID is safe.
flow_type string optional psp_customer (default) or non_psp_customer.
customer_token string optional Your platform's internal customer ID. Required when flow_type is psp_customer.
customer_ip string optional Customer's IP address. Used for fraud and duplicate-entry detection.
customer_user_agent string optional Customer's browser or app user-agent string.
contact_phone string optional E.164 phone number. Required for non_psp_customer if no contact_email.
contact_email string optional Email address. Required for non_psp_customer if no contact_phone.
reference string optional Your own internal reference, stored on the entry for reconciliation.
metadata object optional Arbitrary key-value pairs. Passed through and returned in webhooks.

Response body (201)

FieldTypeDescription
data.entry_idstringGbemidebe entry identifier. Forward this to your payment gateway as metadata so entries can be matched on webhook receipt.
data.statusstringAlways pending on a 201.
data.customer_tokenstringGbemidebe-issued customer identifier. Persist this for returning customers.
data.pending_expires_atstringISO 8601 timestamp after which the pending entry auto-expires.

Code examples

entry.sh bash
curl -X POST https://api.gbemidebe.ng/api/v1/sdk/entry \
  -H "Authorization: Bearer gbemidebe_test_sk_abc123" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "gbemidebe_session_token": "gbemidebe_sess_abc123...",
    "transaction_id": "TXN-20251201-0001",
    "flow_type": "psp_customer",
    "customer_token": "your-internal-customer-id",
    "customer_ip": "41.58.100.200",
    "customer_user_agent": "Mozilla/5.0 (compatible)",
    "reference": "ORD-5521",
    "metadata": { "order_id": "ORD-5521", "product": "electronics" }
  }'
entry.php php
$response = $client->post('/api/v1/sdk/entry', [
    'json' => [
        'gbemidebe_session_token' => $sessionToken,
        'transaction_id'          => 'TXN-20251201-0001',
        'flow_type'               => 'psp_customer',
        'customer_token'          => $customerId,
        'customer_ip'             => $request->ip(),
        'customer_user_agent'     => $request->userAgent(),
        'reference'               => $orderId,
        'metadata'                => ['order_id' => $orderId],
    ],
]);

$entry = json_decode($response->getBody(), true)['data'];
$entryId = $entry['entry_id'];

// Pass $entryId to your payment gateway as metadata
// e.g. Paystack: "gbemidebe_entry_id" => $entryId
entry.js javascript
const res = await fetch(
  'https://api.gbemidebe.ng/api/v1/sdk/entry',
  {
    method:  'POST',
    headers: {
      'Authorization': `Bearer ${apiKey}`,
      'Content-Type':  'application/json',
      'Accept':        'application/json',
    },
    body: JSON.stringify({
      gbemidebe_session_token: sessionToken,
      transaction_id:          'TXN-20251201-0001',
      flow_type:               'psp_customer',
      customer_token:          customerId,
      customer_ip:             req.ip,
      customer_user_agent:     req.headers['user-agent'],
      reference:               orderId,
      metadata:                { order_id: orderId },
    }),
  }
);

const { data } = await res.json();
const { entry_id } = data;

// Forward entry_id to your payment gateway metadata
!

Always forward entry_id to your payment gateway as metadata (e.g. gbemidebe_entry_id). This allows you to match the entry when confirming payment without storing state server-side.


3. Confirm Payment #

Transitions a pending entry to its final state based on whether the underlying payment succeeded or failed. Call this after your payment gateway returns a result — for both success and failure paths.

POST https://api.gbemidebe.ng /api/v1/sdk/entry/confirm
API Key
Base URL
Request Body
POST /api/v1/sdk/entry/confirm

        
!

Always call this endpoint — even when the payment fails. Failing to confirm leaves entries in pending state, polluting the draw pool and skewing analytics. This endpoint is idempotent; calling it twice with the same transaction_id returns the same result.

Request body

FieldTypeRequiredDescription
transaction_id string required The same transaction ID you passed to the entry endpoint. Matches the pending entry.
payment_status string required success or failed.
failure_reason string optional Human-readable failure reason. Only meaningful when payment_status is failed.

Response body (200)

FieldTypeDescription
data.entry_idstringThe Gbemidebe entry identifier.
data.statusstringvalid (payment succeeded, entry joins pot) or failed (excluded from draws).
data.pot_idstringThe pot this entry was added to. Only present when status is valid.
data.entry_numberintegerPosition of this entry in the pot. Only present when status is valid.
data.customer_tokenstringGbemidebe customer token. Persist for returning customers.
data.is_winnerbooleanWhether this entry triggered a draw and won. For Mode 1 (First-to-Fill), this can be true immediately.
data.messagestringHuman-readable summary of the outcome.

Code examples

confirm.sh bash
# On payment success:
curl -X POST https://api.gbemidebe.ng/api/v1/sdk/entry/confirm \
  -H "Authorization: Bearer gbemidebe_test_sk_abc123" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "transaction_id": "TXN-20251201-0001",
    "payment_status": "success",
    "failure_reason": null
  }'

# On payment failure:
curl -X POST https://api.gbemidebe.ng/api/v1/sdk/entry/confirm \
  -H "Authorization: Bearer gbemidebe_test_sk_abc123" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "transaction_id": "TXN-20251201-0001",
    "payment_status": "failed",
    "failure_reason": "Insufficient funds"
  }'
confirm.php php
try {
    // Run your payment gateway charge
    $charge = $gateway->charge([
        'amount'   => $totalAmount,
        'metadata' => ['gbemidebe_entry_id' => $entryId],
    ]);

    // Confirm success
    $client->post('/api/v1/sdk/entry/confirm', [
        'json' => [
            'transaction_id'  => $transactionId,
            'payment_status'  => 'success',
            'failure_reason'  => null,
        ],
    ]);
} catch (\Exception $e) {
    // Confirm failure — always call, even on error
    $client->post('/api/v1/sdk/entry/confirm', [
        'json' => [
            'transaction_id'  => $transactionId,
            'payment_status'  => 'failed',
            'failure_reason'  => $e->getMessage(),
        ],
    ]);
    throw $e;
}
confirm.js javascript
async function confirmEntry(transactionId, status, reason = null) {
  return fetch(
    'https://api.gbemidebe.ng/api/v1/sdk/entry/confirm',
    {
      method:  'POST',
      headers: {
        'Authorization': `Bearer ${apiKey}`,
        'Content-Type':  'application/json',
        'Accept':        'application/json',
      },
      body: JSON.stringify({
        transaction_id:  transactionId,
        payment_status:  status,
        failure_reason:  reason,
      }),
    }
  );
}

try {
  const charge = await processPayment(totalAmount, { gbemidebe_entry_id: entryId });
  await confirmEntry(transactionId, 'success');
} catch (err) {
  await confirmEntry(transactionId, 'failed', err.message);
  throw err;
}

SDK Integration #

If you'd rather not build the opt-in UI from scratch, the Gbemidebe SDK handles the widget, fee display, contact collection, and entry recording automatically. You provide CSS selectors for your checkout elements — the SDK does the rest.

Web

JavaScript SDK

Vanilla JS bundle (< 40 KB gzipped). Load via CDN or install with npm. Auto-integration with your existing checkout selectors.

CDN npm TypeScript
Mobile

React Native & Flutter

Native widgets wrapping the shared TypeScript core. Same API surface as the web SDK — behaviour is identical across platforms.

React Native Flutter Dart

The full SDK reference — including installation, initialization parameters, theming, events, mobile guides, and the go-live checklist — is in the dedicated SDK guide.

Read the SDK Integration Guide

All Endpoints #

Complete route reference for the Gbemidebe partner API.

MethodPathAuthDescription
GET /api/v1/health None Health check. Returns 200 OK with service status. Use for uptime monitoring.
GET /api/v1/cycles Bearer List active raffle cycles for your account. Includes fee, target amount, and current pot fill status.
POST /api/v1/sdk/init Bearer Initialize a session for a transaction. Returns session token, entry fee, and cycle info.
POST /api/v1/sdk/sign Bearer SDK only. Generates HMAC signatures for the SDK. API partners skip this step.
POST /api/v1/sdk/entry Bearer Record a pending raffle entry for a customer who has opted in. Returns entry ID and expiry.
POST /api/v1/sdk/entry/confirm Bearer Confirm or fail a pending entry after payment. Idempotent. Triggers draw if pot fills.
GET /api/v1/draws Bearer List draw results for your cycles, newest first. Includes winner entry ID and payout status.
GET /api/v1/draws/{id}/verify Bearer Verify a draw result. Returns RNG seed components and the winning entry for audit purposes.

Common response envelope

All endpoints return JSON with a consistent envelope:

envelope.json json
{
  "status":  "success",   // "success" | "error"
  "data":    { /* endpoint-specific payload */ },
  "message": "Human-readable summary"
}

// Error shape:
{
  "status":  "error",
  "code":    "VALIDATION_ERROR",
  "message": "The country field is required.",
  "errors":  { "country": ["The country field is required."] }
}

Error Codes #

All API errors return a JSON body with a stable code field you can switch on. HTTP status codes follow standard semantics.

HTTPCodeCauseResolution
400 VALIDATION_ERROR One or more required fields are missing or invalid (e.g. phone format, unknown currency). Read the errors object in the response for field-level messages.
400 INVALID_COUNTRY The country code is not supported or not enabled for your API key. Confirm the country is in your active markets. Contact partnerships to activate new markets.
400 INVALID_CURRENCY The currency does not match the country or is not supported in that market. Use the correct ISO 4217 code for the market (e.g. NGN for NG).
400 INACTIVE_CYCLE No active raffle cycle is configured for the given country and currency combination. Check your cycle configuration in the dashboard. Ensure a Global, Market, or PSP-in-Market cycle is active.
400 DUPLICATE_TRANSACTION A valid or pending entry already exists for this transaction_id. The first call succeeded. Retrieve the existing entry rather than creating a new one. If you need to retry, use a new transaction ID.
401 UNAUTHENTICATED Missing or malformed Authorization header. Include Authorization: Bearer {api_key} in every request.
401 INVALID_API_KEY The API key is not recognised or has been revoked. Verify the key in your dashboard. Check that you are not mixing sandbox and production keys.
410 SESSION_EXPIRED The gbemidebe_session_token is older than 15 minutes. Re-call POST /api/v1/sdk/init to obtain a fresh token, then retry the entry.
410 ENTRY_EXPIRED The pending entry for this transaction ID expired (30-minute window elapsed). Re-init the session and re-create the entry with a new transaction_id.
404 ENTRY_NOT_FOUND No entry found for the provided transaction_id when calling confirm. Confirm you are using the same transaction_id passed to the entry endpoint.
422 POT_FULL The active pot reached its maximum entry count between your init and entry calls. Re-init to get a new session with the next pot. The cycle rolls over automatically.
429 RATE_LIMITED Too many requests in a short window from this API key. Back off exponentially. Default limit: 120 requests per minute per key.
500 INTERNAL_ERROR Unexpected server-side error. Retry with exponential backoff. If the error persists, contact support@gbemidebe.ng with the request ID from the response header.

Retry guidance

Safe to retry (idempotent): DUPLICATE_TRANSACTION, RATE_LIMITED, INTERNAL_ERROR, and POST /api/v1/sdk/entry/confirm for any status.

Not safe to retry without re-init: SESSION_EXPIRED, ENTRY_EXPIRED, POT_FULL.

Request ID header

Every response includes an X-Request-ID header. Include this value when contacting support — it allows us to locate your request in logs immediately.

Ready to go live?

30-minute call. Sandbox keys the same day. Your first rewarded transaction by end of week.