{"version":"1","schema_version":"1.1.0","@context":"https://schema.org","@graph":[{"@type":"HowTo","@id":"https://www.apier.no/workflows.json#workflow.lookup-deadlines","name":"Look up Norwegian business deadlines for a year","description":"Fetch the canonical deadline list (MVA terminer, A-melding, skattemelding, årsregnskap, Foretaksregisteret årsbekreftelse) for a calendar year. Deadlines are pre-adjusted through weekends and Norwegian public holidays, with the original legal deadline surfaced in `adjusted_from` when relevant.","totalTime":"PT1S","step":[{"@type":"HowToStep","position":1,"name":"Call GET /api/v1/public/deadlines?year=YYYY and iterate the returned mappings sorted ascending by deadline.","text":"JSON envelope with data.year, data.deadlines[] (sorted ascending), and _meta.served_from='static'."}]},{"@type":"HowTo","@id":"https://www.apier.no/workflows.json#workflow.check-company-status","name":"Check a Norwegian company's current Brønnøysund status","description":"Fetch the current Tier 1 + (when delegation exists) Tier 2 view of a Norwegian company by org number. `data.data_tier` tells you whether Tier 2 was served; `_meta.served_from` distinguishes cache from live.","totalTime":"PT1S","step":[{"@type":"HowToStep","position":1,"name":"Call GET /api/v1/company/{org}/context with the consumer API key; branch on data.data_tier.","text":"JSON envelope with data.company (tier-1 fields), optional data.tier_2 block, data.data_tier, and _meta with served_from + stale markers."}]},{"@type":"HowTo","@id":"https://www.apier.no/workflows.json#workflow.exchange-rate-lookup","name":"Look up the official NOK exchange rate for a date","description":"Fetch the Norges Bank NOK rate for a currency and date. Weekend/holiday/pre-publication-today requests fall back to the most recent prior business day; `data.date` is the actual publication date and `data.requested_date` is what was asked for.","totalTime":"PT1S","step":[{"@type":"HowToStep","position":1,"name":"Call GET /api/v1/tools/exchange-rate?currency=<ISO>&date=<YYYY-MM-DD>.","text":"JSON envelope with data.rate (number), data.date, data.requested_date, data.cached, and _meta.served_from (cache/live)."}]},{"@type":"HowTo","@id":"https://www.apier.no/workflows.json#workflow.altinn-role-migration","name":"Translate an Altinn 2 code to the Altinn 3 equivalent","description":"Translate an Altinn 2 service or role code to its Altinn 3 equivalent. The 19 June 2026 Altinn 2 deprecation deadline has passed, so this identifies what a legacy code maps to for migration remediation. Gate production migration actions on `verified === true`; unverified entries are convenience references only.","totalTime":"PT1S","step":[{"@type":"HowToStep","position":1,"name":"Call GET /api/v1/tools/altinn-migration?altinn2_code=<CODE>; inspect `verified` before trusting the mapping for production automation.","text":"JSON envelope with data.entry (MigrationEntry), data.deprecation_deadline, data.days_remaining, data.deadline_passed, and _meta.served_from='static'."}]},{"@type":"HowTo","@id":"https://www.apier.no/workflows.json#workflow.obligation-overview","name":"List generic obligations for a Norwegian entity type","description":"Fetch the universal obligation set for a given entity type (AS, ENK, ANS, DA, NUF). Template layer only — `tier_2_required: true` entries need commercial data to evaluate per-company, which requires an Altinn delegation.","totalTime":"PT1S","step":[{"@type":"HowToStep","position":1,"name":"Call GET /api/v1/public/obligations?entity_type=<AS|ENK|ANS|DA|NUF>. Use the `tier_2_required` flag to decide whether to plan a delegation flow next.","text":"JSON envelope with data.entity_type, data.obligations[] (may be empty for ANS/DA/NUF), data.notes[], and _meta.served_from='static'."}]},{"@type":"HowTo","@id":"https://www.apier.no/workflows.json#workflow.reconcile-filing-history","name":"Reconcile a customer's filing history from the audit trail","description":"Pull the consumer's own audit trail (Sporingslogg) for a single org_number across a date range, paging via the opaque cursor (pagination.next_cursor) until exhausted. Useful for accounting firms reconciling activity after the fact, AI-agent operators debugging filing flows, and producing a forensic timeline for dispute defense. Returns ONLY rows YOUR consumer wrote — cross-consumer visibility is structurally impossible.","totalTime":"PT1S","step":[{"@type":"HowToStep","position":1,"name":"Call GET /api/v1/company/{org}/audit with the consumer API key (read:audit scope). Iterate the page; if pagination.has_more, repeat with ?cursor=pagination.next_cursor.","text":"JSON envelope with data.data[] (AuditLogEntry array), data.pagination.has_more, data.pagination.next_cursor (string or absent), the legacy data.pagination.next_before / next_before_id fields, and _meta with response_timestamp + response_hash."}]},{"@type":"HowTo","@id":"https://www.apier.no/workflows.json#workflow.track-changes","name":"Track historical changes for a customer company","description":"Pull all change events for a specific org_number from the change archive (Brreg ingestion + Altinn / DigDir / Norges Bank multi-source). Cursor-paginated; pass next_cursor back as ?cursor= for the next page.","totalTime":"PT1S","step":[{"@type":"HowToStep","position":1,"name":"Call GET /api/v1/changes?source=brreg&entity_id={org_number}&from={iso_date} with the consumer API key (read:changes scope). Iterate the page; if pagination.has_more, repeat with ?cursor=pagination.next_cursor.","text":"JSON envelope with data.data[] (PublicChangeRow array), data.pagination.has_more, data.pagination.next_cursor (string or null), and _meta with response_timestamp + response_hash."}]},{"@type":"HowTo","@id":"https://www.apier.no/workflows.json#workflow.validate-filing","name":"Validate a filing before submission","description":"Run the five preflight checks the dry-run engine performs against a filing payload — company exists, system user authorised, scopes delegated, payload format valid, deadline in future — without submitting anything to Altinn / Skatteetaten / NAV. Use this before any human-approval flow so the agent can present a complete pass/fail picture rather than discovering problems mid-flight.","totalTime":"PT1S","step":[{"@type":"HowToStep","position":1,"name":"Call POST /api/v1/actions/execute?dry_run=true with { org_number, action_type, period, payload }. The endpoint performs all five preflight checks, writes one evaluation_snapshots row + one audit_log row, and returns 200 with the structured DryRunOutcome.","text":"JSON envelope with data.dry_run === true, data.outcome.all_passed (boolean), data.outcome.checks (5 entries, ordered), data.outcome.disclaimer (fixed string), and _meta with response_timestamp + response_hash."}]},{"@type":"HowTo","@id":"https://www.apier.no/workflows.json#workflow.kyb-client-onboarding","name":"Onboard a Norwegian client company from a single org number (KYB)","description":"Composed five-step KYB (know-your-business) onboarding chain over the live company reads: verify the company exists and is safe to act for, resolve who can legally sign for it, read its filed annual accounts, evaluate its regulatory obligations, and expand its concrete deadline calendar. Every step's response carries `_meta.response_hash` and lands a provenance_log row, so the assembled onboarding answer is provenance-backed end-to-end; the verify and authority steps optionally return an offline-verifiable detached-JWS certificate via `?certificate=true`.","totalTime":"PT3S","step":[{"@type":"HowToStep","position":1,"name":"Call GET /api/v1/company/{org}/verify (optionally ?certificate=true) and gate the rest of the chain on data.verification_status — stop on 'fail'.","text":"JSON envelope with data.verification_status (pass/warn/fail/unknown), data.signals (seven tri-state booleans), and _meta with response_hash; with ?certificate=true the data block is a detached-JWS certificate wrapping the same verdict."},{"@type":"HowToStep","position":2,"name":"Call GET /api/v1/company/{org}/authority (optionally ?certificate=true) to resolve who can legally commit the company before any binding onboarding step.","text":"JSON envelope with data.classification (sole/joint/by_role/prokura_only/no_authority/unknown), data.signaturrett_holders, data.prokura_holders, and _meta with response_hash."},{"@type":"HowToStep","position":3,"name":"Call GET /api/v1/company/{org}/accounts for the filed annual-accounts snapshot — treat has_filed_annual_accounts null as unknown, never as 'not filed'.","text":"JSON envelope with data.has_filed_annual_accounts (tri-state), data.last_accounts_year, data.key_figures (with explicit currency), and _meta with response_hash."},{"@type":"HowToStep","position":4,"name":"Call GET /api/v1/company/{org}/obligations to evaluate every Rulebook obligation for the company — insufficient_data verdicts point at the Tier-2 delegation path.","text":"JSON envelope with data.obligations[] (one EvaluationResult per rule: applicable/not_applicable/insufficient_data), data.data_tier, and _meta with response_hash."},{"@type":"HowToStep","position":5,"name":"Call GET /api/v1/company/{org}/deadlines to expand the applicable obligations into the concrete weekend/holiday-adjusted deadline calendar.","text":"JSON envelope with data.deadlines[] (DeadlineEntry rows, due_at ascending, Europe/Oslo offsets), data.data_tier, and _meta with response_hash."}]}],"workflows":[{"id":"workflow.lookup-deadlines","name_en":"Look up Norwegian business deadlines for a year","name_nb":"Slå opp norske forretningsfrister for et år","description_en":"Fetch the canonical deadline list (MVA terminer, A-melding, skattemelding, årsregnskap, Foretaksregisteret årsbekreftelse) for a calendar year. Deadlines are pre-adjusted through weekends and Norwegian public holidays, with the original legal deadline surfaced in `adjusted_from` when relevant.","trigger_intent_en":"What Norwegian business deadlines are coming up in YYYY?","intent":"Find all upcoming Norwegian business deadlines for a given calendar year.","expected_outcome":"An ordered list of deadline entries sorted ascending by date, each with obligation_id, legal deadline, adjusted deadline (when pushed for weekends/holidays), and legal reference.","failure_modes":["invalid_year","upstream_timeout"],"estimated_time_ms":200,"agent_instructions":"1. Call GET /api/v1/public/deadlines?year=<YYYY> (no authentication required).\n2. Parse the response body at data.deadlines — it is sorted ascending by deadline.\n3. For each entry, note obligation_id, deadline (ISO 8601 with Europe/Oslo offset), and adjusted_from (null unless the legal deadline fell on a weekend or holiday).\n4. If data.deadlines is empty, the year is valid but outside our coverage window (2020-2100) — surface VALIDATION_FAILED explanation to the user.","steps":[{"step":1,"capability_id":"public.deadlines","purpose":"Call GET /api/v1/public/deadlines?year=YYYY and iterate the returned mappings sorted ascending by deadline.","expected_response":"JSON envelope with data.year, data.deadlines[] (sorted ascending), and _meta.served_from='static'."}],"requires_auth":false,"tier_minimum":null},{"id":"workflow.check-company-status","name_en":"Check a Norwegian company's current Brønnøysund status","name_nb":"Sjekk en norsk virksomhets gjeldende Brønnøysund-status","description_en":"Fetch the current Tier 1 + (when delegation exists) Tier 2 view of a Norwegian company by org number. `data.data_tier` tells you whether Tier 2 was served; `_meta.served_from` distinguishes cache from live.","trigger_intent_en":"Is company with org_number 999999999 still active, and what is its current name?","intent":"Retrieve the current Brønnøysund-sourced status and registry metadata for a Norwegian company, including Tier 2 commercial metrics when a delegation is in place.","expected_outcome":"Company context object with name, entity_type, NACE codes, status (active/deregistered/dissolved), municipality, signaturrett/prokura — plus Tier 2 fields (employee_count, annual_turnover, total_assets) when the caller has an active delegation.","failure_modes":["org_not_found","tier2_data_missing","upstream_timeout","auth_missing","auth_invalid_key"],"estimated_time_ms":500,"agent_instructions":"1. Ensure you hold a consumer API key in the Authorization: Bearer header.\n2. Call GET /api/v1/company/<ORG_NUMBER>/context with ORG_NUMBER as a 9-digit Norwegian organisation number.\n3. Branch on data.data_tier — 'tier_1' means public-only Brønnøysund fields; 'tier_2' means the delegation is active and commercial metrics are included.\n4. Read _meta.served_from ('cache' vs 'live') and _meta.stale (true when a within-limit cached row was served as an upstream-down fallback).\n5. On 404 org_not_found: surface the explanation.why text (Norwegian) to the user.\n6. On 503 with _meta.stale=true: the data is still usable but older than 24 hours — tell the user freshness is degraded.","steps":[{"step":1,"capability_id":"company.context","purpose":"Call GET /api/v1/company/{org}/context with the consumer API key; branch on data.data_tier.","expected_response":"JSON envelope with data.company (tier-1 fields), optional data.tier_2 block, data.data_tier, and _meta with served_from + stale markers."}],"requires_auth":true,"tier_minimum":"free"},{"id":"workflow.exchange-rate-lookup","name_en":"Look up the official NOK exchange rate for a date","name_nb":"Slå opp offisiell NOK-kurs for en dato","description_en":"Fetch the Norges Bank NOK rate for a currency and date. Weekend/holiday/pre-publication-today requests fall back to the most recent prior business day; `data.date` is the actual publication date and `data.requested_date` is what was asked for.","trigger_intent_en":"What was the EUR/NOK rate on 2026-04-10?","intent":"Fetch the official Norges Bank NOK exchange rate for a currency and optional date, with automatic fallback to the most recent prior business day when the requested date is a weekend, holiday, or before today's ~16:00 Oslo publication.","expected_outcome":"A rate object: base='NOK', quote=<uppercased ISO>, rate (1 quote = rate NOK), date (actual publication date), requested_date (what you asked for), source='Norges Bank'.","failure_modes":["invalid_currency","invalid_date","no_rate_available","upstream_unavailable"],"estimated_time_ms":300,"agent_instructions":"1. Call GET /api/v1/tools/exchange-rate?currency=<ISO>&date=<YYYY-MM-DD> (no authentication).\n2. CURRENCY must be a 3-letter ISO 4217 code Norges Bank tracks (EUR, USD, GBP, SEK, DKK, etc.).\n3. DATE is optional — omit to get today-in-Oslo; future dates are rejected with 400 INVALID_DATE.\n4. Compare data.date to data.requested_date. If they differ, the requested date fell on a weekend / holiday / pre-publication window and the rate is from the most recent prior business day.\n5. On 404 NO_RATE_AVAILABLE: Norges Bank does not track this currency — try EUR or USD as a fallback.\n6. On 502 UPSTREAM_UNAVAILABLE: transient; retry in a few minutes.","steps":[{"step":1,"capability_id":"tools.exchange_rate","purpose":"Call GET /api/v1/tools/exchange-rate?currency=<ISO>&date=<YYYY-MM-DD>.","expected_response":"JSON envelope with data.rate (number), data.date, data.requested_date, data.cached, and _meta.served_from (cache/live)."}],"requires_auth":false,"tier_minimum":null},{"id":"workflow.altinn-role-migration","name_en":"Translate an Altinn 2 code to the Altinn 3 equivalent","name_nb":"Oversett en Altinn 2-kode til Altinn 3-motparten","description_en":"Translate an Altinn 2 service or role code to its Altinn 3 equivalent. The 19 June 2026 Altinn 2 deprecation deadline has passed, so this identifies what a legacy code maps to for migration remediation. Gate production migration actions on `verified === true`; unverified entries are convenience references only.","trigger_intent_en":"What Altinn 3 role corresponds to Altinn 2 code A0208?","intent":"Discover the Altinn 3 resource or role that replaces a given Altinn 2 service/role code, now that the 19 June 2026 Altinn 2 deprecation cliff has passed.","expected_outcome":"A migration entry with altinn2_code, altinn3_code (possibly null if DigDir hasn't published an equivalent yet), migration_notes in Norwegian and English, service_owner, and a `verified` flag indicating whether the mapping has been cross-checked against DigDir's authoritative source.","failure_modes":["invalid_code","not_found","unverified_entry"],"estimated_time_ms":150,"agent_instructions":"1. Call GET /api/v1/tools/altinn-migration?altinn2_code=<CODE> (no authentication). CODE is alphanumeric, max 10 characters (e.g. A0208).\n2. Read data.entry.verified — if false, the mapping is a best-effort reference and MUST NOT drive production migration without human review.\n3. Read data.entry.altinn3_code — null means DigDir has not yet published the Altinn 3 equivalent.\n4. The 19 June 2026 Altinn 2 cliff has passed, so data.deadline_passed is true and data.days_remaining is 0 — tell the user the cutoff is already behind them rather than presenting a countdown.\n5. On 404 NOT_FOUND: the code is not in our static map — point the user at docs.altinn.studio.","steps":[{"step":1,"capability_id":"tools.altinn_migration","purpose":"Call GET /api/v1/tools/altinn-migration?altinn2_code=<CODE>; inspect `verified` before trusting the mapping for production automation.","expected_response":"JSON envelope with data.entry (MigrationEntry), data.deprecation_deadline, data.days_remaining, data.deadline_passed, and _meta.served_from='static'."}],"requires_auth":false,"tier_minimum":null},{"id":"workflow.obligation-overview","name_en":"List generic obligations for a Norwegian entity type","name_nb":"List generelle forpliktelser for en norsk selskapsform","description_en":"Fetch the universal obligation set for a given entity type (AS, ENK, ANS, DA, NUF). Template layer only — `tier_2_required: true` entries need commercial data to evaluate per-company, which requires an Altinn delegation.","trigger_intent_en":"What obligations apply to a Norwegian aksjeselskap (AS)?","intent":"List the universal regulatory obligations (tax, reporting, insurance, registration) that apply to a Norwegian entity type, and identify which of those need Tier 2 commercial data to evaluate per-company.","expected_outcome":"Obligation template set: each entry names the obligation, category, frequency, required/conditionally/never applicability, a Norwegian condition string, tier_2_required flag, and the Lovdata legal reference.","failure_modes":["invalid_entity_type","empty_template_set"],"estimated_time_ms":150,"agent_instructions":"1. Call GET /api/v1/public/obligations?entity_type=<AS|ENK|ANS|DA|NUF> (no authentication).\n2. Iterate data.obligations — an array of obligation templates.\n3. For each obligation with tier_2_required === true, plan ahead: the caller will need an Altinn delegation (workflow.request-delegation, not yet shipped) before the paid /v1/company/{org}/obligations endpoint can evaluate it per-company.\n4. Obligations with required='never' are DELIBERATELY included so agents see they were considered and ruled out (e.g. revisor-plikt for ENK).\n5. ANS, DA, and NUF currently return an empty obligations array with a notes entry — templates are not yet published.","steps":[{"step":1,"capability_id":"public.obligations","purpose":"Call GET /api/v1/public/obligations?entity_type=<AS|ENK|ANS|DA|NUF>. Use the `tier_2_required` flag to decide whether to plan a delegation flow next.","expected_response":"JSON envelope with data.entity_type, data.obligations[] (may be empty for ANS/DA/NUF), data.notes[], and _meta.served_from='static'."}],"requires_auth":false,"tier_minimum":null},{"id":"workflow.reconcile-filing-history","name_en":"Reconcile a customer's filing history from the audit trail","name_nb":"Avstem en kundes innsendingshistorikk fra sporingsloggen","description_en":"Pull the consumer's own audit trail (Sporingslogg) for a single org_number across a date range, paging via the opaque cursor (pagination.next_cursor) until exhausted. Useful for accounting firms reconciling activity after the fact, AI-agent operators debugging filing flows, and producing a forensic timeline for dispute defense. Returns ONLY rows YOUR consumer wrote — cross-consumer visibility is structurally impossible.","trigger_intent_en":"Show me every audit-log row my consumer wrote for org 999999999 in the last 90 days.","intent":"Reconstruct the activity timeline a single consumer's API key generated against one Norwegian organisation, optionally narrowed by action type or initiator class, for reconciliation, debugging, or dispute defense.","expected_outcome":"Ordered (timestamp DESC, id DESC) page of AuditLogEntry rows the consumer wrote for the org, each carrying correlation_id (joins to provenance_log / evaluation_snapshots / compliance_state_events), initiated_by classification, schema_version, and a scrubbed details JSONB.","failure_modes":["auth_missing","scope_insufficient","validation_failed","audit_query_failed"],"estimated_time_ms":250,"agent_instructions":"1. Ensure your API key has the read:audit scope (operator-issued via /v1/admin/keys).\n2. Call GET /api/v1/company/<ORG_NUMBER>/audit?since=<ISO_FROM>&until=<ISO_TO>&limit=200 with the consumer API key.\n3. Iterate data.data — each row is an AuditLogEntry. correlation_id joins to provenance_log / evaluation_snapshots / compliance_state_events from the same originating request.\n4. If pagination.has_more is true, pass pagination.next_cursor back as ?cursor=<value> to fetch the next page (canonical, PR-PAGE-UNIFY). The legacy ?before=&before_id= tuple still works through the deprecation window (responses to it carry Deprecation/Sunset headers); if you use it, both parameters are required together.\n5. To narrow further: ?action=<closed-enum> or ?initiated_by=<human|agent|cron|system|unknown>. Action enum is the closed list at src/lib/audit/types.ts AUDIT_ACTIONS — anything outside the list is rejected at 400.\n6. details.* values matching token / secret / password / bearer / authorization / jwt / api_key / private_key / credential / client_secret / refresh_token are auto-redacted to the literal string [REDACTED] at BOTH write and read time.\n7. An empty array (data.data === []) for a valid (consumer, org) pair is returned as 200, not 404 — that prevents existence-leak across consumers.","steps":[{"step":1,"capability_id":"company.audit","purpose":"Call GET /api/v1/company/{org}/audit with the consumer API key (read:audit scope). Iterate the page; if pagination.has_more, repeat with ?cursor=pagination.next_cursor.","expected_response":"JSON envelope with data.data[] (AuditLogEntry array), data.pagination.has_more, data.pagination.next_cursor (string or absent), the legacy data.pagination.next_before / next_before_id fields, and _meta with response_timestamp + response_hash."}],"requires_auth":true,"tier_minimum":"free"},{"id":"workflow.track-changes","name_en":"Track historical changes for a customer company","name_nb":"Følg historiske endringer for en kunde-virksomhet","description_en":"Pull all change events for a specific org_number from the change archive (Brreg ingestion + Altinn / DigDir / Norges Bank multi-source). Cursor-paginated; pass next_cursor back as ?cursor= for the next page.","trigger_intent_en":"Show me everything that changed for org_number 998877665 in the last 30 days.","intent":"Subscribe (via polling) to changes affecting a Norwegian company, audit historical mutations, or reconstruct what Apier saw at a given time across all four upstream sources.","expected_outcome":"Page of change events (created/updated/deleted) ordered detected_at DESC, id DESC. Each row carries source_snapshot_id for joining to the response receipt, plus a derived observation_kind (first_observation = the archive's first sighting, value_change = a real observed transition). With entity_id supplied (this workflow's golden path) personal role-holder fields are included; without it, role-holder field rows AND whole-entity company created/deleted rows (their snapshots embed role-holder arrays) are withheld — intentionally, not as missing archive data — and personal_fields_withheld is true.","failure_modes":["auth_missing","scope_insufficient","cursor_invalid","validation_failed"],"estimated_time_ms":200,"agent_instructions":"1. Ensure your API key has the read:changes scope (operator-issued via /v1/admin/keys).\n2. Call GET /api/v1/changes with filters: source (brreg|altinn|digdir|norges_bank|nav|skatteetaten), entity_id, change_type (created|updated|deleted), from + to (RFC 3339).\n3. Iterate data.data — each row is a created/updated/deleted event with before_value/after_value JSONB blobs, an RFC 6902 diff array, and a derived observation_kind (treat first_observation rows as the archive's first sighting of that field/entity, not a real-world change).\n4. If pagination.has_more is true, pass pagination.next_cursor back as ?cursor=<value> to fetch the next page. Do NOT modify the cursor — it's HMAC-signed and tampered cursors return 400 CURSOR_INVALID.\n5. correlation_id is intentionally not exposed; use source_snapshot_id to join back to provenance receipts.","steps":[{"step":1,"capability_id":"changes.query","purpose":"Call GET /api/v1/changes?source=brreg&entity_id={org_number}&from={iso_date} with the consumer API key (read:changes scope). Iterate the page; if pagination.has_more, repeat with ?cursor=pagination.next_cursor.","expected_response":"JSON envelope with data.data[] (PublicChangeRow array), data.pagination.has_more, data.pagination.next_cursor (string or null), and _meta with response_timestamp + response_hash."}],"requires_auth":true,"tier_minimum":"starter"},{"id":"workflow.validate-filing","name_en":"Validate a filing before submission","name_nb":"Valider en innlevering før innsending","description_en":"Run the five preflight checks the dry-run engine performs against a filing payload — company exists, system user authorised, scopes delegated, payload format valid, deadline in future — without submitting anything to Altinn / Skatteetaten / NAV. Use this before any human-approval flow so the agent can present a complete pass/fail picture rather than discovering problems mid-flight.","trigger_intent_en":"Will my MVA-melding for 2026-T2 pass before I submit it?","intent":"Pre-flight a regulatory filing payload to discover every problem in one call, BEFORE the live submission burns an idempotency key or surfaces a partial commit at the upstream.","expected_outcome":"DryRunOutcome with all_passed (boolean) and checks[5] (per-check pass/fail with structured error_code on failures). The disclaimer field reminds the agent that a passing dry-run is NOT a guarantee of submission success.","failure_modes":["auth_missing","scope_insufficient","validation_failed","execute_not_available","payload_too_large"],"estimated_time_ms":600,"agent_instructions":"1. Ensure your API key has the read:actions scope (operator-issued via /v1/admin/keys).\n2. Build the request body: { org_number (9 digits), action_type (\"mva_melding\" | \"a_melding\"), period (YYYY-Tn|YYYY-A|YYYY-MM for mva; YYYY-MM for a-melding), payload (per-action shape — see OpenAPI components MvaMeldingPayload / AMeldingPayload). Decimals as STRINGS, not numbers.\n3. POST /api/v1/actions/execute?dry_run=true with the body. Note ?dry_run=true is what makes this call validate-only — omitting it (or sending any value other than \"true\") routes to the live-execute path for supported action types instead of a no-op (still mock-routed by default when ALTINN_MODE=mock, not necessarily a real government submission); see /docs/capability-status for the current live/gated status of each action type before relying on that path.\n4. Read response data.outcome.all_passed. If true, the dry-run found no blockers; live submission uses the live-execute flow (Idempotency-Key + approval token) and is enabled per-operator once Maskinporten/Altinn grants are active.\n5. If false, iterate data.outcome.checks: each row has check (name), passed (bool), error_code (string|null), explanation_summary (string|null). Fix the inputs against the failed checks and retry — the engine never short-circuits, so one call lists every problem.\n6. Treat the disclaimer field as a hard contract: a passing dry-run is NOT a guarantee. Government systems may reject otherwise-valid submissions for reasons outside Apier's view.","steps":[{"step":1,"capability_id":"actions.execute_dry_run","purpose":"Call POST /api/v1/actions/execute?dry_run=true with { org_number, action_type, period, payload }. The endpoint performs all five preflight checks, writes one evaluation_snapshots row + one audit_log row, and returns 200 with the structured DryRunOutcome.","expected_response":"JSON envelope with data.dry_run === true, data.outcome.all_passed (boolean), data.outcome.checks (5 entries, ordered), data.outcome.disclaimer (fixed string), and _meta with response_timestamp + response_hash."}],"requires_auth":true,"tier_minimum":"starter"},{"id":"workflow.kyb-client-onboarding","name_en":"Onboard a Norwegian client company from a single org number (KYB)","name_nb":"Onboard en norsk kundevirksomhet fra ett organisasjonsnummer (KYB)","description_en":"Composed five-step KYB (know-your-business) onboarding chain over the live company reads: verify the company exists and is safe to act for, resolve who can legally sign for it, read its filed annual accounts, evaluate its regulatory obligations, and expand its concrete deadline calendar. Every step's response carries `_meta.response_hash` and lands a provenance_log row, so the assembled onboarding answer is provenance-backed end-to-end; the verify and authority steps optionally return an offline-verifiable detached-JWS certificate via `?certificate=true`.","trigger_intent_en":"Onboard company 999999999 as a client — does it exist, who can sign for it, and what does it owe when?","intent":"Onboard a Norwegian client company from a single organisation number: establish that it is real and active, who holds acting capacity, what its filed accounts show, which obligations apply, and when they fall due — one provenance-stamped answer per question.","expected_outcome":"A provenance-backed onboarding summary assembled from five responses: a pass/warn/fail verification verdict, a signing-authority classification with the named holders, the filed annual-accounts snapshot with key figures, the evaluated obligation catalogue, and the weekend/holiday-adjusted deadline calendar — each response carrying `_meta.response_hash` (joinable to provenance_log), with optional detached-JWS certificates from the verify and authority steps for offline re-verification.","failure_modes":["org_not_found","auth_missing","auth_invalid_key","scope_insufficient","upstream_timeout"],"estimated_time_ms":2500,"agent_instructions":"1. Ensure you hold a consumer API key (Authorization: Bearer) with the read:brreg scope; ORG_NUMBER below is a 9-digit Norwegian organisation number.\n2. Call GET /api/v1/company/<ORG_NUMBER>/verify. Branch on data.verification_status: 'fail' means the company is not active — stop onboarding and surface data.summary; 'warn' means active but no signing authority is defined — continue, but flag it; 'pass' continues. Append ?certificate=true when you want the verdict wrapped in an offline-verifiable detached-JWS certificate.\n3. Call GET /api/v1/company/<ORG_NUMBER>/authority. Read data.classification (sole / joint / by_role / prokura_only / no_authority / unknown) and data.signaturrett_holders + data.prokura_holders to know who must approve binding steps. ?certificate=true is available here too.\n4. Call GET /api/v1/company/<ORG_NUMBER>/accounts. data.has_filed_annual_accounts is tri-state (true / false / null=unknown — never coerce null to false); data.key_figures carries the latest filed year's figures with an explicit currency.\n5. Call GET /api/v1/company/<ORG_NUMBER>/obligations. Each EvaluationResult is applicable / not_applicable / insufficient_data — treat insufficient_data as 'needs a Tier-2 delegation', not as a no.\n6. Call GET /api/v1/company/<ORG_NUMBER>/deadlines. Entries are already weekend/holiday-adjusted with Europe/Oslo offsets; sort order is due_at ascending.\n7. Assemble the onboarding summary from the five data blocks. Keep each response's _meta.response_hash (and the step-2/3 certificates if requested) as the provenance trail for the decision.","steps":[{"step":1,"capability_id":"company.verify","purpose":"Call GET /api/v1/company/{org}/verify (optionally ?certificate=true) and gate the rest of the chain on data.verification_status — stop on 'fail'.","expected_response":"JSON envelope with data.verification_status (pass/warn/fail/unknown), data.signals (seven tri-state booleans), and _meta with response_hash; with ?certificate=true the data block is a detached-JWS certificate wrapping the same verdict."},{"step":2,"capability_id":"company.authority","purpose":"Call GET /api/v1/company/{org}/authority (optionally ?certificate=true) to resolve who can legally commit the company before any binding onboarding step.","expected_response":"JSON envelope with data.classification (sole/joint/by_role/prokura_only/no_authority/unknown), data.signaturrett_holders, data.prokura_holders, and _meta with response_hash."},{"step":3,"capability_id":"company.accounts","purpose":"Call GET /api/v1/company/{org}/accounts for the filed annual-accounts snapshot — treat has_filed_annual_accounts null as unknown, never as 'not filed'.","expected_response":"JSON envelope with data.has_filed_annual_accounts (tri-state), data.last_accounts_year, data.key_figures (with explicit currency), and _meta with response_hash."},{"step":4,"capability_id":"company.obligations","purpose":"Call GET /api/v1/company/{org}/obligations to evaluate every Rulebook obligation for the company — insufficient_data verdicts point at the Tier-2 delegation path.","expected_response":"JSON envelope with data.obligations[] (one EvaluationResult per rule: applicable/not_applicable/insufficient_data), data.data_tier, and _meta with response_hash."},{"step":5,"capability_id":"company.deadlines","purpose":"Call GET /api/v1/company/{org}/deadlines to expand the applicable obligations into the concrete weekend/holiday-adjusted deadline calendar.","expected_response":"JSON envelope with data.deadlines[] (DeadlineEntry rows, due_at ascending, Europe/Oslo offsets), data.data_tier, and _meta with response_hash."}],"requires_auth":true,"tier_minimum":"free"}],"sandbox":{"base_url":"https://www.apier.no/api/v1/sandbox","cors":"open","determinism":"byte-equivalent except _meta.response_timestamp","reserved_test_org_numbers":["999000001","999000002","999000003","999000004","999000005"],"endpoints":[{"capability_id":"company.context","url":"https://www.apier.no/api/v1/company/{org}/context","sandbox_url":"https://www.apier.no/api/v1/sandbox/company/{org}/context"},{"capability_id":"company.obligations","url":"https://www.apier.no/api/v1/company/{org}/obligations","sandbox_url":"https://www.apier.no/api/v1/sandbox/company/{org}/obligations"},{"capability_id":"company.deadlines","url":"https://www.apier.no/api/v1/company/{org}/deadlines","sandbox_url":"https://www.apier.no/api/v1/sandbox/company/{org}/deadlines"},{"capability_id":"company.summary","url":"https://www.apier.no/api/v1/company/{org}/summary","sandbox_url":"https://www.apier.no/api/v1/sandbox/company/{org}/summary"},{"capability_id":"company.audit","url":"https://www.apier.no/api/v1/company/{org}/audit","sandbox_url":"https://www.apier.no/api/v1/sandbox/company/{org}/audit"},{"capability_id":"auth.approval-token","url":"https://www.apier.no/api/v1/auth/approval-token","sandbox_url":"https://www.apier.no/api/v1/sandbox/auth/approval-token"},{"capability_id":"actions.execute","url":"https://www.apier.no/api/v1/actions/execute","sandbox_url":"https://www.apier.no/api/v1/sandbox/actions/execute"}]},"public_sandbox":{"base_url":"https://www.apier.no/api/v1/sandbox/public","cors":"open","determinism":"byte-equivalent except _meta.response_timestamp","fixture_org_number":"999999999","rate_limit_per_ip_per_hour":100,"auth":"none","provenance_log_emitted":false,"mcp_query_log_source_surface":"public_sandbox","copy_paste_examples_url":"https://www.apier.no/sandbox/examples","try_without_signup_workflow":[{"step":"context","method":"GET","url":"https://www.apier.no/api/v1/sandbox/public/company/999999999/context","body":null,"expected_status":200},{"step":"obligations","method":"GET","url":"https://www.apier.no/api/v1/sandbox/public/company/999999999/obligations","body":null,"expected_status":200},{"step":"summary","method":"GET","url":"https://www.apier.no/api/v1/sandbox/public/company/999999999/summary","body":null,"expected_status":200},{"step":"plan","method":"POST","url":"https://www.apier.no/api/v1/sandbox/public/actions/plan","body":{"org_number":"999999999","action":"mva_melding"},"expected_status":200}]},"_meta":{"data_freshness":"2026-07-07T00:00:00.000Z","source":"apier.no internal manifest","schema_version":"1.1.0","workflow_count":9}}