Boost credit limit
curl --request POST \
--url https://api-staging.bsa.ai/v1/credit-boost \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"FullName": "<string>",
"MobileNumber": "<string>"
}
'import requests
url = "https://api-staging.bsa.ai/v1/credit-boost"
payload = {
"FullName": "<string>",
"MobileNumber": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({FullName: '<string>', MobileNumber: '<string>'})
};
fetch('https://api-staging.bsa.ai/v1/credit-boost', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api-staging.bsa.ai/v1/credit-boost",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'FullName' => '<string>',
'MobileNumber' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api-staging.bsa.ai/v1/credit-boost"
payload := strings.NewReader("{\n \"FullName\": \"<string>\",\n \"MobileNumber\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api-staging.bsa.ai/v1/credit-boost")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"FullName\": \"<string>\",\n \"MobileNumber\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api-staging.bsa.ai/v1/credit-boost")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"FullName\": \"<string>\",\n \"MobileNumber\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"mobileNumber": 123,
"finalCreditScore": 123,
"originalCreditLimit": 123,
"mankaCreditLimit": 123,
"finalCreditLimit": 123,
"boostStatus": "<string>",
"executionPeriod": "<string>"
}Credit Scoring
Boost credit limit
Upload a financial statement PDF to attempt a credit-limit increase.
POST
/
v1
/
credit-boost
Boost credit limit
curl --request POST \
--url https://api-staging.bsa.ai/v1/credit-boost \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"FullName": "<string>",
"MobileNumber": "<string>"
}
'import requests
url = "https://api-staging.bsa.ai/v1/credit-boost"
payload = {
"FullName": "<string>",
"MobileNumber": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({FullName: '<string>', MobileNumber: '<string>'})
};
fetch('https://api-staging.bsa.ai/v1/credit-boost', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api-staging.bsa.ai/v1/credit-boost",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'FullName' => '<string>',
'MobileNumber' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api-staging.bsa.ai/v1/credit-boost"
payload := strings.NewReader("{\n \"FullName\": \"<string>\",\n \"MobileNumber\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api-staging.bsa.ai/v1/credit-boost")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"FullName\": \"<string>\",\n \"MobileNumber\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api-staging.bsa.ai/v1/credit-boost")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"FullName\": \"<string>\",\n \"MobileNumber\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"mobileNumber": 123,
"finalCreditScore": 123,
"originalCreditLimit": 123,
"mankaCreditLimit": 123,
"finalCreditLimit": 123,
"boostStatus": "<string>",
"executionPeriod": "<string>"
}Currently unavailable. The credit scoring service does not offer the
statement-based boost at present, so this endpoint returns
501 unimplemented with the message
credit-limit boost is not available: the credit scoring service does not currently offer it.
The contract below is kept for when it returns; do not build a flow that
depends on it today.200 OK with the same response shape; branch on boostStatus and the
limit fields.
The upstream always sends an SMS to the customer’s mobile number
when this endpoint is called, including on validation failures. Treat
every successful call as an outbound communication event.
Content type
multipart/form-data
Form fields
string
required
Customer’s full name as it appears on the statement. For bank
statements, at least two name parts must match the statement
(case-insensitive) or the upstream rejects with a name mismatch.
string
required
Exactly 12 digits in international format (e.g.
255762260621). For
MNO statements, must match the phone number on the statement exactly.file
required
The statement PDF. See Statement requirements
below.
Example
curl -sf -X POST "$BASE/v1/credit-boost" \
-H "Authorization: Bearer $TOKEN" \
-F "FullName=JOHN DEO MWAMBA" \
-F "MobileNumber=255762260621" \
-F "pdf_file=@/path/to/statement.pdf"
import requests
with open("/path/to/statement.pdf", "rb") as f:
resp = requests.post(
f"{BASE}/v1/credit-boost",
headers={"Authorization": f"Bearer {TOKEN}"},
data={"FullName": "JOHN DEO MWAMBA", "MobileNumber": "255762260621"},
files={"pdf_file": ("statement.pdf", f, "application/pdf")},
)
print(resp.status_code, resp.json())
Response
200 OK — same shape across all three scenarios:
Scenario A — Boost applied
{
"mobileNumber": 255762260621,
"finalCreditScore": 500,
"originalCreditLimit": 30000.0,
"mankaCreditLimit": 55000.0,
"finalCreditLimit": 55000.0,
"boostStatus": "YES",
"executionPeriod": "4.32 seconds"
}
Scenario B — No increase
{
"mobileNumber": 255762260621,
"finalCreditScore": 500,
"originalCreditLimit": 30000.0,
"mankaCreditLimit": 18000.0,
"finalCreditLimit": 30000.0,
"boostStatus": "NO",
"executionPeriod": "3.91 seconds"
}
Scenario C — Cooldown active
{
"mobileNumber": 255762260621,
"finalCreditScore": 500,
"originalCreditLimit": 30000.0,
"mankaCreditLimit": 30000.0,
"finalCreditLimit": 30000.0,
"boostStatus": "NO"
}
Fields
integer
integer
number
TZS. Limit before this boost attempt.
number
TZS. Limit derived from the uploaded statement.
number
TZS.
max(originalCreditLimit, mankaCreditLimit) after a successful
evaluation; otherwise echoes originalCreditLimit.string
YES if the limit was raised; NO otherwise (including the cooldown case).string
Upstream processing time. Diagnostic only.
Statement requirements
| Requirement | Detail |
|---|---|
| Format | PDF only (.pdf) |
| Accepted sources | Airtel, Yas, Vodacom, Halotel, NMB, CRDB |
| Rejected sources | HaloPesa, Selcom, any other institution |
| Recency | Last transaction within the past 14 days |
| Coverage — Airtel | Minimum 15 days of history |
| Coverage — all others | Minimum 60 days of history |
| MNO match | Phone number on statement must equal MobileNumber |
| Bank match | At least 2 name parts on the statement must match FullName |
| Authenticity | Unedited original, direct from the provider |
Errors
The upstream uses several non-standard status codes. We translate them onto our standard error code surface:| Code | Caused by |
|---|---|
invalid_argument | File is not a PDF (upstream 400); statement processing failed (402); name/phone mismatch (405); statement not recent enough (406); statement source unsupported, insufficient coverage, or other validation failure surfaced as upstream 401 |
failed_precondition | Statement appears tampered or forged (upstream 403); customer not eligible — current limit ≤ 500 TZS (upstream 500) |
unauthenticated | Token missing/invalid (upstream 401 with detail "Invalid or missing token") |
unavailable | Upstream Manka engine unreachable (upstream 502) |
internal | Database write failed after a successful boost compute (upstream 998); other unhandled server error (501) |
The upstream returns
401 for both genuine auth failures and several
business-validation failures (unsupported source, HaloPesa statement,
insufficient coverage). In those latter cases the API translates the
result to invalid_argument, not unauthenticated. If you need to
distinguish, inspect the message field — auth failures contain the
literal string "Invalid or missing token".
