Developer documentation

Build a backend-to-backend donation flow with the Developer API. Add Akabbo donations to WordPress, embed campaign forms on your website.

Updated July 18, 2026

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.

extensionWordPress plugin Install the Akabbo Donations plugin from WordPress.org to add donation forms to a WordPress site. open_in_new
code_blocksCampaign embed Open a campaign in Akabbo, use the embed option on the campaign view, copy the iframe code, then paste it into an HTML page, CMS custom HTML block, or website builder embed block.
apiDeveloper API Build a custom backend donation flow using OAuth2 client credentials and scoped Bearer tokens.
Important: Never expose your 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.com

Authentication

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.

  1. Sign in to Akabbo Console and create or select the profile that will receive donations.
  2. Open Developer from the side menu.
  3. Create a developer app and give it a clear name that identifies your integration.
  4. Copy the generated client_id and client_secret, then store them in your backend environment or secret manager.
The generated OAuth2 credentials are tied to the selected profile. Keep the 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_HERE
The Bearer token identifies the developer app and the profile connected to that app. The API automatically scopes requests to that profile.

Authenticated 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.

EndpointWhen 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/"
}
Only call the campaign donation endpoint when 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 API

Do not do this:

Donor Browser
  -> Akabbo API with Bearer token

Donation Endpoints

Profile and campaign donations use the same request body. Choose the endpoint based on where the donation should go.

Donation targetEndpointWhen to use it
ProfilePOST/v1/profile/donate/Use this when a donor is donating directly to the authenticated profile.
CampaignPOST/v1/campaign/{campaign_slug}/donate/Use this when a donor is donating to a specific campaign owned by the authenticated profile.
The payload is the same for profile and campaign donations. Only the endpoint changes.

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

FieldDescription
referencePublic 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
}
Use webhooks as the primary way to receive final donation updates. Status checks are useful as a fallback while migrating, investigating a single donation, or recovering from missed webhook delivery.

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/akabbo

2. 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.

EventMeaning
donation.pendingThe donation has been created and is waiting for payment confirmation.
donation.successfulThe donation payment completed successfully.
donation.failedThe 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

HeaderDescription
Content-TypeAlways application/json.
X-Akabbo-EventEvent name, for example donation.successful.
X-Akabbo-DeliveryDelivery attempt identifier.
X-Akabbo-TimestampUnix timestamp used when signing the request.
X-Akabbo-SignatureHMAC 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()
Important: Verify the signature with the raw request body exactly as received. Do not reformat, decode, or re-serialize the JSON before computing the HMAC.

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.

EndpointWhen 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"
}
Store the returned donation reference from the donation create response. It matches the id used by the reconciliation endpoints.

Request Fields

Fields Used by Both MM and CARD

FieldRequiredDescription
amountYesDonation amount. Example: "10000".
currencyYesCurrency code. Defaults to UGX.
emailNoDonor email address.
messageNoOptional donation message.
modeYesPayment mode. Must be either MM or CARD; it cannot be null.
anonymousYesSet to true to hide the donor name publicly. Defaults to false.

Mobile Money Fields

FieldRequiredDescription
fsourceYesFunding source, usually the donor phone number.

Card Fields

FieldRequiredDescription
countryYesCountry code. Defaults to UG.
first_nameYesCard payer first name.
last_nameYesCard payer last name.
streetYesCard billing street address.
cityYesCard billing city.
stateYesCard billing state or region.
zipYesCard billing postal code. Defaults to 256.

Donation Identity Fields

FieldDescription
donorOptional name of the person donating.
charge_donorSet to true if the donor should pay payment charges.

Status Values

Common payment statuses include:

StatusMeaning
PENDINGThe payment has been initiated and is waiting for confirmation.
SUCCESSFULThe payment completed successfully.
COMPLETEDThe payment completed successfully.
FAILEDThe 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."
}
Upload KYC documents to verify the profile via the dashboard & unlock higher collection limits. This affects profile donations and campaigns under that profile.

Backend Integration Example

The developer backend should store the access token securely and call the donation endpoint after a donor submits a form.

Validate donor input on your backend and build an allowlisted payload before calling Akabbo. Do not forward the raw browser request body directly.
// 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_secret on 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 reference for 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