{
  "openapi": "3.0.3",
  "info": {
    "title": "ProofSnap API",
    "version": "2.0.0",
    "license": {
      "name": "Proprietary",
      "url": "https://getproofsnap.com/terms.html"
    },
    "description": "De ProofSnap API laadt een webpagina in een Chromium-browser aan de serverzijde en retourneert een bewijspakket (ZIP).\n\nElk pakket van een vastlegging bevat de screenshot, de HTML van de pagina, de geëxtraheerde paginatekst, metadata.json, forensic_log.json, chain_of_custody.json, evidence.pdf en een manifest.json met de SHA-256 van elk bestand. manifest.json wordt ondertekend met een RSA-4096-sleutel die voor die vastlegging is gegenereerd (de publieke sleutel wordt meegeleverd als publickey.pem, de privésleutel wordt verwijderd) en de SHA-256 van manifest.json wordt ingediend bij OpenTimestamps-calendars voor een verankering in Bitcoin (manifest.json.ots). Als het verzoek aan de calendar mislukt, wordt het pakket zonder het bewijs geleverd en vermeldt manifest.json het niet, zodat de offline scripts en de verifier het als ‘niet opgenomen’ melden en niet als ‘ontbrekend’. Met `include_eidas` op een live sleutel krijgt dezelfde hash ook een gekwalificeerde eIDAS-tijdstempel (RFC 3161, manifest.json.tsr). JPEG-screenshots worden waar mogelijk ondertekend met C2PA Content Credentials: als het ondertekenen mislukt, wordt de vastlegging zonder Content Credentials voltooid. De map verification/ bevat scripts die het pakket offline controleren. Elk manifest.json vermeldt `ots_listed_only_when_present: true` (manifest.json.ots wordt alleen vermeld wanneer het in het pakket zit) en `eidas_marker_version: 1`. Het vermeldt `eidas_requested: true` alleen wanneer het pakket met zijn gekwalificeerde tijdstempel wordt geleverd. Als het token niet kan worden verkregen, wordt het manifest zonder token opnieuw verzegeld. Een pakket dat `eidas_requested: true` vermeldt maar geen manifest.json.tsr bevat, is zijn token dus kwijtgeraakt, en de offline scripts melden FAIL. chain_of_custody.json vermeldt `bitcoin_ots_included` en noemt in `page_modifications.items[]` elke wijziging die vóór de vastlegging aan de pagina is aangebracht. manifest.json bevat sealed_at (wanneer het is samengesteld, nadat alle bestanden zijn gehasht) en de via SNTP gemeten klokafwijkingen van de vastleggingsserver; chain_of_custody.json vermeldt de stappen van de vastlegging in de volgorde waarin ze plaatsvonden, elk met het tijdstip waarop de stap eindigde, en sluit af met custody_record_written (het hashen, de handtekening en de tijdstempels komen daarna en worden bevestigd door manifest.json en de bewijzen).\n\nmetadata.json vermeldt:\n\n- `browser_identity`: de user agent en Client Hints die daadwerkelijk zijn verzonden.\n- `dns`: een onafhankelijke DNS-controle van het adres waarmee de browser verbinding maakte, met de antwoorden van dns.google en cloudflare-dns.com en de uitkomst: confirmed, not_confirmed, resolvers_unavailable, connected_ip_unknown, skipped_ip_literal (de URL bevat een IP-adres, dus er is geen hostnaam om op te zoeken) of skipped_internal_host (een interne hostnaam, die nooit naar de openbare resolvers wordt gestuurd).\n- `url_at_load` en `url_at_screenshot`: het adres direct nadat de pagina was geladen en op het moment dat de afbeelding werd gemaakt (een pagina kan haar eigen adres wijzigen, meestal het deel na #).\n- `redirect_chain`: de redirects van het hoofddocument in chronologische volgorde, elk met de HTTP-status. Het opgevraagde en het uiteindelijke adres worden volledig weergegeven; bij elke tussenliggende stap zijn de querywaarden vervangen door [redacted] (`query_values_redacted`).\n- `tls.source`: waar elk TLS-veld vandaan komt, `browser_connection` of `browser_connection_and_separate_handshake`, het laatste met de `at`, `ip` en `port` van de afzonderlijke handshake die certificate_chain.pem na het laden van de pagina heeft opgehaald.\n\nWanneer een gevraagde stap niet is uitgevoerd (een eIDAS-tijdstempel, C2PA-ondertekening, het archiefverzoek, WHOIS, de tweede klokmeting, een WARC die door de tijdslimiet is afgekapt, of een wachttijd of het uitklappen van inhoud die vanwege de tijdslimiet zijn overgeslagen), vermeldt evidence.pdf dit met een korte reden en vermeldt het herkomstcertificaat de stap in één regel.\n\nVastleggingen zijn asynchroon. POST /captures geeft 202 terug met een capture_id; poll GET /captures/{capture_id} of geef `options.webhook_url` mee. Voor een voltooide vastlegging bevat het antwoord een vooraf ondertekende package_url die maximaal 24 uur geldig is (zie package_url_expires_at); haal de vastlegging opnieuw op voor een nieuwe link.\n\nDe paden hieronder zijn relatief ten opzichte van de server-URL, die de /v1-stage al bevat. `poll_path`-waarden die de API retourneert, bevatten /v1.",
    "contact": {
      "email": "support@getproofsnap.com"
    }
  },
  "servers": [
    {
      "url": "https://api.getproofsnap.com/v1",
      "description": "ProofSnap API (EU)"
    }
  ],
  "security": [
    {
      "ApiKeyAuth": []
    }
  ],
  "tags": [
    {
      "name": "Vastleggingen",
      "description": "Vastleggingen aanmaken, weergeven, ophalen, verwijderen en verifiëren.\n\n**Rate limits.** Voor `POST /captures`, `POST /captures/batch`, `POST /compare`, `POST /captures/{capture_id}/redact` en `POST /certify-hash` geldt een rate limit (snelheidslimiet) per account in vaste vensters van 60 seconden: alle API-sleutels van het account en de dashboardsessie ervan delen één venster (extra sleutels verhogen de limiet niet). Een batch of een certificering van meerdere hashes verbruikt één eenheid per URL of hash. Standaardlimieten per venster per abonnement:\n\n| Abonnement | Verzoeken per 60 s |\n| --- | --- |\n| sandbox | 10 |\n| developer | 60 |\n| startup | 120 |\n| growth | 300 |\n| custom | 1200 |\n| legal_solo | 60 |\n| legal_seats | 120 |\n| website_archive | 60 |\n| archive_only | 30 |\n| elk ander abonnement | 30 |\n\nDe volgende verzoeken vallen ook onder hetzelfde venster en verbruiken één eenheid per verzoek, tenzij anders vermeld: `POST /captures/{capture_id}/verify`, `PATCH /captures/{capture_id}` met action legal_hold, `GET /captures/hosts` (alleen wanneer de lijst opnieuw wordt berekend, niet wanneer een gecachet resultaat wordt geretourneerd), `POST /legal-holds` (één eenheid per 10 vastleggingen), `POST /captures/export-jobs`, en `POST /imports`, `POST /imports/{import_id}/complete` en `GET /imports/{import_id}?upload=true`.\n\nWanneer de limiet wordt overschreden, retourneert de API 429 RATE_LIMITED met de headers `Retry-After`, `X-RateLimit-Limit`, `X-RateLimit-Remaining` en `X-RateLimit-Reset`, plus `error.details.retry_after_seconds`. Een verzoek dat meer eenheden nodig heeft dan één venster toestaat (een certificering van meerdere hashes die groter is dan de limiet per venster), kan nooit slagen en retourneert 400 RATE_LIMIT_COST_TOO_HIGH met `error.details` `{ max_per_window, window_seconds, requested }`. Splits het op in kleinere verzoeken. Een geweigerd verzoek verbruikt geen eenheden, en de headers geven het werkelijk resterende budget weer.\n\n**Maandelijkse limieten.** Vastleggingen, vergelijkingen, weglakkingen en certificeringen met live sleutels tellen mee voor een maandelijks maximum per account (UTC-kalendermaand). Het antwoord op een geaccepteerd verzoek bevat de headers `X-Quota-*`. Wanneer het maximum is bereikt, retourneert de API 402 QUOTA_EXCEEDED met `error.details` `{ reason, used, limit, max_limit, cap, org_member }`, waarbij `cap` de waarde `plan_max`, `customer`, `member_owner` of `org` heeft. Zolang de betaling van een live abonnement achterstallig is of uitblijft, retourneren factureerbare verzoeken 402 PAYMENT_REQUIRED; voor zo'n account worden losse vastleggingen en batches met deze fout geweigerd, ongeacht de sleutelomgeving, dus ook bij testsleutels.\n\n**Lopende vastleggingen en testmodus.** Elk account kan slechts een beperkt aantal vastleggingen tegelijk uitvoeren (developer 10, startup 25, growth 100, legal_solo en legal_seats 25, custom 200, website_archive 25, sandbox 5). Boven die limiet retourneert de API 429 CONCURRENT_CAPTURE_LIMIT; een batch start de URL's die binnen de limiet vallen en vermeldt de overige in `not_admitted` (elk met `code` CONCURRENT_CAPTURE_LIMIT, plus `in_flight_limit`), dus verstuur die later opnieuw. Gebruik in testmodus (een testsleutel, of een sessie zonder gekoppelde facturering) is beperkt tot 200 eenheden per account per UTC-dag (429 TEST_MODE_DAILY_LIMIT; headers `X-Test-Daily-*`), en een vastlegging in testmodus wordt nooit naar het Internet Archive gestuurd (`external_archive.skip_reason` \"test_mode\")."
    },
    {
      "name": "Vergelijken en weglakken",
      "description": "Leid een nieuw verzegeld pakket af van bestaande vastleggingen: een vergelijking van twee vastleggingen, of een weggelakte kopie van één vastlegging."
    },
    {
      "name": "Hashcertificering",
      "description": "Certificeer SHA-256-hashes van bestanden die u zelf bewaart. Alleen de hashes worden verstuurd; de bestanden bereiken ProofSnap nooit."
    },
    {
      "name": "Geplande vastleggingen",
      "description": "Terugkerende vastleggingen (interval of cron) en wijzigingsbewaking (on_change). Elke uitvoering die een vastlegging maakt, is een normale vastlegging: die wordt meegeteld voor de facturering en voor de maandelijkse limieten."
    },
    {
      "name": "Zaken",
      "description": "Werkruimten voor juridische zaken met een bewijsketenlogboek als hashketen. Beschikbaar bij de Legal-abonnementen (legal_solo, legal_seats); andere abonnementen krijgen 403 MATTERS_NOT_ENABLED, met twee uitzonderingen. De eigenaar van een bedrijf waarvan het abonnement is beëindigd, kan de zaken van dat bedrijf nog weergeven, openen en exporteren tot de datum waarop de gegevens van het bedrijf worden verwijderd (data_delete_after); deze toegang is alleen-lezen plus export, en GET /matters voegt dan read_only: true toe. Een account met het Archive only-abonnement kan zijn zaken weergeven, openen en exporteren. Alleen de eigenaar van de zaak kan de zaak hernoemen, archiveren, leden beheren, verzegelen, delen of exporteren. De eigenaar van de zaak is degene die haar heeft aangemaakt. Bij een bedrijfszaak wordt de eigenaar van het bedrijf pas eigenaar van de zaak nadat de aanmaker het bedrijf heeft verlaten. Een bedrijfseigenaar die als lid is toegevoegd, heeft alleen de rechten van een lid. Leden krijgen voor die bewerkingen 403 FORBIDDEN. Elke schrijfbewerking op een zaak (aanmaken, PATCH, vastleggingen toewijzen, delen en het intrekken daarvan, verzegelen, exporteren) vereist een bevestigd e-mailadres (403 EMAIL_NOT_VERIFIED, voor een dashboardsessie en voor een API-sleutel van een account waarvan het adres niet is bevestigd) en wordt voor een live aanroeper (live API-sleutel, of een sessie met gekoppelde facturering) geweigerd met 402 PAYMENT_REQUIRED zolang de betaling van het abonnement (of dat van het bedrijf) achterstallig is of uitblijft. Bij een gearchiveerde zaak worden hernoemen, nieuwe leden, nieuwe vastleggingen, delen en exporteren geweigerd (409 MATTER_ARCHIVED) totdat zij is heropend; een aanroeper die haar niet kan heropenen (de eigenaar van een beëindigd bedrijf, een Archive only-account) kan haar nog wel exporteren."
    },
    {
      "name": "Webhooks",
      "description": "ProofSnap stuurt met POST een JSON-gebeurtenis naar de `webhook_url` die u meegeeft bij een vastlegging, certificering, weglakking of schema. De URL moet https gebruiken, mag geen inloggegevens bevatten (`user:password@`, 400 INVALID_URL) en mag niet verwijzen naar een privé-, loopback-, link-local-, carrier-grade-NAT-, multicast-, gereserveerd of cloudmetadata-adres, ook niet in IPv6-vormen waarin zo'n IPv4-adres is ingebed. Dit wordt gecontroleerd wanneer het verzoek wordt geaccepteerd (422 SSRF_BLOCKED) en opnieuw bij elke aflevering: de hostnaam wordt opgezocht, elk adres wordt gecontroleerd en de aflevering maakt alleen verbinding met een adres dat de controle heeft doorstaan, zodat een host die later naar een geblokkeerd adres verwijst niet wordt benaderd. Als de hostnaam niet kan worden opgezocht, wordt de aflevering niet verzonden. Op elke route wordt een lege webhook_url, of een webhook_url met alleen witruimte, behandeld alsof er geen webhook is opgegeven. /compare verstuurt geen webhooks.\n\n**Accountabonnementen.** In plaats van (of naast) een webhook_url per verzoek kunt u met `POST /webhook-subscriptions` een endpoint aanmelden voor accountgebeurtenissen: elke gebeurtenis van het account die aan de filters van het abonnement voldoet, gaat dan naar dat endpoint, ongeacht welke sleutel, welk schema of welke dashboardsessie haar veroorzaakte. Elk webhookabonnement heeft een eigen secret (eenmalig teruggegeven bij het aanmaken), noemt de gebeurtenistypen die het wil ontvangen en kan filteren op `host` of `schedule_id`; een live abonnement ontvangt alleen gebeurtenissen van live vastleggingen en een testabonnement alleen gebeurtenissen van testvastleggingen. Een API-sleutel toont en wijzigt alleen de abonnementen van zijn eigen omgeving; de dashboardsessie ziet ze allemaal. De afleveringen ervan bevatten `subscription_id` op het hoogste niveau en geen vooraf ondertekende link (package_url, package_url_expires_at, upgraded_proof_url en compare.package_url worden verwijderd; monitor.change_detected bevat compare.compare_id): haal het pakket op met GET /captures/{capture_id}. Vastleggingsgebeurtenissen bevatten ook `capture_id` op het hoogste niveau. De webhook-id van een abonnementsaflevering wordt afgeleid van het abonnement, de vastlegging (of het schema) en de gebeurtenis. De afleverregels en nieuwe pogingen hieronder zijn van toepassing. Daarnaast schakelt een 410-antwoord het abonnement direct uit (disabled_reason gone), schakelen afleveringen die 5 dagen lang blijven mislukken (met minstens één volledige cyclus van mislukte nieuwe pogingen) het uit (failing), en schakelt het intrekken van de API-sleutel die het heeft aangemaakt het uit (key_revoked); PATCH status \"active\" activeert het opnieuw. Elke gebeurtenis wordt bovendien 30 dagen bewaard in `GET /events`.\n\n**Headers**\n\nElke aflevering wordt ondertekend volgens de specificatie [Standard Webhooks](https://www.standardwebhooks.com/), zodat elke officiële Standard Webhooks-bibliotheek haar kan verifiëren:\n\n| Header | Waarde |\n| --- | --- |\n| `Content-Type` | `application/json` |\n| `webhook-id` | `msg_<delivery id>`: gelijk voor dezelfde gebeurtenis van dezelfde vastlegging, zodat een dubbele aflevering dezelfde id heeft. Gebruik deze om te ontdubbelen: verwerk elke webhook-id één keer. `GET /webhook-events/{capture_id}` geeft dezelfde id zonder het voorvoegsel `msg_` terug als `delivery_id` |\n| `webhook-timestamp` | Unix-tijd in seconden waarop de aflevering is verzonden. Valt onder de handtekening |\n| `webhook-signature` | `v1,<base64>`: HMAC-SHA256 over `<webhook-id>.<webhook-timestamp>.<raw body>`. Tijdens een rotatie van het webhook-secret bevat deze header twee door een spatie gescheiden waarden, `v1,<new> v1,<previous>`; accepteer de aflevering als een van beide overeenkomt |\n| `User-Agent` | `ProofSnap-Webhook/2.0` |\n\n**Een aflevering verifiëren.** Verifieer de handtekening over precies de bytes die u hebt ontvangen, vóór het parsen. Weiger een aflevering waarvan de webhook-timestamp meer dan 5 minuten afwijkt van uw klok (de officiële bibliotheken doen dit) en ontdubbel op webhook-id. De ondertekeningssleutel is het deel van het secret na `whsec_`, base64-gedecodeerd. `webhook_secret` is `whsec_` gevolgd door standaard base64, de vorm die de officiële bibliotheken verwachten, dus geef het ongewijzigd door.\n\n```js\n// npm install standardwebhooks\nimport { Webhook } from \"standardwebhooks\";\nconst wh = new Webhook(process.env.PROOFSNAP_WEBHOOK_SECRET); // whsec_... (webhook_secret)\n// rawBody: the request body as received (string or Buffer), headers: the request headers\nconst event = wh.verify(rawBody, headers); // throws on a bad signature or a timestamp outside 5 minutes\n```\n\nZonder bibliotheek (Node):\n\n```js\nconst key = Buffer.from(secret.slice(\"whsec_\".length), \"base64\");\nconst signed = `${headers[\"webhook-id\"]}.${headers[\"webhook-timestamp\"]}.${rawBody}`;\nconst expected = crypto.createHmac(\"sha256\", key).update(signed).digest(\"base64\");\nconst ok = headers[\"webhook-signature\"].split(\" \").some((part) => {\n  const [version, sig] = part.split(\",\");\n  return version === \"v1\" && sig && sig.length === expected.length\n    && crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));\n});\n// and: Math.abs(Date.now() / 1000 - Number(headers[\"webhook-timestamp\"])) <= 300\n```\n\n**Webhook-secret.** Elke API-sleutel heeft een eigen secret (`whsec_...`). Het wordt één keer getoond, wanneer u de sleutel in het dashboard onder ‘Ontwikkelaars’ aanmaakt (het veld `webhook_secret` in het antwoord waarmee de sleutel wordt aangemaakt). Het kan later niet opnieuw worden opgevraagd: het sleuteloverzicht geeft het nooit terug. Als u het kwijt bent, of als het mogelijk is uitgelekt, maakt u in het dashboard onder ‘Ontwikkelaars’ een nieuw secret aan (‘Nieuw webhook-secret’ bij de sleutel). Ook het nieuwe secret wordt maar één keer getoond. **Overlap bij rotatie:** gedurende 24 uur na een rotatie (tot `previous_secret_expires_at` in het rotatieantwoord) wordt elke aflevering ondertekend met zowel het nieuwe als het vorige secret (twee `v1,`-waarden in webhook-signature), zodat u uw endpoint kunt omzetten naar het nieuwe secret zonder afleveringen te missen. Een tweede rotatie terwijl die overlap nog loopt, wordt geweigerd met 409 WEBHOOK_SECRET_ROTATION_PENDING en `error.details.previous_secret_expires_at` (wanneer u weer kunt roteren), zodat er nooit meer dan twee secrets tegelijk actief zijn en een secret waarop ontvangers mogelijk nog vertrouwen nooit voortijdig wordt ingetrokken. Is een secret uitgelekt, trek dan de sleutel in en maak een nieuwe aan. Het intrekken van de sleutel verwijdert beide. Afleveringen gebruiken de secrets van de sleutel op het moment van verzenden, ook afleveringen voor werk dat vóór een rotatie is gestart. Een verzoek vanuit een ingelogde dashboardsessie heeft geen API-sleutel en dus geen webhook-secret, zodat een `webhook_url` in zo'n verzoek wordt geweigerd met 400 `WEBHOOK_REQUIRES_API_KEY` (vastleggingen, batches, weglakkingen, hashcertificeringen, het aanmaken of bewerken van een geplande vastlegging). Geplande uitvoeringen gebruiken het secret van de sleutel die het schema heeft aangemaakt. Als de sleutel is ingetrokken, of het secret ervan op het moment van aflevering niet kan worden gelezen, wordt de gebeurtenis niet verzonden en wordt de poging met de reden gelogd (lees deze met `GET /webhook-events/{capture_id}`).\n\n**Aflevering.** Elke poging heeft een time-out van 10 seconden. Redirects worden nooit gevolgd: een 3xx-antwoord telt als mislukking, net als een antwoord buiten 2xx of een time-out. De body van het antwoord wordt niet gelezen. **Nieuwe pogingen:** een aflevering die een time-out krijgt, geen verbinding kan maken of een antwoord buiten 2xx ontvangt (behalve 410 Gone), wordt ongeveer 5 minuten, 30 minuten, 2 uur, 8 uur en 24 uur later opnieuw verzonden (pogingen 2 tot en met 6, in totaal ongeveer 35 uur), telkens met dezelfde `webhook-id` en een nieuwe `webhook-timestamp`. Daarna wordt de aflevering opgegeven. Een opnieuw verzonden aflevering bevat dezelfde payload zonder de downloadlink van 24 uur (`package_url`, `package_url_expires_at`): haal het pakket op met GET /captures/{capture_id}. Afleveringen voor een vastlegging die inmiddels definitief is verwijderd, vervallen. Een tijdelijke fout bij het lezen van het ondertekeningssecret aan onze kant wordt op dezelfde manier opnieuw geprobeerd. Een 410 Gone, een redirect, een geblokkeerd adres of een ontbrekende of ingetrokken sleutel wordt niet opnieuw geprobeerd. De body van een nieuwe poging kan afwijken van de eerste poging (geen downloadlink): ontdubbel op webhook-id, niet op een hash van de body. Antwoord snel met 2xx. Het pogingenlogboek registreert een algemene foutklasse (connection_failed, timeout, blocked_address, redirect_not_followed, http_<status>), nooit de onbewerkte netwerkfout. certify.completed en redaction.completed worden asynchroon verzonden, kort nadat het antwoord is teruggegeven. Elke poging wordt gelogd; lees het logboek met `GET /webhook-events/{capture_id}`.\n\n**Gebeurtenissen**\n\n`capture.completed`: een vastlegging (los, batchitem of uitvoering van een interval- of cronschema) is voltooid. Webhookabonnementen van het account ontvangen deze gebeurtenis ook voor de basis- en wijzigingsvastleggingen van een on_change-schema (de eigen webhook_url van het schema krijgt daarvoor monitor.change_detected). `capture` heeft dezelfde velden als het antwoord van `GET /captures/{capture_id}` voor een voltooide vastlegging, plus `timestamps.eidas` wanneer een gekwalificeerde tijdstempel is toegevoegd, `schedule_id` (null buiten een schema), `test_mode` en `completed_at`.\n\n```json\n{ \"event\": \"capture.completed\",\n  \"capture\": { \"capture_id\": \"cap_...\", \"status\": \"completed\", \"url\": \"https://example.com\", \"schedule_id\": null, \"test_mode\": false, \"captured_at\": \"...\", \"completed_at\": \"...\",\n    \"package_url\": \"https://...\", \"package_url_expires_at\": \"...\", \"verification_url\": \"https://...\",\n    \"manifest_hash\": \"sha256:...\", \"timestamps\": { \"bitcoin_ots\": { \"status\": \"pending_confirmation\", \"block_height\": null } },\n    \"c2pa\": { \"included\": true, \"signed_screenshot_sha256\": \"sha256:...\" }, \"file_count\": 21, \"size_bytes\": 1234567, \"metadata\": {} } }\n```\n\n`capture.failed`: de vastlegging kon niet worden voltooid. `error.message` is een korte openbare reden (bij een onverwachte fout \"The capture could not be completed.\"); interne foutdetails worden nooit verzonden. Dit geldt ook voor een uitvoering die is vastgelopen of een time-out kreeg: die wordt door een opruimronde ongeveer 30 minuten na de start als mislukt gemarkeerd, en dan wordt capture.failed verzonden (bericht \"Capture did not finish (timed out).\"). Dezelfde opruimronde verzendt certify.failed of redaction.failed voor een hashcertificering of weglakking die niet is voltooid. Een uitvoering die klaar is nadat ze als mislukt is gemeld, wordt niet gefactureerd en verzendt geen capture.completed.\n\n```json\n{ \"event\": \"capture.failed\",\n  \"capture\": { \"capture_id\": \"cap_...\", \"status\": \"failed\", \"url\": \"https://example.com\", \"schedule_id\": null, \"test_mode\": false, \"failed_at\": \"...\",\n    \"error\": { \"code\": \"CAPTURE_FAILED\", \"message\": \"...\" }, \"metadata\": {} } }\n```\n\n`certify.completed`: een hashcertificering is voltooid. `certificate` heeft dezelfde velden als het synchrone antwoord van `POST /certify-hash`. Deze gebeurtenis wordt asynchroon afgeleverd, kort nadat het antwoord is teruggegeven.\n\n`certify.failed`: een hashcertificering kon niet worden voltooid.\n\n```json\n{ \"event\": \"certify.failed\",\n  \"certificate\": { \"certify_id\": \"cert_...\", \"status\": \"failed\", \"failed_at\": \"...\",\n    \"error\": { \"code\": \"CERTIFY_FAILED\", \"message\": \"The certification could not be completed. Retry the request.\" } } }\n```\n\n`redaction.completed`: een weglakking is voltooid. `redaction` heeft dezelfde velden als het antwoord van `POST /captures/{capture_id}/redact`.\n\n`redaction.failed`: een weglakking kon niet worden voltooid. `error.code` is de code die het verzoek zou hebben teruggegeven (bijvoorbeeld REDACTION_LEAK_DETECTED), met `details` als die er zijn; een weglakking die de opruimronde als mislukt heeft gemarkeerd, bevat REDACTION_FAILED.\n\n```json\n{ \"event\": \"redaction.failed\",\n  \"redaction\": { \"redaction_id\": \"red_...\", \"status\": \"failed\", \"redacted_from\": \"cap_...\", \"failed_at\": \"...\",\n    \"error\": { \"code\": \"REDACTION_LEAK_DETECTED\", \"message\": \"...\", \"details\": { \"files\": [\"page.html\"], \"rule_indexes\": [0], \"metadata_fields\": [], \"in_capture_url\": false } } } }\n```\n\n`monitor.change_detected`: een `on_change`-schema heeft vastgesteld dat de paginatekst is gewijzigd en heeft een nieuwe vastlegging gemaakt.\n\n```json\n{ \"event\": \"monitor.change_detected\", \"schedule_id\": \"sch_...\",\n  \"before_capture_id\": \"cap_...\", \"after_capture_id\": \"cap_...\", \"url\": \"https://example.com\",\n  \"compare\": { \"package_url\": \"https://...\", \"diff_summary\": { } },\n  \"diff_status\": \"ok\", \"verdict\": \"changed\", \"text_verdict\": \"changed\", \"images_verdict\": \"unchanged\", \"metadata_verdict\": \"unchanged\",\n  \"detected_at\": \"...\", \"test_mode\": false }\n```\n\n`verdict`, `text_verdict`, `images_verdict` en `metadata_verdict` (sinds 14 oktober 2026) geven de resultaten per categorie weer van de vergelijking in compare.diff_summary (null zonder vergelijking). Een schema met `alert_on: \"text\"` verzendt monitor.change_detected alleen als text_verdict changed is (of als er geen vergelijkingsresultaat is).\n\n`capture.anchor_confirmed` (alleen via abonnementen en GET /events): de Bitcoin-verankering van het OpenTimestamps-bewijs van het pakket is bevestigd, meestal enkele uren na de vastlegging.\n\n```json\n{ \"event\": \"capture.anchor_confirmed\", \"capture_id\": \"cap_...\", \"schedule_id\": null, \"url\": \"https://example.com\",\n  \"confirmed_at\": \"...\", \"bitcoin_block_height\": 968549, \"upgraded_proof_available\": true, \"test_mode\": false }\n```\n\n`schedule.paused` (alleen via abonnementen en GET /events): ProofSnap heeft een schema gepauzeerd. `reason` is `no_billing` (geen API-toegang), `plan_downgrade` (het abonnement staat minder schema's toe, geen on_change of geen nieuw werk), `failures` (vijf mislukte uitvoeringen op rij), `key_revoked` of `invalid_cron`. Een pauze die u zelf instelt, is geen gebeurtenis.\n\n```json\n{ \"event\": \"schedule.paused\", \"schedule_id\": \"sch_...\", \"url\": \"https://example.com\", \"reason\": \"failures\",\n  \"paused_at\": \"...\", \"test_mode\": false }\n```\n\n`diff_status` is `ok` (compare bevat de vergelijking), `no_baseline` (geen eerdere vastlegging om mee te vergelijken) of `failed` (de vergelijking is mislukt; compare is null)."
    },
    {
      "name": "Openbaar",
      "description": "Bewerkingen waarvoor geen API-sleutel nodig is."
    },
    {
      "name": "Pakketten importeren",
      "description": "Importeer bestaande ProofSnap-bewijspakketten (ZIP-bestanden gemaakt door de ProofSnap-extensie: webvastleggingen, sessies met meerdere tabbladen, enkele en gecombineerde bestandscertificeringen; of door deze API: vastleggingen, certificeringen, vergelijkingen, weglakkingen) in uw account, zodat ze worden behandeld als uw API-vastleggingen: 10 jaar bewaard en beschikbaar voor legal hold, zaken en exports.\n\nAlleen Legal-abonnementen (legal_solo, legal_seats), met een live API-sleutel of een dashboardsessie met gekoppelde facturering (een testsleutel, een sessie zonder facturering en de sandbox krijgen 403 IMPORT_NOT_ALLOWED). Bij een niet-bevestigd e-mailadres volgt 403 EMAIL_NOT_VERIFIED, bij een betalingsachterstand 402 PAYMENT_REQUIRED. Een import is geen vastlegging: ze telt niet mee voor de maandelijkse limieten en kent geen kosten per import; alleen de opgeslagen gegevens worden gefactureerd.\n\n**Werkwijze.** 1. `POST /imports` met de bestandsnaam en de exacte grootte in bytes (maximaal 2 GiB): het antwoord bevat één vooraf ondertekende URL per deel van 32 MiB (het laatste deel is kleiner), 1 uur geldig. 2. Upload de bytes van elk deel met PUT naar de bijbehorende URL en bewaar van elk deel de antwoordheader `ETag`. 3. `POST /imports/{import_id}/complete` met elk deelnummer en de bijbehorende ETag. 4. Poll `GET /imports/{import_id}` totdat de status definitief is.\n\n**Statussen.** `uploading` (wacht op de uploads van de delen en de aanroep van complete; een upload die niet binnen 24 uur is voltooid, wordt `expired`), `verifying` (het pakket wordt gecontroleerd), `accepted` (het is geverifieerd: `capture_id` is de nieuwe vastlegging, met dezelfde id als de import), `rejected` (het is niet geverifieerd: `reasons` somt de codes op; alleen het importrecord wordt bewaard), `duplicate` (uw account bevat al een vastlegging met hetzelfde manifest: `duplicate_of`), `expired`.\n\n**Verificatie.** Strikt: de RSA-handtekening over manifest.json, elk vermeld bestand aanwezig met de vermelde SHA-256 (en SHA-512 en grootte waar vermeld), geen bestanden die het manifest niet vermeldt (behalve bestanden die het besturingssysteem toevoegt, zoals .DS_Store, en de van het manifest afgeleide bewijzen), minstens één gehasht bestand, een consistente bewijs-ID, de hashketen van het forensische logboek opnieuw berekend, het OpenTimestamps-bewijs, getoetst aan het manifest (offline), en de gekwalificeerde eIDAS-tijdstempel, getoetst aan de vastgepinde Disig-root met de validatiegegevens in het pakket. De ZIP-structuur wordt gecontroleerd voordat er iets wordt gelezen (geen versleuteling, geen ZIP64, geen links, veilige namen, geen dubbele namen, groottes en CRC-32 van elk item). Een testpakket van de API (met watermerk) wordt geweigerd. API-pakketten die zijn verzegeld voordat evidence.pdf werd gehasht, worden geaccepteerd; dat bestand wordt dan vermeld in `verification.checks.files.unhashed`.\n\n**Niveaus** (`summary`): `integrity` (verified of failed), `time_anchor` (eidas_qualified, eidas_qualified_revocation_unknown, ots_bitcoin_attested_unconfirmed, ots_pending, none) en `provenance`: voor API-pakketten `issued_by_proofsnap` (het manifest staat in het uitgifteregister van de API) of `unverified`; voor extensiepakketten `registered_by_extension_account` (een aangemelde gebruiker van de extensie heeft dat manifest bij het aanmaken geregistreerd) of `not_registered`; `unknown` als het opzoeken niet kon worden uitgevoerd. Het niveau `provenance` laat een import nooit mislukken.\n\n**De vastlegging.** Een geaccepteerde import is een vastlegging met `source` \"import\": ze is te downloaden (de geüploade ZIP byte voor byte) en te verifiëren (POST /captures/{capture_id}/verify voert dezelfde controles opnieuw uit), kan onder legal hold worden geplaatst, naar de prullenbak worden verplaatst en definitief worden verwijderd, verschijnt in zaken (bij een import met `matter_id` registreert het bewijsketenlogboek van de zaak `package.imported`) en in exports, en wordt 10 jaar vanaf de import bewaard. Haar `captured_at` is het tijdstip dat het pakket zelf opgeeft (`import.captured_at_basis` \"package_claim\"), nooit een tijdstip dat ProofSnap heeft waargenomen; `created_at` is het moment waarop de upload is aangemaakt. Vergelijkingen en weglakkingen van een geïmporteerd pakket worden geweigerd (422 IMPORT_NOT_SUPPORTED).\n\n**Limieten.** Maximaal 3 imports in behandeling (uploading of verifying) per account (409 IMPORT_LIMIT), 50 aangemaakte imports per account per UTC-dag (429 RATE_LIMITED, `details.action` \"import_create\"; elk aanmaakverzoek telt mee, ook als het later wordt geweigerd), en de rate limit per minuut van het abonnement op create en complete. Importvolume: maximaal 5 GiB per UTC-dag en 20 GiB per UTC-maand aan opgegeven pakketgroottes per account, geteld bij create (en bij complete voor een oudere upload); daarboven 429 IMPORT_DAILY_BYTES_LIMIT of IMPORT_MONTHLY_BYTES_LIMIT met `Retry-After`; een geannuleerde upload telt niet mee."
    }
  ],
  "paths": {
    "/captures": {
      "post": {
        "tags": [
          "Vastleggingen"
        ],
        "operationId": "createCapture",
        "summary": "Een URL vastleggen",
        "description": "Accepteert de vastlegging en geeft direct 202 terug; de vastlegging wordt op de achtergrond uitgevoerd. Poll `GET /captures/{capture_id}` of geef `options.webhook_url` mee. De hostnaam van de URL wordt opgezocht en gecontroleerd voordat de vastlegging wordt geaccepteerd.\n\nRate limit (snelheidslimiet) per account in vensters van 60 seconden (alle API-sleutels van het account en de bijbehorende dashboardsessie delen één venster). Zodra de limiter is uitgevoerd, bevatten antwoorden van deze route de headers `RateLimit-Policy` en `RateLimit` (gebaseerd op IETF draft-ietf-httpapi-ratelimit-headers) plus de oudere `X-RateLimit-Limit`, `X-RateLimit-Remaining` en `X-RateLimit-Reset`. Een 429 voegt `Retry-After` toe.",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CaptureRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Geaccepteerd. Een idempotente herhaling van een eerder verzoek met dezelfde Idempotency-Key geeft ook 202 terug, met `Idempotent-Replay: true` en de oorspronkelijke body.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotent-Replay": {
                "$ref": "#/components/headers/IdempotentReplay"
              },
              "X-Quota-Limit": {
                "$ref": "#/components/headers/QuotaLimit"
              },
              "X-Quota-Remaining": {
                "$ref": "#/components/headers/QuotaRemaining"
              },
              "X-Quota-Used": {
                "$ref": "#/components/headers/QuotaUsed"
              },
              "X-Quota-Cap": {
                "$ref": "#/components/headers/QuotaCap"
              },
              "X-Quota-Org-Limit": {
                "$ref": "#/components/headers/QuotaOrgLimit"
              },
              "X-Quota-Org-Used": {
                "$ref": "#/components/headers/QuotaOrgUsed"
              },
              "X-Sandbox-Limit": {
                "$ref": "#/components/headers/SandboxLimit"
              },
              "X-Sandbox-Remaining": {
                "$ref": "#/components/headers/SandboxRemaining"
              },
              "X-Sandbox-Used": {
                "$ref": "#/components/headers/SandboxUsed"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CaptureAccepted"
                },
                "example": {
                  "capture_id": "cap_a1B2c3D4e5F6g7H8",
                  "status": "processing",
                  "url": "https://example.com",
                  "poll_path": "/v1/captures/cap_a1B2c3D4e5F6g7H8",
                  "message": "Capture accepted. Poll GET /v1/captures/{capture_id} (or use webhook_url) for completion.",
                  "metadata": {}
                }
              }
            }
          },
          "400": {
            "description": "MALFORMED_REQUEST, MISSING_FIELD, INVALID_URL, INVALID_TIMESTAMP_TYPE, INVALID_VIEWPORT, METADATA_TOO_LARGE of INVALID_EVIDENCE_PACK_OPTIONS. WEBHOOK_REQUIRES_API_KEY: webhook_url verzonden vanuit een dashboardsessie (webhooks vereisen een API-sleutel).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "QUOTA_EXCEEDED (maandelijks maximum of sandboxtegoed; bevat de headers X-Quota-* of X-Sandbox-*) of PAYMENT_REQUIRED.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "403": {
            "description": "EIDAS_REQUIRES_LIVE_KEY: include_eidas met een testsleutel (een ps_live_-sleutel van een account zonder gekoppeld factureringsaccount draait als testsleutel). EMAIL_NOT_VERIFIED: een dashboardsessie waarvan het e-mailadres niet is bevestigd. PLAN_ARCHIVE_ONLY: het abonnement bewaart alleen het archief en maakt geen nieuw werk aan. PLAN_API_READ_ONLY: een API-sleutel van een Website Archive-account (de sleutel is alleen-lezen; gebruik het dashboard). PLAN_API_READ_ONLY met details.reason \"session_outside_dashboard\": een dashboardsessie van een Website Archive-account waarvan het verzoek geen Origin-header heeft, of een Origin-header die geen ProofSnap-dashboardadres is (nieuw werk op dat abonnement wordt alleen in het dashboard aangemaakt; een zachte drempel, geen beveiligingsgrens).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "409": {
            "description": "IDEMPOTENCY_IN_PROGRESS.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "422": {
            "description": "SSRF_BLOCKED, TARGET_UNREACHABLE of IDEMPOTENCY_KEY_REUSED.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED: te veel verzoeken in het huidige venster van 60 seconden. `error.details.retry_after_seconds` herhaalt Retry-After. RateLimit meldt r=0 en het aantal seconden tot het venster opnieuw begint. Ook RATE_LIMITED, voor een dashboardsessie van Website Archive: het daglimiet van 100 eenheden per UTC-dag, gedeeld door losse vastleggingen en batchvastleggingen (één eenheid per URL), compare, redact en certify-hash (één eenheid per verzoek); een verzoek dat niet past, wordt in zijn geheel geweigerd (details.daily_limit, details.remaining, details.action \"wa_session_captures\"; Retry-After en details.retry_after_seconds geven het aantal seconden tot de volgende UTC-dag, en de RateLimit- en X-RateLimit-*-headers per minuut ontbreken). Een korte weigering met alleen details.retry_after_seconds en Retry-After: 1 betekent een gelijktijdige update van de teller: probeer het opnieuw. Bij een weigering die niet aan de aanroeper ligt, worden de eenheden teruggegeven; werk dat met 202 is geaccepteerd en later mislukt, behoudt zijn eenheid. Een zachte drempel, geen beveiligingsgrens. Een losse vastlegging waarvan de Idempotency-Key uit de opslag wordt bediend (een replay, 409 of 422), wordt vóór het daglimiet afgehandeld en kost geen eenheid.\n\nCONCURRENT_CAPTURE_LIMIT: er lopen te veel vastleggingen van het account tegelijk (developer 10, startup 25, growth 100, legal_solo en legal_seats 25, custom 200, website_archive 25, sandbox 5); `details.limit`, `details.in_flight`, `details.requested` en Retry-After geven aan wanneer u het opnieuw kunt proberen.\n\nTEST_MODE_DAILY_LIMIT: werk in testmodus (een testsleutel, of een sessie zonder gekoppelde facturering) is beperkt tot 200 eenheden per account en UTC-dag; `details.used`, `details.limit`, `details.reset_at`, de `X-Test-Daily-*`-headers en Retry-After.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "RATE_LIMITED",
                    "message": "Too many requests. See Retry-After header.",
                    "details": {
                      "retry_after_seconds": 42
                    }
                  },
                  "request_id": "..."
                }
              }
            }
          },
          "500": {
            "description": "CAPTURE_FAILED (de vastlegging kon niet worden gestart; details.capture_id) of INTERNAL_ERROR.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/EidasOrAuthUnavailable"
          }
        }
      },
      "get": {
        "tags": [
          "Vastleggingen"
        ],
        "operationId": "listCaptures",
        "summary": "Uw vastleggingen weergeven",
        "description": "Rijen die u hebt aangemaakt, nieuwste eerst, in elke status (inclusief verwijderde, de prullenbak; filter met status). Elke rij bevat source, redacted_from, label en compared, zodat het type kan worden getoond zonder de rij opnieuw te lezen. Een vastlegging die voor een bedrijf is gemaakt, wordt alleen vermeld zolang u er nog mee mag werken (u hebt een licentie van dat bedrijf of bent de eigenaar); een pagina kan daardoor minder rijen bevatten dan limit, en next_cursor gaat verder na de laatst gelezen rij. Certificaten, vergelijkingen en weglakkingen zijn inbegrepen.\n\nOpzoeken (alleen eigen lijst): url, host of schedule_id, gecombineerd met from / to en status, vinden het record van een pagina op een bepaalde dag. Een ongeldig filter, twee van url / host / schedule_id, of een from die niet vóór to ligt, geeft 400 MALFORMED_REQUEST.",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Paginagrootte, 1 tot 200 (standaard 50). Een waarde buiten dit bereik geeft 400 MALFORMED_REQUEST.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "next_cursor van de vorige pagina (vorm created_at|capture_id, of completed_at|capture_id met order=completed_at). Geef die ongewijzigd door.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Alleen rijen met deze status: processing, completed, failed of deleted (prullenbak). Elke andere waarde geeft 400 MALFORMED_REQUEST.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "url",
            "in": "query",
            "required": false,
            "description": "Alleen vastleggingen van precies deze URL, zoals die is vastgelegd (http of https). Hoogstens één van url, host en schedule_id.",
            "schema": {
              "type": "string",
              "maxLength": 2048
            }
          },
          {
            "name": "host",
            "in": "query",
            "required": false,
            "description": "Alleen vastleggingen waarvan de URL op deze hostnaam staat (niet hoofdlettergevoelig, bijv. www.example.com; subdomeinen zijn afzonderlijke hosts). Hoogstens één van url, host en schedule_id. Rijen die vóór 2026-10-05 zijn aangemaakt, bevatten de host zodra de backfill is uitgevoerd.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "schedule_id",
            "in": "query",
            "required": false,
            "description": "Alleen vastleggingen die door dit schema zijn gemaakt. Hoogstens één van url, host en schedule_id.",
            "schema": {
              "type": "string",
              "pattern": "^sch_[A-Za-z0-9]{8,32}$"
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Aangemaakt op of na dit moment: een datum (YYYY-MM-DD, middernacht UTC) of een ISO 8601-tijdstempel.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Aangemaakt vóór dit moment (exclusief): een datum (YYYY-MM-DD, middernacht UTC) of een ISO 8601-tijdstempel. Moet later zijn dan from. Met schedule_id of url is de nieuwste rij vóór een moment de versie die op dat moment actueel was.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "order",
            "in": "query",
            "required": false,
            "description": "created_at (standaard: nieuwst aangemaakte eerst) of completed_at (laatst voltooide eerst; alleen met status=completed, anders 400 MALFORMED_REQUEST). Met completed_at begrenzen from en to de waarde completed_at, en heeft next_cursor de vorm completed_at|capture_id. Werkt met url, host en schedule_id.",
            "schema": {
              "type": "string",
              "enum": [
                "created_at",
                "completed_at"
              ],
              "default": "created_at"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Een pagina met vastleggingen.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "captures": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "capture_id": {
                            "type": "string"
                          },
                          "url": {
                            "type": "string",
                            "nullable": true
                          },
                          "status": {
                            "type": "string",
                            "nullable": true
                          },
                          "created_at": {
                            "type": "string",
                            "nullable": true
                          },
                          "completed_at": {
                            "type": "string",
                            "nullable": true,
                            "description": "Wanneer de vastlegging is voltooid (null tijdens verwerking of na een fout)."
                          },
                          "batch_id": {
                            "type": "string",
                            "nullable": true
                          },
                          "schedule_id": {
                            "type": "string",
                            "nullable": true
                          },
                          "test_mode": {
                            "type": "boolean",
                            "nullable": true
                          },
                          "external_archive": {
                            "allOf": [
                              {
                                "$ref": "#/components/schemas/ExternalArchive"
                              }
                            ],
                            "description": "Alleen rijen met completed. Hetzelfde als external_archive van GET /captures/{capture_id}."
                          },
                          "label": {
                            "type": "string",
                            "nullable": true,
                            "description": "Het label van de rij (certificaten: het opgegeven label; weglakkingen: welke vastlegging ze weglakken)."
                          },
                          "compared": {
                            "type": "array",
                            "nullable": true,
                            "items": {
                              "type": "string"
                            },
                            "description": "Alleen bij vergelijkingen: de ID's van de twee bronvastleggingen."
                          },
                          "source": {
                            "type": "string",
                            "nullable": true,
                            "description": "Hoe de rij is gemaakt: api (een webvastlegging), schedule, certify-hash (een certificaat), compare (een vergelijking) of redact (een weglakking); null op rijen die ouder zijn dan het veld."
                          },
                          "redacted_from": {
                            "type": "string",
                            "nullable": true,
                            "description": "Alleen weglakkingen: de vastlegging waaruit de weglakking is gemaakt."
                          },
                          "captured_at": {
                            "type": "string",
                            "nullable": true,
                            "format": "date-time",
                            "description": "Wanneer de pagina is vastgelegd (rijen met completed). Bij een weglakking het vastleggingstijdstip van de bronvastlegging (redacted_at is het moment waarop de weglakking is gemaakt)."
                          },
                          "redacted_at": {
                            "type": "string",
                            "format": "date-time",
                            "description": "Alleen weglakkingen: wanneer de weglakking is gemaakt. De captured_at van een weglakking is het vastleggingstijdstip van de bronvastlegging."
                          },
                          "compared_at": {
                            "type": "string",
                            "nullable": true,
                            "format": "date-time",
                            "description": "Alleen bij vergelijkingen: wanneer de vergelijking is gemaakt. Het veld captured_at bevat dezelfde waarde (behouden voor oudere clients); gebruik compared_at."
                          },
                          "manifest_hash": {
                            "type": "string",
                            "nullable": true,
                            "description": "sha256:<hex> van de manifest.json van het pakket (rijen met completed)."
                          },
                          "private_note": {
                            "type": "string",
                            "nullable": true,
                            "description": "Uw privénotitie bij de vastlegging (zie PrivateNote), nooit onderdeel van het bewijspakket."
                          },
                          "legal_hold": {
                            "type": "boolean",
                            "description": "De vastlegging staat onder legal hold (wordt niet verwijderd zolang true; automatisch opgeheven wanneer het abonnement is beëindigd en de 30 dagen daarvan zijn verstreken). Zie PATCH /captures/{capture_id}."
                          }
                        }
                      }
                    },
                    "next_cursor": {
                      "type": "string",
                      "description": "null als deze pagina minder rijen heeft dan limit.",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "MALFORMED_REQUEST: ongeldige cursor, status (een van processing, completed, failed, deleted), limit (1 tot 200) of zoekfilter (url, host, schedule_id, from, to; hoogstens één van url / host / schedule_id; from eerder dan to).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/AuthUnavailable"
          }
        }
      }
    },
    "/captures/batch": {
      "post": {
        "tags": [
          "Vastleggingen"
        ],
        "operationId": "createBatch",
        "summary": "Meerdere URL's vastleggen",
        "description": "Start één vastlegging per URL met dezelfde opties. Elke vastlegging is onafhankelijk: poll elke capture_id afzonderlijk of ontvang één webhook per vastlegging. Verbruikt per URL één rate-limit-eenheid en één eenheid van de maandelijkse limiet. Een batch met meer URL's dan één rate-limit-venster toestaat, kan nooit slagen en wordt daarom geweigerd met 400 RATE_LIMIT_COST_TOO_HIGH in plaats van 429. Splits de batch op in kleinere verzoeken. Een herhaling van een voltooide batch (dezelfde Idempotency-Key en body) wordt geleverd voordat de rate limit wordt toegepast en kost geen rate-limit-eenheid.\n\nRate limit (snelheidslimiet) per account in vensters van 60 seconden (alle API-sleutels van het account en de bijbehorende dashboardsessie delen één venster). Zodra de limiter heeft gedraaid, bevatten antwoorden van deze route de headers `RateLimit-Policy` en `RateLimit` (gebaseerd op IETF draft-ietf-httpapi-ratelimit-headers) plus de oudere `X-RateLimit-Limit`, `X-RateLimit-Remaining` en `X-RateLimit-Reset`. Een 429 voegt `Retry-After` toe.",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BatchRequest"
              },
              "example": {
                "urls": [
                  "https://example.com/a",
                  "https://example.com/b"
                ],
                "options": {
                  "case_reference": "Smith v. Jones"
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Geaccepteerd. Een idempotente herhaling van een voltooide batch met dezelfde Idempotency-Key en body geeft ook 202 terug, met `Idempotent-Replay: true`; die wordt afgehandeld voordat de rate limit wordt toegepast en kost dus geen rate-limit-eenheid.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotent-Replay": {
                "$ref": "#/components/headers/IdempotentReplay"
              },
              "X-Quota-Limit": {
                "$ref": "#/components/headers/QuotaLimit"
              },
              "X-Quota-Remaining": {
                "$ref": "#/components/headers/QuotaRemaining"
              },
              "X-Quota-Used": {
                "$ref": "#/components/headers/QuotaUsed"
              },
              "X-Quota-Cap": {
                "$ref": "#/components/headers/QuotaCap"
              },
              "X-Quota-Org-Limit": {
                "$ref": "#/components/headers/QuotaOrgLimit"
              },
              "X-Quota-Org-Used": {
                "$ref": "#/components/headers/QuotaOrgUsed"
              },
              "X-Sandbox-Limit": {
                "$ref": "#/components/headers/SandboxLimit"
              },
              "X-Sandbox-Remaining": {
                "$ref": "#/components/headers/SandboxRemaining"
              },
              "X-Sandbox-Used": {
                "$ref": "#/components/headers/SandboxUsed"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BatchResult"
                }
              }
            }
          },
          "400": {
            "description": "INVALID_BATCH (details: reason en received, of index, url en een genest reason-object voor de ongeldige URL), MALFORMED_REQUEST of een fout in options. WEBHOOK_REQUIRES_API_KEY: webhook_url verzonden vanuit een dashboardsessie (webhooks vereisen een API-sleutel). RATE_LIMIT_COST_TOO_HIGH: meer URL's dan één venster van de rate limit toestaat; details { reason, max_per_window, window_seconds, requested }.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "QUOTA_EXCEEDED of PAYMENT_REQUIRED. Een batch wordt ook geweigerd met QUOTA_EXCEEDED als het maandelijkse gebruik op dat moment niet kan worden gelezen; probeer het opnieuw.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "403": {
            "description": "EIDAS_REQUIRES_LIVE_KEY: include_eidas met een testsleutel. EMAIL_NOT_VERIFIED: een dashboardsessie waarvan het e-mailadres niet is bevestigd. PLAN_ARCHIVE_ONLY: het abonnement bewaart alleen het archief en maakt geen nieuw werk aan. PLAN_API_READ_ONLY: een API-sleutel van een Website Archive-account (de sleutel is alleen-lezen; gebruik het dashboard). PLAN_API_READ_ONLY met details.reason \"session_outside_dashboard\": een dashboardsessie van een Website Archive-account waarvan het verzoek geen Origin-header heeft, of een Origin-header die geen ProofSnap-dashboardadres is (nieuw werk op dat abonnement wordt alleen in het dashboard aangemaakt; een zachte drempel, geen beveiligingsgrens).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "409": {
            "description": "IDEMPOTENCY_IN_PROGRESS.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "422": {
            "description": "IDEMPOTENCY_KEY_REUSED.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED: te veel verzoeken in het huidige venster van 60 seconden. `error.details.retry_after_seconds` herhaalt Retry-After. RateLimit meldt r=0 en het aantal seconden tot het venster opnieuw begint. Ook RATE_LIMITED, voor een dashboardsessie van Website Archive: het daglimiet van 100 eenheden per UTC-dag, gedeeld door losse vastleggingen en batchvastleggingen (één eenheid per URL), compare, redact en certify-hash (één eenheid per verzoek); een verzoek dat niet past, wordt in zijn geheel geweigerd (details.daily_limit, details.remaining, details.action \"wa_session_captures\"; Retry-After en details.retry_after_seconds geven het aantal seconden tot de volgende UTC-dag, en de RateLimit- en X-RateLimit-*-headers per minuut ontbreken). Een korte weigering met alleen details.retry_after_seconds en Retry-After: 1 betekent een gelijktijdige update van de teller: probeer het opnieuw. Bij een weigering die niet aan de aanroeper ligt, worden de eenheden teruggegeven; werk dat met 202 is geaccepteerd en later mislukt, behoudt zijn eenheid. Een zachte drempel, geen beveiligingsgrens.\n\nCONCURRENT_CAPTURE_LIMIT: geen enkele URL van de batch kon starten, omdat het account al het maximale aantal gelijktijdige vastleggingen heeft lopen (developer 10, startup 25, growth 100, legal_solo en legal_seats 25, custom 200, website_archive 25, sandbox 5); `details.limit`, `details.in_flight`, `details.requested` en Retry-After. Als slechts een deel past, wordt de batch met 202 geaccepteerd en staat de rest in `not_admitted`.\n\nTEST_MODE_DAILY_LIMIT: werk in testmodus (een testsleutel, of een sessie zonder gekoppelde facturering) is beperkt tot 200 eenheden per account en UTC-dag, één per URL; `details.used`, `details.limit`, `details.reset_at`, de `X-Test-Daily-*`-headers en Retry-After.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "RATE_LIMITED",
                    "message": "Too many requests. See Retry-After header.",
                    "details": {
                      "retry_after_seconds": 42
                    }
                  },
                  "request_id": "..."
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/EidasOrAuthUnavailable"
          }
        }
      }
    },
    "/captures/hosts": {
      "get": {
        "tags": [
          "Vastleggingen"
        ],
        "operationId": "listCaptureHosts",
        "summary": "De hostnamen van uw vastleggingen weergeven",
        "description": "Elke hostnaam uit uw eigen lijst met vastleggingen, met per host het aantal vastleggingen en het tijdstip van de laatste vastlegging, voor een domeinfilter: geef een host terug door als GET /captures?host=. count is het aantal rijen dat GET /captures?host=<host> zonder status teruggeeft (binnen de gelezen vastleggingen): test- en live-vastleggingen gelijk, elke status inclusief vastleggingen in de prullenbak (status deleted), zoals de lijst ze toont; definitief verwijderde vastleggingen en rijen zonder pagina-URL (certificaten) worden weggelaten. Een vastlegging telt mee zodra ze haar opgeslagen hostnaam heeft (vastleggingen die vóór 2026-10-05 zijn gemaakt, hebben die zodra de backfill is uitgevoerd). Een bedrijfsvastlegging telt alleen mee zolang u er nog op mag handelen (u hebt een licentie van dat bedrijf of bent er eigenaar van). host is in kleine letters. Gesorteerd op count (hoogste eerst), daarna op host (A tot Z). Maximaal 1000 hosts. De nieuwste 5000 vastleggingen worden gelezen; truncated is true als er meer dan 1000 hosts zijn of vastleggingen die ouder zijn dan die 5000, zodat een host die alleen daarvoor is vastgelegd kan ontbreken. De lijst wordt per account hooguit eens per 30 seconden berekend en kan tot 30 seconden oud zijn (iets ouder terwijl een ander verzoek van het account hem berekent). Het berekenen kost één eenheid van uw rate limit per minuut; een antwoord uit die cache kost niets. Beschikbaar op elk abonnement, voor API-sleutels en de dashboardsessie.",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "De hostnamen.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "hosts",
                    "truncated"
                  ],
                  "properties": {
                    "hosts": {
                      "type": "array",
                      "maxItems": 1000,
                      "items": {
                        "type": "object",
                        "required": [
                          "host",
                          "count",
                          "last_captured_at"
                        ],
                        "properties": {
                          "host": {
                            "type": "string",
                            "example": "www.example.com",
                            "description": "Hostnaam in kleine letters, de waarde die GET /captures?host= verwacht."
                          },
                          "count": {
                            "type": "integer",
                            "minimum": 1,
                            "example": 12,
                            "description": "Vastleggingen van deze host onder de gelezen vastleggingen: de rijen die GET /captures?host= zonder status teruggeeft, inclusief die in de prullenbak."
                          },
                          "last_captured_at": {
                            "type": "string",
                            "format": "date-time",
                            "nullable": true,
                            "example": "2026-10-05T07:48:38Z",
                            "description": "Meest recente vastleggingstijd van deze host (wanneer de pagina is vastgelegd, anders wanneer de vastlegging is aangevraagd), UTC, tot op de seconde."
                          }
                        }
                      }
                    },
                    "truncated": {
                      "type": "boolean",
                      "description": "true als de lijst onvolledig kan zijn: meer dan 1000 hosts, of meer vastleggingen dan de nieuwste 5000 die zijn gelezen."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "MALFORMED_REQUEST: scope wordt niet ondersteund (alleen eigen vastleggingen).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "description": "RATE_LIMITED: de rate limit per minuut is opgebruikt (het berekenen van de lijst kost één eenheid), of de lijst van uw account wordt door een ander verzoek berekend en er is geen eerder resultaat opgeslagen; probeer het na Retry-After seconden opnieuw.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "RATE_LIMITED",
                    "message": "Too many requests. See Retry-After header.",
                    "details": {
                      "retry_after_seconds": 42
                    }
                  },
                  "request_id": "..."
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/AuthUnavailable"
          }
        }
      }
    },
    "/captures/{capture_id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/CaptureId"
        }
      ],
      "get": {
        "tags": [
          "Vastleggingen"
        ],
        "operationId": "getCapture",
        "summary": "Een vastlegging ophalen",
        "description": "Status en, na voltooiing, een nieuwe package_url die maximaal 24 uur geldig is (zie package_url_expires_at). Werkt voor ID's van vastleggingen, certificaten, vergelijkingen en weglakkingen. Leesbaar voor het account dat de rij heeft aangemaakt, de leden van de zaak waartoe de rij behoort en de eigenaar van het bedrijf waarvoor de rij is gemaakt (als het abonnement van het bedrijf is beëindigd, tot het downloadvenster sluit).",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "event",
            "in": "query",
            "required": false,
            "description": "Stuur download mee wanneer u het pakket downloadt: bij een vastlegging in een zaak legt dit capture.downloaded vast in het toegangslogboek van de zaak. Gewone statuspolls laten dit weg.",
            "schema": {
              "type": "string",
              "enum": [
                "download"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "De vastlegging.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CaptureResult"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "FORBIDDEN: niet uw vastlegging.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "CAPTURE_NOT_FOUND: onbekende of onjuist opgemaakte ID, verwijderde vastlegging, of het pakketbestand is niet gevonden.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/AuthUnavailable"
          }
        }
      },
      "delete": {
        "tags": [
          "Vastleggingen"
        ],
        "operationId": "deleteCapture",
        "summary": "Een vastlegging verwijderen",
        "description": "Standaard: verplaatst de vastlegging naar de prullenbak. Ze kan met PATCH worden teruggezet tot restorable_until (30 dagen); daarna wordt ze opgeschoond. Met permanent=true wordt het opgeslagen pakket direct gewist en wordt het record teruggebracht tot een tombstone; dit kan niet ongedaan worden gemaakt. Geen van beide verwijdert pakketten die u al hebt gedownload of het OpenTimestamps-bewijs. Een vastlegging in een open of gearchiveerde zaak wordt eerst uit de zaak verwijderd (geregistreerd in het bewijsketenlogboek ervan) wanneer de aanroeper die zaak kan openen; een aanroeper die dat niet kan (de auteur nadat die uit de zaak is verwijderd), wordt geweigerd met 409 MATTER_LINKED, zowel voor de prullenbak als voor permanent=true, totdat de eigenaar van de zaak de vastlegging uit de zaak verwijdert; een vastlegging in een verzegelde zaak kan niet worden verwijderd. Een vastlegging die nog wordt verwerkt, kan niet worden verwijderd (409 CAPTURE_PROCESSING); verwijder haar zodra ze is voltooid of mislukt. Het verwijderen van een vastlegging die nooit is voltooid, geeft de maandelijkse toelating ervan terug.\n\nRespijtperiode na opheffing van een legal hold: gedurende 14 dagen nadat een legal hold is opgeheven (`deletion_blocked_until` in GET) wordt de vastlegging niet definitief verwijderd (409 CAPTURE_HOLD_GRACE met `details.until`), niet door een persoon en niet door het opschonen van de prullenbak of de bewaartermijn (de verwijdering van het account en het bedrijf na afloop van het abonnement wacht hier niet op). Verplaatsen naar de prullenbak is toegestaan; het opschonen ervan wacht tot het einde van de respijtperiode. Een definitieve verwijdering heft eerst een eventuele S3 Object Lock legal hold op die nog op de opgeslagen objecten staat; wanneer de opslag dat weigert, retourneert het verzoek 409 CAPTURE_LEGAL_HOLD (\"still locked in storage\", probeer het later opnieuw) en wordt er niets verwijderd. Elke verplaatsing naar de prullenbak, elke terugzetting en elke definitieve verwijdering wordt geregistreerd in de gebeurtenisketen van vastleggingen van het account (GET /captures/events). Een weglakking of vergelijking waarvan de bronvastlegging onder legal hold staat, retourneert `source_under_legal_hold: true` wanneer ze naar de prullenbak wordt verplaatst of wordt verwijderd.",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "permanent",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "true"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Verwijderd. Prullenbak: { capture_id, status: \"deleted\", restorable: true, restorable_until }. Definitief: { capture_id, status: \"deleted\", permanent: true }. Al in de prullenbak: { capture_id, status }.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "capture_id": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string"
                    },
                    "restorable": {
                      "type": "boolean"
                    },
                    "restorable_until": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "permanent": {
                      "type": "boolean"
                    },
                    "source_under_legal_hold": {
                      "type": "boolean",
                      "description": "Alleen aanwezig (true) wanneer dit een weglakking of vergelijking is die is gemaakt van een vastlegging die onder legal hold staat."
                    },
                    "deletion_blocked_until": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Prullenbak tijdens de respijtperiode na opheffing: het legen van de prullenbak wacht tot dit tijdstip."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "FORBIDDEN: niet uw vastlegging. FORBIDDEN ook wanneer de vastlegging bij een zaak hoort die niet verzegeld is en de aanroeper (de eigenaar van het bedrijf die handelt op de vastlegging van een licentie, niet de maker ervan) die zaak niet kan openen. PLAN_API_READ_ONLY: een API-sleutel van een Website Archive-account (de sleutel is alleen-lezen; gebruik het dashboard).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "CAPTURE_NOT_FOUND.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "MATTER_SEALED: de vastlegging is een bijlage van een verzegelde zaak. MATTER_LINKED: de vastlegging is een bijlage van een zaak waartoe de aanroeper niet meer behoort; de eigenaar van de zaak verwijdert haar eerst. CAPTURE_PROCESSING: de vastlegging wordt nog verwerkt (error.details.status). CAPTURE_LEGAL_HOLD: de vastlegging staat onder legal hold en wordt door niemand verwijderd (prullenbak of definitief verwijderen). CAPTURE_HOLD_GRACE: een definitieve verwijdering binnen de respijtperiode nadat de legal hold van de vastlegging is opgeheven (LEGAL_HOLD_RELEASE_GRACE_DAYS, 14 dagen); error.details.until geeft aan wanneer dit mogelijk wordt. Verplaatsen naar de prullenbak is tijdens de wachttijd toegestaan.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR: het opgeslagen pakket kon niet worden gewist; er is niets verwijderd.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/AuthUnavailable"
          }
        }
      },
      "patch": {
        "tags": [
          "Vastleggingen"
        ],
        "operationId": "restoreCapture",
        "summary": "Een verwijderde vastlegging terugzetten, de privénotitie instellen, of de legal hold instellen of opheffen",
        "description": "Zet een vastlegging terug uit de prullenbak. Als die bij het verwijderen uit een zaak is gehaald, wordt die weer toegevoegd wanneer die zaak nog open is en de aanroeper de zaak nog kan openen; alleen dan staan matter_id en matter_relinked in het antwoord (matter_id is null als het opnieuw koppelen niet is gebeurd). Een aanroeper die die zaak niet meer kan openen, krijgt de vastlegging terug zonder de zaak, er wordt niets naar het bewijsketenlogboek van de zaak geschreven en het antwoord noemt de zaak niet. Een vastlegging die niet in de prullenbak staat, wordt ongewijzigd teruggegeven.\n\nPrivénotitie: een body met `private_note` (een string van maximaal 200 tekens, of null om die te wissen) stelt in plaats daarvan de privénotitie van de vastlegging in en doet verder niets; dit kan niet worden gecombineerd met `action`. De notitie wordt alleen in het record van de vastlegging opgeslagen, buiten het verzegelde bewijspakket, zodat een wijziging nooit het bewijs of de hashes verandert. Alleen de maker van de vastlegging mag die instellen (de bedrijfseigenaar en leden van de zaak krijgen 403), bij een vastlegging in elke status behalve definitief verwijderd (404). Het antwoord is { capture_id, status, private_note }.\n\nLegal hold: een body `{ \"action\": \"legal_hold\", \"enabled\": true|false, \"reason\"?: string }` stelt de legal hold van de vastlegging in (true) of heft die op (false) en doet verder niets. Een vastlegging onder legal hold wordt niet verwijderd: DELETE (prullenbak of definitief) geeft voor iedereen 409 CAPTURE_LEGAL_HOLD, en noch het legen van de prullenbak noch de verwijdering na afloop van de bewaartermijn raakt die aan; GET toont `storage.retention.earliest_deletion_at` als null. Een legal hold blijft alleen bestaan zolang het account een abonnement heeft dat gegevens bewaart (elk betaald abonnement, Archive only inbegrepen; voor een bedrijfsvastlegging: het bedrijf is actief of wordt als bedrijfsarchief bewaard). Als het abonnement eindigt, blijven de vastleggingen 30 dagen beschikbaar; als er tot dan geen dergelijk abonnement wordt afgesloten, wordt elke legal hold van het account (of het bedrijf) op die datum automatisch opgeheven (actor system, reden subscription_ended, vastgelegd zoals elke opheffing, zonder respijtperiode na opheffing) en worden de vastleggingen samen met de andere verwijderd. Stap vóór die datum over op Archive only (of koop het bedrijfsarchief) om vastleggingen onder legal hold te behouden, of download ze. In die 30 dagen kan geen nieuwe legal hold worden ingesteld (het account heeft geen betaald abonnement), bestaande kunnen nog wel worden opgeheven. De legal hold wordt alleen in het record van de vastlegging opgeslagen, buiten het verzegelde bewijspakket. `reason` is optionele platte tekst van maximaal 200 tekens (geen besturings-, onzichtbare of tekstrichtingstekens; witruimte wordt samengevoegd, leeg betekent geen reden); de reden wordt bij de legal hold bewaard en gewist wanneer de legal hold wordt opgeheven.\n\nWie: een legal hold instellen mag iedereen die met de vastlegging mag werken (de auteur, bij een bedrijfsvastlegging zolang die nog een licentie van dat bedrijf heeft, en de bedrijfseigenaar, ook van een bedrijf waarvan het abonnement is beëindigd zolang het binnen de bewaartermijn valt of als bedrijfsarchief wordt bewaard; het archief alleen via de dashboardsessie). Opheffen: bij een persoonlijke vastlegging de auteur, bij een bedrijfsvastlegging alleen de bedrijfseigenaar (een licentiehouder krijgt 403 LEGAL_HOLD_OWNER_ONLY). Voor het instellen van een legal hold is een betaald abonnement nodig (elk betaald abonnement, Archive only inbegrepen): de gratis sandbox, en ook een account waarvan het abonnement is beëindigd, krijgt 403 LEGAL_HOLD_NOT_AVAILABLE met details.reason \"plan\" (een lopend bedrijfsarchief telt als betaald voor de vastleggingen van dat bedrijf, via de dashboardsessie van de eigenaar), en een vastlegging in testmodus (met watermerk, gemaakt met een testsleutel of zonder gekoppelde facturering) krijgt 403 LEGAL_HOLD_NOT_AVAILABLE met details.reason \"test_capture\". Opheffen werkt bij elk abonnement, ook nadat het abonnement is beëindigd. Bij Website Archive is de API-sleutel alleen-lezen (403 PLAN_API_READ_ONLY), de dashboardsessie mag het wel. Een achterstallig of onbetaald abonnement blokkeert dit niet. Een dashboardsessie of sleutel waarvan het e-mailadres niet is bevestigd, krijgt 403 EMAIL_NOT_VERIFIED.\n\nGeweigerd bij een vastlegging in de prullenbak (409 CAPTURE_IN_TRASH: zet die eerst terug) of die nog wordt verwerkt (409 CAPTURE_PROCESSING); een definitief verwijderde vastlegging, of een waarvan de definitieve verwijdering bezig is, geeft 404. Elk verzoek voor een legal hold kost één eenheid van de rate limit per minuut van het account (429 RATE_LIMITED met Retry-After als die is opgebruikt). Wie de status stuurt die de vastlegging al heeft, verandert niets en krijgt 200 met de opgeslagen velden en `changed: false`. Elke wijziging wordt in het auditlogboek vastgelegd als capture.legal_hold (bij een bedrijfsvastlegging in de tenant van het bedrijf), vastgelegd in de toegangsgeschiedenis van de vastlegging (`legal_hold_on` / `legal_hold_off`, bewaard als de vastlegging later wordt gewist) en, als de vastlegging bij een zaak hoort, in het bewijsketenlogboek van de zaak geschreven als capture.legal_hold_placed of capture.legal_hold_released met de ID van de vastlegging als doel (de reden gaat naar het toegangslogboek van de zaak, nooit in de gehashte keten; de keten van een verzegelde zaak verandert niet). Het antwoord is { capture_id, legal_hold, legal_hold_set_at, legal_hold_reason, changed }.\n\nLegal hold v2: het instellen of opheffen van een legal hold stelt ook een S3 Object Lock legal hold in op, of heft die op van, elke versie van het opgeslagen pakket, het geüpgradede OpenTimestamps-bewijs en de afzonderlijk opgeslagen kopie (`s3_legal_hold` in GET; als de opslag weigert of geen Object Lock-configuratie heeft, blijft de legal hold op het record van de vastlegging gelden en doet de server een nieuwe poging na een mislukte poging). Een opheffing start een respijtperiode van 14 dagen waarin de vastlegging niet definitief wordt verwijderd (niet nadat het abonnement is beëindigd: dan vervangen de 30 dagen na het einde die periode). Een legal hold die door de legal hold van de zaak is ingesteld (`legal_hold_source` \"matter:<id>\"), wordt op de zaak opgeheven zolang die legal hold actief is: hier opheffen geeft 409 LEGAL_HOLD_MATTER (error.details.matter_id voor iemand die de zaak kan lezen). Elke wijziging wordt vastgelegd in de gebeurtenisketen van vastleggingen van het account (GET /captures/events).",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "action": {
                    "type": "string",
                    "description": "restore (de standaard voor een body zonder private_note) of legal_hold (met enabled en de optionele reason).",
                    "enum": [
                      "restore",
                      "legal_hold"
                    ]
                  },
                  "private_note": {
                    "allOf": [
                      {
                        "$ref": "#/components/schemas/PrivateNote"
                      }
                    ],
                    "description": "Stelt de privénotitie in (een string) of wist deze (null of een lege string). Kan niet worden gecombineerd met action."
                  },
                  "enabled": {
                    "type": "boolean",
                    "description": "Bij actie legal_hold (daar verplicht): true stelt de legal hold in, false heft die op."
                  },
                  "reason": {
                    "type": "string",
                    "nullable": true,
                    "maxLength": 200,
                    "description": "Bij actie legal_hold, optioneel: waarom de vastlegging onder legal hold staat, platte tekst van maximaal 200 tekens. Opgeslagen bij de legal hold, buiten het bewijspakket; genegeerd bij opheffen (dan gaat de tekst alleen naar het auditlogboek)."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Teruggezet ({ capture_id, status, restored: true, matter_id?, matter_relinked? }), ongewijzigd ({ capture_id, status }), de privénotitie ingesteld ({ capture_id, status, private_note }), of de legal hold ingesteld ({ capture_id, legal_hold, legal_hold_set_at, legal_hold_reason, changed }).",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "capture_id": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string"
                    },
                    "restored": {
                      "type": "boolean"
                    },
                    "matter_id": {
                      "type": "string",
                      "nullable": true
                    },
                    "matter_relinked": {
                      "type": "boolean"
                    },
                    "private_note": {
                      "type": "string",
                      "nullable": true
                    },
                    "legal_hold": {
                      "type": "boolean",
                      "description": "Legal hold-actie: de hold van de vastlegging na het verzoek."
                    },
                    "legal_hold_set_at": {
                      "type": "string",
                      "format": "date-time",
                      "nullable": true,
                      "description": "Legal hold-actie: wanneer de huidige hold is ingesteld (null wanneer er geen is)."
                    },
                    "legal_hold_reason": {
                      "type": "string",
                      "nullable": true,
                      "description": "Legal hold-actie: de reden die bij de huidige hold is opgeslagen, of null."
                    },
                    "changed": {
                      "type": "boolean",
                      "description": "Legal hold-actie: false wanneer de vastlegging al de gevraagde status had (er is niets gewijzigd of gelogd)."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "MALFORMED_REQUEST: ongeldige JSON, een body die geen object is, een andere action dan restore of legal_hold, private_note gecombineerd met action, of een private_note die geen string of null is, langer is dan 200 tekens of controle-, onzichtbare of tekstrichtingstekens bevat. Voor legal_hold: enabled ontbreekt of is geen boolean, een reason die geen string is, langer is dan 200 tekens of geen platte tekst is, of een onbekend veld.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "FORBIDDEN: niet uw vastlegging. FORBIDDEN ook wanneer de vastlegging bij een zaak hoort die niet verzegeld is en de aanroeper (de eigenaar van het bedrijf die handelt op de vastlegging van een licentie, niet de maker ervan) die zaak niet kan openen. PLAN_API_READ_ONLY: een API-sleutel van een Website Archive-account (de sleutel is alleen-lezen; gebruik het dashboard). Voor private_note: FORBIDDEN voor iedereen behalve de maker van de vastlegging (die er nog steeds op moet mogen handelen). Voor legal_hold: FORBIDDEN wanneer de aanroeper niet op de vastlegging mag handelen; LEGAL_HOLD_OWNER_ONLY wanneer een licentie (niet de eigenaar van het bedrijf) de legal hold van een bedrijfsvastlegging opheft; LEGAL_HOLD_NOT_AVAILABLE bij het instellen van een legal hold zonder betaald abonnement (details.reason plan) of op een vastlegging in testmodus (details.reason test_capture); EMAIL_NOT_VERIFIED voor een niet-bevestigd e-mailadres. De regel over zaken hierboven geldt niet voor een legal hold.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "CAPTURE_NOT_FOUND, ook voor een vastlegging die definitief is verwijderd.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Alleen legal hold. CAPTURE_IN_TRASH: de vastlegging staat in de prullenbak; zet haar eerst terug. CAPTURE_PROCESSING: de vastlegging wordt nog verwerkt (error.details.status). LEGAL_HOLD_MATTER: de hold van de vastlegging is ingesteld door de legal hold van haar zaak, die nog actief is; hef die op bij de zaak. LEGAL_HOLD_SOURCE: de hold van een weglakking of vergelijking die volgt uit een vastlegging waarvan zij is gemaakt (bij een vergelijking een van beide vergeleken vastleggingen), die nog onder legal hold staat (ingesteld op die vastlegging of via haar zaak): het pakket blijft onder hold totdat die hold is opgeheven (`details.source_capture_id`). Een vergelijking staat onder hold zolang een van beide vergeleken vastleggingen onder hold staat, en wordt pas vrijgegeven wanneer dat voor geen van beide meer geldt (`legal_hold_source` \"derived:<capture id>\" noemt de vastlegging die onder hold staat).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Alleen legal hold. RATE_LIMITED: de rate limit per minuut van het account is verbruikt. `error.details.retry_after_seconds` herhaalt Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/AuthUnavailable"
          }
        }
      }
    },
    "/captures/{capture_id}/verify": {
      "parameters": [
        {
          "$ref": "#/components/parameters/CaptureId"
        }
      ],
      "post": {
        "tags": [
          "Vastleggingen"
        ],
        "operationId": "verifyCapture",
        "summary": "Een opgeslagen pakket verifiëren",
        "description": "Leest het opgeslagen pakket en controleert de RSA-handtekening van manifest.json en de SHA-256 van elk bestand dat erin staat. Pakketten groter dan 256 MiB geven 413 VERIFY_PACKAGE_TOO_LARGE; controleer die offline met de meegeleverde scripts. Elk verzoek kost één eenheid van de rate limit per minuut van het account (429 RATE_LIMITED). Als het pakket manifest.json.tsr bevat, controleert ProofSnap ook het eIDAS-tijdstempeltoken (checks.eidas): de handtekening en ondertekende attributen van het token, dat het de SHA-256 van manifest.json bevat, de extendedKeyUsage van het TSA-certificaat, de certificaatketen naar de vastgepinde root-CA Disig QCA4 op het tijdstip van het token (genTime), en de status van het TSA-certificaat volgens het OCSP-antwoord of de CRL die bij uitgifte in eidas_validation.json is vastgelegd (gecontroleerd op handtekening, niet live opgehaald). Deze controle vraagt nooit een nieuwe tijdstempel aan. Er wordt ook gemeld of manifest.json.ots aanwezig is en of de hash van het manifest in het ProofSnap-uitgifteregister staat. Het oordeel hangt alleen af van de handtekening van het manifest en de vermelde bestanden; een token dat de controle niet doorstaat, wordt gemeld in checks.eidas en in warnings (EIDAS_TOKEN_FAILED), maar telt niet mee in het oordeel. Een pakket dat met een gekwalificeerde tijdstempel is uitgegeven, legt eidas_requested: true vast in het ondertekende manifest.json; als zo'n pakket manifest.json.tsr niet meer bevat, worden checks.eidas en checks.eidas_disig als ontbrekend gemeld met de waarschuwing EIDAS_TOKEN_MISSING (de meegeleverde verify-scripts melden FAIL voor hetzelfde pakket). Toegestaan voor de eigenaar van de vastlegging, leden van de zaak en de eigenaar van het bedrijf waarvoor de vastlegging is gemaakt. Voer de scripts in de map verification/ van het pakket uit om de tijdstempels onafhankelijk te controleren.\n\nGeïmporteerde pakketten (source import): de opgeslagen ZIP wordt opnieuw gecontroleerd met de importcontroles (handtekening, elk bestand, bewijsketen, forensische keten, tijdstempels; de herkomst wordt voor API-pakketten opnieuw opgezocht in het API-register en voor extensiepakketten uit het importrecord gehaald). Het antwoord is { capture_id, source, type: \"import\", package_kind, verifier: \"package-verify\", verdict (match, modified, invalid), rechecked: true, manifest_sha256, zip_sha256, zip_matches_import, kind, summary, levels, checks, reasons, provenance }. Een pakket groter dan 512 MiB, of een controle die niet binnen ongeveer 20 seconden klaar is, geeft rechecked: false met een reden (too_large_for_synchronous_check, time_budget, storage_error), verdict: null en stored_verification (het rapport dat bij de import is vastgelegd).",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Verificatieresultaat.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VerifyResult"
                },
                "example": {
                  "verdict": "match",
                  "capture_id": "cap_a1B2c3D4e5F6g7H8",
                  "source": "s3",
                  "checks": {
                    "manifest_signature": {
                      "status": "valid",
                      "algorithm": "RSA-4096"
                    },
                    "declared_files": {
                      "status": "all_match"
                    },
                    "bitcoin_ots": {
                      "status": "present"
                    },
                    "eidas_disig": null,
                    "proofsnap_issuance": {
                      "status": "issued_by_proofsnap",
                      "manifest_sha256": "3f2a...",
                      "issued_at": "2026-09-01T10:00:00.000Z",
                      "type": "capture"
                    },
                    "eidas": null
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "FORBIDDEN.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "CAPTURE_NOT_FOUND: onbekend, verwijderd of geen opgeslagen pakket.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "VERIFY_PACKAGE_TOO_LARGE: het opgeslagen pakket is groter dan 256 MiB.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED: de rate limit (snelheidslimiet) per minuut van het account is opgebruikt.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "CONFIGURATION_ERROR: het opgeslagen pakket kon niet worden gelezen.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/AuthUnavailable"
          }
        }
      }
    },
    "/captures/{capture_id}/redact": {
      "parameters": [
        {
          "$ref": "#/components/parameters/CaptureId"
        }
      ],
      "post": {
        "tags": [
          "Vergelijken en weglakken"
        ],
        "operationId": "redactCapture",
        "summary": "Een weggelakte kopie maken",
        "description": "Bouwt uit een voltooide vastlegging een nieuw pakket waaruit de geselecteerde inhoud is verwijderd, ondertekent het opnieuw (nieuwe RSA-4096-sleutel) en dient het in voor een OpenTimestamps-verankering. Het origineel wordt niet gewijzigd. Synchroon: geeft 201 terug wanneer het pakket klaar is. Wordt geteld als één vastlegging.\n\n- Vakweglakkingen maken gebieden van de screenshot zwart. Een JPEG-screenshot in de vorm waarin de vastlegging hem wegschrijft (sequentieel, grijstinten of YCbCr) wordt weggelakt zonder hem naar pixels te decoderen: de gecomprimeerde blokken (MCU's, 8 tot 32 pixels per zijde) die een vak raakt, worden vervangen door zwarte blokken en elk ander blok blijft ongewijzigd, zodat de rest van de afbeelding niet opnieuw wordt gecomprimeerd (`screenshot_method` `jpeg_dct_blocks`, tot 150 megapixels). Het zwart gemaakte gebied is elk vak, naar buiten afgerond op hele blokken (`painted_rects`), zodat het vak er altijd in valt. Een PNG-screenshot, of een JPEG van een ander soort, wordt gedecodeerd, beschilderd en opnieuw gecodeerd als screenshot.jpeg, waarna de 8 x 8 blokken die een vak raakt in het nieuwe bestand zwart worden gemaakt (`screenshot_method` `decode_repaint_jpeg`, tot 60 megapixels). Een screenshot boven de limiet of met onleesbare afbeeldingsgegevens wordt geweigerd met 422 REDACTION_SCREENSHOT_UNREADABLE voordat er iets in rekening wordt gebracht. Is de header leesbaar maar de afbeeldingsgegevens niet, dan wordt de fout ontdekt nadat de maandelijkse eenheid is gereserveerd, en wordt die eenheid weer vrijgegeven. Tekst- en metadataweglakkingen werken ook zonder vakken. redaction_manifest.json (`screenshot_boxes`) legt de methode, `painted_rects`, het blokraster en een toelichting voor forensisch onderzoekers vast.\n- Tekstweglakkingen verwijderen overeenkomsten uit page.html en domtextcontent.txt, uit elke vrije-tekstwaarde in metadata.json (paginatitel en metawaarden, WHOIS- / RDAP-records, het TLS-subject, waarden van HTTP-antwoordheaders) en uit elke vrije-tekstwaarde in forensic_log.json en chain_of_custody.json (case_reference, case_id, case_note, uw metadata-waarden, geïnjecteerde code en de fouten daarvan, de labels van gesloten pop-ups, legal_context.note). Identificatoren, hashes, tijden en URL's in die records blijven ongewijzigd. Gecodeerde voorkomens worden ook verwijderd: HTML-tekenreferenties (benoemd, decimaal, hexadecimaal, zoals M&uuml;ller of jan&#64;firma.cz), percent-encoding (mailto:jan%40firma.cz) en JavaScript-escapes (\\u0040, \\x40), inclusief coderingen die tot twee niveaus diep genest zijn; de markering vervangt de gecodeerde tekens zelf. De controle na het weglakken op achtergebleven overeenkomsten leest de JSON-bestanden waarde voor waarde en doorzoekt elke tekst zowel zoals geschreven als gedecodeerd, zodat hij faalt bij een overeenkomst in elk van die vormen. Tekstweglakkingen maken de screenshot ook zwart op de plek waar text_layout.json van de bronvastlegging een voorkomen plaatst (`text_rule_rects`): de geschatte regels van het tekstblok die de overeenkomst bevatten, worden zwart gemaakt, en alleen bij een onzekere schatting het hele blok (geteld in `screenshot_boxes.from_text_rules.rects_whole_block` van redaction_manifest.json); voorkomens die niet geplaatst kunnen worden, worden geteld in `text_occurrences_not_located`. Regels zijn hoofdlettergevoelig, tenzij `case_insensitive` is ingesteld.\n- Metadataweglakkingen verwijderen velden uit metadata.json. Twee namen gaan verder. `ip_dns` (ook geaccepteerd: `ip`, `dns`, `resolved_ips`) verwijdert de IP-adressen en DNS-antwoorden van de doelsite uit metadata.json, forensic_log.json en chain_of_custody.json (de eigen adressen van de vastleggingsmachine blijven staan), en verwijdert ze, alleen als volledige adressen, ook uit page.html, domtextcontent.txt en de screenshot als automatische tekstregels (vastgelegd met alleen source ip_dns, nooit het adres of een hash ervan, en niet meegeteld voor de limiet van text_redactions). `cookie_names` (ook `cookie`, `cookies`, `http_headers.cookie`) verwijdert de headers Cookie en Set-Cookie en elke lijst met cookienamen in die records; de aantallen blijven staan. Cookiewaarden worden al in elke vastlegging weggelakt.\n\nEen vak bedekt alleen pixels. Tekst onder een vak blijft in page.html, domtextcontent.txt en metadata.json staan, tenzij u ook een tekstweglakking toevoegt, en een afbeelding onder een vak kan nog steeds worden aangeroepen of ingesloten in page.html (img src of srcset, CSS url(), een data: URI); een tekstweglakking die het adres ervan treft, verwijdert die verwijzing. Het weggelakte pakket neemt uit het origineel alleen screenshot.jpeg (of screenshot.png wanneer geen vak hem heeft vervangen), page.html, domtextcontent.txt, metadata.json en forensic_log.json over, plus de bestanden die het opnieuw schrijft (evidence.pdf, chain_of_custody.json, redaction_manifest.json, de verificatiebestanden en de manifestbestanden). Elk ander bestand van het origineel, zoals images/, image_inventory.json, text_layout.json, page.mhtml, network.har en capture.warc, wordt weggelaten en in redaction_manifest.json vermeld onder dropped_for_leak_protection. Tekstweglakkingen worden ook op deze bestandsnamen toegepast, omdat een afbeeldingskopie naar haar URL is genoemd. chain_of_custody.json behoudt de gebeurtenissen van het origineel over die bestanden, gemarkeerd als artifact_removed_in_redaction en zonder de hashes van bestanden die het weggelakte pakket niet bevat. Een rule_902_certification.pdf van het origineel wordt ook verwijderd, omdat die de bestanden en hashes van het origineel opsomt; de verwijdering wordt vermeld in redaction_manifest.json (removed_as_stale) en in chain_of_custody.json. De vastleggings-URL wordt nooit weggelakt, omdat die deel uitmaakt van het ondertekende manifest: een tekstweglakking waarvan de tekst ook in de vastleggings-URL of de uiteindelijke URL staat, wordt overal elders toegepast, de tekst blijft in die URL staan en de regel wordt vermeld in `rules_in_capture_url` en in redaction_manifest.json (leakage_check.capture_url). Alleen vastleggingen van webpagina's kunnen worden weggelakt (een vergelijking, certificering of weglakking wordt geweigerd met 422 REDACTION_SOURCE_UNSUPPORTED).\n\nEen weglakking van een testvastlegging (gemaakt met een testsleutel, in de sandbox of door een account zonder gekoppeld factureringsaccount) is een testpakket, ongeacht welke sleutel erom vraagt: met watermerk, `test_mode: true` in manifest.json, een testrij (key_env test) die niet wordt geteld en geen maandelijkse eenheid verbruikt. include_eidas bij zo'n weglakking wordt geweigerd met 403 EIDAS_REQUIRES_LIVE_KEY (details.reason).\n\nHet weglakkingsrapport (evidence.pdf) wordt weergegeven in `language` (standaard: de taal van de bronvastlegging), inclusief de hoofdtekst, het voorblad en de testbanner.\n\nVoordat er iets wordt weggelakt, wordt elk bestand dat de weglakking leest of overneemt (de screenshot, page.html, domtextcontent.txt, metadata.json, forensic_log.json, chain_of_custody.json, text_layout.json) en dat in het bronmanifest met een SHA-256 staat, opnieuw gehasht (redaction_manifest.json derived_from.source_files_rehashed); een afwijking of een ontbrekend bestand geeft 422 REDACTION_SOURCE_INTEGRITY_FAILED en er wordt niets uitgegeven, zodat een beschadigde of gewijzigde bron nooit opnieuw wordt verzegeld onder een nieuwe handtekening. Bestanden die het weggelakte pakket weglaat, worden niet gelezen. Een synchrone weglakking waarvan het lezen van de bron te lang duurde, stopt voordat er iets wordt gereserveerd, met 500 CAPTURE_TIME_BUDGET_EXCEEDED.\n\nEen weglakking van een vastlegging onder legal hold staat ook onder legal hold (legal_hold_source \"derived:<capture id>\"), net als elke weglakking van een vastlegging waarop een legal hold wordt ingesteld; het opheffen van de legal hold van de vastlegging heft die legal holds ook op, en zolang de vastlegging onder legal hold blijft, kunnen ze niet afzonderlijk worden opgeheven (409 LEGAL_HOLD_SOURCE). De captured_at van een weglakkingsrij is het vastleggingstijdstip van de bron; redacted_at is het moment waarop de weglakking is gemaakt.\n\nDe weglakking faalt veilig: wanneer de controle na het weglakken een weggelakte term nog ergens in het pakket vindt buiten de vastleggings-URL en de uiteindelijke URL, wordt er geen pakket uitgegeven en geeft het verzoek 422 REDACTION_LEAK_DETECTED terug (`error.details` `{ files, rule_indexes, metadata_fields, in_capture_url }`; de term zelf wordt nooit herhaald). redaction_manifest.json legt `leakage_check.fail_closed: true` vast.\n\nWaarden worden op tekengrenzen ingekort (een teken wordt nooit gesplitst): zoekteksten tot 2000 tekens, redenen tot 200, reason_summary tot 500, custodian-velden tot 200. Redenen, reason_summary en custodian-velden worden op het weglakkingscertificaat afgedrukt en moeten platte tekst zijn: een stuurteken, onzichtbaar teken of tekstrichtingsteken wordt geweigerd met 400.\n\nTijdbudget en nieuwe pogingen: het verzoek antwoordt binnen de limiet van 29 seconden van API Gateway. Het verzegelen gebeurt binnen een tijdbudget dat vanaf het verzoek wordt geteld; als de gekwalificeerde tijdstempel (include_eidas) niet binnen het budget past, wordt hij niet aangevraagd en wordt het pakket geleverd met alleen de OpenTimestamps-verankering (timestamps.eidas status not_obtained, reden time_budget, niet in rekening gebracht), en een OpenTimestamps-aanvraag die niet past, wordt weggelaten (chain_of_custody.json legt ots_skipped vast met reden time_budget). Stuur een Idempotency-Key mee, zodat een nieuwe poging (bijvoorbeeld na een 504 van een gateway of een netwerkfout) dezelfde weglakking teruggeeft in plaats van een tweede aan te maken en in rekening te brengen. Een nieuwe poging terwijl het eerste verzoek nog loopt, geeft 409 IDEMPOTENCY_IN_PROGRESS met de weglakkings-ID in `error.details.capture_id` (poll daarmee GET /captures/{capture_id}). Een latere nieuwe poging geeft 201 met de oorspronkelijke body en `Idempotent-Replay: true`. Een weglakking die mislukt, geeft haar sleutel vrij, zodat een nieuwe poging met dezelfde sleutel opnieuw wordt uitgevoerd.\n\nRate limit per account in vensters van 60 seconden (alle API-sleutels van het account en de bijbehorende dashboardsessie delen één venster). Zodra de limiter is uitgevoerd, bevatten antwoorden van deze route de headers `RateLimit-Policy` en `RateLimit` (gebaseerd op IETF draft-ietf-httpapi-ratelimit-headers) plus de oudere `X-RateLimit-Limit`, `X-RateLimit-Remaining` en `X-RateLimit-Reset`. Een 429 voegt `Retry-After` toe.\n\nEen geïmporteerd bewijspakket (source import, ID's met prefix `imp_`) kan niet worden weggelakt: 422 IMPORT_NOT_SUPPORTED.",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "boxes": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "x": {
                          "type": "number",
                          "minimum": 0
                        },
                        "y": {
                          "type": "number",
                          "minimum": 0
                        },
                        "w": {
                          "type": "number",
                          "minimum": 0
                        },
                        "h": {
                          "type": "number",
                          "minimum": 0
                        },
                        "reason": {
                          "type": "string",
                          "maxLength": 200
                        }
                      },
                      "required": [
                        "x",
                        "y",
                        "w",
                        "h"
                      ]
                    },
                    "maxItems": 200,
                    "description": "Rechthoeken in pixels van de screenshot van de bronvastlegging (x en y vanaf de linkerbovenhoek, w en h de breedte en hoogte). Als alle vier de waarden van een vak tussen 0 en 1 liggen, worden ze gelezen als fracties van de breedte en hoogte van de screenshot. Een vak dat volledig buiten de screenshot ligt, kleurt niets en staat in ignored_boxes; een vak dat over een rand uitsteekt, wordt bij de rand afgesneden en staat in clipped_boxes. Als elk vak erbuiten valt en geen weglakking van tekst of metadata iets verandert, wordt het verzoek geweigerd met 400 REDACTION_NO_OPS, voordat er iets in rekening wordt gebracht."
                  },
                  "text_redactions": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "match": {
                          "oneOf": [
                            {
                              "type": "string",
                              "description": "Letterlijke tekst.",
                              "minLength": 1
                            },
                            {
                              "type": "object",
                              "properties": {
                                "pattern": {
                                  "type": "string",
                                  "description": "Reguliere expressie, 1 tot 500 tekens. Patronen met geneste kwantoren zoals (a+)+ worden geweigerd met 400.",
                                  "minLength": 1,
                                  "maxLength": 500
                                },
                                "flags": {
                                  "type": "string",
                                  "description": "Een van g, i, m, s, u, elk maximaal één keer. y (sticky) wordt geweigerd met 400: die zou maar op één positie overeenkomen en andere voorkomens laten staan.",
                                  "pattern": "^[gimsu]*$"
                                }
                              },
                              "required": [
                                "pattern"
                              ]
                            }
                          ]
                        },
                        "case_insensitive": {
                          "type": "boolean",
                          "default": false,
                          "description": "Zoek een letterlijke string zonder onderscheid tussen hoofdletters en kleine letters (gebruik voor een reguliere expressie de vlag i)."
                        },
                        "reason": {
                          "type": "string",
                          "maxLength": 200,
                          "description": "Platte tekst, afgedrukt op het weglakkingscertificaat."
                        }
                      },
                      "required": [
                        "match"
                      ]
                    },
                    "maxItems": 200
                  },
                  "metadata_fields": {
                    "type": "array",
                    "description": "Veldpaden in metadata.json (een segment `*` komt overeen met elk item van een array), of de namen `ip_dns`, `cookie_names` en `authorization` (zie hierboven).",
                    "items": {
                      "oneOf": [
                        {
                          "type": "string",
                          "description": "Veldpad in metadata.json."
                        },
                        {
                          "type": "object",
                          "properties": {
                            "field": {
                              "type": "string"
                            },
                            "reason": {
                              "type": "string"
                            }
                          },
                          "required": [
                            "field"
                          ]
                        }
                      ]
                    },
                    "maxItems": 100
                  },
                  "reason_summary": {
                    "type": "string",
                    "maxLength": 500,
                    "description": "Platte tekst, afgedrukt op het weglakkingscertificaat."
                  },
                  "custodian": {
                    "type": "object",
                    "properties": {
                      "name": {
                        "type": "string"
                      },
                      "title": {
                        "type": "string"
                      }
                    },
                    "description": "Afgedrukt op het weglakkingsrapport. name en title volgen de regel voor platte tekst (zie PlainText)."
                  },
                  "language": {
                    "type": "string",
                    "description": "Taal van de leesbare PDF's. Een van: en, de, it, fr, es, pt_BR, cs, el, hi, hu, id, ja, ko, nl, pl, ro, ru, sv, tr, vi, zh_CN, zh_TW, ar, he, da, fi, nb, sk, uk. Hoofdletters, koppeltekens en regio worden getolereerd (\"DE\", \"fr-FR\" en \"PT-BR\" worden geaccepteerd); alleen \"pt\" of \"zh\" wordt geweigerd. Een niet-ondersteunde waarde geeft 400 MALFORMED_REQUEST. Optioneel: zonder deze waarde (of als deze leeg is) is het rapport in de taal van de bronvastlegging."
                  },
                  "include_eidas": {
                    "type": "boolean",
                    "description": "Zoals in CaptureOptions.include_eidas.",
                    "default": false
                  },
                  "webhook_url": {
                    "type": "string",
                    "description": "https-URL die redaction.completed ontvangt. Die wordt direct na het 201-antwoord verzonden, niet ervoor. De URL moet https zijn en mag niet verwijzen naar een privé-, loopback-, link-local-, carrier-grade NAT- of cloudmetadata-adres (dezelfde regels als voor een vastleggings-URL; anders 422 SSRF_BLOCKED). Een lege waarde of een waarde met alleen witruimte betekent geen webhook. Vereist een API-sleutel: vanuit een dashboardsessie verzonden wordt het geweigerd met 400 WEBHOOK_REQUIRES_API_KEY, omdat webhooks worden ondertekend met het webhook-secret van de sleutel.",
                    "format": "uri"
                  },
                  "rule_commitment_salt": {
                    "type": "string",
                    "description": "Optioneel. Uw eigen salt van 32 bytes voor de regelcommitments (redaction_manifest.json rule_commitments), als 64 hexadecimale cijfers of base64 / base64url; een salt die uit één herhaalde byte bestaat, wordt geweigerd (400). Stuur deze mee om de salt al vóór de aanroep in handen te hebben: zonder deze maakt de server er een aan en geeft die alleen terug in het 201-antwoord, zodat bij een antwoord dat door een time-out verloren gaat (504 van de gateway terwijl de weglakking toch is voltooid) ook de salt verloren gaat, en een Idempotency-Key-replay bevat hem niet. ProofSnap bewaart de salt niet."
                  }
                },
                "description": "Minstens één van boxes, text_redactions of metadata_fields."
              },
              "example": {
                "text_redactions": [
                  {
                    "match": "jane.doe@example.com",
                    "reason": "personal data"
                  }
                ],
                "reason_summary": "Personal data of a third party"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Weggelakt pakket gereed. Een idempotente replay van een eerdere weglakking met dezelfde Idempotency-Key geeft ook 201, met `Idempotent-Replay: true`: de oorspronkelijke body, met de huidige status en een vers vooraf ondertekende package_url (weggelaten als het pakket niet vooraf kan worden ondertekend; lees die dan met GET /captures/{capture_id}).",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              },
              "Idempotent-Replay": {
                "$ref": "#/components/headers/IdempotentReplay"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "redaction_id": {
                      "type": "string",
                      "example": "red_a1B2c3D4e5F6g7H8"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "completed"
                      ]
                    },
                    "redacted_from": {
                      "type": "string"
                    },
                    "redaction_count": {
                      "type": "integer",
                      "description": "Aantal toegepaste weglakacties."
                    },
                    "original_signature_verified": {
                      "type": "boolean",
                      "description": "Of de oorspronkelijke manifesthandtekening vóór het weglakken geldig was."
                    },
                    "leakage_warnings": {
                      "type": "integer",
                      "description": "Altijd 0: een weglakking waarbij de controle na het weglakken een achtergebleven term vindt, wordt geweigerd met 422 REDACTION_LEAK_DETECTED in plaats van uitgegeven. Behouden voor compatibiliteit."
                    },
                    "package_url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "package_url_expires_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "verify_url": {
                      "type": "string",
                      "description": "Link naar de openbare ProofSnap-verifier.",
                      "format": "uri"
                    },
                    "ignored_boxes": {
                      "type": "array",
                      "items": {
                        "type": "integer",
                        "minimum": 0
                      },
                      "description": "Nulgebaseerde indexen in de boxes van het verzoek, van de vakken die volledig buiten de screenshot lagen en niets hebben afgedekt. Ook vastgelegd in redaction_manifest.json (screenshot_boxes)."
                    },
                    "clipped_boxes": {
                      "type": "array",
                      "items": {
                        "type": "integer",
                        "minimum": 0
                      },
                      "description": "Nulgebaseerde indexen in de boxes van het verzoek, van de vakken die over de rand van de screenshot uitstaken en daar zijn afgesneden. Ook vastgelegd in redaction_manifest.json (screenshot_boxes)."
                    },
                    "screenshot_method": {
                      "type": "string",
                      "nullable": true,
                      "enum": [
                        "jpeg_dct_blocks",
                        "decode_repaint_jpeg",
                        null
                      ],
                      "description": "Hoe de vakken zijn ingekleurd: jpeg_dct_blocks (de gecomprimeerde blokken van de JPEG onder elk vak vervangen door zwarte blokken, elk ander blok ongewijzigd) of decode_repaint_jpeg (de screenshot gedecodeerd, ingekleurd en opnieuw gecodeerd). null als geen enkel vak iets heeft ingekleurd. Ook vastgelegd in redaction_manifest.json (screenshot_boxes.method)."
                    },
                    "painted_rects": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "x": {
                            "type": "integer",
                            "minimum": 0
                          },
                          "y": {
                            "type": "integer",
                            "minimum": 0
                          },
                          "w": {
                            "type": "integer",
                            "minimum": 0
                          },
                          "h": {
                            "type": "integer",
                            "minimum": 0
                          }
                        },
                        "required": [
                          "x",
                          "y",
                          "w",
                          "h"
                        ]
                      },
                      "description": "De gebieden die daadwerkelijk zijn zwartgemaakt, in pixels van de screenshot, één per toegepast vak in de volgorde van de vakken in het verzoek, zonder de genegeerde vakken. Met jpeg_dct_blocks is elk gebied het vak, naar buiten afgerond op hele blokken, zodat het het vak omvat; met decode_repaint_jpeg is het het vak, begrensd tot de screenshot en naar buiten afgerond op hele blokken van 8 x 8 van het nieuwe bestand, die na de hercodering worden zwartgemaakt zodat de compressie geen kleur uit de omgeving in het vak kan brengen. Ook vastgelegd in redaction_manifest.json (screenshot_boxes.painted_rects)."
                    },
                    "text_rule_rects": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Afzonderlijke gebieden van de screenshot die alleen voor de tekstweglakkingen zijn zwartgemaakt, zonder de gebieden die een vak bevatten (een gebied waarop twee weglakregels van toepassing zijn, telt één keer): in de tekstblokken van de text_layout.json van de bron waarvan de tekst door een weglakregel wordt getroffen, de geschatte tekstregels die de treffer bevatten, of het hele blok wanneer de schatting onzeker is. Vermeld in redaction_manifest.json (screenshot_boxes.from_text_rules)."
                    },
                    "text_occurrences_not_located": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Voorkomens van de tekstweglakkingen in de paginatekst die niet op de screenshot konden worden gevonden en daar niet zijn zwartgemaakt (tekst die niet in text_layout.json van de bron staat, zoals tekst in een afbeelding; een blok dat te groot is om voor één term zwart te maken; gebieden voorbij de limiet van één weglakking; een screenshot waarop niet kon worden getekend). Voorkomens in een blok dat volledig buiten de screenshot ligt (daar valt niets zwart te maken), worden hier niet meegeteld; redaction_manifest.json vermeldt ze als occurrences_not_on_screenshot. Nooit meer dan het aantal voorkomens in de paginatekst. Controleer de screenshot en voeg vakken toe. redaction_manifest.json (leakage_check.screenshot_text_advisory) bevat de aantallen per regel."
                    },
                    "text_variants_remaining": {
                      "type": "object",
                      "description": "Ter informatie, nooit een fout: per letterlijke tekstregel de voorkomens die in domtextcontent.txt en in de tekst van page.html van dit pakket zijn achtergebleven, die de regel niet trof maar die wel overeenkomen wanneer hoofdletters en diakritische tekens worden genegeerd (bijvoorbeeld \"boskovické\" bij de hoofdlettergevoelige regel \"Boskovic\"), en of de term in de vastleggings-URL of de uiteindelijke URL staat, die nooit worden weggelakt. `total` telt per regel de grootste van de twee aantallen op (beide bestanden bevatten dezelfde paginatekst). Alleen aantallen, nooit de tekst. Regex-regels worden niet geteld. Staat ook in redaction_manifest.json (leakage_check.text_variants_advisory). Aanwezig wanneer het verzoek tekstweglakkingen bevat.",
                      "properties": {
                        "total": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "per_rule": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "rule_index": {
                                "type": "integer",
                                "minimum": 0
                              },
                              "in_domtextcontent": {
                                "type": "integer",
                                "minimum": 0
                              },
                              "in_page_html_text": {
                                "type": "integer",
                                "minimum": 0
                              },
                              "in_capture_url": {
                                "type": "boolean"
                              }
                            }
                          }
                        }
                      }
                    },
                    "rules_in_capture_url": {
                      "type": "array",
                      "items": {
                        "type": "integer",
                        "minimum": 0
                      },
                      "description": "Aanwezig als het verzoek tekstweglakkingen bevat. Op nul gebaseerde indexen van de tekstregels waarvan de tekst ook in de vastleggings-URL of de uiteindelijke URL staat. Die URL's worden nooit gewijzigd: de tekst is overal elders verwijderd en blijft daar zichtbaar. Leeg als geen enkele regel ermee overeenkomt. Ook in redaction_manifest.json (leakage_check.capture_url, `remains_in_capture_url: true` per regel) en in het weglakkingsrapport."
                    },
                    "rule_commitment_salt": {
                      "type": "string",
                      "pattern": "^[0-9a-f]{64}$",
                      "description": "Aanwezig als het verzoek tekstweglakkingen bevat. De salt van 32 bytes (hex; de rule_commitment_salt van het verzoek als die is meegestuurd) van redaction_manifest.json rule_commitments: per tekstregel de SHA-256 van de salt van de regel gevolgd door de UTF-8-bytes van de canonieke JSON van de regel {\"match\": <text, or {\"pattern\", \"flags\"}>, \"case_insensitive\": <bool>}, waarbij de salt van de regel HMAC-SHA256 is met deze salt als sleutel over de UTF-8-tekst \"rule:<rule_index>\" (zodat één regel kan worden aangetoond door alleen de eigen salt ervan te onthullen, zonder de andere). De regels worden niet opgeslagen en de salt wordt alleen in dit antwoord teruggegeven: niet bij een herhaling met Idempotency-Key, niet in een webhook, het gebeurtenissenlogboek of GET /captures/{capture_id}, en ProofSnap bewaart hem niet. Bewaar hem bij uw administratie: daarmee kunt u aantonen welke regels de weglakking heeft toegepast."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "MALFORMED_REQUEST (details.reason; bijvoorbeeld een reden die geen platte tekst is, of de regex-vlag y), REDACTION_NO_OPS (niets opgegeven, of niets gevonden) of INVALID_URL (webhook_url niet https). WEBHOOK_REQUIRES_API_KEY: webhook_url verzonden vanuit een dashboardsessie (webhooks vereisen een API-sleutel).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "QUOTA_EXCEEDED of PAYMENT_REQUIRED.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "403": {
            "description": "FORBIDDEN: niet uw vastlegging. EIDAS_REQUIRES_LIVE_KEY: include_eidas met een testsleutel, of voor een weglakking van een testvastlegging (dan met details.reason). EMAIL_NOT_VERIFIED: een dashboardsessie waarvan het e-mailadres niet is bevestigd. PLAN_ARCHIVE_ONLY: het abonnement bewaart alleen het archief en maakt geen nieuw werk aan. PLAN_API_READ_ONLY: een API-sleutel van een Website Archive-account (de sleutel is alleen-lezen; gebruik het dashboard). PLAN_API_READ_ONLY met details.reason \"session_outside_dashboard\": een dashboardsessie van een Website Archive-account waarvan het verzoek geen Origin-header heeft, of een die geen adres van het ProofSnap-dashboard is (nieuw werk op dat abonnement wordt alleen in het dashboard aangemaakt; een zachte drempel, geen beveiligingsgrens).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "404": {
            "description": "CAPTURE_NOT_FOUND.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "409": {
            "description": "REDACTION_NOT_READY: de bronvastlegging is niet voltooid. IDEMPOTENCY_IN_PROGRESS: een verzoek met dezelfde Idempotency-Key loopt nog (details.capture_id is de ID van de weglakking om te pollen met GET /captures/{capture_id}). COMPANY_READ_ONLY: de bron hoort bij een bedrijf dat als bedrijfsarchief wordt bewaard.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "422": {
            "description": "REDACTION_SCREENSHOT_UNREADABLE: de beeldgegevens van de screenshot zijn onleesbaar of de screenshot overschrijdt de limiet voor weglakking met vakken (150 megapixel voor een JPEG zoals de vastlegging die schrijft, 60 megapixel voor een PNG of een andere JPEG); weglakkingen van tekst en metadata werken nog steeds zonder vakken. REDACTION_LEAK_DETECTED: een weggelakte term is buiten de vastleggings-URL blijven staan (details { files, rule_indexes, metadata_fields, in_capture_url }; in_capture_url is true als de tekst van de geweigerde regel ook in de vastleggings-URL of de uiteindelijke URL staat, waar die nooit wordt meegeteld). REDACTION_SOURCE_UNSUPPORTED: de bron is geen vastlegging van een webpagina (details { source } of { manifest_type }). SSRF_BLOCKED: webhook_url verwijst naar een geblokkeerd adres. REDACTION_SOURCE_INTEGRITY_FAILED: een bestand van het bronpakket komt niet overeen met de SHA-256 in het ondertekende manifest, of een vermeld bestand ontbreekt (details { mismatched, missing }, alleen bestandsnamen); er is niets weggelakt of in rekening gebracht. IDEMPOTENCY_KEY_REUSED: de Idempotency-Key is al gebruikt met een andere verzoekbody (of vanuit een andere sleutelomgeving).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED: te veel verzoeken in het huidige venster van 60 seconden. `error.details.retry_after_seconds` herhaalt Retry-After. RateLimit meldt r=0 en het aantal seconden tot het venster opnieuw begint. Ook RATE_LIMITED, voor een dashboardsessie van Website Archive: het daglimiet van 100 eenheden per UTC-dag, gedeeld door losse vastleggingen en batchvastleggingen (één eenheid per URL), compare, redact en certify-hash (één eenheid per verzoek); een verzoek dat niet past, wordt in zijn geheel geweigerd (details.daily_limit, details.remaining, details.action \"wa_session_captures\"; Retry-After en details.retry_after_seconds geven het aantal seconden tot de volgende UTC-dag, en de RateLimit- en X-RateLimit-*-headers per minuut ontbreken). Een korte weigering met alleen details.retry_after_seconds en Retry-After: 1 betekent een gelijktijdige update van de teller: probeer het opnieuw. Bij een weigering die niet aan de aanroeper ligt, worden de eenheden teruggegeven; werk dat met 202 is geaccepteerd en later mislukt, behoudt zijn eenheid. Een zachte drempel, geen beveiligingsgrens.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "RATE_LIMITED",
                    "message": "Too many requests. See Retry-After header.",
                    "details": {
                      "retry_after_seconds": 42
                    }
                  },
                  "request_id": "..."
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR: onverwachte serverfout; probeer het opnieuw met exponential backoff. CAPTURE_TIME_BUDGET_EXCEEDED: de synchrone weglakking heeft haar tijdsbudget opgebruikt en is gestopt voordat er een pakket is uitgegeven; er is niets in rekening gebracht. Met `details.reason` `source_read` nam het lezen van het bronpakket het grootste deel van het budget in beslag: het pakket is op dit moment te groot om weg te lakken, probeer het later opnieuw.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/EidasOrAuthUnavailable"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ]
      }
    },
    "/compare": {
      "post": {
        "tags": [
          "Vergelijken en weglakken"
        ],
        "operationId": "compareCaptures",
        "summary": "Twee vastleggingen vergelijken",
        "description": "Vergelijkt twee van uw voltooide vastleggingen (paginatekst, de op de pagina getoonde afbeeldingen, metadata) en maakt een ondertekend vergelijkingspakket. Retourneert 202; poll GET /captures/{compare_id}, dat daarna type, compared en diff_summary bevat. De afbeeldingen worden vergeleken op basis van de afbeeldingsinventaris die elke bronvastlegging heeft verzegeld (image_inventory.json: het adres van elke getoonde afbeelding en de SHA-256 van de bytes ervan), nooit pixel voor pixel; de resultaten staan in image_changes.json en in diff_summary.image_changes. Een bron die is vastgelegd voordat vastleggingen een afbeeldingsinventaris registreerden, wordt nog steeds vergeleken, maar zonder afbeeldingsvergelijking (diff_summary.image_changes.status not_run). Telt als één vastlegging. Gelijk met gelijk (2026-10-14): twee vastleggingen die met een ander apparaatprofiel of met andere vastleggingsinstellingen zijn weergegeven, worden geweigerd met 422 COMPARE_PROFILE_MISMATCH (details.differing, details.before, details.after: alleen de afwijkende velden, geïnjecteerde CSS en JavaScript als hun SHA-256), tenzij allow_profile_mismatch true is, en twee vastleggingen van verschillende pagina's met 422 COMPARE_URL_MISMATCH (details.before en details.after: url en final_url als origin en pad), tenzij allow_url_mismatch true is. De controle wordt eerst uitgevoerd op de twee vastleggingsrijen (voordat er iets wordt gelezen of gereserveerd) en daarna op de verzegelde metadata.json en forensic_log.json van beide pakketten binnen de broncontrole hieronder; een afwijking die pas tijdens de vergelijking wordt gevonden, laat de vergelijking mislukken met die code en geeft de maandelijkse eenheid terug. De tekst wordt regel voor regel vergeleken met een deterministische regeldiff (geen timeout): diff_summary telt regels (text_unit line). Alleen webvastleggingen (gemaakt via de API of door een schema) kunnen worden vergeleken: een hashcertificering, een vergelijking of een weggelakt pakket wordt geweigerd met 422 COMPARE_SOURCE_UNSUPPORTED (`details.capture_id`, `details.source`), voordat er een pakket wordt gelezen en voordat er iets wordt gereserveerd. Voordat er iets wordt vergeleken of gereserveerd, wordt de SHA-256 opnieuw berekend van elk bestand dat met een SHA-256 in de manifest.json van elk bronpakket staat; een bron met een gewijzigd of ontbrekend bestand, of een manifest dat geen enkel bestand met een SHA-256 vermeldt, wordt geweigerd met 422 COMPARE_SOURCE_INTEGRITY_FAILED (geen vergelijking, geen rij, geen maandelijkse eenheid). Alleen die vermelde bestanden worden vergeleken: een bestand in een bronpakket dat het manifest niet met een SHA-256 vermeldt (bijvoorbeeld een toegevoegde text_layout.json, image_inventory.json, page.html of domtextcontent.txt), wordt als afwezig behandeld. De controle wordt bij het verzoek zelf uitgevoerd wanneer beide bronpakketten 50 MB of kleiner zijn en de controle binnen ongeveer 10 seconden klaar is; anders wordt het verzoek geaccepteerd en herhaalt de vergelijking de controle, en een bron die dan faalt, laat de vergelijking mislukken (GET /captures/{compare_id} toont status failed met error.code COMPARE_SOURCE_INTEGRITY_FAILED) en geeft de maandelijkse eenheid terug. Een voltooide vergelijking registreert deze controle in compare.json `sources[].source_files_reverified` en in comparison_method.json `source_verification`. /compare verstuurt geen webhooks: een webhook_url in de body wordt genegeerd. Poll GET /captures/{compare_id}.\n\nEen vergelijking waarbij een testvastlegging betrokken is (gemaakt met een testsleutel, in de sandbox of door een account zonder gekoppeld factureringsaccount) is een testpakket, ongeacht welke sleutel erom vraagt: met watermerk, `test_mode: true` in manifest.json, `test_mode: true` voor die bron in compare.json `sources[]`, en een testrij (key_env test) die niet wordt gefactureerd en geen maandelijkse eenheid verbruikt. include_eidas met een testbron wordt geweigerd met 403 EIDAS_REQUIRES_LIVE_KEY (details.reason).\n\nEr geldt een rate limit (snelheidslimiet) per account in vensters van 60 seconden (alle API-sleutels van het account en de dashboardsessie ervan delen één venster). Zodra de limiter heeft gedraaid, bevatten antwoorden van deze route de headers `RateLimit-Policy` en `RateLimit` (gebaseerd op IETF draft-ietf-httpapi-ratelimit-headers) plus de oudere headers `X-RateLimit-Limit`, `X-RateLimit-Remaining` en `X-RateLimit-Reset`. Een 429 voegt `Retry-After` toe.\n\nStuur een Idempotency-Key mee, zodat een nieuwe poging (bijvoorbeeld na een 504) het eerste antwoord retourneert in plaats van een tweede, gefactureerde uitvoering (24 uur, dezelfde regels als bij POST /captures). De sleutel wordt vóór de broncontrole geclaimd, dus een nieuwe poging terwijl het eerste verzoek nog loopt, retourneert 409 IDEMPOTENCY_IN_PROGRESS; een verzoek dat door de broncontrole of het quotum wordt geweigerd, geeft zijn sleutel vrij, zodat een nieuwe poging met dezelfde sleutel opnieuw wordt gecontroleerd.\n\nEen geïmporteerd bewijspakket (bron import, ID's met het voorvoegsel `imp_`) kan niet worden vergeleken: 422 IMPORT_NOT_SUPPORTED (details.capture_id).",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "capture_id_1": {
                    "type": "string",
                    "description": "De eerdere vastlegging."
                  },
                  "capture_id_2": {
                    "type": "string",
                    "description": "De latere vastlegging. Moet verschillen van capture_id_1."
                  },
                  "language": {
                    "type": "string",
                    "description": "Taal van de vergelijkings-PDF. Alleen de exacte code; bij elke andere waarde wordt zonder melding Engels gebruikt.",
                    "enum": [
                      "en",
                      "de",
                      "it",
                      "fr",
                      "es",
                      "pt_BR",
                      "cs",
                      "el",
                      "hi",
                      "hu",
                      "id",
                      "ja",
                      "ko",
                      "nl",
                      "pl",
                      "ro",
                      "ru",
                      "sv",
                      "tr",
                      "vi",
                      "zh_CN",
                      "zh_TW",
                      "ar",
                      "he",
                      "da",
                      "fi",
                      "nb",
                      "sk",
                      "uk"
                    ]
                  },
                  "include_eidas": {
                    "type": "boolean",
                    "description": "Zoals in CaptureOptions.include_eidas.",
                    "default": false
                  },
                  "include_rule_902": {
                    "type": "boolean",
                    "description": "Voegt een niet-ondertekende certificeringstemplate (rule_902_certification.pdf, zie CaptureOptions.include_rule_902) voor de vergelijking toe. Niet beschikbaar voor vastleggingen die verschillend zijn gerenderd: met allow_profile_mismatch true en een verschil in profiel of instellingen volgt 422 RULE902_NOT_AVAILABLE_FOR_MISMATCH. Momenteel uitgeschakeld: de certificeringstemplate is tijdelijk uitgezet, dus dit veld wordt geaccepteerd en genegeerd (er wordt geen rule_902_certification.pdf gegenereerd of in het pakket opgenomen) en het antwoord bevat de waarschuwing RULE902_DISABLED.",
                    "default": false
                  },
                  "allow_profile_mismatch": {
                    "type": "boolean",
                    "default": false,
                    "description": "Vergelijk twee vastleggingen, ook als ze met een ander apparaatprofiel zijn gemaakt (options.viewport mobile of desktop, viewport_width, viewport_height, device_scale_factor; verzegeld: viewportgrootte, apparaatschaalfactor, mobiele emulatie, user agent zonder versienummers) of met andere vastleggingsinstellingen die bepalen hoe een pagina wordt weergegeven (dark_mode, block_ads, block_resources, inject_css, inject_js op SHA-256, expand_collapsed, dismiss_consent, dismiss_popups, volledige pagina). Zonder deze optie wordt zo'n paar geweigerd met 422 COMPARE_PROFILE_MISMATCH. Met deze optie zijn diff_summary.text_verdict en images_verdict undetermined, is changed null tenzij een inhoudelijk metadataveld verschilt, en wordt include_rule_902 geweigerd."
                  },
                  "allow_url_mismatch": {
                    "type": "boolean",
                    "default": false,
                    "description": "Vergelijk twee vastleggingen van verschillende pagina's. Twee vastleggingen gelden als dezelfde pagina wanneer een adres van de ene (de opgevraagde url, de url en final_url die de metadata.json ervan registreert) na normalisatie gelijk is aan een adres van de andere (host in kleine letters, http en https gelijkgesteld, standaardpoort, fragment, een afsluitende slash en de vluchtige queryparameters weggelaten). Zonder deze optie worden verschillende pagina's geweigerd met 422 COMPARE_URL_MISMATCH."
                  }
                },
                "required": [
                  "capture_id_1",
                  "capture_id_2"
                ]
              },
              "example": {
                "capture_id_1": "cap_a1B2c3D4e5F6g7H8",
                "capture_id_2": "cap_h8G7f6E5d4C3b2A1"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Geaccepteerd.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              },
              "X-Quota-Limit": {
                "$ref": "#/components/headers/QuotaLimit"
              },
              "X-Quota-Remaining": {
                "$ref": "#/components/headers/QuotaRemaining"
              },
              "X-Quota-Used": {
                "$ref": "#/components/headers/QuotaUsed"
              },
              "X-Quota-Cap": {
                "$ref": "#/components/headers/QuotaCap"
              },
              "X-Quota-Org-Limit": {
                "$ref": "#/components/headers/QuotaOrgLimit"
              },
              "X-Quota-Org-Used": {
                "$ref": "#/components/headers/QuotaOrgUsed"
              },
              "X-Sandbox-Limit": {
                "$ref": "#/components/headers/SandboxLimit"
              },
              "X-Sandbox-Remaining": {
                "$ref": "#/components/headers/SandboxRemaining"
              },
              "X-Sandbox-Used": {
                "$ref": "#/components/headers/SandboxUsed"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "compare_id": {
                      "type": "string",
                      "example": "cmp_a1B2c3D4e5F6g7H8"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "processing"
                      ]
                    },
                    "poll_path": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    },
                    "warnings": {
                      "$ref": "#/components/schemas/Rule902DisabledWarnings"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "MALFORMED_REQUEST: ongeldige JSON, een ontbrekende ID, twee keer dezelfde ID, of allow_profile_mismatch / allow_url_mismatch die geen boolean is.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "QUOTA_EXCEEDED of PAYMENT_REQUIRED.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "403": {
            "description": "FORBIDDEN: een vastlegging is niet van u. EIDAS_REQUIRES_LIVE_KEY: include_eidas met een testsleutel, of met een testvastlegging als bron (dan met details.reason). EMAIL_NOT_VERIFIED: een dashboardsessie waarvan het e-mailadres niet is bevestigd. PLAN_ARCHIVE_ONLY: het abonnement bewaart alleen het archief en maakt geen nieuw werk aan. PLAN_API_READ_ONLY: een API-sleutel van een Website Archive-account (de sleutel is alleen-lezen; gebruik het dashboard). PLAN_API_READ_ONLY met details.reason \"session_outside_dashboard\": een dashboardsessie van een Website Archive-account waarvan het verzoek geen Origin-header heeft, of een die geen adres van het ProofSnap-dashboard is (nieuw werk op dat abonnement wordt alleen in het dashboard aangemaakt; een zachte drempel, geen beveiligingsgrens).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "404": {
            "description": "CAPTURE_NOT_FOUND.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "409": {
            "description": "COMPARE_NOT_READY: beide vastleggingen moeten voltooid zijn. COMPANY_READ_ONLY: een bron hoort bij een bedrijf dat als bedrijfsarchief wordt bewaard (er wordt niets nieuws van gemaakt).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "422": {
            "description": "COMPARE_SOURCE_INTEGRITY_FAILED: een bestand van een bronvastlegging komt niet overeen met de SHA-256 die het ondertekende manifest vermeldt, of ontbreekt; er is niets vergeleken of in rekening gebracht. `details.capture_id`, `details.mismatched`, `details.missing` noemen de eerste bron die faalt; `details.reason` is aanwezig wanneer het bronmanifest zelf niet bruikbaar is (bijvoorbeeld \"the source manifest lists no file with a SHA-256\" of \"the source manifest lists no files\"); `details.sources[]` somt elke falende bron op met dezelfde velden. Verifieer de bronvastlegging (POST /captures/{capture_id}/verify). COMPARE_SOURCE_UNSUPPORTED: een bron is geen webvastlegging (een hashcertificering, een vergelijking of een weggelakt pakket); er is niets gelezen, vergeleken of in rekening gebracht. `details.capture_id` noemt die bron en `details.source` het soort ervan wanneer de rij dat vastlegt. Vergelijk in plaats daarvan de oorspronkelijke webvastleggingen. COMPARE_PROFILE_MISMATCH: de twee vastleggingen zijn gemaakt met een ander apparaatprofiel of andere vastleggingsinstellingen; `details.differing` somt de veldnamen op (rijniveau: viewport, viewport_width, viewport_height, device_scale_factor, dark_mode, block_ads, block_resources, inject_css, inject_js, expand_collapsed, dismiss_consent, dismiss_popups, full_page; verzegeld niveau: viewport_width, viewport_height, device_scale_factor, is_mobile, user_agent en dezelfde instellingen), `details.before` en `details.after` de waarden ervan. Stuur allow_profile_mismatch: true mee om toch te vergelijken. COMPARE_URL_MISMATCH: de twee vastleggingen betreffen verschillende pagina's; `details.before` en `details.after` geven url en final_url als origin en pad. Stuur allow_url_mismatch: true mee om toch te vergelijken. RULE902_NOT_AVAILABLE_FOR_MISMATCH: include_rule_902 met allow_profile_mismatch voor vastleggingen die verschillend zijn gerenderd; `details.differing`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED: te veel verzoeken in het huidige venster van 60 seconden. `error.details.retry_after_seconds` herhaalt Retry-After. RateLimit meldt r=0 en het aantal seconden tot het venster opnieuw begint. Ook RATE_LIMITED, voor een dashboardsessie van Website Archive: het daglimiet van 100 eenheden per UTC-dag, gedeeld door losse vastleggingen en batchvastleggingen (één eenheid per URL), compare, redact en certify-hash (één eenheid per verzoek); een verzoek dat niet past, wordt in zijn geheel geweigerd (details.daily_limit, details.remaining, details.action \"wa_session_captures\"; Retry-After en details.retry_after_seconds geven het aantal seconden tot de volgende UTC-dag, en de RateLimit- en X-RateLimit-*-headers per minuut ontbreken). Een korte weigering met alleen details.retry_after_seconds en Retry-After: 1 betekent een gelijktijdige update van de teller: probeer het opnieuw. Bij een weigering die niet aan de aanroeper ligt, worden de eenheden teruggegeven; werk dat met 202 is geaccepteerd en later mislukt, behoudt zijn eenheid. Een zachte drempel, geen beveiligingsgrens.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "RATE_LIMITED",
                    "message": "Too many requests. See Retry-After header.",
                    "details": {
                      "retry_after_seconds": 42
                    }
                  },
                  "request_id": "..."
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/EidasOrAuthUnavailable"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ]
      }
    },
    "/certify-hash": {
      "post": {
        "tags": [
          "Hashcertificering"
        ],
        "operationId": "certifyHash",
        "summary": "Bestandshashes certificeren",
        "description": "Stuur één SHA-256 (hash) of maximaal 100 (hashes). Het pakket bevat manifest.json (met uw hashes), manifest.sig, publickey.pem, evidence.pdf, de scripts in verification/ en, indien beschikbaar, manifest.json.ots.\n\n- Eén hash: synchroon, geeft 200 met het voltooide certificaat.\n- Meerdere hashes met combined: true: één certificaat met alle hashes, synchroon, geeft 200.\n- Meerdere hashes zonder combined: één onafhankelijk certificaat per hash, op de achtergrond gestart; geeft 202 met de certify_ids. Poll GET /captures/{certify_id}; met webhook_url stuurt elk certificaat certify.completed.\n\nElk certificaat telt als één vastlegging voor de meting, de maandelijkse limiet en de rate limit (N hashes zonder combined tellen als N; een N groter dan de limiet per venster geeft 400 RATE_LIMIT_COST_TOO_HIGH). Een `label` wordt op het certificaat afgedrukt: het wordt ingekort tot 200 tekens en moet platte tekst zijn (geen besturings-, onzichtbare of tekstrichtingstekens, anders 400). In een batch noemt een fout over één item dat item in `error.details.index`.\n\nRate limit per account in vensters van 60 seconden (alle API-sleutels van het account en de dashboardsessie delen één venster). Zodra de limiter heeft gedraaid, bevatten antwoorden van deze route de headers `RateLimit-Policy` en `RateLimit` (gebaseerd op het IETF-concept draft-ietf-httpapi-ratelimit-headers) plus de verouderde `X-RateLimit-Limit`, `X-RateLimit-Remaining` en `X-RateLimit-Reset`. Een 429 voegt `Retry-After` toe.\n\nStuur een Idempotency-Key mee, zodat een nieuwe poging (bijvoorbeeld na een 504) het eerste antwoord teruggeeft in plaats van een tweede, gefactureerde uitvoering (24 uur, dezelfde regels als POST /captures).",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "hash": {
                    "type": "string",
                    "description": "SHA-256 als 64 hexadecimale tekens (niet hoofdlettergevoelig). Gebruik hash of hashes."
                  },
                  "label": {
                    "type": "string",
                    "description": "Naam die voor het bestand wordt getoond. Langere waarden worden ingekort tot 200 tekens."
                  },
                  "hashes": {
                    "type": "array",
                    "items": {
                      "oneOf": [
                        {
                          "type": "string",
                          "description": "SHA-256 in hex."
                        },
                        {
                          "type": "object",
                          "properties": {
                            "hash": {
                              "type": "string"
                            },
                            "label": {
                              "type": "string"
                            }
                          },
                          "required": [
                            "hash"
                          ]
                        }
                      ]
                    },
                    "minItems": 1,
                    "maxItems": 100
                  },
                  "combined": {
                    "type": "boolean",
                    "description": "Certificeer alle hashes in één certificaat.",
                    "default": false
                  },
                  "algorithm": {
                    "type": "string",
                    "enum": [
                      "sha256"
                    ],
                    "default": "sha256"
                  },
                  "language": {
                    "type": "string",
                    "description": "Taal van de leesbare PDF's. Een van: en, de, it, fr, es, pt_BR, cs, el, hi, hu, id, ja, ko, nl, pl, ro, ru, sv, tr, vi, zh_CN, zh_TW, ar, he, da, fi, nb, sk, uk. Hoofdletters, koppeltekens en regio worden getolereerd (\"DE\", \"fr-FR\" en \"PT-BR\" worden geaccepteerd); alleen \"pt\" of \"zh\" wordt geweigerd. Een niet-ondersteunde waarde geeft 400 MALFORMED_REQUEST."
                  },
                  "case_reference": {
                    "type": "string",
                    "description": "Afgedrukt op het certificaat. Volgt de regel voor platte tekst (zie PlainText); maximaal 200 tekens.",
                    "maxLength": 200
                  },
                  "include_eidas": {
                    "type": "boolean",
                    "description": "Zoals in CaptureOptions.include_eidas.",
                    "default": false
                  },
                  "include_rule_902": {
                    "type": "boolean",
                    "description": "Voegt een niet-ondertekende certificeringstemplate toe (rule_902_certification.pdf, zie CaptureOptions.include_rule_902). Momenteel uitgeschakeld: de certificeringstemplate is tijdelijk uitgezet, dus dit veld wordt geaccepteerd en genegeerd (er wordt geen rule_902_certification.pdf gegenereerd of in het pakket opgenomen) en het antwoord bevat de waarschuwing RULE902_DISABLED.",
                    "default": false
                  },
                  "webhook_url": {
                    "type": "string",
                    "description": "https-URL die certify.completed ontvangt. De URL moet https zijn en mag niet verwijzen naar een privé-, loopback-, link-local-, carrier-grade NAT- of cloudmetadata-adres (dezelfde regels als voor een vastleggings-URL; anders 422 SSRF_BLOCKED). Een lege waarde of een waarde met alleen witruimte betekent geen webhook. Vereist een API-sleutel: vanuit een dashboardsessie verzonden wordt het geweigerd met 400 WEBHOOK_REQUIRES_API_KEY, omdat webhooks worden ondertekend met het webhook-secret van de sleutel.",
                    "format": "uri"
                  }
                }
              },
              "example": {
                "hash": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
                "label": "contract.pdf"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Certificaat gereed (één hash, of gecombineerd).",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CertifyResult"
                }
              }
            }
          },
          "202": {
            "description": "Er zijn meerdere onafhankelijke certificaten gestart. Een certificaat dat bij de uitvoering wordt geweigerd door de maandelijkse limiet of het sandboxtegoed, mislukt: de bijbehorende rij (GET /captures/{certify_id}) heeft status failed en error.code QUOTA_EXCEEDED.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "batch_id": {
                      "type": "string"
                    },
                    "count": {
                      "type": "integer"
                    },
                    "certify_ids": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "ID's van de certificaten die zijn aangemaakt (inclusief een certificaat waarvan de start is mislukt)."
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "processing"
                      ]
                    },
                    "certificates": {
                      "type": "array",
                      "description": "Eén item per ingediende hash, in volgorde. status failed: dat certificaat kon niet worden gestart (de bijbehorende rij wordt, als die is aangemaakt, als mislukt gemarkeerd); de overige lopen gewoon door.",
                      "items": {
                        "type": "object",
                        "properties": {
                          "certify_id": {
                            "type": "string",
                            "nullable": true
                          },
                          "hash_sha256": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "processing",
                              "failed"
                            ]
                          }
                        }
                      }
                    },
                    "failed_count": {
                      "type": "integer",
                      "description": "Hoeveel items van certificates niet konden starten. Als ze allemaal mislukten, is het antwoord in plaats daarvan 500 CONFIGURATION_ERROR."
                    },
                    "warnings": {
                      "$ref": "#/components/schemas/Rule902DisabledWarnings"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "MALFORMED_REQUEST (details.reason noemt het veld, bijvoorbeeld \"label must be a string\"; details.index noemt het item van een batch), INVALID_HASH (details.index in een batch) of INVALID_URL (webhook_url niet https, of met inloggegevens). WEBHOOK_REQUIRES_API_KEY: webhook_url verzonden vanuit een dashboardsessie (webhooks vereisen een API-sleutel). RATE_LIMIT_COST_TOO_HIGH: meer hashes (zonder combined) dan één rate-limitvenster toestaat; details { max_per_window, window_seconds, requested }.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "PAYMENT_REQUIRED, of QUOTA_EXCEEDED op de synchrone routes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "403": {
            "description": "EIDAS_REQUIRES_LIVE_KEY (details.reason \"no_billing_account\" wanneer een live-sleutel of een sessie als test draait omdat er geen factureringsaccount is gekoppeld). EMAIL_NOT_VERIFIED: een dashboardsessie waarvan het e-mailadres niet is bevestigd, of een API-sleutel van zo'n account. PLAN_ARCHIVE_ONLY: het abonnement bewaart alleen het archief en maakt geen nieuw werk aan. PLAN_API_READ_ONLY: een API-sleutel van een Website Archive-account (de sleutel is alleen-lezen; gebruik het dashboard). PLAN_API_READ_ONLY met details.reason \"session_outside_dashboard\": een dashboardsessie van een Website Archive-account waarvan het verzoek geen Origin-header heeft, of een Origin-header die geen ProofSnap-dashboardadres is (nieuw werk op dat abonnement wordt alleen in het dashboard aangemaakt; een zachte drempel, geen beveiligingsgrens).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "422": {
            "description": "SSRF_BLOCKED: webhook_url verwijst naar een privé-, loopback-, link-local of anderszins geblokkeerd adres.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED: te veel verzoeken in het huidige venster van 60 seconden. `error.details.retry_after_seconds` herhaalt Retry-After. RateLimit meldt r=0 en het aantal seconden tot het venster opnieuw begint. Ook RATE_LIMITED, voor een dashboardsessie van Website Archive: het daglimiet van 100 eenheden per UTC-dag, gedeeld door losse vastleggingen en batchvastleggingen (één eenheid per URL), compare, redact en certify-hash (één eenheid per verzoek); een verzoek dat niet past, wordt in zijn geheel geweigerd (details.daily_limit, details.remaining, details.action \"wa_session_captures\"; Retry-After en details.retry_after_seconds geven het aantal seconden tot de volgende UTC-dag, en de RateLimit- en X-RateLimit-*-headers per minuut ontbreken). Een korte weigering met alleen details.retry_after_seconds en Retry-After: 1 betekent een gelijktijdige update van de teller: probeer het opnieuw. Bij een weigering die niet aan de aanroeper ligt, worden de eenheden teruggegeven; werk dat met 202 is geaccepteerd en later mislukt, behoudt zijn eenheid. Een zachte drempel, geen beveiligingsgrens.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "RATE_LIMITED",
                    "message": "Too many requests. See Retry-After header.",
                    "details": {
                      "retry_after_seconds": 42
                    }
                  },
                  "request_id": "..."
                }
              }
            }
          },
          "500": {
            "description": "CONFIGURATION_ERROR: de certificering is mislukt (details.reason).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/AuthUnavailable"
          },
          "504": {
            "description": "UPSTREAM_TIMEOUT: de synchrone certificering (één hash, of gecombineerd) is niet binnen ongeveer 25 s voltooid. Mogelijk wordt die alsnog voltooid: details.certify_id en details.poll_path noemen de rij die u met GET /captures/{capture_id} kunt pollen.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            }
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ]
      }
    },
    "/webhook-events/{capture_id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/CaptureId"
        }
      ],
      "get": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "listWebhookEvents",
        "summary": "Afleveringslogboek van webhooks",
        "description": "Elke afleveringspoging voor één vastlegging, certificaat of weglakking, oudste eerst.",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Afleveringslogboek.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "capture_id": {
                      "type": "string"
                    },
                    "webhook_url": {
                      "type": "string",
                      "description": "De webhook-URL van de rij (options.webhook_url van een vastlegging, of de URL die met een certificaat of weglakking is opgegeven; null als die er niet is). Voor de eigenaar van het bedrijf waarvoor een vastlegging is gemaakt die het logboek van een licentie leest, alleen scheme, host, poort en pad (query en fragment verwijderd, want die kunnen een token bevatten); die leesactie wordt bewaard in de toegangsgeschiedenis van de vastlegging (actie webhook_events).",
                      "nullable": true
                    },
                    "attempts": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "attempt_number": {
                            "type": "integer"
                          },
                          "attempted_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "status_code": {
                            "type": "integer",
                            "nullable": true
                          },
                          "latency_ms": {
                            "type": "integer",
                            "nullable": true
                          },
                          "success": {
                            "type": "boolean"
                          },
                          "error": {
                            "type": "string",
                            "nullable": true,
                            "description": "null als afgeleverd. Anders een algemene categorie, nooit de ruwe netwerkfout: connection_failed, timeout, blocked_address (de host werd op het moment van aflevering omgezet naar een privé- of anderszins geblokkeerd adres), redirect_not_followed (het endpoint antwoordde met 3xx; omleidingen worden nooit gevolgd), http_<status> (een antwoord buiten 2xx), of een zin die uitlegt waarom de gebeurtenis niet is verzonden (geen API-sleutel, sleutel ingetrokken of niet gevonden, geen webhook-secret, opzoeken van het secret mislukt)."
                          },
                          "delivery_id": {
                            "type": "string",
                            "description": "De webhook-id-header van deze aflevering zonder het voorvoegsel `msg_`."
                          }
                        }
                      }
                    },
                    "attempt_count": {
                      "type": "integer"
                    },
                    "last_success": {
                      "type": "string",
                      "description": "attempted_at van de laatste geslaagde poging.",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "FORBIDDEN: niet uw vastlegging.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "CAPTURE_NOT_FOUND.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/AuthUnavailable"
          }
        }
      }
    },
    "/scheduled-captures": {
      "post": {
        "tags": [
          "Geplande vastleggingen"
        ],
        "operationId": "createSchedule",
        "summary": "Een schema aanmaken",
        "description": "interval en cron: elke uitvoering maakt een vastlegging en stuurt, met webhook_url, capture.completed of capture.failed.\n\non_change: elke uitvoering haalt eerst de pagina op (gewone HTTP, of een browserweergave met js_render) en hasht de genormaliseerde tekst. Die controle is geen vastlegging en wordt niet gefactureerd. De eerste uitvoering maakt een basisvastlegging. Als de hash verandert, wordt een nieuwe vastlegging gemaakt, vergeleken met de vorige, en wordt monitor.change_detected naar webhook_url gestuurd. Een pagina die een fout of vrijwel geen tekst teruggeeft, geldt als geen wijziging.\n\nNa 5 mislukte uitvoeringen op rij pauzeert een schema zichzelf. Als de maandlimiet is bereikt of het abonnement achterstallig is, worden uitvoeringen overgeslagen en blijft het schema actief.\n\nLimieten per abonnement:\n\n| Abonnement | Actieve schema's | Minimale frequentie | on_change |\n| --- | --- | --- | --- |\n| sandbox | 0 | niet beschikbaar | nee |\n| developer | 3 | 300 s | ja |\n| startup | 25 | 300 s | ja |\n| growth | 200 | 300 s | ja |\n| custom | onbeperkt | 300 s | ja |\n| legal_solo | 3 | 300 s | ja |\n| legal_seats | 3 | 300 s | ja |\n| website_archive | 30 | 86400 s | ja |\n| archive_only | 0 | niet beschikbaar | nee |\n\nIdempotency-Key: een nieuwe poging met dezelfde sleutel en body binnen 24 uur krijgt het eerste 201-antwoord terug (header Idempotent-Replay: true) in plaats van een tweede schema; dezelfde sleutel met een andere body geeft 422 IDEMPOTENCY_KEY_REUSED.",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ScheduleRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Aangemaakt.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Schedule"
                }
              }
            }
          },
          "400": {
            "description": "MALFORMED_REQUEST, INVALID_URL, INVALID_CRON, INTERVAL_TOO_SHORT (details.min_interval_seconds) of een fout in de vastleggingsopties. WEBHOOK_REQUIRES_API_KEY: webhook_url verzonden vanuit een dashboardsessie (webhooks vereisen een API-sleutel).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "SCHEDULE_LIMIT_REACHED (details.max_active, details.active) of ON_CHANGE_REQUIRES_PLAN. EMAIL_NOT_VERIFIED: een dashboardsessie waarvan het e-mailadres niet is bevestigd. PLAN_ARCHIVE_ONLY: het abonnement bewaart alleen het archief en maakt geen nieuw werk aan. PLAN_API_READ_ONLY: een API-sleutel van een Website Archive-account (de sleutel is alleen-lezen; gebruik het dashboard). PLAN_API_READ_ONLY met details.reason \"session_outside_dashboard\": een dashboardsessie van een Website Archive-account waarvan het verzoek geen Origin-header heeft, of een die geen adres van het ProofSnap-dashboard is (nieuw werk op dat abonnement wordt alleen in het dashboard aangemaakt; een zachte drempel, geen beveiligingsgrens).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "SSRF_BLOCKED: url of webhook_url verwijst naar een privé-, loopback-, link-local of anderszins geblokkeerd adres.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/AuthUnavailable"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ]
      },
      "get": {
        "tags": [
          "Geplande vastleggingen"
        ],
        "operationId": "listSchedules",
        "summary": "Schema's weergeven",
        "description": "Maximaal 100 van uw schema's.",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Schema's.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "schedules": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Schedule"
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/AuthUnavailable"
          }
        }
      }
    },
    "/scheduled-captures/{schedule_id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ScheduleId"
        }
      ],
      "get": {
        "tags": [
          "Geplande vastleggingen"
        ],
        "operationId": "getSchedule",
        "summary": "Een schema ophalen",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Het schema.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Schedule"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "SCHEDULE_NOT_FOUND.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/AuthUnavailable"
          }
        }
      },
      "patch": {
        "tags": [
          "Geplande vastleggingen"
        ],
        "operationId": "updateSchedule",
        "summary": "Een schema pauzeren, hervatten of bewerken",
        "description": "Stuur status om te pauzeren of te hervatten, en/of een veld van ScheduleRequest om te bewerken. Een veld op het hoogste niveau dat u stuurt, vervangt het opgeslagen veld, ook een expliciete null (webhook_url, timezone en js_render worden zo gewist); een veld dat u weglaat, behoudt de opgeslagen waarde; het resultaat wordt opnieuw als geheel gevalideerd. `options` is de uitzondering op het bewerken per veld: het wordt als volledige map vervangen, niet per sleutel samengevoegd, dus stuur de volledige options die u wilt (lees ze eerst met GET). Een optie die u uit de map weglaat, wordt verwijderd. Bij hervatten wordt de limiet voor actieve schema's gecontroleerd, de teller voor mislukkingen gewist en de volgende uitvoering vanaf nu berekend.\n\nVanuit een dashboardsessie kan geen webhook_url worden ingesteld (400 WEBHOOK_REQUIRES_API_KEY); null of een lege string sturen wist die. Een schema dat vanuit een dashboardsessie is aangemaakt, heeft nooit een API-sleutel gehad om mee te ondertekenen, dus bij bewerken vervalt ook een eerder opgeslagen webhook_url, en ook een API-sleutel kan er geen toevoegen (400 WEBHOOK_REQUIRES_API_KEY): de planner ondertekent de webhooks van een schema met het webhook-secret van de sleutel waarmee het schema is aangemaakt. Maak het schema met een API-sleutel aan om webhooks te ontvangen.\n\nAls url, mode of js_render van een on_change-schema wordt gewijzigd (of een schema naar of van on_change wordt omgezet), wordt de baseline gewist (de opgeslagen probe-hash en de laatste vastlegging), zodat de volgende uitvoering een nieuwe baselinevastlegging maakt in plaats van het nieuwe doel met het oude te vergelijken. De verzoekbody moet een JSON-object zijn (anders 400 MALFORMED_REQUEST).",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "active",
                      "paused"
                    ]
                  },
                  "url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "mode": {
                    "type": "string",
                    "enum": [
                      "interval",
                      "cron",
                      "on_change"
                    ]
                  },
                  "interval_seconds": {
                    "type": "integer"
                  },
                  "cron_expr": {
                    "type": "string"
                  },
                  "timezone": {
                    "type": "string",
                    "nullable": true,
                    "description": "IANA-tijdzone voor cron. null of een lege string zet dit terug op UTC."
                  },
                  "webhook_url": {
                    "type": "string",
                    "description": "Stuur null of een lege string om die te verwijderen. De URL moet https zijn en mag niet verwijzen naar een privé-, loopback-, link-local, carrier-grade NAT- of cloud-metadata-adres (dezelfde regels als voor een vastleggings-URL; anders 422 SSRF_BLOCKED). Vereist aan beide kanten een API-sleutel: verzonden vanuit een dashboardsessie, of verzonden met een API-sleutel voor een schema dat in het dashboard is aangemaakt, wordt dit geweigerd met 400 WEBHOOK_REQUIRES_API_KEY, omdat webhooks worden ondertekend met het webhook-secret van de sleutel waarmee het schema is aangemaakt.",
                    "nullable": true
                  },
                  "js_render": {
                    "type": "boolean",
                    "nullable": true,
                    "description": "null betekent false."
                  },
                  "options": {
                    "allOf": [
                      {
                        "$ref": "#/components/schemas/CaptureOptions"
                      }
                    ],
                    "description": "Vervangt de opgeslagen options als geheel (niet per sleutel samengevoegd). Stuur de volledige map; een weggelaten optie wordt verwijderd. null zet de options terug naar de standaardwaarden. options.webhook_url wordt nooit opgeslagen (zie webhook_url)."
                  },
                  "max_capture_age_seconds": {
                    "type": "integer",
                    "nullable": true,
                    "minimum": 86400,
                    "maximum": 34560000,
                    "description": "Alleen bij on_change: ten minste elke N seconden een volledige vastlegging, ook zonder wijziging (1 tot 400 dagen, langer dan interval_seconds). null verwijdert dit. Overschakelen naar de modus interval of cron laat het vervallen."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Het bijgewerkte schema.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Schedule"
                }
              }
            }
          },
          "400": {
            "description": "MALFORMED_REQUEST (ook als er niets bij te werken is), INVALID_URL, INVALID_CRON of INTERVAL_TOO_SHORT. WEBHOOK_REQUIRES_API_KEY: webhook_url verzonden vanuit een dashboardsessie, of verzonden met een API-sleutel voor een schema dat in het dashboard is aangemaakt (dat heeft geen sleutel waarvan het webhook-secret de webhooks zou kunnen ondertekenen).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "SCHEDULE_LIMIT_REACHED of ON_CHANGE_REQUIRES_PLAN. EMAIL_NOT_VERIFIED: een dashboardsessie waarvan het e-mailadres niet is bevestigd (bij het hervatten van een gepauzeerd schema; pauzeren en bewerken blijven mogelijk). PLAN_API_READ_ONLY: een API-sleutel van een Website Archive-account, bij elke PATCH (pauzeren en bewerken inbegrepen). PLAN_API_READ_ONLY met details.reason \"session_outside_dashboard\": een dashboardsessie van een Website Archive-account waarvan het verzoek geen Origin-header heeft, of een die geen adres van het ProofSnap-dashboard is (nieuw werk op dat abonnement wordt alleen in het dashboard aangemaakt; een zachte drempel, geen beveiligingsgrens). PLAN_ARCHIVE_ONLY: een schema hervatten op het abonnement Archive only.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "SCHEDULE_NOT_FOUND.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "SSRF_BLOCKED: url of webhook_url verwijst naar een privé-, loopback-, link-local of anderszins geblokkeerd adres.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/AuthUnavailable"
          }
        }
      },
      "delete": {
        "tags": [
          "Geplande vastleggingen"
        ],
        "operationId": "deleteSchedule",
        "summary": "Een schema verwijderen",
        "description": "Stopt toekomstige uitvoeringen. Vastleggingen die al zijn gemaakt, blijven bewaard.",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Verwijderd.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "schedule_id": {
                      "type": "string"
                    },
                    "deleted": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "PLAN_API_READ_ONLY: een API-sleutel van een Website Archive-account (de sleutel is alleen-lezen; gebruik het dashboard).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "SCHEDULE_NOT_FOUND.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/AuthUnavailable"
          }
        }
      }
    },
    "/scheduled-captures/{schedule_id}/runs": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ScheduleId"
        }
      ],
      "get": {
        "tags": [
          "Geplande vastleggingen"
        ],
        "operationId": "listScheduleRuns",
        "summary": "Uitvoeringen van een schema weergeven",
        "description": "De 50 nieuwste vastleggingen van dit schema (on_change-controles die geen wijziging vonden, worden niet vermeld).",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Uitvoeringen.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "schedule_id": {
                      "type": "string"
                    },
                    "runs": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "capture_id": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          },
                          "created_at": {
                            "type": "string"
                          },
                          "captured_at": {
                            "type": "string",
                            "nullable": true
                          },
                          "file_count": {
                            "type": "integer",
                            "nullable": true
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "SCHEDULE_NOT_FOUND.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/AuthUnavailable"
          }
        }
      }
    },
    "/matters": {
      "get": {
        "tags": [
          "Zaken"
        ],
        "operationId": "listMatters",
        "summary": "Zaken weergeven",
        "description": "Maximaal 200 zaken waarvan u lid bent. Zaken van een bedrijf dat u hebt verlaten, worden niet vermeld.",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Zaken.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "matters": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Matter"
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "MATTERS_NOT_ENABLED: het abonnement is geen Legal-abonnement.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/AuthUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "Zaken"
        ],
        "operationId": "createMatter",
        "summary": "Een zaak aanmaken",
        "description": "U wordt de eigenaar. Het bewijsketenlogboek begint met een matter.created-vermelding. Tekstvelden worden bijgesneden en ingekort tot 200 tekens.",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 200,
                    "description": "Platte tekst: witruimte wordt samengevoegd en bijgesneden, maximaal 200 tekens, geen stuurtekens, tekens voor tekstrichting (bidi) of onzichtbare tekens (de verbindingstekens U+200C en U+200D, die voor de spelling van het Hindi en Perzisch nodig zijn, zijn toegestaan) (anders 400 MALFORMED_REQUEST). Verplicht. Wordt getoond op de openbare verificatiepagina van de zaak."
                  },
                  "client": {
                    "type": "string",
                    "maxLength": 200,
                    "description": "Platte tekst: witruimte wordt samengevoegd en bijgesneden, maximaal 200 tekens, geen stuurtekens, tekens voor tekstrichting (bidi) of onzichtbare tekens (de verbindingstekens U+200C en U+200D, die voor de spelling van het Hindi en Perzisch nodig zijn, zijn toegestaan) (anders 400 MALFORMED_REQUEST). Leeg of null betekent niet ingesteld (opgeslagen als null). Een waarde die geen string is, wordt geweigerd.",
                    "nullable": true
                  },
                  "case_number": {
                    "type": "string",
                    "maxLength": 200,
                    "description": "Platte tekst: witruimte wordt samengevoegd en bijgesneden, maximaal 200 tekens, geen stuurtekens, tekens voor tekstrichting (bidi) of onzichtbare tekens (de verbindingstekens U+200C en U+200D, die voor de spelling van het Hindi en Perzisch nodig zijn, zijn toegestaan) (anders 400 MALFORMED_REQUEST). Leeg of null betekent niet ingesteld (opgeslagen als null). Een waarde die geen string is, wordt geweigerd. Wordt getoond op de openbare verificatiepagina van de zaak.",
                    "nullable": true
                  },
                  "opposing_party": {
                    "type": "string",
                    "maxLength": 200,
                    "description": "Platte tekst: witruimte wordt samengevoegd en bijgesneden, maximaal 200 tekens, geen stuurtekens, tekens voor tekstrichting (bidi) of onzichtbare tekens (de verbindingstekens U+200C en U+200D, die voor de spelling van het Hindi en Perzisch nodig zijn, zijn toegestaan) (anders 400 MALFORMED_REQUEST). Leeg of null betekent niet ingesteld (opgeslagen als null). Een waarde die geen string is, wordt geweigerd.",
                    "nullable": true
                  },
                  "jurisdiction": {
                    "type": "string",
                    "description": "Vrije tekst. Platte tekst: witruimte wordt samengevoegd en bijgesneden, maximaal 200 tekens, geen stuurtekens, tekens voor tekstrichting (bidi) of onzichtbare tekens (de joiners U+200C en U+200D, die de Hindi- en Perzische spelling nodig heeft, zijn toegestaan) (anders 400 MALFORMED_REQUEST). Leeg of null betekent niet ingesteld (opgeslagen als null). Een waarde die geen string is, wordt geweigerd. Getoond op de openbare verificatiepagina van de zaak.",
                    "maxLength": 200,
                    "nullable": true
                  }
                },
                "required": [
                  "name"
                ]
              },
              "example": {
                "name": "Smith v. Jones",
                "case_number": "2026-CV-0142"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Aangemaakt.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Matter"
                }
              }
            }
          },
          "400": {
            "description": "MISSING_FIELD: name ontbreekt of is leeg. MALFORMED_REQUEST: ongeldige JSON. MALFORMED_REQUEST: een tekstveld dat geen string is, langer is dan 200 tekens of controle- of tekstrichtingstekens bevat.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "PAYMENT_REQUIRED: live aanroeper van wie het abonnement (of het bedrijf) achterstallig of onbetaald is."
          },
          "403": {
            "description": "MATTERS_NOT_ENABLED: het abonnement is geen Legal-abonnement. EMAIL_NOT_VERIFIED: een dashboardsessie waarvan het e-mailadres niet is bevestigd, of een API-sleutel van zo'n account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/AuthUnavailable"
          }
        }
      }
    },
    "/matters/{matter_id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/MatterId"
        }
      ],
      "get": {
        "tags": [
          "Zaken"
        ],
        "operationId": "getMatter",
        "summary": "Een zaak ophalen",
        "description": "De zaak met haar vastleggingen (maximaal 500), haar bewijsketenlogboek en maximaal 100 toegangsgebeurtenissen. `members` geeft het e-mailadres van elk item in `member_uids`, en `actor_emails` koppelt de uid's die in het bewijsketenlogboek en de audit_stream van deze zaak worden genoemd (actoren en het doel van `member.added` / `member.removed`, dus ook verwijderde leden) aan hun e-mailadres; een e-mailadres is null, of de uid ontbreekt in de koppeling, wanneer het niet kan worden gelezen. Alleen aanroepers die de zaak kunnen openen, ontvangen deze velden; `/matter-verify` bevat nooit e-mailadressen. Een aanroeper die de openbare deellink mag intrekken (de eigenaar van de zaak, de eigenaar van een beëindigd of gearchiveerd bedrijf), krijgt ook `share`: `{ share_url, created_at, expires_at }` van de actieve link (`expires_at` is altijd null: links werken tot ze worden ingetrokken), of null wanneer er geen is; andere leden ontvangen het veld nooit. Items in audit_stream bevatten source_ip en user_agent alleen voor de eigenaar van de zaak; leden ontvangen de items zonder deze twee velden.\n\nEen zaak van een bedrijf dat is beëindigd, bevat `read_only: true`.",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "De zaak.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Matter"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "members": {
                          "type": "array",
                          "description": "Eén item per item in `member_uids`, in dezelfde volgorde.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "uid": {
                                "type": "string"
                              },
                              "email": {
                                "type": "string",
                                "format": "email",
                                "nullable": true,
                                "description": "Null wanneer het account niet kan worden gelezen."
                              }
                            }
                          }
                        },
                        "actor_emails": {
                          "type": "object",
                          "description": "`{ uid: email }` voor de personen die in custody_log en audit_stream van deze zaak worden genoemd (de systeemactor uitgezonderd). Een uid waarvan het e-mailadres niet kan worden gelezen, ontbreekt.",
                          "additionalProperties": {
                            "type": "string",
                            "format": "email"
                          }
                        },
                        "captures": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/MatterCapture"
                          }
                        },
                        "custody_log": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/CustodyEntry"
                          }
                        },
                        "audit_stream": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/AuditEntry"
                          }
                        },
                        "share": {
                          "type": "object",
                          "nullable": true,
                          "description": "Alleen voor een aanroeper die de link mag intrekken. Null wanneer er geen actieve link bestaat.",
                          "properties": {
                            "share_url": {
                              "type": "string",
                              "format": "uri"
                            },
                            "created_at": {
                              "type": "string",
                              "format": "date-time",
                              "nullable": true
                            },
                            "expires_at": {
                              "type": "string",
                              "format": "date-time",
                              "nullable": true,
                              "description": "Altijd null: deellinks verlopen niet."
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "MATTERS_NOT_ENABLED: het abonnement is geen Legal-abonnement.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "MATTER_NOT_FOUND.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/AuthUnavailable"
          }
        }
      },
      "patch": {
        "tags": [
          "Zaken"
        ],
        "operationId": "updateMatter",
        "summary": "Een zaak bijwerken",
        "description": "Alleen voor de eigenaar. Tekstvelden worden bijgesneden en ingekort tot 200 tekens. name is verplicht: als het wordt meegestuurd, moet het een niet-lege string zijn; een lege name, een name met alleen witruimte, null of een niet-string wordt geweigerd met 400 en er wordt niets gewijzigd. Leden: add_member_email voegt een persoon toe die al een ProofSnap-account heeft en bij het bedrijf van de zaak hoort; geen account, een niet-bevestigd adres en een account buiten het bedrijf krijgen allemaal dezelfde 404 NOT_FOUND; remove_member_uid verwijdert een lid (niet de eigenaar). Als beide worden meegestuurd, wordt alleen add_member_email toegepast. Een zaak heeft maximaal 50 leden (een volle zaak weigert al voordat het adres wordt opgezocht). Wijzigingen in leden worden in het bewijsketenlogboek vastgelegd. Een wijziging van name, client, case_number, opposing_party, jurisdiction of status wordt in het bewijsketenlogboek vastgelegd als matter.updated; het target daarvan is een JSON-string met fields (de namen van de gewijzigde velden) en, als status is gewijzigd, de nieuwe status. Vrije tekst wordt niet in het gehashte, openbare bewijsketenlogboek opgenomen: de oude en nieuwe waarden gaan naar het toegangslogboek (audit_stream, item matter.updated). Bij een gearchiveerde zaak worden een naamswijziging of add_member_email geweigerd met 409 MATTER_ARCHIVED, tenzij hetzelfde verzoek de zaak heropent (status \"open\"); remove_member_uid en heropenen werken nog wel. Elke uitnodiging telt mee voor een daglimiet van 50 per uitnodigende eigenaar (bedrijfsuitnodigingen vanuit het dashboard en add_member_email bij zaken samen, weigeringen inbegrepen, omdat elke uitnodiging een adres opzoekt); bij het bereiken van de limiet geeft het verzoek 429 RATE_LIMITED terug. Een fout aan de serverkant telt niet mee.\n\nLegal hold: `{ \"legal_hold\": true|false }` alleen stelt de legal hold van de zaak in (alleen de eigenaar van de zaak) en past die in hetzelfde verzoek toe op de vastleggingen ervan, met dezelfde gevolgen als een legal hold op een vastlegging (S3 Object Lock legal hold, bewijsketenitems capture.legal_hold_placed / capture.legal_hold_released, de gebeurtenissenketen van de vastlegging). Het antwoord is de zaak plus `changed` (de vlag is gewijzigd), `captures_held` of `captures_released`, `skipped` ([{ capture_id, code }], bijv. een vastlegging in de prullenbak) en `truncated` (meer dan 1000 vastleggingen: stuur dezelfde waarde opnieuw om verder te gaan). Vastleggingen die later aan de zaak worden toegewezen terwijl de legal hold actief is, krijgen bij de toewijzing een legal hold. Eén rate-limit-eenheid per verzoek plus één per 10 gewijzigde vastleggingen.",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "description": "Moet ten minste één teken bevatten dat geen witruimte is. Laat het weg om de huidige naam te behouden. Dezelfde regel voor platte tekst: witruimte samengevoegd, maximaal 200 tekens, geen controle-, tekstrichtings- of onzichtbare tekens (U+200C en U+200D zijn toegestaan).",
                    "maxLength": 200
                  },
                  "client": {
                    "type": "string",
                    "nullable": true,
                    "description": "Platte tekst: witruimte wordt samengevoegd en bijgesneden, maximaal 200 tekens, geen stuurtekens, tekens voor tekstrichting (bidi) of onzichtbare tekens (de verbindingstekens U+200C en U+200D, die voor de spelling van het Hindi en Perzisch nodig zijn, zijn toegestaan) (anders 400 MALFORMED_REQUEST). null of een string met alleen witruimte maakt het veld leeg (opgeslagen als null). Een waarde die geen string is, wordt geweigerd met 400.",
                    "maxLength": 200
                  },
                  "case_number": {
                    "type": "string",
                    "nullable": true,
                    "description": "Platte tekst: witruimte wordt samengevoegd en bijgesneden, maximaal 200 tekens, geen stuurtekens, tekens voor tekstrichting (bidi) of onzichtbare tekens (de verbindingstekens U+200C en U+200D, die voor de spelling van het Hindi en Perzisch nodig zijn, zijn toegestaan) (anders 400 MALFORMED_REQUEST). null of een string met alleen witruimte maakt het veld leeg (opgeslagen als null). Een waarde die geen string is, wordt geweigerd met 400.",
                    "maxLength": 200
                  },
                  "opposing_party": {
                    "type": "string",
                    "nullable": true,
                    "description": "Platte tekst: witruimte wordt samengevoegd en bijgesneden, maximaal 200 tekens, geen stuurtekens, tekens voor tekstrichting (bidi) of onzichtbare tekens (de verbindingstekens U+200C en U+200D, die voor de spelling van het Hindi en Perzisch nodig zijn, zijn toegestaan) (anders 400 MALFORMED_REQUEST). null of een string met alleen witruimte maakt het veld leeg (opgeslagen als null). Een waarde die geen string is, wordt geweigerd met 400.",
                    "maxLength": 200
                  },
                  "jurisdiction": {
                    "type": "string",
                    "nullable": true,
                    "description": "Platte tekst: witruimte wordt samengevoegd en bijgesneden, maximaal 200 tekens, geen stuurtekens, tekens voor tekstrichting (bidi) of onzichtbare tekens (de verbindingstekens U+200C en U+200D, die voor de spelling van het Hindi en Perzisch nodig zijn, zijn toegestaan) (anders 400 MALFORMED_REQUEST). null of een string met alleen witruimte maakt het veld leeg (opgeslagen als null). Een waarde die geen string is, wordt geweigerd met 400.",
                    "maxLength": 200
                  },
                  "status": {
                    "type": "string",
                    "description": "Gebruik POST /matters/{matter_id}/seal om te verzegelen.",
                    "enum": [
                      "open",
                      "archived"
                    ]
                  },
                  "add_member_email": {
                    "type": "string",
                    "format": "email"
                  },
                  "remove_member_uid": {
                    "type": "string"
                  },
                  "legal_hold": {
                    "type": "boolean",
                    "description": "Legal hold van de zaak, afzonderlijk verzonden (gecombineerd met een ander veld: 400). true plaatst elke huidige vastlegging van de zaak onder hold, evenals elke vastlegging die eraan wordt toegewezen zolang de hold actief is (legal_hold_source \"matter:<id>\"; een vastlegging die al onder hold staat, behoudt haar eigen hold). false heft alleen de holds op die de zaak heeft ingesteld; een handmatige of bulkhold blijft staan. Alleen voor de eigenaar van de zaak. Niet geblokkeerd door een achterstallig abonnement. Instellen vereist een betaald abonnement (403 LEGAL_HOLD_NOT_AVAILABLE, details.reason plan) en vastleggingen in testmodus worden overgeslagen (code LEGAL_HOLD_NOT_AVAILABLE in `skipped`); alleen false wordt ook geaccepteerd nadat het Legal-abonnement is beëindigd."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "De bijgewerkte zaak.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Matter"
                }
              }
            }
          },
          "400": {
            "description": "MALFORMED_REQUEST: name is leeg, bestaat alleen uit witruimte of is geen string; een tekstveld dat geen string is, langer is dan 200 tekens of controle- of tekstrichtingstekens bevat; ongeldige status; niets om bij te werken; al lid; of het verwijderen van de eigenaar.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "PAYMENT_REQUIRED: live aanroeper van wie het abonnement (of het bedrijf) achterstallig of onbetaald is."
          },
          "403": {
            "description": "MATTERS_NOT_ENABLED, FORBIDDEN (niet de eigenaar), LEGAL_HOLD_NOT_AVAILABLE (legal_hold true zonder betaald abonnement) of EMAIL_NOT_VERIFIED: een dashboardsessie waarvan het e-mailadres niet is bevestigd, of een API-sleutel van zo'n account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "MATTER_NOT_FOUND, of NOT_FOUND (geen geschikt account voor het e-mailadres: geen, onbevestigd of buiten het bedrijf, één antwoord voor alle gevallen; of remove_member_uid is geen lid).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "MATTER_SEALED, of MATTER_ARCHIVED (hernoemen of een nieuw lid bij een gearchiveerde zaak). COMPANY_READ_ONLY: de zaak hoort bij een bedrijf dat is beëindigd of als bedrijfsarchief wordt bewaard (aanroeper op een Legal-abonnement; openen, exporteren en het intrekken van de deellink werken nog).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED: de daglimiet van 50 uitnodigingen voor leden per uitnodigende eigenaar is bereikt (bedrijfsuitnodigingen vanuit het dashboard en add_member_email bij zaken tellen samen; details.daily_limit, details.action \"member_invite\"). Retry-After geeft aan wanneer de UTC-dag eindigt.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/AuthUnavailable"
          }
        }
      }
    },
    "/matters/{matter_id}/captures": {
      "parameters": [
        {
          "$ref": "#/components/parameters/MatterId"
        }
      ],
      "post": {
        "tags": [
          "Zaken"
        ],
        "operationId": "assignMatterCaptures",
        "summary": "Vastleggingen toevoegen of verwijderen",
        "description": "Elk lid mag dit aanroepen, maar alleen vastleggingen die de aanroeper zelf heeft gemaakt, worden verplaatst; andere worden teruggegeven in skipped, net als vastleggingen die al in een andere zaak zitten en vastleggingen die al in deze zaak zitten (of, met unassign, er niet in zitten). Items die geen vastleggings-ID zijn, worden genegeerd en er worden er maximaal 100 verwerkt. Elke wijziging wordt in het bewijsketenlogboek vastgelegd. Een gearchiveerde zaak neemt geen nieuwe vastleggingen aan (409 MATTER_ARCHIVED); unassign werkt nog wel.",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "capture_ids": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "minItems": 1,
                    "maxItems": 100
                  },
                  "unassign": {
                    "type": "boolean",
                    "description": "true verwijdert de vastleggingen uit de zaak.",
                    "default": false
                  }
                },
                "required": [
                  "capture_ids"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Resultaat.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "matter_id": {
                      "type": "string"
                    },
                    "assigned": {
                      "type": "integer",
                      "description": "Aantal gewijzigde vastleggingen."
                    },
                    "skipped": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "unassign": {
                      "type": "boolean"
                    },
                    "legal_holds_placed": {
                      "type": "integer",
                      "description": "Aanwezig als de legal hold van de zaak actief is: op hoeveel van de toegewezen vastleggingen deze een legal hold heeft ingesteld."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "MALFORMED_REQUEST: geen geldige vastleggings-ID.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "PAYMENT_REQUIRED: live aanroeper van wie het abonnement (of het bedrijf) achterstallig of onbetaald is."
          },
          "403": {
            "description": "MATTERS_NOT_ENABLED of EMAIL_NOT_VERIFIED: een dashboardsessie waarvan het e-mailadres niet is bevestigd, of een API-sleutel van zo'n account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "MATTER_NOT_FOUND.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "MATTER_SEALED, of MATTER_ARCHIVED (toevoegen aan een gearchiveerde zaak). COMPANY_READ_ONLY: de zaak hoort bij een bedrijf dat is beëindigd of als bedrijfsarchief wordt bewaard (aanroeper op een Legal-abonnement; openen, exporteren en het intrekken van de deellink werken nog).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/AuthUnavailable"
          }
        }
      }
    },
    "/matters/{matter_id}/seal": {
      "parameters": [
        {
          "$ref": "#/components/parameters/MatterId"
        }
      ],
      "post": {
        "tags": [
          "Zaken"
        ],
        "operationId": "sealMatter",
        "summary": "Een zaak verzegelen",
        "description": "Alleen voor de eigenaar. Verankert de hash van de huidige kop van de bewijsketen met OpenTimestamps en, wanneer eIDAS in de deployment is geconfigureerd en de aanroeper live is (een live API-sleutel, of een dashboardsessie met gekoppelde facturering), met een eIDAS-tijdstempel (de eIDAS-tijdstempel van de verzegeling wordt niet gefactureerd; als die mislukt, gaat de verzegeling zonder door). Een testaanroeper (test-API-sleutel of sandboxsessie) wordt alleen met OpenTimestamps verzegeld en de verzegeling registreert eidas_status not_obtained met eidas_reason test_key; het verzoek kent geen eIDAS-optie, dus dit is geen fout. De zaak wordt daarna alleen-lezen: geen bewerkingen, geen wijzigingen aan vastleggingen, en haar vastleggingen kunnen niet worden verwijderd. Een zaak die nog een vastlegging in de prullenbak heeft, wordt niet verzegeld (409 MATTER_HAS_TRASHED_CAPTURES): zet de vastlegging terug of verwijder haar eerst uit de zaak.",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Verzegeld.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "matter_id": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "sealed"
                      ]
                    },
                    "seal": {
                      "$ref": "#/components/schemas/MatterSeal"
                    },
                    "custody_head_hash": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "MALFORMED_REQUEST: de zaak heeft geen kop van de bewijsketen.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "PAYMENT_REQUIRED: live aanroeper van wie het abonnement (of het bedrijf) achterstallig of onbetaald is."
          },
          "403": {
            "description": "MATTERS_NOT_ENABLED: het abonnement is geen Legal-abonnement. FORBIDDEN: niet de eigenaar. EMAIL_NOT_VERIFIED: een dashboardsessie waarvan het e-mailadres niet is bevestigd, of een API-sleutel van zo'n account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "MATTER_NOT_FOUND.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "MATTER_SEALED (al verzegeld), MATTER_CHANGED (het bewijsketenlogboek is tijdens het verzegelen gewijzigd; probeer het opnieuw) of MATTER_HAS_TRASHED_CAPTURES (een vastlegging van de zaak staat in de prullenbak; zet haar eerst terug of verwijder haar uit de zaak). COMPANY_READ_ONLY: de zaak hoort bij een bedrijf dat is beëindigd of als bedrijfsarchief wordt bewaard (aanroeper op een Legal-abonnement; openen, exporteren en het intrekken van de deellink werken nog).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "TIMESTAMP_SERVICE_UNAVAILABLE (de verankering is mislukt, er is niets gewijzigd; probeer het opnieuw) of CONFIGURATION_ERROR.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "EIDAS_SEAL_RETRY: de eIDAS-tijdstempel van de verzegeling is niet op tijd binnengekomen (of wordt nog uitgegeven). Er is niets verzegeld. Probeer het opnieuw na Retry-After seconden: de nieuwe poging ontvangt de tijdstempel die al voor deze kop van de bewijsketen is uitgegeven, geen tweede tijdstempel. SERVICE_UNAVAILABLE: de gateway kon de referentie niet controleren; probeer het opnieuw met backoff.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "EIDAS_SEAL_RETRY",
                    "message": "The eIDAS timestamp of the seal did not arrive in time. Nothing was sealed. Retry after the Retry-After interval; the retry reuses the timestamp already issued for this custody head.",
                    "details": {
                      "reason": "The eIDAS timestamp did not arrive in time. Nothing was sealed. Retry the seal.",
                      "retry_after_seconds": 15
                    }
                  },
                  "request_id": "..."
                }
              }
            }
          }
        }
      }
    },
    "/matters/{matter_id}/export": {
      "parameters": [
        {
          "$ref": "#/components/parameters/MatterId"
        }
      ],
      "post": {
        "tags": [
          "Zaken"
        ],
        "operationId": "exportMatter",
        "summary": "Een zaak exporteren",
        "description": "Alleen voor de eigenaar. Bouwt één ZIP met custody_report.pdf, custody_log.json, matter.json, README.txt, access_log.json (als er toegangsgebeurtenissen zijn) en het bewijspakket van elke vastlegging. Met Bates-nummering aan (de standaard) worden Bates-gestempelde kopieën van elke evidence.pdf toegevoegd onder exhibits/ (bijlagen genummerd vanaf het oudste bewijs) en, met formats.loadfile, een Concordance/Opticon-loadbestand. Met formats.pdfa worden de indieningskopieën ook naar PDF/A geconverteerd wanneer de converter beschikbaar is (bij een synchrone export alleen binnen een tijdsbudget); alleen de kopieën die in pdfa_count worden geteld, zijn PDF/A, de andere zijn PDF's die geen PDF/A-claim maken, en README.txt noemt PDF/A alleen wanneer pdfa_count groter is dan 0.\n\nEen zaak met maximaal 20 vastleggingen wordt synchroon geëxporteerd (200). Een grotere zaak wordt op de achtergrond geëxporteerd: het antwoord is 202 met een job_id; poll GET /matters/{matter_id}/export?job_id=. Vastleggingen die niet binnen de groottelimiet passen (120 MB synchroon; 800 MB en 300 vastleggingen op de achtergrond), worden in de rapporten vermeld maar niet gebundeld.\n\nWerkt voor open en verzegelde zaken. Een gearchiveerde zaak wordt geweigerd met 409 MATTER_ARCHIVED (heropen haar eerst), behalve voor een aanroeper die haar niet kan heropenen: de eigenaar van een beëindigd bedrijf binnen de bewaartermijn daarvan, en een Archive only-account. Elke export wordt in het toegangslogboek geregistreerd als export.generated. Elke export telt mee voor een daglimiet van 50 per gebruiker; een achtergrondexport die mislukt, geeft zijn eenheid terug.",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "bates": {
                    "type": "object",
                    "properties": {
                      "enabled": {
                        "type": "boolean",
                        "default": true
                      },
                      "prefix": {
                        "type": "string",
                        "description": "Alleen letters en cijfers, in hoofdletters, maximaal 12. Standaard: afgeleid van het zaaknummer of de naam van de zaak."
                      },
                      "start": {
                        "type": "integer",
                        "minimum": 1,
                        "default": 1
                      },
                      "exhibit_start": {
                        "type": "integer",
                        "minimum": 1,
                        "default": 1
                      },
                      "pad": {
                        "type": "integer",
                        "minimum": 4,
                        "maximum": 8,
                        "default": 6
                      }
                    }
                  },
                  "formats": {
                    "type": "object",
                    "properties": {
                      "loadfile": {
                        "type": "boolean",
                        "description": "Voeg het e-discovery-loadbestand toe (vereist Bates-nummering).",
                        "default": true
                      },
                      "pdfa": {
                        "type": "boolean",
                        "description": "Converteer de kopieën voor indiening met Bates-stempel naar PDF/A (vereist Bates-nummering). Best effort: een kopie die niet wordt geconverteerd, blijft een PDF zonder PDF/A-claim; pdfa_count in het antwoord geeft aan hoeveel er zijn geconverteerd.",
                        "default": true
                      }
                    }
                  },
                  "export_locale": {
                    "type": "string",
                    "description": "Taal van custody_report.pdf, de bijlagenindex en README.txt: en, de, it, fr, es, pt_BR, cs, el, hi, hu, id, ja, ko, nl, pl, ro, ru, sv, tr, vi, zh_CN, zh_TW, ar, he, da, fi, nb, sk, uk (een koppelteken wordt geaccepteerd in plaats van het onderstrepingsteken). Bij andere waarden wordt Engels gebruikt."
                  },
                  "declaration": {
                    "type": "object",
                    "description": "Geaccepteerd voor compatibiliteit. De tekstvelden (name, title, party, case_caption, executed_location) volgen de regel voor platte tekst (zie PlainText): een waarde met een niet-toegestaan teken wordt geweigerd met 400 MALFORMED_REQUEST. Te lange waarden worden ingekort (name, title, executed_location 120, party 160, case_caption 200 tekens)."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Export gereed.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "matter_id": {
                      "type": "string"
                    },
                    "url": {
                      "type": "string",
                      "description": "Presigned downloadlink, 24 uur geldig.",
                      "format": "uri"
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "capture_count": {
                      "type": "integer"
                    },
                    "captures_included": {
                      "type": "integer"
                    },
                    "captures_skipped": {
                      "type": "integer",
                      "description": "Vastleggingen die in de rapporten staan, maar niet zijn meegebundeld omdat een groottelimiet is bereikt."
                    },
                    "custody_events": {
                      "type": "integer"
                    },
                    "exhibits_count": {
                      "type": "integer",
                      "description": "Exhibitkopieën met Bates-nummering."
                    },
                    "bates_prefix": {
                      "type": "string",
                      "nullable": true
                    },
                    "searchable": {
                      "type": "boolean",
                      "description": "Altijd false: aan de indieningskopieën wordt geen tekstlaag toegevoegd. Behouden voor een stabiele vorm van het antwoord."
                    },
                    "pdfa_count": {
                      "type": "integer",
                      "description": "Indieningskopieën onder exhibits/ die PDF/A zijn. 0 betekent dat geen enkele kopie PDF/A is en geen enkele dat claimt."
                    },
                    "loadfile": {
                      "type": "boolean"
                    },
                    "size_bytes": {
                      "type": "integer"
                    },
                    "generated_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "202": {
            "description": "Export op de achtergrond gestart.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "matter_id": {
                      "type": "string"
                    },
                    "async": {
                      "type": "boolean"
                    },
                    "job_id": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "processing"
                      ]
                    },
                    "poll_path": {
                      "type": "string",
                      "description": "Inclusief de /v1-stage."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "MALFORMED_REQUEST: een exportoptie is ongeldig (details.reason).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "PAYMENT_REQUIRED: live aanroeper van wie het abonnement (of het bedrijf) achterstallig of onbetaald is."
          },
          "403": {
            "description": "MATTERS_NOT_ENABLED: het abonnement is geen Legal-abonnement. FORBIDDEN: niet de eigenaar. EMAIL_NOT_VERIFIED: een dashboardsessie waarvan het e-mailadres niet is bevestigd, of een API-sleutel van zo'n account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "MATTER_NOT_FOUND.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "MATTER_ARCHIVED: heropen eerst de zaak (PATCH status \"open\").",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "CONFIGURATION_ERROR: de export kon niet worden opgebouwd of gestart.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/AuthUnavailable"
          },
          "504": {
            "description": "UPSTREAM_TIMEOUT: de synchrone export is niet binnen ongeveer 25 s voltooid (details.reason). Probeer het opnieuw.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Zaken"
        ],
        "operationId": "getMatterExport",
        "summary": "De status van een achtergrondexport opvragen",
        "description": "Alleen voor de eigenaar. Een taak die 30 minuten na de start nog wordt verwerkt, wordt als mislukt gemeld met de fout \"export timed out\". Een taak eindigt één keer: een taak die als mislukt is gemeld, wordt later nooit alsnog gereed, en een gereed gemelde taak wordt nooit als mislukt gemeld.",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "job_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Status van de taak. Zodra deze gereed is, worden url en de aantallen ingevuld.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "job_id": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "processing",
                        "ready",
                        "failed"
                      ]
                    },
                    "url": {
                      "type": "string",
                      "nullable": true
                    },
                    "captures_included": {
                      "type": "integer",
                      "nullable": true
                    },
                    "captures_skipped": {
                      "type": "integer",
                      "nullable": true
                    },
                    "custody_events": {
                      "type": "integer",
                      "nullable": true
                    },
                    "size_bytes": {
                      "type": "integer",
                      "nullable": true
                    },
                    "error": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "MALFORMED_REQUEST: job_id ontbreekt.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "MATTERS_NOT_ENABLED: het abonnement is geen Legal-abonnement. FORBIDDEN: niet de eigenaar.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "MATTER_NOT_FOUND, of NOT_FOUND voor een onbekende taak.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/AuthUnavailable"
          }
        }
      }
    },
    "/matters/{matter_id}/share": {
      "parameters": [
        {
          "$ref": "#/components/parameters/MatterId"
        }
      ],
      "post": {
        "tags": [
          "Zaken"
        ],
        "operationId": "shareMatter",
        "summary": "Een openbare verificatielink aanmaken",
        "description": "Alleen voor de eigenaar. Geeft een link terug die iedereen kan openen om de naam, het zaaknummer, de jurisdictie, de status, het bewijsketenlogboek en de verzegeling van de zaak te bekijken (zie GET /matter-verify). Er wordt geen inhoud van vastleggingen gedeeld. Een nieuwe aanroep geeft dezelfde link terug. De link werkt totdat u hem intrekt.",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Deellink.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "matter_id": {
                      "type": "string"
                    },
                    "share_url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "token": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "PAYMENT_REQUIRED: live aanroeper van wie het abonnement (of het bedrijf) achterstallig of onbetaald is."
          },
          "403": {
            "description": "MATTERS_NOT_ENABLED: het abonnement is geen Legal-abonnement. FORBIDDEN: niet de eigenaar. EMAIL_NOT_VERIFIED: een dashboardsessie waarvan het e-mailadres niet is bevestigd, of een API-sleutel van zo'n account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "MATTER_NOT_FOUND.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "MATTER_ARCHIVED: een gearchiveerde zaak wordt niet gedeeld. COMPANY_READ_ONLY: de zaak hoort bij een bedrijf dat is beëindigd of als bedrijfsarchief wordt bewaard (aanroeper op een Legal-abonnement; openen, exporteren en het intrekken van de deellink werken nog).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/AuthUnavailable"
          }
        }
      },
      "delete": {
        "tags": [
          "Zaken"
        ],
        "operationId": "revokeMatterShare",
        "summary": "De openbare link intrekken",
        "description": "Alleen voor de eigenaar. De link werkt niet meer. Een latere POST maakt een nieuwe aan. De eigenaar van een beëindigd bedrijf (of van een bedrijf dat als bedrijfsarchief wordt bewaard) en de eigenaar van een zaak op het abonnement Archive only kunnen de link zonder Legal-abonnement intrekken (een link aanmaken blijft een functie van het Legal-abonnement).",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Ingetrokken.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "matter_id": {
                      "type": "string"
                    },
                    "revoked": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "PAYMENT_REQUIRED: live aanroeper van wie het abonnement (of het bedrijf) achterstallig of onbetaald is."
          },
          "403": {
            "description": "MATTERS_NOT_ENABLED, FORBIDDEN (niet de eigenaar) of EMAIL_NOT_VERIFIED: een dashboardsessie waarvan het e-mailadres niet is bevestigd, of een API-sleutel van zo'n account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "MATTER_NOT_FOUND.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/AuthUnavailable"
          }
        }
      }
    },
    "/health": {
      "get": {
        "tags": [
          "Openbaar"
        ],
        "operationId": "health",
        "summary": "Statuscontrole",
        "description": "Altijd 200 zolang de API reageert. Met deep=1 (of true) voegt het antwoord dependencies toe: of elke onderliggende dienst is geconfigureerd, en voor eIDAS een live controle van de tijdstempeldienst. Alleen eIDAS wordt actief gecontroleerd; de andere melden alleen hun configuratie. status wordt degraded wanneer eIDAS is geconfigureerd en de controle mislukt. Een controle die niet op tijd antwoordt, wordt gemeld in dependencies (probe_timeout, of eidas.error timeout), nooit als een mislukte /health. deep=1 heeft een rate limit per client-IP (10 per minuut): daarboven geeft het verzoek 429 RATE_LIMITED met Retry-After.",
        "security": [],
        "parameters": [
          {
            "name": "deep",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "1",
                "true"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Status.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "ok",
                        "degraded"
                      ]
                    },
                    "stage": {
                      "type": "string"
                    },
                    "timestamp": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "uptime_ms": {
                      "type": "integer",
                      "description": "Milliseconden sinds de start van de bedienende instantie."
                    },
                    "version": {
                      "type": "string"
                    },
                    "dependencies": {
                      "type": "object",
                      "properties": {
                        "eidas": {
                          "type": "object",
                          "properties": {
                            "configured": {
                              "type": "boolean"
                            },
                            "healthy": {
                              "type": "boolean",
                              "description": "null als niet geconfigureerd.",
                              "nullable": true
                            },
                            "error": {
                              "type": "string",
                              "description": "Vaste code wanneer de eIDAS-test geen gezonde status meldde: timeout (geen antwoord binnen 15 s), unavailable (de tijdstempeldienst meldde een ongezonde status), error (de test kon niet worden uitgevoerd). Details worden aan de serverkant gelogd en nooit teruggegeven.",
                              "enum": [
                                "timeout",
                                "unavailable",
                                "error"
                              ]
                            }
                          }
                        },
                        "ots": {
                          "type": "object",
                          "properties": {
                            "configured": {
                              "type": "boolean"
                            }
                          }
                        },
                        "c2pa_sign": {
                          "type": "object",
                          "properties": {
                            "configured": {
                              "type": "boolean"
                            }
                          }
                        },
                        "ntp": {
                          "type": "object",
                          "properties": {
                            "configured": {
                              "type": "boolean"
                            }
                          }
                        },
                        "whois": {
                          "type": "object",
                          "properties": {
                            "configured": {
                              "type": "boolean"
                            }
                          }
                        },
                        "error": {
                          "type": "string",
                          "description": "Alleen wanneer de controle van de afhankelijkheden zelf is mislukt: probe_timeout wanneer deze niet binnen 20 s antwoordde, anders probe_failed. Het antwoord is nog steeds 200."
                        }
                      },
                      "description": "Alleen met deep=1."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/ledger/{hash}": {
      "get": {
        "tags": [
          "Openbaar"
        ],
        "operationId": "ledgerLookup",
        "summary": "Een manifesthash opzoeken",
        "description": "Controleert of ProofSnap heeft vastgelegd dat het een pakket heeft uitgegeven waarvan manifest.json deze SHA-256 heeft. Vastleggingen, certificaten, vergelijkingen en weglakkingen worden bij uitgifte geregistreerd, met de hash van het manifest dat wordt geleverd (na de stap van de gekwalificeerde tijdstempel); pakketten die zijn uitgegeven voordat het uitgifteregister bestond, staan er niet in. issued: false betekent alleen dat er geen registratie is.",
        "security": [],
        "parameters": [
          {
            "name": "hash",
            "in": "path",
            "required": true,
            "description": "SHA-256 van manifest.json, 64 hexadecimale tekens, hoofd- of kleine letters, optioneel met het voorvoegsel sha256:.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Resultaat van de zoekopdracht.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "manifest_sha256": {
                      "type": "string"
                    },
                    "issued": {
                      "type": "boolean"
                    },
                    "issued_at": {
                      "type": "string",
                      "nullable": true,
                      "format": "date-time",
                      "description": "Ontbreekt wanneer issued false is."
                    },
                    "type": {
                      "type": "string",
                      "enum": [
                        "capture",
                        "certify",
                        "compare",
                        "redaction"
                      ],
                      "nullable": true,
                      "description": "Ontbreekt wanneer issued false is."
                    },
                    "eidas": {
                      "type": "boolean",
                      "nullable": true,
                      "description": "Of het uitgegeven pakket een gekwalificeerde eIDAS-tijdstempel bevat (manifest.json.tsr). Ontbreekt als issued false is; null voor records die vóór 2026-10-01 zijn geschreven."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "NOT_FOUND: de hash bestaat niet uit 64 hexadecimale tekens.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/matter-verify": {
      "get": {
        "tags": [
          "Openbaar"
        ],
        "operationId": "matterVerify",
        "summary": "Openbare weergave van een gedeelde zaak",
        "description": "Alleen-lezen gegevens achter een deellink van een zaak: de gegevens van de zaak, het bewijsketenlogboek (zodat de hashketen opnieuw kan worden berekend) en de verzegeling met het OpenTimestamps-bewijs en het eIDAS-token. Geen inhoud van vastleggingen, downloadlinks of toegangslogboek. attested_by.organization is de door de eigenaar ingestelde naam van het bedrijf waartoe de zaak behoort (een bedrijfszaak), of null; een persoonlijke zaak noemt geen organisatie. custody_log[].actor_org is het bedrijf waartoe de actor behoorde toen die vermelding werd geschreven (vastgelegd bij de vermelding, buiten de gehashte payload), alleen bij een bedrijfszaak; vermeldingen van vóór de invoering daarvan tonen null. Rate limit per client-IP (standaard 30 verzoeken per 60 seconden).",
        "security": [],
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{16,64}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Weergave van de zaak.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "matter": {
                      "type": "object",
                      "properties": {
                        "name": {
                          "type": "string"
                        },
                        "case_number": {
                          "type": "string",
                          "nullable": true
                        },
                        "jurisdiction": {
                          "type": "string",
                          "nullable": true
                        },
                        "status": {
                          "type": "string"
                        },
                        "sealed": {
                          "type": "boolean"
                        },
                        "attested_by": {
                          "type": "object",
                          "properties": {
                            "organization": {
                              "type": "string",
                              "nullable": true
                            }
                          }
                        },
                        "capture_count": {
                          "type": "integer"
                        },
                        "custody_count": {
                          "type": "integer"
                        },
                        "custody_head_hash": {
                          "type": "string",
                          "nullable": true
                        },
                        "seal": {
                          "type": "object",
                          "properties": {
                            "root_hash": {
                              "type": "string"
                            },
                            "ots_present": {
                              "type": "boolean"
                            },
                            "eidas_present": {
                              "type": "boolean"
                            },
                            "ots_hex": {
                              "type": "string",
                              "nullable": true
                            },
                            "ots_pending": {
                              "type": "boolean"
                            },
                            "tsr_base64": {
                              "type": "string",
                              "nullable": true
                            },
                            "eidas_serial": {
                              "type": "string",
                              "nullable": true
                            },
                            "eidas_issued_at": {
                              "type": "string",
                              "nullable": true
                            },
                            "eidas_ltv": {
                              "type": "boolean"
                            },
                            "sealed_at": {
                              "type": "string"
                            },
                            "eidas_status": {
                              "type": "string",
                              "enum": [
                                "confirmed",
                                "not_obtained",
                                "not_configured"
                              ],
                              "nullable": true,
                              "description": "null bij verzegelingen van vóór 2026-10-01 zonder eIDAS-token."
                            },
                            "eidas_reason": {
                              "type": "string",
                              "enum": [
                                "token_refused",
                                "unavailable",
                                "test_key",
                                "account_limit"
                              ],
                              "description": "Alleen wanneer eidas_status not_obtained is. test_key: de verzegeling is gemaakt door een testaanroeper, dus er is geen eIDAS-tijdstempel aangevraagd. account_limit: het account heeft nog geen betaalde factuur en heeft zijn eIDAS-limiet bereikt (20 per UTC-dag, 50 per UTC-maand), dus er is geen eIDAS-tijdstempel aangevraagd."
                            },
                            "eidas_verification_status": {
                              "type": "string",
                              "nullable": true,
                              "description": "verified als het token bij de verzegeling de ProofSnap-controle heeft doorstaan."
                            },
                            "eidas_revocation_status": {
                              "type": "string",
                              "nullable": true,
                              "enum": [
                                "good",
                                "revoked_after_gen_time",
                                "unknown",
                                "not_checked",
                                null
                              ],
                              "description": "Status van het TSA-certificaat, vastgesteld bij de verzegeling (niet live opgehaald)."
                            },
                            "eidas_validation": {
                              "type": "object",
                              "nullable": true,
                              "additionalProperties": true,
                              "description": "De inhoud van eidas_validation.json die bij de verzegeling is vastgelegd (TSA-keten, vastgezette root, OCSP-antwoord, CRL, verificatierecord), zodat het token in tsr_base64 offline kan worden gecontroleerd. null bij oudere verzegelingen."
                            }
                          },
                          "nullable": true
                        },
                        "created_at": {
                          "type": "string"
                        }
                      }
                    },
                    "custody_log": {
                      "type": "array",
                      "items": {
                        "allOf": [
                          {
                            "$ref": "#/components/schemas/CustodyEntry"
                          },
                          {
                            "type": "object",
                            "properties": {
                              "actor_org": {
                                "type": "string",
                                "nullable": true
                              }
                            }
                          }
                        ]
                      }
                    },
                    "issuer": {
                      "type": "string",
                      "enum": [
                        "ProofSnap"
                      ]
                    },
                    "verified_at": {
                      "type": "string",
                      "nullable": true,
                      "description": "Altijd null."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "NOT_FOUND: ongeldig, onbekend of ingetrokken token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/captures/export-jobs": {
      "post": {
        "tags": [
          "Vastleggingen"
        ],
        "operationId": "startCaptureExportJob",
        "summary": "Alle vastleggingen als één download exporteren (asynchrone taak)",
        "description": "Start een taak die al uw vastleggingen in één download verpakt, opgesplitst in delen van maximaal ongeveer 2 GB. Elk deel is een gewone ZIP: README.txt (Engels) plus README.<language>.txt (dezelfde tekst vertaald, wanneer language niet en is; beide UTF-8 met byte order mark en CRLF-regeleinden; regel 1 van elk bestand verwijst naar het andere), captures.csv (UTF-8 met byte order mark, CRLF; elke vastlegging van de export met de kolommen member (het e-mailadres van de auteur in een bedrijfsexport, leeg in uw eigen export), capture_id, created_at (wanneer het verzoek is ontvangen), captured_at (het moment van vastlegging; leeg bij een mislukte vastlegging), url, status (completed, failed of unavailable), export_part, file, ots_file (captures/<capture_id>.upgraded.ots wanneer dat deel het bewijs bevat, anders leeg), test_mode (true voor een vastlegging gemaakt met een testsleutel: met watermerk, geen bewijs) en note (waarom een pakket niet is opgenomen, ook bij elke rij met unavailable); dezelfde lijst in elk deel, en wanneer delen van elkaar afwijken, is de lijst van het hoogste deel de definitieve) en captures/<capture_id>.zip, het oorspronkelijke verzegelde bewijspakket byte voor byte, plus captures/<capture_id>.upgraded.ots wanneer een bijgewerkt OpenTimestamps-bewijs is opgeslagen. Elk binnenste pakket wordt afzonderlijk geverifieerd met zijn eigen verification/verify.sh of verify.ps1. Een pakket groter dan 2 GB wordt niet opgenomen (vermeld als unavailable met een notitie; download het afzonderlijk met GET /captures/{capture_id}). Mislukte vastleggingen staan alleen in captures.csv; vastleggingen in de prullenbak en definitief verwijderde vastleggingen worden niet geëxporteerd. De items van elk deel krijgen het exporttijdstip als datum (UTC, ook in het extended timestamp-veld). Poll GET /captures/export-jobs/{job_id} tot status ready is. **Hergebruik:** wanneer uw laatste export gereed is, dezelfde scope (en org) en language heeft, nog minstens een dag van zijn downloadvenster over heeft, uw rechten ongewijzigd zijn en er sinds de planning geen vastlegging is toegevoegd, voltooid, naar de prullenbak verplaatst, teruggezet of definitief verwijderd, geeft het verzoek 200 terug met die taak (reused: true): er wordt niets opnieuw opgebouwd en er wordt geen dagelijkse eenheid verbruikt. Een nieuwere export met dezelfde scope die gereed is, vervangt de vorige (de vorige taak wordt expired, expired_reason superseded). Wie de taak mag starten: een live API-sleutel of de dashboardsessie, op elk abonnement inclusief sandbox, Archive only en alleen-lezen sleutels (Website Archive). Een testsleutel krijgt 403 FORBIDDEN (de export bevat live vastleggingen), en GET /captures/export-jobs/{job_id} geeft een testsleutel voor elke taak 404 EXPORT_JOB_NOT_FOUND. Uw eigen export (scope own) bevat uw persoonlijke vastleggingen en de vastleggingen die u hebt gemaakt voor uw huidige bedrijf of een bedrijf waarvan u eigenaar bent, ook via een sleutel terwijl dat bedrijf een lopend bedrijfsarchief is (de vastleggingen van de andere licenties van een archief zijn alleen via de dashboardsessie bereikbaar). scope company (alleen de eigenaar van het bedrijf, ook voor een beëindigd bedrijf binnen zijn downloadvenster; een lopend bedrijfsarchief alleen via de dashboardsessie) exporteert de vastleggingen van elke licentie die voor het bedrijf zijn gemaakt plus uw eigen vastleggingen, met het e-mailadres van de auteur in de kolom member. Eén lopende taak per account (409 EXPORT_JOB_RUNNING noemt die taak), 5 gestarte taken per account en UTC-dag (een mislukte taak geeft haar eenheid terug, een hergebruikte verbruikt er geen), de rate limit per minuut van uw abonnement. Maximaal de nieuwste 20,000 vastleggingen per taak (truncated).",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "scope": {
                    "type": "string",
                    "enum": [
                      "own",
                      "company"
                    ],
                    "default": "own"
                  },
                  "org": {
                    "type": "string",
                    "description": "Bij scope company: een van de bedrijven waarvan u eigenaar bent (huidig of laatste), als u van beide eigenaar bent."
                  },
                  "language": {
                    "type": "string",
                    "default": "en",
                    "description": "Taal van de README-vertaling: README.<language>.txt (de volledige vertaling) naast de Engelse README.txt in elk deel; en levert alleen README.txt op. Een van: en, de, it, fr, es, pt_BR, cs, el, hi, hu, id, ja, ko, nl, pl, ro, ru, sv, tr, vi, zh_CN, zh_TW, ar, he, da, fi, nb, sk, uk. Hoofdletters, koppeltekens en regio worden getolereerd (\"PT-BR\" is pt_BR); alleen \"pt\" of \"zh\" en elke andere waarde geven 400 MALFORMED_REQUEST. Een gereedstaande export wordt alleen voor dezelfde taal hergebruikt. Het dashboard stuurt de taal van zijn interface mee."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Uw gereedstaande export is nog actueel en wordt hergebruikt: geen nieuwe taak, geen dagelijkse eenheid. Lees de links ervan met GET poll_path.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "job_id",
                    "status",
                    "scope",
                    "poll_path",
                    "reused"
                  ],
                  "properties": {
                    "job_id": {
                      "type": "string",
                      "example": "cexp_3Fh8sKq0aZr2LmN1"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "ready"
                      ]
                    },
                    "scope": {
                      "type": "string",
                      "enum": [
                        "own",
                        "company"
                      ]
                    },
                    "poll_path": {
                      "type": "string",
                      "example": "/v1/captures/export-jobs/cexp_3Fh8sKq0aZr2LmN1"
                    },
                    "reused": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    }
                  }
                }
              }
            }
          },
          "202": {
            "description": "De taak is gestart.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "job_id",
                    "status",
                    "scope",
                    "poll_path"
                  ],
                  "properties": {
                    "job_id": {
                      "type": "string",
                      "example": "cexp_3Fh8sKq0aZr2LmN1"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "processing"
                      ]
                    },
                    "scope": {
                      "type": "string",
                      "enum": [
                        "own",
                        "company"
                      ]
                    },
                    "poll_path": {
                      "type": "string",
                      "example": "/v1/captures/export-jobs/cexp_3Fh8sKq0aZr2LmN1"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "MALFORMED_REQUEST: de body is geen JSON-object, of scope / org is ongeldig.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "FORBIDDEN: het verzoek is gedaan met een testsleutel, of met scope company door iemand die binnen het downloadvenster geen eigenaar van een bedrijf is.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "EXPORT_JOB_RUNNING: een taak van het account wordt nog verwerkt; details.job_id en details.poll_path geven aan welke.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/AuthUnavailable"
          }
        }
      }
    },
    "/captures/export-jobs/{job_id}": {
      "get": {
        "tags": [
          "Vastleggingen"
        ],
        "operationId": "getCaptureExportJob",
        "summary": "Status en downloadlinks van een exporttaak voor vastleggingen",
        "description": "De status en voortgang van de taak; zodra die gereed is, één vooraf ondertekende link per deel. Een link is geldig tot url_expires_at: maximaal 24 uur, nooit na download_expires_at en nooit langer dan de referenties waarmee hij is ondertekend (dat kan minder dan een uur zijn); poll opnieuw na url_expires_at voor nieuwe links. download_expires_at ligt 7 dagen na de UTC-middernacht vóór de start van het eerste deel van de taak (6 tot 7 dagen nadat de taak gereed werd); daarna worden de delen verwijderd. De taak verliest haar download eerder (status expired, de delen verwijderd, expired_reason) wanneer een nieuwere export met dezelfde scope haar heeft vervangen, een vastlegging in de taak definitief is verwijderd, de gegevens van het bijbehorende bedrijf zijn verwijderd of uw rechten de taak niet meer dekken (gecontroleerd bij elke leesactie op een taak die gereed is). Een taak die 30 minuten geen voortgang meldt, wordt door deze aanroep als failed beëindigd en wat ze heeft geschreven, wordt verwijderd. Het eerste antwoord met links binnen een uur wordt vastgelegd in het auditlogboek (capture.export_job.links_issued). Alleen het account dat de taak heeft gestart, ziet haar; een bedrijfsexport die een bedrijfsarchief bevat alleen via de dashboardsessie.",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "job_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^cexp_[A-Za-z0-9]{16}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "De taak.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "job_id",
                    "status",
                    "scope",
                    "parts"
                  ],
                  "properties": {
                    "job_id": {
                      "type": "string",
                      "example": "cexp_3Fh8sKq0aZr2LmN1",
                      "pattern": "^cexp_[A-Za-z0-9]{16}$"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "processing",
                        "ready",
                        "failed",
                        "expired"
                      ],
                      "description": "processing terwijl het plan en de delen worden geschreven; ready met de downloadlinks; failed (start een nieuwe taak, de eenheid van die dag is teruggegeven); expired als het downloadvenster voorbij is of de download is ingetrokken (expired_reason; de delen worden verwijderd)."
                    },
                    "scope": {
                      "type": "string",
                      "enum": [
                        "own",
                        "company"
                      ]
                    },
                    "language": {
                      "type": "string",
                      "enum": [
                        "en",
                        "de",
                        "it",
                        "fr",
                        "es",
                        "pt_BR",
                        "cs",
                        "el",
                        "hi",
                        "hu",
                        "id",
                        "ja",
                        "ko",
                        "nl",
                        "pl",
                        "ro",
                        "ru",
                        "sv",
                        "tr",
                        "vi",
                        "zh_CN",
                        "zh_TW",
                        "ar",
                        "he",
                        "da",
                        "fi",
                        "nb",
                        "sk",
                        "uk"
                      ],
                      "description": "De README-taal van de taak (README.<language>.txt naast README.txt; en voor alleen README.txt)."
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time",
                      "nullable": true
                    },
                    "ended_at": {
                      "type": "string",
                      "format": "date-time",
                      "nullable": true
                    },
                    "progress": {
                      "type": "object",
                      "nullable": true,
                      "properties": {
                        "captures_total": {
                          "type": "integer",
                          "nullable": true
                        },
                        "captures_done": {
                          "type": "integer"
                        },
                        "parts_total": {
                          "type": "integer",
                          "nullable": true
                        },
                        "parts_done": {
                          "type": "integer"
                        }
                      }
                    },
                    "truncated": {
                      "type": "boolean",
                      "description": "true als het account meer dan 20,000 exporteerbare vastleggingen heeft: de taak bevat de nieuwste 20,000."
                    },
                    "counts": {
                      "type": "object",
                      "nullable": true,
                      "description": "Ingesteld wanneer de taak gereed is.",
                      "properties": {
                        "captures": {
                          "type": "integer",
                          "description": "Rijen van captures.csv."
                        },
                        "packaged": {
                          "type": "integer",
                          "description": "Pakketten in de delen."
                        },
                        "failed": {
                          "type": "integer",
                          "description": "Mislukte vastleggingen (geen pakket, alleen vermeld in captures.csv)."
                        },
                        "unavailable": {
                          "type": "integer",
                          "description": "Niet opgenomen pakketten: ze konden niet worden gelezen terwijl de export liep, of ze zijn groter dan 2 GB (captures.csv vermeldt welke)."
                        }
                      }
                    },
                    "parts": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "part",
                          "bytes",
                          "count",
                          "download_url"
                        ],
                        "properties": {
                          "part": {
                            "type": "integer",
                            "minimum": 1,
                            "example": 1,
                            "description": "Deelnummer, van 1 tot het aantal delen."
                          },
                          "bytes": {
                            "type": "integer",
                            "example": 1834201112,
                            "description": "Grootte van de deel-ZIP in bytes."
                          },
                          "count": {
                            "type": "integer",
                            "example": 412,
                            "description": "Vastleggingspakketten in dit deel."
                          },
                          "filename": {
                            "type": "string",
                            "example": "proofsnap-captures-2026-10-07T1430Z-part-1-of-3.zip",
                            "description": "Bestandsnaam waaronder de download wordt opgeslagen (Content-Disposition): proofsnap-captures-<UTC-datum en -tijd waarop de taak is gestart>, met -part-N-of-M als er meerdere delen zijn."
                          },
                          "download_url": {
                            "type": "string",
                            "format": "uri",
                            "nullable": true,
                            "description": "Presigned GET van de ZIP van het deel, geldig tot url_expires_at. null als de taak is verlopen."
                          }
                        }
                      }
                    },
                    "download_expires_at": {
                      "type": "string",
                      "format": "date-time",
                      "nullable": true,
                      "description": "Einde van het downloadvenster: 7 dagen na de UTC-middernacht vóór de start van het eerste deel (de levenscyclus van de opslag verwijdert de delen daarna)."
                    },
                    "url_expires_at": {
                      "type": "string",
                      "format": "date-time",
                      "nullable": true,
                      "description": "Wanneer de download_url-waarden van dit antwoord niet meer werken: hooguit 24 uur, nooit na download_expires_at en nooit na het verlopen van de ondertekeningsreferenties. Poll daarna opnieuw voor nieuwe links."
                    },
                    "error": {
                      "type": "string",
                      "nullable": true,
                      "example": "export timed out"
                    },
                    "expired_reason": {
                      "type": "string",
                      "nullable": true,
                      "enum": [
                        "window_ended",
                        "superseded",
                        "rights_changed",
                        "capture_erased",
                        "company_deleted",
                        null
                      ],
                      "description": "Ingesteld wanneer status expired is: window_ended (download_expires_at is verstreken), superseded (een nieuwere export met dezelfde scope), rights_changed (uw rechten dekken de taak niet meer), capture_erased (een vastlegging in de taak is definitief verwijderd), company_deleted (de gegevens van het bijbehorende bedrijf zijn verwijderd)."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "EXPORT_JOB_NOT_FOUND: deze taak bestaat niet, is een taak van een ander account, het record ervan is verlopen (8 dagen na afloop), of het verzoek is gedaan met een testsleutel.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/AuthUnavailable"
          }
        }
      }
    },
    "/webhook-subscriptions": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "listWebhookSubscriptions",
        "summary": "Webhookabonnementen weergeven",
        "description": "De webhookabonnementen van het account, nieuwste eerst, actief en uitgeschakeld, zonder secrets. API-sleutel of dashboardsessie; een alleen-lezen sleutel (Website Archive) mag dit gebruiken.",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Abonnementen.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "subscriptions": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WebhookSubscription"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "503": {
            "$ref": "#/components/responses/AuthUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "createWebhookSubscription",
        "summary": "Abonneren op accountgebeurtenissen",
        "description": "Abonneer een https-endpoint op accountgebeurtenissen: elke overeenkomende gebeurtenis van het account wordt er via POST naartoe gestuurd, ondertekend met het eigen secret van het webhookabonnement (eenmalig teruggegeven in dit antwoord). Vereist een bevestigd e-mailadres (403 EMAIL_NOT_VERIFIED). Toegestaan terwijl een abonnement achterstallig is (er wordt niets gefactureerd). Limieten voor ACTIEVE webhookabonnementen per account: 25 op developer, startup, growth, custom, legal_solo en legal_seats, 10 op website_archive, 0 op archive_only en sandbox (409 WEBHOOK_SUBSCRIPTION_LIMIT). De URL volgt de regels van webhook_url (https, geen inloggegevens, geen privé- of gereserveerd adres; bij elke aflevering opnieuw gecontroleerd). Idempotency-Key wordt ondersteund: een nieuwe poging met dezelfde sleutel en body binnen 24 uur krijgt het eerste antwoord terug, inclusief secret, in plaats van een tweede webhookabonnement. API-sleutel of dashboardsessie; een alleen-lezen sleutel (Website Archive) mag dit gebruiken.",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "url",
                  "events"
                ],
                "additionalProperties": false,
                "properties": {
                  "url": {
                    "type": "string",
                    "example": "https://hooks.example.com/proofsnap"
                  },
                  "events": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 9,
                    "uniqueItems": true,
                    "items": {
                      "$ref": "#/components/schemas/WebhookEventType"
                    }
                  },
                  "filters": {
                    "$ref": "#/components/schemas/WebhookSubscriptionFilters"
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 200,
                    "description": "Platte tekst, maximaal 200 tekens."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Aangemaakt. `secret` wordt alleen hier getoond.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookSubscriptionCreated"
                }
              }
            }
          },
          "400": {
            "description": "MALFORMED_REQUEST (events, filters, description, onbekend veld), MISSING_FIELD (url) of INVALID_URL.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "EMAIL_NOT_VERIFIED.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "WEBHOOK_SUBSCRIPTION_LIMIT, IDEMPOTENCY_IN_PROGRESS.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "SSRF_BLOCKED (de URL verwijst naar een geblokkeerd adres) of IDEMPOTENCY_KEY_REUSED.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/AuthUnavailable"
          }
        }
      }
    },
    "/webhook-subscriptions/{subscription_id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/SubscriptionId"
        }
      ],
      "get": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "getWebhookSubscription",
        "summary": "Een webhookabonnement ophalen",
        "description": "Eén abonnement met de status van de aflevering, zonder het secret. API-sleutel of dashboardsessie; een alleen-lezen sleutel (Website Archive) mag dit gebruiken.",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Het abonnement.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookSubscription"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "WEBHOOK_SUBSCRIPTION_NOT_FOUND: onbekend, verwijderd of van een ander account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/AuthUnavailable"
          }
        }
      },
      "patch": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "updateWebhookSubscription",
        "summary": "Een webhookabonnement wijzigen of opnieuw activeren",
        "description": "Wijzig events, filters (null of {} wist ze) of description, of activeer een uitgeschakeld abonnement opnieuw met status \"active\" (zet de foutentellers terug; wordt getoetst aan de limiet voor actieve abonnementen). De URL kan niet worden gewijzigd: maak een nieuw abonnement aan. Vereist een bevestigd e-mailadres. API-sleutel of dashboardsessie; een alleen-lezen sleutel (Website Archive) mag dit gebruiken.",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "events": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 9,
                    "uniqueItems": true,
                    "items": {
                      "$ref": "#/components/schemas/WebhookEventType"
                    }
                  },
                  "filters": {
                    "type": "object",
                    "allOf": [
                      {
                        "$ref": "#/components/schemas/WebhookSubscriptionFilters"
                      }
                    ],
                    "nullable": true
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 200,
                    "nullable": true
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "active"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Het gewijzigde abonnement.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookSubscription"
                }
              }
            }
          },
          "400": {
            "description": "MALFORMED_REQUEST.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "EMAIL_NOT_VERIFIED.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "WEBHOOK_SUBSCRIPTION_NOT_FOUND: onbekend, verwijderd of van een ander account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "WEBHOOK_SUBSCRIPTION_LIMIT (heractivering boven de limiet).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/AuthUnavailable"
          }
        }
      },
      "delete": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "deleteWebhookSubscription",
        "summary": "Een webhookabonnement verwijderen",
        "description": "Verwijdert het abonnement, het secret ervan en de afleveringen in de wachtrij. 404 voor een onbekend of al verwijderd abonnement (beschouw het als verdwenen). API-sleutel of dashboardsessie; een alleen-lezen sleutel (Website Archive) mag dit gebruiken.",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "204": {
            "description": "Verwijderd.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "WEBHOOK_SUBSCRIPTION_NOT_FOUND: onbekend, verwijderd of van een ander account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/AuthUnavailable"
          }
        }
      }
    },
    "/events": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "listEvents",
        "summary": "Gebeurtenissenlogboek",
        "description": "Elke gebeurtenis die in de afgelopen 30 dagen voor het account is uitgezonden, ongeacht of een abonnement die heeft ontvangen, de nieuwste eerst. Een API-sleutel ziet de gebeurtenissen van de omgeving waarin hij draait (een live-sleutel live-gebeurtenissen, een testsleutel testgebeurtenissen); een dashboardsessie ziet live-gebeurtenissen. Gebruik dit om gebeurtenissen achteraf aan te vullen of om te pollen in plaats van een abonnement te nemen. API-sleutel of dashboardsessie; een alleen-lezen sleutel (Website Archive) mag dit gebruiken.",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "type",
            "in": "query",
            "required": false,
            "description": "Alleen dit type gebeurtenis.",
            "schema": {
              "$ref": "#/components/schemas/WebhookEventType"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "1 tot 100, standaard 25.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "description": "Een gebeurtenis-id: de pagina begint erna (geef next_cursor mee). Een id van een ander account of een andere omgeving, of ouder dan 30 dagen, geeft 400.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Een pagina met gebeurtenissen.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "events": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Event"
                      }
                    },
                    "has_more": {
                      "type": "boolean"
                    },
                    "next_cursor": {
                      "type": "string",
                      "nullable": true,
                      "description": "Geef dit mee als starting_after voor de volgende pagina; null op de laatste pagina."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "MALFORMED_REQUEST (type, limit of starting_after).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "503": {
            "$ref": "#/components/responses/AuthUnavailable"
          }
        }
      }
    },
    "/whoami": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "whoami",
        "summary": "Wie ben ik",
        "description": "Wat de referentie is: abonnement, omgeving, of de API-sleutel alleen-lezen is, en de id, naam en het voorvoegsel van de sleutel (null bij een dashboardsessie). Integraties gebruiken dit om een verbinding te testen en te benoemen. Geen e-mailadres en geen account-id. API-sleutel of dashboardsessie; een alleen-lezen sleutel (Website Archive) mag dit gebruiken.",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "De referentie.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "plan": {
                      "type": "string",
                      "nullable": true,
                      "example": "startup",
                      "enum": [
                        "developer",
                        "startup",
                        "growth",
                        "custom",
                        "legal_solo",
                        "legal_seats",
                        "website_archive",
                        "archive_only",
                        "sandbox",
                        null
                      ]
                    },
                    "api_read_only": {
                      "type": "boolean",
                      "description": "true bij een API-sleutel van een Website Archive-account (die mag lezen en zich abonneren, maar geen vastleggingen maken)."
                    },
                    "env": {
                      "type": "string",
                      "enum": [
                        "live",
                        "test"
                      ],
                      "description": "De omgeving waarin de referentie draait."
                    },
                    "key": {
                      "type": "object",
                      "nullable": true,
                      "properties": {
                        "key_id": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string",
                          "nullable": true
                        },
                        "prefix": {
                          "type": "string",
                          "enum": [
                            "ps_live_",
                            "ps_test_"
                          ]
                        }
                      }
                    },
                    "email_verified": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "503": {
            "$ref": "#/components/responses/AuthUnavailable"
          }
        }
      }
    },
    "/captures/events": {
      "get": {
        "tags": [
          "Vastleggingen"
        ],
        "operationId": "listCaptureEvents",
        "summary": "Gebeurtenissenketen van vastleggingen (verwijderingen en legal holds)",
        "description": "Het logboek van wat er met de vastleggingen van het account is gebeurd, zo opgebouwd dat elke manipulatie aantoonbaar is: naar de prullenbak verplaatsen, terugzetten, definitief verwijderen (erase), het legen van de prullenbak, verwijdering na afloop van de bewaartermijn, verwijdering samen met het account of het bedrijf, legal hold ingesteld en opgeheven (een opheffing door het systeem na afloop van het abonnement heeft actor `system` en detail `subscription_ended`), het resultaat van S3 Object Lock en de import van een bewijspakket (type `import`, detail = het soort pakket). Oudste eerst. De keten overleeft de vastleggingen en de gegevens van het account: ze bevat alleen id's en manifesthashes, geen inhoud van vastleggingen en geen vrije tekst.\n\nZo verifieert u de keten: (1) bereken voor elke gebeurtenis de SHA-256 over de canonieke JSON van de gehashte velden (verification.hashed_fields; sleutels gesorteerd, geen witruimte, ontbrekende waarden als null) en vergelijk die met `hash`; (2) controleer dat elke `prev_hash` gelijk is aan de `hash` van de vorige gebeurtenis (64 nullen voor seq 1) en dat seq geen gaten heeft; (3) controleer bij een verankering dat `head_hash` gelijk is aan de `hash` van gebeurtenis `to_seq`, schrijf `ots_proof_base64` (base64-gedecodeerd) naar een bestand en voer `ots verify -d <head_hash> anchor.ots` uit (OpenTimestamps-client): dit bewijst dat de keten tot en met die gebeurtenis uiterlijk op het moment van het Bitcoin-blok bestond. Verankeringen worden eenmaal per dag gemaakt voor elk account met gebeurtenissen van een eerdere dag.\n\nAPI-sleutel of dashboardsessie, het eigen account; `scope=company` geeft de eigenaar van een bedrijf de keten van de vastleggingen van het bedrijf (`org` kiest er een wanneer de aanroeper eigenaar is van meerdere; een bedrijf dat als bedrijfsarchief wordt bewaard, alleen via de dashboardsessie). Een testsleutel krijgt 403 FORBIDDEN: de keten omvat live vastleggingen en kan niet worden gefilterd zonder haar te breken.",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "1 tot 500, standaard 100.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 100
            }
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "description": "Een gebeurtenis-seq: de pagina begint erna (geef next_starting_after mee).",
            "schema": {
              "type": "integer",
              "minimum": 0
            }
          },
          {
            "name": "scope",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "own",
                "company"
              ],
              "default": "own"
            }
          },
          {
            "name": "org",
            "in": "query",
            "required": false,
            "description": "scope=company: de id van het bedrijf.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Een pagina van de keten.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "chain": {
                      "type": "string"
                    },
                    "scope": {
                      "type": "string",
                      "enum": [
                        "own",
                        "company"
                      ]
                    },
                    "head": {
                      "type": "object",
                      "properties": {
                        "chain": {
                          "type": "string"
                        },
                        "seq": {
                          "type": "integer"
                        },
                        "head_hash": {
                          "type": "string",
                          "nullable": true
                        },
                        "anchored_seq": {
                          "type": "integer"
                        }
                      }
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CaptureEvent"
                      }
                    },
                    "has_more": {
                      "type": "boolean"
                    },
                    "next_starting_after": {
                      "type": "integer",
                      "nullable": true
                    },
                    "anchors": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CaptureEventAnchor"
                      }
                    },
                    "verification": {
                      "type": "object",
                      "properties": {
                        "hash": {
                          "type": "string"
                        },
                        "hashed_fields": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "genesis_prev_hash": {
                          "type": "string"
                        },
                        "anchor": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "MALFORMED_REQUEST: limit, starting_after of scope is ongeldig.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "FORBIDDEN: het verzoek is gedaan met een testsleutel, of met scope=company door iemand die geen eigenaar van het bedrijf is.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/AuthUnavailable"
          }
        }
      }
    },
    "/legal-holds": {
      "post": {
        "tags": [
          "Vastleggingen"
        ],
        "operationId": "bulkLegalHold",
        "summary": "Legal holds in bulk instellen of opheffen",
        "description": "Stelt de legal hold van veel vastleggingen tegelijk in (enabled true) of heft deze op (false). selector bevat precies één van: `capture_ids` (1 tot 500 id's), `matter_id` (de vastleggingen van een zaak die de aanroeper kan openen), `host` (de eigen vastleggingen van de aanroeper van die hostnaam) of `schedule_id` (de eigen vastleggingen van de aanroeper die door een van de schema's van de aanroeper zijn gemaakt). Elke vastlegging doorloopt de regels van PATCH /captures/{capture_id} met action legal_hold en krijgt een eigen resultaat: applied, unchanged (al in die toestand) of geweigerd met de foutcode (bijv. FORBIDDEN, CAPTURE_IN_TRASH, CAPTURE_PROCESSING, LEGAL_HOLD_OWNER_ONLY, LEGAL_HOLD_MATTER, LEGAL_HOLD_SOURCE voor een weglakking waarvan de bronvastlegging nog onder legal hold staat, CAPTURE_NOT_FOUND, LEGAL_HOLD_NOT_AVAILABLE voor een vastlegging in testmodus). De weglakkingen die van elke vastlegging zijn gemaakt, volgen de legal hold daarvan, ook bij een vastlegging die als unchanged wordt gemeld; als dat niet binnen het verzoek klaar is, wordt het binnen enkele minuten voltooid. Voor het instellen van legal holds is een betaald abonnement nodig: de gratis sandbox krijgt 403 LEGAL_HOLD_NOT_AVAILABLE (details.reason \"plan\") voor het hele verzoek; opheffen werkt op elk abonnement. Een hier ingestelde legal hold heeft legal_hold_source \"bulk\"; opheffen in bulk of afzonderlijk werkt hetzelfde. Maximaal 500 vastleggingen per verzoek: bij een zaak, host of schema met meer wordt afgekapt (`truncated: true`, stuur het verzoek opnieuw). De S3 Object Lock legal hold van de eerste 25 gewijzigde vastleggingen wordt in het verzoek toegepast, de rest binnen enkele minuten (`s3_legal_hold.status` pending). Kosten: één rate-limit-eenheid per 10 vastleggingen (minimaal één); een verzoek dat meer nodig heeft dan één venster toestaat, geeft 400 RATE_LIMIT_COST_TOO_HIGH. API-sleutel of dashboardsessie; bij Website Archive is de API-sleutel alleen-lezen (403 PLAN_API_READ_ONLY). Een dashboardsessie of sleutel met een niet-bevestigd e-mailadres krijgt 403 EMAIL_NOT_VERIFIED. Wordt niet geblokkeerd door een achterstallig abonnement.",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "enabled",
                  "selector"
                ],
                "additionalProperties": false,
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  },
                  "reason": {
                    "type": "string",
                    "maxLength": 200,
                    "description": "Platte tekst die bij elke ingestelde legal hold wordt bewaard (niet bij een opheffing); nooit in de gehashte ketens."
                  },
                  "selector": {
                    "type": "object",
                    "minProperties": 1,
                    "maxProperties": 1,
                    "properties": {
                      "capture_ids": {
                        "type": "array",
                        "minItems": 1,
                        "maxItems": 500,
                        "items": {
                          "type": "string"
                        }
                      },
                      "matter_id": {
                        "type": "string"
                      },
                      "host": {
                        "type": "string"
                      },
                      "schedule_id": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Resultaten per vastlegging.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "enabled": {
                      "type": "boolean"
                    },
                    "selector": {
                      "type": "string",
                      "enum": [
                        "capture_ids",
                        "matter_id",
                        "host",
                        "schedule_id"
                      ]
                    },
                    "requested": {
                      "type": "integer"
                    },
                    "truncated": {
                      "type": "boolean"
                    },
                    "applied": {
                      "type": "integer"
                    },
                    "unchanged": {
                      "type": "integer"
                    },
                    "refused": {
                      "type": "integer"
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "capture_id": {
                            "type": "string"
                          },
                          "result": {
                            "type": "string",
                            "enum": [
                              "applied",
                              "unchanged",
                              "refused"
                            ]
                          },
                          "code": {
                            "type": "string",
                            "description": "De foutcode van een weigering."
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "MALFORMED_REQUEST (body, selector, meer dan 500 capture_ids), RATE_LIMIT_COST_TOO_HIGH.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "EMAIL_NOT_VERIFIED, PLAN_API_READ_ONLY, LEGAL_HOLD_NOT_AVAILABLE (instellen zonder betaald abonnement, details.reason plan).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "MATTER_NOT_FOUND (matter_id die de aanroeper niet kan openen), SCHEDULE_NOT_FOUND.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/AuthUnavailable"
          }
        }
      }
    },
    "/imports": {
      "post": {
        "tags": [
          "Pakketten importeren"
        ],
        "operationId": "createImport",
        "summary": "Een pakketimport starten",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "description": "Maakt een import aan en start de upload ervan. Geef de exacte grootte van de ZIP in bytes op; het antwoord bevat één vooraf ondertekende PUT-URL per deel van 32 MiB (het laatste deel is kleiner), 1 uur geldig (`upload.expires_at`; nooit langer dan de ondertekeningsreferenties van de server). Verstuur de bytes van elk deel met PUT naar de bijbehorende URL, bewaar de header `ETag` van elk antwoord en roep daarna binnen 24 uur POST /imports/{import_id}/complete aan. Met `matter_id` wordt de geaccepteerde vastlegging aan die zaak toegevoegd: u moet er op dat moment vastleggingen aan kunnen toevoegen (een open zaak die u kunt openen; de import van een bedrijfslicentie alleen naar een zaak van dat bedrijf). Elke aanmaak telt mee voor de daglimiet van 50.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "filename",
                  "size_bytes"
                ],
                "additionalProperties": false,
                "properties": {
                  "filename": {
                    "type": "string",
                    "maxLength": 200,
                    "description": "De weer te geven bestandsnaam (platte tekst: geen besturingstekens, onzichtbare tekens of tekens voor tekstrichting)."
                  },
                  "size_bytes": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 2147483648,
                    "description": "Exacte grootte van de ZIP in bytes, maximaal 2 GiB."
                  },
                  "matter_id": {
                    "type": "string",
                    "description": "Optioneel: voeg de geaccepteerde vastlegging toe aan deze zaak."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Import aangemaakt; upload de delen.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "import_id": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "uploading"
                      ]
                    },
                    "upload": {
                      "type": "object",
                      "properties": {
                        "upload_id": {
                          "type": "string"
                        },
                        "part_size": {
                          "type": "integer",
                          "example": 33554432
                        },
                        "parts": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "part_number": {
                                "type": "integer"
                              },
                              "url": {
                                "type": "string",
                                "format": "uri",
                                "description": "Presigned S3 UploadPart-URL: PUT precies size_bytes bytes (de URL ondertekent de Content-Length) en lees de response-header ETag. Er is geen andere header nodig."
                              },
                              "size_bytes": {
                                "type": "integer",
                                "description": "De exacte lengte in bytes van dit deel: part_size voor elk deel behalve het laatste, de rest voor het laatste (deel n begint bij (n - 1) x part_size)."
                              }
                            }
                          }
                        },
                        "expires_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "MALFORMED_REQUEST (body, filename, size_bytes, matter_id, onbekend veld).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "PAYMENT_REQUIRED: het abonnement is achterstallig of onbetaald.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "IMPORT_NOT_ALLOWED (details.reason plan: alleen Legal-abonnementen; live_credentials_required: een testsleutel, een sessie zonder gekoppelde facturering of de sandbox), EMAIL_NOT_VERIFIED, FORBIDDEN (een import door een bedrijfslicentie naar een zaak van een ander bedrijf).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "MATTER_NOT_FOUND.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "IMPORT_LIMIT (3 imports in behandeling; details.limit, details.in_progress), MATTER_SEALED, MATTER_ARCHIVED, COMPANY_READ_ONLY.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "IMPORT_TOO_LARGE: size_bytes groter dan 2 GiB (details.max_bytes).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED: de limiet per minuut, of de daglimiet van 50 imports (details.action import_create, details.daily_limit).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/AuthUnavailable"
          }
        }
      },
      "get": {
        "tags": [
          "Pakketten importeren"
        ],
        "operationId": "listImports",
        "summary": "Uw imports weergeven",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "description": "Uw imports, nieuwste eerst. Definitieve imports worden 30 dagen bewaard (de vastlegging van een geaccepteerde import blijft bestaan). Een upload waarvan de 24 uur verstreken zijn, wordt als verlopen getoond.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "next_cursor van de vorige pagina."
          }
        ],
        "responses": {
          "200": {
            "description": "Een pagina met imports.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "imports": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Import"
                      }
                    },
                    "next_cursor": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "MALFORMED_REQUEST (limit, cursor).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "503": {
            "$ref": "#/components/responses/AuthUnavailable"
          }
        }
      }
    },
    "/imports/{import_id}": {
      "parameters": [
        {
          "name": "import_id",
          "in": "path",
          "required": true,
          "description": "Import-ID (imp_ gevolgd door 16 tot 24 letters en cijfers). Een onjuist gevormde ID geeft 404 IMPORT_NOT_FOUND.",
          "schema": {
            "type": "string",
            "pattern": "^imp_[A-Za-z0-9]{16,24}$"
          }
        }
      ],
      "get": {
        "tags": [
          "Pakketten importeren"
        ],
        "operationId": "getImport",
        "summary": "Een import ophalen",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "description": "De import en, zodra die definitief is, het resultaat. Poll dit na POST /imports/{import_id}/complete; verifiëren duurt meestal enkele seconden, bij een pakket van 2 GiB tot enkele minuten. Met ?upload=true bij een upload die nog loopt, voegt het antwoord upload toe: nieuwe vooraf ondertekende URL's (parts) voor de delen die de opslag nog niet heeft en de al ontvangen delen (received: part_number, etag, size_bytes; null wanneer ze niet konden worden opgesomd, waarna elk deel opnieuw vooraf wordt ondertekend), of completed: true wanneer de upload al was voltooid (verstuur complete opnieuw). Gebruik dit om te hervatten na een onderbroken netwerkverbinding, een verlopen URL voor een deel (deze zijn maximaal 1 uur geldig, het uploadvenster 24 uur) of het opnieuw laden van een pagina. ?upload=true kost één eenheid van de rate limit per minuut.",
        "responses": {
          "200": {
            "description": "De import.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Import"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "IMPORT_NOT_FOUND (ook bij de import van een ander account).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED (alleen met ?upload=true).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/AuthUnavailable"
          }
        },
        "parameters": [
          {
            "name": "upload",
            "in": "query",
            "required": false,
            "description": "true (of 1): voeg nieuwe URL's voor delen en de ontvangen delen van een lopende upload toe.",
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "1"
              ]
            }
          }
        ]
      },
      "delete": {
        "tags": [
          "Pakketten importeren"
        ],
        "operationId": "cancelImport",
        "summary": "Een upload annuleren",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "description": "Annuleert een import die nog wordt geüpload: de upload wordt afgebroken, de delen worden verwijderd en de import wordt verwijderd (GET geeft daarna 404).",
        "responses": {
          "200": {
            "description": "Geannuleerd.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "import_id": {
                      "type": "string"
                    },
                    "deleted": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "IMPORT_NOT_FOUND.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "IMPORT_STATE: de import wordt niet langer geüpload (details.status).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/AuthUnavailable"
          }
        }
      }
    },
    "/imports/{import_id}/complete": {
      "parameters": [
        {
          "name": "import_id",
          "in": "path",
          "required": true,
          "description": "Import-ID (imp_ gevolgd door 16 tot 24 letters en cijfers). Een onjuist gevormde ID geeft 404 IMPORT_NOT_FOUND.",
          "schema": {
            "type": "string",
            "pattern": "^imp_[A-Za-z0-9]{16,24}$"
          }
        }
      ],
      "post": {
        "tags": [
          "Pakketten importeren"
        ],
        "operationId": "completeImport",
        "summary": "De upload voltooien en verifiëren",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "description": "Voltooit de upload en start de verificatie. parts is optioneel: stuur elk deelnummer (1 tot N, op volgorde) mee met de ETag die de PUT van dat deel retourneerde, of laat het weg, dan leest de server de geüploade delen uit de opslag (handig in een browser die de ETag-header niet kan lezen). Controleert of de opgeslagen grootte gelijk is aan size_bytes. Poll GET /imports/{import_id}. Een lijst met delen die de opslag weigert, of een deel dat nog niet is geüpload (details.missing_parts), geeft 400 en de upload blijft open: upload wat ontbreekt (GET /imports/{import_id}?upload=true geeft nieuwe URL's) en verstuur het opnieuw. Complete opnieuw versturen nadat een antwoord verloren is gegaan, is veilig: een complete die nog loopt, retourneert 409, een complete die is vastgelopen, wordt na 2 minuten overgenomen, en een upload die al was voltooid, gaat door naar de verificatie. Een grootte die afwijkt van size_bytes verwijdert de upload (422 IMPORT_SIZE_MISMATCH; de import wordt afgewezen, maak een nieuwe aan).",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "parts": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "required": [
                        "part_number",
                        "etag"
                      ],
                      "properties": {
                        "part_number": {
                          "type": "integer",
                          "minimum": 1
                        },
                        "etag": {
                          "type": "string",
                          "description": "De ETag-antwoordheader van de PUT van het deel, met of zonder aanhalingstekens."
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Verificatie gestart.",
            "headers": {
              "X-Request-ID": {
                "$ref": "#/components/headers/XRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "import_id": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "verifying"
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "MALFORMED_REQUEST (parts in de verkeerde volgorde of niet overeenkomend met de geüploade delen; of, zonder parts, een deel dat nog niet is geüpload: details.missing_parts).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "PAYMENT_REQUIRED.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "IMPORT_NOT_ALLOWED, EMAIL_NOT_VERIFIED.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "IMPORT_NOT_FOUND.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "IMPORT_STATE: de import wordt niet meer geüpload (details.status, bijvoorbeeld verifying of expired).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "IMPORT_SIZE_MISMATCH (details.expected, details.actual).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/AuthUnavailable"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Stuur uw API-sleutel als `Authorization: Bearer ps_live_...` (de naam van het schema is niet hoofdlettergevoelig). Een ontbrekende of onjuist opgemaakte header geeft 401 MISSING_API_KEY. Een onbekende, ingetrokken of verlopen sleutel, of een account zonder API-toegang, geeft 401 INVALID_API_KEY met `WWW-Authenticate: Bearer realm=\"ProofSnap API\", error=\"invalid_token\"`. 403 is gereserveerd voor een geldige referentie die iets niet mag doen (FORBIDDEN, abonnementsbeperkingen). Een `ps_live_`-sleutel van een account zonder gekoppeld factureringsaccount (Stripe-klant) werkt als testsleutel: vastleggingen krijgen een watermerk en worden nooit gefactureerd, en include_eidas wordt geweigerd met 403 EIDAS_REQUIRES_LIVE_KEY. De gateway cachet de beslissing voor een sleutel maximaal 60 seconden, dus een ingetrokken sleutel kan nog maximaal 60 seconden blijven werken. Als de referentie door een tijdelijke storing in de backend niet kan worden gecontroleerd, geeft de gateway in plaats daarvan 503 SERVICE_UNAVAILABLE; dat antwoord wordt niet gecachet, dus probeer het opnieuw met backoff. De `WWW-Authenticate`-challenge wordt ongewijzigd teruggegeven bij een 401 van de gateway (de controle van de referentie); bij een 401 van een route-handler levert de gateway dezelfde challenge af onder de naam `x-amzn-Remapped-WWW-Authenticate`."
      }
    },
    "headers": {
      "XRequestId": {
        "description": "Verzoek-ID, ook handig bij supportvragen. Wordt meegestuurd bij succesvolle antwoorden; foutbody's bevatten deze als request_id.",
        "schema": {
          "type": "string"
        }
      },
      "RateLimitLimit": {
        "description": "Toegestane verzoeken in het huidige venster van 60 seconden. Verouderde header, behouden naast RateLimit / RateLimit-Policy.",
        "schema": {
          "type": "integer"
        }
      },
      "RateLimitRemaining": {
        "description": "Resterende verzoeken in het huidige venster. Verouderde header, behouden naast RateLimit / RateLimit-Policy.",
        "schema": {
          "type": "integer"
        }
      },
      "RateLimitReset": {
        "description": "ISO 8601-tijdstip waarop het huidige venster eindigt. Verouderde header, behouden naast RateLimit / RateLimit-Policy.",
        "schema": {
          "type": "string",
          "format": "date-time"
        }
      },
      "RetryAfter": {
        "description": "Aantal seconden wachten voordat u het opnieuw probeert.",
        "schema": {
          "type": "integer"
        }
      },
      "QuotaLimit": {
        "description": "Vastleggingen per maand die in het abonnement zijn inbegrepen (live sleutels bij abonnementen met een maandelijks quotum).",
        "schema": {
          "type": "integer"
        }
      },
      "QuotaRemaining": {
        "description": "Inbegrepen vastleggingen die deze maand nog resteren.",
        "schema": {
          "type": "integer"
        }
      },
      "QuotaUsed": {
        "description": "Voltooide vastleggingen deze maand.",
        "schema": {
          "type": "integer"
        }
      },
      "QuotaCap": {
        "description": "Het maandelijkse maximum dat voor de aanroeper geldt (standaard van het abonnement, eigen limiet of bedrijfslimiet).",
        "schema": {
          "type": "integer"
        }
      },
      "QuotaOrgLimit": {
        "description": "Alleen voor bedrijfsleden: de maandelijkse limiet voor het hele bedrijf, als de eigenaar er een heeft ingesteld.",
        "schema": {
          "type": "integer"
        }
      },
      "QuotaOrgUsed": {
        "description": "Alleen voor bedrijfsleden: vastleggingen die deze maand voor het hele bedrijf zijn toegelaten.",
        "schema": {
          "type": "integer"
        }
      },
      "SandboxLimit": {
        "description": "Sandbox (dashboardsessie zonder abonnement): eenmalig tegoed aan testvastleggingen met watermerk (50).",
        "schema": {
          "type": "integer"
        }
      },
      "SandboxRemaining": {
        "description": "Sandbox: resterend tegoed.",
        "schema": {
          "type": "integer"
        }
      },
      "SandboxUsed": {
        "description": "Sandbox: gebruikt tegoed.",
        "schema": {
          "type": "integer"
        }
      },
      "IdempotentReplay": {
        "description": "\"true\" wanneer dit antwoord een eerder verzoek met dezelfde Idempotency-Key opnieuw afspeelt.",
        "schema": {
          "type": "string",
          "enum": [
            "true"
          ]
        }
      },
      "WWWAuthenticate": {
        "description": "RFC 6750-challenge die bij elke 401 wordt meegestuurd. Bij een 401 die door een route-handler wordt teruggegeven en niet door de gateway, levert API Gateway deze af onder de naam `x-amzn-Remapped-WWW-Authenticate`. Een ongeldige, ingetrokken of verlopen API-sleutel, of een onjuist of ingetrokken sessietoken (INVALID_API_KEY): `Bearer realm=\"ProofSnap API\", error=\"invalid_token\"`. Een ontbrekende of onjuist opgemaakte Authorization-header (MISSING_API_KEY): `Bearer realm=\"ProofSnap API\"`.",
        "schema": {
          "type": "string"
        },
        "example": "Bearer realm=\"ProofSnap API\", error=\"invalid_token\""
      },
      "RateLimitPolicy": {
        "description": "Rate-limitbeleid volgens het IETF-concept draft-ietf-httpapi-ratelimit-headers (structured-field-vorm; gebaseerd op een concept, kan met het concept veranderen): `\"default\";q=<requests per window>;w=60`.",
        "schema": {
          "type": "string"
        },
        "example": "\"default\";q=120;w=60"
      },
      "RateLimit": {
        "description": "Huidige status van de rate limit, volgens het IETF-concept draft-ietf-httpapi-ratelimit-headers (gebaseerd op het concept): `\"default\";r=<requests left>;t=<seconds until the window resets>`. Verzonden bij antwoorden van routes met een rate limit (vastleggingen enkel en batch, compare, redact, certify-hash) nadat de limiter heeft gedraaid, niet alleen bij 429 (een verzoek dat vóór de limiter wordt geweigerd, bijvoorbeeld vanwege authenticatie, heeft deze header niet).",
        "schema": {
          "type": "string"
        },
        "example": "\"default\";r=117;t=42"
      }
    },
    "parameters": {
      "CaptureId": {
        "name": "capture_id",
        "in": "path",
        "required": true,
        "description": "ID van een vastlegging, certificaat, vergelijking, weglakking of geïmporteerd pakket. ID's beginnen met cap_, cert_, cmp_, red_ of imp_. Een onjuist opgemaakte ID geeft 404 CAPTURE_NOT_FOUND.",
        "schema": {
          "type": "string",
          "example": "cap_a1B2c3D4e5F6g7H8"
        }
      },
      "ScheduleId": {
        "name": "schedule_id",
        "in": "path",
        "required": true,
        "description": "Schema-ID. Een schema van een ander account geeft 404 SCHEDULE_NOT_FOUND.",
        "schema": {
          "type": "string",
          "example": "sch_a1B2c3D4e5F6g7H8"
        }
      },
      "MatterId": {
        "name": "matter_id",
        "in": "path",
        "required": true,
        "description": "Zaak-ID. Een zaak waarvan u geen lid bent, geeft 404 MATTER_NOT_FOUND.",
        "schema": {
          "type": "string",
          "example": "mat_a1B2c3D4e5F6g7H8"
        }
      },
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "description": "Optionele clientsleutel die een nieuwe poging veilig maakt. Alleen de eerste 255 tekens worden gebruikt, en sleutels gelden 24 uur lang binnen uw account. Een nieuwe poging met dezelfde sleutel en dezelfde body geeft de oorspronkelijke antwoordbody terug met `Idempotent-Replay: true` (202 voor een enkele vastlegging, een batch of een vergelijking, 201 voor een weglakking; `status` wordt bijgewerkt naar de huidige status van de vastlegging, vergelijking of weglakking, en van elke vastlegging van een batch); er wordt geen nieuwe vastlegging aangemaakt of gefactureerd. Dezelfde sleutel met een andere body geeft 422 IDEMPOTENCY_KEY_REUSED. Een nieuwe poging terwijl het eerste verzoek nog wordt geaccepteerd, geeft 409 IDEMPOTENCY_IN_PROGRESS met de oorspronkelijke ID in `error.details.capture_id` (de batch_id bij een batch).",
        "schema": {
          "type": "string",
          "maxLength": 255
        }
      },
      "SubscriptionId": {
        "name": "subscription_id",
        "in": "path",
        "required": true,
        "description": "ID van het webhookabonnement. Een abonnement van een ander account geeft 404 WEBHOOK_SUBSCRIPTION_NOT_FOUND.",
        "schema": {
          "type": "string",
          "example": "whs_a1B2c3D4e5F6g7H8"
        }
      }
    },
    "responses": {
      "RateLimited": {
        "description": "RATE_LIMITED: te veel verzoeken in het huidige venster van 60 seconden. `error.details.retry_after_seconds` herhaalt Retry-After. RateLimit meldt r=0 en het aantal seconden tot het venster opnieuw begint.",
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          },
          "RateLimit-Policy": {
            "$ref": "#/components/headers/RateLimitPolicy"
          },
          "RateLimit": {
            "$ref": "#/components/headers/RateLimit"
          },
          "X-RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimitLimit"
          },
          "X-RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimitRemaining"
          },
          "X-RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimitReset"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "code": "RATE_LIMITED",
                "message": "Too many requests. See Retry-After header.",
                "details": {
                  "retry_after_seconds": 42
                }
              },
              "request_id": "..."
            }
          }
        }
      },
      "InternalError": {
        "description": "INTERNAL_ERROR: onverwachte serverfout. Probeer het opnieuw met exponential backoff.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "AuthUnavailable": {
        "description": "SERVICE_UNAVAILABLE: de gateway kon de referentie niet controleren (tijdelijke storing in de backend). Niet gecachet: probeer het opnieuw met backoff.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "code": "SERVICE_UNAVAILABLE",
                "message": "Authentication is temporarily unavailable. Please retry."
              },
              "request_id": "..."
            }
          }
        }
      },
      "EidasOrAuthUnavailable": {
        "description": "EIDAS_UNAVAILABLE: include_eidas is aangevraagd, maar gekwalificeerde tijdstempeling is in deze omgeving niet geconfigureerd; probeer het opnieuw zonder include_eidas. SERVICE_UNAVAILABLE: de gateway kon de referentie niet controleren (tijdelijke backendstoring, niet gecachet); probeer het opnieuw met backoff.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "401, twee codes. MISSING_API_KEY: de Authorization-header ontbreekt of is onjuist opgemaakt (`WWW-Authenticate: Bearer realm=\"ProofSnap API\"`). INVALID_API_KEY: er is een referentie meegestuurd, maar die is niet geldig: een onbekende, ingetrokken of verlopen API-sleutel, een account zonder API-toegang, of een ongeldig of ingetrokken sessietoken (`WWW-Authenticate: Bearer realm=\"ProofSnap API\", error=\"invalid_token\"`). Probeer het niet opnieuw met dezelfde referentie. 403 betekent dat de referentie geldig is, maar dit niet mag (FORBIDDEN, abonnementsbeperkingen).",
        "headers": {
          "WWW-Authenticate": {
            "$ref": "#/components/headers/WWWAuthenticate"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "missing": {
                "value": {
                  "error": {
                    "code": "MISSING_API_KEY",
                    "message": "Missing or malformed Authorization header. Send Authorization: Bearer <your API key>."
                  },
                  "request_id": "..."
                }
              },
              "invalid": {
                "value": {
                  "error": {
                    "code": "INVALID_API_KEY",
                    "message": "The API key is invalid, revoked or expired, or the account has no API access."
                  },
                  "request_id": "..."
                }
              }
            }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "description": "Elke fout heeft deze vorm. Foutantwoorden bevatten de header `X-Request-ID` met dezelfde waarde als `request_id`.\n\nVerzoekgrootte: een verzoekbody mag maximaal 6 MB groot zijn (de payloadlimiet van Lambda; API Gateway zelf stopt bij 10 MB). Een grotere body wordt geweigerd voordat deze de API-code bereikt, dus dat antwoord heeft niet deze vorm.\n\n| HTTP | code | Betekenis |\n| --- | --- | --- |\n| 400 | `MALFORMED_REQUEST` | De body is geen geldige JSON (message \"Request body is not valid JSON\"), of het verzoek is ongeldig: wanneer `details.reason` aangeeft welk veld en waarom, herhaalt `message` die reden, anders is het \"The request is invalid.\" |\n| 400 | `MISSING_FIELD` | Een verplicht veld ontbreekt. |\n| 400 | `INVALID_URL` | De URL is geen geldige http(s)-URL, is langer dan 2048 tekens, of een webhook_url is geen https. |\n| 400 | `INVALID_HASH` | certify-hash: een hash bestaat niet uit 64 hexadecimale tekens. |\n| 400 | `INVALID_TIMESTAMP_TYPE` | options.timestamp_type is niet \"bitcoin\". |\n| 400 | `INVALID_VIEWPORT` | options.viewport is niet \"desktop\" of \"mobile\". |\n| 400 | `METADATA_TOO_LARGE` | options.metadata heeft meer dan 10 sleutels, een waarde die geen string is, of een waarde van meer dan 500 tekens. |\n| 400 | `INVALID_EVIDENCE_PACK_OPTIONS` | options.evidence_pack is ongeldig. |\n| 400 | `INVALID_BATCH` | De batch-urls zijn geen array van 2 tot 50 geldige URL's. |\n| 400 | `REDACTION_NO_OPS` | De weglakking heeft geen bewerkingen, of geen ervan leverde een treffer op. |\n| 400 | `INVALID_CRON` | cron_expr kon niet worden geparsed. |\n| 400 | `INTERVAL_TOO_SHORT` | De frequentie van het schema ligt onder het minimum van het abonnement. |\n| 400 | `RATE_LIMIT_COST_TOO_HIGH` | Het verzoek heeft meer rate-limiteenheden nodig dan één venster toestaat; `details.max_per_window`, `details.window_seconds`, `details.requested`. Splits het op. |\n| 400 | `WEBHOOK_REQUIRES_API_KEY` | Er is een `webhook_url` verzonden vanuit een dashboardsessie. Webhooks worden ondertekend met het webhook-secret van een API-sleutel, dus daarvoor is een API-sleutel nodig. |\n| 401 | `MISSING_API_KEY` | De Authorization-header ontbreekt of is onjuist opgemaakt. `WWW-Authenticate: Bearer realm=\"ProofSnap API\"`. |\n| 401 | `INVALID_API_KEY` | Sleutel onbekend, ingetrokken of verlopen, geen API-toegang, of sessietoken ongeldig of ingetrokken. `WWW-Authenticate: Bearer realm=\"ProofSnap API\", error=\"invalid_token\"`. |\n| 402 | `QUOTA_EXCEEDED` | Maandelijks maximum of sandboxtegoed bereikt (ook in de `error` van een certificering op de achtergrond die om die reden is geweigerd). |\n| 402 | `PAYMENT_REQUIRED` | Het live abonnement is achterstallig of onbetaald (ook in de `error` van een geplande vastlegging die om die reden is overgeslagen). |\n| 403 | `FORBIDDEN` | De resource is van iemand anders, of de actie is alleen voor de eigenaar. Message: \"You do not have access to this resource.\" (details.reason geeft waar nuttig meer informatie). |\n| 403 | `EMAIL_NOT_VERIFIED` | Een dashboardsessie waarvan het e-mailadres niet is bevestigd, probeerde iets aan te maken dat blijvend of betaald is of dat mensen uitnodigt (vastleggingen, batches, vergelijkingen, weglakkingen, API-sleutels, webhook-secrets, checkout, abonnementswijzigingen, licentiewijzigingen, certify-hash, schema's, bedrijfsuitnodigingen, zaken en elke wijziging daarvan, leden van zaken, aan zaken toegewezen vastleggingen, deellinks en de intrekking ervan, verzegelingen, exports, legal holds). Lezen blijft mogelijk. Message: \"Confirm your email address first. Use the link we sent you, or send a new one from the dashboard.\" Een API-sleutel wordt op dezelfde manier geweigerd wanneer het e-mailadres van het bijbehorende account niet is bevestigd. |\n| 403 | `EIDAS_REQUIRES_LIVE_KEY` | include_eidas verzonden met een testsleutel (een ps_live_-sleutel van een account zonder gekoppeld factureringsaccount telt als testsleutel), of voor een vergelijking of weglakking van een testvastlegging (`details.reason`). Een live-sleutel of sessie die als test draait omdat er geen factureringsaccount is gekoppeld, krijgt `details.reason` \"no_billing_account\". |\n| 403 | `MATTERS_NOT_ENABLED` | Zaken vereisen een Legal-abonnement. |\n| 403 | `SCHEDULE_LIMIT_REACHED` | De limiet voor actieve schema's van het abonnement is bereikt (of het abonnement heeft geen schema's). |\n| 403 | `ON_CHANGE_REQUIRES_PLAN` | Het abonnement staat on_change-monitoring niet toe. |\n| 403 | `PLAN_ARCHIVE_ONLY` | Met het abonnement Archive only blijft uw bewijs leesbaar, downloadbaar en verifieerbaar; het maakt geen vastleggingen, schema's, certificeringen, vergelijkingen of weglakkingen aan. |\n| 403 | `PLAN_NOT_AVAILABLE` | Dit account kan het abonnement niet kiezen: Archive only wordt gekocht nadat een betaald API-abonnement is beëindigd (zolang het bewijs nog wordt bewaard) of bereikt door van abonnement te wisselen vanuit developer, startup, growth, website_archive of legal_solo; het bedrijfsarchief alleen door de eigenaar van een bedrijf waarvan het abonnement is beëindigd, binnen het downloadvenster ervan. `details.reason` geeft de reden. |\n| 403 | `PLAN_API_READ_ONLY` | Website Archive-abonnement: de API-sleutel is alleen-lezen (weergeven, downloaden, verifiëren, webhooks). Maak vastleggingen, schema's en certificeringen aan in het dashboard, of gebruik een API-abonnement. |\n| 403 | `LEGAL_HOLD_OWNER_ONLY` | Alleen de eigenaar van het bedrijf heft de legal hold van een bedrijfsvastlegging op (een licentiehouder mag deze wel instellen). |\n| 403 | `LEGAL_HOLD_NOT_AVAILABLE` | Voor het instellen van een legal hold zijn een betaald abonnement (`details.reason` \"plan\": de gratis sandbox, of een account waarvan het abonnement is beëindigd) en een live-vastlegging (`details.reason` \"test_capture\": een testvastlegging met watermerk) nodig. Het opheffen van een legal hold wordt hierom nooit geweigerd. |\n| 403 | `IMPORT_NOT_ALLOWED` | Pakketimport vereist een Legal-abonnement (`details.reason` \"plan\") en een live API-sleutel of een dashboardsessie met gekoppelde facturering (`details.reason` \"live_credentials_required\"). |\n| 404 | `CAPTURE_NOT_FOUND` | De vastlegging bestaat niet, is verwijderd, of de ID is onjuist opgemaakt. |\n| 404 | `MATTER_NOT_FOUND` | De zaak bestaat niet of u bent geen lid. |\n| 404 | `IMPORT_NOT_FOUND` | De import bestaat niet, is van een ander account, of de ID is onjuist opgemaakt. |\n| 404 | `WEBHOOK_SUBSCRIPTION_NOT_FOUND` | Het webhookabonnement bestaat niet, is verwijderd, of is van een ander account. |\n| 404 | `SCHEDULE_NOT_FOUND` | Het schema bestaat niet of is van een ander account. |\n| 404 | `NOT_FOUND` | Een andere resource is niet gevonden (hashformaat in het uitgifteregister, deeltoken, exporttaak, lid). |\n| 404 | `EXPORT_JOB_NOT_FOUND` | Een exporttaak voor vastleggingen bestaat niet, is van een ander account, is verlopen, of is opgevraagd met een testsleutel. |\n| 405 | `METHOD_NOT_ALLOWED` | De HTTP-methode is niet toegestaan voor deze route. |\n| 409 | `CAPTURE_PROCESSING` | De vastlegging wordt nog verwerkt en kan nog niet worden verwijderd (en de legal hold ervan kan nog niet worden gewijzigd). |\n| 409 | `CAPTURE_LEGAL_HOLD` | De vastlegging staat onder legal hold en kan niet worden verwijderd. |\n| 409 | `CAPTURE_IN_TRASH` | Legal hold: de vastlegging staat in de prullenbak; zet haar eerst terug. |\n| 409 | `CAPTURE_HOLD_GRACE` | Definitieve verwijdering binnen de respijtperiode nadat de legal hold van de vastlegging is opgeheven; `details.until`. Verplaatsen naar de prullenbak is toegestaan. |\n| 409 | `LEGAL_HOLD_MATTER` | De legal hold van de vastlegging is ingesteld via de legal hold van haar zaak, die nog actief is: hef deze op bij de zaak (`details.matter_id` voor een lezer van de zaak). |\n| 409 | `LEGAL_HOLD_SOURCE` | De legal hold van een weglakking of vergelijking volgt een vastlegging waarvan deze is gemaakt (bij een vergelijking een van beide vergeleken vastleggingen), die nog onder legal hold staat (ingesteld op die vastlegging of via haar zaak): het pakket blijft onder legal hold totdat die legal hold wordt opgeheven (`details.source_capture_id`). |\n| 409 | `COMPANY_READ_ONLY` | Het abonnement van het bedrijf is beëindigd (of het wordt bewaard als bedrijfsarchief): de zaken ervan kunnen alleen nog worden geopend en geëxporteerd en hun deellinks ingetrokken, verder niets, en er wordt niets nieuws afgeleid van de vastleggingen ervan. |\n| 409 | `WEBHOOK_SUBSCRIPTION_LIMIT` | De limiet voor actieve webhookabonnementen van het abonnement is bereikt (25 bij API- en Legal-abonnementen, 10 bij Website Archive, 0 bij Archive only en sandbox); `details.limit`, `details.active`. |\n| 409 | `WEBHOOK_SECRET_ROTATION_PENDING` | Het vorige webhook-secret bevindt zich nog in de overlapperiode van 24 uur; `details.previous_secret_expires_at`. |\n| 409 | `IDEMPOTENCY_IN_PROGRESS` | Het eerste verzoek met deze Idempotency-Key wordt nog geaccepteerd. |\n| 409 | `COMPARE_NOT_READY` | Beide vastleggingen moeten voltooid zijn voordat ze kunnen worden vergeleken. |\n| 409 | `REDACTION_NOT_READY` | De bronvastlegging moet voltooid zijn voordat ze kan worden weggelakt. |\n| 409 | `MATTER_SEALED` | De zaak (of de zaak van de vastlegging) is verzegeld en kan niet worden gewijzigd. |\n| 409 | `MATTER_SEALING` | Er loopt een verzegeling van de zaak (maximaal 60 s): intussen geen toewijzing van vastleggingen, naamswijziging, wijziging van leden, verplaatsing van een gekoppelde vastlegging naar de prullenbak of tweede verzegeling. `Retry-After`. |\n| 409 | `MATTER_ARCHIVED` | De zaak is gearchiveerd: geen nieuwe vastleggingen, naamswijzigingen, nieuwe leden, delen of export; heropen haar eerst (PATCH status \"open\"). Een vastlegging of lid verwijderen werkt nog wel. |\n| 409 | `MATTER_CHANGED` | Het bewijsketenlogboek is tijdens het verzegelen gewijzigd; er is niets verzegeld, probeer het opnieuw. |\n| 409 | `MATTER_LINKED` | Verwijderen van een vastlegging: de vastlegging is een bewijsstuk van een zaak waartoe de aanroeper niet meer behoort. De eigenaar van de zaak verwijdert haar eerst uit de zaak. |\n| 409 | `MATTER_HAS_TRASHED_CAPTURES` | Verzegeling van een zaak: een vastlegging van de zaak staat in de prullenbak. Zet haar terug of verwijder haar uit de zaak en verzegel daarna opnieuw. |\n| 409 | `MEMBER_HAS_INDIVIDUAL_SUBSCRIPTION` | Bedrijfsuitnodiging: het eigen API-abonnement van de genodigde wordt nog verlengd. De genodigde zegt het op, daarna nodigt de eigenaar hem of haar opnieuw uit. |\n| 409 | `IMPORT_LIMIT` | Er zijn al 3 imports bezig (uploaden of verifiëren); `details.limit`, `details.in_progress`. |\n| 409 | `IMPORT_STATE` | De import heeft geen status die dit toestaat (complete of cancel van een import die niet meer wordt geüpload); `details.status`. |\n| 409 | `EXPORT_JOB_RUNNING` | Er wordt nog een export van uw vastleggingen voorbereid; `details.job_id` en `details.poll_path` geven aan welke. Start een nieuwe zodra deze is afgerond. |\n| 413 | `IMPORT_TOO_LARGE` | Het pakket is groter dan 2 GiB; `details.max_bytes`. |\n| 413 | `VERIFY_PACKAGE_TOO_LARGE` | POST /captures/{capture_id}/verify: het opgeslagen pakket is groter dan 256 MiB; verifieer het offline met de meegeleverde scripts. `details.size_bytes`, `details.limit_bytes`. |\n| 422 | `IDEMPOTENCY_KEY_REUSED` | Idempotency-Key hergebruikt met een andere body of een andere sleutelomgeving (live of test). |\n| 422 | `SSRF_BLOCKED` | De URL verwijst naar een privé-, loopback-, link-local- of anderszins geblokkeerd adres. |\n| 422 | `TARGET_UNREACHABLE` | De hostnaam van de URL heeft geen DNS-records of kon niet worden opgelost. |\n| 422 | `REDACTION_SCREENSHOT_UNREADABLE` | De screenshot kon niet worden gedecodeerd voor het weglakken met vakken. |\n| 422 | `REDACTION_LEAK_DETECTED` | Een weggelakte term is buiten de URL van de vastlegging na de weglakking blijven staan; er is niets uitgegeven. `details.files`, `details.rule_indexes`, `details.metadata_fields`, `details.in_capture_url`. |\n| 422 | `REDACTION_SOURCE_UNSUPPORTED` | Alleen vastleggingen van webpagina's kunnen worden weggelakt. `details.source` of `details.manifest_type`. |\n| 422 | `REDACTION_SOURCE_INTEGRITY_FAILED` | Een bestand van de bronvastlegging komt niet overeen met de SHA-256 die het ondertekende manifest ervan vermeldt, of ontbreekt; er is niets uitgegeven. `details.mismatched`, `details.missing`. |\n| 422 | `COMPARE_PROFILE_MISMATCH` | POST /compare: de twee vastleggingen zijn gemaakt met een ander apparaatprofiel of andere vastleggingsinstellingen; `details.differing`, `details.before`, `details.after`. Stuur allow_profile_mismatch: true mee om toch te vergelijken (het tekst- en afbeeldingsresultaat is dan undetermined). |\n| 422 | `COMPARE_URL_MISMATCH` | POST /compare: de twee vastleggingen zijn van verschillende pagina's; `details.before`, `details.after` (url en final_url als origin en pad). Stuur allow_url_mismatch: true mee om toch te vergelijken. |\n| 422 | `RULE902_NOT_AVAILABLE_FOR_MISMATCH` | POST /compare: include_rule_902 voor vastleggingen die verschillend zijn weergegeven (allow_profile_mismatch); `details.differing`. |\n| 422 | `COMPARE_SOURCE_INTEGRITY_FAILED` | Een bestand van een bronvastlegging van POST /compare komt niet overeen met de SHA-256 die het ondertekende manifest ervan vermeldt, of ontbreekt; er is niets vergeleken of in rekening gebracht. `details.capture_id`, `details.mismatched`, `details.missing`, `details.sources[]`. |\n| 422 | `REDACTION_PATTERN_TOO_SLOW` | Een regex-patroon van text_redactions liep op deze pagina te lang (catastrophic backtracking); er is niets uitgegeven of in rekening gebracht. `details.rule_index`. Vereenvoudig het patroon. |\n| 422 | `IMPORT_SIZE_MISMATCH` | Het geüploade pakket heeft niet de aangekondigde size_bytes; de upload is verwijderd. `details.expected`, `details.actual`. |\n| 422 | `IMPORT_NOT_SUPPORTED` | Vergelijken of weglakken van een geïmporteerd bewijspakket. |\n| 429 | `RATE_LIMITED` | Te veel verzoeken in het huidige venster, of een daglimiet is bereikt voor het aanmaken van zaken (200), verzegelen (20), exporteren (50), uitnodigen van leden (50) of pakketimports (50, `import_create`) (`details.daily_limit`, `details.action`). `Retry-After` geeft aan wanneer u het opnieuw kunt proberen. |\n| 429 | `CONCURRENT_CAPTURE_LIMIT` | Er lopen te veel vastleggingen van het account tegelijk (developer 10, startup 25, growth 100, legal_solo en legal_seats 25, custom 200, website_archive 25, sandbox 5). Een batch start zoveel URL's als er passen en vermeldt de rest in `not_admitted`; zonder vrije plek wordt het hele verzoek geweigerd. `details.limit`, `details.in_flight`, `details.requested`, `Retry-After`. |\n| 429 | `TEST_MODE_DAILY_LIMIT` | Gebruik in testmodus (een testsleutel of een sessie zonder gekoppelde facturering; met watermerk, niet gefactureerd) is beperkt tot 200 eenheden per account en UTC-dag (een batch telt zijn URL's). `details.used`, `details.limit`, `details.reset_at`, `Retry-After`. |\n| 429 | `IMPORT_DAILY_BYTES_LIMIT` | Pakketimport: het importvolume van het account voor de UTC-dag (5 GiB aan aangekondigde groottes) is bereikt. `details.period`, `details.limit_bytes`, `details.used_bytes`, `details.requested_bytes`, `details.reset_at`. |\n| 429 | `IMPORT_MONTHLY_BYTES_LIMIT` | Pakketimport: het importvolume van het account voor de UTC-maand (20 GiB) is bereikt. Dezelfde details. |\n| 500 | `INTERNAL_ERROR` | Onverwachte fout. Probeer het opnieuw met backoff. |\n| 500 | `CAPTURE_FAILED` | De vastlegging kon niet worden gestart, of (in de `error` van een mislukte vastlegging) niet worden voltooid. |\n| 500 | `CAPTURE_TIME_BUDGET_EXCEEDED` | In de `error` van een mislukte vastlegging, of bij een synchrone weglakking: de uitvoering heeft haar tijdsbudget overschreden en er is geen pakket uitgegeven of in rekening gebracht. |\n| 500 | `CAPTURE_SCREENSHOT_TIMEOUT` | In de `error` van een mislukte vastlegging: de browser heeft de screenshot niet volledig weergegeven: elke manier om deze te maken (in één keer, kleinere stroken, de eigen screenshot van de browser) liep in een timeout, net als de ene nieuwe poging in een opnieuw gestarte browser wanneer het tijdsbudget daar ruimte voor liet. Er is niets in rekening gebracht; probeer het opnieuw. |\n| 500 | `CERTIFY_FAILED` | In de `error` van een mislukte certificering: de certificering kon niet worden voltooid. |\n| 500 | `REDACTION_FAILED` | In de `error` van een mislukte weglakking: de weglakking kon niet worden voltooid. Een weglakking die om een bekende reden is geweigerd, bevat in plaats daarvan de code van die reden (bijvoorbeeld REDACTION_LEAK_DETECTED). |\n| 500 | `TIMESTAMP_SERVICE_UNAVAILABLE` | Verzegeling van een zaak: de OpenTimestamps-verankering is mislukt; er is niets gewijzigd, probeer het opnieuw. |\n| 500 | `CONFIGURATION_ERROR` | Een stap aan de serverzijde is mislukt of niet geconfigureerd (`details.reason`). |\n| 503 | `SERVICE_UNAVAILABLE` | Authenticatie is tijdelijk niet beschikbaar (de referentie kon niet worden gecontroleerd). Niet gecachet; probeer het opnieuw. |\n| 503 | `EIDAS_UNAVAILABLE` | include_eidas is aangevraagd, maar eIDAS-tijdstempeling is niet geconfigureerd. |\n| 503 | `EIDAS_SEAL_RETRY` | Verzegeling van een zaak: de eIDAS-tijdstempel is niet op tijd binnengekomen; er is niets verzegeld. Probeer het opnieuw na `Retry-After`; de nieuwe poging hergebruikt de al uitgegeven tijdstempel. |\n| 503 | `LIMIT_CHECK_UNAVAILABLE` | Verzegeling van een zaak: de daglimiet ervan kon niet worden gecontroleerd; er is niets gedaan. Probeer het opnieuw. |\n| 504 | `UPSTREAM_TIMEOUT` | Een synchrone verwerkingsstap (certify-hash, export van een zaak) reageerde niet op tijd. Mogelijk wordt deze alsnog voltooid: poll wat `details` noemt (`certify_id`, `poll_path`), of probeer het opnieuw. |\n| 404 | `ORG_INVITE_NOT_FOUND` | Een bedrijfsuitnodiging die onbekend is, aan iemand anders is gericht, niet meer openstaat, of waarvan het bedrijf niet meer actief is (details.reason `company_inactive`). |\n| 409 | `ORG_INVITE_NO_SEATS` | Een bedrijfsuitnodiging accepteren: alle licenties van het bedrijf zijn in gebruik; de uitnodiging blijft openstaan. |\n| 409 | `ORG_INVITE_IN_OTHER_ORG` | Een bedrijfsuitnodiging accepteren: de aanroeper heeft een licentie van een ander bedrijf; de uitnodiging blijft openstaan. |\n| 409 | `ORG_INVITE_HAS_SUBSCRIPTION` | Een bedrijfsuitnodiging accepteren: het eigen API-abonnement van de aanroeper wordt nog verlengd; de uitnodiging blijft openstaan. |\n| 409 | `ORG_OWNER_CANNOT_LEAVE` | De eigenaar van het bedrijf probeerde het eigen bedrijf te verlaten. |\n| 410 | `ORG_INVITE_EXPIRED` | Een bedrijfsuitnodiging accepteren die ouder is dan 7 dagen. |",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "description": "Machineleesbare foutcode (UPPER_SNAKE_CASE)."
              },
              "message": {
                "type": "string",
                "description": "Voor mensen leesbaar bericht."
              },
              "details": {
                "type": "object",
                "additionalProperties": true,
                "description": "Optionele extra context, bijvoorbeeld `reason`, `field` of `retry_after_seconds`."
              }
            },
            "required": [
              "code",
              "message"
            ]
          },
          "request_id": {
            "type": "string",
            "description": "Verzoek-ID."
          }
        },
        "example": {
          "error": {
            "code": "INVALID_URL",
            "message": "URL is not a valid http(s) URL",
            "details": {
              "reason": "Only http and https schemes are allowed"
            }
          },
          "request_id": "..."
        }
      },
      "CaptureOptions": {
        "type": "object",
        "description": "Vastleggingsopties. Elk veld is optioneel.",
        "properties": {
          "scroll_full_page": {
            "type": "boolean",
            "description": "Legt de hele scrollbare pagina vast. false legt alleen de viewport vast.",
            "default": true
          },
          "expand_collapsed": {
            "type": "boolean",
            "description": "Open ingeklapte secties vóór de screenshot: gesloten <details>-elementen, accordeon- en uitklapknoppen (aria-expanded met aria-controls) en tekstbedieningselementen als \"see more\" / \"show replies\". Beveiligd: niets binnen navigatie, headers, menu's of dialoogvensters, geen links naar een andere URL, geen formulierverzending, geen labels die de status wijzigen (like, follow, buy, delete, ...), elk bedieningselement hoogstens één keer, en een bedieningselement dat de pagina probeert te verlaten, stopt het uitklappen, waarna de oorspronkelijke URL opnieuw wordt geladen. De uitkomst wordt per bedieningselement vastgelegd in forensic_log.json (page_modifications.expand_collapsed: gevonden, geopend, gesloten gelaten, redenen voor overslaan, gestarte verzoeken, paginahoogte ervoor en erna) met een samenvatting in metadata.json (expand_collapsed: requested, opened_total, left_closed_total); evidence.pdf en de certificaten vermelden alleen wat daadwerkelijk is geopend.",
            "default": true
          },
          "viewport": {
            "type": "string",
            "description": "desktop is 1920x1080 bij pixelratio 1; mobile is 390x844 bij pixelratio 3 met een user agent van mobiele Safari. Andere waarden geven 400 INVALID_VIEWPORT.",
            "enum": [
              "desktop",
              "mobile"
            ],
            "default": "desktop"
          },
          "viewport_width": {
            "type": "number",
            "minimum": 320,
            "maximum": 3840,
            "description": "Aangepaste viewportbreedte in CSS-pixels. Moet samen met viewport_height worden verzonden, anders 400 MALFORMED_REQUEST."
          },
          "viewport_height": {
            "type": "number",
            "minimum": 240,
            "maximum": 4320,
            "description": "Aangepaste viewporthoogte in CSS-pixels. Moet samen met viewport_width worden verzonden."
          },
          "device_scale_factor": {
            "type": "number",
            "minimum": 1,
            "maximum": 3,
            "description": "Apparaatpixelverhouding van de screenshot."
          },
          "wait_for_selector": {
            "type": "string",
            "description": "Wacht maximaal 10 seconden op deze CSS-selector voordat de vastlegging begint. Verschijnt die niet, dan gaat de vastlegging zonder fout verder. Vastgelegd in forensic_log.json page_modifications.",
            "maxLength": 200
          },
          "wait_ms": {
            "type": "number",
            "minimum": 0,
            "maximum": 15000,
            "description": "Extra wachttijd in milliseconden nadat de pagina is geladen."
          },
          "screenshot_format": {
            "type": "string",
            "description": "jpeg levert screenshot.jpeg; png levert een verliesvrije screenshot.png. C2PA Content Credentials worden alleen aan JPEG-screenshots toegevoegd.",
            "enum": [
              "jpeg",
              "png"
            ],
            "default": "jpeg"
          },
          "url_stamp": {
            "type": "boolean",
            "description": "Voegt boven de paginaweergave een kopstrook toe met de vastgelegde URL, het UTC-tijdstip van de vastlegging (het moment waarop de browser de screenshot maakte; bij een lange pagina die in stroken is vastgelegd, het moment waarop de laatste strook werd gemaakt) en de evidence ID, aangeduid als software-annotatie. De strook wordt boven op de afbeelding geplaatst, zodat er geen pagina-pixel wordt bedekt. chain_of_custody.json legt screenshot_url_header.raw_screenshot_sha256 vast met raw_screenshot_source: voor een pagina die in één keer is vastgelegd (browser_image, browser_png, browser_jpeg_joined, browser_jpeg_reencoded) de SHA-256 van de paginaweergave zoals de browser die maakte, vóór de strook; voor een lange pagina die in stroken is vastgelegd en samengevoegd (joined_bands, joined_bands_lossless, joined_bands_reencoded) de SHA-256 van de samengevoegde afbeelding zoals ProofSnap die met de strook wegschreef, vóór eventuele C2PA Content Credentials. Wanneer de strook niet kan worden toegevoegd (de afbeelding zou er groter door worden dan de 65,535 px die een JPEG kan bevatten, het tekenen is mislukt, of, alleen bij het alternatieve screenshotpad, de paginaweergave is groter dan 32,000,000 pixels), wordt de screenshot zonder strook geleverd: metadata.json legt dit vast in screenshot.url_stamp_skipped en chain_of_custody.json in screenshot_url_header (applied false, met de reden).",
            "default": false
          },
          "network_log": {
            "type": "boolean",
            "description": "Voeg network.har (HAR met verzoek- en antwoordheaders en timing, zonder bodies) en network.original.har toe.",
            "default": true
          },
          "warc": {
            "type": "boolean",
            "description": "Voeg capture.warc (ISO 28500) toe met de HTTP-transacties van het laden van de pagina. De bodies van de antwoorden worden binnen een tijdslimiet gelezen (maximaal 30 s, en alleen zolang het optionele deel van het tijdsbudget van de vastlegging het toelaat, na de tijd die is gereserveerd voor eIDAS, C2PA en de afsluitende klokmeting); een body die door die limiet is afgebroken, wordt leeg opgeslagen met WARC-Truncated: time, een antwoord dat niet is bereikt, wordt weggelaten, en warc_captured in chain_of_custody.json (en warc in forensic_log.json) registreert truncated, truncation_reason, response_pairs, records_total en de aantallen.",
            "default": false
          },
          "external_archive": {
            "type": "boolean",
            "description": "Vraagt de Save Page Now-dienst van het Internet Archive om de URL te archiveren (het Internet Archive bewaart die kopie openbaar). Het resultaat wordt ingedeeld naar wiens verzoek de snapshot opleverde en het tijdstip ervan ten opzichte van het verzoek: created (gemaakt voor dit verzoek), recent_existing (het Internet Archive antwoordde met een eerdere snapshot), created_after_request (er bestaat een nieuwere snapshot, maar het verzoek werd niet bevestigd), existing (er bestaat alleen een oudere snapshot), pending, rate_limited, failed (het Internet Archive weigerde het verzoek), unconfirmed (geen antwoord bevestigde of weigerde het en er is geen snapshot bekend), unavailable, skipped, skipped_sensitive of skipped_query. URL's die inloggegevens lijken te bevatten of tokenachtige parameters, parameternamen of padsegmenten hebben, worden nooit verstuurd. Een URL met een querystring wordt alleen verstuurd wanneer deze optie expliciet op true staat; met de standaardwaarde wordt hij overgeslagen (skipped_query). Het resultaat wordt vastgelegd in forensic_log.json (external_archive, met snapshot_offset_seconds ten opzichte van de vastlegging) en chain_of_custody.json, en door GET /captures/{capture_id} teruggegeven als external_archive. Zet op false voor privé-URL's. Het verzoek wordt verstuurd zodra de pagina is geladen, parallel aan de rest van de vastlegging. Een mislukking laat de vastlegging niet mislukken.",
            "default": true
          },
          "dismiss_consent": {
            "type": "boolean",
            "description": "Zet vóór het laden van de pagina toestemmingscookies voor alles accepteren en probeer cookiebanners te sluiten. Dit wijzigt de pagina en staat daarom standaard uit; wat er is gedaan, wordt vastgelegd in forensic_log.json consent_dismissal.",
            "default": false
          },
          "dismiss_popups": {
            "type": "boolean",
            "description": "Sluit pop-ups voor nieuwsbrieven, promoties en vergelijkbare overlays via hun eigen sluitknop. Het aantal gesloten pop-ups en de gebruikte knoppen worden geregistreerd in forensic_log.json page_modifications. Elke gesloten pop-up wordt geregistreerd in forensic_log.json page_modifications (popups_dismissed, popups_dismissed_controls); evidence.pdf vermeldt hoeveel er zijn gesloten.",
            "default": false
          },
          "dark_mode": {
            "type": "boolean",
            "description": "Weergeven met het donkere kleurenschema. Verandert het uiterlijk van de pagina; vastgelegd in page_modifications van forensic_log.json. Vermeld in evidence.pdf en in page_modifications van forensic_log.json.",
            "default": false
          },
          "block_ads": {
            "type": "boolean",
            "description": "Blokkeert verzoeken naar bekende advertentie- en trackerhosts. Laat inhoud weg; vastgelegd in forensic_log.json page_modifications.",
            "default": false
          },
          "block_resources": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "image",
                "font",
                "media",
                "stylesheet"
              ]
            },
            "description": "Te blokkeren resourcetypen. Laat inhoud weg; vastgelegd in page_modifications van forensic_log.json."
          },
          "inject_css": {
            "type": "string",
            "description": "CSS die vóór de vastlegging aan de pagina wordt toegevoegd. Wijzigt de pagina; letterlijk opgeslagen in forensic_log.json page_modifications. Altijd letterlijk opgeslagen in forensic_log.json page_modifications (injected_css) met injected_css_status `applied` of `failed` en injected_css_error. evidence.pdf vermeldt dat er aangepaste CSS is geïnjecteerd; de code zelf wordt niet afgedrukt.",
            "maxLength": 10000
          },
          "inject_js": {
            "type": "string",
            "description": "JavaScript dat vóór de vastlegging in de pagina wordt uitgevoerd. Wijzigt de pagina. Wordt altijd letterlijk opgeslagen in forensic_log.json page_modifications (injected_js) met injected_js_status en injected_js_error: `applied` (het is tot het einde uitgevoerd), `invalid` (het compileerde niet, bijvoorbeeld door een syntaxfout, dus het is nooit uitgevoerd en heeft de pagina niet gewijzigd), `failed` (het gaf tijdens de uitvoering een fout, dus het kan de pagina hebben gewijzigd voordat het stopte) of `timed_out` (de vastlegging wachtte er na 5 seconden niet langer op; het script kan zijn doorgegaan en de pagina hebben gewijzigd). evidence.pdf vermeldt de uitkomst en verwijst naar forensic_log.json, en het herkomstcertificaat vermeldt geïnjecteerde code bij de paginawijzigingen, tenzij deze `invalid` was; geen van beide drukt de code af.",
            "maxLength": 10000
          },
          "locale": {
            "type": "string",
            "description": "Te emuleren browserlocale, bijvoorbeeld de of de-DE (stelt de browsertaal en Accept-Language in). Alleen emulatie: er wordt geen proxy gebruikt, dus inhoud die afhangt van het IP-adres van de bezoeker weerspiegelt nog steeds de regio van de vastleggingsserver. Vastgelegd in metadata.json geo.",
            "pattern": "^[a-z]{2}(-[A-Z]{2})?$"
          },
          "timezone": {
            "type": "string",
            "description": "Te emuleren tijdzone van de browser (IANA-naam, bijvoorbeeld Europe/Berlin). Alleen emulatie."
          },
          "language": {
            "type": "string",
            "description": "Taal van de leesbare PDF's. Een van: en, de, it, fr, es, pt_BR, cs, el, hi, hu, id, ja, ko, nl, pl, ro, ru, sv, tr, vi, zh_CN, zh_TW, ar, he, da, fi, nb, sk, uk. Hoofdletters, koppeltekens en regio worden getolereerd (\"DE\", \"fr-FR\" en \"PT-BR\" worden geaccepteerd); alleen \"pt\" of \"zh\" wordt geweigerd. Een niet-ondersteunde waarde geeft 400 MALFORMED_REQUEST.",
            "default": "en",
            "example": "de"
          },
          "timestamp_type": {
            "type": "string",
            "description": "Alleen \"bitcoin\" wordt geaccepteerd (de OpenTimestamps-verankering wordt altijd geprobeerd). Elke andere waarde geeft 400 INVALID_TIMESTAMP_TYPE. Gebruik include_eidas voor een gekwalificeerde tijdstempel.",
            "enum": [
              "bitcoin"
            ],
            "default": "bitcoin"
          },
          "include_eidas": {
            "type": "boolean",
            "description": "Vraagt ook een gekwalificeerde eIDAS-tijdstempel (RFC 3161) aan over de SHA-256 van manifest.json, geleverd als manifest.json.tsr (plus eidas_validation.json wanneer er validatiegegevens worden teruggegeven). Alleen live sleutels: een testsleutel geeft 403 EIDAS_REQUIRES_LIVE_KEY. Als voor de omgeving geen eIDAS-dienst is geconfigureerd, wordt het verzoek geweigerd met 503 EIDAS_UNAVAILABLE voordat er iets in rekening wordt gebracht. Voordat een token wordt gebruikt, controleert ProofSnap het (handtekening, de hash die het bevat, de certificaatketen naar de root CA Disig QCA4, de status van het TSA-certificaat) en legt die controle vast in eidas_validation.json (verification). Als de tijdstempelaanvraag tijdens de vastlegging mislukt, of het token die controle niet doorstaat, wordt het pakket zonder tijdstempel geleverd met alleen de OpenTimestamps-verankering, wordt de tijdstempel niet in rekening gebracht en meldt timestamps.eidas (vastleggingsrij, GET /captures/{capture_id}, completed-webhook) status not_obtained met de reden (token_refused, unavailable of time_budget). Vanaf de screenshot laten de optionele stappen van de vastlegging (de WARC-bodies, de archief- en WHOIS-antwoorden, C2PA-ondertekening, de tweede tijdmeting) tijd over voor de tijdstempel en worden ze als eerste ingekort. Wordt per tijdstempel in rekening gebracht.",
            "default": false
          },
          "include_rule_902": {
            "type": "boolean",
            "description": "Voegt rule_902_certification.pdf toe: een niet-ondertekende certificeringstemplate volgens [FRE 902(13)/(14)](https://www.govinfo.gov/content/pkg/USCODE-2023-title28/html/USCODE-2023-title28-app-federalru-dup2-rule902.htm) en [28 U.S.C. 1746](https://www.govinfo.gov/content/pkg/USCODE-2023-title28/html/USCODE-2023-title28-partV-chap115-sec1746.htm) die een persoon invult en ondertekent. De SHA-256 ervan staat in manifest.json. Het bestand wordt ook toegevoegd wanneer evidence_pack.custodian een niet-lege waarde heeft. Als het genereren mislukt, wordt de vastlegging zonder dit bestand voltooid. Momenteel uitgeschakeld: de certificeringstemplate is tijdelijk uitgezet, dus dit veld wordt geaccepteerd en genegeerd (er wordt geen rule_902_certification.pdf gegenereerd of in het pakket opgenomen) en het antwoord bevat de waarschuwing RULE902_DISABLED.",
            "default": false
          },
          "case_reference": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PlainText"
              }
            ],
            "description": "Uw label voor de zaak of het dossier. Witruimte wordt samengevoegd. Volgt de regel voor platte tekst (zie PlainText). Vastgelegd in metadata.json, forensic_log.json en chain_of_custody.json, en afgedrukt op evidence.pdf.",
            "maxLength": 200
          },
          "case_id": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PlainText"
              }
            ],
            "description": "De dossier-ID van uw systeem. Wordt vastgelegd zoals case_reference; volgt de regel voor platte tekst (zie PlainText).",
            "maxLength": 200
          },
          "case_note": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PlainText"
              }
            ],
            "description": "Notitie in vrije tekst. Volgt de regel voor platte tekst (zie PlainText). Vastgelegd in metadata.json, forensic_log.json en chain_of_custody.json (legal_context.note).",
            "maxLength": 500
          },
          "webhook_url": {
            "type": "string",
            "description": "https-URL die capture.completed of capture.failed ontvangt. Zie Webhooks. De URL moet https zijn en mag niet verwijzen naar een privé-, loopback-, link-local-, carrier-grade NAT- of cloudmetadata-adres (dezelfde regels als voor een vastleggings-URL; anders 422 SSRF_BLOCKED). Een lege waarde of een waarde met alleen witruimte betekent geen webhook. Vereist een API-sleutel: vanuit een dashboardsessie verzonden wordt het geweigerd met 400 WEBHOOK_REQUIRES_API_KEY, omdat webhooks worden ondertekend met het webhook-secret van de sleutel.",
            "format": "uri"
          },
          "metadata": {
            "type": "object",
            "additionalProperties": {
              "type": "string",
              "maxLength": 500
            },
            "maxProperties": 10,
            "description": "Maximaal 10 tekenreekstags (waarden van maximaal 500 tekens), die met de vastlegging worden teruggegeven. Overtredingen geven 400 METADATA_TOO_LARGE."
          },
          "evidence_pack": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean",
                "description": "false laat de certificeringspagina weg uit evidence.pdf.",
                "default": true
              },
              "jurisdiction": {
                "type": "string",
                "description": "Vastgelegd in de options van forensic_log.json. Heeft verder geen effect.",
                "enum": [
                  "US-Federal",
                  "EU-eIDAS"
                ],
                "default": "US-Federal"
              },
              "custodian": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Geaccepteerd; wordt niet afgedrukt in de template, waarvan de velden van de verklaarder leeg blijven voor de persoon die hem ondertekent.",
                    "maxLength": 200
                  },
                  "law_firm": {
                    "type": "string",
                    "description": "Geaccepteerd; wordt niet afgedrukt in de template, waarvan de velden van de verklaarder leeg blijven voor de persoon die hem ondertekent.",
                    "maxLength": 200
                  },
                  "role": {
                    "type": "string",
                    "description": "Geaccepteerd; wordt niet gebruikt in de template.",
                    "maxLength": 200
                  },
                  "case_caption": {
                    "type": "string",
                    "description": "Geaccepteerd; wordt niet gebruikt in de template.",
                    "maxLength": 200
                  }
                },
                "additionalProperties": false,
                "description": "Elke waarde volgt de regel voor platte tekst (zie PlainText); anders 400 INVALID_EVIDENCE_PACK_OPTIONS."
              }
            },
            "description": "Ongeldige waarden geven 400 INVALID_EVIDENCE_PACK_OPTIONS."
          }
        }
      },
      "CaptureRequest": {
        "type": "object",
        "required": [
          "url"
        ],
        "properties": {
          "url": {
            "type": "string",
            "description": "Vast te leggen pagina. Alleen http of https, maximaal 2048 tekens. Privé-, loopback-, link-local- en metadata-adressen worden geweigerd met 422 SSRF_BLOCKED, en een hostnaam zonder DNS-records geeft 422 TARGET_UNREACHABLE.",
            "format": "uri",
            "maxLength": 2048
          },
          "options": {
            "$ref": "#/components/schemas/CaptureOptions"
          },
          "private_note": {
            "$ref": "#/components/schemas/PrivateNote"
          }
        },
        "example": {
          "url": "https://example.com",
          "options": {
            "case_reference": "Smith v. Jones",
            "webhook_url": "https://your-app.example/webhooks/proofsnap"
          }
        }
      },
      "CaptureAccepted": {
        "type": "object",
        "properties": {
          "capture_id": {
            "type": "string",
            "example": "cap_a1B2c3D4e5F6g7H8"
          },
          "status": {
            "type": "string",
            "enum": [
              "processing"
            ]
          },
          "url": {
            "type": "string"
          },
          "poll_path": {
            "type": "string",
            "description": "Statuspad inclusief de /v1-stage, bijvoorbeeld /v1/captures/cap_..."
          },
          "message": {
            "type": "string"
          },
          "metadata": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "private_note": {
            "type": "string",
            "description": "De privénotitie zoals opgeslagen (genormaliseerd), alleen aanwezig wanneer er een is verzonden."
          },
          "warnings": {
            "$ref": "#/components/schemas/Rule902DisabledWarnings"
          }
        }
      },
      "CaptureResult": {
        "type": "object",
        "description": "Een vastlegging, certificaat, vergelijking of weglakking. De velden hangen af van status.",
        "properties": {
          "capture_id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "processing",
              "completed",
              "failed"
            ]
          },
          "url": {
            "type": "string",
            "description": "Vastgelegde URL (null voor certificaten en vergelijkingen).",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "batch_id": {
            "type": "string",
            "nullable": true
          },
          "metadata": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "type": {
            "type": "string",
            "description": "comparison bij vergelijkingen, import bij een geïmporteerd bewijspakket.",
            "enum": [
              "comparison",
              "import"
            ]
          },
          "compared": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Alleen bij vergelijkingen: de twee ID's van de bronvastleggingen."
          },
          "diff_summary": {
            "$ref": "#/components/schemas/DiffSummary"
          },
          "estimated_completion_seconds": {
            "type": "integer",
            "description": "Alleen bij processing: ruwe schatting, ten minste 5."
          },
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "CAPTURE_FAILED",
                  "CAPTURE_TIME_BUDGET_EXCEEDED",
                  "CAPTURE_SCREENSHOT_TIMEOUT",
                  "SSRF_BLOCKED",
                  "TARGET_UNREACHABLE",
                  "PAYMENT_REQUIRED",
                  "COMPARE_PROFILE_MISMATCH",
                  "COMPARE_URL_MISMATCH",
                  "RULE902_NOT_AVAILABLE_FOR_MISMATCH",
                  "COMPARE_SOURCE_INTEGRITY_FAILED",
                  "QUOTA_EXCEEDED",
                  "CERTIFY_FAILED",
                  "REDACTION_FAILED"
                ],
                "description": "De openbare reden van de fout (zie de tabel Error). CAPTURE_SCREENSHOT_TIMEOUT: de browser heeft de screenshot niet voltooid; CAPTURE_TIME_BUDGET_EXCEEDED: de uitvoering heeft haar volledige tijdsbudget opgebruikt; geen van beide wordt in rekening gebracht. Een vergelijkingsrij (cmp_) kan ook mislukken met COMPARE_SOURCE_INTEGRITY_FAILED (een bronpakket dat is gewijzigd nadat het verzoek was geaccepteerd), COMPARE_PROFILE_MISMATCH of COMPARE_URL_MISMATCH (alleen gevonden in de verzegelde records die de uitvoering vergelijkt) of RULE902_NOT_AVAILABLE_FOR_MISMATCH; de eenheid wordt teruggegeven. Een hashcertificering die op de achtergrond wordt uitgevoerd, mislukt met QUOTA_EXCEEDED wanneer het maandelijkse maximum is bereikt op het moment dat ze wordt uitgevoerd. CERTIFY_FAILED en REDACTION_FAILED: een hashcertificering of een weglakking kon niet worden voltooid; probeer het verzoek opnieuw."
              },
              "message": {
                "type": "string"
              }
            },
            "description": "Alleen bij failed."
          },
          "failed_at": {
            "type": "string",
            "description": "Alleen bij failed.",
            "format": "date-time"
          },
          "captured_at": {
            "type": "string",
            "description": "Wanneer de pagina is vastgelegd (alleen rijen met completed). Bij een weglakking het vastleggingstijdstip van de bronvastlegging (redacted_at is het moment waarop de weglakking is gemaakt).",
            "format": "date-time"
          },
          "redacted_at": {
            "type": "string",
            "format": "date-time",
            "description": "Alleen weglakkingen: wanneer de weglakking is gemaakt. De captured_at van een weglakking is het vastleggingstijdstip van de bronvastlegging."
          },
          "compared_at": {
            "type": "string",
            "nullable": true,
            "format": "date-time",
            "description": "Alleen bij vergelijkingen: wanneer de vergelijking is gemaakt. Het veld captured_at bevat dezelfde waarde (behouden voor oudere clients); gebruik compared_at."
          },
          "package_url": {
            "type": "string",
            "description": "Alleen bij completed: vooraf ondertekende downloadlink van de pakket-ZIP, maximaal 24 uur geldig (zie package_url_expires_at); lees de vastlegging opnieuw voor een nieuwe link.",
            "format": "uri"
          },
          "package_url_expires_at": {
            "type": "string",
            "description": "Alleen bij completed: wanneer package_url niet meer werkt, hooguit 24 uur na dit antwoord (een link die met tijdelijke serverreferenties is ondertekend, kan eerder ophouden te werken); lees de vastlegging opnieuw voor een nieuwe link.",
            "format": "date-time"
          },
          "verification_url": {
            "type": "string",
            "description": "Alleen bij completed: link naar de openbare ProofSnap-verifier.",
            "format": "uri"
          },
          "manifest_hash": {
            "type": "string",
            "description": "Alleen bij completed: sha256:<hex> van manifest.json.",
            "example": "sha256:3f2a..."
          },
          "timestamps": {
            "$ref": "#/components/schemas/Timestamps"
          },
          "c2pa": {
            "type": "object",
            "properties": {
              "included": {
                "type": "boolean"
              },
              "signed_screenshot_sha256": {
                "type": "string",
                "description": "sha256:<hex> van de ondertekende screenshot.",
                "nullable": true
              }
            },
            "description": "Alleen bij completed."
          },
          "file_count": {
            "type": "integer",
            "description": "Alleen bij completed."
          },
          "size_bytes": {
            "type": "integer",
            "description": "Alleen bij completed."
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "code": {
                  "type": "string",
                  "enum": [
                    "consent_gated"
                  ]
                },
                "reason": {
                  "type": "string",
                  "nullable": true
                },
                "final_host": {
                  "type": "string",
                  "nullable": true
                },
                "final_url": {
                  "type": "string",
                  "nullable": true
                },
                "message": {
                  "type": "string"
                },
                "remedy": {
                  "type": "object",
                  "properties": {
                    "option": {
                      "type": "string"
                    },
                    "value": {
                      "type": "boolean"
                    }
                  }
                }
              }
            },
            "description": "Alleen bij completed, als de vastlegging op een cookietoestemmingsmuur of tussenpagina terechtkwam in plaats van op de pagina. Voer opnieuw uit met dismiss_consent: true."
          },
          "external_archive": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ExternalArchive"
              }
            ],
            "description": "Alleen bij completed. Het Internet Archive-verzoek dat met de optie external_archive is gedaan: het huidige resultaat (status), de toestand die in het pakket is verzegeld (status_at_sealing) en de snapshotlink. { requested: false } als er geen verzoek is gedaan."
          },
          "storage": {
            "$ref": "#/components/schemas/CaptureStorage"
          },
          "private_note": {
            "type": "string",
            "nullable": true,
            "description": "Uw privénotitie (zie PrivateNote). Wordt alleen aan de maker van de vastlegging teruggegeven; nooit onderdeel van het bewijspakket."
          },
          "legal_hold": {
            "type": "boolean",
            "description": "De vastlegging staat onder legal hold: ze wordt niet verwijderd zolang true (PATCH /captures/{capture_id} met action legal_hold). Automatisch opgeheven, en de vastlegging verwijderd, wanneer het abonnement is beëindigd en de 30 dagen daarvan zijn verstreken zonder een abonnement dat gegevens bewaart."
          },
          "legal_hold_set_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Wanneer de huidige legal hold is ingesteld; null zonder legal hold."
          },
          "legal_hold_reason": {
            "type": "string",
            "nullable": true,
            "description": "De reden die bij de huidige legal hold is opgeslagen; null zonder legal hold of reden."
          },
          "legal_hold_set_by_email": {
            "type": "string",
            "nullable": true,
            "description": "Alleen voor de eigenaar van het bedrijf, bij een bedrijfsvastlegging onder legal hold: het e-mailadres van degene die de legal hold heeft ingesteld."
          },
          "legal_hold_source": {
            "type": "string",
            "nullable": true,
            "description": "Waar de huidige legal hold vandaan komt: \"manual\" (PATCH /captures/{capture_id}), \"bulk\" (POST /legal-holds), \"matter:<matter_id>\" (de legal hold van de zaak) of \"derived:<capture_id>\" (een weglakking of vergelijking die onder legal hold staat omdat een vastlegging waarop die is gebaseerd onder legal hold staat). null zonder legal hold."
          },
          "legal_hold_released_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Wanneer de laatste legal hold is opgeheven (begin van de respijtperiode na opheffing); null zolang de legal hold geldt of als die nooit is opgeheven, en na de automatische opheffing aan het einde van een abonnement (geen respijtperiode)."
          },
          "deletion_blocked_until": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Einde van de respijtperiode na een opheffing: tot dan wordt de vastlegging niet definitief verwijderd (definitieve DELETE 409 CAPTURE_HOLD_GRACE, geen opschoning van de prullenbak, geen verwijdering door de bewaartermijn); verplaatsen naar de prullenbak is mogelijk. De verwijdering van het account en het bedrijf na afloop van het abonnement wacht hier niet op. null wanneer er geen lopende respijtperiode is (ook zolang een legal hold actief is: die blokkeert zonder einde)."
          },
          "s3_legal_hold": {
            "type": "object",
            "nullable": true,
            "description": "De S3 Object Lock legal hold van de opgeslagen pakketobjecten (ZIP, bijgewerkt OpenTimestamps-bewijs en het afzonderlijk opgeslagen duplicaat, elke versie). status: on, off, pending (wordt toegepast), failed (wordt door de server opnieuw geprobeerd), unsupported (de opslag heeft geen Object Lock-configuratie; de legal hold op het vastleggingsrecord blijft van kracht), none (er is geen pakket opgeslagen, bijv. bij een mislukte vastlegging).",
            "properties": {
              "status": {
                "type": "string",
                "enum": [
                  "on",
                  "off",
                  "pending",
                  "failed",
                  "unsupported",
                  "none"
                ]
              },
              "at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              }
            }
          },
          "source_held": {
            "type": "boolean",
            "nullable": true,
            "description": "Weglakkingen en vergelijkingen: true als een vastlegging waaruit ze zijn gemaakt onder legal hold staat. Een afgeleid pakket volgt de hold van de bron: zolang een bron onder legal hold staat, staat ook de weglakking of vergelijking onder legal hold (legal_hold_source derived:<capture_id>) en kan die hold niet afzonderlijk worden opgeheven (409 LEGAL_HOLD_SOURCE). null voor andere vastleggingen."
          },
          "import": {
            "$ref": "#/components/schemas/CaptureImport"
          },
          "source": {
            "type": "string",
            "description": "Hoe de vastlegging is gemaakt: api, schedule, certify-hash, compare, redact, import. GET /captures/{capture_id} geeft dit alleen terug voor een geïmporteerd pakket (\"import\"); de rijen van GET /captures bevatten het altijd."
          }
        }
      },
      "Timestamps": {
        "type": "object",
        "properties": {
          "bitcoin_ots": {
            "type": "object",
            "properties": {
              "status": {
                "type": "string",
                "description": "Status van het Bitcoin-OpenTimestamps-bewijs in manifest.json.ots. pending_confirmation: het bewijs wacht op een Bitcoin-blok (meestal een paar uur; de controles beginnen ongeveer 2 uur nadat de vastlegging is voltooid en worden herhaald met steeds langere tussenpozen). verified: een Bitcoin-blok bevestigt de manifesthash (block_height, block_time). not_present: het pakket bevat geen manifest.json.ots (de calendars waren niet bereikbaar toen het pakket werd verzegeld). unconfirmed: binnen 7 dagen controleren is geen bevestiging gevonden; het bewijs in het pakket kan nog steeds worden bijgewerkt en gecontroleerd met de OpenTimestamps-client (ots upgrade, ots verify). package_missing: het bewijspakket staat niet meer in de opslag, dus het bewijs kan niet meer worden gecontroleerd. manifest_mismatch: manifest.json in het opgeslagen pakket komt niet overeen met de manifesthash die bij de verzegeling is vastgelegd; de tijdstempel wordt daarvoor niet bevestigd. Beide zijn definitief. Het pakket zelf wordt nooit gewijzigd. not_checked: de vastlegging is voltooid voordat automatische bevestigingscontroles bestonden en is nooit gecontroleerd; het pakket kan nog een manifest.json.ots bevatten die de OpenTimestamps-client kan bijwerken en verifiëren. Webhooks en antwoorden op het aanmaakverzoek worden bij voltooiing verzonden en vermelden dus pending_confirmation (of not_present wanneer de calendars faalden); lees GET /captures/{capture_id} voor de actuele status. Een pakket wordt verzegeld terwijl het bewijs nog in behandeling is: op dat moment is de manifesthash ingediend bij de OpenTimestamps-calendars. Na bevestiging in Bitcoin-blok {block_height} ({block_time} UTC) toont het bewijs aan dat manifest.json uiterlijk op dat tijdstip bestond; download het bijgewerkte bewijs (upgraded_proof_url) en plaats het naast manifest.json als manifest.json.upgraded.ots, waarna verify.sh en verify.ps1 het automatisch controleren.",
                "enum": [
                  "pending_confirmation",
                  "verified",
                  "not_present",
                  "unconfirmed",
                  "package_missing",
                  "manifest_mismatch",
                  "not_checked"
                ]
              },
              "block_height": {
                "type": "integer",
                "nullable": true,
                "description": "Bitcoin-blokhoogte die het bewijs bevestigt. Ingevuld wanneer status verified is."
              },
              "block_time": {
                "type": "string",
                "format": "date-time",
                "nullable": true,
                "description": "Tijd van dat Bitcoin-blok. Alleen bij GET /captures/{capture_id}, ingesteld als status verified is."
              },
              "verified_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true,
                "description": "Wanneer ProofSnap de verankering heeft bevestigd. Alleen bij GET /captures/{capture_id}, ingesteld als status verified is."
              },
              "upgraded_proof_url": {
                "type": "string",
                "format": "uri",
                "nullable": true,
                "description": "Alleen GET /captures/{capture_id}. Zodra de verankering is geverifieerd, slaat ProofSnap het OpenTimestamps-bewijs op dat via de calendars is bijgewerkt (het bevat de Bitcoin-blokattestatie en is dus offline te verifiëren zonder contact met een calendar). Dit is een vooraf ondertekende download ervan, net als package_url maximaal 24 uur geldig; lees de vastlegging opnieuw voor een nieuwe link. null als er geen is (nog niet geverifieerd, of later opgeslagen door een backfill op de achtergrond). Het verzegelde pakket wordt nooit gewijzigd: sla het bestand naast manifest.json van het pakket op als manifest.json.upgraded.ots. Pakketten die vanaf 2026-09-29 zijn verzegeld, verifiëren het met hun meegeleverde scripts (verification/verify.sh en verify.ps1 gebruiken het in plaats van manifest.json.ots); voer voor een ouder pakket `ots verify manifest.json.upgraded.ots -f manifest.json` uit. Exports van zaken bevatten het als captures/<capture_id>.upgraded.ots naast captures/<capture_id>.zip. Wordt samen met het pakket verwijderd."
              }
            }
          },
          "eidas": {
            "$ref": "#/components/schemas/EidasTimestamp"
          }
        }
      },
      "DiffSummary": {
        "type": "object",
        "properties": {
          "text_insertions": {
            "type": "integer",
            "description": "Toegevoegde regels (dezelfde waarde als text_lines_added; sinds 2026-10-14 wordt de tekst in regels geteld, zie text_unit). Eerder gemaakte vergelijkingen tellen tekens. Verouderd (2026-10-14): behouden voor bestaande lezers, niet verwijderd; gebruik text_lines_added.",
            "deprecated": true
          },
          "text_deletions": {
            "type": "integer",
            "description": "Verwijderde regels (dezelfde waarde als text_lines_removed; regels sinds 2026-10-14, daarvoor tekens). Verouderd (2026-10-14): behouden voor bestaande lezers, niet verwijderd; gebruik text_lines_removed.",
            "deprecated": true
          },
          "text_unit": {
            "type": "string",
            "enum": [
              "line"
            ],
            "description": "De eenheid van de teksttellingen: line (elke regel van domtextcontent.txt met de bijbehorende regelinvoer). Afwezig bij vergelijkingen van vóór 2026-10-14, die tekens telden."
          },
          "text_lines_added": {
            "type": "integer",
            "description": "Toegevoegde regels (regels van de After-tekst in toegevoegde blokken van de regeldiff)."
          },
          "text_lines_removed": {
            "type": "integer",
            "description": "Verwijderde regels (regels van de Before-tekst in verwijderde blokken van de regeldiff)."
          },
          "text_lines_before": {
            "type": "integer",
            "description": "Regels van de Before-tekst."
          },
          "text_lines_after": {
            "type": "integer",
            "description": "Regels van de After-tekst."
          },
          "text_changed_percent": {
            "type": "number",
            "description": "100 x (verwijderde regels + toegevoegde regels) / (regels ervoor + regels erna), één decimaal."
          },
          "text_diff_capped": {
            "type": "boolean",
            "description": "true als de regeldiff zijn werkbudget heeft bereikt en een deel van de teksten als geheel vervangen is geteld (meldt te veel, verbergt nooit een wijziging)."
          },
          "metadata_changes": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "{ key, before, after, class } per afwijkend veld van metadata.json, precies deze: url, final_url, page_title, http_status, http_headers.content-type, http_headers.server, dns.connected_ip, resolved_ips (vergeleken als gesorteerde lijst), tls.issuer, tls.valid_to, en de velden van het apparaatprofiel viewport_width, viewport_height, device_scale_factor, is_mobile en user_agent. class is content (url, final_url, page_title, http_status, http_headers.content-type: telt als wijziging), infrastructure (http_headers.server, dns.connected_ip, resolved_ips, tls.issuer, tls.valid_to: welke server de pagina heeft geleverd, alleen gemeld) of device_profile (hoe de vastlegging de pagina heeft weergegeven, alleen gemeld). Een vergelijking van vóór 2026-10-10 heeft geen class."
          },
          "meta_content": {
            "type": "integer",
            "description": "Afwijkende metadatavelden van de klasse content; deze tellen mee voor changed."
          },
          "meta_infra": {
            "type": "integer",
            "description": "Afwijkende metadatavelden van de klasse infrastructure (serversoftware, verbonden IP, opgeloste IP's, TLS-uitgever en vervaldatum). Een content delivery network antwoordt vanaf wisselende adressen, dus twee keer laden van een ongewijzigde pagina kan hier verschillen opleveren; ter informatie gerapporteerd, nooit meegeteld voor changed."
          },
          "meta_device_profile": {
            "type": "integer",
            "description": "Afwijkende velden van het apparaatprofiel (viewport, schaal, mobiele emulatie, user agent); ter informatie gerapporteerd, nooit meegeteld voor changed."
          },
          "image_changes": {
            "type": "object",
            "description": "De vergelijking van afbeeldingen, op basis van de afbeeldingsinventaris die elke bronvastlegging heeft verzegeld (image_inventory.json). status ran, of not_run met reason source_without_inventory (een bron die is vastgelegd voordat vastleggingen een inventaris registreerden), source_inventory_failed of source_inventory_unreadable; er wordt niets anders voor in de plaats gebruikt. De volledige resultaten per afbeelding staan in image_changes.json van het pakket.",
            "properties": {
              "status": {
                "type": "string",
                "enum": [
                  "ran",
                  "not_run"
                ]
              },
              "reason": {
                "type": "string",
                "nullable": true,
                "enum": [
                  "source_without_inventory",
                  "source_inventory_failed",
                  "source_inventory_unreadable",
                  null
                ],
                "description": "Waarom de afbeeldingen niet zijn vergeleken: source_without_inventory (een bron die is gemaakt voordat vastleggingen een afbeeldingsinventaris vastlegden; nieuwe vastleggingen leggen er een vast), source_inventory_failed (de inventaris kon niet worden vastgelegd toen die vastlegging werd gemaakt) of source_inventory_unreadable; null als de vergelijking is uitgevoerd."
              },
              "images_changed": {
                "type": "integer",
                "nullable": true,
                "description": "Eigen afbeeldingen (geleverd vanaf het eigen registreerbare domein van de pagina, of sinds 2026-10-14 vanaf een domein dat de pagina als eigen og:image of icoon opgeeft, of dat in beide vastleggingen de meeste afbeeldingen van de pagina levert: image_changes.json site_image_hosts) die zijn vervangen, toegevoegd of verwijderd, het afbeeldingsdeel van changed; verplaatste afbeeldingen tellen niet mee. null als de vergelijking niet is uitgevoerd. Verouderd (2026-10-14): behouden voor bestaande lezers, niet verwijderd; gebruik site_images_changed.",
                "deprecated": true
              },
              "first_party": {
                "$ref": "#/components/schemas/ImageChangeCounts"
              },
              "third_party": {
                "$ref": "#/components/schemas/ImageChangeCounts"
              },
              "tracking_pixel": {
                "$ref": "#/components/schemas/ImageChangeCounts"
              },
              "inventories_truncated": {
                "type": "boolean",
                "description": "true als een broninventaris zijn maximumaantal vermeldingen heeft bereikt; zie inventory_cutoff."
              },
              "site_images_changed": {
                "type": "integer",
                "description": "Hetzelfde als images_changed (geteld)."
              },
              "other_images_changed": {
                "type": "integer",
                "description": "Afbeeldingen van andere domeinen en trackingpixels die zijn vervangen, verwijderd of toegevoegd (worden nooit meegeteld)."
              },
              "site_image_hosts": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Registreerbare domeinen die naast dat van de pagina als eigen domein van de site worden geteld (site_image_hosts van image_changes.json)."
              },
              "inventory_cutoff": {
                "type": "object",
                "nullable": true,
                "properties": {
                  "before_only_index_limit": {
                    "type": "integer",
                    "nullable": true,
                    "description": "Een afbeelding die alleen in Before voorkomt, is beslisbaar (removed) wanneer de index in de Before-inventaris hieronder ligt; null: geen limiet (de After-inventaris is volledig)."
                  },
                  "after_only_index_limit": {
                    "type": "integer",
                    "nullable": true,
                    "description": "Een afbeelding die alleen in After voorkomt, is beslisbaar (added) wanneer de index in de After-inventaris hieronder ligt; null: geen limiet (de Before-inventaris is volledig)."
                  }
                },
                "description": "Sinds de tweede audit van 2026-10-14 (image_changes.json algoritme 3) een object, beslist op documentindex: wanneer de ANDERE inventaris haar maximale aantal items heeft bereikt, is een afbeelding die maar aan één kant is gevonden alleen beslisbaar als haar index lager is dan het aantal items van de andere inventaris min een marge (de grootste van 10 items en 5 procent van dat aantal); anders is ze onbepaald (undetermined_reason beyond_inventory_limit). null wanneer geen enkele inventaris is afgekapt. Vergelijkingen van daarvoor bevatten een getal (de relatieve documentpositie van de vorige regel)."
              }
            }
          },
          "changed": {
            "type": "boolean",
            "nullable": true,
            "description": "true als de zichtbare tekst verschilt, een metadataveld over de inhoud verschilt (meta_content), of een first-party afbeelding is vervangen, verwijderd of toegevoegd (image_changes.images_changed). Velden over infrastructuur en apparaatprofiel, verplaatste afbeeldingen (dezelfde bytes op een ander adres), afbeeldingen van derden en trackingpixels worden gemeld maar stellen dit niet in. null (sinds 14 oktober 2026) als niets wat telt betrouwbaar is gewijzigd, maar het resultaat voor tekst of afbeeldingen onbepaald is (vastleggingen anders weergegeven, allow_profile_mismatch). Het pakket vermeldt de regel in comparison_method.json counted_as_change. Verouderd (2026-10-14): behouden voor bestaande lezers, niet verwijderd; gebruik verdict (changed | unchanged | undetermined) en per soort text_verdict, images_verdict, metadata_verdict.",
            "deprecated": true
          },
          "text_verdict": {
            "type": "string",
            "enum": [
              "changed",
              "unchanged",
              "undetermined"
            ],
            "description": "undetermined als de twee vastleggingen met een ander apparaatprofiel of andere vastleggingsinstellingen zijn weergegeven (allow_profile_mismatch)."
          },
          "images_verdict": {
            "type": "string",
            "enum": [
              "changed",
              "unchanged",
              "undetermined",
              "not_run"
            ]
          },
          "metadata_verdict": {
            "type": "string",
            "enum": [
              "changed",
              "unchanged"
            ],
            "description": "Uit de metadatavelden van de inhoudsklasse (adres, eindadres, titel, HTTP-status, contenttype)."
          },
          "verdict": {
            "type": "string",
            "enum": [
              "changed",
              "unchanged",
              "undetermined"
            ],
            "description": "Het totaalresultaat in woorden: changed wanneer changed true is, undetermined wanneer changed null is."
          },
          "undetermined_reason": {
            "type": "string",
            "nullable": true,
            "enum": [
              "render_profile_mismatch",
              "capture_settings_mismatch",
              null
            ]
          },
          "text_changes": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Alleen GET: maximaal 60 gewijzigde tekstfragmenten."
          },
          "change_regions": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Alleen GET: maximaal 60 tekstwijzigingen gelokaliseerd op de screenshots (uit text_layout.json van de bronnen), in screenshotpixels. Elke wijziging bevat counterpart_y_px (haar verticale positie op de andere screenshot, overgenomen van het dichtstbijzijnde ongewijzigde tekstblok) en report_region (het nummer van de wijzigingsplek waarin evidence.pdf haar toont, maximaal 10 plekken, of null), zoals in change_regions.json."
          },
          "change_regions_info": {
            "type": "object",
            "nullable": true
          },
          "order_reversed": {
            "type": "boolean",
            "description": "Alleen GET: true wanneer de als capture_id_1 (Before) opgegeven vastlegging later is gemaakt dan de als capture_id_2 (After) opgegeven vastlegging, zoals order_reversed in compare.json. De vergelijking behoudt de opgegeven volgorde; het rapport en het dashboard vermelden dit."
          },
          "render_profile_mismatch": {
            "type": "boolean",
            "description": "Alleen GET: de twee vastleggingen zijn gerenderd met een ander apparaatprofiel (compare.json)."
          },
          "capture_settings_mismatch": {
            "type": "boolean",
            "description": "Alleen GET: de twee vastleggingen zijn gemaakt met verschillende vastleggingsinstellingen (compare.json)."
          },
          "capture_settings_differing": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "setting": {
                  "type": "string"
                },
                "before": {},
                "after": {}
              }
            },
            "description": "Alleen GET: de instellingen die verschillen, { setting, before, after }."
          },
          "profile_mismatch_allowed": {
            "type": "boolean",
            "description": "Alleen GET: de vergelijking is gemaakt met allow_profile_mismatch voor vastleggingen die verschillend zijn gerenderd. Verouderd (2026-10-14): behouden voor bestaande lezers, niet verwijderd; gebruik render_profile_mismatch en capture_settings_mismatch.",
            "deprecated": true
          },
          "url_mismatch": {
            "type": "boolean",
            "description": "Alleen GET: de twee vastleggingen hebben geen gemeenschappelijk adres (gemaakt met allow_url_mismatch)."
          }
        },
        "description": "Samenvatting van de vergelijking."
      },
      "ImageChangeCounts": {
        "type": "object",
        "properties": {
          "unchanged": {
            "type": "integer"
          },
          "replaced": {
            "type": "integer"
          },
          "moved": {
            "type": "integer"
          },
          "removed": {
            "type": "integer"
          },
          "added": {
            "type": "integer"
          },
          "undetermined": {
            "type": "integer"
          },
          "alt_changed": {
            "type": "integer"
          },
          "position_changed": {
            "type": "integer"
          },
          "undetermined_same_error": {
            "type": "integer",
            "description": "Onbepaalde paren waarvan beide kanten dezelfde HTTP-foutstatus gaven (bijvoorbeeld 404 in beide vastleggingen); opgenomen in undetermined, geteld in het rapport en niet vermeld."
          },
          "occurrences": {
            "type": "integer",
            "description": "Alle resultaten van deze partij: voorkomens op de pagina."
          },
          "distinct": {
            "type": "object",
            "description": "Het aantal verschillende afbeeldingen in de resultaten, in totaal en per categorie (een afbeelding die meerdere keren wordt getoond, telt één keer): elk resultaat staat voor de SHA-256 van zijn After-kant, of van zijn Before-kant als het die niet heeft, of voor het adres van die kant als die geen SHA-256 heeft.",
            "properties": {
              "total": {
                "type": "integer"
              },
              "unchanged": {
                "type": "integer"
              },
              "replaced": {
                "type": "integer"
              },
              "moved": {
                "type": "integer"
              },
              "removed": {
                "type": "integer"
              },
              "added": {
                "type": "integer"
              },
              "undetermined": {
                "type": "integer"
              }
            }
          }
        },
        "description": "Aantallen per categorie, als voorkomens op de pagina (één afbeelding die drie keer wordt getoond, telt drie keer): unchanged, replaced (zelfde adres, andere bytes), moved (ander adres, zelfde bytes; gerapporteerd, niet als wijziging geteld), removed, added, undetermined (een afbeelding die aan een van beide kanten geen SHA-256 heeft, nooit als removed of added geteld); plus de gekoppelde afbeeldingen waarvan de alt-tekst of de plaats tussen de afbeeldingen van de pagina is gewijzigd, de undetermined paren met dezelfde HTTP-fout aan beide kanten, en de unieke afbeeldingen."
      },
      "BatchRequest": {
        "type": "object",
        "required": [
          "urls"
        ],
        "properties": {
          "urls": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uri",
              "maxLength": 2048
            },
            "minItems": 2,
            "maxItems": 50,
            "description": "2 tot 50 URL's, elk gevalideerd zoals CaptureRequest.url. Hostnamen worden niet via DNS gecontroleerd wanneer de batch wordt geaccepteerd; een URL die niet door de DNS- of adrescontrole komt, mislukt later met status failed (en capture.failed)."
          },
          "options": {
            "$ref": "#/components/schemas/CaptureOptions"
          },
          "private_note": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PrivateNote"
              }
            ],
            "description": "Eén privénotitie die bij elke vastlegging van de batch wordt opgeslagen. Uw privénotitie bij de vastlegging (bijvoorbeeld \"mobile capture\"), maximaal 200 tekens, platte tekst (de PlainText-regel). Ze wordt alleen in het record van de vastlegging opgeslagen: ze maakt GEEN deel uit van het verzegelde bewijspakket (niet in de ZIP, de PDF, het manifest of een hash) en heeft geen bewijswaarde, dus ze kan later worden gewijzigd of gewist met PATCH /v1/captures/{capture_id} zonder dat dit het bewijs beïnvloedt. Alleen de maker van de vastlegging kan haar lezen. Laat haar weg, of stuur null of een lege string, voor geen notitie."
          }
        }
      },
      "BatchResult": {
        "type": "object",
        "properties": {
          "batch_id": {
            "type": "string",
            "example": "batch_a1B2c3D4e5F6g7H8"
          },
          "count": {
            "type": "integer"
          },
          "captures": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "capture_id": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "status": {
                  "type": "string",
                  "description": "failed als de vastlegging niet kon worden gestart.",
                  "enum": [
                    "processing",
                    "failed"
                  ]
                }
              }
            }
          },
          "not_admitted": {
            "type": "array",
            "description": "Alleen wanneer de limiet voor lopende vastleggingen van het account is bereikt: de URL's die niet zijn gestart, in de verzonden volgorde. Ze kosten niets (geen rate-limit- of maandelijkse eenheid); stuur ze later opnieuw. Niet aanwezig wanneer elke URL is gestart.",
            "items": {
              "type": "object",
              "properties": {
                "url": {
                  "type": "string"
                },
                "code": {
                  "type": "string",
                  "description": "Waarom de URL niet is gestart.",
                  "enum": [
                    "CONCURRENT_CAPTURE_LIMIT"
                  ]
                }
              }
            }
          },
          "in_flight_limit": {
            "type": "integer",
            "description": "Alleen samen met not_admitted: het aantal vastleggingen dat het account tegelijk mag laten lopen (developer 10, startup 25, growth 100, legal_solo en legal_seats 25, custom 200, website_archive 25, sandbox 5)."
          },
          "warnings": {
            "$ref": "#/components/schemas/Rule902DisabledWarnings"
          }
        }
      },
      "VerifyResult": {
        "type": "object",
        "properties": {
          "verdict": {
            "type": "string",
            "nullable": true,
            "description": "match: handtekening geldig en elk vermeld bestand aanwezig met de vastgelegde SHA-256. modified: handtekening ongeldig of een bestand ontbreekt of is gewijzigd. invalid: het pakket kon niet worden gecontroleerd. Alleen null bij een geïmporteerd pakket dat deze keer niet opnieuw is gecontroleerd (`rechecked: false`, met `reason` en de bij de import vastgelegde verificatie in `stored_verification`).",
            "enum": [
              "match",
              "modified",
              "invalid",
              null
            ]
          },
          "reason": {
            "type": "string",
            "description": "Alleen bij invalid; bij een geïmporteerd pakket met `rechecked: false` waarom het niet opnieuw is gecontroleerd (too_large_for_synchronous_check, time_budget, storage_error)."
          },
          "capture_id": {
            "type": "string"
          },
          "source": {
            "type": "string",
            "enum": [
              "s3"
            ]
          },
          "checks": {
            "type": "object",
            "properties": {
              "manifest_signature": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "valid",
                      "invalid"
                    ]
                  },
                  "algorithm": {
                    "type": "string",
                    "enum": [
                      "RSA-4096"
                    ]
                  }
                }
              },
              "declared_files": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "all_match",
                      "mismatch"
                    ]
                  },
                  "mismatched": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "missing": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              },
              "bitcoin_ots": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string",
                    "description": "Alleen de aanwezigheid van manifest.json.ots; het bewijs zelf wordt hier niet gecontroleerd.",
                    "enum": [
                      "present",
                      "not_present"
                    ]
                  }
                }
              },
              "eidas_disig": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string",
                    "description": "Aanwezigheid van manifest.json.tsr. Het token zelf wordt gecontroleerd in checks.eidas. missing: manifest.json vermeldt eidas_requested (het pakket is met het token uitgegeven), maar manifest.json.tsr zit niet in het pakket.",
                    "enum": [
                      "present",
                      "missing"
                    ]
                  }
                },
                "nullable": true,
                "description": "null als er geen manifest.json.tsr is en manifest.json geen eidas_requested vermeldt."
              },
              "proofsnap_issuance": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string",
                    "description": "issued_by_proofsnap: de manifesthash staat in het ProofSnap-uitgifteregister. unverified: die staat er niet in (pakketten die zijn uitgegeven voordat het register bestond, staan er niet in). unknown: het opzoeken is mislukt.",
                    "enum": [
                      "issued_by_proofsnap",
                      "unverified",
                      "unknown"
                    ]
                  },
                  "manifest_sha256": {
                    "type": "string"
                  },
                  "issued_at": {
                    "type": "string",
                    "nullable": true,
                    "format": "date-time"
                  },
                  "type": {
                    "type": "string"
                  }
                }
              },
              "eidas": {
                "type": "object",
                "nullable": true,
                "description": "null als er geen manifest.json.tsr is en manifest.json geen eidas_requested vermeldt. Het eIDAS-tijdstempeltoken zoals door ProofSnap op verzoek gecontroleerd vanuit het opgeslagen pakket. Dit is een controle die ProofSnap uitvoert; het veld note vermeldt hoe u het token onafhankelijk kunt verifiëren.",
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "verified",
                      "verified_revocation_unknown",
                      "failed",
                      "unavailable",
                      "missing"
                    ],
                    "description": "missing: manifest.json vermeldt eidas_requested: true, dus het pakket is met een gekwalificeerde tijdstempel uitgegeven, maar manifest.json.tsr zit er niet in (het antwoord bevat dan de waarschuwing EIDAS_TOKEN_MISSING). verified: elke controle is geslaagd en de bij de uitgifte vastgelegde status van het TSA-certificaat is bekend (good, of revoked na het tijdstip van het token om een andere reden dan compromittering van de sleutel). verified_revocation_unknown: elke uitgevoerde controle is geslaagd, maar de status van het TSA-certificaat kon niet uit de vastgelegde gegevens worden vastgesteld (revocation.status unknown of not_checked). failed: ten minste één controle is mislukt, zie failures (het antwoord bevat dan de waarschuwing EIDAS_TOKEN_FAILED). unavailable: geen oordeel over het token (reason: not configured, timeout, input_rejected als de verifier de invoer weigerde, bijvoorbeeld te grote validatiegegevens, inconclusive); dit zegt niets over het token."
                  },
                  "reason": {
                    "type": "string",
                    "description": "Alleen bij unavailable: timeout, input_rejected, inconclusive, of een korte uitleg."
                  },
                  "checked_by": {
                    "type": "string",
                    "example": "ProofSnap API on request"
                  },
                  "checked_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "gen_time": {
                    "type": "string",
                    "format": "date-time",
                    "nullable": true,
                    "description": "Het tijdstip in het token (TSTInfo genTime)."
                  },
                  "tsa_name": {
                    "type": "string",
                    "nullable": true,
                    "description": "Uit het certificaat van de ondertekenaar (common name en organisatie), nooit een standaardwaarde."
                  },
                  "serial_number": {
                    "type": "string",
                    "nullable": true
                  },
                  "policy_oid": {
                    "type": "string",
                    "nullable": true
                  },
                  "hash_algorithm": {
                    "type": "string",
                    "nullable": true
                  },
                  "signature_algorithm": {
                    "type": "string"
                  },
                  "signing_certificate_form": {
                    "type": "string",
                    "enum": [
                      "ESSCertIDv2",
                      "ESSCertID"
                    ]
                  },
                  "signer_certificate": {
                    "type": "object",
                    "nullable": true,
                    "properties": {
                      "subject": {
                        "type": "string"
                      },
                      "issuer": {
                        "type": "string"
                      },
                      "serial_number": {
                        "type": "string"
                      },
                      "sha256": {
                        "type": "string"
                      },
                      "not_before": {
                        "type": "string",
                        "format": "date-time"
                      },
                      "not_after": {
                        "type": "string",
                        "format": "date-time"
                      }
                    }
                  },
                  "root": {
                    "type": "object",
                    "properties": {
                      "label": {
                        "type": "string",
                        "example": "CA Disig QCA4"
                      },
                      "sha256": {
                        "type": "string"
                      }
                    }
                  },
                  "chain_checked_at": {
                    "type": "string",
                    "format": "date-time",
                    "nullable": true,
                    "description": "Het tijdstip waarop de keten is gecontroleerd: de genTime van het token, zodat een certificaat dat na de uitgifte van het token is verlopen, het token niet laat falen."
                  },
                  "chain": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "subject": {
                          "type": "string"
                        },
                        "sha256": {
                          "type": "string"
                        }
                      }
                    }
                  },
                  "revocation": {
                    "type": "object",
                    "description": "Status van het TSA-certificaat op basis van de gegevens die bij de uitgifte zijn vastgelegd (basis recorded_at_issuance), geverifieerd via de handtekening en waarvan vereist is dat het op genTime geldig was. Niet live opgehaald.",
                    "properties": {
                      "status": {
                        "type": "string",
                        "enum": [
                          "good",
                          "revoked",
                          "revoked_after_gen_time",
                          "unknown",
                          "not_checked"
                        ],
                        "description": "revoked_after_gen_time: ingetrokken na het tijdstip van het token om een andere reden dan compromittering van de sleutel (het token blijft geverifieerd). not_checked: er zijn geen intrekkingsgegevens vastgelegd of gecontroleerd."
                      },
                      "source": {
                        "type": "string",
                        "enum": [
                          "ocsp",
                          "crl"
                        ],
                        "nullable": true
                      },
                      "basis": {
                        "type": "string",
                        "enum": [
                          "recorded_at_issuance"
                        ]
                      },
                      "produced_at": {
                        "type": "string",
                        "format": "date-time"
                      },
                      "this_update": {
                        "type": "string",
                        "format": "date-time"
                      },
                      "next_update": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true
                      },
                      "reason": {
                        "type": "string",
                        "description": "Waarom de status onbekend is."
                      }
                    }
                  },
                  "checks": {
                    "type": "object",
                    "description": "Elke controle: passed, failed, not_applicable (nonce: geen geregistreerd voor een opgeslagen token), unknown (intrekking zonder bruikbare gegevens) of not_checked (een eerdere fout heeft de controle gestopt).",
                    "properties": {
                      "response_status": {
                        "type": "string",
                        "enum": [
                          "passed",
                          "failed",
                          "not_applicable",
                          "unknown",
                          "not_checked"
                        ]
                      },
                      "message_imprint": {
                        "type": "string",
                        "enum": [
                          "passed",
                          "failed",
                          "not_applicable",
                          "unknown",
                          "not_checked"
                        ]
                      },
                      "nonce": {
                        "type": "string",
                        "enum": [
                          "passed",
                          "failed",
                          "not_applicable",
                          "unknown",
                          "not_checked"
                        ]
                      },
                      "content_type": {
                        "type": "string",
                        "enum": [
                          "passed",
                          "failed",
                          "not_applicable",
                          "unknown",
                          "not_checked"
                        ]
                      },
                      "message_digest": {
                        "type": "string",
                        "enum": [
                          "passed",
                          "failed",
                          "not_applicable",
                          "unknown",
                          "not_checked"
                        ]
                      },
                      "signature": {
                        "type": "string",
                        "enum": [
                          "passed",
                          "failed",
                          "not_applicable",
                          "unknown",
                          "not_checked"
                        ]
                      },
                      "signing_certificate": {
                        "type": "string",
                        "enum": [
                          "passed",
                          "failed",
                          "not_applicable",
                          "unknown",
                          "not_checked"
                        ]
                      },
                      "timestamping_eku": {
                        "type": "string",
                        "enum": [
                          "passed",
                          "failed",
                          "not_applicable",
                          "unknown",
                          "not_checked"
                        ]
                      },
                      "chain": {
                        "type": "string",
                        "enum": [
                          "passed",
                          "failed",
                          "not_applicable",
                          "unknown",
                          "not_checked"
                        ]
                      },
                      "revocation": {
                        "type": "string",
                        "enum": [
                          "passed",
                          "failed",
                          "not_applicable",
                          "unknown",
                          "not_checked"
                        ]
                      }
                    }
                  },
                  "failures": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "check": {
                          "type": "string"
                        },
                        "reason": {
                          "type": "string"
                        }
                      }
                    }
                  },
                  "note": {
                    "type": "string"
                  },
                  "detail": {
                    "type": "string",
                    "nullable": true,
                    "description": "Alleen bij input_rejected: wat de verifier heeft geweigerd."
                  }
                }
              }
            },
            "description": "Ontbreekt wanneer verdict invalid is. Het verdict hangt alleen af van manifest_signature en declared_files; eidas wordt ernaast gerapporteerd en, als die controle mislukt is, in warnings."
          },
          "warnings": {
            "type": "array",
            "description": "Alleen aanwezig als iets buiten het eindoordeel aandacht nodig heeft. EIDAS_TOKEN_FAILED: manifest.json.tsr heeft de controle in checks.eidas niet doorstaan. EIDAS_TOKEN_MISSING: manifest.json vermeldt eidas_requested, maar manifest.json.tsr zit niet in het pakket.",
            "items": {
              "type": "object",
              "properties": {
                "code": {
                  "type": "string",
                  "enum": [
                    "EIDAS_TOKEN_FAILED",
                    "EIDAS_TOKEN_MISSING"
                  ]
                },
                "message": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "ScheduleRequest": {
        "type": "object",
        "required": [
          "url",
          "mode"
        ],
        "properties": {
          "url": {
            "type": "string",
            "description": "Vast te leggen pagina, gevalideerd zoals CaptureRequest.url.",
            "format": "uri",
            "maxLength": 2048
          },
          "mode": {
            "type": "string",
            "description": "interval: elke interval_seconds. cron: volgens cron_expr. on_change: controleer de pagina elke interval_seconds en leg alleen vast als de tekst is gewijzigd.",
            "enum": [
              "interval",
              "cron",
              "on_change"
            ]
          },
          "interval_seconds": {
            "type": "integer",
            "description": "Verplicht voor interval en on_change. Minimaal het minimum van het abonnement (300 bij developer, startup, growth, custom, legal_solo en legal_seats; 86400 bij website_archive), anders 400 INTERVAL_TOO_SHORT.",
            "minimum": 300
          },
          "cron_expr": {
            "type": "string",
            "description": "Verplicht voor cron (maximaal 200 tekens). Een expressie die niet kan worden geparsed, geeft 400 INVALID_CRON; twee opeenvolgende uitvoeringen die dichter bij elkaar liggen dan het minimum van het abonnement geven 400 INTERVAL_TOO_SHORT.",
            "maxLength": 200,
            "example": "0 9 * * 1-5"
          },
          "timezone": {
            "type": "string",
            "description": "IANA-tijdzone waarin cron_expr wordt geëvalueerd. Standaard UTC.",
            "example": "Europe/Berlin"
          },
          "webhook_url": {
            "type": "string",
            "description": "https-URL. Uitvoeringen met interval en cron sturen capture.completed of capture.failed; on_change stuurt monitor.change_detected. De URL moet https zijn en mag niet verwijzen naar een privé-, loopback-, link-local-, carrier-grade NAT- of cloudmetadata-adres (dezelfde regels als voor een vastleggings-URL; anders 422 SSRF_BLOCKED). Een lege waarde of een waarde met alleen witruimte betekent geen webhook. Een schema heeft één webhook_url, op het hoogste niveau: een options.webhook_url wordt niet opgeslagen (als er geen webhook_url op het hoogste niveau wordt meegestuurd, wordt die als zodanig gebruikt), zodat een on_change-schema nooit ook capture.completed stuurt. Vereist een API-sleutel: vanuit een dashboardsessie verzonden wordt het geweigerd met 400 WEBHOOK_REQUIRES_API_KEY, omdat webhooks worden ondertekend met het webhook-secret van de sleutel.",
            "format": "uri"
          },
          "js_render": {
            "type": "boolean",
            "description": "Alleen bij on_change: controleer de pagina met een volledige browserweergave in plaats van een gewone HTTP-fetch (voor pagina's die JavaScript nodig hebben).",
            "default": false
          },
          "options": {
            "$ref": "#/components/schemas/CaptureOptions"
          },
          "max_capture_age_seconds": {
            "type": "integer",
            "nullable": true,
            "minimum": 86400,
            "maximum": 34560000,
            "description": "Alleen bij on_change: maak ook een volledige vastlegging (die stuurt capture.completed) als de nieuwste vastlegging van het schema ouder is dan deze waarde, ook als de pagina niet is gewijzigd. Voor regels die ten minste elke N dagen een kopie vereisen, bijvoorbeeld 7776000 voor 90 dagen. Van 1 dag tot 400 dagen en langer dan interval_seconds; null (standaard) legt alleen bij een wijziging vast. PATCH met null verwijdert dit."
          },
          "alert_on": {
            "type": "string",
            "enum": [
              "any",
              "text"
            ],
            "nullable": true,
            "description": "Alleen bij on_change (2026-10-14): welke gedetecteerde wijziging monitor.change_detected stuurt. any (standaard, het eerdere gedrag): elke wijzigingsvastlegging. text: alleen als de vergelijking met de vorige vastlegging vaststelt dat de zichtbare tekst is gewijzigd (diff_summary.text_verdict changed); een wijzigingsvastlegging waarvan de vergelijking zegt dat de tekst niet is gewijzigd of niet kon worden bepaald, stuurt niets (de vastlegging wordt wel gemaakt, gefactureerd en stuurt capture.completed naar abonnementen). Zonder vergelijkingsresultaat (geen basis, vergelijking mislukt) volgt wel een melding. 400 MALFORMED_REQUEST bij een andere modus of waarde. null bij schema's met interval en cron."
          }
        },
        "example": {
          "url": "https://example.com/terms",
          "mode": "on_change",
          "interval_seconds": 3600,
          "webhook_url": "https://your-app.example/webhooks/proofsnap"
        }
      },
      "Schedule": {
        "type": "object",
        "properties": {
          "schedule_id": {
            "type": "string"
          },
          "url": {
            "type": "string"
          },
          "mode": {
            "type": "string",
            "enum": [
              "interval",
              "cron",
              "on_change"
            ]
          },
          "interval_seconds": {
            "type": "integer",
            "nullable": true
          },
          "cron_expr": {
            "type": "string",
            "nullable": true
          },
          "timezone": {
            "type": "string",
            "nullable": true
          },
          "webhook_url": {
            "type": "string",
            "nullable": true,
            "description": "De webhook van het schema (bij een ouder schema dat deze in options.webhook_url bewaarde, wordt die waarde hier getoond en bevat options deze nooit). Vereist een API-sleutel: vanuit een dashboardsessie wordt dit geweigerd met 400 WEBHOOK_REQUIRES_API_KEY, omdat webhooks worden ondertekend met het webhook-secret van de sleutel."
          },
          "options": {
            "type": "object",
            "description": "Vastleggingsopties met toegepaste standaardwaarden."
          },
          "js_render": {
            "type": "boolean"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "paused"
            ]
          },
          "next_run_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_run_at": {
            "type": "string",
            "nullable": true,
            "format": "date-time"
          },
          "last_capture_id": {
            "type": "string",
            "nullable": true
          },
          "run_count": {
            "type": "integer"
          },
          "consecutive_failures": {
            "type": "integer"
          },
          "pause_reason": {
            "type": "string",
            "description": "Waarom het schema automatisch is gepauzeerd: max_failures (5 mislukte uitvoeringen op rij), no_billing, plan_downgrade (limieten van het abonnement, of een Archive only-abonnement), invalid_cron of key_revoked. null als het actief is of door u is gepauzeerd.",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "max_capture_age_seconds": {
            "type": "integer",
            "nullable": true
          },
          "current_capture_id": {
            "type": "string",
            "nullable": true,
            "description": "De nieuwste VOLTOOIDE vastlegging van het schema (de actuele versie; last_capture_id is de nieuwste die is gestart)."
          },
          "current_captured_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "alert_on": {
            "type": "string",
            "enum": [
              "any",
              "text"
            ],
            "nullable": true,
            "description": "on_change: any of text (any bij een schema dat vóór deze optie is aangemaakt); null bij interval en cron."
          },
          "warnings": {
            "$ref": "#/components/schemas/Rule902DisabledWarnings"
          }
        }
      },
      "Matter": {
        "type": "object",
        "properties": {
          "matter_id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "client": {
            "type": "string",
            "nullable": true
          },
          "case_number": {
            "type": "string",
            "nullable": true
          },
          "opposing_party": {
            "type": "string",
            "nullable": true
          },
          "jurisdiction": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "archived",
              "sealed"
            ]
          },
          "sealed": {
            "type": "boolean"
          },
          "capture_count": {
            "type": "integer"
          },
          "custody_count": {
            "type": "integer",
            "description": "Vermeldingen in het bewijsketenlogboek."
          },
          "custody_head_hash": {
            "type": "string",
            "description": "sha256:<hex> van de nieuwste vermelding in het bewijsketenlogboek.",
            "nullable": true
          },
          "seal": {
            "type": "object",
            "nullable": true,
            "description": "null tot de verzegeling.",
            "allOf": [
              {
                "$ref": "#/components/schemas/MatterSeal"
              }
            ]
          },
          "member_uids": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "created_by": {
            "type": "string",
            "description": "De eigenaar.",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "read_only": {
            "type": "boolean",
            "description": "Aanwezig (true) bij een zaak van een bedrijf dat is beëindigd of als bedrijfsarchief wordt bewaard: alleen openen, exporteren en het intrekken van de deellink."
          },
          "legal_hold": {
            "type": "boolean",
            "description": "De legal hold van de zaak staat aan (PATCH { legal_hold })."
          },
          "legal_hold_set_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "MatterSeal": {
        "type": "object",
        "properties": {
          "root_hash": {
            "type": "string",
            "description": "sha256:<hex>: de kop van de bewijsketen die is verzegeld en bij OpenTimestamps is ingediend."
          },
          "ots": {
            "type": "string",
            "description": "OpenTimestamps-bewijs, hex."
          },
          "ots_pending": {
            "type": "boolean"
          },
          "eidas": {
            "type": "object",
            "properties": {
              "tsr_base64": {
                "type": "string",
                "nullable": true
              },
              "serial": {
                "type": "string",
                "nullable": true
              },
              "issued_at": {
                "type": "string",
                "nullable": true
              },
              "ltv": {
                "type": "boolean"
              },
              "verification_status": {
                "type": "string",
                "enum": [
                  "verified"
                ],
                "description": "Het token heeft bij de verzegeling de ProofSnap-controle doorstaan (gedeelde verifier, dezelfde controles als bij een token van een vastlegging)."
              },
              "revocation_status": {
                "type": "string",
                "enum": [
                  "good",
                  "revoked_after_gen_time",
                  "unknown",
                  "not_checked"
                ],
                "description": "Status van het TSA-certificaat, vastgesteld bij de verzegeling op basis van het OCSP-antwoord of de CRL die toen is verzameld."
              },
              "validation": {
                "type": "object",
                "nullable": true,
                "description": "De inhoud van eidas_validation.json die bij de verzegeling is vastgelegd, voor een offline controle van het token: tsaCertChain, rootCaPem, ocspResponse, crlResponse (weggelaten en in omitted genoemd wanneer te groot), ocspUrl, ocspFetchedAt, ocspStatus, crlUrl, verification. Verzegelingen van vóór 2026-10-01 hebben deze niet.",
                "additionalProperties": true
              }
            },
            "nullable": true,
            "description": "eIDAS-tijdstempel over dezelfde hash als eIDAS in de implementatie is geconfigureerd, de aanroeper live is en een token is verkregen dat de controle heeft doorstaan, anders null (zie eidas_status)."
          },
          "sealed_by": {
            "type": "string",
            "nullable": true
          },
          "sealed_at": {
            "type": "string",
            "format": "date-time"
          },
          "eidas_status": {
            "type": "string",
            "enum": [
              "confirmed",
              "not_obtained",
              "not_configured"
            ],
            "description": "Ontbreekt bij verzegelingen van vóór 2026-10-01. not_obtained: de verzegeling bevat alleen de OpenTimestamps-verankering (eidas_reason token_refused, unavailable of test_key). Een time-out van de eIDAS-tijdstempel levert nooit een verzegeling op: het verzoek geeft dan 503 EIDAS_SEAL_RETRY terug."
          },
          "eidas_reason": {
            "type": "string",
            "enum": [
              "token_refused",
              "unavailable",
              "test_key",
              "account_limit"
            ],
            "description": "Alleen bij not_obtained. test_key: verzegeld door een testaanroeper (test-API-sleutel of sandboxsessie), dus er is geen eIDAS-tijdstempel aangevraagd. account_limit: het account heeft nog geen betaalde factuur en heeft zijn eIDAS-limiet bereikt (20 per UTC-dag, 50 per UTC-maand)."
          }
        },
        "nullable": true
      },
      "CustodyEntry": {
        "type": "object",
        "properties": {
          "seq": {
            "type": "integer"
          },
          "ts": {
            "type": "string",
            "format": "date-time"
          },
          "actor_uid": {
            "type": "string",
            "nullable": true
          },
          "actor_name": {
            "type": "string",
            "nullable": true
          },
          "actor_role": {
            "type": "string",
            "enum": [
              "api",
              "user"
            ],
            "nullable": true
          },
          "action": {
            "type": "string",
            "description": "Bijvoorbeeld matter.created, matter.updated, capture.added, capture.removed, capture.legal_hold_placed, capture.legal_hold_released, member.added, member.removed, package.imported (een bewijspakket dat met matter_id is geïmporteerd; target is de id van de vastlegging)."
          },
          "target": {
            "type": "string",
            "nullable": true
          },
          "prev_hash": {
            "type": "string"
          },
          "entry_hash": {
            "type": "string"
          },
          "details": {
            "type": "object",
            "description": "Alleen bij package.imported: manifest_sha256, zip_sha256, package_kind, verdict, levels. Opgeslagen bij de vermelding, maar NIET gedekt door entry_hash.",
            "additionalProperties": true
          }
        },
        "description": "Eén schakel van de hashketen. entry_hash omvat seq, ts, actor, action, target en prev_hash."
      },
      "MatterCapture": {
        "type": "object",
        "properties": {
          "capture_id": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "nullable": true
          },
          "source": {
            "type": "string"
          },
          "label": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string"
          },
          "captured_at": {
            "type": "string",
            "nullable": true
          },
          "file_count": {
            "type": "integer",
            "nullable": true
          },
          "manifest_hash": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "AuditEntry": {
        "type": "object",
        "properties": {
          "ts": {
            "type": "string"
          },
          "actor_uid": {
            "type": "string",
            "nullable": true
          },
          "actor_name": {
            "type": "string",
            "nullable": true
          },
          "actor_role": {
            "type": "string",
            "nullable": true
          },
          "action": {
            "type": "string",
            "description": "Bijvoorbeeld capture.downloaded, export.generated, share.created, share.revoked."
          },
          "target": {
            "type": "string",
            "nullable": true
          },
          "source_ip": {
            "type": "string",
            "nullable": true,
            "description": "Alleen voor de eigenaar van de zaak (ontbreekt voor leden)."
          },
          "user_agent": {
            "type": "string",
            "nullable": true,
            "description": "Alleen voor de eigenaar van de zaak (ontbreekt voor leden)."
          }
        },
        "description": "Toegangsgebeurtenis. Maakt geen deel uit van de hashketen."
      },
      "CertifyResult": {
        "type": "object",
        "properties": {
          "certify_id": {
            "type": "string",
            "example": "cert_a1B2c3D4e5F6g7H8"
          },
          "status": {
            "type": "string",
            "enum": [
              "completed"
            ]
          },
          "hash_sha256": {
            "type": "string",
            "description": "Alleen voor één certificaat."
          },
          "combined": {
            "type": "boolean",
            "description": "Alleen bij een gecombineerd certificaat."
          },
          "file_count": {
            "type": "integer",
            "description": "Alleen bij een gecombineerd certificaat."
          },
          "files": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string",
                  "description": "Het label dat u hebt verzonden.",
                  "nullable": true
                },
                "sha256": {
                  "type": "string"
                }
              }
            },
            "description": "Alleen bij een gecombineerd certificaat."
          },
          "label": {
            "type": "string",
            "description": "Uw label, of \"N files\" bij een gecombineerd certificaat.",
            "nullable": true
          },
          "certified_at": {
            "type": "string",
            "format": "date-time"
          },
          "timestamps": {
            "type": "object",
            "properties": {
              "bitcoin_ots": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "pending_confirmation",
                      "not_present"
                    ],
                    "description": "pending_confirmation: manifest.json.ots zit in het pakket en wacht op een Bitcoin-blok. not_present: de OpenTimestamps-calendars waren niet bereikbaar, het pakket bevat geen manifest.json.ots en manifest.json vermeldt het niet."
                  },
                  "block_height": {
                    "type": "integer",
                    "nullable": true
                  }
                }
              },
              "eidas": {
                "$ref": "#/components/schemas/EidasTimestamp"
              }
            }
          },
          "package_url": {
            "type": "string",
            "format": "uri"
          },
          "package_url_expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "verify_url": {
            "type": "string",
            "description": "Link naar de openbare ProofSnap-verifier.",
            "format": "uri"
          },
          "warnings": {
            "$ref": "#/components/schemas/Rule902DisabledWarnings"
          }
        }
      },
      "PlainText": {
        "type": "string",
        "description": "Regel voor platte tekst in korte vrije-tekstvelden die worden afgedrukt op documenten voor de rechter of op openbare pagina's (case_reference, case_id, case_note, waarden van evidence_pack.custodian, de case_reference van certify-hash, de custodian van een weglakking, de exportverklaring, namen van bedrijven en zaken). De waarde wordt NFC-genormaliseerd, witruimte wordt samengevoegd en bijgesneden, en de lengte wordt geteld in tekens (Unicode-codepunten). Geweigerd worden: stuurtekens (C0, C1), bidi-embeddings, -overrides en -isolates (U+202A tot U+202E, U+2066 tot U+2069), richtingsmarkeringen (U+200E, U+200F, U+061C), tekens zonder breedte en onzichtbare tekens (U+200B, U+2060 tot U+2064, U+FEFF, U+00AD, U+034F, U+180E, U+206A tot U+206F, U+FFF9 tot U+FFFB), de Hangul-opvultekens (U+115F, U+1160, U+3164, U+FFA0), tagtekens (U+E0000 tot U+E007F), losse surrogaten en tekens voor privégebruik. Letters van elk schrift worden geaccepteerd, waaronder Hangul-lettergrepen, Arabisch, Hebreeuws, Devanagari en CJK, en U+200C / U+200D (ZWNJ, ZWJ) zijn toegestaan omdat de juiste spelling in verschillende schriften ze nodig heeft."
      },
      "EidasTimestamp": {
        "type": "object",
        "description": "eIDAS-uitkomst als include_eidas met een live-sleutel is aangevraagd; ontbreekt als het niet is aangevraagd. confirmed: het pakket bevat manifest.json.tsr (gefactureerd, tenzij de intrekkingsstatus onbekend is). not_obtained: er is geen token meegeleverd en niets gefactureerd; het pakket bevat alleen de OpenTimestamps-verankering (reden token_refused: er kwam een token terug dat de controle bij de uitgifte niet heeft doorstaan; unavailable: er kon geen token worden verkregen; time_budget: het verzoek paste niet in de resterende tijd en is daarom niet gedaan, is door de tijdstempeldienst geweigerd vóór enig verzoek aan de autoriteit, of is afgebroken, wat gebeurt op de synchrone weglakkingsroute of aan het einde van een lange vastlegging; account_limit: het account heeft nog geen betaalde factuur en heeft zijn eIDAS-limiet van 20 tijdstempels per UTC-dag of 50 per UTC-maand bereikt, samen geteld over vastleggingen, batches, schema's, certificeringen, vergelijkingen, weglakkingen en verzegelingen van zaken, dus er is geen verzoek gedaan; accounts met een betaalde factuur zijn niet beperkt). Rijen die vóór 2026-10-01 zijn voltooid, melden alleen confirmed.",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "confirmed",
              "not_obtained"
            ]
          },
          "reason": {
            "type": "string",
            "enum": [
              "token_refused",
              "unavailable",
              "time_budget",
              "account_limit"
            ],
            "description": "Alleen bij not_obtained."
          },
          "serial": {
            "type": "string",
            "nullable": true,
            "description": "Alleen bij confirmed."
          },
          "issued_at": {
            "type": "string",
            "nullable": true,
            "description": "Alleen bij confirmed."
          },
          "revocation": {
            "type": "string",
            "enum": [
              "unknown"
            ],
            "description": "Alleen bij confirmed, en alleen als de status van het TSA-certificaat bij de uitgifte niet kon worden vastgesteld (het token heeft alle andere controles doorstaan). Het token wordt meegeleverd en niet gefactureerd."
          },
          "billed": {
            "type": "boolean",
            "enum": [
              false
            ],
            "description": "Aanwezig (false) samen met revocation unknown: voor dit token is niets gefactureerd."
          }
        }
      },
      "ExternalArchive": {
        "type": "object",
        "description": "Het resultaat van een vastlegging bij het Internet Archive (Wayback Machine). Het Internet Archive is een openbaar archief van een derde partij.",
        "required": [
          "requested"
        ],
        "properties": {
          "requested": {
            "type": "boolean",
            "description": "false als er geen Internet Archive-verzoek is gedaan (external_archive: false, een certificering of vergelijking, een vastlegging van vóór het bestaan van archivering, of het verzoek paste niet in het tijdsbudget). Alle andere velden ontbreken dan."
          },
          "status": {
            "type": "string",
            "enum": [
              "created",
              "recent_existing",
              "created_after_request",
              "existing",
              "pending",
              "rate_limited",
              "failed",
              "unconfirmed",
              "unavailable",
              "skipped",
              "skipped_sensitive",
              "skipped_query"
            ],
            "description": "Huidig resultaat. Voor een Save Page Now-taak die bij het verzegelen nog in behandeling is, leest de planner de taakstatus ongeveer 5 en 30 minuten na het verzoek uit, en dit wordt de definitieve uitkomst (created, recent_existing, failed, rate_limited, of unconfirmed wanneer binnen het controlevenster geen antwoord kwam). created, recent_existing, created_after_request en existing bevatten snapshot_url."
          },
          "status_at_sealing": {
            "type": "string",
            "enum": [
              "created",
              "recent_existing",
              "created_after_request",
              "existing",
              "pending",
              "rate_limited",
              "failed",
              "unconfirmed",
              "unavailable",
              "skipped",
              "skipped_sensitive",
              "skipped_query"
            ],
            "description": "De status die in het pakket is verzegeld (metadata.json, forensic_log.json, chain_of_custody.json en evidence.pdf leggen deze vast). Het pakket zelf verandert nooit; gelijk aan status, tenzij een vervolgcontrole een taak in behandeling heeft afgerond."
          },
          "method": {
            "type": "string",
            "nullable": true,
            "enum": [
              "spn2_authenticated",
              "spn_anonymous",
              null
            ]
          },
          "requested_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "snapshot_url": {
            "type": "string",
            "format": "uri",
            "nullable": true,
            "description": "De openbare kopie in de Wayback Machine, altijd https://web.archive.org/web/{snapshot_timestamp}/{url}."
          },
          "snapshot_timestamp": {
            "type": "string",
            "nullable": true,
            "description": "Wayback-tijdstempel YYYYMMDDhhmmss (UTC)."
          },
          "snapshot_offset_seconds": {
            "type": "integer",
            "nullable": true,
            "description": "Tijdstip van de snapshot min het tijdstip van de vastlegging, in seconden (negatief: vóór de vastlegging)."
          },
          "resolved_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Wanneer de vervolgcontrole van de planner het eindresultaat heeft vastgelegd; null als de verzegelde toestand al definitief was."
          },
          "job_id": {
            "type": "string",
            "nullable": true,
            "description": "Job-ID van Save Page Now (geauthenticeerde verzoeken)."
          },
          "follow_up_pending": {
            "type": "boolean",
            "description": "true zolang de planner de taakstatus nog moet lezen."
          },
          "skip_reason": {
            "type": "string",
            "nullable": true,
            "description": "Waarom de URL niet is verzonden, als status skipped, skipped_sensitive of skipped_query is: credentials_in_url, sensitive_path, opaque_path_segment, sensitive_fragment, sensitive_parameter_name, opaque_parameter_value, query_string (of de status zelf bij oudere rijen)."
          },
          "resolved": {
            "type": "object",
            "nullable": true,
            "properties": {
              "status": {
                "type": "string",
                "enum": [
                  "created",
                  "recent_existing",
                  "failed",
                  "rate_limited",
                  "unresolved"
                ]
              },
              "job_status": {
                "type": "string",
                "nullable": true,
                "description": "success, error of pending zoals gelezen uit de Save Page Now-taak, null als die niet kon worden gelezen."
              },
              "snapshot_url": {
                "type": "string",
                "nullable": true,
                "description": "https://web.archive.org/web/<14 digits>/<URL>, null als die ontbreekt of ongeldig is."
              },
              "snapshot_timestamp": {
                "type": "string",
                "nullable": true
              },
              "checked_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "reason": {
                "type": "string",
                "nullable": true,
                "enum": [
                  "no_request_time",
                  "capture_deleted",
                  "capture_failed",
                  null
                ],
                "description": "Waarom er geen antwoord op de taak is: no_request_time (de rij heeft geen verzoektijd om de leesacties vanaf te plannen), capture_deleted of capture_failed (de vastlegging is naar de prullenbak verplaatst of mislukt terwijl de taak in behandeling was, en is daarom niet opgevolgd; een vastlegging die binnen 30 minuten na het verzoek uit de prullenbak wordt teruggezet, wordt opnieuw opgevolgd). Anders null."
              }
            },
            "description": "Het record van de vervolgcontrole (history), als vaste projectie: snapshot_url alleen wanneer het een geldige Wayback-snapshot-URL van snapshot_timestamp is; reason alleen wanneer de vervolgcontrole eindigde zonder antwoord op de taak."
          }
        }
      },
      "CaptureStorage": {
        "type": "object",
        "description": "Bewaartermijn, de dubbele kopie en (bij geplande vastleggingen) de versiegeschiedenis.",
        "properties": {
          "retention": {
            "type": "object",
            "properties": {
              "years": {
                "type": "integer",
                "example": 10
              },
              "counted_from": {
                "type": "string",
                "format": "date-time",
                "nullable": true,
                "description": "Het latere tijdstip van created_at en het laatst bekende gebruik: de laatste controle die de pagina ongewijzigd aantrof, of het moment waarop een nieuwere vastlegging van hetzelfde schema deze verving."
              },
              "earliest_deletion_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true,
                "description": "counted_from plus years, bij benadering: de verwijdering gebeurt bij een latere ronde van de planner, en latere controles kunnen die verschuiven. null onder een legal hold."
              },
              "legal_hold": {
                "type": "boolean"
              }
            }
          },
          "duplicate_copy": {
            "type": "object",
            "properties": {
              "status": {
                "type": "string",
                "enum": [
                  "none",
                  "pending",
                  "stored",
                  "not_copied"
                ],
                "description": "stored: een tweede kopie van het pakket (en het geüpgradede OTS-bewijs) wordt in een aparte bucket in een andere EU-regio bewaard en samen met het origineel verwijderd. pending: wordt gekopieerd (minuten). none: testpakketten en pakketten van vóór het bestaan van de kopie, totdat ze zijn gekopieerd."
              },
              "copied_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              }
            }
          },
          "version": {
            "type": "object",
            "description": "Alleen geplande vastleggingen.",
            "properties": {
              "superseded_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "superseded_by": {
                "type": "string",
                "nullable": true
              },
              "unchanged_first_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true,
                "description": "Eerste controle (on_change) die vaststelde dat de paginatekst sinds deze vastlegging ongewijzigd is."
              },
              "unchanged_last_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "unchanged_checks": {
                "type": "integer",
                "description": "Vastgelegde controles zonder wijziging (maximaal één per uur)."
              }
            }
          }
        }
      },
      "WebhookEventType": {
        "type": "string",
        "enum": [
          "capture.completed",
          "capture.failed",
          "certify.completed",
          "certify.failed",
          "redaction.completed",
          "redaction.failed",
          "monitor.change_detected",
          "capture.anchor_confirmed",
          "schedule.paused"
        ],
        "description": "Een gebeurtenis uit de catalogus (tag Webhooks)."
      },
      "WebhookSubscriptionFilters": {
        "type": "object",
        "description": "Maximaal één van host en schedule_id. Het abonnement krijgt dan alleen de gebeurtenissen van rijen met die host (hostnaam van de vastgelegde URL in kleine letters) of van dat schema. `{}` voor geen filter.",
        "properties": {
          "host": {
            "type": "string",
            "example": "www.example.com"
          },
          "schedule_id": {
            "type": "string",
            "example": "sch_a1B2c3D4e5F6g7H8"
          }
        }
      },
      "WebhookSubscription": {
        "type": "object",
        "properties": {
          "subscription_id": {
            "type": "string",
            "example": "whs_a1B2c3D4e5F6g7H8"
          },
          "url": {
            "type": "string",
            "description": "https-endpoint waarnaar de gebeurtenissen met POST worden verzonden."
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookEventType"
            }
          },
          "filters": {
            "$ref": "#/components/schemas/WebhookSubscriptionFilters"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "disabled"
            ]
          },
          "env": {
            "type": "string",
            "enum": [
              "live",
              "test"
            ],
            "description": "De omgeving van de referentie waarmee het is aangemaakt. Een live webhookabonnement ontvangt alleen de gebeurtenissen van live rijen, een test-webhookabonnement alleen die van testrijen."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_by": {
            "type": "string",
            "enum": [
              "key",
              "session"
            ],
            "description": "key: aangemaakt met een API-sleutel; het intrekken van die sleutel schakelt het abonnement uit (disabled_reason key_revoked)."
          },
          "consecutive_failures": {
            "type": "integer",
            "description": "Mislukte afleveringen sinds de laatste geslaagde."
          },
          "last_success_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "last_failure_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "last_error": {
            "type": "string",
            "nullable": true,
            "description": "Foutklasse van de laatste mislukte aflevering (timeout, connection_failed, blocked_address, redirect_not_followed, http_<status>)."
          },
          "disabled_reason": {
            "type": "string",
            "nullable": true,
            "enum": [
              "gone",
              "failing",
              "key_revoked",
              null
            ],
            "description": "gone: het endpoint antwoordde met 410. failing: elke aflevering is 5 dagen lang mislukt zonder één succes. key_revoked: de API-sleutel waarmee het is aangemaakt, is ingetrokken. PATCH status active heractiveert het."
          },
          "disabled_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        },
        "required": [
          "subscription_id",
          "url",
          "events",
          "filters",
          "description",
          "status",
          "env",
          "created_at",
          "created_by",
          "consecutive_failures",
          "last_success_at",
          "last_failure_at",
          "last_error",
          "disabled_reason",
          "disabled_at"
        ]
      },
      "WebhookSubscriptionCreated": {
        "type": "object",
        "properties": {
          "subscription_id": {
            "type": "string",
            "example": "whs_a1B2c3D4e5F6g7H8"
          },
          "url": {
            "type": "string",
            "description": "https-endpoint waarnaar de gebeurtenissen met POST worden verzonden."
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookEventType"
            }
          },
          "filters": {
            "$ref": "#/components/schemas/WebhookSubscriptionFilters"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "disabled"
            ]
          },
          "env": {
            "type": "string",
            "enum": [
              "live",
              "test"
            ],
            "description": "De omgeving van de referentie waarmee het is aangemaakt. Een live webhookabonnement ontvangt alleen de gebeurtenissen van live rijen, een test-webhookabonnement alleen die van testrijen."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_by": {
            "type": "string",
            "enum": [
              "key",
              "session"
            ],
            "description": "key: aangemaakt met een API-sleutel; het intrekken van die sleutel schakelt het abonnement uit (disabled_reason key_revoked)."
          },
          "secret": {
            "type": "string",
            "description": "whsec_ gevolgd door standaard base64. Wordt EENMAAL getoond: elke aflevering aan dit abonnement wordt ermee ondertekend (Standard Webhooks). Sla het nu op; het kan niet opnieuw worden uitgelezen. Een Idempotency-Key-herhaling binnen 24 uur geeft dezelfde body terug, inclusief secret."
          }
        },
        "required": [
          "subscription_id",
          "url",
          "events",
          "filters",
          "description",
          "status",
          "env",
          "created_at",
          "created_by",
          "secret"
        ]
      },
      "Event": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "evt_a1B2c3D4e5F6g7H8"
          },
          "type": {
            "$ref": "#/components/schemas/WebhookEventType"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "env": {
            "type": "string",
            "enum": [
              "live",
              "test"
            ]
          },
          "data": {
            "type": "object",
            "description": "De body van de gebeurtenis zoals webhookabonnementen die ontvangen, zonder subscription_id en zonder vooraf ondertekende links (package_url, package_url_expires_at, upgraded_proof_url, compare.package_url)."
          }
        },
        "required": [
          "id",
          "type",
          "created_at",
          "env",
          "data"
        ]
      },
      "PrivateNote": {
        "type": "string",
        "nullable": true,
        "maxLength": 200,
        "description": "Uw privénotitie bij de vastlegging (bijvoorbeeld \"mobile capture\"), maximaal 200 tekens, platte tekst (de PlainText-regel). Die wordt alleen in het record van de vastlegging opgeslagen: ze is GEEN onderdeel van het verzegelde bewijspakket (niet in de ZIP, de PDF, het manifest of een hash) en heeft geen bewijskracht, zodat u ze later met PATCH /v1/captures/{capture_id} kunt wijzigen of wissen zonder het bewijs te beïnvloeden. Alleen de maker van de vastlegging kan ze lezen. Laat het veld weg, of stuur null of een lege tekenreeks, voor geen notitie.",
        "example": "mobile capture"
      },
      "CaptureEvent": {
        "type": "object",
        "description": "Eén gebeurtenis uit de gebeurtenissenketen van de vastleggingen van het account. hash = SHA-256 (hex) van de canonieke JSON van de gehashte velden (v, chain, seq, type, capture_id, manifest_sha256, actor, access, detail, at, prev_hash; sleutels gesorteerd, geen witruimte, ontbrekende waarden als null). prev_hash is de hash van de vorige gebeurtenis, 64 nullen voor seq 1.",
        "properties": {
          "v": {
            "type": "integer",
            "enum": [
              1
            ]
          },
          "chain": {
            "type": "string",
            "description": "Het account: de uid voor persoonlijke vastleggingen, org_<org_id> voor de vastleggingen van een bedrijf."
          },
          "seq": {
            "type": "integer",
            "minimum": 1
          },
          "type": {
            "type": "string",
            "enum": [
              "trash",
              "restore",
              "erase",
              "purge",
              "retention_deletion",
              "account_deletion",
              "company_deletion",
              "legal_hold_on",
              "legal_hold_off",
              "s3_legal_hold",
              "import"
            ]
          },
          "capture_id": {
            "type": "string"
          },
          "manifest_sha256": {
            "type": "string",
            "nullable": true,
            "description": "SHA-256 van het manifest.json van de vastlegging, gelezen vóór een eventuele verwijdering."
          },
          "actor": {
            "type": "string",
            "description": "De uid van het handelende account, of system voor de opschoonrondes van de server."
          },
          "access": {
            "type": "string",
            "description": "author, company_owner, matter_owner of system."
          },
          "detail": {
            "type": "string",
            "nullable": true,
            "description": "legal_hold_on / legal_hold_off: de bron van de legal hold (manual, bulk, matter:<id>, derived:<capture_id>), of bij een opheffing door het systeem (actor system) de redencode subscription_ended; s3_legal_hold: het S3-resultaat (on, off, failed, unsupported, none); import: het soort geïmporteerd pakket. Geen vrije tekst."
          },
          "at": {
            "type": "string",
            "format": "date-time",
            "description": "Wanneer de gebeurtenis naar de keten is geschreven. Nooit eerder dan at van de vorige gebeurtenis."
          },
          "prev_hash": {
            "type": "string"
          },
          "hash": {
            "type": "string"
          },
          "occurred_at": {
            "type": "string",
            "format": "date-time",
            "description": "Wanneer de actie zelf is doorgevoerd (de transactie van de statuswijziging). Niet gehasht. Normaal gelijk aan at, binnen milliseconden; alleen later als de server het toevoegen aan de keten opnieuw heeft geprobeerd (de actie wordt in dezelfde transactie als de wijziging vastgelegd, dus er gaat er geen verloren)."
          }
        }
      },
      "CaptureEventAnchor": {
        "type": "object",
        "description": "Een dagelijkse OpenTimestamps-verankering van de keten: head_hash is de hash van gebeurtenis to_seq, die alle eerdere gebeurtenissen vastlegt.",
        "properties": {
          "anchor_id": {
            "type": "string"
          },
          "day": {
            "type": "string",
            "format": "date"
          },
          "from_seq": {
            "type": "integer"
          },
          "to_seq": {
            "type": "integer"
          },
          "head_hash": {
            "type": "string"
          },
          "ots_proof_base64": {
            "type": "string",
            "nullable": true,
            "description": "Het OpenTimestamps-bewijs (.ots-bestand, base64) over head_hash."
          },
          "ots_status": {
            "type": "string",
            "enum": [
              "pending",
              "bitcoin",
              "unconfirmed"
            ],
            "description": "pending: ots_proof_base64 bevat de commitment van de calendars (ots verify raadpleegt de calendars); bitcoin: die is vervangen door het naar het Bitcoin-blok geüpgradede bewijs (bitcoin_block_height); unconfirmed: binnen 14 dagen is geen Bitcoin-bewijs teruggekomen, het opgeslagen bewijs is nog steeds via de calendars verifieerbaar."
          },
          "bitcoin_block_height": {
            "type": "integer",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ImportSummary": {
        "type": "object",
        "description": "De drie niveaus van een geverifieerd pakket.",
        "properties": {
          "kind_label_key": {
            "type": "string",
            "description": "Labelsleutel van het soort pakket, import_kind_<kind>."
          },
          "integrity": {
            "type": "string",
            "enum": [
              "verified",
              "failed"
            ]
          },
          "time_anchor": {
            "type": "string",
            "enum": [
              "eidas_qualified",
              "eidas_qualified_revocation_unknown",
              "ots_bitcoin_attested_unconfirmed",
              "ots_pending",
              "none"
            ]
          },
          "provenance": {
            "type": "string",
            "nullable": true,
            "enum": [
              "issued_by_proofsnap",
              "unverified",
              "registered_by_extension_account",
              "not_registered",
              "unknown",
              null
            ]
          },
          "registered_at": {
            "type": "string",
            "format": "date-time",
            "description": "Wanneer het extensieregister het pakket heeft vastgelegd (provenance registered_by_extension_account)."
          },
          "issued_at": {
            "type": "string",
            "format": "date-time",
            "description": "Wanneer het API-uitgifteregister de uitgifte heeft vastgelegd (provenance issued_by_proofsnap)."
          }
        }
      },
      "Import": {
        "type": "object",
        "properties": {
          "import_id": {
            "type": "string",
            "example": "imp_a1B2c3D4e5F6g7H8i9J0"
          },
          "status": {
            "type": "string",
            "enum": [
              "uploading",
              "verifying",
              "accepted",
              "rejected",
              "duplicate",
              "expired"
            ]
          },
          "filename": {
            "type": "string"
          },
          "size_bytes": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "upload_expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "Alleen bij uploading: voltooi vóór dit tijdstip (24 uur na het aanmaken)."
          },
          "finished_at": {
            "type": "string",
            "format": "date-time",
            "description": "Wanneer de import zijn eindstatus heeft bereikt."
          },
          "capture_id": {
            "type": "string",
            "description": "Alleen bij accepted: de nieuwe vastlegging (dezelfde id als de import)."
          },
          "duplicate_of": {
            "type": "string",
            "description": "Alleen bij duplicate: uw vastlegging die dit manifest al bevat."
          },
          "kind": {
            "type": "string",
            "enum": [
              "web_capture_v21",
              "web_capture_legacy",
              "session",
              "file_certification_single",
              "file_certification_combined",
              "api_capture",
              "api_certify",
              "api_compare",
              "api_redaction"
            ],
            "description": "Het soort pakket, zodra het is gedetecteerd."
          },
          "summary": {
            "$ref": "#/components/schemas/ImportSummary"
          },
          "reasons": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "rejected en expired: redencodes, bijvoorbeeld file_mismatch, file_missing, unlisted_file, bad_signature, no_hashed_files, unknown_schema, zip_too_large, test_package, ots_digest_mismatch, eidas_token_failed, forensic_chain_broken, size_mismatch, upload_not_completed, verification_incomplete. Alleen codes, nooit bestandsnamen."
          },
          "zip_sha256": {
            "type": "string",
            "description": "SHA-256 van de geüploade ZIP, zodra berekend."
          },
          "manifest_sha256": {
            "type": "string",
            "description": "SHA-256 van het manifest.json van het pakket (geaccepteerd en dubbel)."
          },
          "matter_id": {
            "type": "string",
            "description": "De zaak die werd opgegeven toen de import werd aangemaakt."
          },
          "matter_assignment": {
            "type": "string",
            "enum": [
              "assigned",
              "skipped"
            ],
            "description": "Bij accepted met matter_id: of de vastlegging aan de zaak is toegevoegd (overgeslagen als de zaak inmiddels verzegeld of gearchiveerd was of niet meer voor u toegankelijk was; de import blijft accepted)."
          },
          "upload": {
            "type": "object",
            "description": "Alleen met ?upload=true zolang status uploading is.",
            "properties": {
              "upload_id": {
                "type": "string"
              },
              "part_size": {
                "type": "integer"
              },
              "parts": {
                "type": "array",
                "description": "Presigned PUT-URL's van de delen die nog niet zijn ontvangen; PUT precies size_bytes.",
                "items": {
                  "type": "object",
                  "properties": {
                    "part_number": {
                      "type": "integer"
                    },
                    "url": {
                      "type": "string"
                    },
                    "size_bytes": {
                      "type": "integer"
                    }
                  }
                }
              },
              "expires_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "received": {
                "type": "array",
                "nullable": true,
                "items": {
                  "type": "object",
                  "properties": {
                    "part_number": {
                      "type": "integer"
                    },
                    "etag": {
                      "type": "string"
                    },
                    "size_bytes": {
                      "type": "integer"
                    }
                  }
                }
              },
              "completed": {
                "type": "boolean",
                "description": "De upload was al voltooid: stuur complete opnieuw."
              }
            }
          }
        }
      },
      "ImportVerification": {
        "type": "object",
        "description": "Het verificatierapport dat bij de import is vastgelegd (geen bestandsinhoud, geen vrije tekst).",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "kind": {
            "type": "string",
            "enum": [
              "web_capture_v21",
              "web_capture_legacy",
              "session",
              "file_certification_single",
              "file_certification_combined",
              "api_capture",
              "api_certify",
              "api_compare",
              "api_redaction"
            ]
          },
          "format": {
            "type": "string"
          },
          "kind_label_key": {
            "type": "string"
          },
          "evidence_id": {
            "type": "string",
            "description": "De id die het pakket draagt (ps_..., FC-..., cap_..., cert_..., cmp_..., red_...)."
          },
          "manifest_sha256": {
            "type": "string"
          },
          "zip_sha256": {
            "type": "string"
          },
          "anchor_sha256": {
            "type": "string",
            "description": "De hash die de tijdstempels dekken: die van het manifest, of bij een certificering van één bestand die van het gecertificeerde bestand."
          },
          "anchor_source": {
            "type": "string",
            "enum": [
              "manifest",
              "certified_file"
            ]
          },
          "checks": {
            "type": "object",
            "description": "structure, signature, files (listed, verified, presence_only, unhashed, os_junk_ignored), custody, forensic_chain, ots, eidas, test_mode.",
            "additionalProperties": true
          },
          "summary": {
            "$ref": "#/components/schemas/ImportSummary"
          },
          "reasons": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "CaptureImport": {
        "type": "object",
        "description": "Alleen bij een vastlegging met source \"import\".",
        "properties": {
          "import_id": {
            "type": "string"
          },
          "package_kind": {
            "type": "string",
            "enum": [
              "web_capture_v21",
              "web_capture_legacy",
              "session",
              "file_certification_single",
              "file_certification_combined",
              "api_capture",
              "api_certify",
              "api_compare",
              "api_redaction"
            ]
          },
          "kind_label_key": {
            "type": "string"
          },
          "package_format": {
            "type": "string"
          },
          "original_evidence_id": {
            "type": "string"
          },
          "original_filename": {
            "type": "string",
            "nullable": true
          },
          "imported_at": {
            "type": "string",
            "format": "date-time"
          },
          "captured_at_basis": {
            "type": "string",
            "enum": [
              "package_claim"
            ],
            "description": "captured_at van een geïmporteerd pakket is het tijdstip dat het pakket vermeldt, nooit een tijdstip dat ProofSnap heeft waargenomen."
          },
          "manifest_sha256": {
            "type": "string"
          },
          "zip_sha256": {
            "type": "string"
          },
          "summary": {
            "$ref": "#/components/schemas/ImportSummary"
          },
          "levels": {
            "type": "object",
            "properties": {
              "integrity": {
                "type": "string"
              },
              "time_anchor": {
                "type": "string"
              },
              "provenance": {
                "type": "string",
                "nullable": true
              }
            }
          },
          "verification": {
            "$ref": "#/components/schemas/ImportVerification"
          },
          "provenance": {
            "type": "object",
            "properties": {
              "level": {
                "type": "string"
              },
              "source": {
                "type": "string",
                "nullable": true,
                "enum": [
                  "api_ledger",
                  "extension_registry",
                  null
                ]
              },
              "issued_at": {
                "type": "string",
                "nullable": true
              },
              "registered_at": {
                "type": "string",
                "nullable": true
              },
              "reason": {
                "type": "string"
              }
            }
          }
        }
      },
      "Rule902DisabledWarnings": {
        "type": "array",
        "description": "Alleen aanwezig als het verzoek om de certificeringssjabloon vroeg (include_rule_902: true, of bij een vastlegging een niet-lege evidence_pack.custodian) terwijl de sjabloon is uitgeschakeld. Het verzoek is geaccepteerd en de vlag genegeerd: er wordt geen rule_902_certification.pdf gegenereerd.",
        "items": {
          "type": "object",
          "properties": {
            "code": {
              "type": "string",
              "enum": [
                "RULE902_DISABLED"
              ]
            },
            "message": {
              "type": "string",
              "example": "The FRE 902 certification template (rule_902_certification.pdf) is currently disabled. include_rule_902 was accepted and ignored: the package does not contain the template."
            }
          },
          "required": [
            "code",
            "message"
          ]
        }
      }
    }
  }
}
