Apier

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 Residency

  • Primary database: Supabase Postgres, region eu-north-1 (Stockholm).
  • Hosting (today): Vercel EU edge. Pre-launch migration to a fully EU-hosted backend is planned and tracked in the approvals checklist.
  • Backups: Same region as primary — never replicated outside the EU.

GDPR Legal Basis

  • 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.

Subprocessors

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.

SubprocessorPurposeRegionDPA Status
SupabasePrimary databaseEU (Stockholm, eu-north-1)Signed 2026-05-21 (APPROVALS-CHECKLIST row #13)
VercelBuild + edge hosting (pre-launch; EU migration planned)EU edgeSigned 2026-05-21 (APPROVALS-CHECKLIST row #19, auto-incorporated via Vercel ToS)
SentryError tracking (no PII per scrubber config)EUSigned 2026-05-21 (APPROVALS-CHECKLIST row #20)
PlausiblePrivacy-first analytics (cookieless, no PII)EU (Germany)Not applicable — cookieless, no personal data processed
BetterstackUptime monitoring + hosted status pageEUNot applicable — uptime monitoring only, no data subject content
Maskinporten (Digdir)Norwegian government auth brokerNorway (EU/EEA)Not applicable — public-sector controller-to-controller, bound by Norwegian law not GDPR Art 28
LovdataRead-only legal-text reference (no data sent)Norway (EU/EEA)Not applicable — read-only legal-text reference; no personal or company data transmitted
ResendTransactional email (welcome, magic link, password reset)EU (Ireland)Signed 2026-05-21 (APPROVALS-CHECKLIST row #32, auto-executed on signup)

No external LLM processes customer data during the build phase (CLAUDE.md Rule 13). If this changes the provider will be listed in the subprocessor table above and this statement will be removed.

Data Minimization

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 Retention

  • 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 Sandbox

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).

  • 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_log with source_surface=public_sandbox.
  • Rate limit: 100 requests / hour per IP, on a dedicated bucket separate from the public discovery endpoints — a noisy sandbox client cannot consume the discovery quota.

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.

Audit Trail

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).

Write-conflict detection

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 isolation

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 Rule 27 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 forensics

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 handshake

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-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).

Encryption

  • 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 Response

  • 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.

Certification

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.

Versioned Rulebook

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 Attestation

Agents and procurement tools can fetch this information as JSON at /.well-known/data-sovereignty.

curl -sSL https://apier.no/.well-known/data-sovereignty | jq