Skip to content

Address book ​

Routes are relative to <PUBLIC_BASE_URL>; request and response bodies are JSON unless stated otherwise. Authentication, scopes, pagination and the error format are described in the REST API guides.

GET /api/v1/contacts ​

List the address book

The merged view of the member: shared layer and personal layer side by side, each entry resolved into the profile a composed message would use. Filter by kind (address or domain), layer, q; resolve from the point of view of a mailboxId. Cursor-paginated.

Access — Member session or API key with scope contacts:read.

Parameters

NameInTypeRequired
kindquery"contact" | "domain"no
qquerystringno
layerquery"all" | "shared" | "member"no
mailboxIdquerystringno
cursorquerystringno
limitqueryintegerno

Responses

200 — A page of entries.

FieldTypeRequired
itemsobject[]yes
nextCursorstring | nullyes
JSON Schema
json
{
  "type": "object",
  "properties": {
    "items": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "kind": { "type": "string", "enum": [ "contact", "domain" ] },
          "value": { "type": "string", "minLength": 1, "maxLength": 320 },
          "shared": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "id": { "type": "string" },
                  "displayName": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
                  "language": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
                  "status": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
                  "orgNotes": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
                  "updatedAt": { "type": "string" }
                },
                "required": [
                  "id",
                  "displayName",
                  "language",
                  "status",
                  "orgNotes",
                  "updatedAt"
                ],
                "additionalProperties": false
              },
              { "type": "null" }
            ]
          },
          "member": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "id": { "type": "string" },
                  "formality": {
                    "anyOf": [
                      { "type": "string", "enum": [ "tu", "vous" ] },
                      { "type": "null" }
                    ]
                  },
                  "tone": {
                    "anyOf": [
                      {
                        "type": "string",
                        "enum": [ "formal", "neutral", "casual" ]
                      },
                      { "type": "null" }
                    ]
                  },
                  "language": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
                  "signatureId": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
                  "personalNotes": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
                  "updatedAt": { "type": "string" }
                },
                "required": [
                  "id",
                  "formality",
                  "tone",
                  "language",
                  "signatureId",
                  "personalNotes",
                  "updatedAt"
                ],
                "additionalProperties": false
              },
              { "type": "null" }
            ]
          },
          "memberByMailbox": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": { "type": "string" },
                "formality": {
                  "anyOf": [
                    { "type": "string", "enum": [ "tu", "vous" ] },
                    { "type": "null" }
                  ]
                },
                "tone": {
                  "anyOf": [
                    {
                      "type": "string",
                      "enum": [ "formal", "neutral", "casual" ]
                    },
                    { "type": "null" }
                  ]
                },
                "language": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
                "signatureId": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
                "personalNotes": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
                "updatedAt": { "type": "string" },
                "mailboxId": { "type": "string" }
              },
              "required": [
                "id",
                "formality",
                "tone",
                "language",
                "signatureId",
                "personalNotes",
                "updatedAt",
                "mailboxId"
              ],
              "additionalProperties": false
            }
          },
          "resolved": {
            "type": "object",
            "properties": {
              "formality": {
                "anyOf": [
                  { "type": "string", "enum": [ "tu", "vous" ] },
                  { "type": "null" }
                ]
              },
              "tone": {
                "anyOf": [
                  {
                    "type": "string",
                    "enum": [ "formal", "neutral", "casual" ]
                  },
                  { "type": "null" }
                ]
              },
              "language": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
              "displayName": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
              "signatureText": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
              "signatureId": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
              "status": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
              "notes": { "type": "array", "items": { "type": "string" } }
            },
            "required": [
              "formality",
              "tone",
              "language",
              "displayName",
              "signatureText",
              "signatureId",
              "status",
              "notes"
            ],
            "additionalProperties": false
          },
          "sources": {
            "type": "object",
            "properties": {
              "formality": {
                "anyOf": [
                  {
                    "type": "string",
                    "enum": [
                      "org_default",
                      "shared_domain",
                      "shared_contact",
                      "member_default",
                      "member_mailbox_default",
                      "member_domain",
                      "member_contact",
                      "member_mailbox_domain",
                      "member_mailbox_contact"
                    ]
                  },
                  { "type": "null" }
                ]
              },
              "tone": {
                "anyOf": [
                  {
                    "type": "string",
                    "enum": [
                      "org_default",
                      "shared_domain",
                      "shared_contact",
                      "member_default",
                      "member_mailbox_default",
                      "member_domain",
                      "member_contact",
                      "member_mailbox_domain",
                      "member_mailbox_contact"
                    ]
                  },
                  { "type": "null" }
                ]
              },
              "language": {
                "anyOf": [
                  {
                    "type": "string",
                    "enum": [
                      "org_default",
                      "shared_domain",
                      "shared_contact",
                      "member_default",
                      "member_mailbox_default",
                      "member_domain",
                      "member_contact",
                      "member_mailbox_domain",
                      "member_mailbox_contact"
                    ]
                  },
                  { "type": "null" }
                ]
              },
              "displayName": {
                "anyOf": [
                  {
                    "type": "string",
                    "enum": [
                      "org_default",
                      "shared_domain",
                      "shared_contact",
                      "member_default",
                      "member_mailbox_default",
                      "member_domain",
                      "member_contact",
                      "member_mailbox_domain",
                      "member_mailbox_contact"
                    ]
                  },
                  { "type": "null" }
                ]
              },
              "signatureText": {
                "anyOf": [
                  {
                    "type": "string",
                    "enum": [
                      "org_default",
                      "shared_domain",
                      "shared_contact",
                      "member_default",
                      "member_mailbox_default",
                      "member_domain",
                      "member_contact",
                      "member_mailbox_domain",
                      "member_mailbox_contact"
                    ]
                  },
                  { "type": "null" }
                ]
              },
              "status": {
                "anyOf": [
                  {
                    "type": "string",
                    "enum": [
                      "org_default",
                      "shared_domain",
                      "shared_contact",
                      "member_default",
                      "member_mailbox_default",
                      "member_domain",
                      "member_contact",
                      "member_mailbox_domain",
                      "member_mailbox_contact"
                    ]
                  },
                  { "type": "null" }
                ]
              },
              "notes": {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "org_default",
                    "shared_domain",
                    "shared_contact",
                    "member_default",
                    "member_mailbox_default",
                    "member_domain",
                    "member_contact",
                    "member_mailbox_domain",
                    "member_mailbox_contact"
                  ]
                }
              }
            },
            "required": [
              "formality",
              "tone",
              "language",
              "displayName",
              "signatureText",
              "status",
              "notes"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "kind",
          "value",
          "shared",
          "member",
          "memberByMailbox",
          "resolved",
          "sources"
        ],
        "additionalProperties": false
      }
    },
    "nextCursor": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }
  },
  "required": [ "items", "nextCursor" ],
  "additionalProperties": false
}

400 — The request does not match its schema.

401 — No valid session or API key (auth.unauthenticated).

403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).

429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.

Error codes — request.bad_request, contacts.mailbox_not_found. The common codes (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) apply to every route; see Errors.

DELETE /api/v1/contacts ​

Remove a card

Removes the card of kind/value in the given layer (default member). For the member layer, mailboxId targets the card of that mailbox; without it, only the global card goes and per-mailbox cards survive. The shared layer requires the administrator role (403).

Access — Member session or API key with scope contacts:write.

Parameters

NameInTypeRequired
kindquery"contact" | "domain"yes
valuequerystringyes
layerquery"member" | "shared"no
mailboxIdquerystringno

Responses

204 — Removed.

400 — The request does not match its schema.

401 — No valid session or API key (auth.unauthenticated).

403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).

429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.

Error codes — request.bad_request, auth.forbidden, contacts.invalid_value, contacts.mailbox_not_found, contacts.not_found. The common codes (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) apply to every route; see Errors.

PUT /api/v1/contacts/member ​

Save my card for a contact

Upserts the member’s relational card (formality, tone, language, signature, notes) for an address or a domain. With mailboxId, the card is specific to that mailbox and takes precedence over the global one. Only the fields present are written; null clears one. Returns the full merged entry.

Access — Member session or API key with scope contacts:write.

Request body (application/json)

FieldTypeRequired
kind"contact" | "domain"yes
valuestringyes
mailboxIdstringno
formality"tu" | "vous" | nullno
tone"formal" | "neutral" | "casual" | nullno
languagestring | nullno
signatureIdstring | nullno
personalNotesstring | nullno
JSON Schema
json
{
  "type": "object",
  "properties": {
    "kind": { "type": "string", "enum": [ "contact", "domain" ] },
    "value": { "type": "string", "minLength": 1, "maxLength": 320 },
    "mailboxId": { "type": "string" },
    "formality": {
      "anyOf": [ { "type": "string", "enum": [ "tu", "vous" ] }, { "type": "null" } ]
    },
    "tone": {
      "anyOf": [
        { "type": "string", "enum": [ "formal", "neutral", "casual" ] },
        { "type": "null" }
      ]
    },
    "language": {
      "anyOf": [
        { "type": "string", "pattern": "^[a-z]{2}(-[A-Za-z0-9]{2,8})?$" },
        { "type": "null" }
      ]
    },
    "signatureId": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
    "personalNotes": { "anyOf": [ { "type": "string", "maxLength": 4000 }, { "type": "null" } ] }
  },
  "required": [ "kind", "value" ]
}

Responses

200 — The merged entry.

FieldTypeRequired
entryobjectyes
JSON Schema
json
{
  "type": "object",
  "properties": {
    "entry": {
      "type": "object",
      "properties": {
        "kind": { "type": "string", "enum": [ "contact", "domain" ] },
        "value": { "type": "string", "minLength": 1, "maxLength": 320 },
        "shared": {
          "anyOf": [
            {
              "type": "object",
              "properties": {
                "id": { "type": "string" },
                "displayName": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
                "language": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
                "status": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
                "orgNotes": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
                "updatedAt": { "type": "string" }
              },
              "required": [
                "id",
                "displayName",
                "language",
                "status",
                "orgNotes",
                "updatedAt"
              ],
              "additionalProperties": false
            },
            { "type": "null" }
          ]
        },
        "member": {
          "anyOf": [
            {
              "type": "object",
              "properties": {
                "id": { "type": "string" },
                "formality": {
                  "anyOf": [
                    { "type": "string", "enum": [ "tu", "vous" ] },
                    { "type": "null" }
                  ]
                },
                "tone": {
                  "anyOf": [
                    {
                      "type": "string",
                      "enum": [ "formal", "neutral", "casual" ]
                    },
                    { "type": "null" }
                  ]
                },
                "language": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
                "signatureId": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
                "personalNotes": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
                "updatedAt": { "type": "string" }
              },
              "required": [
                "id",
                "formality",
                "tone",
                "language",
                "signatureId",
                "personalNotes",
                "updatedAt"
              ],
              "additionalProperties": false
            },
            { "type": "null" }
          ]
        },
        "memberByMailbox": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "id": { "type": "string" },
              "formality": {
                "anyOf": [
                  { "type": "string", "enum": [ "tu", "vous" ] },
                  { "type": "null" }
                ]
              },
              "tone": {
                "anyOf": [
                  {
                    "type": "string",
                    "enum": [ "formal", "neutral", "casual" ]
                  },
                  { "type": "null" }
                ]
              },
              "language": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
              "signatureId": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
              "personalNotes": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
              "updatedAt": { "type": "string" },
              "mailboxId": { "type": "string" }
            },
            "required": [
              "id",
              "formality",
              "tone",
              "language",
              "signatureId",
              "personalNotes",
              "updatedAt",
              "mailboxId"
            ],
            "additionalProperties": false
          }
        },
        "resolved": {
          "type": "object",
          "properties": {
            "formality": {
              "anyOf": [
                { "type": "string", "enum": [ "tu", "vous" ] },
                { "type": "null" }
              ]
            },
            "tone": {
              "anyOf": [
                { "type": "string", "enum": [ "formal", "neutral", "casual" ] },
                { "type": "null" }
              ]
            },
            "language": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
            "displayName": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
            "signatureText": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
            "signatureId": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
            "status": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
            "notes": { "type": "array", "items": { "type": "string" } }
          },
          "required": [
            "formality",
            "tone",
            "language",
            "displayName",
            "signatureText",
            "signatureId",
            "status",
            "notes"
          ],
          "additionalProperties": false
        },
        "sources": {
          "type": "object",
          "properties": {
            "formality": {
              "anyOf": [
                {
                  "type": "string",
                  "enum": [
                    "org_default",
                    "shared_domain",
                    "shared_contact",
                    "member_default",
                    "member_mailbox_default",
                    "member_domain",
                    "member_contact",
                    "member_mailbox_domain",
                    "member_mailbox_contact"
                  ]
                },
                { "type": "null" }
              ]
            },
            "tone": {
              "anyOf": [
                {
                  "type": "string",
                  "enum": [
                    "org_default",
                    "shared_domain",
                    "shared_contact",
                    "member_default",
                    "member_mailbox_default",
                    "member_domain",
                    "member_contact",
                    "member_mailbox_domain",
                    "member_mailbox_contact"
                  ]
                },
                { "type": "null" }
              ]
            },
            "language": {
              "anyOf": [
                {
                  "type": "string",
                  "enum": [
                    "org_default",
                    "shared_domain",
                    "shared_contact",
                    "member_default",
                    "member_mailbox_default",
                    "member_domain",
                    "member_contact",
                    "member_mailbox_domain",
                    "member_mailbox_contact"
                  ]
                },
                { "type": "null" }
              ]
            },
            "displayName": {
              "anyOf": [
                {
                  "type": "string",
                  "enum": [
                    "org_default",
                    "shared_domain",
                    "shared_contact",
                    "member_default",
                    "member_mailbox_default",
                    "member_domain",
                    "member_contact",
                    "member_mailbox_domain",
                    "member_mailbox_contact"
                  ]
                },
                { "type": "null" }
              ]
            },
            "signatureText": {
              "anyOf": [
                {
                  "type": "string",
                  "enum": [
                    "org_default",
                    "shared_domain",
                    "shared_contact",
                    "member_default",
                    "member_mailbox_default",
                    "member_domain",
                    "member_contact",
                    "member_mailbox_domain",
                    "member_mailbox_contact"
                  ]
                },
                { "type": "null" }
              ]
            },
            "status": {
              "anyOf": [
                {
                  "type": "string",
                  "enum": [
                    "org_default",
                    "shared_domain",
                    "shared_contact",
                    "member_default",
                    "member_mailbox_default",
                    "member_domain",
                    "member_contact",
                    "member_mailbox_domain",
                    "member_mailbox_contact"
                  ]
                },
                { "type": "null" }
              ]
            },
            "notes": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "org_default",
                  "shared_domain",
                  "shared_contact",
                  "member_default",
                  "member_mailbox_default",
                  "member_domain",
                  "member_contact",
                  "member_mailbox_domain",
                  "member_mailbox_contact"
                ]
              }
            }
          },
          "required": [
            "formality",
            "tone",
            "language",
            "displayName",
            "signatureText",
            "status",
            "notes"
          ],
          "additionalProperties": false
        }
      },
      "required": [
        "kind",
        "value",
        "shared",
        "member",
        "memberByMailbox",
        "resolved",
        "sources"
      ],
      "additionalProperties": false
    }
  },
  "required": [ "entry" ],
  "additionalProperties": false
}

400 — The request does not match its schema.

401 — No valid session or API key (auth.unauthenticated).

403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).

429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.

Error codes — request.bad_request, contacts.invalid_value, contacts.mailbox_not_found, contacts.unknown_signature. The common codes (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) apply to every route; see Errors.

Example request

json
{
  "kind": "contact",
  "value": "bob@example.test",
  "formality": "tu",
  "tone": "casual",
  "language": "fr"
}

DELETE /api/v1/contacts/member ​

Remove my card for a contact

The historical alias of DELETE /api/v1/contacts with layer forced to member. Same handler, same rules.

Access — Member session or API key with scope contacts:write.

Parameters

NameInTypeRequired
kindquery"contact" | "domain"yes
valuequerystringyes
layerquery"member" | "shared"no
mailboxIdquerystringno

Responses

204 — Removed.

400 — The request does not match its schema.

401 — No valid session or API key (auth.unauthenticated).

403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).

429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.

Error codes — request.bad_request, contacts.invalid_value, contacts.mailbox_not_found, contacts.not_found. The common codes (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) apply to every route; see Errors.

PUT /api/v1/contacts/shared ​

Save the shared card of a contact

Upserts the organisation’s card (display name, language, status, notes) for an address or a domain. Administrators only. Returns the entry as seen by the calling administrator.

Access — Member session or API key with scope admin:write (administrator role required).

Request body (application/json)

FieldTypeRequired
kind"contact" | "domain"yes
valuestringyes
displayNamestring | nullno
languagestring | nullno
statusstring | nullno
orgNotesstring | nullno
JSON Schema
json
{
  "type": "object",
  "properties": {
    "kind": { "type": "string", "enum": [ "contact", "domain" ] },
    "value": { "type": "string", "minLength": 1, "maxLength": 320 },
    "displayName": { "anyOf": [ { "type": "string", "maxLength": 200 }, { "type": "null" } ] },
    "language": {
      "anyOf": [
        { "type": "string", "pattern": "^[a-z]{2}(-[A-Za-z0-9]{2,8})?$" },
        { "type": "null" }
      ]
    },
    "status": { "anyOf": [ { "type": "string", "maxLength": 40 }, { "type": "null" } ] },
    "orgNotes": { "anyOf": [ { "type": "string", "maxLength": 4000 }, { "type": "null" } ] }
  },
  "required": [ "kind", "value" ]
}

Responses

200 — The merged entry.

FieldTypeRequired
entryobjectyes

Same schema as PUT /api/v1/contacts/member.

400 — The request does not match its schema.

401 — No valid session or API key (auth.unauthenticated).

403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).

429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.

Error codes — request.bad_request, contacts.invalid_value. The common codes (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) apply to every route; see Errors.

DELETE /api/v1/contacts/shared ​

Remove the shared card of a contact

Administrators only. Personal cards of the members are untouched.

Access — Member session or API key with scope admin:write (administrator role required).

Parameters

NameInTypeRequired
kindquery"contact" | "domain"yes
valuequerystringyes
layerquery"member" | "shared"no
mailboxIdquerystringno

Responses

204 — Removed.

400 — The request does not match its schema.

401 — No valid session or API key (auth.unauthenticated).

403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).

429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.

Error codes — request.bad_request, contacts.invalid_value, contacts.not_found. The common codes (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) apply to every route; see Errors.

GET /api/v1/contacts/defaults ​

Read my profile defaults

What a message uses when no card says otherwise: the member’s defaults, or those of one of their mailboxes with mailboxId. The organisation’s defaults are read elsewhere by administrators.

Access — Member session or API key with scope contacts:read.

Parameters

NameInTypeRequired
mailboxIdquerystringno

Responses

200 — The defaults.

FieldTypeRequired
defaultsobjectyes
JSON Schema
json
{
  "type": "object",
  "properties": {
    "defaults": {
      "type": "object",
      "properties": {
        "formality": {
          "anyOf": [ { "type": "string", "enum": [ "tu", "vous" ] }, { "type": "null" } ]
        },
        "tone": {
          "anyOf": [
            { "type": "string", "enum": [ "formal", "neutral", "casual" ] },
            { "type": "null" }
          ]
        },
        "language": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
        "signatureId": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }
      },
      "required": [ "formality", "tone", "language", "signatureId" ],
      "additionalProperties": false
    }
  },
  "required": [ "defaults" ],
  "additionalProperties": false
}

400 — The request does not match its schema.

401 — No valid session or API key (auth.unauthenticated).

403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).

429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.

Error codes — request.bad_request, contacts.mailbox_not_found. The common codes (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) apply to every route; see Errors.

PUT /api/v1/contacts/defaults ​

Update my profile defaults

Partial: only the fields present are written, null clears one. With mailboxId, the defaults of that mailbox.

Access — Member session or API key with scope contacts:write.

Parameters

NameInTypeRequired
mailboxIdquerystringno

Request body (application/json)

FieldTypeRequired
formality"tu" | "vous" | nullno
tone"formal" | "neutral" | "casual" | nullno
languagestring | nullno
signatureIdstring | nullno
JSON Schema
json
{
  "type": "object",
  "properties": {
    "formality": {
      "anyOf": [ { "type": "string", "enum": [ "tu", "vous" ] }, { "type": "null" } ]
    },
    "tone": {
      "anyOf": [
        { "type": "string", "enum": [ "formal", "neutral", "casual" ] },
        { "type": "null" }
      ]
    },
    "language": {
      "anyOf": [
        { "type": "string", "pattern": "^[a-z]{2}(-[A-Za-z0-9]{2,8})?$" },
        { "type": "null" }
      ]
    },
    "signatureId": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }
  }
}

Responses

200 — The defaults, updated.

FieldTypeRequired
defaultsobjectyes

Same schema as GET /api/v1/contacts/defaults.

400 — The request does not match its schema.

401 — No valid session or API key (auth.unauthenticated).

403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).

429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.

Error codes — request.bad_request, contacts.mailbox_not_found, contacts.not_found. The common codes (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) apply to every route; see Errors.

POST /api/v1/contacts/resolve ​

Resolve recipient profiles

Exactly what a composed message would see for each address: the resolved profile and where each field came from. A POST because a batch of addresses does not belong in a URL. mailboxId is the sending mailbox; without it, the member’s global defaults apply.

Access — Member session or API key with scope contacts:read.

Request body (application/json)

FieldTypeRequired
emailsstring[]yes
mailboxIdstringno
JSON Schema
json
{
  "type": "object",
  "properties": {
    "emails": {
      "minItems": 1,
      "maxItems": 100,
      "type": "array",
      "items": { "type": "string", "minLength": 1, "maxLength": 320 }
    },
    "mailboxId": { "type": "string" }
  },
  "required": [ "emails" ]
}

Responses

200 — The profiles and their sources, by address.

FieldTypeRequired
profilesobjectyes
sourcesobjectyes
JSON Schema
json
{
  "type": "object",
  "properties": {
    "profiles": {
      "type": "object",
      "propertyNames": { "type": "string" },
      "additionalProperties": {
        "type": "object",
        "properties": {
          "formality": {
            "anyOf": [
              { "type": "string", "enum": [ "tu", "vous" ] },
              { "type": "null" }
            ]
          },
          "tone": {
            "anyOf": [
              { "type": "string", "enum": [ "formal", "neutral", "casual" ] },
              { "type": "null" }
            ]
          },
          "language": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
          "displayName": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
          "signatureText": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
          "signatureId": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
          "status": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
          "notes": { "type": "array", "items": { "type": "string" } }
        },
        "required": [
          "formality",
          "tone",
          "language",
          "displayName",
          "signatureText",
          "signatureId",
          "status",
          "notes"
        ],
        "additionalProperties": false
      }
    },
    "sources": {
      "type": "object",
      "propertyNames": { "type": "string" },
      "additionalProperties": {
        "type": "object",
        "properties": {
          "formality": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "org_default",
                  "shared_domain",
                  "shared_contact",
                  "member_default",
                  "member_mailbox_default",
                  "member_domain",
                  "member_contact",
                  "member_mailbox_domain",
                  "member_mailbox_contact"
                ]
              },
              { "type": "null" }
            ]
          },
          "tone": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "org_default",
                  "shared_domain",
                  "shared_contact",
                  "member_default",
                  "member_mailbox_default",
                  "member_domain",
                  "member_contact",
                  "member_mailbox_domain",
                  "member_mailbox_contact"
                ]
              },
              { "type": "null" }
            ]
          },
          "language": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "org_default",
                  "shared_domain",
                  "shared_contact",
                  "member_default",
                  "member_mailbox_default",
                  "member_domain",
                  "member_contact",
                  "member_mailbox_domain",
                  "member_mailbox_contact"
                ]
              },
              { "type": "null" }
            ]
          },
          "displayName": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "org_default",
                  "shared_domain",
                  "shared_contact",
                  "member_default",
                  "member_mailbox_default",
                  "member_domain",
                  "member_contact",
                  "member_mailbox_domain",
                  "member_mailbox_contact"
                ]
              },
              { "type": "null" }
            ]
          },
          "signatureText": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "org_default",
                  "shared_domain",
                  "shared_contact",
                  "member_default",
                  "member_mailbox_default",
                  "member_domain",
                  "member_contact",
                  "member_mailbox_domain",
                  "member_mailbox_contact"
                ]
              },
              { "type": "null" }
            ]
          },
          "status": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "org_default",
                  "shared_domain",
                  "shared_contact",
                  "member_default",
                  "member_mailbox_default",
                  "member_domain",
                  "member_contact",
                  "member_mailbox_domain",
                  "member_mailbox_contact"
                ]
              },
              { "type": "null" }
            ]
          },
          "notes": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "org_default",
                "shared_domain",
                "shared_contact",
                "member_default",
                "member_mailbox_default",
                "member_domain",
                "member_contact",
                "member_mailbox_domain",
                "member_mailbox_contact"
              ]
            }
          }
        },
        "required": [
          "formality",
          "tone",
          "language",
          "displayName",
          "signatureText",
          "status",
          "notes"
        ],
        "additionalProperties": false
      }
    }
  },
  "required": [ "profiles", "sources" ],
  "additionalProperties": false
}

400 — The request does not match its schema.

401 — No valid session or API key (auth.unauthenticated).

403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).

429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.

Error codes — request.bad_request, contacts.mailbox_not_found. The common codes (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) apply to every route; see Errors.

POST /api/v1/contacts/import/preview ​

Preview a CSV import

Reads the CSV, detects the delimiter, proposes a column mapping and returns sample rows. Changes nothing. A POST because the file is in the body.

Access — Member session or API key with scope contacts:read.

Request body (application/json)

FieldTypeRequired
csvstringyes
delimiter"," | ";" | "\t" | "auto"no
JSON Schema
json
{
  "type": "object",
  "properties": {
    "csv": { "type": "string", "minLength": 1, "maxLength": 2097152 },
    "delimiter": { "default": "auto", "type": "string", "enum": [ ",", ";", "\t", "auto" ] }
  },
  "required": [ "csv" ]
}

Responses

200 — The proposed mapping and samples.

FieldTypeRequired
columnsstring[]yes
samplestring[][]yes
suggestedMappingobjectyes
rowCountintegeryes
delimiter"," | ";" | "\t"yes
JSON Schema
json
{
  "type": "object",
  "properties": {
    "columns": { "type": "array", "items": { "type": "string" } },
    "sample": {
      "type": "array",
      "items": { "type": "array", "items": { "type": "string" } }
    },
    "suggestedMapping": {
      "type": "object",
      "properties": {
        "email": { "type": "string", "minLength": 1 },
        "displayName": { "type": "string", "minLength": 1 },
        "formality": { "type": "string", "minLength": 1 },
        "tone": { "type": "string", "minLength": 1 },
        "language": { "type": "string", "minLength": 1 },
        "notes": { "type": "string", "minLength": 1 }
      },
      "additionalProperties": false
    },
    "rowCount": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 },
    "delimiter": { "type": "string", "enum": [ ",", ";", "\t" ] }
  },
  "required": [ "columns", "sample", "suggestedMapping", "rowCount", "delimiter" ],
  "additionalProperties": false
}

400 — The request does not match its schema.

401 — No valid session or API key (auth.unauthenticated).

403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).

429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.

Error codes — request.bad_request, contacts.invalid_csv. The common codes (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) apply to every route; see Errors.

POST /api/v1/contacts/import ​

Import contacts from a CSV

Writes the rows into the member layer (optionally for one mailbox) or, for administrators, into the shared layer. Existing cards are updated or skipped according to onConflict; the response counts what was created, updated and skipped, with the reason per skipped row.

Access — Member session or API key with scope contacts:write.

Request body (application/json)

FieldTypeRequired
csvstringyes
delimiter"," | ";" | "\t" | "auto"no
mappingobjectyes
layer"member" | "shared"yes
mailboxIdstringno
onConflict"skip" | "update"yes
JSON Schema
json
{
  "type": "object",
  "properties": {
    "csv": { "type": "string", "minLength": 1, "maxLength": 2097152 },
    "delimiter": { "default": "auto", "type": "string", "enum": [ ",", ";", "\t", "auto" ] },
    "mapping": {
      "type": "object",
      "properties": {
        "email": { "type": "string", "minLength": 1 },
        "displayName": { "type": "string", "minLength": 1 },
        "formality": { "type": "string", "minLength": 1 },
        "tone": { "type": "string", "minLength": 1 },
        "language": { "type": "string", "minLength": 1 },
        "notes": { "type": "string", "minLength": 1 }
      },
      "required": [ "email" ]
    },
    "layer": { "type": "string", "enum": [ "member", "shared" ] },
    "mailboxId": { "type": "string" },
    "onConflict": { "type": "string", "enum": [ "skip", "update" ] }
  },
  "required": [ "csv", "mapping", "layer", "onConflict" ]
}

Responses

200 — The import report.

FieldTypeRequired
createdintegeryes
updatedintegeryes
skippedobject[]yes
JSON Schema
json
{
  "type": "object",
  "properties": {
    "created": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 },
    "updated": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 },
    "skipped": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "row": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "maximum": 9007199254740991
          },
          "reason": {
            "type": "string",
            "enum": [ "missing_email", "invalid_email", "duplicate" ]
          }
        },
        "required": [ "row", "reason" ],
        "additionalProperties": false
      }
    }
  },
  "required": [ "created", "updated", "skipped" ],
  "additionalProperties": false
}

400 — The request does not match its schema.

401 — No valid session or API key (auth.unauthenticated).

403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).

429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.

This operation accepts an Idempotency-Key header: replaying the same request with the same key returns the original response instead of acting twice. See Idempotency.

Error codes — request.bad_request, auth.forbidden, contacts.mailbox_not_found, contacts.invalid_csv, contacts.unknown_column. The common codes (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) apply to every route; see Errors.

POST /api/v1/contacts/enrichment/runs ​

Start an enrichment run

Reads the sent mail of one mailbox over lookbackDays and asks a model to propose cards for the most frequent correspondents. Queued, not done: 202 with the run id; poll its state, then read the suggestions.

Access — Member session or API key with scope contacts:write.

Request body (application/json)

FieldTypeRequired
mailboxIdstringyes
lookbackDaysintegerno
maxContactsintegerno
JSON Schema
json
{
  "type": "object",
  "properties": {
    "mailboxId": { "type": "string", "minLength": 1 },
    "lookbackDays": { "default": 365, "type": "integer", "minimum": 1, "maximum": 730 },
    "maxContacts": { "default": 50, "type": "integer", "minimum": 1, "maximum": 200 }
  },
  "required": [ "mailboxId" ]
}

Responses

202 — The run, queued.

FieldTypeRequired
runIdstringyes
JSON Schema
json
{
  "type": "object",
  "properties": { "runId": { "type": "string" } },
  "required": [ "runId" ],
  "additionalProperties": false
}

400 — The request does not match its schema.

401 — No valid session or API key (auth.unauthenticated).

403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).

429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.

This operation accepts an Idempotency-Key header: replaying the same request with the same key returns the original response instead of acting twice. See Idempotency.

Error codes — request.bad_request, contacts.mailbox_not_found. The common codes (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) apply to every route; see Errors.

GET /api/v1/contacts/enrichment/runs/{id} ​

Read an enrichment run

Its status and counters. A run of another member is a 404.

Access — Member session or API key with scope contacts:read.

Parameters

NameInTypeRequired
idpathstringyes

Responses

200 — The run.

FieldTypeRequired
idstringyes
mailboxIdstringyes
status"pending" | "running" | "done" | "failed"yes
startedAtstring | nullyes
finishedAtstring | nullyes
summaryobjectyes
errorstring | nullyes
JSON Schema
json
{
  "type": "object",
  "properties": {
    "id": { "type": "string" },
    "mailboxId": { "type": "string" },
    "status": { "type": "string", "enum": [ "pending", "running", "done", "failed" ] },
    "startedAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
    "finishedAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
    "summary": {
      "type": "object",
      "properties": {
        "contacts": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 },
        "withProposal": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 },
        "llmSkipped": { "type": "boolean" }
      },
      "required": [ "contacts", "withProposal", "llmSkipped" ],
      "additionalProperties": false
    },
    "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }
  },
  "required": [ "id", "mailboxId", "status", "startedAt", "finishedAt", "summary", "error" ],
  "additionalProperties": false
}

401 — No valid session or API key (auth.unauthenticated).

403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).

429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.

Error codes — contacts.enrichment_run_not_found. The common codes (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) apply to every route; see Errors.

GET /api/v1/contacts/suggestions ​

List contact suggestions

What enrichment proposed, most frequent correspondents first, cursor-paginated. status defaults to pending. Each item says whether the address already has a card, so the screen does not propose a duplicate.

Access — Member session or API key with scope contacts:read.

Parameters

NameInTypeRequired
mailboxIdquerystringno
statusquery"pending" | "accepted" | "rejected"no
cursorquerystringno
limitqueryintegerno

Responses

200 — A page of suggestions.

FieldTypeRequired
itemsobject[]yes
nextCursorstring | nullyes
JSON Schema
json
{
  "type": "object",
  "properties": {
    "items": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "mailboxId": { "type": "string" },
          "email": { "type": "string" },
          "displayName": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
          "evidence": {
            "type": "object",
            "properties": {
              "sentCount": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 },
              "receivedCount": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 },
              "lastAt": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }
            },
            "required": [ "sentCount", "receivedCount", "lastAt" ],
            "additionalProperties": false
          },
          "proposal": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "formality": { "type": "string", "enum": [ "tu", "vous" ] },
                  "tone": {
                    "type": "string",
                    "enum": [ "formal", "neutral", "casual" ]
                  },
                  "language": { "type": "string" },
                  "notes": { "type": "string" }
                },
                "additionalProperties": false
              },
              { "type": "null" }
            ]
          },
          "confidence": {
            "anyOf": [
              { "type": "number", "minimum": 0, "maximum": 1 },
              { "type": "null" }
            ]
          },
          "existing": {
            "type": "object",
            "properties": {
              "layer": {
                "anyOf": [
                  { "type": "string", "enum": [ "member", "shared" ] },
                  { "type": "null" }
                ]
              }
            },
            "required": [ "layer" ],
            "additionalProperties": false
          },
          "status": { "type": "string", "enum": [ "pending", "accepted", "rejected" ] },
          "createdAt": { "type": "string" }
        },
        "required": [
          "id",
          "mailboxId",
          "email",
          "displayName",
          "evidence",
          "proposal",
          "confidence",
          "existing",
          "status",
          "createdAt"
        ],
        "additionalProperties": false
      }
    },
    "nextCursor": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }
  },
  "required": [ "items", "nextCursor" ],
  "additionalProperties": false
}

400 — The request does not match its schema.

401 — No valid session or API key (auth.unauthenticated).

403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).

429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.

Error codes — request.bad_request, contacts.mailbox_not_found. The common codes (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) apply to every route; see Errors.

POST /api/v1/contacts/suggestions/accept-all ​

Accept every pending suggestion

Every pending suggestion of the mailbox becomes a card, as proposed, in the mailbox or member scope. Suggestions decided meanwhile are skipped; accepted counts those that actually switched.

Access — Member session or API key with scope contacts:write.

Request body (application/json)

FieldTypeRequired
mailboxIdstringyes
scope"mailbox" | "member"yes
JSON Schema
json
{
  "type": "object",
  "properties": {
    "mailboxId": { "type": "string", "minLength": 1 },
    "scope": { "type": "string", "enum": [ "mailbox", "member" ] }
  },
  "required": [ "mailboxId", "scope" ]
}

Responses

200 — How many were accepted.

FieldTypeRequired
acceptedintegeryes
JSON Schema
json
{
  "type": "object",
  "properties": {
    "accepted": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 }
  },
  "required": [ "accepted" ],
  "additionalProperties": false
}

400 — The request does not match its schema.

401 — No valid session or API key (auth.unauthenticated).

403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).

429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.

This operation accepts an Idempotency-Key header: replaying the same request with the same key returns the original response instead of acting twice. See Idempotency.

Error codes — request.bad_request, contacts.mailbox_not_found. The common codes (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) apply to every route; see Errors.

POST /api/v1/contacts/suggestions/{id}/accept ​

Accept a suggestion

The suggestion becomes a card of the member, with the proposed values or the edits given, in the mailbox or member scope. Decided once: a suggestion already accepted or rejected is a 404.

Access — Member session or API key with scope contacts:write.

Parameters

NameInTypeRequired
idpathstringyes

Request body (application/json)

FieldTypeRequired
scope"mailbox" | "member"yes
editsobjectno
JSON Schema
json
{
  "type": "object",
  "properties": {
    "scope": { "type": "string", "enum": [ "mailbox", "member" ] },
    "edits": {
      "type": "object",
      "properties": {
        "displayName": { "type": "string", "maxLength": 200 },
        "formality": { "type": "string", "enum": [ "tu", "vous" ] },
        "tone": { "type": "string", "enum": [ "formal", "neutral", "casual" ] },
        "language": { "type": "string", "pattern": "^[a-z]{2}(-[A-Za-z0-9]{2,8})?$" },
        "notes": { "type": "string", "maxLength": 4000 }
      }
    }
  },
  "required": [ "scope" ]
}

Responses

200 — The merged entry.

FieldTypeRequired
entryobjectyes

Same schema as PUT /api/v1/contacts/member.

400 — The request does not match its schema.

401 — No valid session or API key (auth.unauthenticated).

403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).

429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.

This operation accepts an Idempotency-Key header: replaying the same request with the same key returns the original response instead of acting twice. See Idempotency.

Error codes — request.bad_request, contacts.suggestion_not_found, contacts.mailbox_not_found, contacts.invalid_value, contacts.unknown_signature. The common codes (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) apply to every route; see Errors.

POST /api/v1/contacts/suggestions/{id}/reject ​

Reject a suggestion

The address will not be proposed again by later runs.

Access — Member session or API key with scope contacts:write.

Parameters

NameInTypeRequired
idpathstringyes

Responses

204 — Rejected.

401 — No valid session or API key (auth.unauthenticated).

403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).

429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.

Error codes — contacts.suggestion_not_found. The common codes (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) apply to every route; see Errors.

GET /api/v1/admin/contacts/defaults ​

Read the organisation writing defaults

Formality, tone, language and signature applied when neither the contact nor the member says otherwise.

Access — Member session or API key with scope admin:read (administrator role required).

Responses

200 — The defaults.

FieldTypeRequired
defaultsobjectyes

Same schema as GET /api/v1/contacts/defaults.

401 — No valid session or API key (auth.unauthenticated).

403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).

429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.

PUT /api/v1/admin/contacts/defaults ​

Set the organisation writing defaults

Three states per field: absent (unchanged), null (cleared), a value.

Access — Member session or API key with scope admin:write (administrator role required).

Request body (application/json)

FieldTypeRequired
formality"tu" | "vous" | nullno
tone"formal" | "neutral" | "casual" | nullno
languagestring | nullno
signatureIdstring | nullno

Same schema as PUT /api/v1/contacts/defaults.

Responses

200 — The defaults, after write.

FieldTypeRequired
defaultsobjectyes

Same schema as GET /api/v1/contacts/defaults.

400 — The request does not match its schema.

401 — No valid session or API key (auth.unauthenticated).

403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).

429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.

Error codes — request.bad_request. The common codes (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) apply to every route; see Errors.