Read one action
https://api.granska.cloud/v1/actions/:idReads one action in full — the brief it was written with, and every section it writes.
What this answers that the list does not
GET /v1/actions tells you which actions you may run and what to call them
on a screen. This tells you what one will actually write: the brief the action was written with,
and every section of the document it produces, each with the instructions for that section.
That is the difference between offering an action to your caseworkers and knowing what they will get when they press it. It is also how you read back an action your own organisation authored — the text comes out in the shape it was written in.
The list endpoint is untouched and stays cheap. This one resolves a record, so call it when you need the contents, not to build a picker.
curl https://api.granska.cloud/v1/actions/action_overklagande \
-H "Authorization: Bearer $TOKEN"{
"action": {
"id": "action_overklagande",
"name": "Överklagandeunderlag",
"tenantId": "SYSTEM",
"tagline": "Skriv fram grunderna för ett överklagande",
"description": "Sammanställer utredningens brister till ett underlag för överklagande.",
"category": "Rättsmedel",
"instructions": "Write the grounds for an appeal from the flaws the analysis found. Argue from the provisions the analysis cites and add none of your own.",
"sections": [
{
"key": "grounds",
"label": "Grunder",
"type": "FLAW_MAPPED_LIST",
"instructions": "One ground per flaw, each naming the provision the investigation failed to apply."
},
{
"key": "conclusion",
"label": "Yrkande",
"type": "TEXT",
"variant": "QUOTE",
"instructions": "State what is asked of the appellate body, in one paragraph."
}
],
"fitsProfileIds": [
"lss_utredning"
],
"jurisdictions": [
"SE"
],
"practiceAreas": [
"DISABILITY_SUPPORT"
],
"outputLanguage": "Swedish",
"status": "PUBLISHED",
"sortOrder": 10,
"updatedAt": "2026-08-04T09:12:44.000Z",
"instructionsUpdatedAt": "2026-07-22T11:03:19.000Z"
}
}instructions is not the prompt
instructions is what the action was told to produce — one stored string, written by whoever built
the action, in our own authoring interface. Each entry in sections carries its own instructions,
which is the brief for that one part of the document.
Neither is the prompt the model receives. That prompt is assembled when a job runs, out of these strings plus the engine's own mission, its output schema and its rules for citation and evidence. None of that is stored, so none of it is published here or anywhere else in this API.
What the two fields are good for is judgement rather than reproduction: reading them tells you what an action is aiming at, in enough detail to decide whether it belongs in front of your own staff. Sending the same strings to a model of your own will not reproduce our output, and is not what they are published for.
What a section carries
sections is the document the action writes, in order. Each entry has a key — stable, and how the
generated section is addressed in the result — a label a person reads, and a type:
TEXT— one passage of prose.LIST— a list of points.FLAW_MAPPED_LIST— a list with one entry per flaw the audit found, each tied back to the finding it comes from.
variant is presentation only, where an author set one: how the section is meant to be shown, not
what goes in it.
The action's own fields
tenantId says who owns the record. SYSTEM is ours — an action every organisation licensed for it
runs the same way, and one you cannot edit. Your own id means your organisation authored it.
category and sortOrder are your filing, not the record's. An action of ours that you have
moved into a category of your own reads here exactly as it does in your own menu, and our copy is
untouched by it.
fitsProfileIds is the binding — which audits the action is written for. This endpoint reads it from
the one record the action runs from, so it answers for what the action will actually do.
GET /v1/actions publishes a field of the same name, and the two can
disagree: the list lays your organisation's copy over ours field by field, so a copy that carries no
binding of its own is listed under our binding and is absent here. Where they differ, this endpoint's
answer is the one the action runs with. The field is absent when nobody has decided, which is not the
same answer as "none": an action with no binding is offered for every profile.
jurisdictions and practiceAreas say which legal orders and which fields of practice an action is
written for. Both are absent where nobody has decided, and neither narrows what you may run — they
describe the action rather than gating it.
outputLanguage is the language the action writes in, where the action sets one. Absent, it follows
the profile and then your organisation's own setting.
updatedAt is when the record was last written, in ISO 8601. instructionsUpdatedAt is narrower and
more useful if you cache: it moves when the action changes what it generates, and not when someone
edits a tagline. Both are caching hints rather than freshness guarantees, and both are absent on
records written before we started stamping.
Two things this endpoint deliberately does not answer: who built the record, and whether it is offered for sale. Neither is your organisation's business to read, and the first is nobody's.
Errors
An action your tenant is not licensed for answers 404, exactly as an id that does not exist does —
the two are deliberately indistinguishable, so this endpoint cannot be used to find out whether
another organisation holds an action. Note the divergence from
POST /v1/action, which answers 403 for an unlicensed action: the endpoints on
this path agree with each other instead, exactly as
GET /v1/profiles/:id does.
| Error | When |
|---|---|
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. |
404NOT_FOUND | No action with that id, or none this tenant is licensed for. The gateway does not distinguish the two. |
500INTERNAL_ERROR | An unexpected server-side failure. Not probable from outside — reaching it means something is wrong. |