> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.taktoria.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.taktoria.com/_mcp/server.

# Authentication

> Authenticate to the Taktoria API with a workspace API key or a signed-in session: every scope, what a session may do, app roles and subscriptions.

The API accepts an organization-scoped API key as a bearer token:

```
Authorization: Bearer tk_live_...
```

Create keys in **Settings → API keys**. The secret is shown once, at creation:
Taktoria stores only a SHA-256 hash of it, so a lost key must be rotated rather
than recovered.

## Scopes

A key carries a list of scopes, each `resource:verb`. `*` grants everything;
`resource:*` grants every verb on one resource, such as `launches:*`. The
key form in **Settings → API keys** offers exactly these, grouped by app.
`GET /me` needs none: any valid key may ask what it is. Nor does
`/me/notification-preferences`, how the key's maker is told about each kind
of notification: it is theirs alone.

Only a workspace admin can create a key, so a key's scopes are a workspace
admin's to give. The workspace settings a key can reach (the organization,
billing, email domains and partner domains, and app domains) are the ones only
workspace admins can change in the app.

| App       | Scope                                              | Grants                                                                                                 |
| --------- | -------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| Platform  | `apps:read` / `apps:write`                         | Which apps this workspace subscribes to, and which give new members a seat                             |
| Platform  | `members:read`                                     | Who is in the workspace                                                                                |
| Platform  | `auditLogs:read`                                   | Every change made in the workspace: who, what, when and from where                                     |
| Platform  | `seats:read` / `seats:write`                       | Which apps a person holds a seat on, and who is waiting for one                                        |
| Platform  | `appRoles:write`                                   | What a person may do in each app: Admin, Member or Viewer (read with `members:read`)                   |
| Platform  | `teams:read` / `teams:write`                       | Functional and organizational teams                                                                    |
| Platform  | `organization:read` / `organization:write`         | The workspace's name, address, fiscal year and sign-in policy                                          |
| Platform  | `billing:read` / `billing:write`                   | The subscription: the apps paid for, their seats, and monthly or yearly                                |
| Platform  | `domains:read` / `domains:write`                   | Email domains: joining automatically, closing the workspace, and who follows go links and joins events |
| Platform  | `appDomains:read` / `appDomains:write`             | Hostnames of the workspace's own that serve its apps                                                   |
| Platform  | `mcpConnections:read` / `mcpConnections:write`     | AI assistants connected through MCP, and whether the workspace allows them                             |
| Platform  | `slackConnections:read` / `slackConnections:write` | Slack accounts connected with Google, which let Slack commands act as their owners                     |
| LaunchCal | `launches:read` / `launches:write`                 | Launches, their dates, their status and going live                                                     |
| LaunchCal | `steps:read` / `steps:write`                       | Review steps, their decisions and nudges                                                               |
| LaunchCal | `insights:read` / `insights:write`                 | Insights recorded against a launch                                                                     |
| LaunchCal | `artifacts:read` / `artifacts:write`               | A launch's press release, dashboard and learnings, and the links kept beside them                      |
| LaunchCal | `templates:read` / `templates:write`               | Launch templates and their steps, which launches copy their steps from                                 |
| Dory      | `events:read` / `events:write`                     | Events, their codes and their settings                                                                 |
| Dory      | `questions:read` / `questions:write`               | Questions asked, answered and dismissed                                                                |
| Dory      | `polls:read` / `polls:write`                       | Polls, quizzes and their results                                                                       |
| Dory      | `participants:read`                                | Who took part, and the numbers an event produced, or all of them together                              |
| Go Links  | `golinks:read` / `golinks:write`                   | Short links, where they point, who may follow them and who may change them                             |
| Go Links  | `clicks:read`                                      | How often a link is followed, and when                                                                 |
| OKR Hub   | `objectives:read` / `objectives:write`             | Objectives and the quarter they belong to                                                              |
| OKR Hub   | `keyResults:read` / `keyResults:write`             | Key results and their targets                                                                          |
| OKR Hub   | `checkIns:read` / `checkIns:write`                 | Progress recorded against a key result                                                                 |
| OKR Hub   | `grades:read` / `grades:write`                     | The scorecard: grades set at a checkpoint, comments and who changed them                               |
| Memegen   | `memes:read` / `memes:write`                       | Memes, their captions and the feed                                                                     |
| Memegen   | `memeTemplates:read` / `memeTemplates:write`       | The backgrounds memes are made from                                                                    |

## Signed-in sessions

A signed-in person's session cookie authenticates too. A key acts for the
whole workspace, within its scopes; a session gets no more than the app would
give that person:

* **Reads** are filtered to what they may see. A private objective, and its
  key results, are there only for its owners, its team, whoever created it
  and the people holding one of its key results.
* **Writes** are refused with `403 read_only_session`, except on Dory's
  events, questions and polls, Go Links' links, Memegen's memes and
  templates, OKR Hub's objectives, key results, check-ins and grades, and
  LaunchCal, which check who is asking (below), and the person's own
  notification preferences (`PATCH /me/notification-preferences`) and MCP
  and Slack connections (their own; a workspace admin's, anybody's). Changing
  seats, apps, the workspace's settings, its
  subscription or its domains takes a key.
* The subscription, email domains, partner domains and app domains are read
  only by a workspace admin's session, as their settings pages are only a
  workspace admin's; anybody else gets `403 admin_required`.
* An app's endpoints answer only if the person holds a seat on the app
  (`403 seat_required`).
* A **Viewer** of an app reads it as a member would and changes nothing:
  every write scope of the app's resources is refused for their session with
  `403 viewer_only`, before the endpoint runs. See App roles, below.
* In a workspace that requires signing in with Google, a session made any
  other way is refused at every endpoint with `403 google_sign_in_required`.
  Keys are not a sign-in and are unaffected.
* A person in several workspaces picks one with the `X-Organization-Id`
  header; otherwise it is the workspace they last opened.

The difference shows in Dory. An event can be closed to a list of people who
may run it, and a session reaches only the events that person could run in
the app: an event with no list, one they are on (themselves, or through a
team on its list or a team inside one), one they created, or any event if
they are an app admin of Dory. The API and the app ask one rule for this, so the answer
is the same in both. Every other event is a `404` at every endpoint,
and is left out of `GET /dory/events`, exactly as an event in another
workspace would be. A key reaches every event in its workspace.

Go Links works as the Go Links app does. A session may create links, and
may change, publish, archive or delete the ones that person may edit: a link
that names no editors, one they are named on, one they created, or any link
if they are an app admin of Go Links. Any other link is `403 forbidden` to `PATCH` and
`DELETE`, though it is still listed and read. The editors a request names
must be members of the workspace, for a session and a key alike: anybody
else is a `422`, and nothing is saved. A draft's `url`, and any link's
`access.domains`, are `null` to a session that may not edit the link, and
`q` does not match a draft's destination for it. A key changes and sees
every link in its workspace.

Memegen takes a session's writes on the app's terms too. A session makes a
meme as that person, as the app's composer does: the captions are fitted to
the template's boxes, and a meme with no words is refused (`422`). It deletes
the person's own memes, and anybody's for an app admin of Memegen; anybody
else's is `403 forbidden`. The template library is Memegen's app admins', as
in the app: adding, uploading, changing or archiving a template takes an app
admin's session (`403 admin_required` otherwise), and archived templates and
one-off backgrounds are listed only for an app admin. A key keeps the API's own rules and
writes anything in its workspace, as sent.

OKR Hub works as the OKR Hub app does. A session may create objectives, for
anybody in the workspace. Changing an objective, or adding, changing,
reordering or deleting its key results, is for its owner, its team (and the
teams inside it), whoever created it, or an app admin of OKR Hub.
Checking in, setting a key result's `score` and writing its scorecard cells
are also for the key result's own owners, directly or through a team. Any
other change is `403 forbidden`, and an objective the person may not see,
or a key result of one, is a `404` before anything else is asked. That holds
for app admins too: a private objective an app admin has no part in is not
there for them. A key changes everything in its workspace.

LaunchCal takes a session's writes on the person's own terms, as the app
does. Anybody with a LaunchCal seat adds, edits, publishes, launches and
deletes any launch of the workspace, adds and decides its steps, and writes
its artifacts, links, comments, updates and insights, all in their own name.
A comment or an update is changed only by its author (`403 forbidden`), and
an insight is recorded as the person: naming somebody else in `byUserId` is
`403 forbidden`. The process (launch templates and their steps, functional
teams and who is on them) is LaunchCal's app admins' to change, and a new
organizational team, which is the workspace's, a workspace admin's: anybody
else gets `403 admin_required`, and reads them as before.

## App roles

Each person has a role in each app, on top of their role in the workspace:

* **Admin**: the app's admin powers, in that app only. LaunchCal's process
  (launch templates, their steps, functional teams), editing any objective
  they can see in OKR Hub, running any Dory event, editing any go link, and
  Memegen's template library and moderation. Wherever this page says "an
  app admin", it means an admin of that app; "a workspace admin" is an owner
  or admin of the workspace. It gives nothing in the workspace's settings.
* **Member**: the app as it has always been. Everybody is a Member unless
  somebody chooses otherwise.
* **Viewer**: reads what a member would, and changes nothing. A Viewer's
  session gets `403 viewer_only` on every write; an MCP connection of theirs
  is read-only whatever it was granted, and the consent page offers them only
  **Read only** (see [MCP servers](/ai-assistants/mcp-servers)).

Workspace owners and admins are Admin of every app, always: their app roles
cannot be changed (`409 workspace_admin`). A key acts for the workspace and
is never a Viewer.

`GET /members` returns every member's `appRoles`, one per app.
`PUT /members/{userId}/app-roles/{app}` with `{ "role": "admin" | "member" |
"viewer" }` sets one, with `appRoles:write`: a key and a workspace admin's
session in any app, an app admin's session in that app alone (`403
forbidden` elsewhere). A role neither gives nor takes a seat, and a Viewer
still needs one.

To automate, use a key. Taktoria's own sign-in endpoints (`/api/auth/*`) are
protected by Google reCAPTCHA and refuse a request without a token from the
sign-in page, so a script cannot sign in with a password to get a session.

## Subscriptions

App endpoints also require the organization to hold a subscription for that
app that is active, trialing or past due: a payment Stripe is still retrying
does not stop anything. Without one, or with one cancelled, paused, unpaid or
incomplete, you get `402 subscription_required`, whose message says which.
`details.status` is the app's subscription status, and `details.paymentIssue`
is `unpaid` when Stripe stopped retrying a failed payment and the app is
locked until a workspace admin updates the payment details, as `paymentIssue` on
`GET /subscription` says it.