# hafn > Mail and calendar for European companies, built to be operated by your AI agent. > Old Norse for "harbour". Data in the EU, under European law, portable on request. > Status: invitation-only pilot. spec v0.3 · 2026-08-30 hafn is a mail + calendar + tasks + files server (JMAP-native, built on Stalwart). An external agent operates a customer account through a scoped, revocable key — permissions per JMAP method, no domain-wide delegation. The agent proposes; the human approves anything irreversible. Everything the agent does is logged and yours. ## Access - Endpoint (JMAP): https://mail.hafn.eu/jmap/ - MCP shell: streamable HTTP, one POST endpoint; tools filtered by the caller's granted scopes. Not yet publicly exposed — the address is handed out at onboarding. Details: /agents.md - Auth: Bearer format API_... (HTTP Basic is rejected) - Keys: x:ApiKey — per-method permissions, expiresAt, allowedIps. Replace = exactly the listed methods, nothing else. Rotation is overlapping: new key issued before old revoked. ## Provisioning (invitation-only) - POST https://hafn.eu/v1/konto body: {"inbjudningskod": "hafn-xxxx-xxxx-xxxx", "kontonamn": "...", "adminkontakt": "..."} -> {konto, adress, engangslosenord, apinyckel, apinyckelns_scope, jmap, imap, smtp} - The apinyckel (Bearer, API_...) is shown once. Field names are Swedish; the authoritative schema is https://hafn.eu/openapi.json (OpenAPI 3.1). - Full integration doc: /docs.md - No invite? email hello@hafn.eu — machine-readable application format: /invite.md ## Tools (MCP — reads consolidated, writes reversible, irreversible named) hafn_brevlador list mailboxes hafn_post_sok search mail (never returns bodies) hafn_post_las read one message hafn_post_markera flag / file a message hafn_kal_serier recurring series as masters (not occurrences) hafn_kal_agenda occurrences, expanded (all-day flag corrected) hafn_kal_las read one event hafn_kal_boka book event (tentative by default) hafn_kal_andra change event (optimistic concurrency via state) hafn_kal_avsluta_serie end a series (sets `until`) hafn_delad_tilldela assign a message to a person (shared inbox) hafn_delad_markera per-person read marker (shared inbox) hafn_delad_oversikt who is handling what (shared inbox) hafn_kal_avboka cancel event (irreversible · own scope) hafn_post_skicka send a draft (irreversible · own scope) tools/list is filtered per token: a tool outside your scope does not exist. Full tool surface with budgets and error style: /agents.md ## Calendar semantics (measured against the live server) - recurrenceRule is singular and an object. RFC 8984's plural array is rejected. - Exceptions: recurrenceOverrides, keyed on the occurrence's original start. - Ask for masters; expand only when needed (expansion drops the all-day flag). - status: tentative | confirmed | cancelled is a first-class field. - Creating an event REQUIRES calendarIds: {"": true}. Omitting it fails with invalidProperties. Find the id via Calendar/get (isDefault marks the default one); a Task goes in its own calendar, e.g. a VTODO-only list. - Tasks: no Task/get — CalendarEvent methods carry "@type": "Task" + progress. ## Limits - Outbound: 100 messages/day, counted per sender address. Message 101 is deferred to the next UTC day, not bounced. Request more: POST /v1/begar-mer or hello@hafn.eu - The agent's key carries no send permission by default. ## Boundaries (what we do not promise) - No autonomous irreversible sends. A human approves those. - Provenance is best-effort today; the tamper-evident write-log is the record. - Streams do not yet obey scope — treated as a known gap, mitigated in the shell. - Full statement of what we do and do not promise: /principer.md ## Human view This document is the site. Humans read the same thing, typeset: https://hafn.eu/ /llms-full.txt · /docs.md · /agents.md · /invite.md · /principer.md /.well-known/security.txt · /humans.txt