Authorisation and access

One place decides what each person may see

An organisation, its spaces, its roles, its named permissions and its API keys should not have to be governed in five different places. Missivia combines them into a single authorisation, computed on every call, server-side, whatever the calling client: web console, script or integration.

Entities, spaces and memberships

A space is a data perimeter, not a tab: what it holds is not visible from any other.

An organisation holds spaces – one per data perimeter: a brand, a subsidiary, an environment. A person reaches a space through a membership, which carries their role in that space: the same person may be an editor here and a plain viewer elsewhere.

The space concerned by a call is never supplied by the caller: it is derived from the authentication token, and every database query goes through an access layer that enforces it. A resource belonging to another space is reported as not found, not as forbidden – and every resource type has an automated isolation test, run in continuous integration.

What a space owns
  • Its contacts, lists, segments and consent register.
  • Its templates, fragments, files and public pages.
  • Its journeys, campaigns and service messages.
  • Its sending domains, sending quotas and bounce thresholds.
  • Its retention periods, API keys and webhook subscriptions.

How an authorisation is computed

Five elements, in that order, on every call. Nothing is decided by the interface making the call.

  1. 1

    Space

    The call is bound to a space, derived from the token. It cannot name another one.

  2. 2

    Role

    The membership brings a role, that is, a set of capabilities inherited as a whole – not a checkbox per screen.

  3. 3

    Named permissions

    They add to the role, one at a time, and are granted to a designated person: access to sensitive fields is one of them.

  4. 4

    Key class

    Every API key carries a class that caps the whole: what it cannot reach, no role can give back to it.

  5. 5

    Decision

    A single matrix decides, server-side, and records its decision in the audit log – refusals included.

Rights that are inherited, cumulative and capped

  • Inherited. A role is a coherent set of capabilities, not a list of exceptions. You change the role of a membership; you do not revisit thirty checkboxes.
  • Cumulative. A named permission – access to sensitive fields, for instance – adds to the role without replacing it. It is granted and withdrawn separately, and its grant is dated and logged.
  • Capped. The class of the API key bounds the result. A key meant for technical operations does not reach a contact record, even if it carries a role that would allow it: the capability is stored, it is simply never exercised. Creating a key with a capability outside its class is refused, with the list of rejected capabilities.
  • Verifiable. One dedicated call returns what the key can effectively do: the intersection of what it carries and what its class allows.
  • Technical / data separation. Platform operating roles run the sends and administer the technical side; none of them reads a record, an identifiable event, a message or an identifiable export.
  • No impersonation. A platform role cannot borrow a customer user’s account, or “view as” them.
  • Indistinguishable refusals. Access that has not been granted returns “not found”, never “forbidden”: whether someone has access to people cannot be observed from the outside.
  • Every access is logged, and a per-data-subject export returns everything that was done to that person, by whom and when.
Why an interface must ask what it may do

A console that builds its navigation on the capabilities requested when a key was created shows screens that will answer “not found”. Missivia therefore exposes the effective authorisation – the intersection of the key’s capabilities and its class ceiling – and navigation is built on that answer. What the screen offers is what the server will grant.

The same principle applies to the human actor: actions taken from an administration interface are attached to the person who triggered them, through a signed header, in addition to the key used. That header grants no right; it names, it does not authorise.

The matrix, in plain words

What each role reaches, written once and enforced everywhere.

Pick a role: the matrix highlights what it reaches.
What each role reaches. “Read” means consultation without modification; “—” means the operation does not exist for that role. The first three columns are roles of the customer, who is the data controller; the last two are platform operating roles.
What is requested Data administrator Editor Viewer Operations Technical administration
Records, histories, identifiable exportsYesYesRead——
Sensitive fields of a recordWith a named permission, and logged——
Consents and data-subject rightsYesYesRead——
Lists, segments, journeys, campaignsYesYesReadYes—
Templates, files, public pages, webhooksYesYesReadYesYes
StatisticsExact valuesExact valuesExact valuesAggregates, masked below thresholdAggregates, masked below threshold
Retention periods, full export, permanent deletionYes————
Keys, domains, integrations, job healthYes———Yes
Audit logWith identitiesWith identitiesWith identitiesWithout identitiesWithout identities

Data administrator. Records, consents and data-subject rights, retention periods, full export and permanent deletion. Sensitive fields still require a named permission, and every read it allows is logged.

Editor. Works on the content: contacts, lists, segments, journeys, campaigns, templates and public pages. Sets neither retention periods, nor keys, nor domains.

Viewer. Consults without modifying, with exact statistical values, and reads the audit log with identities.

Operations (platform). Runs the sends – lists, segments, journeys, campaigns – without ever reaching a record. GET /v1/contacts/ctc_… answers 404 not found, never “forbidden”; the refusal is logged, and no read entry accompanies it. Counters are aggregates, masked below twenty people.

Technical administration. Domains, keys, integrations and job health. Reads no record, no message, no identifiable export: the same 404 not found. Cannot grant itself a data role.

The restriction is published in the API contract, operation by operation: it can be read before it is tested, and an automated check fails if an operation serving personal data is added without being covered.

How to read this matrix
  • “Read” means consultation without modification.
  • “—” means the operation does not exist for that role: the call answers “not found”.
  • The first three columns are roles of the customer, who is the data controller; the last two are platform operating roles.
  • “Aggregates, masked below threshold”: a counter covering fewer than twenty people is masked, and so are the rates that could be derived from it. A small group points back to individuals.
  • A named permission never moves a column: it opens a row, for a designated person, and every read it allows is logged.

Users, keys and sessions

Two ways in – an authenticated person, or an API key – and a single way to be authorised.

  • Two-factor authentication for everyone, with no exception by role; single sign-on through the customer’s identity provider is available.
  • An API key carries explicit capabilities, a class, and nothing implicit: the secret is never returned after creation, revocation is immediate.
  • Restriction by IP address or range is available, together with rate limiting of authentication failures per origin.
  • Test keys and production keys are distinct and visibly prefixed; a test key sends nothing to the outside world.
  • Every key creation, change and revocation is written to the audit log, with the human actor when there is one.
  • Console keys: a key marked “console” requires, on every call, the signed header that names the person. An action taken from the console therefore always carries its actor. The marking is set on creation and never changes.
  • No cross-space access: a platform account sees no space by default. It only enters one through a membership with a platform role, at the invitation of that space’s administrator. The role of a new platform account is granted in a separate second step, never by the person themselves or by the person who invited them.
  • Masked addresses in the console for roles that lack the capability to reveal them; for the others, revealing is an explicit action, logged before display.
  • Single sign-on linking: the provider’s identity is pinned to the account at the first link, which requires an address verified by the provider; an identity already linked to another account is refused. A return without a second factor attested by the provider goes through the console’s two-factor authentication.
Least privilege, in practice
  • An integration that only ingests contacts gets contact writes, nothing else: no templates, no exports, no statistics.
  • A technical operations key administers domains, quotas and job health; it reads no record, no message, no identifiable export.
  • A key meant to run sends drives lists, journeys and campaigns, and only ever sees aggregate statistics, masked below the threshold.
  • A capability requested outside the key’s class is not silently ignored: creation is refused, and the answer names the rejected capabilities.