GRANSKA

Read one profile

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

Reads one profile in full — every granskare it runs, and every legal source each of those holds.

Bearer tokenSpends no quota

What this answers that the list does not

GET /v1/profiles tells you which audits you may run and which reviewers are inside each one. This tells you what those reviewers contain: the brief each one was written with, every statute provision it enforces, and every legal source it holds — in the same spelling POST /v1/profiles accepts them in.

That is what makes an edit safe. workers on PATCH /v1/profiles/:id replaces the whole set of reviewers, so changing one reviewer's provisions while leaving the others alone means sending the others back unchanged. Read them here, change the one you meant to change, and send the array back.

The list endpoint is untouched and stays cheap. This one reads a record per reviewer, so call it when you need the contents, not to build a picker.

Request
curl https://api.granska.cloud/v1/profiles/lss_utredning \
  -H "Authorization: Bearer $TOKEN"
Response
{
  "profile": {
    "id": "lss_utredning",
    "name": "LSS-utredning",
    "documentType": "LSS",
    "categoryPath": "Funktionsstöd",
    "tenantId": "SYSTEM",
    "status": "PUBLISHED",
    "unlisted": false,
    "jurisdictions": [
      "SE"
    ],
    "asOf": "Rättsläget 2019",
    "userHelpText": "För utredningar om insatser enligt LSS.",
    "urlSlug": "lss-utredning",
    "outputLanguage": "Swedish",
    "updatedAt": "2026-08-04T09:12:44.000Z",
    "workers": [
      {
        "id": "SYSTEM_worker_legal",
        "name": "Rättslig grund",
        "instructions": "Weigh the investigation against the conditions for the measure applied for.",
        "shared": true,
        "legalSourceCount": 3,
        "rules": [
          {
            "jurisdiction": "SE",
            "work": "1993:387",
            "pinpoint": "par_7§"
          },
          {
            "jurisdiction": "SE",
            "work": "1993:387",
            "pinpoint": "par_9a§"
          }
        ],
        "snippetIds": [
          "lss_insatser_allmanna_rad"
        ]
      },
      {
        "id": "SYSTEM_worker_objectivity",
        "name": "Objektivitetsgranskare",
        "instructions": "Look for value-laden wording and conclusions the document does not support.",
        "shared": false,
        "legalSourceCount": 1,
        "rules": [
          {
            "jurisdiction": "SE",
            "work": "2017:900",
            "pinpoint": "kap_5_par_1§"
          }
        ],
        "snippetIds": []
      }
    ]
  }
}

What a reviewer carries

rules are provisions of published law, each { jurisdiction, work, pinpoint }work is which law, pinpoint is where in it, and both are opaque strings we never parse. snippetIds are the keys of authored legal sources, which GET /v1/snippets lists. The two are different kinds of identifier and are never mixed.

legalSourceCount is rules.length + snippetIds.length. It is redundant beside the arrays on purpose: it is the number our own write path caps a reviewer against, so it is what to check your array lengths against after an edit.

instructions is what the reviewer was told to look for — one stored string, written by whoever built the reviewer, here or in our own authoring interface. It is not the prompt the model receives. That prompt is assembled when a job runs, out of these instructions plus the engine's own rules for citation, evidence and output, and none of it is stored or published.

shared means another of your profiles names the same reviewer. Editing it changes what that other profile runs, and nothing in the request or the response will mention it.

id on a reviewer is longer than the profile's own id and carries an organisation prefix. That is not something to trim: send it back exactly as it appears here. The prefix also tells you who owns the reviewer, which decides what sending it back does — see A reviewer we own below.

A reviewer the profile names but that no longer resolves to a record is left out of this response entirely, rather than answered as an empty one. It is left out of the profile too if you send the array back: workers replaces the whole set, so a reference that has gone missing is dropped by the next edit you make.

Sending it back

shared and legalSourceCount are computed for this response — they are not stored on the reviewer and there is nothing to write them to. Drop those two before sending workers to PATCH /v1/profiles/:id; the edit endpoint accepts id, name, instructions, rules and snippetIds, and refuses anything else with a 400 naming the field rather than ignoring it. Everything else comes back in the shape it goes out in, so the edit is: read, drop the two, change the one reviewer you meant to change, send the whole array back.

A reviewer we own

Your own profile may name a reviewer that belongs to us rather than to you — that is what a profile copied from one of ours starts out as, and its id says so: SYSTEM_ and the template prefixes are ours, your own reviewers carry your organisation's id.

Sending one of those back to PATCH /v1/profiles/:id does not edit it, and cannot: other organisations run the same record. What happens instead is that your organisation gets its own copy — written under your prefix, carrying whatever you sent — and the profile is repointed at the copy. The response is 200 and names nothing of this. workers replaces the whole set, so this happens whether you changed that reviewer or merely sent it back untouched alongside the one you did change.

From then on the profile runs your copy and our own updates of the original no longer reach it, which is sometimes exactly what you want and is not something you can undo through this API. Two things to plan for:

  • A profile that was tracking our reviewer stops tracking it. If you want to keep tracking it, do not edit the profile's workers at all — there is no way to send one reviewer and leave the others as references.
  • The copy is weighed as a new reviewer. A reviewer of ours holding more legal sources than your workspace's ceiling allows is fine where it stands, but the copy has nothing stored to be measured against, so the edit can be refused with too-many-legal-sources naming a reviewer you never touched. Either drop sources from it or ask us to raise the ceiling.

The profile's own fields

Everything the list endpoint answers, plus three the list does not carry.

jurisdictions are the legal orders analyses under this profile apply — a set, so a Swedish authority applying GDPR is ["SE", "EU"].

asOf is a label, and only a label. It records that an author deliberately pinned this profile to an older wording of the law — "Rättsläget 2019" — so that a profile which is meant to be historical can be told apart from one that is merely out of date. There is no resolution behind it: nothing computes "the version in force on that date". The field is absent when no author set one.

outputLanguage is the language analyses under this profile answer in, where the profile sets one.

updatedAt is when the owning record was last written, in ISO 8601, and is a caching hint rather than a freshness guarantee. It is absent on records written before we started stamping.

Errors

A profile 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 a profile. Note the divergence from POST /v1/analyze, which answers 403 for an unlicensed profile: the two endpoints on this path — reading and editing — agree with each other instead.

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 profile 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.