The service form must be prefilled with the applicant’s name, date of birth and phone as soon as they type their National ID. KEZA exposes a learner profile where the ID is part of the URL path. New idea in this lesson: path parameters.
KEZA API guide · the slice you need
GET /v1/learners/{nationalId}
Authorization: Bearer <token from /v1/auth/login>
200 { "nationalId", "firstName", "lastName", "dateOfBirth", "gender", "phone", "district",
"enrollments": [ { "programCode", "status", "year" } ] }
404 { "code": "LEARNER_NOT_FOUND" } well-formed ID that KEZA does not know
400 { "code": "INVALID_NATIONAL_ID" } not 16 digits starting with 1
401 { "code": "UNAUTHORIZED" }
Mock path /victor/keza/v1/learners/{nationalId}. The {nationalId} segment matches any value, and rules can test it with a Path variable condition.
1 · Request: GET /victor/keza/v1/learners/{nationalId}
2 · Responses (the first matching rule wins)
Rule 1 · Known learner (Aline)
| Match on | Key | Operator | Value | |
|---|---|---|---|---|
| If | Header | Authorization | equals | Bearer keza-victor-token-7f3a |
| and | Path variable | nationalId | equals | 1199880012345678 |
Respond with 200 Body type json
{
"nationalId": "1199880012345678",
"firstName": "Aline",
"lastName": "Mukamana",
"dateOfBirth": "1998-04-12",
"gender": "F",
"phone": "0788000111",
"district": "Gasabo",
"enrollments": [
{
"programCode": "WEB_BASICS",
"status": "COMPLETED",
"year": 2025
}
]
}
Rule 2 · Well-formed but unknown ID
| Match on | Key | Operator | Value | |
|---|---|---|---|---|
| If | Header | Authorization | equals | Bearer keza-victor-token-7f3a |
| and | Path variable | nationalId | matches (regex) | ^1[0-9]{15}$ |
Respond with 404 Body type json
{
"code": "LEARNER_NOT_FOUND",
"message": "No learner matches the supplied National ID."
}
Rule 3 · Malformed ID
| Match on | Key | Operator | Value | |
|---|---|---|---|---|
| If | Header | Authorization | equals | Bearer keza-victor-token-7f3a |
Respond with 400 Body type json
{
"code": "INVALID_NATIONAL_ID",
"message": "nationalId must be 16 digits starting with 1."
}
Otherwise — when no rule above matches
Respond with 401 Body type json
{
"code": "UNAUTHORIZED",
"message": "Missing or invalid bearer token."
}
3 · Label
| Field | Value |
|---|---|
| Name | Victor KEZA Learner Profile |
| Code | VICTOR_KEZA_LEARNER |
| Service | KEZA |
| Category | victor-course |
| Active | ✔ served by the mock server right away |
^1[0-9]{15}$: any other well-formed ID is unknown (404).
Rule 1 of the learner mock as the portal shows it

Rule 2: any other well-formed ID gets 404

The Otherwise card of the learner mock
The endpoint URL contains the same {nationalId} placeholder. The mapping’s path object fills it.
| Field | Value | |
|---|---|---|
| **Endpoint adapter *** | FMAPPING (fetch) |
locked after save |
| **Endpoint code *** | VICTOR_KEZA_LEARNER_FETCH |
locked after save |
| **Endpoint url *** | http://mock-service-engine.apps:8000/victor/keza/v1/learners/{nationalId} |
|
| **Endpoint type *** | FETCH (set by the adapter) |
|
| **Endpoint name *** | Victor KEZA Learner Profile |
|
| **Partner *** | KEZA |
locked after save |
| Active | ON |
|
| **Endpoint description *** | Victor course: prefill the learner profile from KEZA (path parameter). |
**Endpoint Mapping ***
{
"method": "GET",
"path": { "nationalId": nationalId },
"auth": {
"type": "USER_CREDENTIALS",
"baseUrl": "http://mock-service-engine.apps:8000",
"parameters": {
"authPath": "/victor/keza/v1/auth/login",
"bodyTemplate": { "username": "irembo-integration", "password": "Keza@2026" }
},
"tokenConfig": {
"storageKey": "VICTOR_KEZA_TOKEN",
"headerName": "Authorization",
"tokenPrefix": "Bearer",
"expiryBuffer": 300,
"extractPath": "data.token"
}
}
}
{key} placeholder with the same name in the endpoint URL. Here payload nationalId fills {nationalId}.Secrets · training literal. Hard-coded only because KEZA is a training mock. In a real integration use the environment variable instead: Keza@2026 → "${KEZA_PASSWORD}" (the whole quoted string, nothing before or after; see Lesson 3.4).
HTTP 200 1.37 s captured on staging · 26 Sep 2026
1 · Request (curl)
curl -s -X POST "https://integration-gateway.staging.iremboinc.com/integration/v1/fetch/sync" \
-H "Authorization: Bearer $TOKEN" \
-H "portal: integration-management-portal" \
-H "Content-Type: application/json" \
-d '{
"endpointCode": "VICTOR_KEZA_LEARNER_FETCH",
"callerId": "victor-course",
"requester": "victor-course",
"payload": {
"nationalId": "1199880012345678"
}
}' | jq
2 · Real response
{
"status": true,
"responseCode": "integration_870000",
"responseMessage": "integration_870000",
"data": {
"id": "dbcbb7b6-23c6-46d3-a0dd-aa02c1bbc389",
"endpointCode": "VICTOR_KEZA_LEARNER_FETCH",
"callerId": "victor-course",
"response": "{\n \"nationalId\": \"1199880012345678\",\n \"firstName\": \"Aline\",\n \"lastName\": \"Mukamana\",\n \"dateOfBirth\": \"1998-04-12\",\n \"gender\": \"F\",\n \"phone\": \"0788000111\",\n \"district\": \"Gasabo\",\n \"enrollments\": [\n {\n \"programCode\": \"WEB_BASICS\",\n \"status\": \"COMPLETED\",\n \"year\": 2025\n }\n ]\n}",
"adapterCode": "FMAPPING"
},
"timestamp": "2026-09-26T09:48:05.565570117",
"traceId": "6ab778b44596c074ede3eb06b7fbe1a4"
}
3 · Decoded data.response
{
"nationalId": "1199880012345678",
"firstName": "Aline",
"lastName": "Mukamana",
"dateOfBirth": "1998-04-12",
"gender": "F",
"phone": "0788000111",
"district": "Gasabo",
"enrollments": [
{
"programCode": "WEB_BASICS",
"status": "COMPLETED",
"year": 2025
}
]
}
4 · What to notice
…/learners/1199880012345678: the placeholder was filled.HTTP 500 0.73 s captured on staging · 26 Sep 2026
1 · Request (curl)
curl -s -X POST "https://integration-gateway.staging.iremboinc.com/integration/v1/fetch/sync" \
-H "Authorization: Bearer $TOKEN" \
-H "portal: integration-management-portal" \
-H "Content-Type: application/json" \
-d '{
"endpointCode": "VICTOR_KEZA_LEARNER_FETCH",
"callerId": "victor-course",
"requester": "victor-course",
"payload": {
"nationalId": "1190000000000009"
}
}' | jq
2 · Real response
{
"httpStatus": 500,
"exception": "PartnerException",
"responseCode": "PARTNER_ERROR_FETCH-VICTOR_KEZA_LEARNER_FETCH",
"responseMessage": "{\n \"code\": \"LEARNER_NOT_FOUND\",\n \"message\": \"No learner matches the supplied National ID.\"\n}",
"fieldErrors": null
}
3 · What to notice
HTTP 500 0.67 s captured on staging · 26 Sep 2026
1 · Request (curl)
curl -s -X POST "https://integration-gateway.staging.iremboinc.com/integration/v1/fetch/sync" \
-H "Authorization: Bearer $TOKEN" \
-H "portal: integration-management-portal" \
-H "Content-Type: application/json" \
-d '{
"endpointCode": "VICTOR_KEZA_LEARNER_FETCH",
"callerId": "victor-course",
"requester": "victor-course",
"payload": {
"nationalId": "12345"
}
}' | jq
2 · Real response
{
"httpStatus": 500,
"exception": "PartnerException",
"responseCode": "PARTNER_ERROR_FETCH-VICTOR_KEZA_LEARNER_FETCH",
"responseMessage": "{\n \"code\": \"INVALID_NATIONAL_ID\",\n \"message\": \"nationalId must be 16 digits starting with 1.\"\n}",
"fieldErrors": null
}
3 · What to notice
Gotcha · Path placeholders must match exactly
- The URL placeholder and the path key must have the same name and case:
{nationalId}↔"nationalId".- If the payload field can be missing, test that case too and check the URL in the Communication Trace before go-live.
- The URL field rejects
%,$,~,@, spaces and:in the path. The portal does not warn you; the save fails with 400 BAD_REQUEST.
A partner URL is https://api.keza.rw/v1/learners/{id}/history and the form field is nationalId. Which mapping fills it?
C. The key must equal the placeholder name (id); the value is the JSONata path to the payload field (nationalId).
Your turn · change one thing
Add a rule to your learner mock, above the regex rule, for Eric (
1199770098765432) that returns Eric’s profile. Create${YOUR_FIRST_NAME}_KEZA_LEARNER_FETCH(FMAPPING: it prefills the form) on/${your_first_name}/keza/v1/learners/{nationalId}and prove Aline, Eric and12345. Replace the variables with your first name before saving.
← 1.6 Call it and read the result · 2.2 Headers, query and JSONata functions: training centres →