Integration Options
Choose the simplest integration that matches your website. Use the API only when you need a custom donation experience or server-side automation.
client_secret or Bearer token in frontend JavaScript, HTML, mobile apps, or public donation forms. Your backend server should call the Akabbo API. Base URL
https://api.akabbo.comAuthentication
The Developer API uses OAuth2 client credentials. The developer backend exchanges its client_id and client_secret for an access token.
Create OAuth2 Credentials
Create a developer app from the Akabbo Console before requesting an access token. The console generates the OAuth2 client credentials for the profile you want the app to collect donations for.
- Sign in to Akabbo Console and create or select the profile that will receive donations.
- Open Developer from the side menu.
- Create a developer app and give it a clear name that identifies your integration.
- Copy the generated
client_idandclient_secret, then store them in your backend environment or secret manager.
client_secret on your backend only and rotate the app credentials from the console if the secret is exposed. Get Access Token
POST/oauth/token/
curl -X POST https://api.akabbo.com/oauth/token/ \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET"Token Response
{
"access_token": "ACCESS_TOKEN_HERE",
"expires_in": 36000,
"token_type": "Bearer",
"scope": "read write"
}Use the access token in API requests:
Authorization: Bearer ACCESS_TOKEN_HEREAuthenticated Profile
Use this endpoint to verify the profile attached to the authenticated developer OAuth credential before listing campaigns or initiating donations.
GET/v1/profile/
curl https://api.akabbo.com/v1/profile/ \
-H "Authorization: Bearer ACCESS_TOKEN_HERE"Profile Response
{
"id": "9f6d8b2a-7c11-4f44-9d32-2c6b5c1a1111",
"slug": "clean-water-uganda",
"name": "Clean Water Uganda",
"handle": "cleanwateruganda",
"email": "hello@cleanwateruganda.org",
"bio": "We support water, sanitation, and hygiene projects across Uganda.",
"country": "UG",
"currency": "UGX",
"verified": true,
"is_active": true
}Campaign Discovery
Use campaign discovery before showing campaign donation options on your website. These endpoints return the campaigns available to the authenticated profile, including the slug, status, goal, currency, progress, image, and public Akabbo URL.
| Endpoint | When to use it |
|---|---|
| GET/v1/campaign/ | List campaigns owned by the profile connected to the Bearer token. |
| GET/v1/campaign/{campaign_slug}/ | Fetch details for a single campaign before initiating a donation to that campaign. |
List Campaigns
curl https://api.akabbo.com/v1/campaign/ \
-H "Authorization: Bearer ACCESS_TOKEN_HERE"List Campaigns Response
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": "7f1e2a3b-6c4d-4f8a-9b22-21f8d0d2a111",
"slug": "community-water-project",
"title": "Community Water Project",
"description": "Help provide clean water access for the community.",
"currency": "UGX",
"goal_amount": "5000000.00",
"amount_raised": "1250000.00",
"progress": 25.0,
"is_active": true,
"featured_image": "https://api.akabbo.com/media/campaigns/community-water.jpg",
"public_url": "https://akabbo.com/c/community-water-project/"
}
]
}Retrieve Campaign
curl https://api.akabbo.com/v1/campaign/community-water-project/ \
-H "Authorization: Bearer ACCESS_TOKEN_HERE"Retrieve Campaign Response
{
"id": "7f1e2a3b-6c4d-4f8a-9b22-21f8d0d2a111",
"slug": "community-water-project",
"title": "Community Water Project",
"description": "Help provide clean water access for the community.",
"currency": "UGX",
"goal_amount": "5000000.00",
"amount_raised": "1250000.00",
"progress": 25.0,
"is_active": true,
"featured_image": "https://api.akabbo.com/media/campaigns/community-water.jpg",
"public_url": "https://akabbo.com/c/community-water-project/"
}is_active is true. Store the slug and use it in /v1/campaign/{campaign_slug}/donate/. Recommended Donation Flow
The donor should submit the donation form to the developer's backend, not directly to Akabbo.
Donor Browser
-> Developer Backend
-> Akabbo APIDo not do this:
Donor Browser
-> Akabbo API with Bearer tokenDonation Endpoints
Profile and campaign donations use the same request body. Choose the endpoint based on where the donation should go.
| Donation target | Endpoint | When to use it |
|---|---|---|
| Profile | POST/v1/profile/donate/ | Use this when a donor is donating directly to the authenticated profile. |
| Campaign | POST/v1/campaign/{campaign_slug}/donate/ | Use this when a donor is donating to a specific campaign owned by the authenticated profile. |
Mobile Money Donation Request
curl -X POST https://api.akabbo.com/v1/campaign/community-water-project/donate/ \
-H "Authorization: Bearer ACCESS_TOKEN_HERE" \
-H "Content-Type: application/json" \
-d '{
"amount": "10000",
"currency": "UGX",
"anonymous": false,
"donor": "Jane Doe",
"charge_donor": false,
"fsource": "2567XXXXXXX",
"email": "jane@example.com",
"message": "Supporting this campaign",
"mode": "MM",
"share_contact": false
}'Card Donation Request
curl -X POST https://api.akabbo.com/v1/campaign/community-water-project/donate/ \
-H "Authorization: Bearer ACCESS_TOKEN_HERE" \
-H "Content-Type: application/json" \
-d '{
"amount": "10000",
"currency": "UGX",
"anonymous": false,
"donor": "Jane Doe",
"charge_donor": false,
"email": "jane@example.com",
"message": "Supporting this campaign",
"mode": "CARD",
"country": "UG",
"first_name": "Jane",
"last_name": "Doe",
"street": "Plot 10 Kampala Road",
"city": "Kampala",
"state": "Central",
"zip": "00256",
"share_contact": false
}'Donation Response
{
"reference": "1b1c1111-2222-3333-4444-777777777777",
"link": null
} For MM payments, link is null. For CARD payments, link contains the gateway checkout URL the user should open to complete payment.
Response Fields
| Field | Description |
|---|---|
reference | Public donation reference. Store this value and use it to check payment status. |
link | Gateway checkout link for CARD payments. This is null for MM payments. |
Check Profile Donation Status
GET/v1/profile/donate/{reference}/status/
curl https://api.akabbo.com/v1/profile/donate/0a0b1111-2222-3333-4444-555555555555/status/ \
-H "Authorization: Bearer ACCESS_TOKEN_HERE"Check Campaign Donation Status
GET/v1/campaign/{campaign_slug}/donation/{reference}/status/
curl https://api.akabbo.com/v1/campaign/community-water-project/donation/1b1c1111-2222-3333-4444-777777777777/status/ \
-H "Authorization: Bearer ACCESS_TOKEN_HERE"Status Response
The status response is the same for profile and campaign donations. Only the status endpoint changes.
{
"ref": "0a0b1111-2222-3333-4444-555555555555",
"status": "PENDING",
"network_response": null
}Donation Webhooks
Akabbo webhooks notify your backend when a donation moves through payment states. Register a webhook URL in the Akabbo Console, verify every request signature, then process each event once.
1. Create a Webhook Endpoint
Create an HTTPS endpoint on your server that accepts POST requests from Akabbo.
POST https://partner.example.com/webhooks/akabbo2. Register the Webhook
Open the developer options in the Akabbo Console and add the webhook URL. Akabbo returns the webhook secret only when the webhook is created or rotated, so store it immediately in your backend environment or secret manager.
3. Receive Donation Events
When a donation changes, Akabbo sends a signed JSON payload to the registered URL.
| Event | Meaning |
|---|---|
donation.pending | The donation has been created and is waiting for payment confirmation. |
donation.successful | The donation payment completed successfully. |
donation.failed | The donation payment failed. |
Webhook Payload
{
"id": "3f7f8a5d-9d12-4d2c-b4e5-77c0e1111111",
"event": "donation.successful",
"created_at": "2026-07-18T13:20:00+00:00",
"data": {
"id": "2f0d5d3e-6c7a-4f9e-9f31-8a2d7f111111",
"campaign_slug": "community-water-project",
"amount": "50000.00",
"currency": "UGX",
"status": "SUCCESSFUL",
"mode": "MM",
"donor": "Jane Doe",
"anonymous": false,
"email": "jane@example.com",
"message": "Keep going.",
"frequency": "one-time"
}
}Webhook Headers
| Header | Description |
|---|---|
Content-Type | Always application/json. |
X-Akabbo-Event | Event name, for example donation.successful. |
X-Akabbo-Delivery | Delivery attempt identifier. |
X-Akabbo-Timestamp | Unix timestamp used when signing the request. |
X-Akabbo-Signature | HMAC SHA-256 signature prefixed with sha256=. |
Content-Type: application/json
X-Akabbo-Event: donation.successful
X-Akabbo-Delivery: 123
X-Akabbo-Timestamp: 1784380800
X-Akabbo-Signature: sha256=...4. Verify the Signature
Verify the signature before parsing or trusting the event. Build the signed payload from the timestamp, a period, and the raw request body exactly as received.
import hashlib
import hmac
timestamp = request.headers["X-Akabbo-Timestamp"]
received_signature = request.headers["X-Akabbo-Signature"]
raw_body = request.body
signed_payload = timestamp.encode("utf-8") + b"." + raw_body
expected_signature = "sha256=" + hmac.new(
webhook_secret.encode("utf-8"),
signed_payload,
hashlib.sha256,
).hexdigest()
if not hmac.compare_digest(expected_signature, received_signature):
reject_request()5. Return a 2xx Response
Return 200 OK or any other 2xx response after accepting the event. If your server returns 4xx, 5xx, times out, or cannot be reached, Akabbo retries delivery.
6. Handle Duplicate Events
Akabbo may send the same webhook more than once, especially during retries. Store processed event IDs and ignore any event ID that has already been handled.
{
"id": "3f7f8a5d-9d12-4d2c-b4e5-77c0e1111111"
}7. Manage Webhooks
Use the developer options in the Akabbo Console to list webhooks, update the URL, enable or disable delivery, and rotate the secret. After rotating a secret, update your server to use the new value before relying on new deliveries.
Donation Reconciliation
Use donation reconciliation when your backend misses a webhook, restarts during payment processing, or needs to build a back-office donation report. These endpoints return campaign donations attached to the authenticated profile.
| Endpoint | When to use it |
|---|---|
| GET/v1/donations/ | List campaign donations for the authenticated profile. |
| GET/v1/donations/?campaign={campaign_slug} | List donations for one campaign. |
| GET/v1/donations/{donation_pid}/ | Fetch details for a single campaign donation. |
List Donations
curl https://api.akabbo.com/v1/donations/ \
-H "Authorization: Bearer ACCESS_TOKEN_HERE"Filter Donations by Campaign
curl "https://api.akabbo.com/v1/donations/?campaign=community-water-project" \
-H "Authorization: Bearer ACCESS_TOKEN_HERE"List Donations Response
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": "2f0d5d3e-6c7a-4f9e-9f31-8a2d7f111111",
"campaign_slug": "community-water-project",
"amount": "50000.00",
"currency": "UGX",
"status": "SUCCESSFUL",
"mode": "MM",
"donor": "Jane Doe",
"anonymous": false,
"email": "jane@example.com",
"message": "Keep going.",
"frequency": "one-time"
}
]
}Retrieve Donation
curl https://api.akabbo.com/v1/donations/2f0d5d3e-6c7a-4f9e-9f31-8a2d7f111111/ \
-H "Authorization: Bearer ACCESS_TOKEN_HERE"Retrieve Donation Response
{
"id": "2f0d5d3e-6c7a-4f9e-9f31-8a2d7f111111",
"campaign_slug": "community-water-project",
"amount": "50000.00",
"currency": "UGX",
"status": "SUCCESSFUL",
"mode": "MM",
"donor": "Jane Doe",
"anonymous": false,
"email": "jane@example.com",
"message": "Keep going.",
"frequency": "one-time"
}reference from the donation create response. It matches the id used by the reconciliation endpoints. Request Fields
Fields Used by Both MM and CARD
| Field | Required | Description |
|---|---|---|
amount | Yes | Donation amount. Example: "10000". |
currency | Yes | Currency code. Defaults to UGX. |
email | No | Donor email address. |
message | No | Optional donation message. |
mode | Yes | Payment mode. Must be either MM or CARD; it cannot be null. |
anonymous | Yes | Set to true to hide the donor name publicly. Defaults to false. |
Mobile Money Fields
| Field | Required | Description |
|---|---|---|
fsource | Yes | Funding source, usually the donor phone number. |
Card Fields
| Field | Required | Description |
|---|---|---|
country | Yes | Country code. Defaults to UG. |
first_name | Yes | Card payer first name. |
last_name | Yes | Card payer last name. |
street | Yes | Card billing street address. |
city | Yes | Card billing city. |
state | Yes | Card billing state or region. |
zip | Yes | Card billing postal code. Defaults to 256. |
Donation Identity Fields
| Field | Description |
|---|---|
donor | Optional name of the person donating. |
charge_donor | Set to true if the donor should pay payment charges. |
Status Values
Common payment statuses include:
| Status | Meaning |
|---|---|
PENDING | The payment has been initiated and is waiting for confirmation. |
SUCCESSFUL | The payment completed successfully. |
COMPLETED | The payment completed successfully. |
FAILED | The payment failed. |
Error Responses
Invalid or Missing Token
{
"detail": "Authentication credentials were not provided."
}Donation Not Found
{
"detail": "Donation not found"
}Validation Error
{
"amount": [
"A valid number is required."
]
}Payment Limit or Permission Error
{
"error": "This profile has reached the maximum amount allowed for unverified profiles."
}Backend Integration Example
The developer backend should store the access token securely and call the donation endpoint after a donor submits a form.
// Example Node.js/Express-style flow
app.post("/api/donate", async (req, res) => {
const token = await getAkabboAccessToken();
const payload = {
amount: req.body.amount,
currency: req.body.currency || "UGX",
anonymous: Boolean(req.body.anonymous),
donor: req.body.donor || null,
charge_donor: Boolean(req.body.charge_donor),
fsource: req.body.fsource,
email: req.body.email || null,
message: req.body.message || "",
mode: "MM",
share_contact: Boolean(req.body.share_contact)
};
const response = await fetch(
"https://api.akabbo.com/v1/campaign/community-water-project/donate/",
{
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json"
},
body: JSON.stringify(payload)
}
);
const data = await response.json();
res.status(response.status).json(data);
});Security Checklist
- Keep
client_secreton the backend only. - Keep Bearer tokens on the backend only.
- Do not call Akabbo directly from browser JavaScript with a Bearer token.
- Validate donor input and send only approved payload fields to Akabbo.
- Store the returned
referencefor webhook matching, status fallback, and reconciliation. - Store webhook secrets securely and rotate them if they are exposed.
- Verify webhook signatures using the raw request body before processing events.
- Process each webhook event ID once so retries do not duplicate internal records.
Last updated: July 18, 2026
