Skip to content

Get a File ​

Returns a short-lived download link for one file on a request you raised. Use it to download:

  • the files you sent when you created the request,
  • 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.

Endpoint ​

MethodURL
GET/api/requests/:requestID/files/:fileID
Path parameterDescription
requestIDThe request's requestID
fileIDA files[].id from Get a Request or from a callback

Sign it like any GET, with an empty body:

<timestamp>.GET./api/requests/6650f1a2b3c4d5e6f7a8b9e1/files/0b6f2c1e-8d4a-4f3b-9e7c-5a1d2b3c4e5f.

You can only see requests raised with your API key, and their files.

Response ​

data on success:

FieldTypeDescription
idstringThe file's ID
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
urlstringA direct download link. Valid for 5 minutes
expiresAtstringWhen url stops working (ISO 8601, UTC)

Downloading the file ​

url is a pre-signed storage link. Download it with a plain GET straight away:

  • Send no Johari headers: no x-api-key, x-timestamp or x-signature. Do not sign it.
  • It works for 5 minutes, until expiresAt.
  • Do not store url. Ask this endpoint for a fresh link each time you need the file.

Example ​

bash
REQUEST_ID="6650f1a2b3c4d5e6f7a8b9e1"
FILE_ID="0b6f2c1e-8d4a-4f3b-9e7c-5a1d2b3c4e5f"
TS=$(date +%s)
SIG=$(printf '%s' "$TS.GET./api/requests/$REQUEST_ID/files/$FILE_ID." \
  | openssl dgst -sha256 -hmac "$SIGNING_SECRET" \
  | sed 's/^.* //')
curl "$BASE_URL/api/requests/$REQUEST_ID/files/$FILE_ID" \
  -H "x-api-key: $API_KEY" \
  -H "x-timestamp: $TS" \
  -H "x-signature: $SIG"

# Then download the link from data.url, with no Johari headers:
# curl -o invoice-1042.pdf "<data.url>"
js
const fs = require("fs");

const requestID = "6650f1a2b3c4d5e6f7a8b9e1";
const fileID = "0b6f2c1e-8d4a-4f3b-9e7c-5a1d2b3c4e5f";

// callJohari is from the Signing page
const result = await callJohari("GET", `/api/requests/${requestID}/files/${fileID}`);

if (result.httpStatus === 200) {
  // A plain GET, with no Johari headers
  const download = await fetch(result.data.url);
  fs.writeFileSync(result.data.name, Buffer.from(await download.arrayBuffer()));
} else {
  console.error(result.httpStatus, result.message);
}
python
import requests  # pip install requests

request_id = "6650f1a2b3c4d5e6f7a8b9e1"
file_id = "0b6f2c1e-8d4a-4f3b-9e7c-5a1d2b3c4e5f"

# call_johari is from the Signing page
result = call_johari("GET", f"/api/requests/{request_id}/files/{file_id}")

if result["httpStatus"] == 200:
    # A plain GET, with no Johari headers
    download = requests.get(result["data"]["url"])
    with open(result["data"]["name"], "wb") as f:
        f.write(download.content)
else:
    print(result["httpStatus"], result["message"])

Response 200:

json
{
  "status": "success",
  "data": {
    "id": "0b6f2c1e-8d4a-4f3b-9e7c-5a1d2b3c4e5f",
    "name": "invoice-1042.pdf",
    "type": "application/pdf",
    "size": 48213,
    "url": "https://storage.example.com/...",
    "expiresAt": "2026-10-30T12:05:00.000Z"
  },
  "message": "File link created successfully"
}

The url above is shortened. Use the whole value you receive, exactly as it is.

Response 404, request not found:

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

You get Request not found when the request ID is wrong, when the request was raised with a different API key, or when it was deleted in Johari.

Response 404, file not found:

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

You get File not found when the request exists but has no file with that fileID.

The usual 401 errors apply too. See Errors.

Common mistakes ​

Sending Johari headers to the download link

url is a pre-signed storage link, not a Johari API call. Download it with a plain GET. Do not sign it, and send no x-api-key, x-timestamp or x-signature headers. Only the call to /api/requests/:requestID/files/:fileID is signed.

Storing the link

The link stops working after 5 minutes. Store the fileID, and ask for a fresh link each time.