HomeTrust

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.

Header
x-api-key: ht_live_3f9b0c1e8a7d4b2c9e6f5a1d0b8c7e2f

Base 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" }'
Response
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.

EndpointDescription
GET/rolesList roles. Query: status=active|archived, search. Returns { items: Role[] }.
POST/rolesCreate a role from CreateRoleInput. Returns 201 Role with checks and pricePerApplicant.
GET/roles/:idGet one of your roles.
PATCH/roles/:idUpdate title, description, location or status.
DELETE/roles/:idArchive a role. Returns { ok: true }.
GET/roles/:id/applicantsPaginated 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.

EndpointDescription
POST/roles/:id/invitesBody { applicants: [{firstName,lastName,email?,phone?}], channel: email|sms|link }. Returns 201 { items, charged, balance }. 402 if the wallet is short.
POST/applicants/:id/resendRe-send the invite and extend its expiry (invited, in_progress or expired only).
POST/applicants/:id/cancelCancel 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.

EndpointDescription
GET/applicantsPaginated list. Query: status, roleId, riskLevel, search, page, pageSize.
GET/applicants/:idApplicantDetail 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.

EndpointDescription
POST/instant-checksBody InstantCheckInput { idType, idNumber, firstName, lastName, dob?, selfieUrl? }. Returns 201 InstantCheck.
GET/instant-checksPaginated history of your instant checks.
GET/instant-checks/:idGet a single instant check.

Watchlist & incidents

Search verified HomeTrust Protect incidents and report new ones. Only verified or resolved incidents are returned by search.

EndpointDescription
POST/watchlist/searchBody { idNumber?, phone?, name? } (at least one). Returns { matches: WatchlistMatch[] }.
GET/incidentsPaginated incidents you reported. Query: status, page.
POST/incidentsReport an incident (CreateIncidentInput). Returns 201 Incident with status submitted.
GET/incidents/:idGet an incident you reported.

Catalog & wallet

Read prices and packages, and check your balance before large invite batches.

EndpointDescription
GET/catalogPublic. Checks, packages, role types and add-on prices.
GET/walletWallet balance and totals.
GET/wallet/transactionsPaginated 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.

Request
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

Request & response
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.

Error response
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 }
  }
}
CodeHTTPMeaning
BAD_REQUEST400Malformed JSON or missing required parameter.
UNAUTHORIZED401Missing, invalid or revoked API key.
INSUFFICIENT_FUNDS402Wallet balance is too low. Top up and retry.
FORBIDDEN403The key's account cannot access this resource.
NOT_FOUND404Resource does not exist or belongs to another account.
CONFLICT409The request conflicts with the current state.
INVITE_EXPIRED / INVITE_CANCELLED410The candidate link is no longer usable.
VALIDATION_ERROR422Field 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.