Skip to content

Get a Request ​

Returns the current state of a request you raised. Use it when:

  • you did not give a callbackURL, or
  • your callback server was down for a long time and you want to catch up, or
  • you just want to double-check before acting.

Endpoint ​

MethodURL
GET/api/requests/:requestID
Path parameterDescription
requestIDThe requestID you got when you created the request, or from a callback
<timestamp>.GET./api/requests/6650f1a2b3c4d5e6f7a8b9e1.

You can only see requests raised with your API key. That includes child requests made from them.

Response ​

data is the request summary. It has the same fields as a callback, in the same order, without event, deliveryID and changedAt:

FieldTypeDescription
requestIDstringThe request's ID
requestNumberstringIts number in Johari
statusstringCurrent status. See Request Statuses
statusTitlestringStatus as people see it in Johari, e.g. "Pending Payment"
descriptionstringWhat the payment is for
amountnumberThe request amount
currencystringe.g. "TZS"
amountRemainingnumber | nullOnly for a request being paid in parts: how much is still unpaid. Otherwise null
modeOfPaymentstring"cash" or "bank"
isUrgentbooleantrue if the request is marked urgent
companyobjectThe requesting company: { "id": string, "name": string }
siteobjectThe requesting site: { "id": string, "name": string }
costCodestringThe cost code value
isChildRequestbooleantrue for one part of a request being paid in parts. See Partial payments
mainRequestIDstring | nullFor a child request, the request it belongs to. Otherwise null
notestring | nullOnly the note written with the latest status change, e.g. the rejection reason, the return reason, a payment note or an approval comment. null if that change had no note. Never an older note
createdAtstringWhen the request was created (ISO 8601, UTC)
createdBystringName of the person in Johari who created it. For requests you raise, the API key's user's name followed by (API), e.g. "Neema Mollel (API)". Johari shows the same in the request, its log and the WhatsApp notifications, so people can see a system raised it
approvedAtstring | nullWhen it was approved (ISO 8601, UTC)
approvedBystring | nullName of the person who approved it. If the request was approved automatically (because the key's user may approve it themselves), it is also the key's user's name followed by (API)
rejectedAtstring | nullWhen it was rejected (ISO 8601, UTC)
rejectedBystring | nullName of the person who rejected it
confirmedAtstring | nullWhen accounts confirmed it, sending it for payment (ISO 8601, UTC)
confirmedBystring | nullName of the person who confirmed it
paidAtstring | nullWhen it was paid (ISO 8601, UTC)
paidBystring | nullName of the person who paid it
efdAtstring | nullWhen the EFD (tax) receipt was uploaded (ISO 8601, UTC)
efdBystring | nullName of the person who uploaded the EFD receipt
returnedAtstring | nullWhen it was returned (ISO 8601, UTC)
returnedBystring | nullName of the person who returned it
returnedTostring | nullWho the cashier returned it to: "creator", "approver" or "confirmer". null if it was never returned. While status is "returned" and this is "creator", the request is waiting for your system to answer. Keeps its last value after the request moves on, so always check status too. See Returned requests
filesarrayEvery file on the request. See Files

The ...At fields say when that step happened. The ...By fields are the name of the person in Johari who did it. Each one is null until that step happens.

note is only ever the note from the latest status change. Johari's other internal notes are not shared.

Returned requests ​

returnedTo says who the cashier returned the request to.

  • While status is "returned" and returnedTo is "creator", the request is waiting for your system to answer. See Respond to a Return.
  • When it is "approver" or "confirmer", that person answers it in Johari. You just receive the callbacks.

returnedTo keeps its last value after the request moves on, so always check status too. See Returned.

Files ​

files lists every file on the request:

  • the files sent when the request was created,
  • the proof of payment the cashier uploads when paying,
  • the EFD receipt,
  • any file people attach to the request in Johari (for example an accountant's upload),
  • any file your system adds with Add Files.

Each item:

FieldTypeDescription
idstringThe file's ID. Use it with Get a File to download it
namestringThe file name, e.g. "invoice-1042.pdf"
typestring | nullThe file type, e.g. "application/pdf", or null
sizenumber | nullThe size in bytes, or null

There is no download link in files. Ask Get a File for a fresh link each time you need one. Internal storage paths are never shown.

Example ​

bash
REQUEST_ID="6650f1a2b3c4d5e6f7a8b9e1"
TS=$(date +%s)
SIG=$(printf '%s' "$TS.GET./api/requests/$REQUEST_ID." \
  | openssl dgst -sha256 -hmac "$SIGNING_SECRET" \
  | sed 's/^.* //')
curl "$BASE_URL/api/requests/$REQUEST_ID" \
  -H "x-api-key: $API_KEY" \
  -H "x-timestamp: $TS" \
  -H "x-signature: $SIG"
js
const requestID = "6650f1a2b3c4d5e6f7a8b9e1";
const result = await callJohari("GET", `/api/requests/${requestID}`);
console.log(result.data.status);
python
request_id = "6650f1a2b3c4d5e6f7a8b9e1"
result = call_johari("GET", f"/api/requests/{request_id}")
print(result["data"]["status"])

Response 200, for a request that has been paid and completed:

json
{
  "status": "success",
  "data": {
    "requestID": "6650f1a2b3c4d5e6f7a8b9e1",
    "requestNumber": "1042",
    "status": "completed",
    "statusTitle": "Completed",
    "description": "Cement for slab",
    "amount": 150000,
    "currency": "TZS",
    "amountRemaining": null,
    "modeOfPayment": "bank",
    "isUrgent": false,
    "company": {
      "id": "6650f1a2b3c4d5e6f7a8b901",
      "name": "Example Construction Ltd"
    },
    "site": {
      "id": "3c9a7f52-1d4e-4b8a-9f6c-2e1d5a7b8c90",
      "name": "Block A Apartments"
    },
    "costCode": "1003",
    "isChildRequest": false,
    "mainRequestID": null,
    "note": null,
    "createdAt": "2026-10-29T09:58:04.112Z",
    "createdBy": "Neema Mollel (API)",
    "approvedAt": "2026-10-29T10:12:36.571Z",
    "approvedBy": "Amina Juma",
    "rejectedAt": null,
    "rejectedBy": null,
    "confirmedAt": "2026-10-29T13:40:18.230Z",
    "confirmedBy": "Grace Kimaro",
    "paidAt": "2026-10-30T07:55:02.918Z",
    "paidBy": "Joseph Mwakyusa",
    "efdAt": "2026-10-30T11:20:45.067Z",
    "efdBy": "Joseph Mwakyusa",
    "returnedAt": null,
    "returnedBy": null,
    "returnedTo": null,
    "files": [
      {
        "id": "0b6f2c1e-8d4a-4f3b-9e7c-5a1d2b3c4e5f",
        "name": "invoice-1042.pdf",
        "type": "application/pdf",
        "size": 48213
      },
      {
        "id": "5e9a1b7c-3d2f-4a6e-8b1c-9d0e2f3a4b5c",
        "name": "payment-slip-1042.pdf",
        "type": "application/pdf",
        "size": 102944
      },
      {
        "id": "a2c4e6f8-1b3d-4f5a-9c7e-0d2b4f6a8c1e",
        "name": "efd-receipt-1042.jpg",
        "type": "image/jpeg",
        "size": 231570
      }
    ]
  },
  "message": "Request fetched successfully"
}

Response 404:

json
{
  "status": "error",
  "data": null,
  "message": "Request not found"
}

You get 404 when the ID is wrong, when the request was raised with a different API key, or when it was deleted in Johari. If the request has a callbackURL, a deletion also sends a request.deleted callback.

Polling ​

If you rely on this endpoint instead of callbacks, do not call it in a tight loop. Requests move at human speed: checking each open request every 5 to 15 minutes is plenty. Stop checking once the status is final (completed, rejected or master-partial-payment-completed), or once you get 404 because the request was deleted.