GRANSKA

Publish an example document

POSThttps://api.granska.cloud/v1/examples

Publishes an example document — the document and the audit produced from it — against one of your own profiles.

Bearer tokenSpends no quota

Who can read what you publish here

Read this paragraph before you send the first request. An example you publish is stored against your organisation and is readable by every user in it — not only whoever published it, and not only administrators. It is not readable by any other organisation, and it is not reachable without an account. MANI's platform staff can read it only while they hold a time-limited staff membership of your organisation, and every such membership is recorded.

Shared examples — the ones that appear for every organisation on the platform — are a different thing, and you cannot publish one; see "It belongs to your organisation" below.

So what you are deciding is what your own colleagues may see. An example is a demonstration rather than a record: publish fictitious material, or material your organisation is content to circulate internally. Do not publish a real case unless it has been anonymised: an example is kept until you delete it. There is no per-user restriction on an example, and no way to hide one from part of your organisation.

If you publish something you should not have, DELETE /v1/examples/:exampleId removes it. It cannot un-read it.

Request
curl -X POST https://api.granska.cloud/v1/examples \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "LSS — avslag med bristande motivering",
    "profileId": "lss_utredning",
    "pdfBase64": "JVBERi0xLjQKJcfsj6IK…",
    "jsonOutput": { "identifier": "job_b2e08a", "workers": {}, "reducer": {} },
    "contentConfirmation": "fictitious-or-anonymised",
    "uploadAttestation": { "agreementVersion": "legal:2", "uploadAuthorised": true }
  }'
Response
{
  "success": true,
  "exampleId": "tenant_9f3a_example_7QpL2vRk8mTx",
  "profileId": "lss_utredning",
  "tenantId": "tenant_9f3a"
}

What an example is made of

An example is one document together with the audit that was produced from it, stored so that both can be shown again without running anything:

  • pdfBase64 — the document itself, base64-encoded and inline.
  • jsonOutput — the stored audit. This is exactly what GET /v1/jobs/:jobId hands back as result.diagnostics for a run that asked for it, so the workflow is: analyse, read the job, publish what you got.

A run only carries diagnostics if you asked for them. Send includeDiagnostics: true to POST /v1/analyze, then take result.diagnostics off the finished job and send it here unchanged.

Publishing costs you nothing

This endpoint runs no analysis and spends no part of your quota. You already paid for the run that produced the audit; storing a copy of it is free.

The reviewers' instructions are removed on the way in

Whatever your request carries, the stored jsonOutput keeps no system_prompt and no user_prompt under workers. Everything else survives untouched — each reviewer's output, its loaded_rules, the reducer, the identifiers — so an example you publish replays exactly as one of ours does.

The reason is the first section of this page. A stored example is readable by everyone in your organisation, and a shared example by every organisation on the platform, so the prompts in a capture would reach a far wider audience than whoever assembled them — and neither of us can tell from here what a prompt in a request you assembled yourself contains. What we can tell is that nothing that reads an example needs them. If you send them anyway, they are dropped rather than refused: the request still answers 201.

It belongs to your organisation, and only you can attach it

The example is stored against the organisation your token belongs to. You cannot send tenantId — that is a 400 — and there is therefore no way to publish a shared example, the kind that appears for every organisation on the platform. Those are ours to publish.

profileId must name a profile you can see: one of your own, or a shared one. A profile belonging to another organisation answers 404, the same as an id that does not exist, and nothing is stored.

The document has a ceiling

pdfBase64 must be under 700 000 characters — roughly a 500 KB PDF. Above that the request is refused with the size it measured, because the document and the whole stored audit share one database record and that record has a hard limit. The same ceiling applies to the web application, so this is not an API-only restriction.

If your document is larger, the way through is to publish a shorter extract of it. An example is a demonstration rather than an archive.

Request

ParameterDescription
name
string·body·required
What the example is called in the pickers that offer it.
profileId
string·body·required
The profile the example is published against. One of your own or a shared one; a profile belonging to another organisation is a 404.
pdfBase64
string·body·required
The document itself, base64-encoded and inline. Under 700 000 characters, which is the ceiling the browser is held to as well.
jsonOutput
object·body·required
The stored audit the example replays — the diagnostics GET /v1/jobs/:jobId returns for a run that asked for them.Stored without each worker's system_prompt and user_prompt, whatever the request carries: an example document is readable by anyone, so a prompt sent here would be published.
contentConfirmation
"fictitious-or-anonymised"·body·required
Your confirmation that the document is fictitious or anonymised. An example is kept until it is deleted and read by everyone in your organisation, so it must not contain a real person's information. Separate from uploadAttestation: neither satisfies the other.
uploadAttestation
{ agreementVersion: string, uploadAuthorised: true }·body
Your confirmation, for this one document, that you are authorised to submit it under the current upload agreement. Send it only once the person or system you act for has confirmed it. Required once the agreement is enforced, together with your organisation's acceptance, which an administrator gives in the GRANSKA app. It never replaces contentConfirmation.

Errors

Nothing is stored unless the whole request is accepted: every refusal below happens before the write.

ErrorWhen
400
BAD_REQUEST
A required field is missing, contentConfirmation is not "fictitious-or-anonymised", a field the server owns was sent (id, tenantId, createdAt, updatedAt), a field this route does not accept was sent at all, pdfBase64 is 700 000 characters or longer, or uploadAttestation is not { agreementVersion, uploadAuthorised: true } naming a published version of the agreement — uploadAuthorised: false included — and details.reason is INVALID_ATTESTATION.
401
UNAUTHORIZED
The Authorization header is missing, is not a readable bearer token, or names no tenant.
401
TOKEN_EXPIRED
The access token was issued by this gateway and has since expired. Not probed: it needs a token older than its own lifetime.
403
FORBIDDEN
The workspace has no configuration, or the upload agreement is enforced and the API client belongs to a workspace that is not a customer organisation, so nobody can accept for it, and details.reason is INTEGRATION_OUTSIDE_ORGANISATION. Not probed: it needs such a client.
404
NOT_FOUND
The profileId names no profile this organisation can see — its own or a shared one. Not probed: a bearer probe would have to name a profile that cannot exist, and the same request is what the 400 above already proves.
409
LEGAL_AGREEMENT_REQUIRED
The upload agreement is enforced and this document does not meet it: the organisation has not accepted the current version, or the request carries no uploadAttestation. details.missing names which; an administrator accepts for the organisation in the GRANSKA app, never through this API. Refused before an upload URL is issued, a document is fetched or a run is spent. Not probed: it needs the agreement enforced.
409
STALE_AGREEMENT_VERSION
uploadAttestation names an earlier published version of the agreement. details.currentVersion is the one to read and attest. Not probed: it needs a second published version.
500
INTERNAL_ERROR
An unexpected server-side failure. Not probable from outside — reaching it means something is wrong.
503
SERVICE_UNAVAILABLE
The upload agreement could not be checked; details.reason is AGREEMENT_UNAVAILABLE and a Retry-After header says when to try again. Never an answer about acceptance. Not probed: it needs an injected infrastructure failure.

Send it without writing a client

If your organisation already has an account, an administrator can publish an example from the API tester at /admin/api-tester — and can do the same thing through the ordinary admin panel, which runs the analysis for you.