Download a job's PDF
https://api.granska.cloud/v1/jobs/:jobId/result.pdfDownloads a completed job's report or generated document as the same PDF the app exports.
What you get
A completed analysis job answers with the analysis report; a completed action job answers with the generated action document. It is the same document a user gets from the download button in the app: the same layout, the same fonts and the same wording, built from the same result and from your workspace's configuration — the review profile names that head each angle of a review package, and the section layout of the action. Only the file metadata, such as the creation time, differs between two downloads.
The response is Content-Type: application/pdf with a Content-Disposition filename made only of
letters, digits, -, _ and ., so it is safe to write to disk as it stands. Save the body as
binary; it is not JSON.
This is our document about the file you sent, not that file. The PDF you uploaded is never returned.
What it does not do
It renders, it does not store. The PDF is built from the job's result when you ask for it and
streamed back. It is not written to a database, a bucket or a cache, and the response carries
Cache-Control: no-store.
It does not keep the job alive. Downloading reads the job exactly as
reading a job does and changes nothing on it, so it does not reset the retention
clock. The retention sweep deletes the job and its stored result once it has not changed in the last
fifteen minutes, whether or not you have downloaded, and from then on this route answers 404 like
every other route that names the job — there is nothing left to render from. If you need the document
later, save the file.
It costs no run. Rendering is not an analysis, and this route draws nothing from your quota. You can download the same job as often as you like until it is swept.
curl https://api.granska.cloud/v1/jobs/job_7d41c9/result.pdf \
-H "Authorization: Bearer $TOKEN" \
--fail --output report.pdfHTTP/1.1 200 OK
Content-Type: application/pdf
Content-Disposition: attachment; filename="analysis-job_7d41c9.pdf"
Cache-Control: no-store
%PDF-1.3 … (the report, as binary PDF)The legal trace, on request
Add ?includeTrace=true and a completed analysis's PDF ends with its legal trace: for each review
in the run, the legal sources its findings cite and the sources its reviewers were supplied,
the citations that do not match the sources supplied to that review, and the sources the strengths
cite. It is the same appendix, in the same place after the report, that the app adds to its export
when the trace is switched on, and it is labelled in the analysis's own language.
curl "https://api.granska.cloud/v1/jobs/job_7d41c9/result.pdf?includeTrace=true" -H "Authorization: Bearer $TOKEN" --fail --output report-with-trace.pdf
Only the literal true turns it on. Leave the parameter out, or send false, and you get the
document described above, unchanged, even when the run has a trace. Any other value — 1, TRUE,
an empty value — or the parameter sent twice answers 400 BAD_REQUEST. The check comes after the job
is found and shown to be yours, so a 404 or 403 comes first.
Traces exist for analyses only. An action job asked for its trace answers 409 CONFLICT; ask
again without includeTrace, or with false, for its document.
No trace means 409, never a PDF without one. An analysis whose run has no valid trace — one
that finished before traces existed, or whose stored trace does not pass validation — answers
409 CONFLICT instead of a document. A file without the appendix would look like a trace that
cited nothing. Reading the job with includeTrace=true then reports its
result.analysisTrace as null.
Supplied means given to the reviewer, not relied upon. A source listed as supplied was loaded into what that reviewer was shown. It is not a statement that the assessment rested on it, and the appendix keeps it apart from the sources the report cites.
Nothing else changes: the trace is read from the job's own stored result, the document is rendered
on request and not stored, the response carries Cache-Control: no-store, the download draws
nothing from your quota and does not keep the job alive, and the retention sweep removes the trace
with the rest of the job.
Request
| Parameter | Description |
|---|---|
jobIdstring·path·required | The id POST /v1/analyze or POST /v1/action returned, once GET /v1/jobs/:jobId reports it COMPLETED. |
includeTrace"true" | "false"·query | Set to "true" to append the analysis's legal trace: the same appendix, after the report, that the app's export adds when the trace is switched on. Omitted or "false", the PDF is unchanged. An action job, or an analysis whose run has no valid trace, answers 409. Any other value, or the parameter sent twice, answers 400. |
Errors
Every refusal is the ordinary JSON error envelope, never a partial PDF — check the status before
saving the body. With curl --fail, as in the example, a refusal leaves no file behind.
A job still QUEUED or IN_PROGRESS, and a job that FAILED, has no document and answers
409 CONFLICT. Poll the job until it is COMPLETED. With
includeTrace=true, an action job and an analysis without a valid trace also answer 409, and an
includeTrace other than true or false, or sent twice, answers 400. A job your credential
did not start answers 403: another organisation's job, one another API client of yours started, or
one a person started in the web application or the API tester. So does an action job whose action
your workspace is no longer licensed for, because the action's layout is what the document is built
from. A job that never existed, or
has already been swept, answers 404.
| Error | When |
|---|---|
400BAD_REQUEST | includeTrace is something other than "true" or "false", or is sent more than once. Checked after the job is found and shown to be yours, so a 404 or 403 comes first. Not probed: it needs a job the probing credential started. |
401UNAUTHORIZED | The Authorization header is missing, is not a readable bearer token, or names no tenant. |
401TOKEN_EXPIRED | The access token was issued by this gateway and has since expired. Not probed: it needs a token older than its own lifetime. |
403FORBIDDEN | The job exists but your credential did not start it (another organisation's job, one another API client of yours started, or one a person started in the web application or the API tester), or it is an action job whose action is no longer licensed for this tenant, so its layout cannot be resolved. Not probed: it needs a job started by a second credential. |
404NOT_FOUND | No job with that id, or the retention sweep has already removed it. Downloading the PDF does not delete the job and does not keep it alive; `sweepStaleData` removes it once it has been untouched for 15 minutes, and nothing is kept to render from after that. |
409CONFLICT | The job has no document to render: it is still QUEUED or IN_PROGRESS, or it FAILED. Poll GET /v1/jobs/:jobId until COMPLETED. Also when includeTrace=true names an action job, since legal traces exist for analyses only, or an analysis whose run has no valid legal trace, rather than a PDF without one. Not probed: it needs a job that is mid-flight or failed, or one the probing credential started. |
500INTERNAL_ERROR | An unexpected server-side failure. Not probable from outside — reaching it means something is wrong. |