Bragg Integration Guide
What Bragg Is
Bragg is NFC/Soft-POS infrastructure. You bring your licensed banking rails; Bragg turns any Android phone into a certified payment terminal.
Bragg's job ends at returning the cryptogram and tap event. What you do with it is yours.
Quickstart — Sandbox in 60 Seconds
Use the sandbox at sandbox.bragglabs.com.ng. Pre-seeded credentials are on the portal landing page.
# 1. Get a partner token
curl -s -X POST https://sandbox.bragglabs.com.ng/auth/token \
-H "Content-Type: application/json" \
-d '{"partner_id":"sandboxdemo","secret":"sandboxdemo2026xx"}' | jq .token
# 2. Generate an NFC payment token (5 min expiry)
curl -s "https://sandbox.bragglabs.com.ng/v1/wallets/WALLET_ID/token?amount=5000" \
-H "Authorization: Bearer $TOKEN" | jq .
# 3. Submit a transaction
curl -s -X POST https://sandbox.bragglabs.com.ng/v1/transactions \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"wallet_id":"WALLET_ID","merchant_id":"MERCHANT_ID","amount":5000,"currency":"NGN","pin":"1234"}' | jq .
Authentication
Partner Token
All partner API calls require a JWT. Obtain one via:
POST /auth/token
Content-Type: application/json
{
"partner_id": "your-partner-id",
"secret": "your-partner-secret"
}
// Response
{
"token": "eyJ...",
"partner_id": "your-partner-id",
"expires_in": "24h"
}
Pass on every request: Authorization: Bearer eyJ...
Tokens expire after 24 hours. Refresh by calling /auth/token again.
User Token (Customer App)
For customer-facing operations (balance check, PIN set):
POST /auth/user
Content-Type: application/json
{
"wallet_id": "0665ee59-be51-4855-b9d2-8727deac21df",
"pin": "1234"
}
Wallets
Create a Wallet
POST /v1/wallets
Authorization: Bearer {partner_token}
{
"user_id": "your-internal-user-id",
"first_name": "Amara",
"last_name": "Okafor",
"currency": "NGN"
}
// Response
{
"id": "0665ee59-be51-4855-b9d2-8727deac21df",
"user_id": "your-internal-user-id",
"balance": 0,
"currency": "NGN",
"created_at": "2026-06-06T07:23:36Z",
"version": 1
}
Store the id — it's the wallet identifier used in all subsequent calls.
Set Wallet PIN
The customer sets a 4–6 digit PIN before their first tap:
POST /v1/wallets/{wallet_id}/pin
Authorization: Bearer {partner_token}
{ "pin": "1234" }
Merchants
Register a Merchant
POST /v1/merchants
Authorization: Bearer {partner_token}
{
"business_name": "Kemi's Store",
"owner_name": "Kemi Adeyemi",
"email": "kemi@store.com",
"phone": "08012345678",
"settlement_account": "0123456789",
"bank_code": "058",
"address": "12 Broad Street, Lagos Island",
"state": "Lagos"
}
// Response
{
"id": "6bd82ff7-2d1d-4af0-ac06-87d3c7eb3594",
"status": "PENDING",
"terminal_id": "TID-487568-342150"
}
Merchants start as PENDING. Bragg admin approves them — or request auto-approval for your sandbox testing.
The NFC Tap Flow
The core Bragg flow in four steps:
Generate HCE Token (Customer App)
Called before the customer taps. Token expires in 5 minutes.
Customer Taps Merchant Phone
HCE transmits the token over NFC. BraggTerminal SDK reads it and surfaces token + cryptogram to your app.
Submit Transaction
Your app posts the tap event to POST /v1/transactions.
Receive tap.completed Webhook
Bragg fires the full NFC payload to your webhook. Use it to trigger your own payment processing.
Step 1 — Generate HCE Token
GET /v1/wallets/{wallet_id}/token?amount=5000
Authorization: Bearer {partner_token}
// Response
{
"token": "MDY2NWVlNTkt...",
"cryptogram": "055c2f8a5ed6...",
"expires_at": 1780734862
}
Step 3 — Submit Transaction
POST /v1/transactions
Authorization: Bearer {partner_token}
{
"wallet_id": "0665ee59-be51-4855-b9d2-8727deac21df",
"merchant_id": "6bd82ff7-2d1d-4af0-ac06-87d3c7eb3594",
"amount": 5000,
"currency": "NGN",
"token": "MDY2NWVlNTkt...",
"cryptogram": "055c2f8a5ed6..."
}
// Response
{
"id": "742b9942-f01f-4bed-84e6-faa01ac876d5",
"status": "APPROVED",
"amount": 5000,
"fraud_score": 0.02,
"type": "NFC_TAP"
}
Step 4 — tap.completed Webhook Payload
{
"id": "delivery-uuid",
"event_type": "tap.completed",
"created_at": "2026-06-06T08:29:22Z",
"data": {
"transaction_id": "742b9942-...",
"wallet_token": "MDY2NWVlNTkt...",
"cryptogram": "055c2f8a5ed6...",
"wallet_id": "0665ee59-...",
"merchant_id": "6bd82ff7-...",
"terminal_id": "6bd82ff7-...",
"amount": 5000,
"currency": "NGN",
"status": "APPROVED",
"fraud_score": 0.02
}
}
Webhooks
Register an Endpoint
POST /v1/webhooks
Authorization: Bearer {partner_token}
{
"partner_id": "your-partner-id",
"url": "https://your-app.com/bragg/webhook",
"secret": "your-webhook-secret-min-16-chars",
"events": ["tap.completed", "settlement.batch"]
}
Leave events as [] to receive all event types.
Verify Signatures
Every webhook includes an X-Bragg-Signature header. Always verify it before processing.
# Python
import hmac, hashlib
def verify(secret: str, payload: bytes, signature: str) -> bool:
expected = "sha256=" + hmac.new(
secret.encode(), payload, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)
// Node.js
const crypto = require('crypto');
function verify(secret, payload, signature) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}
Event Types
| Event | When |
|---|---|
tap.completed | Every NFC tap (approved or declined) |
settlement.batch | Daily settlement batch ready for disbursement |
transaction.approved | Transaction approved |
transaction.declined | Transaction declined |
fraud.signal | Cross-partner fraud signal raised |
Settlement
Bragg calculates settlement batches and sends them to you. You disburse to merchants via your own NIBSS/NIP rails.
Daily at 23:00 UTC
Bragg calculates unsettled approved transactions per merchant.
settlement.batch webhook fires
Contains net amounts, settlement account, and bank code for each merchant.
You disburse
Transfer net_amount to settlement_account at bank_code using nibss_reference as narration.
Confirm to Bragg
POST /v1/settlements/{id}/confirm with your NIP session reference.
settlement.batch Webhook
{
"event_type": "settlement.batch",
"settlement_id": "b7c67632-1353-4710-81a1-b778815bde14",
"merchant_id": "6bd82ff7-...",
"gross_amount": 5000,
"platform_fee": 25,
"net_amount": 4975,
"transaction_count": 1,
"currency": "NGN",
"settlement_account": "0123456789",
"bank_code": "058",
"nibss_reference": "BRAGG-20260606-6BD82FF7-747340"
}
Confirm Settlement
POST /v1/settlements/{settlement_id}/confirm
Authorization: Bearer {partner_token}
{ "partner_nip_reference": "your-nip-session-id" }
// Response
{
"id": "b7c67632-...",
"status": "COMPLETED",
"partner_nip_reference": "your-nip-session-id",
"settled_at": "2026-06-06T08:18:40Z"
}
net_amount is already after fee deduction.
Android SDK
Customer App — BraggSDK (HCE)
// build.gradle
implementation 'ng.bragglabs:sdk:1.0.0'
BraggSDK.init(
context = this,
baseUrl = "https://bragglabs.com.ng",
partnerId = "your-partner-id"
)
The SDK handles HCE token generation and NFC transmission automatically.
Merchant App — BraggTerminal (NFC Reader)
BraggTerminal.init(
context = this,
baseUrl = "https://bragglabs.com.ng",
merchantId = "6bd82ff7-2d1d-4af0-ac06-87d3c7eb3594"
)
// Listen for taps
BraggTerminal.onTap { token, cryptogram, amount ->
// Submit to your backend → POST /v1/transactions
}
BraggTerminal also supports Card-on-Phone — it falls back to EMV PPSE when a physical Verve/Visa/Mastercard contactless card taps instead of an HCE phone.
Requirements
| Android | 8.0+ (API 26) |
| Merchant device | NFC hardware required |
| Customer device | HCE support (most Android phones 2018+) |
API Reference
| Method | Endpoint | Description |
|---|---|---|
| POST | /auth/token | Get partner JWT |
| POST | /auth/user | Get user JWT |
| POST | /v1/wallets | Create wallet |
| GET | /v1/wallets/{id} | Get wallet |
| GET | /v1/partner/wallets | List your wallets |
| POST | /v1/wallets/{id}/pin | Set/update PIN |
| GET | /v1/wallets/{id}/token | Generate HCE payment token |
| POST | /v1/transactions | Submit tap transaction |
| GET | /v1/transactions/{id} | Get transaction |
| GET | /v1/transactions?wallet_id= | List transactions |
| POST | /v1/merchants | Register merchant |
| GET | /v1/merchants | List merchants |
| GET | /v1/merchants/{id}/balance | Get settlement balance |
| POST | /v1/webhooks | Register webhook endpoint |
| GET | /v1/settlements | List settlement batches |
| POST | /v1/settlements/{id}/confirm | Confirm disbursement |
| POST | /v1/kyc/verify-account | NUBAN name enquiry |
Error Responses
All errors: { "error": "human readable message" }
| HTTP Code | Meaning |
|---|---|
| 400 | Bad request — check your payload |
| 401 | Invalid or expired token |
| 403 | Forbidden — wrong partner scope |
| 404 | Resource not found |
| 409 | Conflict — duplicate or wrong state |
| 422 | Validation error (KYC, account check) |
| 429 | Rate limit exceeded |
| 500 | Server error — contact support |
Going Live
Get a production partner account
Contact partners@bragglabs.com.ng to provision credentials.
Update base URL
Switch from sandbox.bragglabs.com.ng to bragglabs.com.ng in your SDK init and API calls.
Rebuild Android APKs
Update the baseUrl in both BraggSDK and BraggTerminal init calls and publish to Play Store.
Configure your webhook endpoint
Must be HTTPS and respond within 10 seconds. Bragg retries up to 3 times with exponential backoff.
Wire up NIBSS/NIP rails
Your backend must handle settlement.batch webhooks and call the confirm endpoint after disbursement.