Seadused ja valitsemine
Riigi Teataja: kehtivad seadused
Seaduste ja määruste ametlikud tekstid koos redaktsioonidega, otse Riigi Teataja liidesest.
Riigi Teataja (riigiteataja.ee) on koht, kus Eesti seadused ja määrused ametlikult avaldatakse, ja selle avalikust liidesest saab akti kehtiva terviktekstiga kätte pealkirja järgi. Sama pealkirja all on tavaliselt rida ajaloolisi redaktsioone, ja kohalikke määrusi on sama nimega mitmel omavalitsusel korraga: koerte ja kasside pidamise eeskirju on 64. Nii et vastust otsides on kaks küsimust, mitte üks: milline redaktsioon täna kehtib ja kes selle andis.
Mida su agent peab oskama
GETPiisab lehe tõmbamisest. Kõik vajalikud päringud on tavalised HTTPS-päringud. Iga agent, kes oskab veebilehe alla laadida, saab selle juhendi läbi teha.
Vastused, mis on siit tulnud
- Kui pika etteteatamisega saab mind koondada?Kui pika etteteatamisega saab tööandja mind koondada, kui olen samas ettevõttes töötanud kuus aastat?2026-08-03
Juhend ise
Inglise keeles, sest seda loeb mudel.
Riigi Teataja Legal Acts
Vetted module (use this first)
riigiteataja.mjs ships in https://kodanikukratt.ee/kratt-agent.tar.gz (plain ESM, Node 22+) and is re-verified against the live source:
import { show, save } from "./kratt.mjs";
import { openAct, findInAct } from "./riigiteataja.mjs";
const act = await openAct("Töölepingu seadus"); // the redaction in force TODAY
show({ id: act.globaalID, title: act.title, status: act.status, from: act.inForceFrom, to: act.inForceTo, url: act.url, note: act.note });
save(`akt_${act.globaalID}.html`, act.html); // ~550 KB — load() it later, never print it whole
// One script, every § the answer will name: the § the question is about AND
// the neighbouring rules the reader acts on. The three terms below are this
// question's (notice periods, compensation, deadline to contest) — pick your
// own, but pick them all up front. Naming a § you never printed is how wrong
// law gets stated.
for (const term of ["etteteatamise", "§ 100", "§ 105"]) {
for (const hit of findInAct(act.html, term, { max: 2 })) {
show(hit.text, 4000); // hit.text IS one whole § — never truncate it
}
}
openAct(title)does the whole in-force dance: PAGED title search → the act that title names → validity window covers today → newest first → confirmaktiStaatus: "KEHTIV"→ fetch the text. A title search returns one row per historical redaction, so never just take the first hit.- The title does not have to be exact. An inflected or reordered one resolves ("koerte ja kasside pidamise eeskirjas Saaremaa vallas" → the act titled "Koerte ja kasside pidamise eeskiri Saaremaa vallas"), so pass the title as the page you read writes it rather than guessing at a base form. When the resolution was not exact the result carries a
notewith the register's own title — cite that one. - It THROWS rather than choosing between acts, and the message says what to send back. Two shapes, both meaning "name it and call again":
- several titles matched — call
openActagain with one of the titles it lists, copied verbatim; - one title, several issuing bodies — 64 municipalities have an act titled exactly "Koerte ja kasside pidamise eeskiri", so pass
openAct(title, {issuer: "Saaremaa Vallavolikogu"}), or ask again with a title that names the place. Which body issued a local regulation IS the answer to a question about one place; reading whichever came first gives the reader another municipality's rules in a form they cannot tell from their own.
- several titles matched — call
- It also THROWS when it cannot confirm which redaction is in force. That is the correct outcome, not a tool failure to work around: say the current text could not be confirmed and link the act. Do NOT fall back to a redaction you found some other way — a superseded act reads exactly like a current one, and the reader cannot tell.
- Two traps this module handles, and any hand-rolled fallback must handle too (issue #85). The search is paged: "Vabariigi Valitsuse seadus" has 227 rows and the redaction in force is on page 3, so a single
limiit=200request does not contain it at all. Andkehtivus.lopp: nulldoes not mean "still in force" — 23 of its 90 exact-title rows carry a null end date with start dates back to 2003, every one superseded. OnlyaktiStaatusfrom the public API decides. findInAct(html, term, {max, window})returns each hit as the whole§it falls in — heading, every lõige, every punkt — so an enumeration can never arrive cut off.termis a phrase ("ülempiir") or a section number ("§ 97"), and the § that DEFINES the term sorts ahead of §§ that merely cite it. Search something specific: "maksimaalne" matches an unrelated elatisabi clause.hit.textis already one complete §, so print it whole —show(hit.text, 4000). Ashow(hit.text, 700)puts the cut back where it was.windowraises the per-§ character cap (default 3000) and cannot lower it, so there is no small number to pass by mistake. Omit it.- Do NOT go back to
html.indexOf("§ 97")+substring(). Act HTML is ~85% hover-menu markup, so a 2500-character slice holds three sentences of law and stops mid-list; that is exactly what produced wrong law before. - If a § is long enough that some parts were held back, the text says so in
[…NOT printed…]. Read the rest before relying on them.
- Also exported:
findCurrentAct(title, {issuer}),actText(globaalID),resolveActTitle(title),searchActs(title),actSections(html). - Why the module exists beyond convenience: the
blob-htmlroute answers HTTP 406 to the helper's default JSONAcceptheader, so a plainget()on it fails. The module sendsAccept: text/html. Re-verified 2026-08-02 on act 236514:Accept: application/json→ 406 with an empty body, whileAccept: text/htmland a request carrying noAcceptheader both → 200 with 380 KB of act HTML. So a hand-rolled fetch that sets no header will not reproduce the failure, and this claim is only ever true of a caller that asks for JSON.
claim the `blob-html` route answers **HTTP 406** to the helper's default JSON `Accept` header
# The act id is fetched rather than written down, and it is checked to resolve
# BEFORE the refusal is asserted: this route negotiates the media type before it
# looks the act up, so a withdrawn id answers 406 to JSON and 404 to HTML
# (measured 2026-08-02) and would satisfy every line below while proving nothing
# about the header.
GET https://www.riigiteataja.ee/api/oigusakt_otsing/1/otsi?leht=1&limiit=1&pealkiri=riigieelarve
expect-status 200
capture akt /"globaalID"\s*:\s*"?(\d+)/
GET https://www.riigiteataja.ee/public-api/api/v1/akt/{{akt}}/blob-html
header Accept: text/html
expect-status 200
GET https://www.riigiteataja.ee/public-api/api/v1/akt/{{akt}}/blob-html
header Accept: application/json
expect-status 406
# The control is that same served act asking for HTML: the only difference from
# the step above is the one header the claim is about.
control GET https://www.riigiteataja.ee/public-api/api/v1/akt/{{akt}}/blob-html
header Accept: text/html
Grounding rule: state a rule, number, deadline or exception ONLY if it is in text you printed in this conversation. Three failures this is written from, all observed:
- Do not finish a list. Printed points 1)–3) of § 97 lg 2? Point 4) is not yours to state. Print the § again if the output was cut.
- Do not reach into a § you did not fetch. Naming the citizen's options is required of you, and it is exactly where memory creeps in: an answer on notice periods asserted § 100 and § 105 without fetching either, and put the redundancy payment at two months' average wage where § 100 lg 1 says one. This covers anything you STATE, not only what you cite by § number — "the general deadline for contesting it is 30 days" is the same claim with the citation removed.
- Fetch them. Every § the answer will touch goes in the SAME script as the § the question is about:
for (const t of ["§ 97", "§ 100", "§ 105"]). It costs no extra step. - Did not fetch it? Then name the topic and link the act, without a number. "Töölepingu seadus reguleerib ka koondamishüvitist ja ülesütlemise vaidlustamise tähtaega — täpsed tingimused on aktis." That satisfies the obligation to say what the reader can do, and invents nothing. It is always the better answer than a plausible figure.
- Fetch them. Every § the answer will touch goes in the SAME script as the § the question is about:
- Do not restate a value from memory once you have the right §. § 40 of Perehüvitiste seadus was located and answered "kolmekordne"; the fetched text says "kahekordne".
If a § could not be retrieved, say so and link the act. An honest gap is worth more than a fluent paragraph the reader cannot check.
The endpoints below are the fallback if a module call throws.
Access
Public JSON search API and linked XML act texts. No authentication.
Retrieve
Start with a bounded search:
GET https://www.riigiteataja.ee/api/oigusakt_otsing/1/otsi?leht=1&limiit=25&pealkiri=riigieelarve
Useful query parameters include leht (page), limiit (page size), and pealkiri (title text). Add filters from the Riigi Teataja search UI only after confirming their exact URL names.
The response contains:
staatusandparing;metaandmed.kokku,metaandmed.leht, andmetaandmed.limiit;aktid, includingglobaalID,terviktekstID,pealkiri,liik,valjaandja,kehtivus,staatus, and relativeurl.
Resolve a returned url, such as /akt/22451.xml, against https://www.riigiteataja.ee to retrieve the official act representation.
Return
Preserve legal IDs, title, type, issuer, validity start/end, publication status, text type, act URL, search parameters, page metadata, and retrieval time. Clearly distinguish current and historical versions.
Limits
- A broad unfiltered search is valid but returns historical as well as current acts.
- Legal validity must be read from the returned version metadata, not inferred from search order.
- Do not rewrite relative act URLs incorrectly; resolve them against the Riigi Teataja origin.
- When citing an act to the user, link a URL built only from fetched identifiers:
https://www.riigiteataja.ee/akt/{globaalID}or the search row's relativeurl(drop the.xml) resolved against the origin. Never type an abbreviation-style URL (/akt/VÕS) from memory: the abbreviation lives in the fetchedlyhendfield and must be copied verbatim if used at all — ASCII-folding it (VÕS → VOS) produces a dead link that still returns HTTP 200, because the SPA shell answers every path and fails only client-side.
Verify
Require HTTP 200 JSON, staatus: OK, integer pagination metadata, and a parseable aktid array. At least one returned act must contain globaalID, pealkiri, kehtivus, and url before reporting success.
Contract drift warning (verified 2026-07-24)
Resolving act representations like /akt/{id}.xml against https://www.riigiteataja.ee now returns the HTML application shell (SPA), NOT the act text — regardless of Accept header. Do not treat that HTML as act content, and do NOT recite paragraph (§) text from memory as if it were fetched.
Working text endpoints (verified 2026-07-24):
GET https://www.riigiteataja.ee/public-api/api/v1/akt/{globaalID}— act metadata JSON.GET https://www.riigiteataja.ee/public-api/api/v1/akt/{globaalID}/blob-html— full consolidated act HTML. Responses can exceed 500 KB and the endpoint ignores Range headers, so a truncating fetch returns only the head of the act. Filter it where you fetched it rather than reading it whole; if you cannot, take the head as all you have — cite the paragraphs it actually contains, and for anything past the cut point say so and link the act instead of reciting it from memory.
If those endpoints are unavailable, ground answers in the search API's metadata only (title, validity, status, act URL), summarize at that level, and link the act for the user to read the text.
EU origin: EUR-Lex
For a law transposing EU law, the seletuskiri (via riigikogu-open-data draft texts) cites a CELEX number, e.g. 32019L1937. A plain server-side fetch of eur-lex.europa.eu gets an empty HTTP 202 (WAF challenge) — fetch https://publications.europa.eu/resource/celex/{CELEX} with headers Accept: application/xhtml+xml and Accept-Language: et, both mandatory (verified 2026-07-27; ~450 KB XHTML of the full Estonian text — filter it down before reading, never take it whole). Mandatory means the request fails silently without them: the same URL sent with no Accept answers HTTP 200 with 11.7 MB of application/rdf+xml describing the act — its dates, languages and identifiers, not a word of its text (verified 2026-08-03 with node). Cite for the reader: https://eur-lex.europa.eu/legal-content/ET/TXT/?uri=CELEX:{CELEX}.
Module API
openAct(title, {issuer}) -> {globaalID, title, issuer, inForceFrom, inForceTo, status, url, html, note} findInAct(html, term, {max, window}) -> [{paragraph, heading, text}]