Everything in the portal can be proven with two tools: a Keycloak token and curl. Run this once per session (the token expires after a few minutes; just re-run it when you get a 401).
Terminal · get a token
# 1. Your staging portal login (never paste real passwords into shared files)
export USER_EMAIL="you@example.com"
export USER_PASSWORD="your-password"
# 2. Get a Keycloak access token (it expires after a few minutes; re-run if you get 401)
TOKEN=$(curl -s -X POST \
"https://keycloak.staging.iremboinc.com/realms/irembo-staging/protocol/openid-connect/token" \
-d grant_type=password -d client_id=integration-management-portal \
-d username="$USER_EMAIL" -d password="$USER_PASSWORD" | jq -r .access_token)
echo ${TOKEN:0:20}... # prints the start of the token if it worked
The mapping test runs only the request pass of your mapping against a sample payload and shows what the engine would send. It calls nobody, so it is safe and fast. It has no portal button: call it with curl.
HTTP 200 captured on staging · 26 Sep 2026
1 · Request (curl)
curl -s -X POST "https://integration-gateway.staging.iremboinc.com/integration/v1/endpoints/VICTOR_KEZA_ELIGIBILITY_FETCH/mapping/test" \
-H "Authorization: Bearer $TOKEN" \
-H "portal: integration-management-portal" \
-H "Content-Type: application/json" \
-d '{
"nationalId": "1199880012345678",
"programCode": "DATA_ANALYTICS"
}' | jq '.data'
2 · What the engine would send
{
"method": "GET",
"query": {
"nationalId": "1199880012345678",
"program": "DATA_ANALYTICS"
},
"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"
}
}
}
3 · What to notice
program now holds the value of programCode: the rename worked.Now call /fetch/sync. The engine logs in, calls the eligibility mock and wraps the answer in the success envelope.
HTTP 200 0.87 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_ELIGIBILITY_FETCH",
"callerId": "victor-course",
"requester": "victor-course",
"payload": {
"nationalId": "1199880012345678",
"programCode": "DATA_ANALYTICS"
}
}' | jq
2 · Real response
{
"status": true,
"responseCode": "integration_870000",
"responseMessage": "integration_870000",
"data": {
"id": "2e438be6-0c78-478f-871f-abd9e735d2d7",
"endpointCode": "VICTOR_KEZA_ELIGIBILITY_FETCH",
"callerId": "victor-course",
"response": "{\n \"found\": true,\n \"learner\": {\n \"nationalId\": \"1199880012345678\",\n \"firstName\": \"Aline\",\n \"lastName\": \"Mukamana\",\n \"district\": \"Gasabo\"\n },\n \"eligibility\": {\n \"status\": \"ELIGIBLE\",\n \"programCode\": \"DATA_ANALYTICS\",\n \"reasonCode\": null,\n \"evaluatedAt\": \"2026-09-26T08:00:00Z\"\n }\n}",
"adapterCode": "FMAPPING"
},
"timestamp": "2026-09-26T09:59:40.977889843",
"traceId": "6ab77b6c9f1234521f86de28934960bd"
}
3 · Decoded data.response
{
"found": true,
"learner": {
"nationalId": "1199880012345678",
"firstName": "Aline",
"lastName": "Mukamana",
"district": "Gasabo"
},
"eligibility": {
"status": "ELIGIBLE",
"programCode": "DATA_ANALYTICS",
"reasonCode": null,
"evaluatedAt": "2026-09-26T08:00:00Z"
}
}
4 · What to notice
integration_870000: the engine, the login and the mock all worked.data.response is a string; tab 3 shows it parsed with jq '.data.response | fromjson'.adapterCode FMAPPING confirms a fetch endpoint answered.traceId and the data.id (the Request id) when you report problems: both find the call’s log lines in Kibana (Lesson 6.3).Always prove the unhappy paths too. Read each result before you open the explanation.
HTTP 200 0.71 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_ELIGIBILITY_FETCH",
"callerId": "victor-course",
"requester": "victor-course",
"payload": {
"nationalId": "1199770098765432",
"programCode": "DATA_ANALYTICS"
}
}' | jq
2 · Real response
{
"status": true,
"responseCode": "integration_870000",
"responseMessage": "integration_870000",
"data": {
"id": "b31c07f5-3572-4e78-9f74-895286a5905c",
"endpointCode": "VICTOR_KEZA_ELIGIBILITY_FETCH",
"callerId": "victor-course",
"response": "{\n \"found\": true,\n \"learner\": {\n \"nationalId\": \"1199770098765432\",\n \"firstName\": \"Eric\",\n \"lastName\": \"Habimana\",\n \"district\": \"Huye\"\n },\n \"eligibility\": {\n \"status\": \"NOT_ELIGIBLE\",\n \"programCode\": \"DATA_ANALYTICS\",\n \"reasonCode\": \"AGE_LIMIT\",\n \"evaluatedAt\": \"2026-09-26T08:00:00Z\"\n }\n}",
"adapterCode": "FMAPPING"
},
"timestamp": "2026-09-26T09:59:41.728642914",
"traceId": "6ab77b6d2466da5a590f4315e382c5dc"
}
3 · Decoded data.response
{
"found": true,
"learner": {
"nationalId": "1199770098765432",
"firstName": "Eric",
"lastName": "Habimana",
"district": "Huye"
},
"eligibility": {
"status": "NOT_ELIGIBLE",
"programCode": "DATA_ANALYTICS",
"reasonCode": "AGE_LIMIT",
"evaluatedAt": "2026-09-26T08:00:00Z"
}
}
4 · What to notice
HTTP 500 1.60 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_ELIGIBILITY_FETCH",
"callerId": "victor-course",
"requester": "victor-course",
"payload": {
"nationalId": "1190000000000009",
"programCode": "DATA_ANALYTICS"
}
}' | jq
2 · Real response
{
"httpStatus": 500,
"exception": "PartnerException",
"responseCode": "PARTNER_ERROR_FETCH-VICTOR_KEZA_ELIGIBILITY_FETCH",
"responseMessage": "{\n \"found\": false,\n \"code\": \"LEARNER_NOT_FOUND\",\n \"message\": \"No learner matches the supplied National ID.\"\n}",
"fieldErrors": null
}
3 · What to notice
PARTNER_ERROR_FETCH-<endpoint code>.responseMessage: that is how you know what KEZA said.PARTNER_ERROR_* unless a successCondition accepts them (Lesson 4.3). Do not waste time looking for a bug in the engine.HTTP 404 0.57 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_ELIGIBILTY_FETCH",
"callerId": "victor-course",
"requester": "victor-course",
"payload": {
"nationalId": "1199880012345678",
"programCode": "DATA_ANALYTICS"
}
}' | jq
2 · Real response
{
"httpStatus": 404,
"exception": "NotFoundException",
"responseCode": "ENDPOINT_NOT_FOUND_EXCEPTION",
"responseMessage": "ENDPOINT_NOT_FOUND_EXCEPTION",
"fieldErrors": []
}
3 · What to notice
endpointCode (ELIGIBLTY) gives 404 ENDPOINT_NOT_FOUND_EXCEPTION.Reading a fetch result
| You see | It means | Look at |
|---|---|---|
200 integration_870000 |
Partner answered 2xx | Decode data.response and check the business fields |
500 PARTNER_ERROR_FETCH-<code> |
Partner answered 4xx/5xx or timed out | responseMessage (the partner body), then the trace in Requests |
404 ENDPOINT_NOT_FOUND_EXCEPTION |
No endpoint with that code | Spelling and case of endpointCode |
400 BAD_REQUEST |
Envelope field missing (callerId, requester, payload…) | Your curl body |

API Requests, searched by endpoint code VICTOR_KEZA

Request info for a successful fetch
Gotcha · Same application number, same old payload
When a fetch payload carries an
applicationNumber, the engine treats repeat calls for the same partner, endpoint and application as the same request and reuses the first payload it stored, even if you changed a field. When testing, use a fresh application number (or none) for each variation.
You call VICTOR_KEZA_ELIGIBILITY_FETCH and get HTTP 500 PARTNER_ERROR_FETCH-VICTOR_KEZA_ELIGIBILITY_FETCH with responseMessage {"code":"UNAUTHORIZED"}. What is the most likely cause?
A. The partner body says UNAUTHORIZED, so the engine reached the partner but the partner rejected the token. An expired Keycloak token would be rejected by the gateway before the engine (plain 401, no PARTNER_ERROR).
Your turn · prove your own endpoint
Run the mapping test and the four fetch calls against your
${YOUR_FIRST_NAME}_KEZA_ELIGIBILITY_FETCH. Then open Requests, search your endpoint code and open the trace of the unknown-learner call. Screenshot it: it is the first item of your capstone evidence pack.
← 1.5 Your first endpoint, with login authentication · 2.1 Path parameters: prefill the learner profile →