GRANSKA

Read one action

GEThttps://api.granska.cloud/v1/actions/:id

Reads one action in full — the brief it was written with, and every section it writes.

Bearer tokenSpends no quota

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.

Request
curl https://api.granska.cloud/v1/actions/action_overklagande \
  -H "Authorization: Bearer $TOKEN"
Response
{
  "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.

ErrorWhen
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.
404
NOT_FOUND
No action with that id, or none this tenant is licensed for. The gateway does not distinguish the two.
500
INTERNAL_ERROR
An unexpected server-side failure. Not probable from outside — reaching it means something is wrong.