Skip to content

Errors ​

When something goes wrong you get an HTTP status other than 200, and the usual envelope with "status": "error".

json
{
  "status": "error",
  "data": "Please provide a modeOfPayment of cash or bank",
  "message": "Please provide a modeOfPayment of cash or bank"
}

Read the HTTP status first, then the text. For most errors data and message hold the same text. For authentication errors, message is always "Unauthorized to access" and data says exactly what is wrong.

HTTP status codes ​

HTTPMeaningRetry?
200It worked
400Something in your data is wrong or missingNo. Fix the data first
401Signing or API key problemNo. Fix your signing, key or clock
403The key's user cannot use Johari yet, or may not do this (for example, a request was returned to someone else)No. Read the message
404Not found, not visible to your key, or a wrong pathNo
429Too many calls. See Too many requestsYes, after the Retry-After header's number of seconds. For POST, use the same idempotencyKey and body
409That idempotencyKey was already usedDepends on data. With data: no, it worked, data is what the first call returned. With data: null: yes, after a few seconds with the same key. See Idempotency
500Something went wrong on Johari's sideYes, after a short wait. For POST, use the same idempotencyKey and body

Authentication errors ​

These can come from any endpoint.

HTTPdataCauseFix
401Please provide the x-api-key, x-signature and x-timestamp headersOne or more headers missing. The message does not say which oneSend all three. See Signing
401Request timestamp is too old or invalidx-timestamp is more than 60 seconds away from Johari's clock, or is not only digits (Unix seconds)Use the current time in seconds, digits only, made fresh for each call. Check your server clock
401Invalid or disabled API keyThe key is wrong, it was disabled in Johari, or it was rotated and you are still using the old pairCheck the key: the whole value, jh_key_ included. If it was rotated, use the new pair. If it was disabled, get a new key. See Getting Started
401Invalid signatureThe signature does not match, or x-signature is not exactly 64 hex charactersSign with the whole signing secret, jh_secret_ included. After a rotation, use the new secret. See Common mistakes. Check your code against the test vectors
401The owner of this API key is disabled or no longer existsThe Johari user the key belongs to was disabledAsk your Johari administrator
403The owner of this API key must review and accept their permission agreement before using the system. Please sign in to the web portal to complete this.The key's user has not accepted Johari's permissions agreementThat user must sign in to Johari once and accept it

Example:

json
{
  "status": "error",
  "data": "Invalid signature",
  "message": "Unauthorized to access"
}

Too many requests ​

These can come from any endpoint. Johari answers 429 when:

  • an API key goes over its limit. Per API key, per server, per minute: at most 120 calls in total, and at most 30 writes within those. Writes are creates, responses to returns and file uploads together (POST /api/requests, POST /api/requests/:requestID/respond-to-return and POST /api/requests/:requestID/files), or
  • one address had 30 failed sign-ins (any 401) within a minute. Every call from that address then gets 429 for the rest of that minute, even with a valid key.

The answer has a Retry-After header: the number of seconds until the minute resets. The body is always:

json
{
  "status": "error",
  "data": "Too many requests. Please wait before trying again",
  "message": "Too many requests. Please wait before trying again"
}

Wait Retry-After seconds, then send the call again. For a POST, send the same idempotencyKey and the same body, so nothing is done twice.

Lookup errors ​

EndpointHTTPmessageCause
GET /api/companies/:companyID/sites404Company not foundWrong ID, or the key's user cannot raise requests for this company
GET /api/companies/:companyID/cost-codes404Company not foundSame as above
GET /api/sites/:siteID/approvers404Site not foundWrong ID, or the key's user cannot raise requests at this site

Create request errors ​

POST /api/requests. Checks run in the order below, and you get the first problem found. In three stages: (1) the idempotencyKey, so a key that was already used is answered before any other field is looked at; (2) each field's type and format, in the order of the table, then callbackURL, then files; (3) whether requestingCompany, requestingSite, costCode and admin are in your lookup lists.

In the files[0] messages, the number is the position of the bad file in files, starting at 0. For example, files[2] is the third file.

HTTPmessageFix
400Please provide an idempotencyKey of up to 255 charactersSend a text idempotencyKey, 1 to 255 characters
400This idempotencyKey was already used for a different request. Use a new key for a new requestEvery new request needs a new key. A key already used for a response or an upload cannot be used either. On a retry, send exactly the same body. See Idempotency
400Please provide a descriptionSend a non-empty text description
400description cannot be longer than 500 charactersShorten it
400Please provide an amount greater than 0Send a number above 0, or a string like "150000", "150,000" or "150,000.50"
400amount must be a number such as 150000, or a string such as "150,000.50"A string amount held something other than digits, thousands commas and one decimal part (e.g. "0x10", "1e3", "-5", ".5"). Send a JSON number, or a string like "150,000.50"
400Amount cannot have more than 2 decimal placesRound to 2 decimal places
400amount is too largeThe amount is above 90,071,992,547,409.91, the most Johari can store. Check the amount
400Please provide a valid requestingCompanySend a company id from List Companies
400Please provide a requestingSiteSend a site id as text
400Please provide a costCodeSend the cost code value as text: "1003", not 1003
400Please provide a modeOfPayment of cash or bankUse "cash" or "bank"
400Please provide a currency of USD, TZS, KES, ZAR, AED, GBPUse one of those, or leave currency out for TZS
400isUrgent must be true or falseSend true or false, not "yes"
400notes must be textSend a string
400notes cannot be longer than 2000 charactersShorten it
400Please provide an admin: an approver id from the site's approvers listadmin is missing, or is not a 24-hex-character ID. It is required: send an approver id from List Approvers for requestingSite
400callbackURL cannot be longer than 2048 charactersShorten it. callbackURL is at most 2048 characters
400Please provide a valid callbackURLSend a full https:// URL your server answers on. On the live server it must point to a public address: localhost, and IP addresses that are loopback, private, link-local, carrier-grade NAT, multicast or otherwise reserved, are refused. See Turning callbacks on
400files must be a listSend files as a JSON array, or leave it out
400You can attach at most 5 filesSend 5 files or fewer
400files[0] must be an object with name, contentType and contentEach item in files must be an object. The number in files[N] is the position of the bad file, starting at 0
400files[0].name must be a file name of up to 255 characters, without slashes or invisible charactersSend a non-empty name of up to 255 characters, with no / or \, and no control or invisible formatting characters (for example right-to-left marks)
400files[0].contentType must be one of application/pdf, image/jpeg, image/png, image/webp, image/heic, application/msword, application/vnd.openxmlformats-officedocument.wordprocessingml.document, application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheetUse one of the listed types, exactly as written. See Attaching files
400files[0].content must be the file encoded as base64Send the file as standard base64: A–Z, a–z, 0–9, +, /, with = padding, length a multiple of 4. No data: prefix, no line breaks
400files[0].content is emptyThe file has no bytes. Send the whole file
400files[0].content does not match its contentTypeThe file's first bytes are not of the type in contentType (for example a PDF without %PDF- in its first 1024 bytes). Pick contentType from the file's extension with the table, and send the real file, not placeholder text
400The attached files cannot be more than 25 MB togetherKeep all files together at or under 25 MB (decoded)
400requestingCompany was not found among the companies you can raise requests forUse an id from List Companies
400requestingSite was not found among this company's sites you can raise requests forUse an id from List Sites for the same company
400costCode is not one of this company's cost codesUse a value from List Cost Codes for the same company
400admin is not one of this site's approversUse an id from List Approvers for the same site as requestingSite
400Requesting company is disabledThe company was disabled in Johari. Ask your administrator
400An error occurred, please try againThe body is not valid JSON. Check your JSON. (data is null here)
413An error occurred, please try againThe body is over 35 MB, usually because of the attached files. Keep the files at or under 25 MB together, which keeps the body under 35 MB. (data is null here)
409A request with this idempotencyKey has already been processedNot a failure: data has the request that was created. See Idempotency
409A request with this idempotencyKey is still being processedWait a few seconds and send it again with the same key
500Failed to upload the files, please try again laterRetry after a short wait with the same idempotencyKey and the same body
500Failed to create request, please try again laterRetry after a short wait with the same idempotencyKey

Get request errors ​

GET /api/requests/:requestID

HTTPmessageCause
404Request not foundWrong ID, the request was raised with a different API key, or it was deleted in Johari

Get file errors ​

GET /api/requests/:requestID/files/:fileID

HTTPmessageCause
404Request not foundWrong request ID, the request was raised with a different API key, or it was deleted in Johari
404File not foundThe request has no file with that fileID. Use a files[].id from Get a Request or a callback

For both, data is null.

Respond to a Return errors ​

POST /api/requests/:requestID/respond-to-return. Checks run in the order below, and you get the first problem found. data holds the same text as message, except where noted.

HTTPmessageFix
400Please provide an idempotencyKey of up to 255 charactersSend a text idempotencyKey, 1 to 255 characters
400This idempotencyKey was already used for a different request. Use a new key for a new requestThe key was already used with a different body, for a different request, or for another call (a create or an upload). Use a new key for each new response. On a retry, send exactly the same body to the same URL. See Idempotency
409A request with this idempotencyKey has already been processedNot a failure: your response was already submitted. data is what that call returned. See Idempotency
409A request with this idempotencyKey is still being processedWait a few seconds and send it again with the same key. (data is null here)
404Request not foundWrong request ID, the request was raised with a different API key, or it was deleted in Johari
403This request was returned to its confirmer (Amina Juma), so only they can respond to itThe request was returned to its approver or confirmer (the message names them), not to your key's user. That person answers it in Johari. Wait for the callback
400Please provide a responsenote is missing, empty or not text. Send your answer as text
400note cannot be longer than 2000 charactersShorten it
400This request is not in returned statusThe request is not returned: it was never returned, or the return was already answered. Check it with Get a Request
403You do not have permissionYour key's user can no longer raise requests at the request's site. Ask your Johari administrator

A body that is not valid JSON gets 400, and a body over 35 MB gets 413, before any of these checks. Both are the same as for creating a request (message An error occurred, please try again, data null).

Add Files errors ​

POST /api/requests/:requestID/files. Checks run in the order below, and you get the first problem found. data holds the same text as message, except where noted.

HTTPmessageFix
400Please provide an idempotencyKey of up to 255 charactersSend a text idempotencyKey, 1 to 255 characters
400This idempotencyKey was already used for a different request. Use a new key for a new requestThe key was already used with a different body, for a different request, or for another call (a create or a response). Use a new key for each new upload. On a retry, send exactly the same body to the same URL. See Idempotency
409A request with this idempotencyKey has already been processedNot a failure: the files were already added. data is what that call returned. See Idempotency
409A request with this idempotencyKey is still being processedWait a few seconds and send it again with the same key. (data is null here)
404Request not foundWrong request ID, the request was raised with a different API key, or it was deleted in Johari
400You can only upload up to 3 filesSend 1 to 3 files. For more, make another call with a new key
400files must be a listSend files as a JSON array
400Any files[0]... messageThe same messages and fixes as for creating a request. The number is the position of the bad file in files, starting at 0
400The attached files cannot be more than 25 MB togetherKeep all files together at or under 25 MB (decoded)
400Please provide a filefiles is missing or an empty list. Send 1 to 3 files
500Failed to upload the files, please try again laterRetry after a short wait with the same idempotencyKey and the same body
403You do not have permissionYour key's user no longer has access to the request's site. Ask your Johari administrator

A body that is not valid JSON gets 400, and a body over 35 MB gets 413, before any of these checks. Both are the same as for creating a request (message An error occurred, please try again, data null).

Server errors ​

HTTPmessageWhat to do
500An error occurred, please try again (data is null)Retry after a short wait. For a POST, use the same idempotencyKey and body, so nothing is done twice. If it keeps happening, contact your Johari administrator

Wrong path ​

A path that does not exist (for example a typo like /api/companys, or /api/requests without an ID) returns:

json
{
  "status": "error",
  "data": null,
  "message": "This endpoint does not exist"
}

with HTTP 404. The signature is checked before the path, so an unsigned call to a wrong path gets the 401 for missing headers instead. If a signed call that works on other paths gets 404, check the path for a typo.

When to retry ​

Network error or timeout ──► retry (same idempotencyKey)
429                      ──► retry after Retry-After seconds (same idempotencyKey)
500                      ──► retry after a short wait (same idempotencyKey)
409 with data null       ──► retry after a few seconds (same idempotencyKey)
409 with data            ──► stop: it worked
400 / 401 / 403 / 404    ──► stop: fix the cause first