Developers
HomeTrust API reference
A predictable REST API with JSON bodies, camelCase keys and amounts in integer RWF. Everything you can do in the dashboard, you can do in code.
Authentication
Authenticate every request with your secret key in the x-api-key header. Keys look like ht_live_… and are tied to your employer account and wallet.
Create a key
Log in and open Settings → API keys. Name the key (e.g. "ATS production") and copy the secret — it is shown only once.
Keep it secret
Only call the API from your server. Revoke a leaked key immediately from Settings; revoked keys return 401.
x-api-key: ht_live_3f9b0c1e8a7d4b2c9e6f5a1d0b8c7e2fBase URL & conventions
https://hometrust-api.bahafrica.com/api- Send and receive JSON with
Content-Type: application/json. Keys are camelCase. - Money values are integers in Rwandan francs (RWF). Dates are ISO 8601 in UTC.
- List endpoints return
{ items, total, page, pageSize }. Use?page=1&pageSize=20. - Charges are deducted from your prepaid wallet at the moment of the request.
Quickstart: instant ID check
Verify an ID number against the national registry. Provide a selfie URL (from the uploads endpoint) to add a face match.
curl -X POST https://hometrust-api.bahafrica.com/api/instant-checks \
-H "x-api-key: ht_live_YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{ "idType": "national_id", "idNumber": "1199080012345678", "firstName": "Claudine", "lastName": "Uwase", "dob": "1990-04-12" }'HTTP/1.1 201 Created
{
"id": "ic_8f2a1c",
"idType": "national_id",
"idNumber": "1199080012345678",
"firstName": "Claudine",
"lastName": "Uwase",
"dob": "1990-04-12",
"selfieUrl": null,
"status": "verified",
"result": {
"registryFound": true,
"registryName": "UWASE Claudine",
"registryDob": "1990-04-12",
"registryGender": "female",
"nameMatch": 98,
"dobMatch": true,
"faceMatch": null,
"watchlistMatches": 0
},
"price": 500,
"createdAt": "2026-10-27T09:14:03.000Z"
}status is verified, not_found or mismatch. nameMatch and faceMatch are 0–100 scores.
Roles
A role is a job opening with a package of checks. Price per applicant is computed by the server from the package, add-ons and number of guarantors/references.
| Endpoint | Description |
|---|---|
GET/roles | List roles. Query: status=active|archived, search. Returns { items: Role[] }. |
POST/roles | Create a role from CreateRoleInput. Returns 201 Role with checks and pricePerApplicant. |
GET/roles/:id | Get one of your roles. |
PATCH/roles/:id | Update title, description, location or status. |
DELETE/roles/:id | Archive a role. Returns { ok: true }. |
GET/roles/:id/applicants | Paginated applicants for a role. Query: status, search, page. |
Invites
Invite 1–50 candidates at once. The total (pricePerApplicant × n) is debited from your wallet in a single transaction; links expire after 7 days.
| Endpoint | Description |
|---|---|
POST/roles/:id/invites | Body { applicants: [{firstName,lastName,email?,phone?}], channel: email|sms|link }. Returns 201 { items, charged, balance }. 402 if the wallet is short. |
POST/applicants/:id/resend | Re-send the invite and extend its expiry (invited, in_progress or expired only). |
POST/applicants/:id/cancel | Cancel an invite that has not been started. Returns { applicant, refunded }. |
Applicants & reports
Applicants move through invited → in_progress → submitted → completed. The detail endpoint includes the full verification report.
| Endpoint | Description |
|---|---|
GET/applicants | Paginated list. Query: status, roleId, riskLevel, search, page, pageSize. |
GET/applicants/:id | ApplicantDetail with checks, guarantors, references and VerificationReport (trust score, risk level, verdict, sections). |
Instant checks
A single registry lookup with optional selfie face match and watchlist screen. Each call costs RWF 500.
| Endpoint | Description |
|---|---|
POST/instant-checks | Body InstantCheckInput { idType, idNumber, firstName, lastName, dob?, selfieUrl? }. Returns 201 InstantCheck. |
GET/instant-checks | Paginated history of your instant checks. |
GET/instant-checks/:id | Get a single instant check. |
Watchlist & incidents
Search verified HomeTrust Protect incidents and report new ones. Only verified or resolved incidents are returned by search.
| Endpoint | Description |
|---|---|
POST/watchlist/search | Body { idNumber?, phone?, name? } (at least one). Returns { matches: WatchlistMatch[] }. |
GET/incidents | Paginated incidents you reported. Query: status, page. |
POST/incidents | Report an incident (CreateIncidentInput). Returns 201 Incident with status submitted. |
GET/incidents/:id | Get an incident you reported. |
Catalog & wallet
Read prices and packages, and check your balance before large invite batches.
| Endpoint | Description |
|---|---|
GET/catalog | Public. Checks, packages, role types and add-on prices. |
GET/wallet | Wallet balance and totals. |
GET/wallet/transactions | Paginated transactions. Query: type, category, status. |
More examples
Create a role
packageId is one of basic, standard, premium, custom. For custom, addOns is the full list of checks.
POST /api/roles
x-api-key: ht_live_YOUR_SECRET_KEY
{
"title": "Live-in nanny — Kimihurura",
"category": "domestic",
"roleType": "nanny",
"packageId": "premium",
"addOns": ["health"],
"guarantorsRequired": 2,
"referencesRequired": 1,
"location": "Gasabo, Kigali"
}Invite candidates
POST /api/roles/rl_51c9/invites
x-api-key: ht_live_YOUR_SECRET_KEY
{
"channel": "sms",
"applicants": [
{ "firstName": "Claudine", "lastName": "Uwase", "phone": "+250788123456" },
{ "firstName": "Jean", "lastName": "Habimana", "email": "jean@example.rw" }
]
}Search the watchlist
POST /api/watchlist/search
x-api-key: ht_live_YOUR_SECRET_KEY
{ "idNumber": "1199080012345678" }
HTTP/1.1 200 OK
{
"matches": [
{
"incidentId": "in_7a20",
"incidentType": "theft",
"incidentDate": "2026-03-14",
"district": "Gasabo",
"status": "verified",
"matchedOn": ["id_number"],
"confidence": 100
}
]
}Errors
Errors use standard HTTP status codes and a consistent body. details is optional and, for validation errors, lists the offending fields.
HTTP/1.1 402 Payment Required
{
"error": {
"code": "INSUFFICIENT_FUNDS",
"message": "Your wallet balance is too low for this request.",
"details": { "required": 39000, "balance": 12000 }
}
}| Code | HTTP | Meaning |
|---|---|---|
| BAD_REQUEST | 400 | Malformed JSON or missing required parameter. |
| UNAUTHORIZED | 401 | Missing, invalid or revoked API key. |
| INSUFFICIENT_FUNDS | 402 | Wallet balance is too low. Top up and retry. |
| FORBIDDEN | 403 | The key's account cannot access this resource. |
| NOT_FOUND | 404 | Resource does not exist or belongs to another account. |
| CONFLICT | 409 | The request conflicts with the current state. |
| INVITE_EXPIRED / INVITE_CANCELLED | 410 | The candidate link is no longer usable. |
| VALIDATION_ERROR | 422 | Field validation failed; see details for each field. |
Sandbox
In sandbox mode (GET /config returns "sandbox": true) the national registry is simulated. Use these rules to test every outcome:
- ID numbers ending in 0000 are not found in the registry.
- ID numbers ending in 9999 return a different name (name mismatch).
- ID numbers ending in 8888 fail the face match.
- Any other well-formed ID is found and matches the submitted name.
- Rwandan national IDs encode birth year in digits 2–5 and gender in digit 6 (8 = male, 7 = female).
Need help? Email us via the contact page or read the Online SLA.
Ready to integrate?
Create a business account, generate an API key in Settings and make your first instant check today.