# Billwebb Technologies — merchant collection API Base URL: https://billwebb.admin.yakani.org API version: v1 Currency: UGX (integer amounts) This is the live API. Creating a collection sends a real mobile-money approval prompt. Do not use real customer details for experiments. ## Access and authentication 1. Register or sign in at https://billwebb.admin.yakani.org/signup. 2. In Dashboard → Collections & wallet, create an API key. Copy it when shown; it is displayed once. 3. Send the key in the x-api-key header from your server only. Never place it in browser JavaScript, mobile application code, public repositories, or an LLM prompt. Use a server-side environment secret. 4. An API key is scoped to the merchant account that created it. Key creation and withdrawal requests require the merchant's signed-in session; an API key cannot perform those actions. ## Create a collection POST /api/v1/transactions Headers: Content-Type: application/json; x-api-key: YOUR_SERVER_SIDE_API_KEY; Idempotency-Key: a unique 12–100 character value containing letters, numbers or hyphens. Body: {"amount":50000,"phone":"0771234567","payerName":"Customer"} amount is the UGX amount to credit to the merchant wallet after payment is confirmed, not the amount charged to the payer. Minimum amount is UGX 500. The calculated collection total may not exceed UGX 10,000,000. Uganda phone numbers may be written as 0771234567, 771234567 or 256771234567. payerName is optional. The server adds a 1% app charge, rounded to a whole UGX, plus a withdrawal reserve: UGX 600 for amounts up to 60,000; UGX 1,200 for 60,001–500,000; UGX 2,000 for 500,001–1,000,000; UGX 2,400 above 1,000,000. ioTec may charge its 4% customer surcharge separately. Use the returned totalUgx and provider terms for what the payer is charged; do not assume the merchant amount is the payer total. Example response (HTTP 201): {"payment":{"reference":"BW-...","amountUgx":50000,"baseUgx":50000,"appFeeUgx":500,"withdrawalReserveUgx":600,"totalUgx":51100,"providerSurchargePercent":4,"phone":"256771234567","status":"pending","providerStatus":"..."}} Store the reference. A pending response or a sent prompt is not proof of payment. Reusing the same Idempotency-Key with the same amount and phone returns the existing payment with duplicate:true; reusing it with different details returns HTTP 409. Generate a new key only for a genuinely new customer payment. A timeout or HTTP 502 with an outcome-unknown reference must be checked before another attempt. ## Verify a payment GET /api/v1/transactions?reference=BW-... Header: x-api-key: YOUR_SERVER_SIDE_API_KEY Response: {"payment":{"reference":"BW-...","amountUgx":50000,"status":"pending|success|failed","providerStatus":"...","totalUgx":51100,"createdAt":"..."}} Only status=success means the payment is confirmed and the merchant wallet can be credited. If verification is temporarily unavailable, the API may return HTTP 503 with verificationAvailable:false and the last known payment state. Do not mark it paid then. Poll with a sensible delay and stop on success or failed; do not create another collection merely because verification is delayed. GET /api/v1/transactions with x-api-key lists the 20 most recent payments. ## Read the merchant wallet GET /api/v1/merchant-wallet Header: x-api-key: YOUR_SERVER_SIDE_API_KEY Response includes wallet.currency="UGX", wallet.collectedUgx, wallet.balanceUgx, wallet.withdrawalHeldUgx, recent payments and recent withdrawals. Only confirmed successful collections contribute to the balance. Requested withdrawals hold funds. ## Withdrawals In the signed-in dashboard, complete identity, phone and business verification. Withdrawals to MTN/Airtel or an approved bank account are requests for manual review; a request does not send money automatically. The recipient gets the requested withdrawal amount minus the displayed withdrawal fee. Do not tell customers that a payout has happened until Billwebb marks it paid. The server-side API key is for collections, payment lookup and wallet reading; it is not a payout credential. ## Error handling 401: missing or invalid key. 400: invalid amount, phone or Idempotency-Key. 409: conflicting key. 429: too many new collection requests (maximum five per minute per merchant). 502: collection outcome unknown; check the reference. 503: provider or verification temporarily unavailable. Error bodies may contain an error object with code/message, or a simple error string. Never expose a private API key in logs or error messages.