# Get a single annex Source: https://docs.law4devs.eu/api-reference/annexes/get-a-single-annex /openapi.yaml get /frameworks/{slug}/annexes/{annex_number} # List annexes for a framework Source: https://docs.law4devs.eu/api-reference/annexes/list-annexes-for-a-framework /openapi.yaml get /frameworks/{slug}/annexes # Get a single article Source: https://docs.law4devs.eu/api-reference/articles/get-a-single-article /openapi.yaml get /frameworks/{slug}/articles/{article_number} Returns the full article including its content, ordered paragraphs, and associated tags. # Get related articles and requirements Source: https://docs.law4devs.eu/api-reference/articles/get-related-articles-and-requirements /openapi.yaml get /frameworks/{slug}/articles/{article_number}/related Returns articles and requirements that cross-reference this article via `linked_article_numbers`. Includes both outbound references (articles this one links to) and inbound references (articles linking to this one). # List articles for a framework Source: https://docs.law4devs.eu/api-reference/articles/list-articles-for-a-framework /openapi.yaml get /frameworks/{slug}/articles Returns paginated articles for a framework. Supports optional filtering by tag slug and full-text search within article titles and content. # Content update history for a framework Source: https://docs.law4devs.eu/api-reference/changelog/content-update-history-for-a-framework /openapi.yaml get /frameworks/{slug}/changelog Returns content update log entries for a specific framework, newest first. Use this to check when data was last updated and how many records were added or modified. # Global content update changelog Source: https://docs.law4devs.eu/api-reference/changelog/global-content-update-changelog /openapi.yaml get /changelog Returns content update log entries across all frameworks, newest first. Filter by framework slug or date to scope results. # Compare articles across multiple frameworks Source: https://docs.law4devs.eu/api-reference/compare/compare-articles-across-multiple-frameworks /openapi.yaml get /compare Returns articles grouped by framework slug for side-by-side comparison. Accepts a comma-separated list of framework slugs. Optionally filter articles by tag to focus the comparison on a specific topic. # List all compliance deadlines across all frameworks Source: https://docs.law4devs.eu/api-reference/compliance/list-all-compliance-deadlines-across-all-frameworks /openapi.yaml get /compliance/deadlines Returns all compliance deadlines across all active frameworks, ordered by date. Useful for building compliance calendars. Filter by framework slug to scope to a single regulation. # List compliance deadlines for a framework Source: https://docs.law4devs.eu/api-reference/compliance/list-compliance-deadlines-for-a-framework /openapi.yaml get /frameworks/{slug}/compliance/deadlines Returns all compliance deadlines for the framework, ordered by date. # Get framework detail Source: https://docs.law4devs.eu/api-reference/frameworks/get-framework-detail /openapi.yaml get /frameworks/{slug} Returns full framework metadata including counts (articles, recitals, requirements, annexes, tags) and coverage percentages. # List all active frameworks Source: https://docs.law4devs.eu/api-reference/frameworks/list-all-active-frameworks /openapi.yaml get /frameworks Returns all active EU regulatory frameworks ordered by slug. # Health check Source: https://docs.law4devs.eu/api-reference/health/health-check /openapi.yaml get /health Returns API status and database connectivity. # Get a single recital Source: https://docs.law4devs.eu/api-reference/recitals/get-a-single-recital /openapi.yaml get /frameworks/{slug}/recitals/{recital_number} # List recitals for a framework Source: https://docs.law4devs.eu/api-reference/recitals/list-recitals-for-a-framework /openapi.yaml get /frameworks/{slug}/recitals Returns paginated recitals (preamble paragraphs) ordered by recital number. # List requirements across all frameworks Source: https://docs.law4devs.eu/api-reference/requirements/list-requirements-across-all-frameworks /openapi.yaml get /requirements Cross-framework requirements endpoint. Returns all requirements from all active frameworks. Optionally filter to a single framework, stakeholder role, type, or tag. # List requirements for a framework Source: https://docs.law4devs.eu/api-reference/requirements/list-requirements-for-a-framework /openapi.yaml get /frameworks/{slug}/requirements Returns paginated compliance requirements extracted from a framework's articles. Filterable by stakeholder role, requirement type, and tag. # Full-text search across articles, recitals, and requirements Source: https://docs.law4devs.eu/api-reference/search/full-text-search-across-articles-recitals-and-requirements /openapi.yaml get /search Searches article titles and content, recital content, and requirement text across all active frameworks. Returns ranked results with context snippets. Supports filtering by framework, tag, stakeholder role, and result type. Query must be at least 2 characters and at most 500 characters. # Coverage statistics for a single framework Source: https://docs.law4devs.eu/api-reference/stats/coverage-statistics-for-a-single-framework /openapi.yaml get /frameworks/{slug}/stats Returns article, recital, requirement, annex, and tag counts for the framework, including coverage percentages vs. expected totals. # Coverage statistics for all active frameworks Source: https://docs.law4devs.eu/api-reference/stats/coverage-statistics-for-all-active-frameworks /openapi.yaml get /stats Returns stats for every active framework in one call. # Get tag detail Source: https://docs.law4devs.eu/api-reference/tags/get-tag-detail /openapi.yaml get /tags/{slug} Returns a single tag with its keywords and metadata. # List all tags Source: https://docs.law4devs.eu/api-reference/tags/list-all-tags /openapi.yaml get /tags Returns all semantic tags used to classify articles and requirements. # Changelog Source: https://docs.law4devs.eu/changelog API and SDK release history. ## v1.0.2 — March 2026 * **Java SDK** `1.0.2` — `Requirement` and `ComplianceDeadline` models corrected to match actual API response fields * **Rust SDK** `1.0.2` — `ComplianceDeadline` model corrected; added deserializer to handle `article_number` returned as integer * **Flutter SDK** `1.0.2` — `ComplianceDeadline` and `Requirement` models corrected to match actual API response fields ## v1.0.0 — March 2026 * **PHP SDK** (`law4devs/law4devs`) published to Packagist — PHP 8.1+, Composer, zero extra dependencies * **Java SDK** (`eu.law4devs:law4devs-sdk`) published to Maven Central — Java 17+, zero dependencies * **Flutter SDK** (`law4devs`) published to pub.dev — Dart 3+, Flutter & CLI, Stream-based auto-pagination * **Rust SDK** (`law4devs`) published to crates.io — Rust 1.85+, async Tokio runtime * **ReDoc** API reference now available at `https://api.law4devs.eu/redoc` — clean read-only view ideal for sharing with stakeholders ## v1.0.0 — 2026 * **19 EU frameworks** available (CRA, GDPR, NIS2, DORA, AI Act, eIDAS 2.0, DSA, DMA, Data Act, DGA, MiCAR, and more) * **21 REST API endpoints** covering frameworks, articles, recitals, annexes, requirements, compliance deadlines, tags, search, stats, compare, and changelog * **Python SDK** (`law4devs`) published to PyPI * **TypeScript SDK** (`law4devs`) published to npm * OpenAPI 3.0.3 spec available at `/v1/openapi.json` * Interactive Swagger UI at `https://api.law4devs.eu/docs` * Rate limiting: 100 requests/minute per IP * CORS enabled for all origins on GET/OPTIONS # OpenAPI Spec Source: https://docs.law4devs.eu/developer-tools/openapi-spec Machine-readable OpenAPI 3.0.3 specification for import and code generation The Law4Devs API is fully described in an OpenAPI 3.0.3 specification. You can use this to import the API into your preferred HTTP client, generate typed SDK code, or run automated contract tests. ## Access the Spec | Format | URL | | ------ | ----------------------------------------- | | JSON | `https://api.law4devs.eu/v1/openapi.json` | ```bash theme={null} # Download the JSON spec curl https://api.law4devs.eu/v1/openapi.json -o law4devs-openapi.json ``` ## Import into API Clients 1. Open Postman and click **Import** 2. Select **Link** and paste `https://api.law4devs.eu/v1/openapi.json` 3. Click **Import** — all endpoints appear as a new collection 4. Set the `X-API-Key` variable in the collection variables to your API key 1. Click **Create** → **Import from URL** 2. Paste `https://api.law4devs.eu/v1/openapi.json` 3. Click **Fetch and Import** 4. Add `X-API-Key` as a header in the collection settings 1. In your collection, click **...** → **Import** 2. Choose **OpenAPI** and paste the spec URL or upload the downloaded file 3. Bruno generates requests for all endpoints automatically ## Code Generation Use [openapi-generator](https://openapi-generator.tech/) to generate a typed client in any language from the spec: ```bash theme={null} # Generate a Python client openapi-generator generate \ -i https://api.law4devs.eu/v1/openapi.json \ -g python \ -o ./generated-client # Generate a Go client openapi-generator generate \ -i https://api.law4devs.eu/v1/openapi.json \ -g go \ -o ./generated-client ``` The official SDKs (Python, TypeScript, PHP) are hand-crafted and include features like auto-pagination and retry logic that generated clients do not provide. Use code generation only if your language isn't covered by an official SDK. ## Spec Details | Property | Value | | --------------- | --------------------------------------- | | Spec version | OpenAPI 3.0.3 | | API version | 1.0 | | Total endpoints | 21 | | Authentication | `X-API-Key` header | | Response format | JSON | | Pagination | Cursor-based with `page` and `per_page` | ## Versioning The current API version is `1.0`. Every response includes the `X-API-Version` header so your application can detect version changes programmatically. Breaking changes will increment the major version and be announced in the [Changelog](/changelog). # Developer Tools Source: https://docs.law4devs.eu/developer-tools/overview Interactive API explorer, spec viewer, and machine-readable OpenAPI documentation Law4Devs ships with a complete set of developer tools — from an interactive API explorer you can use to try any endpoint right in your browser, to a machine-readable OpenAPI spec you can import into Postman, Insomnia, or a code generator. Interactive API explorer with live request testing and authentication support. Clean, read-only API reference — ideal for sharing with stakeholders. Machine-readable OpenAPI 3.0.3 spec for Postman, Insomnia, and code generation. ## Quick Links | Tool | URL | Use For | | ------------ | ----------------------------------------- | ------------------- | | Swagger UI | `https://api.law4devs.eu/docs` | Interactive testing | | ReDoc | `https://api.law4devs.eu/redoc` | Reading & sharing | | OpenAPI JSON | `https://api.law4devs.eu/v1/openapi.json` | Import into tools | # ReDoc Source: https://docs.law4devs.eu/developer-tools/redoc Clean, read-only API reference documentation ReDoc renders the same OpenAPI spec as Swagger UI, but as a clean three-panel reference document — ideal for reading carefully, printing, or sharing with non-technical stakeholders. **URL:** `https://api.law4devs.eu/redoc` ## Swagger UI vs ReDoc | | Swagger UI | ReDoc | | ------------------------------- | :---------------: | :---------------: | | Interactive "Try it out" | ✓ | — | | Read request/response schemas | ✓ | ✓ | | Clean print / PDF export | — | ✓ | | Deep-link to specific endpoints | — | ✓ | | Best for | Engineers testing | Reviews & sharing | ## Navigating ReDoc ReDoc organises endpoints in a left sidebar grouped by tag. Click any endpoint to jump directly to its documentation. The right column always shows example request and response payloads. **Direct link to an endpoint:** append the anchor to the URL, for example: ``` https://api.law4devs.eu/redoc#tag/Frameworks/operation/list_frameworks ``` ## Sharing with Stakeholders ReDoc is the recommended view to share with compliance officers, legal teams, or product managers who need to understand the API without running code. The layout is clean and printable — use your browser's Print → Save as PDF for a permanent offline copy. # Swagger UI Source: https://docs.law4devs.eu/developer-tools/swagger Interactively explore and test all Law4Devs API endpoints in your browser Swagger UI provides a browser-based interface where you can browse every endpoint, read its documentation, and make live API calls — all without writing any code. **URL:** `https://api.law4devs.eu/docs` ## Authenticating in Swagger UI Before you can use "Try it out", you need to provide your API key: Navigate to `https://api.law4devs.eu/docs` in your browser. Click the **Authorize** button (lock icon) in the top-right of the page. In the dialog that appears, paste your API key into the `X-API-Key` field and click **Authorize**. ``` X-API-Key: YOUR_API_KEY ``` Close the dialog. Expand any endpoint, click **Try it out**, fill in any parameters, and hit **Execute**. ## Example: Listing Frameworks 1. Expand `GET /v1/frameworks` 2. Click **Try it out** 3. Set `per_page` to `5` 4. Click **Execute** You'll see the full request URL, the `curl` command you can copy, and the live JSON response directly below. ## When to Use Swagger UI Try endpoints before writing SDK code — verify responses and parameters quickly. Reproduce API issues interactively to isolate whether the problem is client-side. Discover all available endpoints and their parameters in one place. Share a specific endpoint with a colleague by copying the URL from the address bar. # Supported Frameworks Source: https://docs.law4devs.eu/frameworks All 19 EU regulatory frameworks available in the Law4Devs API. Use these slugs in API calls and SDK methods: `client.articles.list("gdpr")`, `/frameworks/cra/articles`, etc. ## Framework List | Slug | Name | CELEX | Status | Documentation | | ---------------------------------------------------- | -------------------------------------------- | ---------- | ---------- | ------------------------------------------ | | [`cra`](/frameworks/cra) | Cyber Resilience Act | 32024R2847 | Active | [View Docs](/frameworks/cra) | | [`nis2`](/frameworks/nis2) | NIS2 Directive | 32022L2555 | Active | [View Docs](/frameworks/nis2) | | [`dora`](/frameworks/dora) | Digital Operational Resilience Act | 32022R2554 | Active | [View Docs](/frameworks/dora) | | [`gdpr`](/frameworks/gdpr) | General Data Protection Regulation | 32016R0679 | Active | [View Docs](/frameworks/gdpr) | | [`ai_act`](/frameworks/ai_act) | AI Act | 32024R1689 | Active | [View Docs](/frameworks/ai_act) | | [`eidas`](/frameworks/eidas) | eIDAS Regulation | 32014R0910 | Active | [View Docs](/frameworks/eidas) | | [`dsa`](/frameworks/dsa) | Digital Services Act | 32022R2065 | Active | [View Docs](/frameworks/dsa) | | [`dma`](/frameworks/dma) | Digital Markets Act | 32022R1925 | Active | [View Docs](/frameworks/dma) | | [`data_act`](/frameworks/data_act) | Data Act | 32023R2854 | Active | [View Docs](/frameworks/data_act) | | [`dga`](/frameworks/dga) | Data Governance Act | 32022R0868 | Active | [View Docs](/frameworks/dga) | | [`eidas2`](/frameworks/eidas2) | eIDAS 2.0 | 32024R1183 | Active | [View Docs](/frameworks/eidas2) | | [`cer`](/frameworks/cer) | Critical Entities Resilience Directive | 32022L2557 | Active | [View Docs](/frameworks/cer) | | [`psd2`](/frameworks/psd2) | Payment Services Directive 2 | 32015L2366 | Active | [View Docs](/frameworks/psd2) | | [`mica`](/frameworks/mica) | Markets in Crypto-Assets Regulation | 32023R1114 | Active | [View Docs](/frameworks/mica) | | [`cybersecurity_act`](/frameworks/cybersecurity_act) | Cybersecurity Act | 32019R0881 | Active | [View Docs](/frameworks/cybersecurity_act) | | [`eprivacy`](/frameworks/eprivacy) | ePrivacy Directive | 32002L0058 | Active | [View Docs](/frameworks/eprivacy) | | [`red`](/frameworks/red) | Radio Equipment Directive | 32014L0053 | Active | [View Docs](/frameworks/red) | | [`csrd`](/frameworks/csrd) | Corporate Sustainability Reporting Directive | 32022L2464 | Active | [View Docs](/frameworks/csrd) | | [`nis1`](/frameworks/nis1) | NIS Directive (Original) | 32016L1148 | Superseded | [View Docs](/frameworks/nis1) | `nis1` has `status: "superseded"` — it is still accessible for historical reference but NIS2 is the current directive. ## Stakeholder Roles Across all frameworks, the following stakeholder roles are defined: | Role | Description | | --------------------------- | ------------------------------------------------------------------------ | | `manufacturer` | Entities that develop, produce, or place products/services on the market | | `importer` | Entities that bring products/services from third countries into the EU | | `distributor` | Entities that make products/services available in the EU supply chain | | `authorized_representative` | EU-based representatives for non-EU entities | | `open_source_steward` | Maintainers of open source components (CRA-specific) | | `notified_body` | Designated conformity assessment bodies | | `market_surveillance` | National competent authorities overseeing compliance | | `consumer` | End users, data subjects, or recipients of services | ## Requirement Types Requirements across frameworks are classified into these types: | Type | Description | | ------------- | ---------------------------------------------------------------- | | `obligation` | Mandatory actions that must be taken | | `prohibition` | Actions or practices that are forbidden | | `right` | Entitlements granted to stakeholders (e.g., data subject rights) | | `procedure` | Processes that must be followed (e.g., incident reporting) | | `general` | General provisions, definitions, and scope | ## Using Slugs ```bash theme={null} # Get all CRA articles curl https://api.law4devs.eu/v1/frameworks/cra/articles # Get GDPR framework details curl https://api.law4devs.eu/v1/frameworks/gdpr # Get AI Act requirements by stakeholder role curl https://api.law4devs.eu/v1/frameworks/ai_act/requirements?role=manufacturer # Compare NIS2 and DORA curl "https://api.law4devs.eu/v1/compare?frameworks=nis2,dora" ``` ```python theme={null} # Python SDK example from law4devs import Law4Devs client = Law4Devs(api_key="your-api-key") # List articles from multiple frameworks cra_articles = client.articles.list("cra") gdpr_articles = client.articles.list("gdpr") # Get requirements by stakeholder role requirements = client.requirements.list("ai_act", role="manufacturer") # Search across frameworks results = client.search(query="incident reporting", frameworks=["nis2", "dora", "cra"]) ``` # AI Act Source: https://docs.law4devs.eu/frameworks/ai_act Regulation (EU) 2024/1689 laying down harmonised rules on artificial intelligence. ## Overview | Field | Value | | ------------ | ---------------------------------------------------------------------------------------------- | | **CELEX** | 32024R1689 | | **Status** | Active | | **Articles** | 113 | | **Recitals** | 180 | | **EUR-Lex** | [View Official Text](https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:32024R1689) | ## Compliance Deadlines | Date | Type | Description | | ---------- | ------------------- | ------------------------------------ | | 2024-08-01 | Entry into force | Regulation enters into force | | 2025-02-02 | Partial application | Prohibited AI systems rules apply | | 2027-08-02 | Full application | Full application of all requirements | ## Stakeholder Roles The AI Act defines obligations for the following stakeholder roles: * **manufacturer** - Providers of AI systems (developers, vendors) * **importer** - Entities placing AI systems from third countries on EU market * **distributor** - Entities making AI systems available in the EU * **authorized\_representative** - EU representatives for non-EU providers * **notified\_body** - Conformity assessment bodies for high-risk AI * **market\_surveillance** - National competent authorities * **consumer** - Users and persons affected by AI systems ## Requirement Types Requirements in the AI Act are classified as: * **obligation** - Mandatory requirements for AI system providers * **prohibition** - Prohibited AI practices (unacceptable risk) * **right** - Rights of persons affected by AI systems * **procedure** - Conformity assessment and post-market monitoring procedures * **general** - General provisions and definitions ## API Usage ```bash theme={null} # Get all AI Act articles curl https://api.law4devs.eu/v1/frameworks/ai_act/articles # Get AI Act framework details curl https://api.law4devs.eu/v1/frameworks/ai_act # Get AI Act requirements curl https://api.law4devs.eu/v1/frameworks/ai_act/requirements # Get AI Act recitals curl https://api.law4devs.eu/v1/frameworks/ai_act/recitals ``` ```python theme={null} # Python SDK from law4devs import Law4Devs client = Law4Devs(api_key="your-api-key") # List all AI Act articles articles = client.articles.list("ai_act") # Get AI Act requirements by stakeholder role requirements = client.requirements.list("ai_act", role="manufacturer") # Search AI Act articles by tag articles = client.articles.search("ai_act", query="high-risk") ``` ```typescript theme={null} // TypeScript SDK import { Law4Devs } from '@law4devs/sdk'; const client = new Law4Devs({ apiKey: 'your-api-key' }); // List all AI Act articles const articles = await client.articles.list('ai_act'); // Get AI Act requirements by stakeholder role const requirements = await client.requirements.list('ai_act', { role: 'manufacturer' }); ``` ## Key Topics * Prohibited AI practices (unacceptable risk) * High-risk AI system classification and requirements * General-purpose AI models and GPAI obligations * Transparency requirements for certain AI systems * Conformity assessment and CE marking * Post-market monitoring and incident reporting * AI regulatory sandboxes and testing # Critical Entities Resilience Directive (CER) Source: https://docs.law4devs.eu/frameworks/cer Directive (EU) 2022/2557 on the Resilience of Critical Entities. ## Overview | Field | Value | | ------------ | ---------------------------------------------------------------------------------------------- | | **CELEX** | 32022L2557 | | **Status** | Active | | **Articles** | 29 | | **Recitals** | 45 | | **EUR-Lex** | [View Official Text](https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:32022L2557) | ## Compliance Deadlines | Date | Type | Description | | ---------- | ---------------- | ------------------------------------ | | 2022-12-27 | Entry into force | Directive enters into force | | 2024-10-17 | Transposition | Member States transposition deadline | ## Stakeholder Roles The CER Directive defines obligations for the following stakeholder roles: * **manufacturer** - Providers of security solutions for critical entities * **importer** - Entities bringing security services into the EU * **distributor** - Service providers supporting critical infrastructure * **authorized\_representative** - EU representatives for non-EU providers * **notified\_body** - Bodies for resilience assessment * **market\_surveillance** - National competent authorities * **consumer** - Recipients of essential services ## Requirement Types Requirements in CER are classified as: * **obligation** - Mandatory resilience and risk assessment requirements * **prohibition** - Restricted practices affecting critical infrastructure * **right** - Rights of critical entities and service recipients * **procedure** - Incident notification and reporting procedures * **general** - General provisions and definitions ## API Usage ```bash theme={null} # Get all CER articles curl https://api.law4devs.eu/v1/frameworks/cer/articles # Get CER framework details curl https://api.law4devs.eu/v1/frameworks/cer # Get CER requirements curl https://api.law4devs.eu/v1/frameworks/cer/requirements # Get CER recitals curl https://api.law4devs.eu/v1/frameworks/cer/recitals ``` ```python theme={null} # Python SDK from law4devs import Law4Devs client = Law4Devs(api_key="your-api-key") # List all CER articles articles = client.articles.list("cer") # Get CER requirements by stakeholder role requirements = client.requirements.list("cer", role="manufacturer") # Search CER articles by tag articles = client.articles.search("cer", query="critical infrastructure") ``` ```typescript theme={null} // TypeScript SDK import { Law4Devs } from '@law4devs/sdk'; const client = new Law4Devs({ apiKey: 'your-api-key' }); // List all CER articles const articles = await client.articles.list('cer'); // Get CER requirements by stakeholder role const requirements = await client.requirements.list('cer', { role: 'manufacturer' }); ``` ## Key Topics * Critical entity identification and designation * Risk assessment and resilience measures * Physical and digital security requirements * Incident notification obligations * Cross-border cooperation and information exchange * Supervision and enforcement mechanisms * Relationship with NIS2 Directive (sister legislation) # Cyber Resilience Act (CRA) Source: https://docs.law4devs.eu/frameworks/cra Regulation (EU) 2024/2847 on horizontal cybersecurity requirements for products with digital elements. ## Overview | Field | Value | | ------------ | ---------------------------------------------------------------------------------------------- | | **CELEX** | 32024R2847 | | **Status** | Active | | **Articles** | 71 | | **Recitals** | 130 | | **EUR-Lex** | [View Official Text](https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:32024R2847) | ## Compliance Deadlines | Date | Type | Description | | ---------- | ------------------- | ------------------------------------------ | | 2024-12-10 | Entry into force | Regulation enters into force | | 2026-06-11 | Partial application | Chapter IV applies (Reporting obligations) | | 2026-09-11 | Partial application | Article 14 applies (Delegated acts) | | 2027-12-11 | Full application | Full application of all requirements | ## Stakeholder Roles The CRA defines obligations for the following stakeholder roles: * **manufacturer** - Places products with digital elements on the EU market * **importer** - Imports products from third countries into the EU * **distributor** - Makes products available in the EU supply chain * **authorized\_representative** - Acts on behalf of manufacturers * **open\_source\_steward** - Manages open source components * **notified\_body** - Conformity assessment bodies * **market\_surveillance** - National authorities overseeing compliance * **consumer** - End users of products with digital elements ## Requirement Types Requirements in the CRA are classified as: * **obligation** - Mandatory actions that must be taken * **prohibition** - Actions that are forbidden * **right** - Entitlements granted to stakeholders * **procedure** - Processes that must be followed * **general** - General provisions and definitions ## API Usage ```bash theme={null} # Get all CRA articles curl https://api.law4devs.eu/v1/frameworks/cra/articles # Get CRA framework details curl https://api.law4devs.eu/v1/frameworks/cra # Get CRA requirements curl https://api.law4devs.eu/v1/frameworks/cra/requirements # Get CRA recitals curl https://api.law4devs.eu/v1/frameworks/cra/recitals ``` ```python theme={null} # Python SDK from law4devs import Law4Devs client = Law4Devs(api_key="your-api-key") # List all CRA articles articles = client.articles.list("cra") # Get CRA requirements by stakeholder role requirements = client.requirements.list("cra", role="manufacturer") # Search CRA articles by tag articles = client.articles.search("cra", query="vulnerability") ``` ```typescript theme={null} // TypeScript SDK import { Law4Devs } from '@law4devs/sdk'; const client = new Law4Devs({ apiKey: 'your-api-key' }); // List all CRA articles const articles = await client.articles.list('cra'); // Get CRA requirements by stakeholder role const requirements = await client.requirements.list('cra', { role: 'manufacturer' }); ``` ## Key Topics * Essential requirements for products with digital elements * Vulnerability handling and disclosure * CE marking and conformity assessment * Market surveillance and enforcement * Supply chain security obligations # Corporate Sustainability Reporting Directive (CSRD) Source: https://docs.law4devs.eu/frameworks/csrd Directive (EU) 2022/2464 on Corporate Sustainability Reporting. ## Overview | Field | Value | | ------------ | ---------------------------------------------------------------------------------------------- | | **CELEX** | 32022L2464 | | **Status** | Active | | **Articles** | 8 | | **Recitals** | 84 | | **EUR-Lex** | [View Official Text](https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:32022L2464) | ## Compliance Deadlines | Date | Type | Description | | ---------- | ------------------- | ------------------------------------------------------- | | 2023-01-05 | Entry into force | Directive enters into force | | 2025-01-01 | Partial application | Large public-interest entities (FY2024 reporting) apply | | 2026-01-01 | Partial application | Large companies (FY2025 reporting) apply | | 2027-01-01 | Partial application | Listed SMEs (FY2026 reporting) apply | ## Stakeholder Roles The CSRD defines obligations for the following stakeholder roles: * **manufacturer** - Developers of sustainability reporting software and tools * **importer** - Non-EU companies with significant EU operations * **distributor** - Assurance service providers for sustainability data * **authorized\_representative** - EU representatives for non-EU undertakings * **notified\_body** - Independent assurance service providers * **market\_surveillance** - National competent authorities * **consumer** - Investors and stakeholders using sustainability information ## Requirement Types Requirements in CSRD are classified as: * **obligation** - Mandatory sustainability reporting requirements * **prohibition** - Restricted reporting practices and greenwashing * **right** - Rights of stakeholders to access sustainability information * **procedure** - Assurance and digital tagging procedures * **general** - General provisions and definitions ## API Usage ```bash theme={null} # Get all CSRD articles curl https://api.law4devs.eu/v1/frameworks/csrd/articles # Get CSRD framework details curl https://api.law4devs.eu/v1/frameworks/csrd # Get CSRD requirements curl https://api.law4devs.eu/v1/frameworks/csrd/requirements # Get CSRD recitals curl https://api.law4devs.eu/v1/frameworks/csrd/recitals ``` ```python theme={null} # Python SDK from law4devs import Law4Devs client = Law4Devs(api_key="your-api-key") # List all CSRD articles articles = client.articles.list("csrd") # Get CSRD requirements by stakeholder role requirements = client.requirements.list("csrd", role="manufacturer") # Search CSRD articles by tag articles = client.articles.search("csrd", query="ESG") ``` ```typescript theme={null} // TypeScript SDK import { Law4Devs } from '@law4devs/sdk'; const client = new Law4Devs({ apiKey: 'your-api-key' }); // List all CSRD articles const articles = await client.articles.list('csrd'); // Get CSRD requirements by stakeholder role const requirements = await client.requirements.list('csrd', { role: 'manufacturer' }); ``` ## Key Topics * Sustainability reporting scope and applicability * European Sustainability Reporting Standards (ESRS) * Double materiality assessment (impact and financial) * Environmental, Social, and Governance (ESG) disclosures * Digital tagging and machine-readable format (XHTML) * Limited assurance requirements * Value chain reporting (upstream and downstream) * Non-EU company reporting obligations # Cybersecurity Act (CSA) Source: https://docs.law4devs.eu/frameworks/cybersecurity_act Regulation (EU) 2019/881 on ENISA and on ICT Cybersecurity Certification. ## Overview | Field | Value | | ------------ | ---------------------------------------------------------------------------------------------- | | **CELEX** | 32019R0881 | | **Status** | Active | | **Articles** | 69 | | **Recitals** | 110 | | **EUR-Lex** | [View Official Text](https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:32019R0881) | ## Compliance Deadlines | Date | Type | Description | | ---------- | ---------------- | ------------------------------------------------- | | 2019-06-27 | Entry into force | Regulation enters into force and full application | ## Stakeholder Roles The Cybersecurity Act defines obligations for the following stakeholder roles: * **manufacturer** - ICT product and service providers seeking certification * **importer** - Entities bringing certified ICT products into the EU * **distributor** - Providers of certified cybersecurity services * **authorized\_representative** - EU representatives for non-EU manufacturers * **notified\_body** - Conformity assessment bodies for cybersecurity certification * **market\_surveillance** - National certification authorities and ENISA * **consumer** - Users of certified ICT products and services ## Requirement Types Requirements in the Cybersecurity Act are classified as: * **obligation** - Mandatory requirements for certification schemes * **prohibition** - Restricted certification claims and practices * **right** - Rights of certificate holders and users * **procedure** - Certification and peer review procedures * **general** - General provisions and definitions ## API Usage ```bash theme={null} # Get all Cybersecurity Act articles curl https://api.law4devs.eu/v1/frameworks/cybersecurity_act/articles # Get Cybersecurity Act framework details curl https://api.law4devs.eu/v1/frameworks/cybersecurity_act # Get Cybersecurity Act requirements curl https://api.law4devs.eu/v1/frameworks/cybersecurity_act/requirements # Get Cybersecurity Act recitals curl https://api.law4devs.eu/v1/frameworks/cybersecurity_act/recitals ``` ```python theme={null} # Python SDK from law4devs import Law4Devs client = Law4Devs(api_key="your-api-key") # List all Cybersecurity Act articles articles = client.articles.list("cybersecurity_act") # Get Cybersecurity Act requirements by stakeholder role requirements = client.requirements.list("cybersecurity_act", role="manufacturer") # Search Cybersecurity Act articles by tag articles = client.articles.search("cybersecurity_act", query="certification") ``` ```typescript theme={null} // TypeScript SDK import { Law4Devs } from '@law4devs/sdk'; const client = new Law4Devs({ apiKey: 'your-api-key' }); // List all Cybersecurity Act articles const articles = await client.articles.list('cybersecurity_act'); // Get Cybersecurity Act requirements by stakeholder role const requirements = await client.requirements.list('cybersecurity_act', { role: 'manufacturer' }); ``` ## Key Topics * ENISA (European Union Agency for Cybersecurity) mandate and governance * European cybersecurity certification schemes * ICT product, service, and process certification * Assurance levels (basic, substantial, high) * Mutual recognition of certificates across Member States * Peer review mechanisms for certification bodies * Relationship with sector-specific regulations # Data Act Source: https://docs.law4devs.eu/frameworks/data_act Regulation (EU) 2023/2854 on Harmonised Rules on Fair Access to and Use of Data. ## Overview | Field | Value | | ------------ | ---------------------------------------------------------------------------------------------- | | **CELEX** | 32023R2854 | | **Status** | Active | | **Articles** | 50 | | **Recitals** | 120 | | **EUR-Lex** | [View Official Text](https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:32023R2854) | ## Compliance Deadlines | Date | Type | Description | | ---------- | ---------------- | ------------------------------------ | | 2024-01-11 | Entry into force | Regulation enters into force | | 2025-09-12 | Full application | Full application of all requirements | ## Stakeholder Roles The Data Act defines obligations for the following stakeholder roles: * **manufacturer** - Manufacturers of connected products and IoT devices * **importer** - Entities placing connected products on the EU market * **distributor** - Providers of related services * **authorized\_representative** - EU representatives for non-EU manufacturers * **notified\_body** - Bodies for conformity assessment * **market\_surveillance** - National competent authorities * **consumer** - Data holders and users of connected products ## Requirement Types Requirements in the Data Act are classified as: * **obligation** - Mandatory data access and sharing requirements * **prohibition** - Restrictions on data use and contractual terms * **right** - Rights of data holders (users) to access and share data * **procedure** - Data sharing request and compensation procedures * **general** - General provisions and definitions ## API Usage ```bash theme={null} # Get all Data Act articles curl https://api.law4devs.eu/v1/frameworks/data_act/articles # Get Data Act framework details curl https://api.law4devs.eu/v1/frameworks/data_act # Get Data Act requirements curl https://api.law4devs.eu/v1/frameworks/data_act/requirements # Get Data Act recitals curl https://api.law4devs.eu/v1/frameworks/data_act/recitals ``` ```python theme={null} # Python SDK from law4devs import Law4Devs client = Law4Devs(api_key="your-api-key") # List all Data Act articles articles = client.articles.list("data_act") # Get Data Act requirements by stakeholder role requirements = client.requirements.list("data_act", role="manufacturer") # Search Data Act articles by tag articles = client.articles.search("data_act", query="data sharing") ``` ```typescript theme={null} // TypeScript SDK import { Law4Devs } from '@law4devs/sdk'; const client = new Law4Devs({ apiKey: 'your-api-key' }); // List all Data Act articles const articles = await client.articles.list('data_act'); // Get Data Act requirements by stakeholder role const requirements = await client.requirements.list('data_act', { role: 'manufacturer' }); ``` ## Key Topics * User rights to access and use IoT-generated data * Data holder and data recipient obligations * Fair, reasonable, and non-discriminatory (FRAND) terms * Switching between data processing services * Interoperability requirements for connected products * Public sector data access in exceptional circumstances * Protection of trade secrets and confidential information * Unfair contractual terms protection for SMEs # Data Governance Act (DGA) Source: https://docs.law4devs.eu/frameworks/dga Regulation (EU) 2022/868 on European Data Governance. ## Overview | Field | Value | | ------------ | ---------------------------------------------------------------------------------------------- | | **CELEX** | 32022R0868 | | **Status** | Active | | **Articles** | 38 | | **Recitals** | 63 | | **EUR-Lex** | [View Official Text](https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:32022R0868) | ## Compliance Deadlines | Date | Type | Description | | ---------- | ---------------- | ------------------------------------ | | 2022-06-23 | Entry into force | Regulation enters into force | | 2023-09-24 | Full application | Full application of all requirements | ## Stakeholder Roles The DGA defines obligations for the following stakeholder roles: * **manufacturer** - Developers of data intermediation services * **importer** - Entities bringing data services into the EU * **distributor** - Data intermediation service providers * **authorized\_representative** - EU representatives for non-EU providers * **notified\_body** - Bodies for monitoring data intermediation services * **market\_surveillance** - National competent authorities * **consumer** - Data subjects and data holders ## Requirement Types Requirements in the DGA are classified as: * **obligation** - Mandatory requirements for data intermediaries * **prohibition** - Restrictions on data use and governance practices * **right** - Rights of data holders and data users * **procedure** - Registration and notification procedures * **general** - General provisions and definitions ## API Usage ```bash theme={null} # Get all DGA articles curl https://api.law4devs.eu/v1/frameworks/dga/articles # Get DGA framework details curl https://api.law4devs.eu/v1/frameworks/dga # Get DGA requirements curl https://api.law4devs.eu/v1/frameworks/dga/requirements # Get DGA recitals curl https://api.law4devs.eu/v1/frameworks/dga/recitals ``` ```python theme={null} # Python SDK from law4devs import Law4Devs client = Law4Devs(api_key="your-api-key") # List all DGA articles articles = client.articles.list("dga") # Get DGA requirements by stakeholder role requirements = client.requirements.list("dga", role="manufacturer") # Search DGA articles by tag articles = client.articles.search("dga", query="data intermediary") ``` ```typescript theme={null} // TypeScript SDK import { Law4Devs } from '@law4devs/sdk'; const client = new Law4Devs({ apiKey: 'your-api-key' }); // List all DGA articles const articles = await client.articles.list('dga'); // Get DGA requirements by stakeholder role const requirements = await client.requirements.list('dga', { role: 'manufacturer' }); ``` ## Key Topics * Re-use of public sector data (with restrictions) * Data intermediation services and neutrality requirements * Data altruism organizations and registration * European Data Innovation Board * Cross-sector data governance frameworks * Technical infrastructure for data sharing * Protection of commercially sensitive information # Digital Markets Act (DMA) Source: https://docs.law4devs.eu/frameworks/dma Regulation (EU) 2022/1925 on Contestable and Fair Markets in the Digital Sector. ## Overview | Field | Value | | ------------ | ---------------------------------------------------------------------------------------------- | | **CELEX** | 32022R1925 | | **Status** | Active | | **Articles** | 54 | | **Recitals** | 109 | | **EUR-Lex** | [View Official Text](https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:32022R1925) | ## Compliance Deadlines | Date | Type | Description | | ---------- | ------------------- | ------------------------------------------- | | 2022-10-01 | Entry into force | Regulation enters into force | | 2023-07-03 | Partial application | Gatekeeper designation application deadline | ## Stakeholder Roles The DMA defines obligations for the following stakeholder roles: * **manufacturer** - Core platform service providers and gatekeepers * **importer** - Entities bringing core platform services into the EU * **distributor** - Business users of core platform services * **authorized\_representative** - EU representatives for non-EU gatekeepers * **notified\_body** - Bodies for compliance function verification * **market\_surveillance** - European Commission (enforcement authority) * **consumer** - End users of core platform services ## Requirement Types Requirements in the DMA are classified as: * **obligation** - Do's and don'ts for gatekeepers (Articles 5, 6, 7) * **prohibition** - Prohibited practices for gatekeepers * **right** - Rights of business users and end users * **procedure** - Gatekeeper designation and compliance procedures * **general** - General provisions and definitions ## API Usage ```bash theme={null} # Get all DMA articles curl https://api.law4devs.eu/v1/frameworks/dma/articles # Get DMA framework details curl https://api.law4devs.eu/v1/frameworks/dma # Get DMA requirements curl https://api.law4devs.eu/v1/frameworks/dma/requirements # Get DMA recitals curl https://api.law4devs.eu/v1/frameworks/dma/recitals ``` ```python theme={null} # Python SDK from law4devs import Law4Devs client = Law4Devs(api_key="your-api-key") # List all DMA articles articles = client.articles.list("dma") # Get DMA requirements by stakeholder role requirements = client.requirements.list("dma", role="manufacturer") # Search DMA articles by tag articles = client.articles.search("dma", query="gatekeeper") ``` ```typescript theme={null} // TypeScript SDK import { Law4Devs } from '@law4devs/sdk'; const client = new Law4Devs({ apiKey: 'your-api-key' }); // List all DMA articles const articles = await client.articles.list('dma'); // Get DMA requirements by stakeholder role const requirements = await client.requirements.list('dma', { role: 'manufacturer' }); ``` ## Key Topics * Gatekeeper designation criteria (size, users, entrenched position) * Do's and don'ts for gatekeepers (Articles 5, 6, 7) * Interoperability and data portability requirements * Self-preferencing restrictions * Business user rights and fair treatment * End user choice and switching rights * Market investigations and enforcement * Fines and periodic penalty payments # Digital Operational Resilience Act (DORA) Source: https://docs.law4devs.eu/frameworks/dora Regulation (EU) 2022/2554 on digital operational resilience for the financial sector. ## Overview | Field | Value | | ------------ | ---------------------------------------------------------------------------------------------- | | **CELEX** | 32022R2554 | | **Status** | Active | | **Articles** | 64 | | **Recitals** | 106 | | **EUR-Lex** | [View Official Text](https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:32022R2554) | ## Compliance Deadlines | Date | Type | Description | | ---------- | ---------------- | ------------------------------------ | | 2023-01-16 | Entry into force | Regulation enters into force | | 2025-01-17 | Full application | Full application of all requirements | ## Stakeholder Roles DORA defines obligations for the following stakeholder roles: * **manufacturer** - ICT service providers and tool developers * **importer** - Entities importing ICT services into the EU * **distributor** - Financial entities using ICT services * **authorized\_representative** - EU representatives for third-country providers * **notified\_body** - Oversight bodies for critical ICT providers * **market\_surveillance** - Competent authorities and ESAs * **consumer** - Financial service end users ## Requirement Types Requirements in DORA are classified as: * **obligation** - Mandatory ICT risk management requirements * **prohibition** - Restricted practices and dependencies * **right** - Rights of financial entities and users * **procedure** - Incident reporting and testing procedures * **general** - General provisions and definitions ## API Usage ```bash theme={null} # Get all DORA articles curl https://api.law4devs.eu/v1/frameworks/dora/articles # Get DORA framework details curl https://api.law4devs.eu/v1/frameworks/dora # Get DORA requirements curl https://api.law4devs.eu/v1/frameworks/dora/requirements # Get DORA recitals curl https://api.law4devs.eu/v1/frameworks/dora/recitals ``` ```python theme={null} # Python SDK from law4devs import Law4Devs client = Law4Devs(api_key="your-api-key") # List all DORA articles articles = client.articles.list("dora") # Get DORA requirements by stakeholder role requirements = client.requirements.list("dora", role="manufacturer") # Search DORA articles by tag articles = client.articles.search("dora", query="ICT risk") ``` ```typescript theme={null} // TypeScript SDK import { Law4Devs } from '@law4devs/sdk'; const client = new Law4Devs({ apiKey: 'your-api-key' }); // List all DORA articles const articles = await client.articles.list('dora'); // Get DORA requirements by stakeholder role const requirements = await client.requirements.list('dora', { role: 'manufacturer' }); ``` ## Key Topics * ICT risk management framework * ICT-related incident reporting * Digital operational resilience testing * ICT third-party risk management * Information and intelligence sharing * Critical ICT third-party provider oversight # Digital Services Act (DSA) Source: https://docs.law4devs.eu/frameworks/dsa Regulation (EU) 2022/2065 on a Single Market For Digital Services. ## Overview | Field | Value | | ------------ | ---------------------------------------------------------------------------------------------- | | **CELEX** | 32022R2065 | | **Status** | Active | | **Articles** | 93 | | **Recitals** | 156 | | **EUR-Lex** | [View Official Text](https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:32022R2065) | ## Compliance Deadlines | Date | Type | Description | | ---------- | ------------------- | -------------------------------------- | | 2022-11-16 | Entry into force | Regulation enters into force | | 2023-08-25 | Partial application | Obligations for VLOPs and VLOSEs apply | | 2024-02-17 | Full application | Full application of all requirements | ## Stakeholder Roles The DSA defines obligations for the following stakeholder roles: * **manufacturer** - Providers of intermediary services and platforms * **importer** - Entities bringing digital services into the EU * **distributor** - Hosting and caching service providers * **authorized\_representative** - EU representatives for non-EU providers * **notified\_body** - Bodies for out-of-court dispute settlement * **market\_surveillance** - Digital Services Coordinators and Commission * **consumer** - Recipients of digital services (users) ## Requirement Types Requirements in the DSA are classified as: * **obligation** - Mandatory requirements for intermediary services * **prohibition** - Restricted practices (e.g., dark patterns, certain advertising) * **right** - Rights of service recipients (users) * **procedure** - Notice and action procedures, complaint handling * **general** - General provisions and definitions ## API Usage ```bash theme={null} # Get all DSA articles curl https://api.law4devs.eu/v1/frameworks/dsa/articles # Get DSA framework details curl https://api.law4devs.eu/v1/frameworks/dsa # Get DSA requirements curl https://api.law4devs.eu/v1/frameworks/dsa/requirements # Get DSA recitals curl https://api.law4devs.eu/v1/frameworks/dsa/recitals ``` ```python theme={null} # Python SDK from law4devs import Law4Devs client = Law4Devs(api_key="your-api-key") # List all DSA articles articles = client.articles.list("dsa") # Get DSA requirements by stakeholder role requirements = client.requirements.list("dsa", role="manufacturer") # Search DSA articles by tag articles = client.articles.search("dsa", query="content moderation") ``` ```typescript theme={null} // TypeScript SDK import { Law4Devs } from '@law4devs/sdk'; const client = new Law4Devs({ apiKey: 'your-api-key' }); // List all DSA articles const articles = await client.articles.list('dsa'); // Get DSA requirements by stakeholder role const requirements = await client.requirements.list('dsa', { role: 'manufacturer' }); ``` ## Key Topics * Intermediary service liability and safe harbors * Content moderation and notice-and-action mechanisms * Online platform transparency and reporting * Very Large Online Platforms (VLOPs) additional obligations * Very Large Online Search Engines (VLOSEs) obligations * Advertising transparency and restrictions * Dark patterns and user interface manipulation * Risk assessments and independent auditing # eIDAS Regulation Source: https://docs.law4devs.eu/frameworks/eidas Regulation (EU) 910/2014 on electronic identification and trust services for electronic transactions in the internal market. ## Overview | Field | Value | | ------------ | ---------------------------------------------------------------------------------------------- | | **CELEX** | 32014R0910 | | **Status** | Active | | **Articles** | 52 | | **Recitals** | 77 | | **EUR-Lex** | [View Official Text](https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:32014R0910) | ## Compliance Deadlines | Date | Type | Description | | ---------- | ---------------- | ---------------------------- | | 2014-09-17 | Entry into force | Regulation enters into force | ## Stakeholder Roles The eIDAS Regulation defines obligations for the following stakeholder roles: * **manufacturer** - Developers of trust service solutions and eID systems * **importer** - Entities bringing trust services into the EU * **distributor** - Trust service providers making services available * **authorized\_representative** - Representatives for non-EU trust service providers * **notified\_body** - Conformity assessment bodies for qualified trust services * **market\_surveillance** - National supervisory bodies * **consumer** - Users of trust services and eID ## Requirement Types Requirements in eIDAS are classified as: * **obligation** - Mandatory requirements for trust service providers * **prohibition** - Restricted practices for trust services * **right** - Rights of trust service users * **procedure** - Procedures for notification and supervision * **general** - General provisions and definitions ## API Usage ```bash theme={null} # Get all eIDAS articles curl https://api.law4devs.eu/v1/frameworks/eidas/articles # Get eIDAS framework details curl https://api.law4devs.eu/v1/frameworks/eidas # Get eIDAS requirements curl https://api.law4devs.eu/v1/frameworks/eidas/requirements # Get eIDAS recitals curl https://api.law4devs.eu/v1/frameworks/eidas/recitals ``` ```python theme={null} # Python SDK from law4devs import Law4Devs client = Law4Devs(api_key="your-api-key") # List all eIDAS articles articles = client.articles.list("eidas") # Get eIDAS requirements by stakeholder role requirements = client.requirements.list("eidas", role="manufacturer") # Search eIDAS articles by tag articles = client.articles.search("eidas", query="trust service") ``` ```typescript theme={null} // TypeScript SDK import { Law4Devs } from '@law4devs/sdk'; const client = new Law4Devs({ apiKey: 'your-api-key' }); // List all eIDAS articles const articles = await client.articles.list('eidas'); // Get eIDAS requirements by stakeholder role const requirements = await client.requirements.list('eidas', { role: 'manufacturer' }); ``` ## Key Topics * Electronic identification schemes (eID) * Qualified trust services * Electronic signatures (advanced, qualified) * Electronic seals and timestamps * Website authentication certificates * Electronic registered delivery services * Cross-border recognition and interoperability # European Digital Identity Regulation (eIDAS 2.0) Source: https://docs.law4devs.eu/frameworks/eidas2 Regulation (EU) 2024/1183 amending eIDAS — European Digital Identity. ## Overview | Field | Value | | ------------ | ---------------------------------------------------------------------------------------------- | | **CELEX** | 32024R1183 | | **Status** | Active | | **Articles** | 2 | | **Recitals** | 78 | | **EUR-Lex** | [View Official Text](https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:32024R1183) | ## Compliance Deadlines | Date | Type | Description | | ---------- | ------------------- | --------------------------------------------------- | | 2024-05-20 | Entry into force | Regulation enters into force | | 2026-11-21 | Partial application | EUDI Wallet must be made available by Member States | ## Stakeholder Roles The eIDAS 2.0 Regulation defines obligations for the following stakeholder roles: * **manufacturer** - Developers of EUDI Wallet solutions and components * **importer** - Entities bringing wallet solutions into the EU * **distributor** - Providers of wallet services and relying parties * **authorized\_representative** - EU representatives for non-EU providers * **notified\_body** - Conformity assessment bodies for wallet components * **market\_surveillance** - National supervisory bodies * **consumer** - EU citizens and residents using digital identity wallets ## Requirement Types Requirements in eIDAS 2.0 are classified as: * **obligation** - Mandatory requirements for wallet providers and relying parties * **prohibition** - Restricted practices for identity verification * **right** - Rights of wallet users (access, portability, privacy) * **procedure** - Certification and notification procedures * **general** - General provisions and definitions ## API Usage ```bash theme={null} # Get all eIDAS 2.0 articles curl https://api.law4devs.eu/v1/frameworks/eidas2/articles # Get eIDAS 2.0 framework details curl https://api.law4devs.eu/v1/frameworks/eidas2 # Get eIDAS 2.0 requirements curl https://api.law4devs.eu/v1/frameworks/eidas2/requirements # Get eIDAS 2.0 recitals curl https://api.law4devs.eu/v1/frameworks/eidas2/recitals ``` ```python theme={null} # Python SDK from law4devs import Law4Devs client = Law4Devs(api_key="your-api-key") # List all eIDAS 2.0 articles articles = client.articles.list("eidas2") # Get eIDAS 2.0 requirements by stakeholder role requirements = client.requirements.list("eidas2", role="manufacturer") # Search eIDAS 2.0 articles by tag articles = client.articles.search("eidas2", query="wallet") ``` ```typescript theme={null} // TypeScript SDK import { Law4Devs } from '@law4devs/sdk'; const client = new Law4Devs({ apiKey: 'your-api-key' }); // List all eIDAS 2.0 articles const articles = await client.articles.list('eidas2'); // Get eIDAS 2.0 requirements by stakeholder role const requirements = await client.requirements.list('eidas2', { role: 'manufacturer' }); ``` ## Key Topics * European Digital Identity Wallet (EUDI Wallet) * Mandatory acceptance by public services and large platforms * Wallet components and certification * Person identification data and attributes * Privacy and data minimization requirements * Cross-border interoperability * User consent and control mechanisms * Security and fraud prevention # ePrivacy Directive (ePD) Source: https://docs.law4devs.eu/frameworks/eprivacy Directive 2002/58/EC on Privacy and Electronic Communications (as amended). ## Overview | Field | Value | | ------------ | ---------------------------------------------------------------------------------------------- | | **CELEX** | 32002L0058 | | **Status** | Active | | **Articles** | 21 | | **Recitals** | 48 | | **EUR-Lex** | [View Official Text](https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:32002L0058) | ## Compliance Deadlines | Date | Type | Description | | ---------- | ---------------- | ------------------------------------------------------- | | 2002-07-31 | Entry into force | Directive enters into force (multiple amendments since) | ## Stakeholder Roles The ePrivacy Directive defines obligations for the following stakeholder roles: * **manufacturer** - Developers of cookies, tracking technologies, and communication tools * **importer** - Entities bringing electronic communication services into the EU * **distributor** - Providers of publicly available electronic communications services * **authorized\_representative** - EU representatives for non-EU service providers * **notified\_body** - Bodies for communications service certification * **market\_surveillance** | National regulatory authorities * **consumer** - Users of electronic communications services ## Requirement Types Requirements in the ePrivacy Directive are classified as: * **obligation** - Mandatory requirements for electronic communications providers * **prohibition** - Prohibited practices (unsolicited communications, unauthorized access) * **right** - Rights of subscribers and users * **procedure** - Consent and notification procedures * **general** - General provisions and definitions ## API Usage ```bash theme={null} # Get all ePrivacy Directive articles curl https://api.law4devs.eu/v1/frameworks/eprivacy/articles # Get ePrivacy Directive framework details curl https://api.law4devs.eu/v1/frameworks/eprivacy # Get ePrivacy Directive requirements curl https://api.law4devs.eu/v1/frameworks/eprivacy/requirements # Get ePrivacy Directive recitals curl https://api.law4devs.eu/v1/frameworks/eprivacy/recitals ``` ```python theme={null} # Python SDK from law4devs import Law4Devs client = Law4Devs(api_key="your-api-key") # List all ePrivacy Directive articles articles = client.articles.list("eprivacy") # Get ePrivacy Directive requirements by stakeholder role requirements = client.requirements.list("eprivacy", role="manufacturer") # Search ePrivacy Directive articles by tag articles = client.articles.search("eprivacy", query="cookie consent") ``` ```typescript theme={null} // TypeScript SDK import { Law4Devs } from '@law4devs/sdk'; const client = new Law4Devs({ apiKey: 'your-api-key' }); // List all ePrivacy Directive articles const articles = await client.articles.list('eprivacy'); // Get ePrivacy Directive requirements by stakeholder role const requirements = await client.requirements.list('eprivacy', { role: 'manufacturer' }); ``` ## Key Topics * Cookie consent requirements (the "cookie law") * Confidentiality of communications * Traffic and location data restrictions * Unsolicited communications (spam, cold calls) * Security of networks and services * Caller/called line identification restrictions * Relationship with GDPR (lex specialis) # General Data Protection Regulation (GDPR) Source: https://docs.law4devs.eu/frameworks/gdpr Regulation (EU) 2016/679 on the protection of natural persons with regard to the processing of personal data. ## Overview | Field | Value | | ------------ | ---------------------------------------------------------------------------------------------- | | **CELEX** | 32016R0679 | | **Status** | Active | | **Articles** | 99 | | **Recitals** | 173 | | **EUR-Lex** | [View Official Text](https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:32016R0679) | ## Compliance Deadlines | Date | Type | Description | | ---------- | ---------------- | ------------------------------------ | | 2016-05-24 | Entry into force | Regulation enters into force | | 2018-05-25 | Full application | Full application of all requirements | ## Stakeholder Roles The GDPR defines obligations for the following stakeholder roles: * **manufacturer** - Developers of data processing systems and tools * **importer** - Entities bringing data processing services into the EU * **distributor** - Processors making data services available * **authorized\_representative** - EU representatives for non-EU controllers/processors * **notified\_body** - Certification bodies for data protection * **market\_surveillance** - Supervisory authorities (DPAs) * **consumer** - Data subjects (natural persons) ## Requirement Types Requirements in the GDPR are classified as: * **obligation** - Mandatory data protection requirements * **prohibition** - Processing activities that are forbidden * **right** - Data subject rights (access, rectification, erasure, etc.) * **procedure** - Procedures for compliance and breach notification * **general** - General provisions and definitions ## API Usage ```bash theme={null} # Get all GDPR articles curl https://api.law4devs.eu/v1/frameworks/gdpr/articles # Get GDPR framework details curl https://api.law4devs.eu/v1/frameworks/gdpr # Get GDPR requirements curl https://api.law4devs.eu/v1/frameworks/gdpr/requirements # Get GDPR recitals curl https://api.law4devs.eu/v1/frameworks/gdpr/recitals ``` ```python theme={null} # Python SDK from law4devs import Law4Devs client = Law4Devs(api_key="your-api-key") # List all GDPR articles articles = client.articles.list("gdpr") # Get GDPR requirements by stakeholder role requirements = client.requirements.list("gdpr", role="manufacturer") # Search GDPR articles by tag articles = client.articles.search("gdpr", query="consent") ``` ```typescript theme={null} // TypeScript SDK import { Law4Devs } from '@law4devs/sdk'; const client = new Law4Devs({ apiKey: 'your-api-key' }); // List all GDPR articles const articles = await client.articles.list('gdpr'); // Get GDPR requirements by stakeholder role const requirements = await client.requirements.list('gdpr', { role: 'manufacturer' }); ``` ## Key Topics * Lawful basis for processing * Data subject rights (access, rectification, erasure, portability, objection) * Controller and processor obligations * Data protection impact assessments * Breach notification requirements * International data transfers * Administrative fines and penalties # Markets in Crypto-Assets Regulation (MiCA) Source: https://docs.law4devs.eu/frameworks/mica Regulation (EU) 2023/1114 on Markets in Crypto-Assets. ## Overview | Field | Value | | ------------ | ---------------------------------------------------------------------------------------------- | | **CELEX** | 32023R1114 | | **Status** | Active | | **Articles** | 149 | | **Recitals** | 119 | | **EUR-Lex** | [View Official Text](https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:32023R1114) | ## Compliance Deadlines | Date | Type | Description | | ---------- | ------------------- | ------------------------------------------------------ | | 2023-06-29 | Entry into force | Regulation enters into force | | 2024-06-30 | Partial application | Asset-referenced tokens and e-money tokens rules apply | | 2024-12-30 | Full application | Full application of all requirements | ## Stakeholder Roles MiCA defines obligations for the following stakeholder roles: * **manufacturer** - Issuers of crypto-assets and CASP developers * **importer** - Crypto-asset service providers from third countries * **distributor** - Trading platforms and exchanges * **authorized\_representative** - EU representatives for non-EU issuers * **notified\_body** - Bodies for crypto-asset service certification * **market\_surveillance** - National competent authorities and ESMA * **consumer** - Holders and users of crypto-assets ## Requirement Types Requirements in MiCA are classified as: * **obligation** - Mandatory requirements for issuers and CASPs * **prohibition** - Prohibited market practices (market manipulation, insider trading) * **right** - Rights of crypto-asset holders * **procedure** - Authorization and whitepaper notification procedures * **general** - General provisions and definitions ## API Usage ```bash theme={null} # Get all MiCA articles curl https://api.law4devs.eu/v1/frameworks/mica/articles # Get MiCA framework details curl https://api.law4devs.eu/v1/frameworks/mica # Get MiCA requirements curl https://api.law4devs.eu/v1/frameworks/mica/requirements # Get MiCA recitals curl https://api.law4devs.eu/v1/frameworks/mica/recitals ``` ```python theme={null} # Python SDK from law4devs import Law4Devs client = Law4Devs(api_key="your-api-key") # List all MiCA articles articles = client.articles.list("mica") # Get MiCA requirements by stakeholder role requirements = client.requirements.list("mica", role="manufacturer") # Search MiCA articles by tag articles = client.articles.search("mica", query="stablecoin") ``` ```typescript theme={null} // TypeScript SDK import { Law4Devs } from '@law4devs/sdk'; const client = new Law4Devs({ apiKey: 'your-api-key' }); // List all MiCA articles const articles = await client.articles.list('mica'); // Get MiCA requirements by stakeholder role const requirements = await client.requirements.list('mica', { role: 'manufacturer' }); ``` ## Key Topics * Crypto-asset whitepaper requirements * Asset-referenced tokens (ARTs) and e-money tokens (EMTs) * Crypto-asset service providers (CASPs) authorization * Trading platforms and exchange requirements * Market abuse prevention (insider trading, market manipulation) * Consumer protection and disclosure requirements * Custody and wallet service requirements * Environmental impact disclosures # NIS Directive (Original) Source: https://docs.law4devs.eu/frameworks/nis1 Directive (EU) 2016/1148 on Network and Information Systems Security. ## Overview | Field | Value | | ------------ | ---------------------------------------------------------------------------------------------- | | **CELEX** | 32016L1148 | | **Status** | Superseded | | **Articles** | 27 | | **Recitals** | 75 | | **EUR-Lex** | [View Official Text](https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:32016L1148) | ## Compliance Deadlines | Date | Type | Description | | ---------- | ---------------- | ---------------------------- | | 2016-07-19 | Entry into force | Directive enters into force | | 2024-10-18 | Repealed | Superseded by NIS2 Directive | **This directive has been repealed.** NIS1 was superseded by the NIS2 Directive (EU) 2022/2555 as of October 18, 2024. This documentation is provided for historical reference and to understand the evolution of EU cybersecurity law. ## Stakeholder Roles The NIS Directive defined obligations for the following stakeholder roles: * **manufacturer** - Providers of digital products and services * **importer** - Entities operating essential services * **distributor** - Digital service providers * **authorized\_representative** - EU representatives for non-EU providers * **notified\_body** - Bodies for security certification * **market\_surveillance** - National competent authorities and CSIRTs * **consumer** - Users of essential and digital services ## Requirement Types Requirements in NIS1 were classified as: * **obligation** - Security and notification requirements * **prohibition** - Restricted security practices * **right** - Rights of operators and users * **procedure** - Incident notification procedures * **general** - General provisions and definitions ## API Usage ```bash theme={null} # Get all NIS1 articles (historical reference) curl https://api.law4devs.eu/v1/frameworks/nis1/articles # Get NIS1 framework details curl https://api.law4devs.eu/v1/frameworks/nis1 # Get NIS1 requirements curl https://api.law4devs.eu/v1/frameworks/nis1/requirements # Get NIS1 recitals curl https://api.law4devs.eu/v1/frameworks/nis1/recitals ``` ```python theme={null} # Python SDK from law4devs import Law4Devs client = Law4Devs(api_key="your-api-key") # List all NIS1 articles (historical reference) articles = client.articles.list("nis1") # Get NIS1 requirements by stakeholder role requirements = client.requirements.list("nis1", role="manufacturer") # Search NIS1 articles by tag articles = client.articles.search("nis1", query="essential services") ``` ```typescript theme={null} // TypeScript SDK import { Law4Devs } from '@law4devs/sdk'; const client = new Law4Devs({ apiKey: 'your-api-key' }); // List all NIS1 articles (historical reference) const articles = await client.articles.list('nis1'); // Get NIS1 requirements by stakeholder role const requirements = await client.requirements.list('nis1', { role: 'manufacturer' }); ``` ## Key Topics (Historical) * Operators of Essential Services (OES) identification * Digital Service Providers (DSPs) requirements * Security and notification requirements * National cybersecurity strategies * Cooperation and information sharing mechanisms * Network of CSIRTs (Computer Security Incident Response Teams) * Transition to NIS2 (expanded scope, harmonized requirements) # NIS2 Directive Source: https://docs.law4devs.eu/frameworks/nis2 Directive (EU) 2022/2555 on measures for a high common level of cybersecurity across the Union. ## Overview | Field | Value | | ------------ | ---------------------------------------------------------------------------------------------- | | **CELEX** | 32022L2555 | | **Status** | Active | | **Articles** | 46 | | **Recitals** | 144 | | **EUR-Lex** | [View Official Text](https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:32022L2555) | ## Compliance Deadlines | Date | Type | Description | | ---------- | ---------------- | ------------------------------------ | | 2022-12-27 | Entry into force | Directive enters into force | | 2024-10-17 | Transposition | Member States transposition deadline | ## Stakeholder Roles The NIS2 Directive defines obligations for the following stakeholder roles: * **manufacturer** - Providers of digital products and services * **importer** - Entities importing digital services into the EU * **distributor** - Service providers in the supply chain * **authorized\_representative** - EU representatives for non-EU entities * **notified\_body** - Designated conformity assessment bodies * **market\_surveillance** - National competent authorities * **consumer** - Users of essential and important services ## Requirement Types Requirements in NIS2 are classified as: * **obligation** - Mandatory cybersecurity risk management measures * **prohibition** - Restricted practices and non-compliant behaviors * **right** - Rights of entities and users * **procedure** - Incident reporting and notification procedures * **general** - General provisions and scope definitions ## API Usage ```bash theme={null} # Get all NIS2 articles curl https://api.law4devs.eu/v1/frameworks/nis2/articles # Get NIS2 framework details curl https://api.law4devs.eu/v1/frameworks/nis2 # Get NIS2 requirements curl https://api.law4devs.eu/v1/frameworks/nis2/requirements # Get NIS2 recitals curl https://api.law4devs.eu/v1/frameworks/nis2/recitals ``` ```python theme={null} # Python SDK from law4devs import Law4Devs client = Law4Devs(api_key="your-api-key") # List all NIS2 articles articles = client.articles.list("nis2") # Get NIS2 requirements by stakeholder role requirements = client.requirements.list("nis2", role="manufacturer") # Search NIS2 articles by tag articles = client.articles.search("nis2", query="incident reporting") ``` ```typescript theme={null} // TypeScript SDK import { Law4Devs } from '@law4devs/sdk'; const client = new Law4Devs({ apiKey: 'your-api-key' }); // List all NIS2 articles const articles = await client.articles.list('nis2'); // Get NIS2 requirements by stakeholder role const requirements = await client.requirements.list('nis2', { role: 'manufacturer' }); ``` ## Key Topics * Essential and important entity classification * Cybersecurity risk management measures * Incident reporting obligations (early warning, incident notification, final report) * Supply chain security requirements * Management body liability * Information sharing and cooperation # Payment Services Directive 2 (PSD2) Source: https://docs.law4devs.eu/frameworks/psd2 Directive (EU) 2015/2366 on Payment Services in the Internal Market. ## Overview | Field | Value | | ------------ | ---------------------------------------------------------------------------------------------- | | **CELEX** | 32015L2366 | | **Status** | Active | | **Articles** | 117 | | **Recitals** | 113 | | **EUR-Lex** | [View Official Text](https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:32015L2366) | ## Compliance Deadlines | Date | Type | Description | | ---------- | ------------------- | ----------------------------------------------------- | | 2016-01-13 | Entry into force | Directive enters into force | | 2018-01-13 | Transposition | Member States transposition deadline | | 2021-01-01 | Partial application | Strong Customer Authentication for e-commerce applies | ## Stakeholder Roles The PSD2 defines obligations for the following stakeholder roles: * **manufacturer** - Developers of payment systems and APIs * **importer** - Payment service providers operating cross-border * **distributor** - Account servicing payment service providers * **authorized\_representative** - Agents and branches of payment institutions * **notified\_body** - Bodies for payment service certification * **market\_surveillance** - National competent authorities * **consumer** - Payment service users (payers and payees) ## Requirement Types Requirements in PSD2 are classified as: * **obligation** - Mandatory requirements for payment service providers * **prohibition** - Restricted practices (e.g., surcharging) * **right** - Rights of payment service users * **procedure** - Authorization and passporting procedures * **general** - General provisions and definitions ## API Usage ```bash theme={null} # Get all PSD2 articles curl https://api.law4devs.eu/v1/frameworks/psd2/articles # Get PSD2 framework details curl https://api.law4devs.eu/v1/frameworks/psd2 # Get PSD2 requirements curl https://api.law4devs.eu/v1/frameworks/psd2/requirements # Get PSD2 recitals curl https://api.law4devs.eu/v1/frameworks/psd2/recitals ``` ```python theme={null} # Python SDK from law4devs import Law4Devs client = Law4Devs(api_key="your-api-key") # List all PSD2 articles articles = client.articles.list("psd2") # Get PSD2 requirements by stakeholder role requirements = client.requirements.list("psd2", role="manufacturer") # Search PSD2 articles by tag articles = client.articles.search("psd2", query="SCA") ``` ```typescript theme={null} // TypeScript SDK import { Law4Devs } from '@law4devs/sdk'; const client = new Law4Devs({ apiKey: 'your-api-key' }); // List all PSD2 articles const articles = await client.articles.list('psd2'); // Get PSD2 requirements by stakeholder role const requirements = await client.requirements.list('psd2', { role: 'manufacturer' }); ``` ## Key Topics * Strong Customer Authentication (SCA) requirements * Account Information Services (AIS) * Payment Initiation Services (PIS) * Access to Account (XS2A) and API requirements * Payment service provider licensing * Consumer protection and liability rules * Transaction security and communication requirements * Surcharge prohibition # Radio Equipment Directive (RED) Source: https://docs.law4devs.eu/frameworks/red Directive 2014/53/EU on Radio Equipment. ## Overview | Field | Value | | ------------ | ---------------------------------------------------------------------------------------------- | | **CELEX** | 32014L0053 | | **Status** | Active | | **Articles** | 52 | | **Recitals** | 75 | | **EUR-Lex** | [View Official Text](https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:32014L0053) | ## Compliance Deadlines | Date | Type | Description | | ---------- | ------------------- | -------------------------------------------------- | | 2014-05-22 | Entry into force | Directive enters into force | | 2016-06-13 | Full application | Full application of all requirements | | 2025-08-01 | Partial application | Article 3(3)(d-f) cybersecurity requirements apply | ## Stakeholder Roles The RED defines obligations for the following stakeholder roles: * **manufacturer** - Manufacturers of radio equipment and wireless devices * **importer** - Entities placing radio equipment on the EU market * **distributor** - Entities making radio equipment available in the supply chain * **authorized\_representative** - EU representatives for non-EU manufacturers * **notified\_body** - Conformity assessment bodies for radio equipment * **market\_surveillance** - National market surveillance authorities * **consumer** - Users of radio equipment and wireless devices ## Requirement Types Requirements in RED are classified as: * **obligation** - Mandatory essential requirements for radio equipment * **prohibition** - Restricted equipment and practices * **right** - Rights of economic operators and users * **procedure** - Conformity assessment and CE marking procedures * **general** - General provisions and definitions ## API Usage ```bash theme={null} # Get all RED articles curl https://api.law4devs.eu/v1/frameworks/red/articles # Get RED framework details curl https://api.law4devs.eu/v1/frameworks/red # Get RED requirements curl https://api.law4devs.eu/v1/frameworks/red/requirements # Get RED recitals curl https://api.law4devs.eu/v1/frameworks/red/recitals ``` ```python theme={null} # Python SDK from law4devs import Law4Devs client = Law4Devs(api_key="your-api-key") # List all RED articles articles = client.articles.list("red") # Get RED requirements by stakeholder role requirements = client.requirements.list("red", role="manufacturer") # Search RED articles by tag articles = client.articles.search("red", query="cybersecurity") ``` ```typescript theme={null} // TypeScript SDK import { Law4Devs } from '@law4devs/sdk'; const client = new Law4Devs({ apiKey: 'your-api-key' }); // List all RED articles const articles = await client.articles.list('red'); // Get RED requirements by stakeholder role const requirements = await client.requirements.list('red', { role: 'manufacturer' }); ``` ## Key Topics * Essential requirements for radio equipment * Health and safety requirements (Article 3(1)(a)) * EMC requirements (Article 3(1)(b)) * Efficient use of radio spectrum (Article 3(2)) * Cybersecurity requirements for connected devices (Article 3(3)(d-f)) * CE marking and conformity assessment * Interoperability and accessory requirements * Software and firmware update requirements # Source: https://docs.law4devs.eu/index
Law4Devs
Law4Devs

Structured EU regulatory compliance data for developers

## What is Law4Devs? Law4Devs is a REST API and SDKs that turns EU regulatory texts into structured, queryable JSON. Instead of reading 300-page PDF regulations, your application queries articles, recitals, requirements, compliance deadlines, and tags via simple HTTP calls. **19 EU frameworks** — CRA, GDPR, NIS2, DORA, AI Act, eIDAS 2.0, DSA, DMA, Data Act, MiCA, and more. How It Works Zero-dependency. `pip install law4devs` Zero-dependency. `npm install law4devs` Composer package. `composer require law4devs/law4devs` Zero-dependency. Maven · Java 17+ pub.dev · Dart 3+ · Flutter & CLI crates.io · Rust 1.85+ · Tokio async Interactive docs for all 21 endpoints. All 19 EU regulations, with CELEX numbers. # Quick Start Source: https://docs.law4devs.eu/quickstart Make your first Law4Devs API call in under 60 seconds. Pick your preferred method: ```bash theme={null} # List all frameworks curl -H "X-API-Key: YOUR_API_KEY" \ https://api.law4devs.eu/v1/frameworks # Get GDPR articles curl -H "X-API-Key: YOUR_API_KEY" \ "https://api.law4devs.eu/v1/frameworks/gdpr/articles?per_page=5" # Search across all frameworks curl -H "X-API-Key: YOUR_API_KEY" \ "https://api.law4devs.eu/v1/search?q=data+breach+notification" ``` ```bash theme={null} pip install law4devs ``` ```python theme={null} import os from law4devs import Law4DevsClient client = Law4DevsClient(api_key=os.environ["LAW4DEVS_API_KEY"]) # List all frameworks page = client.frameworks.list() for fw in page.data: print(fw.slug, fw.name) # Iterate all GDPR articles (auto-pagination) for article in client.articles.iter("gdpr"): print(article.article_number, article.title) # Search results = client.search.query("data breach notification") print(f"Found {results.meta.total} results") ``` ```bash theme={null} npm install law4devs ``` ```typescript theme={null} import { Law4DevsClient } from 'law4devs'; const client = new Law4DevsClient({ apiKey: process.env.LAW4DEVS_API_KEY!, }); // List all frameworks const page = await client.frameworks.list(); page.data.forEach(fw => console.log(fw.slug, fw.name)); // Iterate all GDPR articles (auto-pagination) for await (const article of client.articles.iter('gdpr')) { console.log(article.articleNumber, article.title); } // Search const results = await client.search.query('data breach notification'); console.log(`Found ${results.meta.total} results`); ``` ## Next Steps # Annexes Source: https://docs.law4devs.eu/sdks/flutter/annexes Access regulatory annexes with the Flutter SDK. ## Overview Annexes are supplementary legal documents attached to EU regulations. They often contain technical requirements, classification criteria, and reference tables. *** ## Methods ### `list()` ```dart theme={null} Future> list(String frameworkSlug, {int? page, int? perPage}) ``` ### `get()` ```dart theme={null} Future get(String frameworkSlug, String annexNumber) ``` ### `iter()` ```dart theme={null} Stream iter(String frameworkSlug, {int perPage = 20}) ``` *** ## Models ### `AnnexSummary` | Field | Type | Description | | --------------- | --------- | ----------------------------- | | `id` | `int` | Internal ID | | `frameworkSlug` | `String` | Parent framework | | `number` | `String` | Annex number (e.g. `I`, `II`) | | `title` | `String?` | Optional title | | `position` | `int` | Ordering position | ### `Annex` Extends `AnnexSummary` with: | Field | Type | Description | | --------- | -------- | ------------------ | | `content` | `String` | Full annex content | *** ## Examples ### List CRA annexes ```dart theme={null} final page = await client.annexes.list('cra'); for (final a in page.data) { print('Annex ${a.number}: ${a.title}'); } ``` ### Get a specific annex ```dart theme={null} final annex = await client.annexes.get('cra', 'I'); print(annex.content); ``` ### Auto-paginate ```dart theme={null} await for (final a in client.annexes.iter('ai-act')) { print('Annex ${a.number}: ${a.title}'); } ``` # Articles Source: https://docs.law4devs.eu/sdks/flutter/articles Access regulatory articles with the Flutter SDK. ## Overview Articles are the core legal text units of each framework. Each article has a number, title, content, paragraphs, and associated tags. *** ## Methods ### `list()` ```dart theme={null} Future> list(String frameworkSlug, {int? page, int? perPage}) ``` ### `get()` ```dart theme={null} Future
get(String frameworkSlug, String articleNumber) ``` ### `related()` ```dart theme={null} Future> related(String frameworkSlug, String articleNumber, {int? page, int? perPage}) ``` ### `iter()` ```dart theme={null} Stream iter(String frameworkSlug, {int perPage = 20}) ``` *** ## Models ### `ArticleSummary` | Field | Type | Description | | ---------------- | ----------- | ------------------------------ | | `id` | `int` | Internal ID | | `frameworkSlug` | `String` | Parent framework (e.g. `gdpr`) | | `number` | `String` | Article number (e.g. `5`) | | `title` | `String` | Article title | | `position` | `int` | Ordering position | | `paragraphCount` | `int` | Number of paragraphs | | `tags` | `List` | Semantic tags | ### `Article` Extends `ArticleSummary` with: | Field | Type | Description | | ------------ | ------------------------ | --------------------- | | `content` | `String` | Full text content | | `paragraphs` | `List` | Structured paragraphs | ### `ArticleParagraph` | Field | Type | Description | | -------------- | -------- | ------------------------------------ | | `paragraphRef` | `String` | Paragraph reference (e.g. `1`, `1a`) | | `content` | `String` | Paragraph text | | `position` | `int` | Ordering position | *** ## Examples ### List articles for a framework ```dart theme={null} final page = await client.articles.list('gdpr'); print('${page.meta.total} GDPR articles'); for (final a in page.data) { print('Art. ${a.number}: ${a.title}'); } ``` ### Get a specific article ```dart theme={null} final art5 = await client.articles.get('gdpr', '5'); print(art5.content); for (final p in art5.paragraphs) { print('[${p.paragraphRef}] ${p.content}'); } ``` ### Get related articles ```dart theme={null} final related = await client.articles.related('gdpr', '5'); for (final a in related.data) { print('Related: Art. ${a.number} — ${a.title}'); } ``` ### Auto-paginate all articles ```dart theme={null} await for (final a in client.articles.iter('cra')) { print('Art. ${a.number}: ${a.title} (${a.paragraphCount} paragraphs)'); } ``` # Compliance Deadlines Source: https://docs.law4devs.eu/sdks/flutter/compliance Access compliance deadlines and enforcement dates with the Flutter SDK. ## Overview Compliance deadlines track enforcement dates, transition periods, and regulatory milestones across all EU frameworks. *** ## Methods ### `deadlines()` ```dart theme={null} Future> deadlines({String? frameworkSlug, int? page, int? perPage}) ``` ### `iterDeadlines()` ```dart theme={null} Stream iterDeadlines({String? frameworkSlug, int perPage = 20}) ``` *** ## Model ### `ComplianceDeadline` | Field | Type | Description | | --------------- | --------- | ------------------ | | `id` | `int` | Internal ID | | `frameworkSlug` | `String` | Source framework | | `title` | `String` | Deadline name | | `description` | `String?` | Detail description | | `deadline` | `String` | ISO 8601 date | | `deadlineType` | `String` | Type of deadline | *** ## Examples ### All upcoming deadlines ```dart theme={null} await for (final d in client.compliance.iterDeadlines()) { print('[${d.frameworkSlug.toUpperCase()}] ${d.deadline} — ${d.title}'); } ``` ### NIS2-specific deadlines ```dart theme={null} await for (final d in client.compliance.iterDeadlines(frameworkSlug: 'nis2')) { print('${d.deadline}: ${d.title}'); } ``` ### Paginated deadlines ```dart theme={null} final page = await client.compliance.deadlines(frameworkSlug: 'dora'); print('${page.meta.total} DORA deadlines'); for (final d in page.data) { print('${d.deadline} — ${d.title}'); } ``` # Error Handling Source: https://docs.law4devs.eu/sdks/flutter/error-handling Handle API errors gracefully with the Flutter SDK. ## Exception Hierarchy ``` Law4DevsException (base) ├── NotFoundError (404) ├── ValidationError (400) ├── RateLimitError (429) └── ServerError (5xx) ``` All exceptions implement Dart's `Exception` interface and expose `message` and `statusCode`. *** ## Catching Errors ```dart theme={null} import 'package:law4devs/law4devs.dart'; try { final fw = await client.frameworks.get('nonexistent'); } on NotFoundError catch (e) { print('404: ${e.message}'); } on ValidationError catch (e) { print('400: ${e.message}'); } on RateLimitError catch (e) { print('429: Rate limited — SDK retried automatically'); } on ServerError catch (e) { print('${e.statusCode}: ${e.message}'); } on Law4DevsException catch (e) { print('API error ${e.statusCode}: ${e.message}'); } ``` *** ## Retry Behavior The SDK automatically retries `429` and `5xx` responses with exponential backoff: | Attempt | Delay | | --------- | --------- | | 1st retry | 1 second | | 2nd retry | 2 seconds | | 3rd retry | 4 seconds | `404` and `400` are thrown immediately without retrying. *** ## Safe Get Pattern ```dart theme={null} Future safeGet(Law4DevsClient client, String slug) async { try { return await client.frameworks.get(slug); } on NotFoundError { return null; } } final fw = await safeGet(client, 'unknown-slug'); if (fw != null) { print(fw.name); } ``` *** ## Error Properties | Property | Type | Description | | ------------ | -------- | -------------------------- | | `message` | `String` | Error message from the API | | `statusCode` | `int?` | HTTP status code | # Frameworks Source: https://docs.law4devs.eu/sdks/flutter/frameworks List, retrieve, and iterate EU regulatory frameworks with the Flutter SDK. ## Overview The `frameworks` resource provides access to all 19 EU regulatory frameworks — GDPR, CRA, NIS2, AI Act, DORA, and more. *** ## Methods ### `list()` ```dart theme={null} Future> list({int? page, int? perPage}) ``` **Parameters** | Parameter | Type | Default | Description | | --------- | ------ | ------- | -------------- | | `page` | `int?` | `1` | Page number | | `perPage` | `int?` | `20` | Items per page | ### `get()` ```dart theme={null} Future get(String slug) ``` ### `iter()` ```dart theme={null} Stream iter({int perPage = 20}) ``` Lazy stream — fetches the next page only when the current page is exhausted. *** ## Models ### `Framework` | Field | Type | Description | | -------------- | --------- | --------------------------------- | | `id` | `int` | Internal ID | | `slug` | `String` | URL-safe identifier (e.g. `gdpr`) | | `name` | `String` | Full regulation name | | `shortName` | `String` | Abbreviation (e.g. `GDPR`) | | `celexNumber` | `String` | EUR-Lex CELEX identifier | | `description` | `String?` | Short description | | `isActive` | `bool` | Whether actively maintained | | `status` | `String` | `active` or `superseded` | | `articleCount` | `int` | Number of scraped articles | | `recitalCount` | `int` | Number of scraped recitals | | `createdAt` | `String` | ISO 8601 timestamp | ### `FrameworkDetail` Extends `Framework` with: | Field | Type | Description | | ------------------ | -------- | ------------------------- | | `eurLexUrl` | `String` | Link to EUR-Lex source | | `requirementCount` | `int` | Number of requirements | | `annexCount` | `int` | Number of annexes | | `tagCount` | `int` | Number of associated tags | | `coverage` | `Map?` | Coverage statistics | *** ## Examples ### List all frameworks ```dart theme={null} final page = await client.frameworks.list(); print('${page.meta.total} frameworks'); for (final fw in page.data) { print('${fw.slug} — ${fw.name} (${fw.articleCount} articles)'); } ``` ### Get framework detail ```dart theme={null} final gdpr = await client.frameworks.get('gdpr'); print('GDPR: ${gdpr.articleCount} articles, ${gdpr.recitalCount} recitals'); print('EUR-Lex: ${gdpr.eurLexUrl}'); ``` ### Auto-paginate all frameworks ```dart theme={null} await for (final fw in client.frameworks.iter()) { if (fw.status == 'active') { print(fw.name); } } ``` ### Filter active frameworks ```dart theme={null} final page = await client.frameworks.list(); final active = page.data.where((fw) => fw.isActive).toList(); print('${active.length} active frameworks'); ``` # Installation Source: https://docs.law4devs.eu/sdks/flutter/installation Install the Law4Devs Flutter/Dart SDK and make your first API call. ## Requirements * Dart SDK >=3.0.0 * Works on Flutter (mobile, web, desktop) and Dart CLI ## Install Add to your `pubspec.yaml`: ```yaml theme={null} dependencies: law4devs: ^1.0.0 ``` Then run: ```bash theme={null} dart pub get ``` Or for Flutter projects: ```bash theme={null} flutter pub get ``` ## First Call ```dart theme={null} import 'package:law4devs/law4devs.dart'; final client = Law4DevsClient( apiKey: Platform.environment['LAW4DEVS_API_KEY'], ); final page = await client.frameworks.list(); print(page.data.first.name); // e.g. "Cyber Resilience Act" ``` ## Configuration ```dart theme={null} final client = Law4DevsClient( apiKey: Platform.environment['LAW4DEVS_API_KEY'], baseUrl: 'https://api.law4devs.eu/v1', // default timeout: const Duration(seconds: 30), // default maxRetries: 3, // retries on 429/5xx ); ``` | Option | Type | Default | Description | | ------------ | ---------- | ----------------------- | --------------------------------------------------- | | `apiKey` | `String?` | `null` | Your API key (required for authenticated endpoints) | | `baseUrl` | `String` | Production URL | Override for dev/staging environment | | `timeout` | `Duration` | `Duration(seconds: 30)` | Per-request HTTP timeout | | `maxRetries` | `int` | `3` | Retries on 429/5xx responses | ## Environment Variable ```bash theme={null} export LAW4DEVS_API_KEY="your-key" ``` ```dart theme={null} final client = Law4DevsClient( apiKey: Platform.environment['LAW4DEVS_API_KEY'], ); ``` Never hardcode your API key in source code or commit it to version control. ## Verify the Install ```dart theme={null} // verify_install.dart import 'package:law4devs/law4devs.dart'; import 'dart:io'; void main() async { final client = Law4DevsClient( apiKey: Platform.environment['LAW4DEVS_API_KEY'], ); final page = await client.frameworks.list(); print('${page.meta.total} frameworks'); } ``` ```bash theme={null} LAW4DEVS_API_KEY=your-key dart run verify_install.dart # → 19 frameworks ``` # Pagination Source: https://docs.law4devs.eu/sdks/flutter/pagination Understand manual and automatic pagination in the Flutter SDK. ## Overview Every `list()` call returns a `Page`. The `iter()` method returns a `Stream` that lazily auto-paginates across all pages. *** ## The `Page` Object ```dart theme={null} final page = await client.frameworks.list(page: 1, perPage: 20); page.data // List — items on this page page.meta.total // int — total items across all pages page.meta.page // int — current page number page.meta.perPage // int — items per page page.meta.pages // int — total number of pages page.links.next // String? — URL of next page (null if last page) page.links.prev // String? — URL of previous page (null if first page) page.hasNext // bool — true if more pages exist ``` **`PageMeta` fields** | Field | Type | Description | | ------------ | -------- | ------------------ | | `apiVersion` | `String` | API version string | | `total` | `int` | Total items | | `page` | `int` | Current page | | `perPage` | `int` | Items per page | | `pages` | `int` | Total pages | *** ## Manual Pagination ```dart theme={null} var page = 1; while (true) { final result = await client.frameworks.list(page: page, perPage: 20); for (final fw in result.data) { print(fw.name); } if (!result.hasNext) break; page++; } ``` *** ## Automatic Pagination with `iter()` `iter()` returns a `Stream` — it fetches the next page only when the current page is exhausted: ```dart theme={null} await for (final fw in client.frameworks.iter()) { print(fw.name); } ``` With a custom page size: ```dart theme={null} await for (final a in client.articles.iter('gdpr', perPage: 50)) { print(a.title); } ``` *** ## Early Stopping ```dart theme={null} await for (final fw in client.frameworks.iter()) { if (fw.slug == 'gdpr') { print('Found GDPR'); break; } } ``` *** ## Progress Tracking ```dart theme={null} var count = 0; final total = (await client.frameworks.list()).meta.total; await for (final fw in client.frameworks.iter()) { count++; print('[$count/$total] ${fw.name}'); } ``` # Quick Start Source: https://docs.law4devs.eu/sdks/flutter/quickstart Make your first Law4Devs API call with Dart/Flutter in under 60 seconds. ## Install ```yaml theme={null} dependencies: law4devs: ^1.0.0 ``` ```bash theme={null} dart pub get ``` ## Basic Usage ```dart theme={null} import 'package:law4devs/law4devs.dart'; import 'dart:io'; void main() async { final client = Law4DevsClient( apiKey: Platform.environment['LAW4DEVS_API_KEY'], ); // List all frameworks final page = await client.frameworks.list(); for (final fw in page.data) { print('${fw.slug} — ${fw.name}'); } // List first 5 CRA articles final articles = await client.articles.list('cra', perPage: 5); for (final a in articles.data) { print('Art. ${a.number}: ${a.title}'); } // Auto-paginate all GDPR articles await for (final a in client.articles.iter('gdpr')) { print('${a.number} ${a.title}'); } // Search across all frameworks final results = await client.search.query('data breach notification'); print('Found ${results.meta.total} results'); } ``` ## Next Steps # Recitals Source: https://docs.law4devs.eu/sdks/flutter/recitals Access regulatory recitals (preamble) with the Flutter SDK. ## Overview Recitals are the preamble sections of EU regulations. They provide context, intent, and interpretive guidance for the articles that follow. *** ## Methods ### `list()` ```dart theme={null} Future> list(String frameworkSlug, {int? page, int? perPage}) ``` ### `get()` ```dart theme={null} Future get(String frameworkSlug, String recitalNumber) ``` ### `iter()` ```dart theme={null} Stream iter(String frameworkSlug, {int perPage = 20}) ``` *** ## Model ### `Recital` | Field | Type | Description | | --------------- | --------- | ----------------- | | `id` | `int` | Internal ID | | `frameworkSlug` | `String` | Parent framework | | `number` | `String` | Recital number | | `title` | `String?` | Optional title | | `text` | `String` | Full text content | | `position` | `int` | Ordering position | *** ## Examples ### List recitals ```dart theme={null} final page = await client.recitals.list('gdpr'); print('${page.meta.total} GDPR recitals'); ``` ### Get a specific recital ```dart theme={null} final r1 = await client.recitals.get('gdpr', '1'); print(r1.text); ``` ### Auto-paginate all recitals ```dart theme={null} await for (final r in client.recitals.iter('nis2')) { print('Recital ${r.number}: ${r.text.substring(0, 80)}...'); } ``` # Requirements Source: https://docs.law4devs.eu/sdks/flutter/requirements Query compliance requirements with the Flutter SDK. ## Overview Requirements are structured compliance obligations extracted from regulatory articles. Filter by framework to scope results. *** ## Methods ### `list()` ```dart theme={null} Future> list({String? frameworkSlug, int? page, int? perPage}) ``` ### `iter()` ```dart theme={null} Stream iter({String? frameworkSlug, int perPage = 20}) ``` *** ## Model ### `Requirement` | Field | Type | Description | | ----------------- | --------- | ------------------ | | `id` | `int` | Internal ID | | `frameworkSlug` | `String` | Source framework | | `articleNumber` | `String` | Article reference | | `title` | `String?` | Optional title | | `description` | `String` | Requirement text | | `requirementType` | `String` | Obligation type | | `stakeholder` | `String?` | Target stakeholder | *** ## Examples ### All requirements across all frameworks ```dart theme={null} await for (final r in client.requirements.iter()) { print('[${r.frameworkSlug.toUpperCase()}] Art. ${r.articleNumber}: ${r.description}'); } ``` ### Requirements for a specific framework ```dart theme={null} await for (final r in client.requirements.iter(frameworkSlug: 'cra')) { print('[${r.requirementType}] ${r.description}'); } ``` ### Paginated request ```dart theme={null} final page = await client.requirements.list(frameworkSlug: 'nis2', perPage: 50); print('${page.meta.total} NIS2 requirements'); ``` # Search Source: https://docs.law4devs.eu/sdks/flutter/search Full-text search across all EU frameworks with the Flutter SDK. ## Overview The search resource lets you query articles, recitals, and annexes across all 19 EU frameworks simultaneously. Optionally filter by framework or result type. *** ## Methods ### `query()` ```dart theme={null} Future> query( String q, { String? framework, String? resultType, int? page, int? perPage, }) ``` **Parameters** | Parameter | Type | Description | | ------------ | --------- | --------------------------------------------- | | `q` | `String` | Search query | | `framework` | `String?` | Filter by framework slug (e.g. `gdpr`) | | `resultType` | `String?` | Filter by type: `article`, `recital`, `annex` | | `page` | `int?` | Page number | | `perPage` | `int?` | Items per page | ### `iter()` ```dart theme={null} Stream iter(String q, {String? framework, String? resultType, int perPage = 20}) ``` *** ## Model ### `SearchResult` | Field | Type | Description | | --------------- | --------- | -------------------------------- | | `id` | `int` | Internal ID | | `frameworkSlug` | `String` | Source framework | | `type` | `String` | `article`, `recital`, or `annex` | | `number` | `String` | Item number | | `title` | `String?` | Item title | | `matchContext` | `String?` | Surrounding matched text snippet | *** ## Examples ### Basic search ```dart theme={null} final results = await client.search.query('incident notification 72 hours'); print('Found ${results.meta.total} results'); for (final r in results.data) { print('[${r.frameworkSlug.toUpperCase()}] ${r.type} ${r.number}: ${r.title}'); } ``` ### Search within a specific framework ```dart theme={null} final results = await client.search.query( 'data breach', framework: 'gdpr', ); ``` ### Filter by type ```dart theme={null} final results = await client.search.query( 'supply chain security', resultType: 'article', ); ``` ### Auto-paginate search results ```dart theme={null} await for (final r in client.search.iter('personal data')) { print('[${r.frameworkSlug}] ${r.number} — ${r.title}'); } ``` # Tags Source: https://docs.law4devs.eu/sdks/flutter/tags Access semantic tags with the Flutter SDK. ## Overview Tags provide semantic classification for articles across all frameworks. Use them to find related articles across different regulations. *** ## Methods ### `list()` ```dart theme={null} Future> list({int? page, int? perPage}) ``` ### `get()` ```dart theme={null} Future get(String slug) ``` ### `iter()` ```dart theme={null} Stream iter({int perPage = 20}) ``` *** ## Model ### `Tag` | Field | Type | Description | | ------------- | -------------- | --------------------- | | `id` | `int` | Internal ID | | `slug` | `String` | URL-safe identifier | | `name` | `String` | Display name | | `description` | `String?` | Description | | `keywords` | `List` | Associated keywords | | `color` | `String?` | Hex color for display | | `createdAt` | `String` | ISO 8601 timestamp | *** ## Examples ### List all tags ```dart theme={null} final page = await client.tags.list(); for (final t in page.data) { print('${t.name} — ${t.slug}'); } ``` ### Get a specific tag ```dart theme={null} final tag = await client.tags.get('data-protection'); print('Keywords: ${tag.keywords.join(', ')}'); ``` ### Auto-paginate ```dart theme={null} await for (final t in client.tags.iter()) { print(t.name); } ``` # SDKs Source: https://docs.law4devs.eu/sdks/index Official Law4Devs SDKs for Python, TypeScript, PHP, Java, Flutter, and Rust

Choose Your SDK

Official client libraries for Python, TypeScript, PHP, Java, Flutter, and Rust — all with full feature parity.

`pip install law4devs` · Python 3.8+ `npm install law4devs` · Node 18+ / Deno / Bun `composer require law4devs/law4devs` · PHP 8.1+ Maven · Java 17+ · Zero dependencies `pub add law4devs` · Dart 3+ · Flutter & CLI `cargo add law4devs` · Rust 1.85+ · Tokio async ## Feature Comparison | Feature | Python | TypeScript | PHP | Java | Flutter | Rust | | ----------------------------- | :-------: | :--------------: | :-: | :--------: | :-------------: | :-----------: | | Auto-pagination with `iter()` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | Full-text search | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | Rate limit retry with backoff | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | Async / non-blocking support | ✓ asyncio | ✓ Promise/async | — | — | ✓ Future/Stream | ✓ Tokio/async | | Type safety | ✓ hints | ✓ interfaces | — | ✓ Records | ✓ null-safe | ✓ ownership | | Stream-based iteration | — | ✓ AsyncGenerator | — | ✓ Iterable | ✓ Stream | ✓ Stream | | Framework filtering | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | Compliance deadline queries | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | Mobile / web compatible | — | — | — | — | ✓ | — | ## Which SDK Should I Use? You're building backend automation, data pipelines, or scripts. Best for compliance engineers who want to process and analyse regulatory data programmatically. You're building Node.js APIs, serverless functions, or browser-side tools. Best when you need async operations and strong type safety. You're working in a Laravel, Symfony, or WordPress application. The PHP SDK integrates directly into Composer-based projects with no extra setup. You're building JVM-based services, Spring Boot applications, or enterprise backend systems. The Java SDK uses only stdlib — no external dependencies required. You're building a mobile, desktop, or web Flutter app, or a Dart CLI tool. The only SDK with full Flutter Web compatibility and Stream-based auto-pagination. You're building high-performance backend services, CLI tools, or systems software where memory safety and zero-cost abstractions matter. Best for latency-critical applications that need async Tokio runtimes and compile-time type guarantees. ## Supported Frameworks All 19 EU regulatory frameworks are available across every SDK — from GDPR and CRA to the AI Act, DORA, NIS2, and beyond. Full list of 19 EU regulations with CELEX numbers and status # Annexes Source: https://docs.law4devs.eu/sdks/java/annexes List and retrieve annexes from EU regulatory frameworks with the Law4Devs Java SDK. ## Overview Annexes contain supplementary material referenced by articles — including technical specifications, classification tables, and approved lists. Use `client.annexes()` to access them. *** ## Methods ### `list(String frameworkSlug)` Returns the first page of annexes for a framework. ```java theme={null} Page page = client.annexes().list("cra"); for (AnnexSummary a : page.data()) { System.out.println("Annex " + a.number() + ": " + a.title()); } ``` **Returns** `Page` *** ### `list(String frameworkSlug, int page, int perPage)` Fetch a specific page with custom page size. ```java theme={null} Page page = client.annexes().list("cra", 1, 10); ``` **Parameters** | Parameter | Type | Default | Description | | --------------- | -------- | -------- | ---------------------------- | | `frameworkSlug` | `String` | required | Framework slug, e.g. `"cra"` | | `page` | `int` | `1` | Page number (1-indexed) | | `perPage` | `int` | `20` | Items per page (max 100) | **Returns** `Page` *** ### `get()` Fetch full detail for a single annex including all sections. ```java theme={null} Annex annex = client.annexes().get("cra", "I"); System.out.println(annex.title()); // Essential cybersecurity requirements for (var section : annex.sections()) { System.out.println(section.reference() + ": " + section.text()); } ``` **Parameters** | Parameter | Type | Description | | --------------- | -------- | ------------------------------------ | | `frameworkSlug` | `String` | Framework slug | | `annexNumber` | `String` | Annex identifier, e.g. `"I"`, `"II"` | **Returns** `Annex` *** ### `iter()` Lazily iterate all annexes for a framework. ```java theme={null} for (AnnexSummary a : client.annexes().iter("cra")) { System.out.println("Annex " + a.number() + ": " + a.title()); } ``` **Returns** `Iterable` *** ## Models ### `AnnexSummary` Returned by `list()` and `iter()`. | Field | Type | Description | | ----------------- | -------- | ---------------------------- | | `id()` | `int` | Internal numeric ID | | `number()` | `String` | Annex identifier, e.g. `"I"` | | `title()` | `String` | Annex title | | `frameworkSlug()` | `String` | Parent framework slug | | `sectionCount()` | `int` | Number of sections | ### `Annex` Returned by `get()`. Includes all `AnnexSummary` fields plus: | Field | Type | Description | | ------------ | -------------------- | -------------------------- | | `sections()` | `List` | All sections of this annex | ### `AnnexSection` | Field | Type | Description | | ------------- | -------- | --------------------------------------- | | `reference()` | `String` | Section reference, e.g. `"1"`, `"1(a)"` | | `text()` | `String` | Full section text | *** ## Examples ### List all CRA annexes ```java theme={null} for (AnnexSummary a : client.annexes().iter("cra")) { System.out.printf("Annex %-4s %s (%d sections)%n", a.number(), a.title(), a.sectionCount()); } ``` ### Extract all security requirements from CRA Annex I ```java theme={null} Annex annexI = client.annexes().get("cra", "I"); System.out.println("# " + annexI.title() + "\n"); for (var section : annexI.sections()) { System.out.println(section.reference() + ". " + section.text()); } ``` # Articles Source: https://docs.law4devs.eu/sdks/java/articles List and retrieve articles from EU regulatory frameworks with the Law4Devs Java SDK. ## Overview Articles are the numbered provisions within each regulation. Use `client.articles()` to list, fetch, and iterate articles for any framework. *** ## Methods ### `list(String frameworkSlug)` Returns the first page of articles for a framework. ```java theme={null} Page page = client.articles().list("gdpr"); for (ArticleSummary a : page.data()) { System.out.println("Art. " + a.number() + " — " + a.title()); } ``` **Returns** `Page` *** ### `list(String frameworkSlug, int page, int perPage)` Fetch a specific page with custom page size. ```java theme={null} Page page = client.articles().list("cra", 1, 10); ``` **Parameters** | Parameter | Type | Default | Description | | --------------- | -------- | -------- | ----------------------------- | | `frameworkSlug` | `String` | required | Framework slug, e.g. `"gdpr"` | | `page` | `int` | `1` | Page number (1-indexed) | | `perPage` | `int` | `20` | Items per page (max 100) | **Returns** `Page` *** ### `get()` Fetch full detail for a single article including all paragraphs. ```java theme={null} Article article = client.articles().get("gdpr", "17"); System.out.println(article.title()); // Right to erasure System.out.println(article.number()); // 17 for (var p : article.paragraphs()) { System.out.println(p.reference() + ": " + p.text()); } ``` **Parameters** | Parameter | Type | Description | | --------------- | -------- | -------------------------------------- | | `frameworkSlug` | `String` | Framework slug | | `articleNumber` | `String` | Article number, e.g. `"17"` or `"17a"` | **Returns** `Article` *** ### `related()` Fetch articles related to a given article (cross-references). ```java theme={null} Page related = client.articles().related("gdpr", "17"); for (ArticleSummary a : related.data()) { System.out.println("Related: Art. " + a.number() + " — " + a.title()); } ``` **Returns** `Page` *** ### `iter()` Lazily iterate all articles for a framework. ```java theme={null} for (ArticleSummary a : client.articles().iter("gdpr")) { System.out.println(a.number() + " " + a.title()); } ``` **Returns** `Iterable` *** ## Models ### `ArticleSummary` Returned by `list()`, `related()`, and `iter()`. | Field | Type | Description | | ------------------ | -------- | --------------------------- | | `id()` | `int` | Internal numeric ID | | `number()` | `String` | Article number, e.g. `"17"` | | `title()` | `String` | Article title | | `frameworkSlug()` | `String` | Parent framework slug | | `paragraphCount()` | `int` | Number of paragraphs | ### `Article` Returned by `get()`. Includes all `ArticleSummary` fields plus: | Field | Type | Description | | -------------- | ------------------------ | ----------------------------- | | `paragraphs()` | `List` | All paragraphs of the article | | `tags()` | `List` | Associated compliance tags | ### `ArticleParagraph` | Field | Type | Description | | ------------- | -------- | ----------------------------------------- | | `reference()` | `String` | Paragraph reference, e.g. `"1"`, `"1(a)"` | | `text()` | `String` | Full paragraph text | *** ## Examples ### Print all NIS2 articles ```java theme={null} for (ArticleSummary a : client.articles().iter("nis2")) { System.out.printf("Art. %-4s %s%n", a.number(), a.title()); } ``` ### Find articles containing specific obligations ```java theme={null} Article art = client.articles().get("gdpr", "33"); System.out.println(art.title()); // Notification of a personal data breach for (var p : art.paragraphs()) { if (p.text().contains("72 hours")) { System.out.println("72-hour rule in: " + p.reference()); } } ``` ### Collect all DORA articles into a list ```java theme={null} var all = new java.util.ArrayList(); for (ArticleSummary a : client.articles().iter("dora")) { all.add(a); } System.out.println("Total DORA articles: " + all.size()); ``` # Compliance Deadlines Source: https://docs.law4devs.eu/sdks/java/compliance Query compliance deadlines and enforcement dates with the Law4Devs Java SDK. ## Overview Compliance deadlines track the key enforcement dates, transition periods, and application dates for each regulation. Use `client.compliance()` to access them. *** ## Methods ### `deadlines()` Returns the first page of compliance deadlines across all frameworks. ```java theme={null} Page page = client.compliance().deadlines(); for (ComplianceDeadline d : page.data()) { System.out.printf("[%s] %s — %s%n", d.frameworkSlug(), d.date(), d.description()); } ``` **Returns** `Page` *** ### `deadlines(String frameworkSlug)` Returns the first page of deadlines scoped to one framework. ```java theme={null} Page page = client.compliance().deadlines("nis2"); ``` **Returns** `Page` *** ### `deadlines(String frameworkSlug, int page, int perPage)` Fetch a specific page with custom page size. ```java theme={null} Page page = client.compliance().deadlines("dora", 1, 50); ``` **Parameters** | Parameter | Type | Default | Description | | --------------- | -------- | ------- | --------------------------------- | | `frameworkSlug` | `String` | `null` | Scope to one framework (optional) | | `page` | `int` | `1` | Page number (1-indexed) | | `perPage` | `int` | `20` | Items per page (max 100) | **Returns** `Page` *** ### `iterDeadlines()` Lazily iterate all compliance deadlines across all frameworks. ```java theme={null} for (ComplianceDeadline d : client.compliance().iterDeadlines()) { System.out.println(d.frameworkSlug() + " — " + d.date() + ": " + d.description()); } ``` **Returns** `Iterable` *** ### `iterDeadlines(String frameworkSlug)` Lazily iterate all deadlines for a specific framework. ```java theme={null} for (ComplianceDeadline d : client.compliance().iterDeadlines("cra")) { System.out.println(d.date() + ": " + d.description()); } ``` **Returns** `Iterable` *** ## Model ### `ComplianceDeadline` | Field | Type | Description | | -------------------- | -------- | ----------------------------------------------------- | | `id()` | `int` | Internal numeric ID | | `frameworkSlug()` | `String` | Parent framework slug | | `frameworkName()` | `String` | Human-readable framework name | | `date()` | `String` | ISO 8601 deadline date | | `description()` | `String` | What must be complied with by this date | | `deadlineType()` | `String` | e.g. `"enforcement"`, `"transition"`, `"application"` | | `articleReference()` | `String` | Source article or recital reference | *** ## Examples ### Print upcoming CRA deadlines in order ```java theme={null} var deadlines = new java.util.ArrayList(); for (ComplianceDeadline d : client.compliance().iterDeadlines("cra")) { deadlines.add(d); } deadlines.sort(java.util.Comparator.comparing(ComplianceDeadline::date)); for (ComplianceDeadline d : deadlines) { System.out.printf("%s %s%n", d.date(), d.description()); } ``` ### List all enforcement deadlines across frameworks ```java theme={null} for (ComplianceDeadline d : client.compliance().iterDeadlines()) { if ("enforcement".equals(d.deadlineType())) { System.out.printf("[%-6s] %s — %s%n", d.frameworkSlug(), d.date(), d.description()); } } ``` ### Build a timeline report ```java theme={null} var byFramework = new java.util.TreeMap>(); for (ComplianceDeadline d : client.compliance().iterDeadlines()) { byFramework.computeIfAbsent(d.frameworkSlug(), k -> new java.util.ArrayList<>()).add(d); } byFramework.forEach((fw, list) -> { System.out.println("\n## " + fw.toUpperCase()); list.forEach(d -> System.out.println(" " + d.date() + " — " + d.description())); }); ``` # Error Handling Source: https://docs.law4devs.eu/sdks/java/error-handling Handle API errors gracefully with the Law4Devs Java SDK. ## Exception Hierarchy All SDK exceptions extend `eu.law4devs.sdk.exceptions.Law4DevsException`, which itself extends `RuntimeException`. ``` RuntimeException └── Law4DevsException base — any SDK error ├── NotFoundError 404 — resource not found ├── ValidationError 400 — invalid parameters ├── RateLimitError 429 — rate limit exceeded └── ServerError 5xx — server-side error ``` Every exception exposes a `getStatusCode()` method: ```java theme={null} try { Article article = client.articles().get("gdpr", 9999); } catch (NotFoundError e) { System.out.println("Not found: " + e.getMessage()); // message from API System.out.println("Status: " + e.getStatusCode()); // 404 } ``` *** ## Catching Specific Errors ```java theme={null} import eu.law4devs.sdk.exceptions.Law4DevsException; import eu.law4devs.sdk.exceptions.NotFoundError; import eu.law4devs.sdk.exceptions.RateLimitError; import eu.law4devs.sdk.exceptions.ValidationError; try { var page = client.articles().list("invalid-framework"); } catch (NotFoundError e) { // Framework not found System.out.println("Framework not found: " + e.getMessage()); } catch (ValidationError e) { // Bad request parameters System.out.println("Invalid request: " + e.getMessage()); } catch (RateLimitError e) { // Retry after a delay (SDK already retried 3 times) System.out.println("Rate limit hit: " + e.getMessage()); } catch (Law4DevsException e) { // All other SDK errors (server errors, network failures) System.out.println("SDK error (" + e.getStatusCode() + "): " + e.getMessage()); } ``` *** ## Automatic Retry The SDK automatically retries on `429 Too Many Requests` and `5xx Server Error` responses using exponential backoff: | Attempt | Wait | | --------- | --------- | | 1st retry | 1 second | | 2nd retry | 2 seconds | | 3rd retry | 4 seconds | After `maxRetries` (default `3`) attempts, the exception is re-thrown. To disable retries, set `maxRetries(0)`: ```java theme={null} Law4DevsClient client = Law4DevsClient.builder() .apiKey(System.getenv("LAW4DEVS_API_KEY")) .maxRetries(0) .build(); ``` `NotFoundError` (404) and `ValidationError` (400) are never retried — they represent client errors that will not resolve by retrying. *** ## Network Errors Connection failures (timeout, DNS, SSL) throw `Law4DevsException` with a message describing the failure: ```java theme={null} import eu.law4devs.sdk.exceptions.Law4DevsException; try { var page = client.frameworks().list(); } catch (Law4DevsException e) { System.out.println("SDK error: " + e.getMessage()); } ``` *** ## Errors in `iter()` Loops Exceptions can be thrown mid-iteration if a page request fails. Wrap `iter()` loops in a try/catch: ```java theme={null} try { for (ArticleSummary a : client.articles().iter("gdpr")) { System.out.println(a.title()); } } catch (Law4DevsException e) { System.out.println("Error mid-iteration: " + e.getMessage()); } ``` *** ## Best Practices * Always catch `Law4DevsException` as a fallback to handle unexpected errors * Wrap `iter()` loops in a try/catch — exceptions can occur mid-iteration if a page request fails * Use `getStatusCode()` to distinguish error types when handling `Law4DevsException` generically * For batch processing, implement a retry strategy at the application level if you need finer control than the built-in retry * Prefer specific exception types (`NotFoundError`, `RateLimitError`) over the base class for cleaner error handling # Frameworks Source: https://docs.law4devs.eu/sdks/java/frameworks List and retrieve EU regulatory frameworks with the Law4Devs Java SDK. ## Overview Frameworks represent EU regulations such as GDPR, CRA, NIS2, and the AI Act. Use `client.frameworks()` to list and fetch them. *** ## Methods ### `list()` Returns a page of all available frameworks. ```java theme={null} Page page = client.frameworks().list(); System.out.printf("Found %d frameworks%n", page.meta().total()); for (Framework fw : page.data()) { System.out.printf(" %-12s %s%n", fw.slug(), fw.name()); } ``` **Returns** `Page` *** ### `list(int page, int perPage)` Fetch a specific page with custom page size. ```java theme={null} Page page = client.frameworks().list(1, 5); ``` **Parameters** | Parameter | Type | Default | Description | | --------- | ----- | ------- | ------------------------ | | `page` | `int` | `1` | Page number (1-indexed) | | `perPage` | `int` | `20` | Items per page (max 100) | **Returns** `Page` *** ### `get()` Fetch full detail for a single framework by slug. ```java theme={null} FrameworkDetail cra = client.frameworks().get("cra"); System.out.println(cra.name()); // Cyber Resilience Act System.out.println(cra.celexNumber()); // 32024R2847 System.out.println(cra.articleCount()); // number of synced articles ``` **Parameters** | Parameter | Type | Description | | --------- | -------- | ----------------------------- | | `slug` | `String` | Framework slug, e.g. `"gdpr"` | **Returns** `FrameworkDetail` *** ### `iter()` Lazily iterate all frameworks across all pages. ```java theme={null} for (Framework fw : client.frameworks().iter()) { System.out.println(fw.slug() + ": " + fw.name()); } ``` **Returns** `Iterable` *** ## Models ### `Framework` Returned by `list()` and `iter()`. | Field | Type | Description | | ---------------- | --------- | ---------------------------------------- | | `id()` | `int` | Internal numeric ID | | `slug()` | `String` | URL-safe identifier, e.g. `"cra"` | | `name()` | `String` | Full framework name | | `shortName()` | `String` | Short name, e.g. `"CRA"` | | `celexNumber()` | `String` | EUR-Lex CELEX identifier | | `description()` | `String` | Brief description | | `isActive()` | `boolean` | Whether data is actively maintained | | `status()` | `String` | `"active"`, `"draft"`, or `"superseded"` | | `articleCount()` | `int` | Number of synced articles | | `recitalCount()` | `int` | Number of synced recitals | | `createdAt()` | `String` | ISO 8601 creation timestamp | ### `FrameworkDetail` Returned by `get()`. Includes all `Framework` fields plus: | Field | Type | Description | | -------------------- | -------- | --------------------------------- | | `eurLexUrl()` | `String` | Link to the official EUR-Lex page | | `requirementCount()` | `int` | Number of extracted requirements | | `annexCount()` | `int` | Number of annexes | | `tagCount()` | `int` | Number of associated tags | *** ## Examples ### List all active frameworks ```java theme={null} for (Framework fw : client.frameworks().iter()) { if (fw.isActive()) { System.out.printf("%-12s %s (%d articles)%n", fw.slug(), fw.name(), fw.articleCount()); } } ``` ### Build a framework lookup map ```java theme={null} var map = new java.util.LinkedHashMap(); for (Framework fw : client.frameworks().iter()) { map.put(fw.slug(), fw); } Framework gdpr = map.get("gdpr"); System.out.println(gdpr.name()); // General Data Protection Regulation ``` ### Print frameworks with full article coverage ```java theme={null} var page = client.frameworks().list(); for (Framework fw : page.data()) { if (fw.expectedArticles() > 0 && fw.articleCount() >= fw.expectedArticles()) { System.out.println(fw.name() + " — fully synced"); } } ``` # Installation Source: https://docs.law4devs.eu/sdks/java/installation Install the Law4Devs Java SDK and make your first API call. ## Requirements * Java 17 or later * Maven 3.6+ (build tool) * No runtime dependencies — uses Java standard library only ## Install Add to your `pom.xml`: ```xml theme={null} eu.law4devs.sdk java 1.0.0 ``` Then run: ```bash theme={null} mvn install ``` To pin to a specific version: ```xml theme={null} eu.law4devs.sdk java 1.0.0 ``` ## First Call ```java theme={null} import eu.law4devs.sdk.Law4DevsClient; import eu.law4devs.sdk.models.Framework; import eu.law4devs.sdk.pagination.Page; Law4DevsClient client = Law4DevsClient.builder() .apiKey(System.getenv("LAW4DEVS_API_KEY")) .build(); Page page = client.frameworks().list(); System.out.println(page.data().get(0).name()); // e.g. "Cyber Resilience Act" ``` ## Configuration ```java theme={null} import eu.law4devs.sdk.Law4DevsClient; import java.time.Duration; Law4DevsClient client = Law4DevsClient.builder() .apiKey(System.getenv("LAW4DEVS_API_KEY")) .baseUrl("https://api.law4devs.eu/v1") // default .timeout(Duration.ofSeconds(30)) // default 30s .maxRetries(3) // retries on 429/5xx, default 3 .build(); ``` | Option | Type | Default | Description | | ------------ | ---------- | ------------------------ | --------------------------------------------------- | | `apiKey` | `String` | `null` | Your API key (required for authenticated endpoints) | | `baseUrl` | `String` | Production URL | Override for dev/staging environment | | `timeout` | `Duration` | `Duration.ofSeconds(30)` | Per-request HTTP timeout | | `maxRetries` | `int` | `3` | Retries on 429/5xx responses | ## Environment Variable ```bash theme={null} export LAW4DEVS_API_KEY="your-key" ``` ```java theme={null} Law4DevsClient client = Law4DevsClient.builder() .apiKey(System.getenv("LAW4DEVS_API_KEY")) .build(); ``` Never hardcode your API key in source code or commit it to version control. ## Verify the Install ```java theme={null} // VerifyInstall.java import eu.law4devs.sdk.Law4DevsClient; public class VerifyInstall { public static void main(String[] args) { var client = Law4DevsClient.builder() .apiKey(System.getenv("LAW4DEVS_API_KEY")) .build(); System.out.println(client.frameworks().list().meta().total() + " frameworks"); } } ``` ```bash theme={null} LAW4DEVS_API_KEY=your-key mvn compile exec:java -Dexec.mainClass="VerifyInstall" # → 19 frameworks ``` # Pagination Source: https://docs.law4devs.eu/sdks/java/pagination How pagination works in the Law4Devs Java SDK. ## Overview All `list()` methods return a `Page` object. Every page includes the data list, metadata about the total result set, and links to the next/previous page. *** ## The `Page` Object ```java theme={null} Page page = client.articles().list("gdpr"); // Data List articles = page.data(); // items on this page int count = page.data().size(); // number of items returned // Metadata int total = page.meta().total(); // total results across all pages int current = page.meta().page(); // current page number int perPage = page.meta().perPage(); // items per page int pages = page.meta().pages(); // total number of pages // Links String nextUrl = page.links().next(); // URL of next page (null if last) String prevUrl = page.links().prev(); // URL of previous page (null if first) ``` All model fields in Java records are accessed via method calls (e.g. `page.meta().total()`, not `page.meta().total`). *** ## Manual Pagination Control pagination yourself with `page` and `perPage` parameters: ```java theme={null} int pageNum = 1; Page page; do { page = client.articles().list("gdpr", pageNum, 50); for (ArticleSummary a : page.data()) { System.out.println(a.title()); } pageNum++; } while (page.links().next() != null); ``` *** ## Auto-Pagination with `iter()` Every resource exposes an `iter()` method that automatically fetches all pages and yields individual items: ```java theme={null} // Yields ArticleSummary one by one — no manual paging needed for (ArticleSummary a : client.articles().iter("gdpr")) { System.out.println(a.number() + " " + a.title()); } ``` Internally, `iter()` uses `PageIterator`, which calls `list()` repeatedly and follows `links().next()` until there are no more pages. Use `iter()` whenever you need all items. It handles pagination transparently without loading the full result set into memory at once — each page is fetched only when the previous one is exhausted. *** ## Collecting into a List If you need all results in a `List`, iterate and collect: ```java theme={null} var all = new java.util.ArrayList(); for (ArticleSummary a : client.articles().iter("cra")) { all.add(a); } System.out.println(all.size() + " total articles"); ``` Or with streams: ```java theme={null} import java.util.stream.StreamSupport; var all = StreamSupport .stream(client.articles().iter("cra").spliterator(), false) .toList(); System.out.println(all.size() + " total articles"); ``` *** ## Early Stopping Break out of an `iter()` loop at any point — no extra pages are fetched after the break: ```java theme={null} for (ArticleSummary a : client.articles().iter("gdpr")) { if (a.number().equals("50")) { System.out.println("Found Art. 50: " + a.title()); break; } } ``` *** ## Progress Reporting ```java theme={null} Page first = client.articles().list("gdpr", 1, 20); int total = first.meta().total(); int processed = 0; for (ArticleSummary a : client.articles().iter("gdpr")) { processed++; System.out.printf("\r%d / %d", processed, total); } System.out.println(); ``` *** ## Pagination Parameters | Parameter | Type | Default | Description | | --------- | ----- | ------- | ------------------------ | | `page` | `int` | `1` | Page number (1-indexed) | | `perPage` | `int` | `20` | Items per page (max 100) | # Quick Start Source: https://docs.law4devs.eu/sdks/java/quickstart Make your first Law4Devs API call with Java in under 60 seconds. ## Install Add to your `pom.xml`: ```xml theme={null} eu.law4devs.sdk java 1.0.0 ``` ## Basic Usage ```java theme={null} import eu.law4devs.sdk.Law4DevsClient; import eu.law4devs.sdk.models.ArticleSummary; import eu.law4devs.sdk.models.Framework; var client = Law4DevsClient.builder() .apiKey(System.getenv("LAW4DEVS_API_KEY")) .build(); // List all frameworks var page = client.frameworks().list(); for (Framework fw : page.data()) { System.out.println(fw.slug() + " — " + fw.name()); } // List first 5 CRA articles var articles = client.articles().list("cra", 1, 5); for (ArticleSummary a : articles.data()) { System.out.println("Art. " + a.number() + ": " + a.title()); } // Auto-paginate all GDPR articles for (ArticleSummary a : client.articles().iter("gdpr")) { System.out.println(a.number() + " " + a.title()); } // Search across all frameworks var results = client.search().query("data breach notification"); System.out.println("Found " + results.meta().total() + " results"); ``` ## Next Steps # Recitals Source: https://docs.law4devs.eu/sdks/java/recitals List and retrieve recitals from EU regulatory frameworks with the Law4Devs Java SDK. ## Overview Recitals are the numbered preamble paragraphs that provide context and legislative intent for each regulation. Use `client.recitals()` to access them. *** ## Methods ### `list(String frameworkSlug)` Returns the first page of recitals for a framework. ```java theme={null} Page page = client.recitals().list("gdpr"); for (Recital r : page.data()) { System.out.println("Recital " + r.number() + ": " + r.title()); } ``` **Returns** `Page` *** ### `list(String frameworkSlug, int page, int perPage)` Fetch a specific page with custom page size. ```java theme={null} Page page = client.recitals().list("cra", 1, 50); ``` **Parameters** | Parameter | Type | Default | Description | | --------------- | -------- | -------- | ----------------------------- | | `frameworkSlug` | `String` | required | Framework slug, e.g. `"gdpr"` | | `page` | `int` | `1` | Page number (1-indexed) | | `perPage` | `int` | `20` | Items per page (max 100) | **Returns** `Page` *** ### `get()` Fetch a single recital by number. ```java theme={null} Recital r = client.recitals().get("gdpr", "4"); System.out.println(r.title()); // Right to the protection of personal data System.out.println(r.text()); // Full recital text ``` **Parameters** | Parameter | Type | Description | | --------------- | -------- | -------------------------- | | `frameworkSlug` | `String` | Framework slug | | `recitalNumber` | `String` | Recital number, e.g. `"4"` | **Returns** `Recital` *** ### `iter()` Lazily iterate all recitals for a framework. ```java theme={null} for (Recital r : client.recitals().iter("nis2")) { System.out.println("Recital " + r.number() + ": " + r.title()); } ``` **Returns** `Iterable` *** ## Model ### `Recital` | Field | Type | Description | | ----------------- | -------- | -------------------------- | | `id()` | `int` | Internal numeric ID | | `number()` | `String` | Recital number, e.g. `"4"` | | `title()` | `String` | Short title or topic | | `text()` | `String` | Full recital text | | `frameworkSlug()` | `String` | Parent framework slug | *** ## Examples ### Search recitals for a keyword ```java theme={null} for (Recital r : client.recitals().iter("gdpr")) { if (r.text().contains("consent")) { System.out.println("Recital " + r.number() + ": " + r.title()); } } ``` ### Build a context document for legal review ```java theme={null} var sb = new StringBuilder(); sb.append("# CRA Recitals\n\n"); for (Recital r : client.recitals().iter("cra")) { sb.append("## Recital ").append(r.number()).append("\n"); sb.append(r.text()).append("\n\n"); } System.out.println(sb); ``` # Requirements Source: https://docs.law4devs.eu/sdks/java/requirements Query extracted compliance requirements with the Law4Devs Java SDK. ## Overview Requirements are structured, machine-readable compliance obligations extracted from articles. They are classified by type and stakeholder role. Use `client.requirements()` to list and iterate them. *** ## Methods ### `list()` Returns the first page of requirements across all frameworks. ```java theme={null} Page page = client.requirements().list(); for (Requirement r : page.data()) { System.out.printf("[%s] %s — %s%n", r.frameworkSlug(), r.requirementType(), r.description()); } ``` **Returns** `Page` *** ### `list(String frameworkSlug)` Returns the first page of requirements scoped to a specific framework. ```java theme={null} Page page = client.requirements().list("cra"); ``` **Returns** `Page` *** ### `list(String frameworkSlug, int page, int perPage)` Fetch a specific page with custom page size. ```java theme={null} Page page = client.requirements().list("nis2", 1, 50); ``` **Parameters** | Parameter | Type | Default | Description | | --------------- | -------- | ------- | --------------------------------- | | `frameworkSlug` | `String` | `null` | Scope to one framework (optional) | | `page` | `int` | `1` | Page number (1-indexed) | | `perPage` | `int` | `20` | Items per page (max 100) | **Returns** `Page` *** ### `iter()` Lazily iterate all requirements across all frameworks. ```java theme={null} for (Requirement r : client.requirements().iter()) { System.out.println(r.description()); } ``` **Returns** `Iterable` *** ### `iter(String frameworkSlug)` Lazily iterate all requirements for a specific framework. ```java theme={null} for (Requirement r : client.requirements().iter("dora")) { System.out.printf(" [%s] %s%n", r.requirementType(), r.description()); } ``` **Returns** `Iterable` *** ## Model ### `Requirement` | Field | Type | Description | | ------------------- | -------- | -------------------------------------------------- | | `id()` | `int` | Internal numeric ID | | `frameworkSlug()` | `String` | Parent framework slug | | `articleNumber()` | `String` | Source article number | | `requirementType()` | `String` | e.g. `"MUST"`, `"SHOULD"`, `"MAY"` | | `description()` | `String` | Human-readable requirement description | | `stakeholderRole()` | `String` | Target role, e.g. `"manufacturer"`, `"controller"` | | `legalReference()` | `String` | Article and paragraph reference | *** ## Examples ### List MUST requirements for CRA manufacturers ```java theme={null} for (Requirement r : client.requirements().iter("cra")) { if ("MUST".equals(r.requirementType()) && "manufacturer".equals(r.stakeholderRole())) { System.out.println("Art. " + r.articleNumber() + ": " + r.description()); } } ``` ### Count requirements by framework ```java theme={null} var counts = new java.util.LinkedHashMap(); for (Requirement r : client.requirements().iter()) { counts.merge(r.frameworkSlug(), 1, Integer::sum); } counts.forEach((fw, n) -> System.out.printf("%-12s %d requirements%n", fw, n)); ``` ### Build a compliance checklist ```java theme={null} var checklist = new java.util.ArrayList(); for (Requirement r : client.requirements().iter("gdpr")) { if ("controller".equals(r.stakeholderRole())) { checklist.add("[ ] " + r.description()); } } checklist.forEach(System.out::println); ``` # Search Source: https://docs.law4devs.eu/sdks/java/search Full-text search across all EU frameworks with the Law4Devs Java SDK. ## Overview The search endpoint lets you query across all frameworks simultaneously. Results include articles, recitals, and requirements that match your query. Use `client.search().query()` to search. *** ## Methods ### `query(String q)` Search across all frameworks with default pagination. ```java theme={null} Page results = client.search().query("incident notification 72 hours"); System.out.printf("Found %d results%n", results.meta().total()); for (SearchResult r : results.data()) { System.out.printf(" [%s] %s — %s%n", r.frameworkSlug().toUpperCase(), r.type(), r.title() != null ? r.title() : r.matchContext()); } ``` **Returns** `Page` *** ### `query(String q, String framework, String resultType, int page, int perPage)` Search with optional framework scope, result type filter, and custom pagination. ```java theme={null} Page results = client.search().query( "breach notification", // query "gdpr", // framework filter (null = all) "article", // result type filter (null = all) 1, // page 20 // perPage ); ``` **Parameters** | Parameter | Type | Default | Description | | ------------ | -------- | -------- | --------------------------------------------------------- | | `q` | `String` | required | Search query | | `framework` | `String` | `null` | Limit to one framework slug | | `resultType` | `String` | `null` | Filter by type: `"article"`, `"recital"`, `"requirement"` | | `page` | `int` | `1` | Page number | | `perPage` | `int` | `20` | Items per page | **Returns** `Page` *** ### `iter(String q)` Lazily iterate all search results for a query. ```java theme={null} for (SearchResult r : client.search().iter("cyber resilience")) { System.out.printf("[%s] %s%n", r.frameworkSlug(), r.matchContext()); } ``` **Returns** `Iterable` *** ### `iter(String q, String framework, String resultType)` Lazily iterate with optional framework and result-type filters. ```java theme={null} for (SearchResult r : client.search().iter("encryption", "cra", "requirement")) { System.out.println(r.matchContext()); } ``` **Returns** `Iterable` *** ## Model ### `SearchResult` | Field | Type | Description | | ------------------- | -------- | -------------------------------------------- | | `type()` | `String` | `"article"`, `"recital"`, or `"requirement"` | | `frameworkSlug()` | `String` | Framework the result belongs to | | `frameworkName()` | `String` | Human-readable framework name | | `articleNumber()` | `String` | For article results | | `recitalNumber()` | `String` | For recital results | | `paragraphRef()` | `String` | Specific paragraph reference | | `title()` | `String` | Result heading, if any | | `requirementType()` | `String` | For requirement results | | `matchContext()` | `String` | Snippet showing the match in context | | `url()` | `String` | Direct API URL to the full resource | *** ## Examples ### Search within a specific framework ```java theme={null} Page results = client.search().query( "breach notification", "gdpr", null, 1, 20); for (SearchResult r : results.data()) { System.out.println(r.frameworkSlug() + " — " + (r.title() != null ? r.title() : r.matchContext())); } ``` ### Filter by result type ```java theme={null} Page results = client.search().query( "encryption at rest", null, "requirement", 1, 20); for (SearchResult r : results.data()) { System.out.println("- " + r.matchContext()); } ``` ### Cross-framework comparison ```java theme={null} String[] frameworks = {"nis2", "dora", "cra"}; for (String fw : frameworks) { Page r = client.search().query( "incident notification", fw, null, 1, 1); System.out.printf("%-6s %d results%n", fw, r.meta().total()); } ``` Use the `framework` parameter to narrow results when you know which regulation you're working with. Cross-framework search (without a filter) is great for discovering related obligations across different laws. # Tags Source: https://docs.law4devs.eu/sdks/java/tags List and retrieve compliance tags with the Law4Devs Java SDK. ## Overview Tags are semantic labels that categorise articles and requirements by topic (e.g. `data-breach`, `encryption`, `audit`). Use `client.tags()` to list and fetch them. *** ## Methods ### `list()` Returns the first page of all available tags. ```java theme={null} Page page = client.tags().list(); for (Tag t : page.data()) { System.out.println(t.slug() + " — " + t.name()); } ``` **Returns** `Page` *** ### `list(int page, int perPage)` Fetch a specific page with custom page size. ```java theme={null} Page page = client.tags().list(1, 50); ``` **Parameters** | Parameter | Type | Default | Description | | --------- | ----- | ------- | ------------------------ | | `page` | `int` | `1` | Page number (1-indexed) | | `perPage` | `int` | `20` | Items per page (max 100) | **Returns** `Page` *** ### `get()` Fetch a single tag by slug. ```java theme={null} Tag tag = client.tags().get("data-breach"); System.out.println(tag.name()); // Data Breach System.out.println(tag.description()); // Requirements related to data breach... ``` **Parameters** | Parameter | Type | Description | | --------- | -------- | ------------------------------ | | `slug` | `String` | Tag slug, e.g. `"data-breach"` | **Returns** `Tag` *** ### `iter()` Lazily iterate all tags. ```java theme={null} for (Tag t : client.tags().iter()) { System.out.println(t.slug() + ": " + t.name()); } ``` **Returns** `Iterable` *** ## Model ### `Tag` | Field | Type | Description | | ---------------- | -------- | ----------------------------------------- | | `id()` | `int` | Internal numeric ID | | `slug()` | `String` | URL-safe identifier, e.g. `"data-breach"` | | `name()` | `String` | Human-readable tag name | | `description()` | `String` | What this tag covers | | `articleCount()` | `int` | Number of tagged articles | *** ## Examples ### Print all available tags ```java theme={null} for (Tag t : client.tags().iter()) { System.out.printf("%-25s %d articles%n", t.name(), t.articleCount()); } ``` ### Build a tag index ```java theme={null} var index = new java.util.LinkedHashMap(); for (Tag t : client.tags().iter()) { index.put(t.slug(), t); } Tag breach = index.get("data-breach"); System.out.println(breach.name() + " covers " + breach.articleCount() + " articles"); ``` # Annexes Source: https://docs.law4devs.eu/sdks/php/annexes Fetch framework annexes with the Law4Devs PHP SDK. ## Overview Annexes are supplementary parts of EU regulations that appear after the main articles. They often contain technical requirements, lists, or tables referenced in the body. Use `$client->annexes` to access them. *** ## Methods ### `list()` Returns a page of annex summaries for a framework. ```php theme={null} $page = $client->annexes->list('cra'); foreach ($page->data as $annex) { printf(" Annex %s: %s\n", $annex->annexNumber, $annex->title); } ``` **Returns** `Page` *** ### `get()` Fetch the full content of a single annex. ```php theme={null} $annex = $client->annexes->get('cra', 1); echo $annex->title . "\n"; echo $annex->content . "\n"; ``` **Returns** `Annex` *** ### `iter()` Iterate over all annex summaries in a framework. ```php theme={null} foreach ($client->annexes->iter('cra') as $annex) { echo $annex->annexNumber . ': ' . $annex->title . "\n"; } ``` **Returns** `\Generator` *** ## Models ### `AnnexSummary` | Field | Type | Description | | --------------- | -------- | ------------------------------------ | | `id` | `int` | Internal numeric ID | | `frameworkSlug` | `string` | Parent framework slug | | `annexNumber` | `string` | Annex identifier, e.g. `'I'`, `'II'` | | `title` | `string` | Annex heading | ### `Annex` Returned by `get()`. Extends `AnnexSummary` with: | Field | Type | Description | | --------- | -------- | ------------------------------ | | `content` | `string` | Full text content of the annex | # Articles Source: https://docs.law4devs.eu/sdks/php/articles Fetch and iterate framework articles with the Law4Devs PHP SDK. ## Overview Articles are the primary legal content of each framework. Every article belongs to exactly one framework, identified by its slug. Use `$client->articles` to list, fetch, and navigate them. *** ## Methods ### `list()` Returns a page of article summaries for the given framework. ```php theme={null} $page = $client->articles->list('gdpr', page: 1, perPage: 20); printf("GDPR has %d articles\n", $page->meta->total); foreach ($page->data as $article) { printf(" Art. %s: %s\n", $article->articleNumber, $article->title); } ``` **Parameters** | Parameter | Type | Default | Description | | ---------------- | ----------- | -------- | ----------------------------- | | `$frameworkSlug` | `string` | required | Framework slug, e.g. `'gdpr'` | | `$page` | `int\|null` | `null` | Page number | | `$perPage` | `int\|null` | `null` | Items per page (1–100) | **Returns** `Page` *** ### `get()` Fetch the full content of a single article, including all paragraphs. ```php theme={null} $article = $client->articles->get('gdpr', 17); echo $article->title . "\n"; echo $article->content . "\n"; foreach ($article->paragraphs as $para) { printf(" §%s: %s\n", $para->paragraphRef, $para->content); } ``` **Parameters** | Parameter | Type | Description | | ---------------- | ------------- | ----------------------------------- | | `$frameworkSlug` | `string` | Framework slug | | `$articleNumber` | `int\|string` | Article number within the framework | **Returns** `Article` *** ### `related()` Fetch articles related to a given article. ```php theme={null} $page = $client->articles->related('gdpr', 17); foreach ($page->data as $article) { printf(" Related: Art. %s — %s\n", $article->articleNumber, $article->title); } ``` **Returns** `Page` *** ### `iter()` Iterate over all articles in a framework across all pages. ```php theme={null} foreach ($client->articles->iter('gdpr') as $article) { printf("Art. %s: %s\n", $article->articleNumber, $article->title); } ``` **Returns** `\Generator` *** ## Models ### `ArticleSummary` Returned by `list()`, `related()`, and `iter()`. | Field | Type | Description | | ---------------- | -------- | ----------------------------------- | | `id` | `int` | Internal numeric ID | | `frameworkSlug` | `string` | Parent framework slug | | `articleNumber` | `string` | Article number within the framework | | `title` | `string` | Article heading | | `position` | `int` | Ordering position in the framework | | `paragraphCount` | `int` | Number of paragraphs | | `tags` | `Tag[]` | Tags applied to this article | ### `Article` Returned by `get()`. Extends `ArticleSummary` with: | Field | Type | Description | | ------------ | -------------------- | -------------------------------------- | | `content` | `string` | Full plain-text content of the article | | `paragraphs` | `ArticleParagraph[]` | Ordered list of paragraph objects | ### `ArticleParagraph` | Field | Type | Description | | -------------- | -------- | ------------------------------------ | | `paragraphRef` | `string` | Label, e.g. `'1'`, `'2a'`, `'intro'` | | `content` | `string` | Paragraph text | | `position` | `int` | Ordering position within the article | *** ## Examples ### Print every GDPR article ```php theme={null} foreach ($client->articles->iter('gdpr') as $article) { printf("Art. %3s %s\n", $article->articleNumber, $article->title ?: '(untitled)'); } ``` ### Fetch a specific article and display paragraphs ```php theme={null} $article = $client->articles->get('gdpr', 17); echo "Article {$article->articleNumber}: {$article->title}\n"; echo str_repeat('=', 60) . "\n"; foreach ($article->paragraphs as $para) { echo "\n({$para->paragraphRef}) {$para->content}\n"; } ``` ### Collect all articles into an array for fast lookup ```php theme={null} $byNumber = []; foreach ($client->articles->iter('cra') as $article) { $byNumber[$article->articleNumber] = $article; } echo $byNumber['10']->title . "\n"; ``` Use `iter()` when you need every article. Use `list()` when you only need one page or want to control pagination yourself. # Compliance Deadlines Source: https://docs.law4devs.eu/sdks/php/compliance Fetch compliance deadlines for EU frameworks with the Law4Devs PHP SDK. ## Overview Compliance deadlines are structured dates extracted from regulatory texts, indicating when specific obligations must be met. Use `$client->compliance` to access them. *** ## Methods ### `list()` Returns a page of compliance deadlines for a framework. ```php theme={null} $page = $client->compliance->list('nis2'); foreach ($page->data as $deadline) { printf(" [%s] %s — %s\n", $deadline->deadlineDate, $deadline->deadlineType, $deadline->description ?? 'No description', ); } ``` **Returns** `Page` *** ### `iter()` Iterate over all compliance deadlines in a framework. ```php theme={null} foreach ($client->compliance->iter('dora') as $deadline) { echo $deadline->deadlineDate . ': ' . $deadline->description . "\n"; } ``` **Returns** `\Generator` *** ## Model ### `ComplianceDeadline` | Field | Type | Description | | --------------- | -------------- | --------------------------------------------- | | `id` | `int` | Internal numeric ID | | `frameworkSlug` | `string` | Parent framework slug | | `articleNumber` | `string\|null` | Source article number | | `paragraphRef` | `string\|null` | Paragraph reference | | `deadlineDate` | `string` | ISO 8601 date | | `deadlineType` | `string` | Type, e.g. `'transposition'`, `'application'` | | `description` | `string\|null` | Human-readable description of the obligation | *** ## Example ### Print all upcoming deadlines sorted by date ```php theme={null} $deadlines = []; foreach ($client->compliance->iter('nis2') as $d) { $deadlines[] = $d; } usort($deadlines, fn($a, $b) => $a->deadlineDate <=> $b->deadlineDate); foreach ($deadlines as $d) { printf("[%s] %s\n", $d->deadlineDate, $d->description ?? $d->deadlineType); } ``` # Error Handling Source: https://docs.law4devs.eu/sdks/php/error-handling Handle API errors gracefully with the Law4Devs PHP SDK. ## Exception Hierarchy All SDK exceptions extend `Law4Devs\Exceptions\Law4DevsException`, which itself extends `\RuntimeException`. ``` \RuntimeException └── Law4DevsException base — any SDK error ├── NotFoundError 404 — resource not found ├── ValidationError 400 — invalid parameters ├── RateLimitError 429 — rate limit exceeded └── ServerError 5xx — server-side error ``` Every exception exposes a `statusCode` property: ```php theme={null} try { $article = $client->articles->get('gdpr', 9999); } catch (\Law4Devs\Exceptions\NotFoundError $e) { echo 'Not found: ' . $e->getMessage() . "\n"; // message from API echo 'Status: ' . $e->statusCode . "\n"; // 404 } ``` *** ## Catching Specific Errors ```php theme={null} use Law4Devs\Exceptions\Law4DevsException; use Law4Devs\Exceptions\NotFoundError; use Law4Devs\Exceptions\RateLimitError; use Law4Devs\Exceptions\ValidationError; try { $page = $client->articles->list('invalid-framework'); } catch (NotFoundError $e) { // Framework not found echo 'Framework not found: ' . $e->getMessage() . "\n"; } catch (ValidationError $e) { // Bad request parameters echo 'Invalid request: ' . $e->getMessage() . "\n"; } catch (RateLimitError $e) { // Retry after a delay (SDK already retried 3 times) echo 'Rate limit hit: ' . $e->getMessage() . "\n"; } catch (Law4DevsException $e) { // All other SDK errors (server errors, network failures) echo 'SDK error (' . $e->statusCode . '): ' . $e->getMessage() . "\n"; } ``` *** ## Automatic Retry The SDK automatically retries on `429 Too Many Requests` and `5xx Server Error` responses using exponential backoff: | Attempt | Wait | | --------- | --------- | | 1st retry | 1 second | | 2nd retry | 2 seconds | | 3rd retry | 4 seconds | After `maxRetries` (default `3`) attempts, the exception is re-thrown. To disable retries, set `maxRetries: 0`: ```php theme={null} $client = new \Law4Devs\Client( apiKey: getenv('LAW4DEVS_API_KEY'), maxRetries: 0, ); ``` *** ## Network Errors Connection failures (timeout, DNS, SSL) throw `Law4DevsException` with `statusCode` of `null`: ```php theme={null} use Law4Devs\Exceptions\Law4DevsException; try { $page = $client->frameworks->list(); } catch (Law4DevsException $e) { if ($e->statusCode === null) { echo 'Network error: ' . $e->getMessage() . "\n"; } } ``` *** ## Best Practices * Always catch `Law4DevsException` as a fallback to handle unexpected errors * Check `$e->statusCode` to distinguish network errors (`null`) from API errors * Wrap `iter()` loops in a try/catch — exceptions can occur mid-iteration if a page request fails * For batch processing, implement a retry strategy at the application level if you need finer control than the built-in retry # Frameworks Source: https://docs.law4devs.eu/sdks/php/frameworks List and retrieve EU regulatory frameworks with the Law4Devs PHP SDK. ## Overview Frameworks represent EU regulations such as GDPR, CRA, NIS2, and the AI Act. Use `$client->frameworks` to list and fetch them. *** ## Methods ### `list()` Returns a page of all available frameworks. ```php theme={null} $page = $client->frameworks->list(); printf("Found %d frameworks\n", $page->meta->total); foreach ($page->data as $fw) { printf(" %-12s %s\n", $fw->slug, $fw->name); } ``` **Returns** `Page` *** ### `get()` Fetch a single framework by slug, including detailed counts. ```php theme={null} $cra = $client->frameworks->get('cra'); echo $cra->name . "\n"; // Cyber Resilience Act echo $cra->celexNumber . "\n"; // 32024R2847 echo $cra->articleCount . " articles\n"; ``` **Parameters** | Parameter | Type | Description | | --------- | -------- | ----------------------------- | | `$slug` | `string` | Framework slug, e.g. `'gdpr'` | **Returns** `Framework` *** ### `iter()` Iterate over all frameworks across all pages. ```php theme={null} foreach ($client->frameworks->iter() as $fw) { echo $fw->slug . ': ' . $fw->name . "\n"; } ``` **Returns** `\Generator` *** ## Models ### `FrameworkSummary` Returned by `list()` and `iter()`. | Field | Type | Description | | -------------- | -------------- | ---------------------------------------- | | `id` | `int` | Internal numeric ID | | `slug` | `string` | URL-safe identifier, e.g. `'cra'` | | `name` | `string` | Full framework name | | `shortName` | `string` | Short name, e.g. `'CRA'` | | `celexNumber` | `string` | EUR-Lex CELEX identifier | | `description` | `string\|null` | Brief description | | `isActive` | `bool` | Whether data is actively maintained | | `status` | `string` | `'active'`, `'draft'`, or `'superseded'` | | `articleCount` | `int` | Number of synced articles | | `recitalCount` | `int` | Number of synced recitals | | `createdAt` | `string` | ISO 8601 creation timestamp | ### `Framework` Returned by `get()`. Extends `FrameworkSummary` with: | Field | Type | Description | | ------------------ | ------------- | --------------------------------- | | `eurLexUrl` | `string` | Link to the official EUR-Lex page | | `requirementCount` | `int` | Number of extracted requirements | | `annexCount` | `int` | Number of annexes | | `tagCount` | `int` | Number of associated tags | | `coverage` | `array\|null` | Coverage statistics | *** ## Examples ### List all active frameworks ```php theme={null} foreach ($client->frameworks->iter() as $fw) { if ($fw->isActive) { printf("%-12s %s (%d articles)\n", $fw->slug, $fw->name, $fw->articleCount); } } ``` ### Find frameworks with full article coverage ```php theme={null} $page = $client->frameworks->list(); foreach ($page->data as $fw) { if ($fw->expectedArticles > 0 && $fw->articleCount >= $fw->expectedArticles) { echo $fw->name . " — fully synced\n"; } } ``` # Installation Source: https://docs.law4devs.eu/sdks/php/installation Install the Law4Devs PHP SDK and make your first API call. ## Requirements * PHP 8.1 or later * `ext-curl` and `ext-json` extensions (enabled by default in most PHP installations) * No runtime dependencies — zero third-party packages required ## Install ```bash theme={null} composer require law4devs/law4devs ``` To pin to a specific version: ```bash theme={null} composer require law4devs/law4devs:^0.1 ``` ## First Call ```php theme={null} frameworks->list(); echo $page->data[0]->name; // e.g. "Cyber Resilience Act" ``` ## Configuration ```php theme={null} use Law4Devs\Client; $client = new Client( apiKey: $_ENV['LAW4DEVS_API_KEY'], baseUrl: 'https://api.law4devs.eu/v1', // default timeout: 30, // seconds, default 30 maxRetries: 3, // retries on 429/5xx, default 3 ); ``` | Option | Type | Default | Description | | ------------ | -------- | -------------- | ---------------------------- | | `apiKey` | `string` | `''` | **Required.** Your API key | | `baseUrl` | `string` | Production URL | Override for dev environment | | `timeout` | `int` | `30` | Request timeout in seconds | | `maxRetries` | `int` | `3` | Retries on 429/5xx | ## Environment Variable ```bash theme={null} export LAW4DEVS_API_KEY="your-key" ``` ```php theme={null} use Law4Devs\Client; $client = new Client(apiKey: getenv('LAW4DEVS_API_KEY')); ``` Never hardcode your API key in source code or commit it to version control. ## Verify the Install ```bash theme={null} LAW4DEVS_API_KEY=your-key php -r " require 'vendor/autoload.php'; \$c = new \Law4Devs\Client(apiKey: getenv('LAW4DEVS_API_KEY')); echo \$c->frameworks->list()->meta->total . ' frameworks\n'; " ``` # Pagination Source: https://docs.law4devs.eu/sdks/php/pagination How pagination works in the Law4Devs PHP SDK. ## Overview All `list()` methods return a `Page` object. Every page includes the data array, metadata about the total result set, and links to the next/previous page. *** ## The `Page` Object ```php theme={null} $page = $client->articles->list('gdpr'); // Data $articles = $page->data; // ArticleSummary[] $count = $page->count(); // items on this page // Metadata $total = $page->meta->total; // total results across all pages $current = $page->meta->page; // current page number $perPage = $page->meta->perPage; // items per page $pages = $page->meta->pages; // total number of pages // Links $nextUrl = $page->links->next; // URL of next page (null if last) $prevUrl = $page->links->prev; // URL of previous page (null if first) ``` *** ## Manual Pagination Control pagination yourself with `$page` and `$perPage`: ```php theme={null} $pageNum = 1; do { $page = $client->articles->list('gdpr', page: $pageNum, perPage: 50); foreach ($page->data as $article) { echo $article->title . "\n"; } $pageNum++; } while ($page->links->next !== null); ``` *** ## Auto-Pagination with `iter()` Every resource exposes an `iter()` method that automatically fetches all pages and yields individual items: ```php theme={null} // Yields ArticleSummary one by one — no manual paging needed foreach ($client->articles->iter('gdpr') as $article) { echo $article->articleNumber . ' ' . $article->title . "\n"; } ``` Internally, `iter()` calls `list()` repeatedly, following `links->next` until there are no more pages. Use `iter()` whenever you need all items. It handles pagination transparently and uses PHP generators, so it doesn't load the full result set into memory at once. *** ## Collecting into an Array If you need a full array, use `iterator_to_array()`: ```php theme={null} $articles = iterator_to_array($client->articles->iter('cra')); echo count($articles) . " total articles\n"; ``` *** ## Pagination Parameters | Parameter | Type | Default | Description | | ---------- | ----------- | ------- | ------------------------ | | `$page` | `int\|null` | `1` | Page number (1-indexed) | | `$perPage` | `int\|null` | `20` | Items per page (max 100) | # Quick Start Source: https://docs.law4devs.eu/sdks/php/quickstart Make your first Law4Devs API call with PHP in under 60 seconds. ## Install ```bash theme={null} composer require law4devs/law4devs ``` ## Basic Usage ```php theme={null} frameworks->list(); foreach ($page->data as $fw) { echo $fw->slug . ' — ' . $fw->name . "\n"; } // List first 5 CRA articles $articles = $client->articles->list('cra', perPage: 5); foreach ($articles->data as $article) { echo 'Art. ' . $article->articleNumber . ': ' . $article->title . "\n"; } // Auto-paginate all GDPR articles foreach ($client->articles->iter('gdpr') as $article) { echo $article->articleNumber . ' ' . $article->title . "\n"; } // Search across all frameworks $results = $client->search->query('data breach notification'); echo 'Found ' . $results->meta->total . " results\n"; ``` ## Next Steps # Recitals Source: https://docs.law4devs.eu/sdks/php/recitals Fetch and iterate framework recitals with the Law4Devs PHP SDK. ## Overview Recitals are the numbered preamble paragraphs of EU regulations that provide context and intent for the operative articles. Use `$client->recitals` to access them. *** ## Methods ### `list()` Returns a page of recitals for the given framework. ```php theme={null} $page = $client->recitals->list('gdpr', perPage: 10); printf("GDPR has %d recitals\n", $page->meta->total); foreach ($page->data as $recital) { printf(" Recital %s: %.80s...\n", $recital->recitalNumber, $recital->content); } ``` **Returns** `Page` *** ### `get()` Fetch a single recital by number. ```php theme={null} $recital = $client->recitals->get('gdpr', 1); echo $recital->content . "\n"; ``` **Returns** `Recital` *** ### `iter()` Iterate over all recitals in a framework. ```php theme={null} foreach ($client->recitals->iter('nis2') as $recital) { echo $recital->recitalNumber . ' — ' . substr($recital->content, 0, 80) . "\n"; } ``` **Returns** `\Generator` *** ## Model ### `Recital` | Field | Type | Description | | --------------- | -------- | ----------------------------------- | | `id` | `int` | Internal numeric ID | | `frameworkSlug` | `string` | Parent framework slug | | `recitalNumber` | `string` | Recital number within the framework | | `content` | `string` | Full recital text | | `position` | `int` | Ordering position in the framework | # Requirements Source: https://docs.law4devs.eu/sdks/php/requirements Fetch extracted compliance requirements with the Law4Devs PHP SDK. ## Overview Requirements are structured compliance obligations extracted from regulatory articles. Each requirement has a type, optional deadline, linked articles, and stakeholder roles. Use `$client->requirements` to access them. *** ## Methods ### `list()` Returns a page of requirements for the given framework. ```php theme={null} $page = $client->requirements->list('cra', perPage: 20); printf("CRA has %d requirements\n", $page->meta->total); foreach ($page->data as $req) { printf(" [%s] %s\n", $req->requirementType, substr($req->requirementText, 0, 80)); } ``` **Returns** `Page` *** ### `get()` Fetch a single requirement by ID. ```php theme={null} $req = $client->requirements->get('cra', 42); echo $req->requirementText . "\n"; ``` **Returns** `Requirement` *** ### `iter()` Iterate over all requirements in a framework. ```php theme={null} foreach ($client->requirements->iter('gdpr') as $req) { echo $req->requirementType . ': ' . substr($req->requirementText, 0, 60) . "\n"; } ``` **Returns** `\Generator` *** ## Model ### `Requirement` | Field | Type | Description | | ---------------------- | -------------- | ---------------------------------------------- | | `id` | `int` | Internal numeric ID | | `frameworkSlug` | `string` | Parent framework slug | | `articleNumber` | `int\|null` | Source article number | | `paragraphRef` | `string\|null` | Paragraph reference within the article | | `paragraphContent` | `string\|null` | Text of the source paragraph | | `requirementText` | `string` | The extracted requirement | | `requirementType` | `string` | Category, e.g. `'obligation'`, `'prohibition'` | | `complianceDeadline` | `string\|null` | Deadline date (ISO 8601) if specified | | `linkedArticleNumbers` | `int[]` | Related article numbers | | `stakeholderRoles` | `string[]` | Affected parties, e.g. `'manufacturer'` | | `tags` | `Tag[]` | Tags applied to this requirement | | `createdAt` | `string` | ISO 8601 creation timestamp | *** ## Examples ### Filter by requirement type ```php theme={null} foreach ($client->requirements->iter('cra') as $req) { if ($req->requirementType === 'obligation') { echo '- ' . $req->requirementText . "\n"; } } ``` ### Find requirements with deadlines ```php theme={null} foreach ($client->requirements->iter('nis2') as $req) { if ($req->complianceDeadline !== null) { printf("[%s] %s\n", $req->complianceDeadline, substr($req->requirementText, 0, 80)); } } ``` # Search Source: https://docs.law4devs.eu/sdks/php/search Full-text search across all EU frameworks with the Law4Devs PHP SDK. ## Overview The search endpoint lets you query across all frameworks simultaneously. Results include articles, recitals, and requirements that match your query. Use `$client->search->query()` to search. *** ## Methods ### `query()` Search across all frameworks (or a specific one). ```php theme={null} // Search everything $results = $client->search->query('incident notification 72 hours'); printf("Found %d results\n", $results->meta->total); foreach ($results->data as $result) { printf(" [%s] %s — %s\n", strtoupper($result->frameworkSlug), $result->type, $result->title ?? $result->matchContext, ); } ``` **Parameters** | Parameter | Type | Default | Description | | ------------- | -------------- | -------- | --------------------------------------------------------- | | `$q` | `string` | required | Search query | | `$framework` | `string\|null` | `null` | Limit to one framework slug | | `$resultType` | `string\|null` | `null` | Filter by type: `'article'`, `'recital'`, `'requirement'` | | `$page` | `int\|null` | `null` | Page number | | `$perPage` | `int\|null` | `null` | Items per page | **Returns** `Page` *** ## Model ### `SearchResult` | Field | Type | Description | | ----------------- | -------------- | -------------------------------------------- | | `type` | `string` | `'article'`, `'recital'`, or `'requirement'` | | `frameworkSlug` | `string` | Framework the result belongs to | | `frameworkName` | `string` | Human-readable framework name | | `articleNumber` | `int\|null` | For article results | | `recitalNumber` | `int\|null` | For recital results | | `paragraphRef` | `string\|null` | Specific paragraph reference | | `title` | `string\|null` | Result heading, if any | | `requirementType` | `string\|null` | For requirement results | | `matchContext` | `string` | Snippet showing the match in context | | `url` | `string` | Direct API URL to the full resource | *** ## Examples ### Search across specific frameworks ```php theme={null} $results = $client->search->query( q: 'breach notification', framework: 'gdpr', ); foreach ($results->data as $r) { echo $r->frameworkSlug . ' — ' . ($r->title ?? $r->matchContext) . "\n"; } ``` ### Filter by result type ```php theme={null} $results = $client->search->query( q: 'encryption at rest', resultType: 'requirement', ); foreach ($results->data as $r) { echo '- ' . $r->matchContext . "\n"; } ``` ### Cross-framework search ```php theme={null} $frameworks = ['nis2', 'dora', 'cra']; foreach ($frameworks as $fw) { $results = $client->search->query('incident notification', framework: $fw); printf("%-6s %d results\n", $fw, $results->meta->total); } ``` Use the `$framework` parameter to narrow results when you know which regulation you're working with. Cross-framework search (without a filter) is great for discovering related obligations across different laws. # Tags Source: https://docs.law4devs.eu/sdks/php/tags List and retrieve taxonomy tags with the Law4Devs PHP SDK. ## Overview Tags are a structured taxonomy used to categorize articles, recitals, and requirements. Use `$client->tags` to browse all available tags. *** ## Methods ### `list()` Returns a page of tags, optionally filtered by framework. ```php theme={null} // All tags $page = $client->tags->list(); // Tags relevant to CRA $page = $client->tags->list(frameworkSlug: 'cra'); foreach ($page->data as $tag) { printf(" %-20s %s\n", $tag->slug, $tag->name); } ``` **Returns** `Page` *** ### `get()` Fetch a single tag by slug. ```php theme={null} $tag = $client->tags->get('data-breach'); echo $tag->name . ': ' . $tag->description . "\n"; ``` **Returns** `Tag` *** ### `iter()` Iterate over all tags. ```php theme={null} foreach ($client->tags->iter() as $tag) { echo $tag->slug . "\n"; } ``` **Returns** `\Generator` *** ## Model ### `Tag` | Field | Type | Description | | ------------- | -------------- | --------------------------- | | `id` | `int` | Internal numeric ID | | `slug` | `string` | URL-safe identifier | | `name` | `string` | Display name | | `description` | `string\|null` | What this tag means | | `keywords` | `string[]` | Related search keywords | | `color` | `string` | Hex color for display | | `createdAt` | `string` | ISO 8601 creation timestamp | # Annexes Source: https://docs.law4devs.eu/sdks/python/annexes Fetch and iterate framework annexes with the Law4Devs Python SDK. ## Overview Annexes are supplementary parts of EU legislation, often containing technical specifications, lists, or tables that are referenced within the articles. Each annex belongs to one framework and is identified by its annex number (or letter, e.g. `"I"`, `"II"`, `"A"`). *** ## Methods ### `list()` Returns a page of annex summaries for a framework. ```python theme={null} page = client.annexes.list("cra", page=1, per_page=20) print(f"CRA has {page.meta.total} annexes") for annex in page.data: print(f" Annex {annex.annex_number}: {annex.title}") ``` **Parameters** | Parameter | Type | Default | Description | | ---------------- | ----- | -------- | ---------------------------- | | `framework_slug` | `str` | required | Framework slug, e.g. `"cra"` | | `page` | `int` | `1` | Page number | | `per_page` | `int` | `20` | Items per page (1–100) | **Returns** `Page[AnnexSummary]` *** ### `get()` Fetch the full content of a single annex. ```python theme={null} annex = client.annexes.get("cra", "I") print(annex.title) print(annex.content) ``` **Parameters** | Parameter | Type | Description | | ---------------- | ----- | ------------------------------------------- | | `framework_slug` | `str` | Framework slug | | `annex_number` | `str` | Annex identifier, e.g. `"I"`, `"II"`, `"A"` | **Returns** `Annex` *** ### `iter()` Iterate over all annexes in a framework. ```python theme={null} for annex in client.annexes.iter("cra"): print(f"Annex {annex.annex_number}: {annex.title}") ``` **Parameters** — same kwargs as `list()` (except `page`). **Returns** `Iterator[AnnexSummary]` *** ## Models ### `AnnexSummary` Returned by `list()` and `iter()`. | Field | Type | Description | | ---------------- | ------------- | -------------------------------------- | | `id` | `int` | Internal numeric ID | | `framework_slug` | `str` | Parent framework slug | | `annex_number` | `str` | Annex identifier, e.g. `"I"` | | `title` | `str \| None` | Annex heading | | `position` | `int` | Ordering position within the framework | | `section_count` | `int` | Number of sections within this annex | ### `Annex` Returned by `get()`. Extends `AnnexSummary` with: | Field | Type | Description | | ---------- | -------------------- | ------------------------------------ | | `content` | `str \| None` | Full plain-text content of the annex | | `sections` | `list[AnnexSection]` | Structured sections, if parsed | ### `AnnexSection` | Field | Type | Description | | ---------------- | ----- | ---------------------------------- | | `id` | `int` | Internal numeric ID | | `section_number` | `str` | Section label, e.g. `"1"`, `"1.1"` | | `content` | `str` | Section text | | `position` | `int` | Ordering position within the annex | *** ## Examples ### List all CRA annexes ```python theme={null} from law4devs import Law4DevsClient client = Law4DevsClient() for annex in client.annexes.iter("cra"): print(f"Annex {annex.annex_number}: {annex.title or '(untitled)'} ({annex.section_count} sections)") ``` ### Read the full content of an annex ```python theme={null} annex = client.annexes.get("cra", "I") print(f"=== {annex.title} ===\n") if annex.sections: for section in annex.sections: print(f"\n{section.section_number}. {section.content}") else: print(annex.content) ``` ### Check which frameworks have annexes ```python theme={null} for fw in client.frameworks.iter(): detail = client.frameworks.get(fw.slug) if detail.annex_count > 0: print(f"{fw.short_name}: {detail.annex_count} annexes") ``` ### Collect all annex content for a framework ```python theme={null} all_content = [] for annex in client.annexes.iter("gdpr"): full = client.annexes.get("gdpr", annex.annex_number) all_content.append({ "annex": annex.annex_number, "title": annex.title, "content": full.content, }) print(f"Fetched {len(all_content)} GDPR annexes") ``` Annex numbers follow the official EU publication format. Roman numerals (`"I"`, `"II"`) are most common, but some frameworks use letters or Arabic numerals. # Articles Source: https://docs.law4devs.eu/sdks/python/articles Fetch and iterate framework articles with the Law4Devs Python SDK. ## Overview Articles are the primary legal content of each framework. Every article belongs to exactly one framework, identified by its `framework_slug`. Use `client.articles` to list, fetch, and navigate them. *** ## Methods ### `list()` Returns a page of article summaries for the given framework. ```python theme={null} page = client.articles.list("gdpr", page=1, per_page=20) print(f"GDPR has {page.meta.total} articles") for article in page.data: print(f" Art. {article.article_number}: {article.title}") ``` **Parameters** | Parameter | Type | Default | Description | | ---------------- | ----- | -------- | ----------------------------- | | `framework_slug` | `str` | required | Framework slug, e.g. `"gdpr"` | | `page` | `int` | `1` | Page number | | `per_page` | `int` | `20` | Items per page (1–100) | **Returns** `Page[ArticleSummary]` *** ### `get()` Fetch the full content of a single article, including all paragraphs. ```python theme={null} article = client.articles.get("gdpr", 17) print(article.title) print(article.content) for para in article.paragraphs: print(f" §{para.paragraph_number}: {para.content}") ``` **Parameters** | Parameter | Type | Description | | ---------------- | ----- | ----------------------------------- | | `framework_slug` | `str` | Framework slug | | `article_number` | `int` | Article number within the framework | **Returns** `Article` *** ### `related()` Fetch articles related to a given article (linked via shared requirements or tags). ```python theme={null} page = client.articles.related("gdpr", 17) for article in page.data: print(f" Related: Art. {article.article_number} — {article.title}") ``` **Parameters** | Parameter | Type | Default | Description | | ---------------- | ----- | -------- | ---------------------- | | `framework_slug` | `str` | required | Framework slug | | `article_number` | `int` | required | Article number | | `page` | `int` | `1` | Page number | | `per_page` | `int` | `20` | Items per page (1–100) | **Returns** `Page[ArticleSummary]` *** ### `iter()` Iterate over all articles in a framework across all pages. ```python theme={null} for article in client.articles.iter("gdpr"): print(f"Art. {article.article_number}: {article.title}") ``` **Parameters** — same kwargs as `list()` (except `page`). **Returns** `Iterator[ArticleSummary]` *** ## Models ### `ArticleSummary` Returned by `list()`, `related()`, and `iter()`. | Field | Type | Description | | ----------------- | ------------- | ----------------------------------- | | `id` | `int` | Internal numeric ID | | `framework_slug` | `str` | Parent framework slug | | `article_number` | `int` | Article number within the framework | | `title` | `str \| None` | Article heading | | `position` | `int` | Ordering position in the framework | | `paragraph_count` | `int` | Number of paragraphs | | `tags` | `list[str]` | Tag slugs applied to this article | ### `Article` Returned by `get()`. Extends `ArticleSummary` with: | Field | Type | Description | | ------------ | ----------------- | -------------------------------------- | | `content` | `str \| None` | Full plain-text content of the article | | `paragraphs` | `list[Paragraph]` | Ordered list of paragraph objects | ### `Paragraph` | Field | Type | Description | | ------------------ | ----- | ------------------------------------ | | `id` | `int` | Internal numeric ID | | `paragraph_number` | `str` | Label, e.g. `"1"`, `"2a"`, `"intro"` | | `content` | `str` | Paragraph text | | `position` | `int` | Ordering position within the article | *** ## Examples ### Print every GDPR chapter with article titles ```python theme={null} from law4devs import Law4DevsClient client = Law4DevsClient() for article in client.articles.iter("gdpr"): print(f"Art. {article.article_number:>3} {article.title or '(untitled)'}") ``` ### Fetch a specific article and display it ```python theme={null} article = client.articles.get("gdpr", 17) print(f"Article {article.article_number}: {article.title}") print("=" * 60) for para in article.paragraphs: print(f"\n({para.paragraph_number}) {para.content}") ``` ### Find all articles tagged with a specific tag ```python theme={null} tagged = [ a for a in client.articles.iter("gdpr") if "data-breach" in a.tags ] print(f"{len(tagged)} articles tagged 'data-breach'") ``` ### Cross-reference related articles ```python theme={null} article = client.articles.get("gdpr", 33) # Personal data breach notification related = client.articles.related("gdpr", 33) print(f"Art. 33 is related to {related.meta.total} other articles:") for r in related.data: print(f" Art. {r.article_number}: {r.title}") ``` ### Collect all articles into a dict for fast lookup ```python theme={null} articles_by_number = { a.article_number: a for a in client.articles.iter("cra") } print(articles_by_number[10].title) ``` Use `iter()` when you need every article. Use `list()` when you only need one page or want to control pagination yourself. # Compliance Deadlines Source: https://docs.law4devs.eu/sdks/python/compliance Query compliance deadlines across EU frameworks with the Law4Devs Python SDK. ## Overview Compliance deadlines are specific dates by which organizations must satisfy a legal obligation. Each deadline is extracted from a framework article and classified by type (e.g. `"transposition"`, `"application"`, `"notification"`). *** ## Methods ### `deadlines()` Returns a page of compliance deadlines. Without filters, returns deadlines across all frameworks. ```python theme={null} # All deadlines page = client.compliance.deadlines() print(f"Total deadlines: {page.meta.total}") # Filter by framework page = client.compliance.deadlines(framework_slug="nis2") for dl in page.data: print(f"{dl.deadline_date} [{dl.deadline_type}] {dl.description}") ``` **Parameters** | Parameter | Type | Default | Description | | ---------------- | ----- | ------- | ------------------------------ | | `framework_slug` | `str` | `None` | Filter to a specific framework | | `page` | `int` | `1` | Page number | | `per_page` | `int` | `20` | Items per page (1–100) | **Returns** `Page[ComplianceDeadline]` *** ### `iter_deadlines()` Iterate over all deadlines, optionally filtered by framework. ```python theme={null} for deadline in client.compliance.iter_deadlines(framework_slug="nis2"): print(f"{deadline.deadline_date}: {deadline.description}") ``` **Parameters** — same kwargs as `deadlines()` (except `page`). **Returns** `Iterator[ComplianceDeadline]` *** ## Model ### `ComplianceDeadline` | Field | Type | Description | | ---------------- | ------------- | ------------------------------------------------------------------------------ | | `id` | `int` | Internal numeric ID | | `framework_slug` | `str` | Parent framework slug | | `article_number` | `int \| None` | Source article number | | `paragraph_ref` | `str \| None` | Source paragraph label | | `deadline_date` | `str` | ISO 8601 date, e.g. `"2024-10-17"` | | `deadline_type` | `str` | `"transposition"`, `"application"`, `"notification"`, `"review"`, or `"other"` | | `description` | `str` | Plain-text description of the obligation | *** ## Examples ### Print upcoming deadlines sorted by date ```python theme={null} from law4devs import Law4DevsClient from datetime import date client = Law4DevsClient() today = date.today().isoformat() upcoming = sorted( [dl for dl in client.compliance.iter_deadlines() if dl.deadline_date >= today], key=lambda d: d.deadline_date, ) print("Upcoming compliance deadlines:") for dl in upcoming: print(f" {dl.deadline_date} [{dl.framework_slug}] {dl.description[:60]}") ``` ### Deadlines for a specific framework ```python theme={null} print("NIS2 compliance deadlines:") for dl in client.compliance.iter_deadlines(framework_slug="nis2"): source = f"Art. {dl.article_number}" if dl.article_number else "general" print(f" {dl.deadline_date} ({source}) {dl.description}") ``` ### Group deadlines by type ```python theme={null} from collections import defaultdict by_type = defaultdict(list) for dl in client.compliance.iter_deadlines(): by_type[dl.deadline_type].append(dl) for dtype, deadlines in sorted(by_type.items()): print(f"{dtype}: {len(deadlines)} deadlines") ``` ### Export a deadline calendar to JSON ```python theme={null} import json calendar = [] for dl in client.compliance.iter_deadlines(): calendar.append({ "date": dl.deadline_date, "framework": dl.framework_slug, "type": dl.deadline_type, "description": dl.description, "source_article": dl.article_number, }) calendar.sort(key=lambda x: x["date"]) with open("compliance_calendar.json", "w") as f: json.dump(calendar, f, indent=2) print(f"Exported {len(calendar)} deadlines") ``` ### Check if a specific deadline has passed ```python theme={null} from datetime import date dl = client.compliance.deadlines(framework_slug="nis2").data[0] deadline_date = date.fromisoformat(dl.deadline_date) if deadline_date < date.today(): print(f"OVERDUE: {dl.description}") else: days_left = (deadline_date - date.today()).days print(f"{days_left} days until: {dl.description}") ``` Deadline dates are extracted from the official legislative text and may not reflect national transposition extensions. Always verify against the official EUR-Lex publication for legal certainty. # Error Handling Source: https://docs.law4devs.eu/sdks/python/error-handling Handle API errors gracefully with the Law4Devs Python SDK exception hierarchy. ## Overview All SDK errors inherit from `Law4DevsError`. Each exception class maps to a specific HTTP status code or error condition. The SDK automatically retries transient errors (429 and 5xx) with exponential backoff before raising. *** ## Exception Hierarchy ``` Law4DevsError ├── NotFoundError (HTTP 404) ├── ValidationError (HTTP 400 / 422) ├── RateLimitError (HTTP 429) └── ServerError (HTTP 500 / 502 / 503 / 504) ``` All exceptions expose a `status_code` attribute containing the underlying HTTP status code. ```python theme={null} from law4devs.exceptions import ( Law4DevsError, NotFoundError, ValidationError, RateLimitError, ServerError, ) ``` *** ## Exception Reference ### `Law4DevsError` Base class for all SDK exceptions. Catch this to handle any API error. | Attribute | Type | Description | | ------------- | ----- | ---------------------------- | | `status_code` | `int` | HTTP status code | | `message` | `str` | Human-readable error message | ### `NotFoundError` Raised when the requested resource does not exist (HTTP 404). ```python theme={null} try: article = client.articles.get("gdpr", 9999) except NotFoundError as e: print(f"Article not found: {e.message}") ``` ### `ValidationError` Raised when the request parameters are invalid (HTTP 400 or 422). ```python theme={null} try: page = client.articles.list("gdpr", per_page=999) # exceeds max except ValidationError as e: print(f"Bad request: {e.message}") ``` ### `RateLimitError` Raised when the rate limit is exceeded and all retries are exhausted (HTTP 429). ```python theme={null} try: results = client.search.query("encryption") except RateLimitError as e: print(f"Rate limited. Retry after: {e.retry_after} seconds") ``` ### `ServerError` Raised when the server returns a 5xx response and all retries are exhausted. ```python theme={null} try: page = client.frameworks.list() except ServerError as e: print(f"Server error {e.status_code}: {e.message}") ``` *** ## Auto-Retry Behaviour The SDK automatically retries failed requests on HTTP 429 and 5xx responses. Retries use exponential backoff with jitter. * **Default retries**: 3 * **Backoff**: 1s, 2s, 4s (with jitter) * **Retried status codes**: 429, 500, 502, 503, 504 If all retries are exhausted, the appropriate exception (`RateLimitError` or `ServerError`) is raised. Configure the retry count at construction time: ```python theme={null} from law4devs import Law4DevsClient # Disable retries client = Law4DevsClient(max_retries=0) # More retries for a long-running batch job client = Law4DevsClient(max_retries=5) ``` *** ## Practical Patterns ### Handle a missing resource ```python theme={null} from law4devs import Law4DevsClient from law4devs.exceptions import NotFoundError client = Law4DevsClient() try: article = client.articles.get("gdpr", 999) except NotFoundError: print("That article doesn't exist") ``` ### `safe_get` helper A utility that returns `None` instead of raising on 404: ```python theme={null} from law4devs.exceptions import NotFoundError def safe_get_article(client, framework_slug, article_number): try: return client.articles.get(framework_slug, article_number) except NotFoundError: return None article = safe_get_article(client, "gdpr", 999) if article is None: print("Not found") else: print(article.title) ``` ### Catch all API errors ```python theme={null} from law4devs.exceptions import Law4DevsError try: page = client.frameworks.list() except Law4DevsError as e: print(f"API error {e.status_code}: {e.message}") ``` ### Handle rate limits explicitly ```python theme={null} import time from law4devs.exceptions import RateLimitError def fetch_with_backoff(client, framework_slug): for attempt in range(5): try: return list(client.articles.iter(framework_slug)) except RateLimitError as e: wait = getattr(e, "retry_after", 2 ** attempt) print(f"Rate limited. Waiting {wait}s (attempt {attempt + 1}/5)") time.sleep(wait) raise RuntimeError("Exhausted retries") ``` ### Distinguish error types ```python theme={null} from law4devs.exceptions import ( NotFoundError, ValidationError, RateLimitError, ServerError, Law4DevsError, ) try: result = client.articles.get("gdpr", article_number) except NotFoundError: print("Article not found — check the article number") except ValidationError as e: print(f"Invalid parameters: {e.message}") except RateLimitError: print("Too many requests — slow down or add an API key") except ServerError as e: print(f"Server problem ({e.status_code}) — try again later") except Law4DevsError as e: print(f"Unexpected error {e.status_code}: {e.message}") ``` ### Validate a framework slug before use ```python theme={null} from law4devs.exceptions import NotFoundError def framework_exists(client, slug): try: client.frameworks.get(slug) return True except NotFoundError: return False if not framework_exists(client, "unknown-framework"): print("Framework not found") ``` *** ## Timeout Errors Network timeouts raise Python's built-in `TimeoutError` (not a `Law4DevsError`). Configure the timeout per client: ```python theme={null} # Allow up to 60 seconds per request client = Law4DevsClient(timeout=60) ``` ```python theme={null} try: page = client.frameworks.list() except TimeoutError: print("Request timed out — the server may be slow") ``` For batch jobs that process thousands of items, set `max_retries=5` and a generous `timeout=60`. The SDK will handle intermittent failures automatically. 404 responses are **not** retried automatically — they indicate a resource genuinely does not exist, not a transient failure. # Frameworks Source: https://docs.law4devs.eu/sdks/python/frameworks List, inspect, and iterate EU regulatory frameworks with the Law4Devs Python SDK. ## Overview Frameworks are the top-level resource — every article, recital, requirement, and annex belongs to one. Use the `client.frameworks` resource to query them. *** ## Methods ### `list()` Returns the first page of frameworks. ```python theme={null} page = client.frameworks.list(page=1, per_page=20) print(page.meta.total) # total number of frameworks for fw in page.data: print(fw.slug, fw.name) ``` **Parameters** | Parameter | Type | Default | Description | | ---------- | ----- | ------- | ---------------------- | | `page` | `int` | `1` | Page number | | `per_page` | `int` | `20` | Items per page (1–100) | **Returns** `Page[Framework]` *** ### `get()` Fetch a single framework by its slug. ```python theme={null} fw = client.frameworks.get("gdpr") print(fw.name) # "General Data Protection Regulation" print(fw.eurlex_url) # "https://eur-lex.europa.eu/..." print(fw.requirement_count) print(fw.coverage) # dict with sync coverage info ``` **Parameters** | Parameter | Type | Description | | --------- | ----- | ------------------------------------------------ | | `slug` | `str` | Framework slug, e.g. `"gdpr"`, `"cra"`, `"nis2"` | **Returns** `FrameworkDetail` *** ### `iter()` Iterate over every framework across all pages without manual pagination. ```python theme={null} for fw in client.frameworks.iter(): print(fw.slug, fw.name) ``` **Parameters** — same kwargs as `list()` (except `page`, which is managed internally). **Returns** `Iterator[Framework]` *** ## Models ### `Framework` Returned by `list()` and `iter()`. | Field | Type | Description | | ------------------- | ------------- | ---------------------------------------- | | `id` | `int` | Internal numeric ID | | `slug` | `str` | URL-safe identifier, e.g. `"gdpr"` | | `name` | `str` | Full official name | | `short_name` | `str` | Abbreviated name, e.g. `"GDPR"` | | `celex_number` | `str \| None` | EUR-Lex CELEX identifier | | `description` | `str \| None` | Plain-text summary | | `is_active` | `bool` | Whether the framework is in force | | `status` | `str` | `"active"`, `"draft"`, or `"superseded"` | | `expected_articles` | `int \| None` | Known article count from official text | | `expected_recitals` | `int \| None` | Known recital count | | `last_synced_at` | `str \| None` | ISO 8601 timestamp of last data sync | | `created_at` | `str` | ISO 8601 timestamp of record creation | | `article_count` | `int` | Articles currently in the database | | `recital_count` | `int` | Recitals currently in the database | ### `FrameworkDetail` Returned by `get()`. Extends `Framework` with: | Field | Type | Description | | ------------------- | ------------- | ---------------------------------------- | | `eurlex_url` | `str \| None` | Canonical EUR-Lex URL | | `requirement_count` | `int` | Parsed compliance requirements | | `annex_count` | `int` | Annexes in the database | | `tag_count` | `int` | Tags applied to this framework's content | | `coverage` | `dict` | Sync coverage statistics | *** ## Examples ### Print a framework summary table ```python theme={null} from law4devs import Law4DevsClient client = Law4DevsClient() print(f"{'Slug':<12} {'Name':<45} {'Articles':>8} {'Status'}") print("-" * 75) for fw in client.frameworks.iter(): print(f"{fw.slug:<12} {fw.name:<45} {fw.article_count:>8} {fw.status}") ``` ### Filter active frameworks only ```python theme={null} active = [fw for fw in client.frameworks.iter() if fw.status == "active"] print(f"{len(active)} active frameworks") ``` ### Inspect a framework's coverage ```python theme={null} fw = client.frameworks.get("cra") print(f"CRA: {fw.article_count}/{fw.expected_articles} articles synced") print(f" {fw.recital_count}/{fw.expected_recitals} recitals synced") ``` ### Look up a framework by short name ```python theme={null} all_frameworks = list(client.frameworks.iter()) match = next((f for f in all_frameworks if f.short_name == "NIS2"), None) if match: print(match.slug) # "nis2" ``` Framework slugs are stable identifiers — safe to hardcode in your application. They will not change between API versions. Some frameworks have `status="superseded"` (e.g. NIS1). The data is still accessible and browsable, but these frameworks are no longer in force. # Installation Source: https://docs.law4devs.eu/sdks/python/installation Install the Law4Devs Python SDK and make your first API call. ## Requirements * Python 3.9 or later * No runtime dependencies — the SDK uses only the Python standard library (`urllib`) ## Install ```bash theme={null} pip install law4devs ``` To pin to a specific version: ```bash theme={null} pip install law4devs==1.0.0 ``` ## First Call ```python theme={null} import os from law4devs import Law4DevsClient client = Law4DevsClient(api_key=os.environ["LAW4DEVS_API_KEY"]) page = client.frameworks.list() print(page.data[0].name) # e.g. "Cyber Resilience Act" ``` ## Configuration ```python theme={null} import os from law4devs import Law4DevsClient client = Law4DevsClient( api_key=os.environ["LAW4DEVS_API_KEY"], base_url="https://api.law4devs.eu/v1", # default timeout=30, # seconds, default 30 max_retries=3, # retries on 429/5xx, default 3 ) ``` | Option | Type | Default | Description | | ------------- | ----- | -------------- | ---------------------------- | | `api_key` | `str` | — | **Required.** Your API key | | `base_url` | `str` | Production URL | Override for dev environment | | `timeout` | `int` | `30` | Request timeout in seconds | | `max_retries` | `int` | `3` | Retries on 429/5xx | ## Environment Variable ```bash theme={null} export LAW4DEVS_API_KEY="your-key" ``` ```python theme={null} import os from law4devs import Law4DevsClient client = Law4DevsClient(api_key=os.environ["LAW4DEVS_API_KEY"]) ``` Never hardcode your API key in source code or commit it to version control. ## Verify the Install ```bash theme={null} LAW4DEVS_API_KEY=your-key python -c " from law4devs import Law4DevsClient import os c = Law4DevsClient(api_key=os.environ['LAW4DEVS_API_KEY']) print(c.frameworks.list().meta.total, 'frameworks') " ``` # Pagination Source: https://docs.law4devs.eu/sdks/python/pagination Manual pagination and auto-pagination with iter() in the Law4Devs Python SDK. ## Overview Every list endpoint returns a `Page[T]` object. You can either paginate manually using the `.links` and `.meta` fields, or let the SDK handle it automatically with `iter()`. *** ## The `Page[T]` Object ```python theme={null} page = client.frameworks.list() # The items on this page page.data # list[Framework] # Metadata about the full result set page.meta.total # int — total items across all pages page.meta.page # int — current page number (1-indexed) page.meta.per_page # int — items per page page.meta.pages # int — total number of pages # Navigation links (None if not applicable) page.links.next # str | None — URL of the next page page.links.prev # str | None — URL of the previous page ``` *** ## Manual Pagination Loop through pages yourself by checking `page.links.next`: ```python theme={null} from law4devs import Law4DevsClient client = Law4DevsClient() page_num = 1 while True: page = client.frameworks.list(page=page_num, per_page=10) for fw in page.data: print(fw.name) if page.links.next is None: break page_num += 1 print(f"Done. Total frameworks: {page.meta.total}") ``` *** ## Auto-Pagination with `iter()` Every resource exposes an `iter()` method that fetches pages on demand and yields items one at a time. This is the recommended approach for processing complete result sets. ### Iterate all frameworks ```python theme={null} for fw in client.frameworks.iter(): print(fw.slug, fw.name) ``` ### Iterate all articles in a framework ```python theme={null} for article in client.articles.iter("gdpr"): print(f"Art. {article.article_number}: {article.title}") ``` ### Iterate with a custom page size Use `per_page` to control how many items are fetched per HTTP request. Larger values reduce the number of requests but use more memory per page. ```python theme={null} # Fetch 100 articles per request instead of the default 20 for article in client.articles.iter("gdpr", per_page=100): print(article.article_number) ``` *** ## Collecting Results into a List ```python theme={null} # Collect all requirements for a framework into a list all_requirements = list(client.requirements.iter(framework_slug="gdpr")) print(f"Loaded {len(all_requirements)} requirements") ``` For large frameworks, collecting everything into a list can use significant memory. Prefer streaming with a `for` loop when you don't need random access. *** ## The `per_page` Parameter All `list()` and `iter()` methods accept a `per_page` parameter: * Minimum: `1` * Maximum: `100` * Default: `20` ```python theme={null} # Smallest possible pages (useful for sampling) page = client.articles.list("gdpr", per_page=1) print(f"GDPR has {page.meta.total} articles total") # Largest pages (fewer HTTP requests) for article in client.articles.iter("cra", per_page=100): process(article) ``` *** ## Checking Total Count Without Fetching All Items Request a single item to read `meta.total` without loading the full data set: ```python theme={null} page = client.requirements.list(framework_slug="nis2", per_page=1) print(f"NIS2 has {page.meta.total} requirements") ``` *** ## Detailed Examples ### Process all frameworks with progress reporting ```python theme={null} frameworks = list(client.frameworks.iter()) total = len(frameworks) for i, fw in enumerate(frameworks, 1): print(f"[{i}/{total}] Processing {fw.slug}...") articles = list(client.articles.iter(fw.slug)) print(f" {len(articles)} articles") ``` ### Collect all requirements across all frameworks ```python theme={null} all_requirements = [] for fw in client.frameworks.iter(): for req in client.requirements.iter(framework_slug=fw.slug): all_requirements.append(req) print(f"Total requirements across all frameworks: {len(all_requirements)}") ``` ### Paginate with an offset (skip first N items) ```python theme={null} # Skip the first 40 items (pages 1–2 at per_page=20) and start from page 3 page = client.articles.list("gdpr", page=3, per_page=20) for article in page.data: print(article.article_number) ``` ### Stop iteration early Because `iter()` yields lazily, you can stop at any point without fetching the remaining pages: ```python theme={null} import itertools # Only process the first 50 articles, regardless of total count for article in itertools.islice(client.articles.iter("gdpr"), 50): print(article.title) ``` `iter()` is lazy — it only fetches the next page when the current page's items are exhausted. If you break out of the loop early, no unnecessary HTTP requests are made. # Quick Start Source: https://docs.law4devs.eu/sdks/python/quickstart Build your first Law4Devs integration in Python in under five minutes. ## Prerequisites Install the SDK first: ```bash theme={null} pip install law4devs ``` Then create a client: ```python theme={null} from law4devs import Law4DevsClient client = Law4DevsClient() ``` *** ## List All Frameworks Fetch the first page of available EU regulatory frameworks: ```python theme={null} from law4devs import Law4DevsClient client = Law4DevsClient() page = client.frameworks.list() print(f"Total frameworks: {page.meta.total}") for fw in page.data: print(f" {fw.slug}: {fw.name} ({fw.article_count} articles)") ``` Example output: ``` Total frameworks: 19 gdpr: General Data Protection Regulation (99 articles) cra: Cyber Resilience Act (71 articles) nis2: Network and Information Security Directive 2 (46 articles) ... ``` *** ## Iterate All CRA Articles Use `iter()` to walk every article without managing pagination yourself: ```python theme={null} for article in client.articles.iter("cra"): print(f"Art. {article.article_number}: {article.title}") ``` `iter()` automatically fetches subsequent pages until the list is exhausted. *** ## Fetch a Single Article ```python theme={null} article = client.articles.get("gdpr", 17) print(article.title) # "Right to erasure ('right to be forgotten')" for para in article.paragraphs: print(f" §{para.paragraph_number}: {para.content[:80]}...") ``` *** ## Search Across All Frameworks ```python theme={null} results = client.search.query("data breach notification", framework="gdpr") for r in results.data: print(r.match_context) ``` Narrow by result type: ```python theme={null} results = client.search.query("incident response", result_type="article") ``` *** ## Get Compliance Requirements Pull every GDPR requirement and print its type: ```python theme={null} for req in client.requirements.iter(framework_slug="gdpr"): print(f"[{req.requirement_type}] Art. {req.article_number} — {req.requirement_text[:60]}...") ``` *** ## Get Compliance Deadlines ```python theme={null} for deadline in client.compliance.iter_deadlines(framework_slug="nis2"): print(f"{deadline.deadline_date}: {deadline.description}") ``` *** ## Next Steps # Recitals Source: https://docs.law4devs.eu/sdks/python/recitals Fetch and iterate framework recitals with the Law4Devs Python SDK. ## Overview Recitals are the preamble paragraphs of EU legislation. They provide interpretive context for the articles that follow and are often cited when explaining the intent behind a legal requirement. Each recital belongs to one framework. *** ## Methods ### `list()` Returns a page of recitals for a framework. ```python theme={null} page = client.recitals.list("gdpr", page=1, per_page=20) print(f"GDPR has {page.meta.total} recitals") for recital in page.data: print(f" Recital {recital.recital_number}: {recital.content[:80]}...") ``` **Parameters** | Parameter | Type | Default | Description | | ---------------- | ----- | -------- | ----------------------------- | | `framework_slug` | `str` | required | Framework slug, e.g. `"gdpr"` | | `page` | `int` | `1` | Page number | | `per_page` | `int` | `20` | Items per page (1–100) | **Returns** `Page[Recital]` *** ### `get()` Fetch a single recital by its number. ```python theme={null} recital = client.recitals.get("gdpr", 26) print(f"Recital {recital.recital_number}") print(recital.content) ``` **Parameters** | Parameter | Type | Description | | ---------------- | ----- | ----------------------------------- | | `framework_slug` | `str` | Framework slug | | `recital_number` | `int` | Recital number within the framework | **Returns** `Recital` *** ### `iter()` Iterate over all recitals in a framework across all pages. ```python theme={null} for recital in client.recitals.iter("gdpr"): print(f"({recital.recital_number}) {recital.content[:120]}...") ``` **Parameters** — same kwargs as `list()` (except `page`). **Returns** `Iterator[Recital]` *** ## Model ### `Recital` | Field | Type | Description | | ---------------- | ----- | ----------------------------------- | | `id` | `int` | Internal numeric ID | | `framework_slug` | `str` | Parent framework slug | | `recital_number` | `int` | Recital number within the framework | | `content` | `str` | Full text of the recital | | `position` | `int` | Ordering position in the framework | *** ## Examples ### Print all GDPR recitals ```python theme={null} from law4devs import Law4DevsClient client = Law4DevsClient() for r in client.recitals.iter("gdpr"): print(f"({r.recital_number}) {r.content}\n") ``` ### Search recital text for a keyword ```python theme={null} keyword = "consent" matches = [ r for r in client.recitals.iter("gdpr") if keyword.lower() in r.content.lower() ] print(f"{len(matches)} GDPR recitals mention '{keyword}':") for r in matches: print(f" Recital {r.recital_number}") ``` ### Fetch a specific recital ```python theme={null} # Recital 26 is the famous "not a natural person" recital r = client.recitals.get("gdpr", 26) print(r.content) ``` ### Count recitals per framework ```python theme={null} frameworks = list(client.frameworks.iter()) for fw in frameworks: page = client.recitals.list(fw.slug, per_page=1) print(f"{fw.short_name}: {page.meta.total} recitals") ``` Recitals are numbered sequentially within each framework. Recital numbers are stable and match the official EUR-Lex publication. # Requirements Source: https://docs.law4devs.eu/sdks/python/requirements Query parsed compliance requirements from EU frameworks using the Law4Devs Python SDK. ## Overview Requirements are machine-readable compliance obligations extracted from framework articles. Each requirement is tied to a specific article (and optionally a paragraph), carries a type classification, and lists the stakeholder roles it applies to. *** ## Methods ### `list()` Returns a page of requirements. Without filters, this returns requirements across all frameworks. ```python theme={null} # All requirements (all frameworks) page = client.requirements.list() print(f"Total requirements: {page.meta.total}") # Filter to a single framework page = client.requirements.list(framework_slug="gdpr") for req in page.data: print(f"[{req.requirement_type}] Art. {req.article_number}: {req.requirement_text[:60]}...") ``` **Parameters** | Parameter | Type | Default | Description | | ---------------- | ----- | ------- | ---------------------- | | `framework_slug` | `str` | `None` | Filter by framework | | `page` | `int` | `1` | Page number | | `per_page` | `int` | `20` | Items per page (1–100) | **Returns** `Page[Requirement]` *** ### `iter()` Iterate over all requirements, optionally filtered by framework. ```python theme={null} for req in client.requirements.iter(framework_slug="cra"): print(req.requirement_text) ``` **Parameters** — same kwargs as `list()` (except `page`). **Returns** `Iterator[Requirement]` *** ## Model ### `Requirement` | Field | Type | Description | | ------------------------ | ------------- | ------------------------------------------------------------------------------------- | | `id` | `int` | Internal numeric ID | | `framework_slug` | `str` | Parent framework slug | | `article_number` | `int` | Source article number | | `paragraph_ref` | `str \| None` | Paragraph label, e.g. `"1"`, `"2a"` | | `paragraph_content` | `str \| None` | Full text of the source paragraph | | `requirement_text` | `str` | Extracted obligation text | | `requirement_type` | `str` | Classification: `"obligation"`, `"prohibition"`, `"permission"`, `"definition"`, etc. | | `compliance_deadline` | `str \| None` | ISO 8601 date if a deadline is specified | | `linked_article_numbers` | `list[int]` | Related articles within the same framework | | `stakeholder_roles` | `list[str]` | Roles this applies to, e.g. `["controller", "processor"]` | | `tags` | `list[str]` | Tag slugs | | `created_at` | `str` | ISO 8601 creation timestamp | *** ## Examples ### Build a compliance checklist ```python theme={null} from law4devs import Law4DevsClient client = Law4DevsClient() print("GDPR Compliance Checklist") print("=" * 60) obligations = [ req for req in client.requirements.iter(framework_slug="gdpr") if req.requirement_type == "obligation" ] for i, req in enumerate(obligations, 1): roles = ", ".join(req.stakeholder_roles) or "all parties" print(f"\n[{i}] Art. {req.article_number} — {roles}") print(f" {req.requirement_text}") if req.compliance_deadline: print(f" Deadline: {req.compliance_deadline}") ``` ### Filter requirements for a specific role ```python theme={null} controller_reqs = [ req for req in client.requirements.iter(framework_slug="gdpr") if "controller" in req.stakeholder_roles ] print(f"GDPR has {len(controller_reqs)} requirements for data controllers") ``` ### Find requirements with deadlines ```python theme={null} with_deadlines = [ req for req in client.requirements.iter() if req.compliance_deadline is not None ] for req in sorted(with_deadlines, key=lambda r: r.compliance_deadline): print(f"{req.compliance_deadline} [{req.framework_slug}] Art. {req.article_number}") ``` ### Group requirements by type ```python theme={null} from collections import defaultdict by_type = defaultdict(list) for req in client.requirements.iter(framework_slug="nis2"): by_type[req.requirement_type].append(req) for req_type, reqs in sorted(by_type.items()): print(f"{req_type}: {len(reqs)} requirements") ``` ### Export requirements to a CSV ```python theme={null} import csv with open("gdpr_requirements.csv", "w", newline="") as f: writer = csv.DictWriter(f, fieldnames=[ "article_number", "paragraph_ref", "requirement_type", "stakeholder_roles", "requirement_text", ]) writer.writeheader() for req in client.requirements.iter(framework_slug="gdpr"): writer.writerow({ "article_number": req.article_number, "paragraph_ref": req.paragraph_ref or "", "requirement_type": req.requirement_type, "stakeholder_roles": "; ".join(req.stakeholder_roles), "requirement_text": req.requirement_text, }) print("Exported to gdpr_requirements.csv") ``` The `requirement_type` field makes it easy to separate hard obligations from definitions and permissions. Filter on `"obligation"` or `"prohibition"` when building audit checklists. `linked_article_numbers` contains article numbers within the same framework, not cross-framework references. # Search Source: https://docs.law4devs.eu/sdks/python/search Full-text search across EU frameworks with the Law4Devs Python SDK. ## Overview The search endpoint performs full-text search across articles, recitals, and requirements. Results include a `match_context` snippet showing where the query matched. Use filters to narrow by framework or content type. *** ## Methods ### `query()` Run a full-text search and return a page of results. ```python theme={null} results = client.search.query("data breach notification") print(f"Found {len(results.data)} results") for r in results.data: print(f" [{r.type}] {r.framework_slug} — {r.match_context[:80]}...") ``` **Parameters** | Parameter | Type | Default | Description | | ------------- | ----- | -------- | -------------------------------------------- | | `q` | `str` | required | Search query string | | `framework` | `str` | `None` | Limit to a specific framework slug | | `result_type` | `str` | `None` | `"article"`, `"recital"`, or `"requirement"` | | `page` | `int` | `1` | Page number | | `per_page` | `int` | `20` | Items per page (1–100) | **Returns** `Page[SearchResult]` `page.meta.pages` is always `0` for search results. The search endpoint does not support traditional page-count pagination. Use the presence of `page.links.next` to detect whether more results exist. *** ## Model ### `SearchResult` | Field | Type | Description | | ------------------ | ------------- | --------------------------------------------------------- | | `type` | `str` | Result type: `"article"`, `"recital"`, or `"requirement"` | | `framework_slug` | `str` | Framework the result belongs to | | `framework_name` | `str` | Human-readable framework name | | `article_number` | `int \| None` | Article number (for article and requirement results) | | `recital_number` | `int \| None` | Recital number (for recital results) | | `paragraph_ref` | `str \| None` | Paragraph label (for requirement results) | | `title` | `str \| None` | Article or section heading | | `requirement_type` | `str \| None` | Requirement classification (requirement results only) | | `match_context` | `str` | Snippet of matched text with surrounding context | | `url` | `str` | Direct API URL to the matched resource | *** ## Examples ### Basic search ```python theme={null} from law4devs import Law4DevsClient client = Law4DevsClient() results = client.search.query("data breach notification") for r in results.data: print(f"[{r.type:12}] {r.framework_slug:8} {r.title or ''}") print(f" ...{r.match_context}...") print() ``` ### Search within a specific framework ```python theme={null} results = client.search.query("legitimate interest", framework="gdpr") print(f"{len(results.data)} GDPR results for 'legitimate interest'") for r in results.data: ref = f"Art. {r.article_number}" if r.article_number else f"Recital {r.recital_number}" print(f" {ref}: {r.match_context[:80]}") ``` ### Filter by result type ```python theme={null} # Only articles articles = client.search.query("incident response", result_type="article") # Only requirements reqs = client.search.query("incident response", result_type="requirement") print(f"Articles: {len(articles.data)}, Requirements: {len(reqs.data)}") ``` ### Search across multiple frameworks manually ```python theme={null} frameworks_to_search = ["gdpr", "nis2", "cra"] query = "vulnerability disclosure" all_results = [] for fw in frameworks_to_search: page = client.search.query(query, framework=fw) all_results.extend(page.data) print(f"'{query}' found in {len(all_results)} places across {len(frameworks_to_search)} frameworks") ``` ### Paginate through search results ```python theme={null} query = "security measures" page = client.search.query(query, per_page=50) all_results = list(page.data) # meta.pages is always 0 for search — check links.next instead while page.links.next: page = client.search.query(query, page=page.meta.page + 1, per_page=50) all_results.extend(page.data) print(f"Total results collected: {len(all_results)}") ``` ### Display results grouped by framework ```python theme={null} from collections import defaultdict results = client.search.query("encryption", per_page=100) by_framework = defaultdict(list) for r in results.data: by_framework[r.framework_name].append(r) for fw_name, matches in sorted(by_framework.items()): print(f"\n{fw_name} ({len(matches)} matches):") for r in matches: ref = f"Art. {r.article_number}" if r.article_number else f"Recital {r.recital_number}" print(f" {ref}: {r.match_context[:60]}...") ``` Combine `framework` and `result_type` filters to narrow results significantly. For example, `result_type="requirement"` limits results to machine-readable obligations, which is useful when building compliance checkers. Search queries are matched against parsed text content. Very short queries (one or two characters) may produce unexpected results. Use at least three characters for meaningful searches. # Tags Source: https://docs.law4devs.eu/sdks/python/tags List and inspect classification tags with the Law4Devs Python SDK. ## Overview Tags are predefined classification labels applied to articles and requirements. They enable cross-framework filtering by topic — for example, finding all content related to `"data-breach"` across GDPR, NIS2, and the CRA. *** ## Methods ### `list()` Returns a page of all tags. ```python theme={null} page = client.tags.list() print(f"Total tags: {page.meta.total}") for tag in page.data: print(f" {tag.slug}: {tag.name}") ``` **Parameters** | Parameter | Type | Default | Description | | ---------- | ----- | ------- | ---------------------- | | `page` | `int` | `1` | Page number | | `per_page` | `int` | `20` | Items per page (1–100) | **Returns** `Page[Tag]` *** ### `get()` Fetch a single tag by its slug. ```python theme={null} tag = client.tags.get("data-breach") print(tag.name) # "Data Breach" print(tag.description) # "Articles relating to personal data breach..." print(tag.keywords) # ["breach", "notification", "incident", ...] print(tag.color) # "#e74c3c" ``` **Parameters** | Parameter | Type | Description | | --------- | ----- | ------------------------------ | | `slug` | `str` | Tag slug, e.g. `"data-breach"` | **Returns** `Tag` *** ### `iter()` Iterate over all tags. ```python theme={null} for tag in client.tags.iter(): print(f"{tag.slug}: {tag.name}") ``` **Returns** `Iterator[Tag]` *** ## Model ### `Tag` | Field | Type | Description | | ------------- | ------------- | ----------------------------------------- | | `id` | `int` | Internal numeric ID | | `slug` | `str` | URL-safe identifier, e.g. `"data-breach"` | | `name` | `str` | Human-readable label | | `description` | `str \| None` | What this tag covers | | `keywords` | `list[str]` | Keywords used to auto-tag content | | `color` | `str \| None` | Hex color code for UI display | | `created_at` | `str` | ISO 8601 creation timestamp | *** ## Examples ### Print all tags with descriptions ```python theme={null} from law4devs import Law4DevsClient client = Law4DevsClient() for tag in client.tags.iter(): desc = tag.description or "(no description)" print(f"{tag.name:25} {desc[:60]}") ``` ### Look up a tag's keywords ```python theme={null} tag = client.tags.get("consent") print(f"Auto-tag keywords for '{tag.name}':") for keyword in tag.keywords: print(f" - {keyword}") ``` ### Find all articles tagged with a given label ```python theme={null} target_tag = "security" matches = [ article for article in client.articles.iter("nis2") if target_tag in article.tags ] print(f"{len(matches)} NIS2 articles tagged '{target_tag}':") for a in matches: print(f" Art. {a.article_number}: {a.title}") ``` ### Build a tag index across all frameworks ```python theme={null} tag_index = {} # tag_slug -> list of (framework, article_number) for fw in client.frameworks.iter(): for article in client.articles.iter(fw.slug): for tag_slug in article.tags: tag_index.setdefault(tag_slug, []).append( (fw.slug, article.article_number) ) # Print most used tags for slug, refs in sorted(tag_index.items(), key=lambda x: -len(x[1])): print(f"{slug}: {len(refs)} articles") ``` Tag slugs are stable — safe to hardcode in application logic or store in databases as foreign keys. # Annexes Source: https://docs.law4devs.eu/sdks/rust/annexes List and retrieve framework annexes with the Rust SDK. ## Methods ```rust theme={null} client.annexes.list(framework_slug: &str, page: Option, per_page: Option) -> Result, Law4DevsError> client.annexes.get(framework_slug: &str, annex_number: &str) -> Result client.annexes.iter(framework_slug: &str, per_page: u32) -> impl Stream> ``` *** ## List Annexes ```rust theme={null} let page = client.annexes.list("cra", None, None).await?; for a in &page.data { println!("Annex {}: {}", a.annex_number, a.title); } ``` *** ## Get an Annex ```rust theme={null} let annex = client.annexes.get("cra", "I").await?; println!("{}", annex.content); ``` *** ## Auto-Paginate ```rust theme={null} use futures::StreamExt; let mut stream = Box::pin(client.annexes.iter("cra", 20)); while let Some(result) = stream.next().await { let a = result?; println!("Annex {} — {}", a.annex_number, a.title); } ``` *** ## `AnnexSummary` Fields | Field | Type | Description | | ----------------- | ---------------- | ----------------------------- | | `id` | `u32` | Internal ID | | `framework_slug` | `String` | Parent framework slug | | `annex_number` | `String` | Annex identifier (e.g. `"I"`) | | `title` | `String` | Annex title | | `content_summary` | `Option` | Brief summary | ## `Annex` Additional Fields | Field | Type | Description | | --------- | -------- | --------------- | | `content` | `String` | Full annex text | # Articles Source: https://docs.law4devs.eu/sdks/rust/articles List, retrieve, and iterate articles with the Rust SDK. ## Methods ```rust theme={null} // List articles for a framework client.articles.list(framework_slug: &str, page: Option, per_page: Option) -> Result, Law4DevsError> // Get a specific article client.articles.get(framework_slug: &str, article_number: &str) -> Result // Get related articles client.articles.related(framework_slug: &str, article_number: &str, page: Option, per_page: Option) -> Result, Law4DevsError> // Auto-paginate all articles client.articles.iter(framework_slug: &str, per_page: u32) -> impl Stream> ``` *** ## List Articles ```rust theme={null} let page = client.articles.list("gdpr", None, None).await?; for a in &page.data { println!("Art. {}: {}", a.article_number, a.title); } ``` *** ## Get an Article ```rust theme={null} let article = client.articles.get("gdpr", "5").await?; println!("{}", article.content); for para in &article.paragraphs { println!("[{}] {}", para.paragraph_ref, para.content); } ``` *** ## Related Articles ```rust theme={null} let related = client.articles.related("gdpr", "5", None, None).await?; for a in &related.data { println!("{}: {}", a.article_number, a.title); } ``` *** ## Auto-Paginate ```rust theme={null} use futures::StreamExt; let mut stream = Box::pin(client.articles.iter("nis2", 20)); while let Some(result) = stream.next().await { let a = result?; println!("Art. {} — {}", a.article_number, a.title); } ``` *** ## `ArticleSummary` Fields | Field | Type | Description | | ----------------- | ---------- | --------------------------- | | `id` | `u32` | Internal ID | | `framework_slug` | `String` | Parent framework slug | | `article_number` | `String` | Article number (e.g. `"5"`) | | `title` | `String` | Article title | | `position` | `u32` | Order within the framework | | `paragraph_count` | `u32` | Number of paragraphs | | `tags` | `Vec` | Associated compliance tags | *** ## `Article` Additional Fields | Field | Type | Description | | ------------ | ----------------------- | --------------------- | | `content` | `String` | Full article text | | `paragraphs` | `Vec` | Individual paragraphs | # Compliance Deadlines Source: https://docs.law4devs.eu/sdks/rust/compliance List and iterate compliance deadlines with the Rust SDK. ## Methods ```rust theme={null} client.compliance.deadlines(framework_slug: Option<&str>, page: Option, per_page: Option) -> Result, Law4DevsError> client.compliance.iter_deadlines(framework_slug: Option<&str>, per_page: u32) -> impl Stream> ``` *** ## List Deadlines ```rust theme={null} // All deadlines let page = client.compliance.deadlines(None, None, None).await?; // Deadlines for NIS2 let page = client.compliance.deadlines(Some("nis2"), None, None).await?; for d in &page.data { println!("{} — {}", d.deadline, d.title); } ``` *** ## Auto-Paginate ```rust theme={null} use futures::StreamExt; let mut stream = Box::pin(client.compliance.iter_deadlines(Some("nis2"), 20)); while let Some(result) = stream.next().await { let d = result?; println!("{} — {}", d.deadline, d.title); } ``` *** ## `ComplianceDeadline` Fields | Field | Type | Description | | ------------------- | ---------------- | --------------------- | | `id` | `u32` | Internal ID | | `framework_slug` | `String` | Parent framework slug | | `title` | `String` | Deadline title | | `description` | `Option` | Detailed description | | `deadline` | `String` | ISO 8601 date | | `deadline_type` | `Option` | Type of deadline | | `article_reference` | `Option` | Source article | # Error Handling Source: https://docs.law4devs.eu/sdks/rust/error-handling Handle API errors gracefully with the Rust SDK. ## Error Enum ``` Law4DevsError ├── NotFound(String) — 404 Not Found ├── Validation(String) — 400 Bad Request ├── RateLimit — 429 Too Many Requests ├── Server(u16, String) — 5xx Server Error ├── Network(reqwest::Error)— Network/timeout failure └── Parse(serde_json::Error)— JSON deserialization failure ``` `Law4DevsError` implements `std::error::Error` and `Display` via `thiserror`. *** ## Matching Errors ```rust theme={null} use law4devs::{Law4DevsClient, Law4DevsError}; match client.frameworks.get("nonexistent").await { Ok(fw) => println!("{}", fw.name), Err(Law4DevsError::NotFound(msg)) => eprintln!("404: {msg}"), Err(Law4DevsError::Validation(msg)) => eprintln!("400: {msg}"), Err(Law4DevsError::RateLimit) => eprintln!("429: rate limited"), Err(Law4DevsError::Server(code, msg)) => eprintln!("{code}: {msg}"), Err(Law4DevsError::Network(e)) => eprintln!("network: {e}"), Err(e) => eprintln!("other: {e}"), } ``` *** ## With `?` Propagation ```rust theme={null} async fn get_cra_articles(client: &Law4DevsClient) -> Result, Law4DevsError> { let page = client.articles.list("cra", None, Some(50)).await?; Ok(page.data) } ``` *** ## Retry Behavior The SDK automatically retries `429` and `5xx` responses with exponential backoff: | Attempt | Delay | | --------- | --------- | | 1st retry | 1 second | | 2nd retry | 2 seconds | | 3rd retry | 4 seconds | `404` and `400` are thrown immediately without retrying. *** ## Safe Get Pattern ```rust theme={null} async fn safe_get(client: &Law4DevsClient, slug: &str) -> Option { match client.frameworks.get(slug).await { Ok(fw) => Some(fw), Err(Law4DevsError::NotFound(_)) => None, Err(e) => { eprintln!("Error: {e}"); None } } } ``` *** ## Streams and Errors Each item in an `iter()` stream is a `Result`. A single failed page stops the stream: ```rust theme={null} use futures::StreamExt; let mut stream = Box::pin(client.articles.iter("gdpr", 20)); while let Some(result) = stream.next().await { match result { Ok(a) => println!("{}", a.title), Err(e) => { eprintln!("Stream error: {e}"); break; } } } ``` # Frameworks Source: https://docs.law4devs.eu/sdks/rust/frameworks List and retrieve EU regulatory frameworks with the Rust SDK. ## Methods ```rust theme={null} // List frameworks (paginated) client.frameworks.list(page: Option, per_page: Option) -> Result, Law4DevsError> // Get a framework by slug client.frameworks.get(slug: &str) -> Result // Auto-paginate all frameworks client.frameworks.iter(per_page: u32) -> impl Stream> ``` *** ## List Frameworks ```rust theme={null} let page = client.frameworks.list(None, None).await?; println!("{} frameworks", page.meta.total); for fw in &page.data { println!("{} — {}", fw.slug, fw.name); } ``` With pagination: ```rust theme={null} let page = client.frameworks.list(Some(1), Some(10)).await?; ``` *** ## Get a Framework ```rust theme={null} let gdpr = client.frameworks.get("gdpr").await?; println!("{}: {} articles", gdpr.name, gdpr.article_count); ``` *** ## Auto-Paginate ```rust theme={null} use futures::StreamExt; let mut stream = Box::pin(client.frameworks.iter(20)); while let Some(result) = stream.next().await { let fw = result?; println!("{}", fw.name); } ``` *** ## `Framework` Fields | Field | Type | Description | | --------------- | ---------------- | --------------------------------- | | `id` | `u32` | Internal ID | | `slug` | `String` | URL-safe identifier (e.g. `gdpr`) | | `name` | `String` | Full name | | `short_name` | `String` | Abbreviated name | | `celex_number` | `String` | EUR-Lex CELEX number | | `description` | `Option` | Short description | | `is_active` | `bool` | Whether actively maintained | | `status` | `String` | `active` or `superseded` | | `article_count` | `u32` | Number of scraped articles | | `recital_count` | `u32` | Number of scraped recitals | *** ## `FrameworkDetail` Additional Fields | Field | Type | Description | | ------------------- | --------------- | ------------------------------- | | `eurlex_url` | `String` | Link to original EUR-Lex source | | `requirement_count` | `u32` | Total requirements | | `annex_count` | `u32` | Total annexes | | `tag_count` | `u32` | Number of associated tags | | `coverage` | `Option` | Scrape coverage statistics | # Installation Source: https://docs.law4devs.eu/sdks/rust/installation Install the Law4Devs Rust SDK and make your first API call. ## Requirements * Rust 1.85+ * Tokio async runtime ## Install Add to your `Cargo.toml`: ```toml theme={null} [dependencies] law4devs = "0.1" tokio = { version = "1", features = ["full"] } futures = "0.3" ``` Or use `cargo add`: ```bash theme={null} cargo add law4devs cargo add tokio --features full cargo add futures ``` ## First Call ```rust theme={null} use law4devs::Law4DevsClient; #[tokio::main] async fn main() -> Result<(), Box> { let client = Law4DevsClient::new(); let page = client.frameworks.list(None, None).await?; println!("{} frameworks", page.meta.total); Ok(()) } ``` ## Configuration ```rust theme={null} use std::time::Duration; use law4devs::Law4DevsClient; let client = Law4DevsClient::builder() .api_key(std::env::var("LAW4DEVS_API_KEY").unwrap_or_default()) .base_url("https://api.law4devs.eu/v1") // default .timeout(Duration::from_secs(30)) // default .max_retries(3) // retries on 429/5xx .build(); ``` | Option | Type | Default | Description | | ------------- | ---------- | -------------- | --------------------------------------------------- | | `api_key` | `&str` | `None` | Your API key (required for authenticated endpoints) | | `base_url` | `&str` | Production URL | Override for dev/staging environment | | `timeout` | `Duration` | 30 seconds | Per-request HTTP timeout | | `max_retries` | `u32` | `3` | Retries on 429/5xx with exponential backoff | ## Environment Variable ```bash theme={null} export LAW4DEVS_API_KEY="your-key" ``` ```rust theme={null} let client = Law4DevsClient::builder() .api_key(std::env::var("LAW4DEVS_API_KEY").unwrap_or_default()) .build(); ``` Never hardcode your API key in source code or commit it to version control. ## Verify the Install ```rust theme={null} // src/main.rs use law4devs::Law4DevsClient; #[tokio::main] async fn main() -> Result<(), Box> { let client = Law4DevsClient::builder() .api_key(std::env::var("LAW4DEVS_API_KEY").unwrap_or_default()) .build(); let page = client.frameworks.list(None, None).await?; println!("{} frameworks", page.meta.total); Ok(()) } ``` ```bash theme={null} LAW4DEVS_API_KEY=your-key cargo run # → 19 frameworks ``` # Pagination Source: https://docs.law4devs.eu/sdks/rust/pagination Understand manual and automatic pagination in the Rust SDK. ## Overview Every `list()` / `deadlines()` / `query()` call returns a `Page`. The `iter()` / `iter_deadlines()` methods return a lazy `Stream` that auto-paginates across all pages. *** ## The `Page` Object ```rust theme={null} let page = client.frameworks.list(Some(1), Some(20)).await?; page.data // Vec — items on this page page.meta.total // u32 — total items across all pages page.meta.page // u32 — current page number page.meta.per_page // u32 — items per page page.meta.pages // u32 — total number of pages page.links.next // Option — URL of next page page.links.prev // Option — URL of previous page page.has_next() // bool — true if more pages exist ``` **`PageMeta` fields** | Field | Type | Description | | ---------- | ----- | -------------- | | `total` | `u32` | Total items | | `page` | `u32` | Current page | | `per_page` | `u32` | Items per page | | `pages` | `u32` | Total pages | *** ## Manual Pagination ```rust theme={null} let mut page_num = 1u32; loop { let page = client.frameworks.list(Some(page_num), Some(20)).await?; for fw in &page.data { println!("{}", fw.name); } if !page.has_next() { break; } page_num += 1; } ``` *** ## Automatic Pagination with `iter()` `iter()` returns a `Stream` — it fetches the next page only when the current page is exhausted. Use `Box::pin` to make it `Unpin` for `.next().await`: ```rust theme={null} use futures::StreamExt; let mut stream = Box::pin(client.frameworks.iter(20)); while let Some(result) = stream.next().await { let fw = result?; println!("{}", fw.name); } ``` With a custom page size: ```rust theme={null} let mut stream = Box::pin(client.articles.iter("gdpr", 50)); while let Some(result) = stream.next().await { let a = result?; println!("{}", a.title); } ``` *** ## Early Stopping ```rust theme={null} let mut stream = Box::pin(client.frameworks.iter(20)); while let Some(result) = stream.next().await { let fw = result?; if fw.slug == "gdpr" { println!("Found GDPR"); break; } } ``` *** ## Progress Tracking ```rust theme={null} let total = client.frameworks.list(None, None).await?.meta.total; let mut count = 0u32; let mut stream = Box::pin(client.frameworks.iter(20)); while let Some(result) = stream.next().await { let fw = result?; count += 1; println!("[{count}/{total}] {}", fw.name); } ``` # Quick Start Source: https://docs.law4devs.eu/sdks/rust/quickstart Make your first Law4Devs API call with Rust in under 60 seconds. ## Install ```toml theme={null} [dependencies] law4devs = "0.1" tokio = { version = "1", features = ["full"] } futures = "0.3" ``` ## Basic Usage ```rust theme={null} use futures::StreamExt; use law4devs::Law4DevsClient; #[tokio::main] async fn main() -> Result<(), Box> { let client = Law4DevsClient::builder() .api_key(std::env::var("LAW4DEVS_API_KEY").unwrap_or_default()) .build(); // List all frameworks let page = client.frameworks.list(None, None).await?; for fw in &page.data { println!("{} — {}", fw.slug, fw.name); } // List first 5 CRA articles let articles = client.articles.list("cra", None, Some(5)).await?; for a in &articles.data { println!("Art. {}: {}", a.article_number, a.title); } // Auto-paginate all GDPR articles let mut stream = Box::pin(client.articles.iter("gdpr", 20)); while let Some(result) = stream.next().await { let a = result?; println!("{} {}", a.article_number, a.title); } // Search across all frameworks let results = client .search .query("data breach notification", None, None, None, None) .await?; println!("Found {} results", results.meta.total); Ok(()) } ``` ## Next Steps # Recitals Source: https://docs.law4devs.eu/sdks/rust/recitals List and retrieve recitals with the Rust SDK. ## Methods ```rust theme={null} client.recitals.list(framework_slug: &str, page: Option, per_page: Option) -> Result, Law4DevsError> client.recitals.get(framework_slug: &str, recital_number: &str) -> Result client.recitals.iter(framework_slug: &str, per_page: u32) -> impl Stream> ``` *** ## List Recitals ```rust theme={null} let page = client.recitals.list("gdpr", None, None).await?; for r in &page.data { println!("Recital {}: {}...", r.recital_number, &r.content[..60]); } ``` *** ## Get a Recital ```rust theme={null} let recital = client.recitals.get("gdpr", "1").await?; println!("{}", recital.content); ``` *** ## Auto-Paginate ```rust theme={null} use futures::StreamExt; let mut stream = Box::pin(client.recitals.iter("gdpr", 20)); while let Some(result) = stream.next().await { let r = result?; println!("Recital {}", r.recital_number); } ``` *** ## `Recital` Fields | Field | Type | Description | | ---------------- | -------- | -------------------------- | | `id` | `u32` | Internal ID | | `framework_slug` | `String` | Parent framework slug | | `recital_number` | `String` | Recital number | | `content` | `String` | Full recital text | | `position` | `u32` | Order within the framework | # Requirements Source: https://docs.law4devs.eu/sdks/rust/requirements List and iterate compliance requirements with the Rust SDK. ## Methods ```rust theme={null} client.requirements.list(framework_slug: Option<&str>, page: Option, per_page: Option) -> Result, Law4DevsError> client.requirements.iter(framework_slug: Option<&str>, per_page: u32) -> impl Stream> ``` *** ## List Requirements ```rust theme={null} // All requirements let page = client.requirements.list(None, None, None).await?; // Requirements for a specific framework let page = client.requirements.list(Some("cra"), None, None).await?; ``` *** ## Auto-Paginate ```rust theme={null} use futures::StreamExt; let mut stream = Box::pin(client.requirements.iter(Some("cra"), 20)); while let Some(result) = stream.next().await { let r = result?; println!("[{}] {}", r.requirement_type, &r.requirement_text[..60.min(r.requirement_text.len())]); } ``` *** ## `Requirement` Fields | Field | Type | Description | | ------------------- | ---------------- | ---------------------------- | | `id` | `u32` | Internal ID | | `framework_slug` | `String` | Parent framework slug | | `requirement_type` | `String` | Type/category of requirement | | `requirement_text` | `String` | Full requirement text | | `article_reference` | `Option` | Source article number | | `stakeholder_type` | `Option` | Target stakeholder | # Search Source: https://docs.law4devs.eu/sdks/rust/search Search across all EU frameworks with the Rust SDK. ## Methods ```rust theme={null} client.search.query( q: &str, framework: Option<&str>, result_type: Option<&str>, page: Option, per_page: Option, ) -> Result, Law4DevsError> client.search.iter( q: &str, framework: Option<&str>, result_type: Option<&str>, per_page: u32, ) -> impl Stream> ``` *** ## Basic Search ```rust theme={null} let results = client .search .query("incident notification 72 hours", None, None, None, None) .await?; println!("Found {} results", results.meta.total); for r in &results.data { println!("[{}] {} {}: {}", r.framework_slug.to_uppercase(), r.result_type, r.number, r.title); } ``` *** ## Filtered Search ```rust theme={null} // Search within a specific framework let results = client .search .query("data breach", Some("gdpr"), None, None, Some(20)) .await?; // Filter by result type let results = client .search .query("encryption", Some("gdpr"), Some("article"), None, None) .await?; ``` *** ## Auto-Paginate Results ```rust theme={null} use futures::StreamExt; let mut stream = Box::pin(client.search.iter("vulnerability disclosure", None, None, 20)); while let Some(result) = stream.next().await { let r = result?; println!("[{}] {}", r.framework_slug, r.title); } ``` *** ## `SearchResult` Fields | Field | Type | Description | | ---------------- | ---------------- | -------------------------------------- | | `id` | `u32` | Internal ID | | `framework_slug` | `String` | Source framework | | `result_type` | `String` | `article`, `recital`, or `requirement` | | `number` | `String` | Article/recital/requirement number | | `title` | `String` | Title or first line | | `snippet` | `Option` | Matched text excerpt | | `score` | `f64` | Relevance score | # Tags Source: https://docs.law4devs.eu/sdks/rust/tags List and retrieve compliance tags with the Rust SDK. ## Methods ```rust theme={null} client.tags.list(page: Option, per_page: Option) -> Result, Law4DevsError> client.tags.get(slug: &str) -> Result client.tags.iter(per_page: u32) -> impl Stream> ``` *** ## List Tags ```rust theme={null} let page = client.tags.list(None, None).await?; for tag in &page.data { println!("{} — {} articles", tag.name, tag.article_count); } ``` *** ## Get a Tag ```rust theme={null} let tag = client.tags.get("data-protection").await?; println!("{}: {} frameworks", tag.name, tag.framework_count); ``` *** ## Auto-Paginate ```rust theme={null} use futures::StreamExt; let mut stream = Box::pin(client.tags.iter(50)); while let Some(result) = stream.next().await { let t = result?; println!("{}", t.slug); } ``` *** ## `Tag` Fields | Field | Type | Description | | ----------------- | ---------------- | ------------------------- | | `id` | `u32` | Internal ID | | `slug` | `String` | URL-safe identifier | | `name` | `String` | Display name | | `description` | `Option` | Tag description | | `framework_count` | `u32` | Frameworks using this tag | | `article_count` | `u32` | Articles with this tag | # Annexes Source: https://docs.law4devs.eu/sdks/typescript/annexes List, retrieve, and iterate framework annexes with the Law4Devs TypeScript SDK. ## Overview Annexes are supplementary parts of EU legislation, often containing technical specifications, lists, or tables referenced within the articles. Each annex belongs to one framework and is identified by its annex number or letter (e.g. `"I"`, `"II"`, `"A"`). *** ## Methods ### `list()` Returns a page of annex summaries for a framework. ```typescript theme={null} const page = await client.annexes.list('cra', { page: 1, perPage: 20 }); console.log(`CRA has ${page.meta.total} annexes`); for (const annex of page.data) { console.log(`Annex ${annex.annexNumber}: ${annex.title}`); } ``` **Parameters** | Parameter | Type | Default | Description | | --------------- | -------- | -------- | ---------------------------- | | `frameworkSlug` | `string` | required | Framework slug, e.g. `"cra"` | | `page` | `number` | `1` | Page number | | `perPage` | `number` | `20` | Items per page (1–100) | **Returns** `Promise>` *** ### `get()` Fetch the full content of a single annex. ```typescript theme={null} const annex = await client.annexes.get('cra', 'I'); console.log(`Annex ${annex.annexNumber}: ${annex.title}`); console.log(annex.content); ``` **Parameters** | Parameter | Type | Description | | --------------- | -------- | ------------------------------------------- | | `frameworkSlug` | `string` | Framework slug | | `annexNumber` | `string` | Annex identifier, e.g. `"I"`, `"II"`, `"A"` | **Returns** `Promise` *** ### `iter()` Iterate over all annexes in a framework. ```typescript theme={null} for await (const annex of client.annexes.iter('cra')) { console.log(`Annex ${annex.annexNumber}: ${annex.title}`); } ``` **Parameters** — same options as `list()` (except `page`, which is managed internally). **Returns** `AsyncGenerator` *** ## Models ### `AnnexSummary` Returned by `list()` and `iter()`. | Field | Type | Description | | --------------- | ---------------- | ------------------------------------------- | | `id` | `number` | Internal numeric ID | | `frameworkSlug` | `string` | Parent framework slug | | `annexNumber` | `string` | Annex identifier, e.g. `"I"`, `"II"`, `"A"` | | `title` | `string \| null` | Annex title | | `position` | `number` | Display order within the framework | ### `Annex` Returned by `get()`. Extends `AnnexSummary` with: | Field | Type | Description | | --------- | ---------------- | --------------- | | `content` | `string \| null` | Full annex text | *** ## Example: enumerate all CRA annexes ```typescript theme={null} const annexTitles: string[] = []; for await (const annex of client.annexes.iter('cra')) { annexTitles.push(`Annex ${annex.annexNumber}: ${annex.title ?? '(no title)'}`); } console.log('CRA Annexes:'); for (const title of annexTitles) { console.log(` - ${title}`); } ``` # Articles Source: https://docs.law4devs.eu/sdks/typescript/articles List, retrieve, and iterate framework articles with the Law4Devs TypeScript SDK. ## Overview Articles are the primary normative units of each EU regulation. Each article belongs to one framework and may contain multiple numbered paragraphs. Articles can also be tagged and cross-referenced to related articles. *** ## Methods ### `list()` Returns a page of article summaries for a framework. ```typescript theme={null} const page = await client.articles.list('gdpr', { page: 1, perPage: 20 }); console.log(`GDPR has ${page.meta.total} articles`); for (const article of page.data) { console.log(`Art. ${article.articleNumber}: ${article.title} (${article.paragraphCount} paragraphs)`); } ``` **Parameters** | Parameter | Type | Default | Description | | --------------- | -------- | -------- | ----------------------------- | | `frameworkSlug` | `string` | required | Framework slug, e.g. `"gdpr"` | | `page` | `number` | `1` | Page number | | `perPage` | `number` | `20` | Items per page (1–100) | **Returns** `Promise>` *** ### `get()` Fetch the full content of a single article including all paragraphs. ```typescript theme={null} const article = await client.articles.get('gdpr', 5); console.log(`Art. ${article.articleNumber}: ${article.title}`); console.log(article.content); for (const para of article.paragraphs) { console.log(` [${para.paragraphRef}] ${para.content}`); } ``` **Parameters** | Parameter | Type | Description | | --------------- | -------- | -------------- | | `frameworkSlug` | `string` | Framework slug | | `articleNumber` | `number` | Article number | **Returns** `Promise
` *** ### `related()` Fetch articles related to a given article (cross-references and thematic links). ```typescript theme={null} const related = await client.articles.related('gdpr', 5); console.log(`${related.meta.total} articles related to GDPR Art. 5`); for (const article of related.data) { console.log(` Art. ${article.articleNumber}: ${article.title}`); } ``` **Parameters** | Parameter | Type | Description | | --------------- | -------- | -------------- | | `frameworkSlug` | `string` | Framework slug | | `articleNumber` | `number` | Article number | **Returns** `Promise>` *** ### `iter()` Iterate over all articles in a framework without managing pagination. ```typescript theme={null} for await (const article of client.articles.iter('gdpr')) { const tagList = article.tags.map(t => t.slug).join(', '); console.log(`Art. ${article.articleNumber}: ${article.title} [${tagList}]`); } ``` **Parameters** — same options as `list()` (except `frameworkSlug` is the first positional argument and `page` is managed internally). **Returns** `AsyncGenerator` *** ## Models ### `ArticleSummary` Returned by `list()`, `related()`, and `iter()`. | Field | Type | Description | | ---------------- | ---------------- | ---------------------------------- | | `id` | `number` | Internal numeric ID | | `frameworkSlug` | `string` | Parent framework slug | | `articleNumber` | `number` | Article number | | `title` | `string \| null` | Article title | | `position` | `number` | Display order within the framework | | `paragraphCount` | `number` | Number of paragraphs | | `tags` | `Tag[]` | Tags applied to the article | ### `Article` Returned by `get()`. Extends `ArticleSummary` with: | Field | Type | Description | | ------------ | -------------------- | ------------------------- | | `content` | `string \| null` | Full article text | | `paragraphs` | `ArticleParagraph[]` | Structured paragraph list | ### `ArticleParagraph` | Field | Type | Description | | -------------- | -------- | --------------------------------------- | | `paragraphRef` | `string` | Paragraph reference, e.g. `"1"`, `"2a"` | | `content` | `string` | Paragraph text | | `position` | `number` | Display order within the article | *** ## Example: find all articles tagged "data-breach" ```typescript theme={null} const breachArticles: string[] = []; for await (const article of client.articles.iter('gdpr')) { const hasTag = article.tags.some(t => t.slug === 'data-breach'); if (hasTag) { breachArticles.push(`Art. ${article.articleNumber}: ${article.title}`); } } console.log('Data-breach articles:', breachArticles); ``` # Compliance Deadlines Source: https://docs.law4devs.eu/sdks/typescript/compliance Query and iterate compliance deadlines with the Law4Devs TypeScript SDK. ## Overview Compliance deadlines are specific dates by which organisations must have completed certain obligations under a regulation. They are extracted from articles and classified by type (e.g. `"transposition"`, `"application"`, `"notification"`). *** ## Methods ### `deadlines()` Returns a page of compliance deadlines, optionally filtered to a single framework. ```typescript theme={null} const page = await client.compliance.deadlines({ frameworkSlug: 'nis2' }); console.log(`NIS2 has ${page.meta.total} compliance deadlines`); for (const deadline of page.data) { const date = deadline.deadlineDate ?? 'no fixed date'; console.log(`[${deadline.deadlineType}] ${date}: ${deadline.description}`); } ``` **Parameters** | Parameter | Type | Default | Description | | --------------- | -------- | ------- | ------------------------------ | | `frameworkSlug` | `string` | — | Filter to a specific framework | | `page` | `number` | `1` | Page number | | `perPage` | `number` | `20` | Items per page (1–100) | **Returns** `Promise>` *** ### `iterDeadlines()` Iterate over all compliance deadlines without managing pagination. ```typescript theme={null} for await (const deadline of client.compliance.iterDeadlines({ frameworkSlug: 'nis2' })) { const date = deadline.deadlineDate ?? 'ongoing'; const ref = deadline.paragraphRef ? ` §${deadline.paragraphRef}` : ''; console.log(`Art. ${deadline.articleNumber}${ref} — ${deadline.deadlineType}: ${date}`); console.log(` ${deadline.description}`); } ``` The method is `iterDeadlines()`, not `iter()`. This is intentional — the compliance resource covers deadlines across multiple frameworks rather than scoping to a single one. **Parameters** — same options as `deadlines()` (except `page`, which is managed internally). **Returns** `AsyncGenerator` *** ## Model ### `ComplianceDeadline` | Field | Type | Description | | --------------- | ---------------- | ------------------------------------------------------------------- | | `id` | `number` | Internal numeric ID | | `frameworkSlug` | `string` | Parent framework slug | | `articleNumber` | `number` | Source article number | | `paragraphRef` | `string \| null` | Source paragraph reference | | `deadlineDate` | `string \| null` | ISO 8601 deadline date, or `null` if ongoing | | `deadlineType` | `string` | Category, e.g. `"transposition"`, `"application"`, `"notification"` | | `description` | `string` | Human-readable description of the obligation | *** ## Example: build a deadline calendar ```typescript theme={null} interface CalendarEntry { date: string; framework: string; type: string; description: string; } const calendar: CalendarEntry[] = []; // Collect deadlines for two frameworks in parallel const [nis2Page, craPage] = await Promise.all([ client.compliance.deadlines({ frameworkSlug: 'nis2', perPage: 100 }), client.compliance.deadlines({ frameworkSlug: 'cra', perPage: 100 }), ]); for (const deadline of [...nis2Page.data, ...craPage.data]) { if (deadline.deadlineDate) { calendar.push({ date: deadline.deadlineDate, framework: deadline.frameworkSlug.toUpperCase(), type: deadline.deadlineType, description: deadline.description, }); } } calendar.sort((a, b) => a.date.localeCompare(b.date)); for (const entry of calendar) { console.log(`${entry.date} [${entry.framework}] ${entry.type}: ${entry.description}`); } ``` To get deadlines across all frameworks at once, call `iterDeadlines()` without a `frameworkSlug` filter. # Error Handling Source: https://docs.law4devs.eu/sdks/typescript/error-handling Handle API errors gracefully with the Law4Devs TypeScript SDK. ## Exception hierarchy All errors thrown by the SDK extend `Law4DevsError`, which itself extends the built-in `Error`. Use `instanceof` checks to handle specific error types. ``` Law4DevsError ├── NotFoundError (HTTP 404) ├── ValidationError (HTTP 422) ├── RateLimitError (HTTP 429) └── ServerError (HTTP 5xx) ``` Every error class exposes a `statusCode` property with the underlying HTTP status code. ```typescript theme={null} import { Law4DevsError, NotFoundError, ValidationError, RateLimitError, ServerError, } from 'law4devs'; ``` *** ## Basic try/catch ```typescript theme={null} try { const article = await client.articles.get('gdpr', 999); console.log(article.title); } catch (err) { if (err instanceof NotFoundError) { console.error(`Not found (${err.statusCode}): ${err.message}`); } else if (err instanceof ValidationError) { console.error(`Invalid request (${err.statusCode}): ${err.message}`); } else if (err instanceof RateLimitError) { console.error(`Rate limited (${err.statusCode}) — all retries exhausted`); } else if (err instanceof ServerError) { console.error(`Server error (${err.statusCode}) — all retries exhausted`); } else if (err instanceof Law4DevsError) { console.error(`API error (${err.statusCode}): ${err.message}`); } else { throw err; // network error or unexpected — re-throw } } ``` *** ## Auto-retry behaviour The SDK automatically retries on `RateLimitError` (429) and `ServerError` (5xx) using exponential backoff with jitter. Only after all retries are exhausted is the error thrown to your code. ```typescript theme={null} // Configure retry behaviour at construction time const client = new Law4DevsClient({ maxRetries: 3, // default: 3 — set to 0 to disable retries timeout: 30000, // ms — each individual attempt has this timeout }); ``` Timeouts are enforced via `AbortController`, so they apply per-attempt (not across all retries combined). `NotFoundError` and `ValidationError` are never retried — they indicate client-side issues that will not resolve on retry. *** ## TypeScript narrowing with a typed helper Define a `safeGet` helper to return a typed result or a structured error without propagating exceptions: ```typescript theme={null} import { Law4DevsClient, NotFoundError, Law4DevsError, Article } from 'law4devs'; type Result = | { ok: true; value: T } | { ok: false; error: Law4DevsError }; async function safeGet(fn: () => Promise): Promise> { try { return { ok: true, value: await fn() }; } catch (err) { if (err instanceof Law4DevsError) { return { ok: false, error: err }; } throw err; // unexpected — re-throw } } // Usage const result = await safeGet(() => client.articles.get('gdpr', 5)); if (result.ok) { console.log(result.value.title); // TypeScript knows result.value is Article } else { if (result.error instanceof NotFoundError) { console.log('Article does not exist'); } else { console.error(`Unexpected error: ${result.error.message}`); } } ``` *** ## Handling errors inside `for await` Errors thrown during `iter()` or `iterDeadlines()` are propagated out of the `for await` loop. Wrap the loop in try/catch to handle them: ```typescript theme={null} try { for await (const article of client.articles.iter('gdpr')) { console.log(article.title); } } catch (err) { if (err instanceof RateLimitError) { console.error('Rate limit hit during iteration — consider reducing perPage or adding delays'); } else { throw err; } } ``` *** ## Error reference | Class | HTTP Status | When thrown | Retried? | | ----------------- | ----------- | --------------------------------- | -------- | | `NotFoundError` | 404 | Requested resource does not exist | No | | `ValidationError` | 422 | Invalid parameters or query | No | | `RateLimitError` | 429 | Too many requests | Yes | | `ServerError` | 5xx | Server-side error | Yes | | `Law4DevsError` | any | Base class — catch-all | Depends | After all retries are exhausted, `RateLimitError` and `ServerError` are thrown normally. Always include these in your catch blocks for production code. # Frameworks Source: https://docs.law4devs.eu/sdks/typescript/frameworks List, retrieve, and iterate EU regulatory frameworks with the Law4Devs TypeScript SDK. ## Overview Frameworks are the top-level objects in Law4Devs. Each framework represents one EU regulatory act (e.g. GDPR, NIS2, CRA) and acts as a namespace for articles, recitals, requirements, and annexes. *** ## Methods ### `list()` Returns a page of framework summaries. ```typescript theme={null} const page = await client.frameworks.list({ page: 1, perPage: 20 }); console.log(`${page.meta.total} frameworks across ${page.meta.pages} pages`); for (const fw of page.data) { console.log(`${fw.slug}: ${fw.name} (${fw.articleCount} articles)`); } ``` **Parameters** | Parameter | Type | Default | Description | | --------- | -------- | ------- | ---------------------- | | `page` | `number` | `1` | Page number | | `perPage` | `number` | `20` | Items per page (1–100) | **Returns** `Promise>` *** ### `get()` Fetch the full detail of a single framework by its slug. ```typescript theme={null} const gdpr = await client.frameworks.get('gdpr'); console.log(gdpr.name); console.log(`Coverage: ${gdpr.coverage}%`); console.log(`EUR-Lex: ${gdpr.eurLexUrl}`); ``` **Parameters** | Parameter | Type | Description | | --------- | -------- | ------------------------------------------------ | | `slug` | `string` | Framework slug, e.g. `"gdpr"`, `"nis2"`, `"cra"` | **Returns** `Promise` *** ### `iter()` Iterate over all frameworks without managing pagination. ```typescript theme={null} for await (const fw of client.frameworks.iter({ perPage: 50 })) { console.log(`${fw.shortName} — last synced: ${fw.lastSyncedAt}`); } ``` **Parameters** — same options as `list()` (except `page`, which is managed internally). **Returns** `AsyncGenerator` *** ## Models ### `Framework` Returned by `list()` and `iter()`. | Field | Type | Description | | ------------------ | ---------------- | -------------------------------------------- | | `id` | `number` | Internal numeric ID | | `slug` | `string` | URL-safe identifier, e.g. `"gdpr"` | | `name` | `string` | Full official name | | `shortName` | `string` | Short name, e.g. `"GDPR"` | | `celexNumber` | `string \| null` | EUR-Lex CELEX identifier | | `description` | `string \| null` | Plain-text summary | | `isActive` | `boolean` | Whether the framework is published | | `status` | `string` | `"active"`, `"superseded"`, or `"draft"` | | `expectedArticles` | `number \| null` | Target article count for sync progress | | `expectedRecitals` | `number \| null` | Target recital count for sync progress | | `lastSyncedAt` | `string \| null` | ISO 8601 timestamp of last content sync | | `createdAt` | `string` | ISO 8601 creation timestamp | | `articleCount` | `number` | Number of articles currently in the database | | `recitalCount` | `number` | Number of recitals currently in the database | ### `FrameworkDetail` Returned by `get()`. Extends `Framework` with: | Field | Type | Description | | ------------------ | ---------------- | -------------------------------------- | | `eurLexUrl` | `string \| null` | Direct link to EUR-Lex source document | | `requirementCount` | `number` | Number of extracted requirements | | `annexCount` | `number` | Number of annexes | | `tagCount` | `number` | Number of tags applied | | `coverage` | `number` | Sync coverage percentage (0–100) | *** ## Example: filter active frameworks ```typescript theme={null} const activeFrameworks: string[] = []; for await (const fw of client.frameworks.iter()) { if (fw.isActive && fw.status === 'active') { activeFrameworks.push(fw.slug); } } console.log('Active frameworks:', activeFrameworks.join(', ')); ``` NIS1 has `status: "superseded"` and remains browsable for historical reference. Filter by `status === "active"` if you only want current legislation. # Installation Source: https://docs.law4devs.eu/sdks/typescript/installation Install the Law4Devs TypeScript SDK and make your first API call. ## Requirements * **Node.js 18+** (uses the native `fetch` API — no polyfill needed) * npm, yarn, or pnpm The SDK uses the global `fetch` available in Node 18 and all modern browsers. No additional HTTP dependencies are required. *** ## Install ```bash theme={null} npm install law4devs ``` ```bash theme={null} yarn add law4devs ``` ```bash theme={null} pnpm add law4devs ``` *** ## First Call ```typescript theme={null} import { Law4DevsClient } from 'law4devs'; const client = new Law4DevsClient({ apiKey: process.env.LAW4DEVS_API_KEY!, }); const page = await client.frameworks.list(); console.log(`Found ${page.meta.total} EU regulatory frameworks`); for (const fw of page.data) { console.log(`${fw.shortName}: ${fw.name}`); } ``` *** ## Constructor Options ```typescript theme={null} const client = new Law4DevsClient({ apiKey: process.env.LAW4DEVS_API_KEY!, baseUrl: 'https://api.law4devs.eu/v1', // default timeout: 30_000, // ms before request is aborted (default: 30000) maxRetries: 3, // retries on 429/5xx (default: 3) }); ``` | Option | Type | Default | Description | | ------------ | -------- | -------------- | ------------------------------- | | `apiKey` | `string` | — | **Required.** Your API key | | `baseUrl` | `string` | Production URL | Override for dev environment | | `timeout` | `number` | `30000` | Request timeout in milliseconds | | `maxRetries` | `number` | `3` | Retries on 429/5xx | Never hardcode your API key in source code or commit it to version control. Always use environment variables. Retries use exponential backoff. Timeouts are enforced via `AbortController` and work in both Node.js and browsers. # Pagination Source: https://docs.law4devs.eu/sdks/typescript/pagination Manual pagination and auto-pagination with AsyncGenerator in the Law4Devs TypeScript SDK. ## The `Page` type Every `list()` method returns a `Promise>`. The `Page` object has three properties: ```typescript theme={null} interface Page { data: T[]; // items on the current page meta: PageMeta; // totals and current position links: PageLinks; // pre-built URLs for adjacent pages } interface PageMeta { total: number; // total items across all pages page: number; // current page number (1-based) perPage: number; // items per page pages: number; // total number of pages apiVersion: string; // API version string } interface PageLinks { next: string | null; // URL of the next page, or null on the last page prev: string | null; // URL of the previous page, or null on the first page } ``` *** ## Manual pagination Use `links.next` to detect when there are more pages and increment `page` manually. ```typescript theme={null} let page = 1; let total = 0; while (true) { const result = await client.frameworks.list({ page, perPage: 10 }); total += result.data.length; console.log(`Page ${result.meta.page}/${result.meta.pages}: ${result.data.length} items`); if (!result.links.next) break; // no more pages page++; } console.log(`Fetched ${total} frameworks in total`); ``` *** ## Auto-pagination with `for await` Every resource exposes an `iter()` method (or `iterDeadlines()` for compliance) that returns an `AsyncGenerator`. Pages are fetched lazily — only when the current batch is exhausted. ```typescript theme={null} // Frameworks for await (const fw of client.frameworks.iter()) { console.log(fw.slug); } // Articles scoped to a framework for await (const article of client.articles.iter('gdpr')) { console.log(`Art. ${article.articleNumber}: ${article.title}`); } // Compliance deadlines for await (const deadline of client.compliance.iterDeadlines({ frameworkSlug: 'nis2' })) { console.log(deadline.description); } ``` TypeScript infers the correct item type from the generator signature: ```typescript theme={null} // article is typed as ArticleSummary — no cast needed for await (const article of client.articles.iter('cra')) { const tags: Tag[] = article.tags; // fully typed } ``` *** ## Early exit Because `iter()` is a lazy `AsyncGenerator`, breaking out of the loop stops further fetches immediately. ```typescript theme={null} // Find the first article tagged 'security' — stop as soon as found let found: ArticleSummary | undefined; for await (const article of client.articles.iter('nis2')) { if (article.tags.some(t => t.slug === 'security')) { found = article; break; // no further pages are fetched } } if (found) { console.log(`First security article: Art. ${found.articleNumber}`); } ``` *** ## Controlling page size Pass `perPage` to `iter()` or `list()` to tune the number of items fetched per HTTP request. Valid range is 1–100; default is 20. ```typescript theme={null} // Fewer, larger pages — fewer HTTP requests for await (const req of client.requirements.iter({ frameworkSlug: 'gdpr', perPage: 100, })) { // process requirement } ``` For large datasets, a higher `perPage` value reduces the number of HTTP round-trips. For interactive UIs where you want to display results quickly, a smaller `perPage` value returns the first items faster. *** ## Collecting all results into an array ```typescript theme={null} async function collectAll(gen: AsyncGenerator): Promise { const items: T[] = []; for await (const item of gen) { items.push(item); } return items; } const allFrameworks = await collectAll(client.frameworks.iter()); console.log(`${allFrameworks.length} total frameworks`); ``` Collecting all results into memory with a helper like `collectAll` is convenient but be mindful of response size. For large resources such as requirements or articles across all frameworks, stream and process items one-by-one inside the `for await` loop instead. # Quick Start Source: https://docs.law4devs.eu/sdks/typescript/quickstart Practical examples to get up and running with the Law4Devs TypeScript SDK. ## Setup ```typescript theme={null} import { Law4DevsClient } from 'law4devs'; const client = new Law4DevsClient(); ``` *** ## 1. List all frameworks ```typescript theme={null} const page = await client.frameworks.list({ perPage: 50 }); console.log(`${page.meta.total} frameworks available`); for (const fw of page.data) { const label = fw.status === 'active' ? fw.shortName : `${fw.shortName} (${fw.status})`; console.log(`${label}: ${fw.name}`); } ``` *** ## 2. Iterate all CRA articles Use `for await` to walk through every article without managing pagination manually. ```typescript theme={null} let count = 0; for await (const article of client.articles.iter('cra')) { console.log(`Art. ${article.articleNumber}: ${article.title}`); count++; } console.log(`Iterated ${count} CRA articles`); ``` `iter()` fetches pages lazily. Break out of the loop early with `break` as soon as you have what you need — subsequent pages are never fetched. *** ## 3. Search with a framework filter ```typescript theme={null} const results = await client.search.query('security requirements', { framework: 'nis2', resultType: 'article', perPage: 10, }); for (const result of results.data) { console.log(`[${result.type}] Art. ${result.articleNumber}: ${result.title}`); console.log(` ...${result.matchContext}...`); } ``` *** ## 4. Get compliance deadlines with `iterDeadlines()` ```typescript theme={null} for await (const deadline of client.compliance.iterDeadlines({ frameworkSlug: 'nis2' })) { const date = deadline.deadlineDate ?? 'ongoing'; console.log(`${deadline.deadlineType} — ${date}: ${deadline.description}`); } ``` The compliance resource uses `iterDeadlines()` rather than `iter()` because it operates across multiple frameworks rather than within a single one. *** ## 5. Error handling ```typescript theme={null} import { Law4DevsClient, NotFoundError, RateLimitError } from 'law4devs'; const client = new Law4DevsClient({ maxRetries: 3 }); try { const article = await client.articles.get('gdpr', 999); console.log(article.title); } catch (err) { if (err instanceof NotFoundError) { console.error(`Article not found (HTTP ${err.statusCode})`); } else if (err instanceof RateLimitError) { console.error('Rate limit hit — the SDK retries automatically, but all retries were exhausted'); } else { throw err; // re-throw unexpected errors } } ``` *** ## Next Steps # Recitals Source: https://docs.law4devs.eu/sdks/typescript/recitals List, retrieve, and iterate framework recitals with the Law4Devs TypeScript SDK. ## Overview Recitals are the numbered "whereas" clauses that precede the operative articles of an EU regulation. They provide legislative context and interpretation guidance but are not themselves legally binding obligations. Each recital belongs to one framework. *** ## Methods ### `list()` Returns a page of recital summaries for a framework. ```typescript theme={null} const page = await client.recitals.list('gdpr', { page: 1, perPage: 20 }); console.log(`GDPR has ${page.meta.total} recitals`); for (const recital of page.data) { const preview = recital.content.slice(0, 80); console.log(`Recital ${recital.recitalNumber}: ${preview}...`); } ``` **Parameters** | Parameter | Type | Default | Description | | --------------- | -------- | -------- | ----------------------------- | | `frameworkSlug` | `string` | required | Framework slug, e.g. `"gdpr"` | | `page` | `number` | `1` | Page number | | `perPage` | `number` | `20` | Items per page (1–100) | **Returns** `Promise>` *** ### `get()` Fetch a single recital by its number. ```typescript theme={null} const recital = await client.recitals.get('gdpr', 1); console.log(`Recital ${recital.recitalNumber}`); console.log(recital.content); ``` **Parameters** | Parameter | Type | Description | | --------------- | -------- | -------------- | | `frameworkSlug` | `string` | Framework slug | | `recitalNumber` | `number` | Recital number | **Returns** `Promise` *** ### `iter()` Iterate over all recitals in a framework. ```typescript theme={null} for await (const recital of client.recitals.iter('nis2')) { console.log(`Recital ${recital.recitalNumber} (position ${recital.position})`); } ``` **Parameters** — same options as `list()` (except `page`, which is managed internally). **Returns** `AsyncGenerator` *** ## Model ### `Recital` | Field | Type | Description | | --------------- | -------- | ---------------------------------- | | `id` | `number` | Internal numeric ID | | `frameworkSlug` | `string` | Parent framework slug | | `recitalNumber` | `number` | Recital number | | `content` | `string` | Full recital text | | `position` | `number` | Display order within the framework | *** ## Example: extract recitals containing a keyword ```typescript theme={null} const keyword = 'legitimate interest'; const matches: number[] = []; for await (const recital of client.recitals.iter('gdpr')) { if (recital.content.toLowerCase().includes(keyword)) { matches.push(recital.recitalNumber); } } console.log(`Recitals mentioning "${keyword}":`, matches.join(', ')); ``` For more structured full-text search across recitals and articles, use `client.search.query()` with `resultType: 'recital'`. # Requirements Source: https://docs.law4devs.eu/sdks/typescript/requirements List and iterate extracted compliance requirements with the Law4Devs TypeScript SDK. ## Overview Requirements are structured compliance obligations extracted from regulation articles. Each requirement is linked back to its source article and paragraph, categorised by type, and optionally includes a compliance deadline and stakeholder roles. *** ## Methods ### `list()` Returns a page of requirements, optionally filtered to a single framework. ```typescript theme={null} // All requirements across all frameworks const allPage = await client.requirements.list(); // Requirements for a specific framework const gdprPage = await client.requirements.list({ frameworkSlug: 'gdpr' }); console.log(`GDPR has ${gdprPage.meta.total} requirements`); for (const req of gdprPage.data) { console.log(`Art. ${req.articleNumber} [${req.requirementType}]: ${req.requirementText.slice(0, 80)}...`); } ``` **Parameters** | Parameter | Type | Default | Description | | --------------- | -------- | ------- | ------------------------------ | | `frameworkSlug` | `string` | — | Filter to a specific framework | | `page` | `number` | `1` | Page number | | `perPage` | `number` | `20` | Items per page (1–100) | **Returns** `Promise>` *** ### `iter()` Iterate over requirements, optionally filtered to a framework. ```typescript theme={null} for await (const req of client.requirements.iter({ frameworkSlug: 'cra' })) { console.log(`[${req.requirementType}] Art. ${req.articleNumber}: ${req.requirementText.slice(0, 60)}...`); } ``` **Parameters** — same options as `list()` (except `page`, which is managed internally). **Returns** `AsyncGenerator` *** ## Model ### `Requirement` | Field | Type | Description | | ---------------------- | ---------------- | --------------------------------------------------------------------- | | `id` | `number` | Internal numeric ID | | `frameworkSlug` | `string` | Parent framework slug | | `articleNumber` | `number` | Source article number | | `paragraphRef` | `string \| null` | Source paragraph reference, e.g. `"1"`, `"2a"` | | `paragraphContent` | `string \| null` | Full text of the source paragraph | | `requirementText` | `string` | Extracted requirement obligation text | | `requirementType` | `string` | Category, e.g. `"obligation"`, `"prohibition"`, `"right"` | | `complianceDeadline` | `string \| null` | ISO 8601 deadline date, if applicable | | `linkedArticleNumbers` | `number[]` | Related article numbers | | `stakeholderRoles` | `string[]` | Roles this requirement applies to, e.g. `["controller", "processor"]` | | `tags` | `Tag[]` | Tags applied to the requirement | | `createdAt` | `string` | ISO 8601 creation timestamp | *** ## Example: build a compliance checklist ```typescript theme={null} interface ChecklistItem { id: number; article: string; type: string; text: string; deadline: string | null; roles: string[]; } async function buildChecklist(frameworkSlug: string): Promise { const checklist: ChecklistItem[] = []; for await (const req of client.requirements.iter({ frameworkSlug })) { checklist.push({ id: req.id, article: `Art. ${req.articleNumber}${req.paragraphRef ? `(${req.paragraphRef})` : ''}`, type: req.requirementType, text: req.requirementText, deadline: req.complianceDeadline, roles: req.stakeholderRoles, }); } // Sort obligations before other types checklist.sort((a, b) => { if (a.type === 'obligation' && b.type !== 'obligation') return -1; if (b.type === 'obligation' && a.type !== 'obligation') return 1; return 0; }); return checklist; } const craChecklist = await buildChecklist('cra'); console.log(`CRA compliance checklist: ${craChecklist.length} items`); ``` Requirements are extracted from article paragraphs through automated analysis. Cross-check against the source article text — accessible via `client.articles.get()` — for authoritative wording. # Search Source: https://docs.law4devs.eu/sdks/typescript/search Full-text search across all frameworks with the Law4Devs TypeScript SDK. ## Overview The search resource provides full-text search across all framework content — articles, recitals, and requirements. Results include a `matchContext` snippet showing the matched text in context. *** ## Methods ### `query()` Execute a full-text search and return a page of results. ```typescript theme={null} const results = await client.search.query('data breach notification', { framework: 'gdpr', resultType: 'article', page: 1, perPage: 20, }); console.log(`Found ${results.meta.total} matching articles`); for (const result of results.data) { console.log(`[${result.type}] ${result.frameworkName} Art. ${result.articleNumber}: ${result.title}`); console.log(` ...${result.matchContext}...`); console.log(` ${result.url}`); } ``` **Parameters** | Parameter | Type | Default | Description | | ------------ | -------- | -------- | -------------------------------------------------------------------- | | `query` | `string` | required | Search terms | | `framework` | `string` | — | Filter results to a specific framework slug | | `resultType` | `string` | — | Filter by content type: `"article"`, `"recital"`, or `"requirement"` | | `page` | `number` | `1` | Page number | | `perPage` | `number` | `20` | Items per page (1–100) | **Returns** `Promise>` `meta.pages` is always `0` for search results. This is an endpoint limitation — use `meta.total` and `links.next` to determine if more results exist. *** ### `iter()` Iterate over all search results without managing pagination. ```typescript theme={null} for await (const result of client.search.iter('security requirements')) { console.log(`[${result.type}] ${result.frameworkName} — ${result.title ?? result.requirementType}`); } ``` **Parameters** — same options as `query()` (except `page`, which is managed internally). **Returns** `AsyncGenerator` *** ## Model ### `SearchResult` | Field | Type | Description | | ----------------- | ---------------- | --------------------------------------------------------- | | `type` | `string` | Result type: `"article"`, `"recital"`, or `"requirement"` | | `frameworkSlug` | `string` | Parent framework slug | | `frameworkName` | `string` | Parent framework display name | | `articleNumber` | `number \| null` | Article number (for article and requirement results) | | `recitalNumber` | `number \| null` | Recital number (for recital results) | | `paragraphRef` | `string \| null` | Source paragraph reference | | `title` | `string \| null` | Article title (for article results) | | `requirementType` | `string \| null` | Requirement type (for requirement results) | | `matchContext` | `string` | Snippet of text surrounding the match | | `url` | `string` | Deep link to the matched item in the API | *** ## Examples ### Search for a concept across all frameworks ```typescript theme={null} const results = await client.search.query('legitimate interest'); for (const result of results.data) { const ref = result.articleNumber ? `Art. ${result.articleNumber}` : `Recital ${result.recitalNumber}`; console.log(`${result.frameworkName} ${ref}: ...${result.matchContext}...`); } ``` ### Collect all requirement results mentioning a term ```typescript theme={null} const requirementMatches: SearchResult[] = []; for await (const result of client.search.iter('incident response', { resultType: 'requirement', })) { requirementMatches.push(result); } console.log(`Found ${requirementMatches.length} matching requirements`); ``` Combine `resultType: 'article'` with `iter()` for a simple way to find every article across all frameworks that mentions a specific term — no need to iterate frameworks first. # Tags Source: https://docs.law4devs.eu/sdks/typescript/tags List, retrieve, and iterate taxonomy tags with the Law4Devs TypeScript SDK. ## Overview Tags are the Law4Devs taxonomy — a curated set of labels applied to articles and requirements across all frameworks. Using tags you can find related content spanning multiple regulations (e.g. all articles tagged `"data-breach"` across GDPR, NIS2, and CRA). *** ## Methods ### `list()` Returns a page of tags. ```typescript theme={null} const page = await client.tags.list({ perPage: 50 }); console.log(`${page.meta.total} tags in the taxonomy`); for (const tag of page.data) { console.log(`${tag.slug}: ${tag.name}`); } ``` **Parameters** | Parameter | Type | Default | Description | | --------- | -------- | ------- | ---------------------- | | `page` | `number` | `1` | Page number | | `perPage` | `number` | `20` | Items per page (1–100) | **Returns** `Promise>` *** ### `get()` Fetch a single tag by its slug. ```typescript theme={null} const tag = await client.tags.get('data-breach'); console.log(tag.name); console.log(tag.description); console.log('Keywords:', tag.keywords.join(', ')); ``` **Parameters** | Parameter | Type | Description | | --------- | -------- | -------------------------------------------- | | `slug` | `string` | Tag slug, e.g. `"data-breach"`, `"security"` | **Returns** `Promise` *** ### `iter()` Iterate over all tags. ```typescript theme={null} for await (const tag of client.tags.iter()) { console.log(`#${tag.slug} — ${tag.name} (color: ${tag.color ?? 'none'})`); } ``` **Parameters** — same options as `list()` (except `page`, which is managed internally). **Returns** `AsyncGenerator` *** ## Model ### `Tag` | Field | Type | Description | | ------------- | ---------------- | ---------------------------------------------- | | `id` | `number` | Internal numeric ID | | `slug` | `string` | URL-safe identifier, e.g. `"data-breach"` | | `name` | `string` | Display name, e.g. `"Data Breach"` | | `description` | `string \| null` | Plain-text description of what this tag covers | | `keywords` | `string[]` | Keywords used for auto-tagging | | `color` | `string \| null` | Hex color for UI display, e.g. `"#e53e3e"` | | `createdAt` | `string` | ISO 8601 creation timestamp | *** ## Example: build a tag index ```typescript theme={null} const tagIndex = new Map(); for await (const tag of client.tags.iter()) { tagIndex.set(tag.slug, { name: tag.name, keywords: tag.keywords, }); } console.log(`Indexed ${tagIndex.size} tags`); console.log(tagIndex.get('security')); ``` # Authentication Source: https://docs.law4devs.eu/security/authentication All Law4Devs API endpoints require an API key. ## API Key Required Every request to the Law4Devs API must include a valid API key. Pass it via the `X-API-Key` header: ```bash theme={null} curl -H "X-API-Key: YOUR_API_KEY" \ https://api.law4devs.eu/v1/frameworks ``` ```python theme={null} from law4devs import Law4DevsClient client = Law4DevsClient(api_key="YOUR_API_KEY") ``` ```typescript theme={null} import { Law4DevsClient } from 'law4devs'; const client = new Law4DevsClient({ apiKey: 'YOUR_API_KEY' }); ``` ```php theme={null} use Law4Devs\Client; $client = new Client(apiKey: 'YOUR_API_KEY'); ``` Never hardcode your API key in source code. Use environment variables. ## Environment Variables ```python theme={null} import os from law4devs import Law4DevsClient client = Law4DevsClient(api_key=os.environ["LAW4DEVS_API_KEY"]) ``` ```typescript theme={null} import { Law4DevsClient } from 'law4devs'; const client = new Law4DevsClient({ apiKey: process.env.LAW4DEVS_API_KEY!, }); ``` ```php theme={null} use Law4Devs\Client; $client = new Client(apiKey: getenv('LAW4DEVS_API_KEY')); ``` Get your API key at [law4devs.eu](https://law4devs.eu). # CORS Policy Source: https://docs.law4devs.eu/security/cors-policy Cross-origin request configuration for browser-based integrations Law4Devs supports cross-origin requests so you can call the API directly from browser applications. CORS is configured at the infrastructure level — no extra setup is needed for most use cases. ## Allowed Configuration | Property | Value | | ------------------- | -------------------------------------------- | | **Allowed methods** | `GET`, `OPTIONS` | | **Allowed origins** | `*` | | **Allowed headers** | `Content-Type`, `Authorization`, `X-API-Key` | | **Preflight cache** | 3600 seconds (1 hour) | The API is read-only — only `GET` requests are permitted. `OPTIONS` is allowed for CORS preflight checks. ## Browser Example ```typescript theme={null} const response = await fetch( 'https://api.law4devs.eu/v1/frameworks', { headers: { 'X-API-Key': import.meta.env.VITE_LAW4DEVS_API_KEY, }, } ); const data = await response.json(); console.log(data.data); ``` Never expose your API key in a browser application's source code or public repository. Use environment variables and server-side proxying for production browser integrations. # Rate Limits Source: https://docs.law4devs.eu/security/rate-limits Per-plan request rate limits for the Law4Devs API. ## Rate Limits by Plan | Plan | Req/min | Req/day | Monthly cap | Max per\_page | | ------ | ------- | ------- | ----------- | ------------- | | Growth | 10 | 500 | 10,000 | 10 | | Pro | 30 | 5,000 | 100,000 | 25 | | Scale | 100 | 50,000 | Unlimited | 50 | | Custom | Custom | Custom | Unlimited | 100 | When you exceed the rate limit, the API returns **HTTP 429**: ```json theme={null} { "error": { "code": "RATE_LIMIT_EXCEEDED", "message": "Too many requests." } } ``` All SDKs automatically retry on 429 with exponential backoff (up to 3 retries by default). You can control this with `max_retries` / `maxRetries`: ```python theme={null} client = Law4DevsClient( api_key=os.environ["LAW4DEVS_API_KEY"], max_retries=5, ) ``` ```typescript theme={null} const client = new Law4DevsClient({ apiKey: process.env.LAW4DEVS_API_KEY!, maxRetries: 5, }); ``` ```php theme={null} $client = new Client( apiKey: getenv('LAW4DEVS_API_KEY'), maxRetries: 5, ); ``` ```rust theme={null} let client = Law4DevsClient::builder() .api_key(std::env::var("LAW4DEVS_API_KEY").unwrap()) .max_retries(5) .build(); ``` ## Plan Comparison | Plan | Monthly | Annual | Req/min | Req/day | Monthly cap | Max per\_page | API Keys | Support | Uptime SLA | | ---------- | ---------- | ------ | ------- | ------- | ----------- | ------------- | --------- | --------------------------------- | ---------- | | **Growth** | €29 | €290 | 10 | 500 | 10,000 | 10 | 10 | Email (48h) | 99.5% | | **Pro** | €89 | €890 | 30 | 5,000 | 100,000 | 25 | 50 | Priority email + Slack (24h) | 99.9% | | **Scale** | €229 | €2,290 | 100 | 50,000 | Unlimited | 50 | 100 | Dedicated Slack + account manager | 99.9% | | **Custom** | Contact us | — | Custom | Custom | Unlimited | 100 | Unlimited | Dedicated Slack + account manager | 99.99% | Upgrade your plan at [law4devs.eu](https://law4devs.eu). # Security Headers Source: https://docs.law4devs.eu/security/security-headers HTTP response headers Law4Devs sets on every API response Every response from the Law4Devs API includes a set of security headers designed to protect both the API and its consumers. These headers are set automatically — no configuration required on your end. ## Response Headers | Header | Value | Purpose | | ------------------------ | --------------------------------- | ------------------------------------------------------------------- | | `X-Content-Type-Options` | `nosniff` | Prevents MIME-type sniffing attacks in browsers | | `X-Frame-Options` | `DENY` | Prevents the API response from being embedded in iframes | | `X-XSS-Protection` | `1; mode=block` | Legacy XSS filter for older browsers | | `Referrer-Policy` | `strict-origin-when-cross-origin` | Controls how much referrer information is included | | `X-API-Version` | `1.0` | Signals the current API version on every response | | `X-Request-ID` | UUID (per-request) | Unique identifier for each request — use this when reporting issues | The `Server` header is stripped from all responses to avoid fingerprinting the underlying infrastructure. ## Using `X-Request-ID` Every response carries a unique `X-Request-ID` header. Log this value alongside your application errors — it lets the Law4Devs team trace a specific request end-to-end when you report an issue. ```bash theme={null} curl -v -H "X-API-Key: YOUR_API_KEY" \ https://api.law4devs.eu/v1/frameworks 2>&1 | grep X-Request-ID # < X-Request-ID: 4f2a1c8e-9b3d-47e2-a1f0-8c5d6e7b9a2c ``` ## Using `X-API-Version` The `X-API-Version` header is included in every response so your application can detect version changes without parsing the URL. ```python theme={null} import requests response = requests.get( "https://api.law4devs.eu/v1/frameworks", headers={"X-API-Key": "YOUR_API_KEY"}, ) api_version = response.headers.get("X-API-Version") print(f"API version: {api_version}") # API version: 1.0 ```