Skip to main content
Authentication is one exchange: you hold a long-lived API key (maat_partner_…), you swap it at POST /v1/token for a short-lived bearer token that is scoped to exactly one Maat workspace, and you send that token in the Authorization header on every call. That is the whole model.
All examples in these docs use https://maatstays.wtf/v1 — the only environment this contract is published against today. Which tenancy your requests land in is decided by your key, never by the hostname.

The key

  • Your key looks like maat_partner_a1b2c3d4e5f60718_… and is shown once, when it is issued. Store it in a secret manager; we hold only a hash and cannot show it again.
  • Every key is issued for exactly one workspace — a workspace is one tenancy on Maat, the container a portfolio’s properties and calendars live in — and is either a sandbox key or a production key. Nothing you send can change that — see Sandbox.
  • The key opens exactly one door: the token exchange. It never authenticates a contract endpoint directly.
  • The exchange takes an empty body, on purpose. The workspace, the environment and your identity all come from the key itself; anything you put in the body is ignored.

The token

  • Tokens live for fifteen minutes by default and never more than an hour (expires_in tells you exactly). Mint one per batch of work, not one per request — the exchange has its own, separate rate ceiling.
  • Store the key, mint tokens from it. Never persist a token, and never send one as a cookie or in a query string — the API reads the Authorization header and nothing else.
  • Revocation does not wait for expiry. We re-check the key behind a token on every request, so revoking a key kills its outstanding tokens on their next call.

When it is refused

A refused exchange is one indistinguishable 401 whether the key is unknown, revoked, or the secret is wrong — telling those apart would let anyone probe which credentials exist. If a key that worked yesterday is refused today, talk to your Maat contact; the answer is on our side.
The token endpoint is the one place in this API that answers refusals with a plain { "message": … } body rather than a problem document. Every other response is application/problem+json — see Errors.