Apier.no
Trust & Data Sovereignty
The compliance posture behind every API response — where the data lives, the GDPR basis we operate under, who the subprocessors are, and the live Rulebook whose version ships on every _meta.
Data ResidencyStatus: Beta
Database: Stockholm, live. Compute: Vercel EU edge today; full-EU backend before real company data flows.
- Backups: Same region as primary — never replicated outside the EU.
GDPR Legal BasisStatus: Beta
- Tier 1 company role republication: legitimate interest — Article 6(1)(f) — over the public Brønnøysund registry.
- API consumer account data: contractual necessity — Article 6(1)(b).
Full policy lives at /privacy. Our GDPR role (controller vs processor) is deliberately not pre-declared on this page — it is settled per deployment in the contract that governs the integration.
SubprocessorsStatus: Beta
Every subprocessor processes data exclusively inside the EU. DPA status below reflects the approvals checklist on the current build — values flip as agreements are signed.
| Subprocessor | Purpose | Region | DPA Status |
|---|---|---|---|
| Supabase | Primary database | EU (Stockholm, eu-north-1) | Signed 2026-05-21 |
| Vercel | Build + edge hosting (pre-launch; EU migration planned) | EU edge | Signed 2026-05-21 (auto-incorporated via Vercel ToS) |
| Sentry | Error tracking (no PII per scrubber config) | EU | Signed 2026-05-21 |
| Plausible | Privacy-first analytics (cookieless, no PII) | EU (Germany) | Not applicable — cookieless, no personal data processed |
| Betterstack | Uptime monitoring + hosted status page | EU | Not applicable — uptime monitoring only, no data subject content |
| Maskinporten (Digdir) | Norwegian government auth broker | Norway (EU/EEA) | Not applicable — public-sector controller-to-controller, bound by Norwegian law not GDPR Art 28 |
| Lovdata | Read-only legal-text reference (no data sent) | Norway (EU/EEA) | Not applicable — read-only legal-text reference; no personal or company data transmitted |
| Resend | Transactional email (welcome, magic link) | EU (Ireland) | Signed 2026-05-21 (auto-executed on signup) |
No external LLM processes customer data during the build phase. If this changes the provider will be listed in the subprocessor table above and this statement will be removed.
Integrating Apier on behalf of your own clients? The customer Data Processing Agreement (Art 28) sets out Apier's obligations as Processor — distinct from the subprocessor DPAs above. Draft template, pending lawyer review.
Data MinimizationStatus: Live
We store the fields the Rulebook requires to evaluate an obligation — not a full company snapshot. Roles (signatur, prokura) are stored as roles, not as named persons. Tier 2 data is fetched on-demand and cached only as long as the evaluation pipeline needs it.
Log RetentionStatus: Live
- API request / response logs: 30 days, then auto-deleted.
- Delta logs: anonymised after 12 months.
- Error logs: 30 days.
- Maskinporten / Altinn tokens: never logged — only timestamp, scope, and success / failure.
- Evaluation snapshots: 24 months (forensic).
Full policy lives at /privacy.
Public SandboxStatus: Live
The /api/v1/sandbox/public/* surface is unauthenticated. Response bodies contain no real personal data about Norwegian businesses, role- holders, or third parties — every request resolves against synthetic fixture org 999999999, and the response body carries _meta.is_sandbox: true so SDK clients can branch on the marker rather than the URL. The one personal-data touchpoint is the caller's IP address (used transiently for rate-limiting per the section below).
- No real personal data is processed: every sandbox call resolves against synthetic fixture data. Org-numbers, role-holder names, accounting periods — all fabricated. The realistic synthetic graph adds MOD-11-valid synthetic org-numbers (818000006, 818111118, 818333331, 818444443, 818555555) for AI parsers that validate Brønnøysund checksums, but Apier NEVER queries Brønnøysund with these numbers — every read is satisfied from in-process fixtures + ephemeral session state.
- IP handling: IP addresses are processed transiently for rate-limiting only — legitimate interest, retention strictly under one hour (the fixed-window bucket key flushes at the next window boundary).
- Provenance: public-sandbox responses are deliberately excluded from the production provenance / audit chain — synthetic traffic must not dilute the forensic value of real-data receipts. Observability is captured separately on
mcp_query_logwithsource_surface=public_sandbox. - Ephemeral session state: the authenticated
/api/v1/sandbox/*surface accepts the synthetic bearerapier_sandbox_test_<suffix>and persists mock mutations under the supplied suffix in asandbox_sessionstable with strict per-suffix RLS isolation and a 15-minute TTL. The suffix never appears in logs or audit trail in plaintext (SHA-256 hashed before any log write); rows are deleted lazily on read. - Hard upstream invariant: NO sandbox path EVER makes a real call to Altinn, Maskinporten, Brønnøysund, Skatteetaten, or NAV — including on writes. The receipts the sandbox returns are HMAC-derived from synthetic inputs and carry an explicit
_sandbox_markerfield so no parser can mistake them for real government acknowledgements. - Rate-limit semantics: 100 requests / hour per IP on a dedicated bucket separate from the public discovery endpoints — a noisy sandbox client cannot consume the discovery quota. Per-suffix per-minute limits keep one
apier_sandbox_test_<suffix>agent from starving every other sandbox agent on the same org. An agent-loop breaker observes the same sandbox call fingerprint over a 20-second window and returns 429AGENT_LOOP_DETECTEDon the 9th identical hit — bodies are static and never echo the request. A global sandbox circuit breaker sheds sandbox load with 503SANDBOX_TEMPORARILY_UNAVAILABLEunder sustained Supabase stress, protecting the shared infrastructure before degradation can cascade into production routes. All guard rails fail OPEN on their own infrastructure error — they protect the sandbox, never cause outages on it. No real personal data is processed by any guard-rail audit row (the loop table stores ONLY sha256 fingerprints and timestamps).
Copy-paste cURL examples live at /sandbox/examples. The internal /api/v1/sandbox/* surface (used by SDK harnesses for failure-flow testing) accepts the wider 999000001-005 fixture set.
Human approval on binding writesStatus: Beta
Binding government writes (MVA-melding, A-melding, etc.) can be gated on an explicit human approval step. When the gate is enabled, /api/v1/actions/execute suspends the call and returns 412 HITL_PENDING_APPROVALwith a body that contains ONLY the pending action's ID, status, 15-minute expiry, and an approval URL built from the server-configured base URL — never from the inbound Host header (Host-header injection would let an attacker phish the human approver). Validated payload is NEVER echoed in the 412.
- Exactly-once release: an atomic conditional UPDATE claims PENDING → APPROVING (the row lock is the concurrency gate; two concurrent approves serialise). The government call flows through the existing idempotency store with a deterministic key (
hitl:<id>) so a crash or retry cannot double-submit. APPROVING is NEVER auto-reverted (a blind revert would permit double-submit); reconciliation reads the idempotency record. - Reserved scope: approve + reject are gated on
act:approve, a closed reserved scope marked §15-legal-gated and NOT default- granted at v1. The middleware short-circuits with 403 SCOPE_RESERVED regardless of the holder's scopes — an agent's own API key cannot approve its own binding writes today. - Cross-key isolation: every read/write filters on the requester's api_key_id. An unknown id and another consumer's id both return the IDENTICAL
HITL_NOT_FOUNDenvelope — existence is not disclosed across api_keys. - Compliance trail: every transition writes one append-only
audit_logrow carrying the pending action ID + the approver's api_key_id + the decision. The row's ownresolved_bycolumn is an independent regulator-readable trail. - Suspension webhook: the existing webhook delivery primitive fires fire-and-forget on suspension, HMAC-signed and SSRF- guarded. Payload contains ONLY
{event, pending_action_id, action_type, expires_at}— never the validated payload (a webhook endpoint is a wider audience than the api_key holder). - Status — scaffold: routes + state machine + endpoints + webhook all exist and are fully unit-testable, but enforcement on the live
/api/v1/actions/executepath is behind theHITL_ENFORCEfeature flag — FAIL-SAFE OFF (only the exact strings"true"or"1"enable; anything else, including typos, returns OFF). When OFF (default), live execute behaves byte-for-byte unchanged.
Audit TrailStatus: Live
Every request threads through an append-only audit log keyed on a per-request correlation id — the X-Correlation-ID header is set on both the request and the response, so a single UUID reconstructs the full slice of history a given call produced (audit row, evaluation snapshot, state transition). The log has no UPDATE / DELETE path — the immutability is enforced at three layers (RLS, BEFORE-triggers, application code).
Every government submission also mints an append-only receipt whose government_response_raw field captures the verbatim upstream response — immutable forensic proof of exactly what the authority returned, preserved without interpretation.
Write-conflict detectionStatus: Live
Apier is designed for orchestrated environments where multiple AI agents — potentially from different vendors — operate concurrently on behalf of the same Norwegian organization. When two requests with different correlation IDs target the same action on the same organization within a short window, the second request returns HTTP 409with the first request’s correlation_id in first_correlation_id. Agents can either retry with the same Idempotency-Key to receive the original receipt (after the first request completes), or wait the indicated retry_after_seconds and retry with a fresh request.
The 409 itself is NOT cached by Idempotency-Key (the response sets X-Apier-Skip-Idempotency-Cache: 1), so retries after the lock expires behave correctly. Lock TTLs are configured per action type:
- MVA submission: 10 minutes
- A-melding submission: 5 minutes
- Skattemelding submission: 20 minutes
- Årsregnskap submission: 30 minutes
- System-user delegation: 1 minute
Sandbox writes are namespaced separately from production — a sandbox lock never blocks a production write and vice versa. Lock acquire is atomic via Postgres advisory locks (race-free under concurrency). Every conflict produces an immutable audit_log entry with action: “write_conflict_blocked”.
Per-organization rate isolationStatus: Live
Apier maintains TWO independent rate-limit dimensions per authenticated request, running in parallel; the lower of the two binds the 429 decision. The per-(API key, endpoint) per-minute bucket is the established per-tier cap. The per-(API key, organisation number) per-day bucket bounds runaway-agent failure modes — a runaway loop on one client org cannot starve siblings sharing the same API key.
When either dimension exceeds, the response is HTTP 429 with header X-RateLimit-Dimension set to per_key, per_org, or both indicating which bucket tripped. The Retry-After header carries the seconds until the binding window resets — for the per-org dimension that is seconds-until-next-Oslo- midnight (DST-aware via Postgres timezone conversion).
Per-org daily bucket sizes per tier (sized at ~100× typical daily volume — the dimension exists to bound failure modes, not to monetize):
- Free: 1,000 requests / org / day
- Starter: 5,000 requests / org / day
- Professional: 25,000 requests / org / day
- Enterprise: unlimited
Scope: per-org applies ONLY to GET endpoints whose URL path carries the org_number (/api/v1/company/{org}/* and /api/v1/auth/permissions/{org}). Write endpoints — protected by approval tokens and idempotency keys — and routes whose org_number lives in the request body are out of scope for this iteration.
Per-agent forensicsStatus: Live
Three OPTIONAL request headers — X-Agent-Vendor, X-Agent-Model, X-Agent-Run-Id — let an agent self-attest its identity for the audit trail. Apier persists the sanitised values to audit_log and mcp_query_log; the values never gate auth, scope, rate-limit, or idempotency, and never appear in any response body, response header, _meta block, or error message.
Self-attested means unverified. The columns identify the agent the caller CLAIMS to be — useful for forensic reconstruction (“every row produced by this run id”), useless as a trust signal. Authorization remains anchored on the API key (Authorization: Bearer) and its scopes.
Contents are sanitised at the boundary: ASCII control chars, Unicode line/paragraph separators, and BOM are stripped; values are clamped to 64 / 128 / 256 UTF-8 bytes respectively on a code-point boundary. Omitted headers persist as SQL NULL.
Spec-compliant MCP handshakeStatus: Live
Apier implements the MCP initialize JSON-RPC method and the post-handshake notifications/initialized acknowledgement, per the Model Context Protocol specification. Supported protocol versions: 2025-11-25, 2025-06-18, 2025-03-26, 2024-11-05; the highest mutually-supported version is selected at handshake time. The transport is stateless streamable-http — every POST authenticates fresh via Authorization: Bearer; there is no session token or server-side handshake state.
The handshake advertises only { tools: { listChanged: false } } — Apier does not implement sampling, elicitation, resources, prompts, logging, or roots, and never advertises capabilities it does not serve. Client capability hints sent during initialize are accepted for forensic logging but never echoed back in the response (minimal-disclosure principle).
EncryptionStatus: Live
- In transit: TLS 1.2+ across both layers — Vercel edge (consumer traffic) and Supabase Postgres (database connections). Matches the disclosure in
docs/legal/processing-activities-register.md§security so the trust posture stays consistent across consumer-facing and legal-register surfaces. - At rest: AES-256 on the primary database and on every backup, same region as primary. Backups are never replicated outside the EU.
- Government tokens: Maskinporten and Altinn 3 access tokens are never persisted to logs. The audit log records timestamp + scope + outcome only — the token bytes themselves never leave the token-acquisition path.
Incident ResponseStatus: Beta
- Datatilsynet: breach notification within 72 hours per GDPR Article 33.
- Affected customers: direct notification without undue delay per GDPR Article 34, with the facts of the breach and the remediation timeline.
- Public summary: for breaches affecting customer data, a redacted post-mortem is published at /security within 30 days.
CertificationStatus: Planned
Apier holds no formal ISO 27001 or SOC 2 certification today. The architecture is structurally aligned with the controls those frameworks evaluate — EU-only data residency, row-level security on every table, append-only audit log, encryption in transit and at rest, no external LLM data flows. Formal certification will be pursued when customer or procurement requirements justify the audit cycle.
Vulnerability disclosure policy + safe harbor + 90-day coordinated window: /security.
Upstream ResilienceStatus: Beta
A reliability layer sits between your agent and the Norwegian government APIs, so transient upstream chaos does not surface as a raw failure in your workflow.
- Circuit breaker: consecutive upstream failures trip the breaker for that government system; while it is open the agent receives a degraded signal (
EXECUTION_CIRCUIT_OPEN) rather than a cascade of 502s, and retries are skipped until a single probe confirms recovery. - Smart cache: rule evaluations and deadline calculations are cached behind a verified TTL and never served stale — an expired entry refetches live.
_meta.served_from(live/cache) and_meta.cache_age_mssurface the source on every response. - Standardised errors: upstream HTTP chaos — timeouts, 5xx, malformed bodies — is normalised to Apier's stable structured error envelope (a machine-readable
error_codeplus a Norwegian-language explanation), so your agent always gets a predictable error shape, never a raw upstream body. - Live readiness probe:
GET /api/v1/health/agent-readinessreturns the current upstream circuit-breaker state plus 30-day per-workflow-class success rates and p95 latency — check it before submitting a batch compliance workflow.
Versioned RulebookStatus: Live
The table below is live-queried from rule_versions. Every row records why a rule version was created — the origin column maps directly to the schema's structured enum.
No rule updates yet. This table auto-populates as the Rulebook evolves.
Machine-Readable AttestationStatus: Live
Agents and procurement tools can fetch this information as JSON at /.well-known/data-sovereignty.
curl -sSL https://www.apier.no/.well-known/data-sovereignty | jqApier's API contract is published as a single OpenAPI 3.1 source of truth and linted in CI on every change; every Rulebook-influenced response carries _meta.rulebook_version, so an agent can pin and replay against the exact rulebook version that produced an answer.
ArchitectureStatus: Beta
How a request flows through Apier — and where the data stops. An agent calls the API layer over HTTPS; the layer reads and writes an EU-resident Supabase database and calls Norwegian government APIs over Maskinporten. Government access tokens are never persisted, the audit log is append-only, and no personal or company data leaves Norway / the EU/EEA.
Who runs thisStatus: Live
Apier is operated by Grov Digital, a Norwegian enkeltpersonforetak (sole proprietorship), org.nr. 833 397 982. The organisation number resolves to Apier's verifiable public entry in the Brønnøysund Enhetsregisteret.
Production access and accreditations granted by Norwegian government infrastructure, with issuer and date:
| Accreditation | Issuer | Date |
|---|---|---|
| Altinn 3 System Register — Systemleverandør (System Vendor) | Digdir / Altinn | Confirmed 2026-06-09 |
| Maskinporten production client (5 production scopes) | Digdir (Samarbeidsportalen) | Verified 2026-06-06 |
| DM-39 — MVA-meldingvalidation & submission (Test + Production). Live binding submission is gated (dry-run-only); validation & dry-run are available today. | Skatteetaten | Granted 2026-05-29 |
| Access packages | Altinn | Granted 2026-06-09 |
Business continuityStatus: Beta
- Data export: On request or at account closure we provide a complete machine-readable (JSON) export of your account data — key metadata, usage records, and stored configuration.
- 90-day wind-down: If we ever discontinue the service we will give at least 90 days' written notice, keep export/read access open throughout the wind-down, and publish the timeline here and on /status.
Operational credentials and infrastructure access are documented and recoverable, so the service can be responsibly maintained or wound down even if the founder is temporarily unavailable. This is an internal documentation-and-recovery practice, not a formal third-party credential-escrow arrangement.
Founding customersStatus: Planned
Reserved for our founding customers. Once the first integrations go live, the organisations building on Apier will be listed here — with their permission. No logos or names are shown until then.
Data Processing Agreement (DPA)Status: Planned
A Data Processing Agreement (Art 28) is available on request. A downloadable version is published here after legal review.
FAQ
- Where is Apier's data hosted?
- Database: Stockholm, live. Compute: Vercel EU edge today; full-EU backend before real company data flows. Government access tokens are never persisted, and the audit log records only timestamp, scope, and outcome.
- Who are Apier's subprocessors?
- Every subprocessor Apier engages processes data exclusively inside the EU. The full list — each subprocessor's purpose, region, and data-processing-agreement status — is in the Subprocessors table above.
- Is there a human approval step before binding government writes?
- Yes — binding government writes such as MVA-melding and A-melding can be gated on an explicit human approval step. When the gate is enabled, the execute call suspends and returns 412 HITL_PENDING_APPROVAL carrying only the pending action's ID, status, expiry, and an approval URL — never the submitted payload. The approve action is gated on a reserved scope that an agent's own API key cannot grant itself.
- How can I prove what Apier did on my behalf?
- Every request threads through an append-only audit log keyed on a per-request X-Correlation-ID, set on both the request and the response, so a single correlation ID reconstructs the full slice of history a call produced. The log has no UPDATE or DELETE path. Every government submission also mints an append-only receipt whose government_response_raw field captures the verbatim upstream response.