{"openapi":"3.0.3","info":{"title":"Ssentezo Wallet API","version":"unversioned","description":"Merchant API generated from the public routes and validation rules. Provider proxy routes are excluded. This API has no URL version prefix."},"servers":[{"url":"https:\/\/wallet.ssentezo.com","description":"Production host. Sandbox is an organization status, not a different host in this repository."}],"components":{"securitySchemes":{"basicAuth":{"type":"http","scheme":"basic","description":"Username is api_user_id and password is api_key."}}},"paths":{"\/api\/acc_balance":{"post":{"operationId":"account-balance","tags":["Wallet"],"summary":"Read the organization wallet balance.","description":"","security":[{"basicAuth":[]}],"responses":{"200":{"description":"Success envelope from the current controller.","content":{"application\/json":{"example":{"response":"OK","data":{"amount":150000,"formatted":"150,000","currency":"UGX"}}}}},"401":{"description":"The Authorization header is missing."},"403":{"description":"The organization user is suspended."},"400":{"description":"Validation error. The currency message is returned in error.message."}},"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","required":["currency"],"properties":{"currency":{"type":"string","description":"Only UGX is accepted. Rule: required|string|in:UGX"}}},"example":{"currency":"UGX"}}}}}},"\/api\/deposit":{"post":{"operationId":"deposit","tags":["Mobile money collections"],"summary":"Ask a customer to approve a mobile-money collection into the wallet.","description":"The external reference is stored before the remaining fields are validated. A rejected request can still consume that reference.\nThe phone number selects MTN or Airtel. There is no provider field.\nMinimum amount enforced by the API and the collection service is UGX 500. The fee comes from the organization collection policy.\nA successful response means the prompt was created. transactionStatus is pending until the customer approves and reconciliation completes.","security":[{"basicAuth":[]}],"responses":{"200":{"description":"Success envelope from the current controller.","content":{"application\/json":{"example":{"response":"OK","data":{"transactionStatus":"PENDING","ssentezoWalletReference":"generated-uuid","financialTransactionId":""}}}}},"401":{"description":"The Authorization header is missing."},"403":{"description":"The organization user is suspended."},"400":{"description":"Validation failed, or the collection service rejected the request. error.message or error.errors explains it."},"500":{"description":"The collection could not be started."}},"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","required":["externalReference","msisdn","amount","currency"],"properties":{"externalReference":{"type":"string","description":"Your reference for this payment. Rule: required|string|max:255|unique:api_requests,external_id"},"msisdn":{"type":"string","description":"Accepts 0, 256, or +256 followed by a 9-digit number starting with 2, 3, or 7. Rule: Ugandan mobile number"},"amount":{"type":"number","description":"Collection amount in UGX. Rule: required|numeric|min:500"},"currency":{"type":"string","description":"Must be UGX. Rule: required|string|in:UGX"},"reason":{"type":"string","description":"Not validated as required on this endpoint. Keep it short and plain. Rule: sent to the collection service"},"name":{"type":"string","description":"Payer name, when you have it. Rule: nullable|string"},"success_callback":{"type":"string","description":"URL that receives one unsigned POST after success. The legacy field callback is used when success_callback is empty. Rule: nullable|url|max:255"},"failure_callback":{"type":"string","description":"URL that receives one unsigned POST after failure. Rule: nullable|url|max:255"}}},"example":{"externalReference":"order-1001","msisdn":"256772123456","amount":5000,"currency":"UGX","reason":"Order 1001","name":"Ada Lovelace","success_callback":"https:\/\/example.com\/payments\/ssentezo","failure_callback":"https:\/\/example.com\/payments\/ssentezo"}}}}}},"\/api\/withdraw":{"post":{"operationId":"withdraw","tags":["Mobile money payouts"],"summary":"Send mobile money from the wallet to a customer.","description":"Minimum amount is UGX 500.\nThe fee comes from the organization disbursement policy.\nHTTP 202 means the payout was accepted, not that the recipient has been paid. Confirm with Check status.","security":[{"basicAuth":[]}],"responses":{"202":{"description":"Success envelope from the current controller.","content":{"application\/json":{"example":{"response":"OK","data":{"transactionStatus":"PENDING","ssentezoWalletReference":"generated-uuid","financialTransactionId":""}}}}},"401":{"description":"The Authorization header is missing."},"403":{"description":"The organization user is suspended."},"400":{"description":"Validation failed or the disbursement service returned an error."},"500":{"description":"Unexpected disbursement error."}},"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","required":["externalReference","msisdn","amount","currency"],"properties":{"externalReference":{"type":"string","description":"Your payout reference. Rule: required|string|max:255|unique"},"msisdn":{"type":"string","description":"Same phone format as collection. Rule: Ugandan mobile number"},"amount":{"type":"number","description":"Amount in UGX. Rule: required|numeric|min:500"},"currency":{"type":"string","description":"Must be UGX. Rule: required|in:UGX"},"reason":{"type":"string","description":"Shown on the transaction. Rule: nullable|string|min:2|max:150"},"name":{"type":"string","description":"Recipient name. Rule: nullable|string"},"success_callback":{"type":"string","description":"Unsigned success notification URL. Rule: nullable|url|max:255"},"failure_callback":{"type":"string","description":"Unsigned failure notification URL. Rule: nullable|url|max:255"}}},"example":{"externalReference":"payout-1001","msisdn":"256772123456","amount":5000,"currency":"UGX","reason":"Supplier payment"}}}}}},"\/api\/card\/collect":{"post":{"operationId":"card-collect","tags":["Visa and Mastercard"],"summary":"Start a hosted Visa or Mastercard checkout.","description":"Redirect the cardholder to data.checkoutUrl. Do not embed card numbers in your own page.\nThe wallet is credited only after PegPay confirms the payment.\nThe fee is the organization card-collection policy. The published schedule is 5 percent. Some organizations have a different rate.\nAPI validation allows UGX 500 and above. The hosted checkout and card policy band used for new merchants is UGX 500 to 7,000,000.\nredirect_url is the browser return after a successful card payment. success_callback and failure_callback are server POSTs.","security":[{"basicAuth":[]}],"responses":{"200":{"description":"Success envelope from the current controller.","content":{"application\/json":{"example":{"response":"OK","data":{"transactionStatus":"PENDING","ssentezoWalletReference":"generated-uuid","externalReference":"card-1001","checkoutUrl":"https:\/\/wallet.ssentezo.com\/pegpay\/card\/checkout\/generated-uuid"}}}}},"401":{"description":"The Authorization header is missing."},"403":{"description":"The organization user is suspended."},"422":{"description":"Validation failed. error.errors contains the field messages."},"400":{"description":"Card checkout could not be created."},"500":{"description":"Unexpected card-collection error."}},"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","required":["externalReference","amount","currency","reason","name","email"],"properties":{"externalReference":{"type":"string","description":"Your order reference. Rule: required|unique|max:255"},"amount":{"type":"number","description":"Amount in UGX. Rule: required|numeric|min:500"},"currency":{"type":"string","description":"Must be UGX. Rule: required|in:UGX"},"reason":{"type":"string","description":"Shown as the payment description. Rule: required|min:2|max:150"},"name":{"type":"string","description":"Cardholder name. Rule: required|min:2|max:100"},"email":{"type":"string","description":"Cardholder email. Rule: required|email|max:255"},"success_callback":{"type":"string","description":"Server notification after success. Rule: nullable|url|max:255"},"failure_callback":{"type":"string","description":"Server notification after failure. Rule: nullable|url|max:255"},"redirect_url":{"type":"string","description":"Browser return URL after a successful checkout. Rule: nullable|url|max:255"}}},"example":{"externalReference":"card-1001","amount":10000,"currency":"UGX","reason":"Order 1001","name":"Ada Lovelace","email":"ada@example.com","redirect_url":"https:\/\/example.com\/orders\/1001"}}}}}},"\/api\/get_status\/{externalReference}":{"post":{"operationId":"transaction-status","tags":["Transaction status"],"summary":"Read the status of a collection, payout, or card payment by your reference.","description":"SUCCEEDED is internal status 3 (completed). FAILED is internal status 4. Every other internal status, including pending, reconcile, and reversed, is returned as PENDING.\nThe lookup is limited to the authenticated organization. Another merchant reference returns the same not-found response.\nConfirm payment with this endpoint. A callback or browser redirect is not proof of payment. Compare externalId, amount, and currency with the order before fulfilling it.","security":[{"basicAuth":[]}],"responses":{"200":{"description":"Success envelope from the current controller.","content":{"application\/json":{"example":{"response":"OK","data":{"transactionStatus":"PENDING","ssentezoWalletReference":"generated-uuid","externalId":"order-1001","financialTransactionId":null,"amount":5000,"reason":"Order 1001","currency":"UGX","msisdn":"256772123456","transactionTime":"2026-10-20T12:00:00.000000Z"}}}}},"401":{"description":"The Authorization header is missing."},"403":{"description":"The organization user is suspended."},"404":{"description":"The external reference or wallet transaction was not found."}},"parameters":[{"name":"externalReference","in":"path","required":true,"schema":{"type":"string"},"description":"The reference you sent when creating the transaction."}]}},"\/api\/transaction-history":{"post":{"operationId":"transaction-history","tags":["Transaction history"],"summary":"List wallet transactions for one date or a date range.","description":"Provide date, or both startDate and endDate. Dates use Y-m-d.\nSUCCEEDED, FAILED, and PENDING use the same mapping as Check status.\nprimaryOnly true hides charge rows.","security":[{"basicAuth":[]}],"responses":{"200":{"description":"Success envelope from the current controller.","content":{"application\/json":{"example":{"response":"OK","data":{"transactions":[{"ssentezoWalletReference":"generated-uuid","externalReference":"order-1001","transactionStatus":"SUCCEEDED","transactionType":"COLLECTION","isCharge":false,"amount":5000,"charges":200,"reason":"Order 1001","currency":"UGX","msisdn":"256772123456","financialTransactionId":"provider-reference","transactionTime":"2026-10-20T12:00:00.000000Z"}],"pagination":{"currentPage":1,"perPage":20,"total":1,"lastPage":1}}}}}},"401":{"description":"The Authorization header is missing."},"403":{"description":"The organization user is suspended."},"400":{"description":"Validation failed, including a missing date range."}},"requestBody":{"required":false,"content":{"application\/json":{"schema":{"type":"object","required":[],"properties":{"date":{"type":"string","description":"Single day. Required unless startDate and endDate are both set. Rule: nullable|date_format:Y-m-d"},"startDate":{"type":"string","description":"Range start. Rule: nullable|date_format:Y-m-d"},"endDate":{"type":"string","description":"Range end. Rule: nullable|date_format:Y-m-d|after_or_equal:startDate"},"transactionStatus":{"type":"string","description":"Filter. Rule: nullable|in:SUCCEEDED,FAILED,PENDING"},"transactionType":{"type":"string","description":"Filter. Rule: nullable|in:COLLECTION,DISBURSEMENT"},"primaryOnly":{"type":"boolean","description":"Hide charge rows when true. Rule: true or false"},"search":{"type":"string","description":"Matches reference, amount, reason, or phone. Rule: nullable|max:255"},"page":{"type":"integer","description":"Page number. Rule: nullable|integer|min:1"},"perPage":{"type":"integer","description":"Defaults to 50. Rule: nullable|integer|min:1|max:200"}}},"example":{"date":"2026-10-20","primaryOnly":true,"perPage":20}}}}}},"\/api\/msisdn-verification":{"post":{"operationId":"msisdn-verification","tags":["Recipient lookup"],"summary":"Look up the registered name for a Ugandan mobile number.","description":"","security":[{"basicAuth":[]}],"responses":{"200":{"description":"Success envelope from the current controller.","content":{"application\/json":{"example":{"response":"OK","data":{"msisdn":"256772123456","FirstName":"ADA","Surname":"LOVELACE"}}}}},"401":{"description":"The Authorization header is missing."},"403":{"description":"The organization user is suspended."},"400":{"description":"Invalid number or the provider lookup failed."}},"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","required":["msisdn"],"properties":{"msisdn":{"type":"string","description":"Number to look up. Rule: Ugandan mobile number"}}},"example":{"msisdn":"256772123456"}}}}}},"\/api\/verify_msisdn":{"post":{"operationId":"verify-msisdn-v1","tags":["Recipient lookup"],"summary":"Older phone-name lookup. Prefer \/api\/msisdn-verification.","description":"The field name is phoneNumber, not msisdn. The response is the provider payload rather than the response\/data envelope.","security":[{"basicAuth":[]}],"responses":{"200":{"description":"Success envelope from the current controller.","content":{"application\/json":{"example":{"Status":"OK","FirstName":"ADA","Surname":"LOVELACE"}}}},"401":{"description":"Validation failed. This legacy route uses HTTP 401 for invalid input."},"403":{"description":"The organization user is suspended."}},"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","required":["phoneNumber"],"properties":{"phoneNumber":{"type":"string","description":"Number to look up. Rule: Ugandan mobile number"}}},"example":{"phoneNumber":"256772123456"}}}}}},"\/api\/push-to-bank\/get-banks":{"post":{"operationId":"get-banks","tags":["Bank transfers"],"summary":"List banks you can pay.","description":"","security":[{"basicAuth":[]}],"responses":{"200":{"description":"Success envelope from the current controller.","content":{"application\/json":{"example":{"response":"OK","data":{"banks":[{"id":1,"bank_name":"Example Bank","address":"Kampala","swift_code":"EXAMPLE"}]}}}}},"401":{"description":"The Authorization header is missing."},"403":{"description":"The organization user is suspended."}}}},"\/api\/push-to-bank\/request-bank-transfer":{"post":{"operationId":"bank-transfer","tags":["Bank transfers"],"summary":"Request a transfer from the wallet to a bank account.","description":"Amount must be from UGX 2,500 to UGX 50,000,000.\nThe fixed fee follows the organization push-to-bank policy. The published bands are 5,000; 6,000; 9,000; 13,500; and 16,500 UGX.\nThe field name is external_reference, with an underscore. This request does not store success or failure callback URLs.\nPENDING means the request was accepted. Confirm with the bank status endpoint.","security":[{"basicAuth":[]}],"responses":{"200":{"description":"Success envelope from the current controller.","content":{"application\/json":{"example":{"response":"OK","data":{"transactionStatus":"PENDING","ssentezoWalletReference":"generated-uuid","externalReference":"bank-1001"}}}}},"401":{"description":"The Authorization header is missing."},"403":{"description":"The organization user is suspended."},"422":{"description":"Validation failed."},"400":{"description":"The transfer was rejected or could not be created."}},"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","required":["external_reference","bank_id","account_name","account_number","amount"],"properties":{"external_reference":{"type":"string","description":"Your transfer reference. Rule: required|unique:api_requests,external_id"},"bank_id":{"type":"integer","description":"Bank id from Get banks. Rule: required|integer|exists:banks,id"},"account_name":{"type":"string","description":"Account name. Rule: required|min:2|max:255"},"account_number":{"type":"string","description":"Digits only. Rule: required|numeric"},"amount":{"type":"number","description":"Amount in UGX. Rule: required|numeric|min:2500|max:50000000"},"reason":{"type":"string","description":"Transfer narration. Rule: nullable|min:2|max:255"}}},"example":{"external_reference":"bank-1001","bank_id":1,"account_name":"Ada Lovelace","account_number":"1234567890","amount":250000,"reason":"Supplier settlement"}}}}}},"\/api\/push-to-bank\/check-bank-transfer-status":{"post":{"operationId":"bank-transfer-status","tags":["Bank transfers"],"summary":"Read a bank transfer that belongs to the authenticated organization.","description":"Unlike the general status route, this lookup is limited to the authenticated organization.","security":[{"basicAuth":[]}],"responses":{"200":{"description":"Success envelope from the current controller.","content":{"application\/json":{"example":{"response":"OK","data":{"transactionStatus":"PENDING","ssentezoWalletReference":"generated-uuid","externalReference":"bank-1001","amount":250000,"transactionTime":"2026-10-20T12:00:00.000000Z"}}}}},"401":{"description":"The Authorization header is missing."},"403":{"description":"The organization user is suspended."},"422":{"description":"Validation failed."},"404":{"description":"The reference is missing or is not a transaction on this organization."}},"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","required":["external_reference"],"properties":{"external_reference":{"type":"string","description":"Reference used on the transfer request. Rule: required|exists:api_requests,external_id"}}},"example":{"external_reference":"bank-1001"}}}}}},"\/api\/ssentezo\/register":{"post":{"operationId":"partner-register","tags":["Partner account creation"],"summary":"Create a sandbox organization. Used by the Ssentezo MFI integration.","description":"Any existing API credential can call this route. The response includes a new API user and key. Store that secret once and do not log it.\nThe new organization starts in SANDBOX.\nThis is not the merchant self-signup page. People creating their own account use the website registration form.","security":[{"basicAuth":[]}],"responses":{"201":{"description":"Success envelope from the current controller.","content":{"application\/json":{"example":{"status":"SUCCESS","response":"OK","data":{"USERNAME":"new-api-user-id","API_KEY":"returned-once","message":"Account was created successfully. Save the key; it is the API password."}}}}},"401":{"description":"The Authorization header is missing."},"403":{"description":"The organization user is suspended."},"422":{"description":"Validation failed. The account was not created."},"400":{"description":"The account could not be created."}},"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","required":["company_name","email","password","phone","first_name","last_name","address"],"properties":{"company_name":{"type":"string","description":"Organization name. Rule: required|unique:organizations,name|max:255"},"email":{"type":"string","description":"Owner email. Rule: required|email|unique:users,email"},"password":{"type":"string","description":"Owner password. Send it only over HTTPS and do not log it. Rule: required|string"},"phone":{"type":"string","description":"Owner phone. Rule: Ugandan number, unique"},"first_name":{"type":"string","description":"Owner first name. Rule: required|string"},"last_name":{"type":"string","description":"Owner last name. Rule: required|string"},"address":{"type":"string","description":"Organization address. Rule: required|string"}}},"example":{"company_name":"Example Supplies Ltd","email":"owner@example.com","password":"use-a-secret-from-your-environment","phone":"256772123456","first_name":"Ada","last_name":"Lovelace","address":"Kampala"}}}}}}}}