API Reference.
Server-to-server REST API for partners integrating Gbemidebe without the SDK. Authenticate with your API key, call three endpoints, and you're live.
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:
API Integration
Your backend calls Gbemidebe APIs directly, server-to-server. No SDK on the client. Full control over the UI and flow.
SDK Integration
Embed the Gbemidebe JS or mobile SDK in your checkout. The SDK handles the widget, opt-in flow, and API calls automatically.
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
| Environment | Base URL |
|---|---|
| Sandbox | https://sandbox-api.gbemidebe.com |
| Production | https://api.gbemidebe.com |
| Local dev | http://gbemidebe.test |
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: Bearer gbemidebe_test_sk_abc123...
Key formats
| Environment | Key prefix | Notes |
|---|---|---|
| Sandbox | gbemidebe_test_ | Issued on request; no live money, deterministic draws. |
| Production | gbemidebe_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.
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.
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.
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
curl -X POST https://sandbox-api.gbemidebe.com/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
curl -X POST https://sandbox-api.gbemidebe.com/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
curl -X POST https://sandbox-api.gbemidebe.com/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.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
| 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)
| Field | Type | Description |
|---|---|---|
| status | string | Always success on a 200. |
| data.gbemidebe_session_token | string | Signed session token. Pass this verbatim to the entry endpoint. Expires after 15 minutes. |
| data.entry_fee | integer | Entry fee amount in smallest currency unit. Display this to the customer before opt-in. |
| data.currency | string | Echoes the requested currency. |
| data.raffle_mode | string | first_to_fill or pot_full_random. |
| data.compliance_text | string | Localized legal disclaimer text. Display this near the opt-in to satisfy regulatory requirements. |
| data.cycle_info.code | string | Human-readable cycle identifier (e.g. ng-daily). |
| data.cycle_info.target_amount | integer | Pot target amount in smallest currency unit. |
| data.cycle_info.entries_per_pot | integer | Maximum number of entries before the draw triggers. |
| data.expires_at | string | ISO 8601 timestamp. Session token is invalid after this time. |
Code examples
curl -X POST https://sandbox-api.gbemidebe.com/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" # } # }
use GuzzleHttp\Client; $client = new Client([ 'base_uri' => 'https://sandbox-api.gbemidebe.com', '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
const response = await fetch( 'https://sandbox-api.gbemidebe.com/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.
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_type | When to use | Extra 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
| Field | Type | Required | Description |
|---|---|---|---|
| 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)
| Field | Type | Description |
|---|---|---|
| data.entry_id | string | Gbemidebe entry identifier. Forward this to your payment gateway as metadata so entries can be matched on webhook receipt. |
| data.status | string | Always pending on a 201. |
| data.customer_token | string | Gbemidebe-issued customer identifier. Persist this for returning customers. |
| data.pending_expires_at | string | ISO 8601 timestamp after which the pending entry auto-expires. |
Code examples
curl -X POST https://sandbox-api.gbemidebe.com/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" } }'
$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
const res = await fetch( 'https://sandbox-api.gbemidebe.com/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.
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
| Field | Type | Required | Description |
|---|---|---|---|
| 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)
| Field | Type | Description |
|---|---|---|
| data.entry_id | string | The Gbemidebe entry identifier. |
| data.status | string | valid (payment succeeded, entry joins pot) or failed (excluded from draws). |
| data.pot_id | string | The pot this entry was added to. Only present when status is valid. |
| data.entry_number | integer | Position of this entry in the pot. Only present when status is valid. |
| data.customer_token | string | Gbemidebe customer token. Persist for returning customers. |
| data.is_winner | boolean | Whether this entry triggered a draw and won. For Mode 1 (First-to-Fill), this can be true immediately. |
| data.message | string | Human-readable summary of the outcome. |
Code examples
# On payment success: curl -X POST https://sandbox-api.gbemidebe.com/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://sandbox-api.gbemidebe.com/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" }'
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; }
async function confirmEntry(transactionId, status, reason = null) { return fetch( 'https://sandbox-api.gbemidebe.com/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.
JavaScript SDK
Vanilla JS bundle (< 40 KB gzipped). Load via CDN or install with npm. Auto-integration with your existing checkout selectors.
React Native & Flutter
Native widgets wrapping the shared TypeScript core. Same API surface as the web SDK — behaviour is identical across platforms.
The full SDK reference — including installation, initialization parameters, theming, events, mobile guides, and the go-live checklist — is in the dedicated SDK guide.
All Endpoints #
Complete route reference for the Gbemidebe partner API.
| Method | Path | Auth | Description |
|---|---|---|---|
| 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:
{
"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.
| HTTP | Code | Cause | Resolution |
|---|---|---|---|
| 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.