| search_authoritiesA | Search public bodies on fragdenstaat.de. GREEN: no auth, no side effects. Args:
query: free-text search, e.g. a town or an authority name.
jurisdiction: optional filter — numeric id, slug ("rheinland-pfalz") or
name ("Rheinland-Pfalz"). The API itself only accepts the id.
limit: maximum number of results (server page size is 50).
|
| get_authorityA | Full record for one public body, including its laws. GREEN. The API does not expose ``default_law``, so the law the REST API would apply is
recomputed here the way froide's ``get_applicable_law()`` does it
(``order_by('-meta', '-priority')``). That default is almost always the *meta* law,
which is why the API cannot be used to file under a specific act.
Args:
id: numeric public body id.
|
| get_lawC | One freedom-of-information act with its deadline rules. GREEN. Args:
id: numeric law id, e.g. from ``get_authority(...)["laws"]``.
|
| check_jurisdictionA | Which authority covers a given place? GREEN — returns the evidence trail. Resolves ``/georegion/?name=<place>``, walks its ``part_of`` chain upwards, and asks
``/publicbody/?regions=<id>`` level by level, from the most specific outwards. It
stops at the **first level that yields any authority**, because that is the body
actually responsible; the remaining, wider levels are reported by id only.
This matters in states such as Rhineland-Palatinate, where an *Ortsgemeinde* is often
not listed on fragdenstaat.de at all while the *Verbandsgemeindeverwaltung* that runs
its administration is.
Args:
place_name: name of the municipality, district or state.
include_wider: also list the authorities of the wider levels. Off by default —
at country level that is thousands of bodies and tells you nothing.
|
| list_my_requestsA | Your own FOI requests. YELLOW: needs a token (scope read:request), read only. Args:
status: optional filter, e.g. "awaiting_response" or "resolved".
limit: maximum number of requests to return.
|
| get_requestB | One FOI request in detail. YELLOW (public requests work without a token). Args:
id: numeric request id.
|
| get_messagesB | All messages of a request, oldest first. YELLOW. Args:
request_id: numeric request id.
|
| list_attachmentsB | Attachments belonging to one message. YELLOW. Args:
message_id: numeric message id (from get_messages).
|
| download_attachmentA | Download one attachment into a local directory. YELLOW: reading only. The directory has to exist already: this tool will not create a path, because
``target_dir`` is a model-chosen argument and the file name comes from the API, and
together they were enough to drop a file into e.g. ~/.config/autostart/. Set
``FDS_MCP_DOWNLOAD_DIR`` to confine downloads to one directory.
Args:
attachment_id: numeric attachment id (from list_attachments).
target_dir: an existing local directory.
|
| check_deadlinesA | Your open requests whose statutory deadline has passed. YELLOW. froide exposes no "deadline expired" flag; it is computed from ``due_date`` against
the current time, exactly as the frontend does.
|
| build_reply_draftA | Prepare a follow-up message to an authority. YELLOW: reads the API, sends nothing. The counterpart of ``build_submit_url`` for a request that already exists. It looks
the request up, validates the text against the rules that apply to a follow-up, and
hands back the finished text plus the URL of the form. **Pressing send stays with
you**, and there is no tool argument that changes that: replying is not possible
through the API at all. Measured 2026-09-05 (tests/test_api_contract.py):
``POST /api/v1/message/`` refuses ``kind: "email"``, and the web view at
``/anfrage/<slug>/send/message/`` answers 302 to the login page whether or not a
bearer token is attached.
A follow-up is validated differently from a request. froide does **not** frame it:
the textarea arrives prefilled with a salutation, the placeholder U+2026 and a
closing formula, and exactly what stands in it is what the authority receives. So
R19 requires a salutation and a closing formula — the inverse of R10 — R04 rejects
the placeholder that is sitting in the form right now, R06 keeps e-mail addresses and
IBANs out of a public thread, and the subject is capped at 230 characters.
Args:
request_id: numeric id of your existing request.
text: the complete message, salutation and closing formula included.
subject: reply subject. Defaults to "AW: <title> [#<id>]".
path: optional path to a ``.yaml`` file to write the draft to. Needed only if you
intend to use ``send_reply_via_browser`` later; the file is written with
``status: draft`` and a placeholder confirmation token.
|
| create_request_draftA | Write a local YAML draft. RED tier, but performs NO network traffic at all. The file starts in status ``draft`` with a placeholder confirmation token. A human
has to read the text, set ``status: approved`` and replace ``confirmation_token``
before anything can be submitted.
Args:
path: where to write the YAML file.
subject: request subject, 8-230 characters.
body: the request text, at most 5000 characters.
publicbody_id: recipient authority id.
law_wunsch_id: the law you want to file under.
law_api_default_id: the law the REST API would apply — see get_authority().
law_wunsch_law_type: law_type of the desired law, e.g. "IFG" or "UIG".
publicbody_name: authority name, for the L01 cross-check.
publicbody_email: authority e-mail, for the L01 cross-check.
ermittelt_ueber: how responsibility was established (a URL or a sentence).
public: whether the request will be public. Public means CC0 and visible to all.
full_text: True sends your text verbatim, False lets froide frame it.
submit_via: "web_form" (human presses send) or "api" (immediate dispatch).
dry_run: True (default) returns the draft without writing the file.
|
| validate_draftA | Run the rule set against a draft. RED tier. Offline rules R01-R16 always run. The live rules L01-L04 (authority exists, law is
offered, API default law, duplicate check) need the network and therefore only run
with ``dry_run=False``.
Args:
path: path to the draft YAML file.
dry_run: True (default) runs offline rules only and does not touch the file.
|
| build_submit_urlA | Build the prefilled web form URL — the recommended way out. RED tier. This is the only route on which the legal basis can actually be chosen
(``?law_type=...``); the REST API's MakeRequestSerializer has no such field. Sending
stays with the human.
fragdenstaat.de answers GET URLs above roughly 4096 bytes with HTTP 400 (measured
2026-09-05). Longer drafts therefore get a two-step answer: a short URL that
prefills subject and law_type, plus the body in a sidecar text file.
Args:
path: path to the draft YAML file.
dry_run: True (default) does not write the sidecar .body.txt file.
|
| submit_requestA | Actually POST the request to fragdenstaat.de. RED tier — IRREVERSIBLE. ``POST /api/v1/request/`` sends the e-mail to the authority immediately. There is no
draft, no preview and no undo. Five gates therefore have to agree, in this order:
1. the draft's status is ``approved`` (a human set it),
2. no ERROR finding is open,
3. ``law.wunsch_id == law.api_default_id`` — the API cannot set law_type, so a
mismatch would file the request under the wrong act,
4. ``confirmation_token`` matches the value a human wrote into the draft file,
5. the local throttle ledger says another submission stays inside
5/5min, 6/6h, 10/24h, 20/7d.
Args:
path: path to the approved draft YAML file.
confirmation_token: must equal the ``confirmation_token`` inside the draft file.
dry_run: True (default) runs every gate and reports, but sends nothing.
|