---
title: "05 Team Accounts"
canonical_url: https://mdflow.cz/share/c/h0L7MvX5Cy5sST3qPnApqHOmrQ36JTVy169qJMtDMya9Kc30zm04cg9OuPSSPwvj/57c04cf4-e638-411f-9803-9eb18950ba92
md_url: https://mdflow.cz/share/c/h0L7MvX5Cy5sST3qPnApqHOmrQ36JTVy169qJMtDMya9Kc30zm04cg9OuPSSPwvj/57c04cf4-e638-411f-9803-9eb18950ba92.md
visibility: public
---

# Team Accounts

A **Team account** is a shared MDflow tenant: its own members, roles, workspaces, folders and markdown documents, billed separately from anyone's personal plan at **€9.99 per active member each month** with a **7-day trial**.

Team accounts are available to **every signed-in MDflow user**. There is no waitlist, allowlist or eligibility flag — open the Teams icon in the source rail, or go to `/teams`, and create one. Access to a *specific* Team account is governed only by membership, role, and that account's billing state.

## The one thing to understand first

A Team account is **not** a shared personal workspace. Team content lives in a separate tenant domain with its own storage and its own authorization rules.

Consequences that surprise people:

- Creating a Team account changes nothing about your personal workspaces. They stay private and stay on whatever plan you personally pay for.
- Your personal Pro subscription grants you nothing inside a Team, and a Team seat grants you nothing in your personal workspaces. The two are completely independent.
- A **Team seat grants Pro-equivalent entitlement for Team content**. Every active member gets autosave and version history on Team documents without anyone buying personal Pro.
- Personal tokens (`mdf_…`) are rejected on Team API routes, and Team tokens (`mdt_…`) are rejected on personal routes. They are not interchangeable.

---

## 1. Creating a Team and inviting people

**Create:** open Teams from the source rail. If you are not yet a member of any Team, you get a create-or-join entry point. One profile can belong to at most **10 Team accounts**.

**Invite:** from Team settings, invite by email as **admin** or **member**, setting each workspace permission *before* sending. Invitation rules:

| Rule | Value |
| --- | --- |
| Invitation lifetime | 7 days |
| Resend cooldown | 60 seconds |
| Sends per invitation per 24 hours | 5 |
| Pending invitations per account | 50 |
| Members per account | 50 |

The invitee must accept while signed in to an MDflow account whose **confirmed** email matches the invitation. Failed deliveries are visible in Team settings and can be retried.

**Invitations are never billed.** Only an accepted invitation creates a membership, and only a membership creates a seat.

## 2. Roles

| Capability | Owner | Admin | Member |
| --- | :---: | :---: | :---: |
| Read and write Team content | Yes | Yes | Per workspace |
| Access to every workspace | Yes | Yes | Only where granted |
| Create / rename / describe / delete workspaces | Yes | Yes | No |
| Invite, remove and manage members | Yes | Yes | No |
| Assign per-member workspace access | Yes | Yes | No |
| Create Team access tokens | Yes | Yes | Yes |
| See and revoke *everyone's* tokens | Yes | Yes | Own only |
| Rename the Team account | Yes | No | No |
| Billing: subscribe, portal, recover | Yes | No | No |
| Transfer ownership | Yes | No | No |
| Leave the Team | No | Yes | Yes |
| Delete the Team account | Yes | No | No |

There is exactly **one owner**, and the owner cannot leave or be removed — ownership must be transferred first. Only the owner can see or touch billing; admins cannot.

## 3. Workspaces and per-member access

Owners and admins reach every Team workspace implicitly. Regular members reach only what they are granted, set from the workspace actions menu:

| Level | What the member can do |
| --- | --- |
| **No access** | The workspace is hidden completely |
| **Read only** | View, quick open, copy, download, export, import into a personal workspace, present — no Team-content changes |
| **Read, write & delete** | Everything above, plus create, organize, edit, share, encrypt, restore versions, duplicate and delete |

Two behaviours worth knowing:

- **A new workspace grants regular members nothing** until an owner or admin assigns access.
- **Promoting a member to admin clears their per-workspace access** (admins don't need it), and demoting them back to member does *not* restore it. The promotion asks for confirmation for exactly this reason.

Drag-and-drop moves stay inside one Team workspace; moving content *between* Team workspaces is not yet supported.

## 4. Pricing and billing

### 4.1 Seats are derived, never purchased

```text
billed quantity = number of accepted memberships
```

There is no seat pool, no allowance, and no "2 seats remaining". You never buy N seats.

- The owner occupies a seat: a Team of 5 members is quantity 5, not 5 + owner.
- Adding a member is never blocked or pre-charged by billing.
- Membership changes are pushed to Stripe by an hourly reconciliation, so the billed quantity catches up **within an hour**.

| | |
| --- | --- |
| Price | €9.99 per active member per month |
| Trial | 7 days, once per Team account ever |
| Initial quantity | Headcount at the moment the owner subscribes |
| Seat changes | Prorated, settled on the next invoice — never charged or refunded at the moment they happen |
| Promotion codes | Accepted at checkout |
| Payer | The owner only |

Teams is not listed on the public pricing page; the price appears in the Team settings billing card.

**Money moves on the billing cycle; access moves immediately.** Removing a member ends their access at once and writes a credit for the unused remainder onto the next invoice. Swapping one member for another inside the same hour usually costs nothing at all, because the count never changes by the time Stripe is updated.

### 4.2 Access states

`access_state` is the single authority over what a Team can do:

| State | Read | Write | Accept invitations |
| --- | :---: | :---: | :---: |
| **setup** — created, not yet subscribed | Yes | No | Yes |
| **active** — paying or in trial | Yes | Yes | Yes |
| **grace** — payment lapsed, first 30 days | Yes | No | No |
| **read_only** — after those 30 days | Yes | No | No |
| **deleting** — deletion in progress | No | No | No |

Members can join and organize themselves in **setup** before the owner ever subscribes; nothing is billed until checkout, and the first invoice simply covers whoever is there by then.

### 4.3 When a payment fails

There is only ever one payer, so "a member's payment failed" cannot happen — it is always the Team's payment, and it affects everyone at once.

1. On the **first** failed payment the account moves to **grace** and Team content becomes **read-only immediately**. There is no tolerance window.
2. The 30-day clock starts then and **does not restart** on later retry failures.
3. Invitations cannot be sent or accepted while lapsed, so the Team cannot grow.
4. After 30 days the account becomes **read_only**. For users nothing changes — both states are read-only.

**Nothing is ever deleted by a lapse.** Reads, exports and existing public share links keep working indefinitely. A lapsed Team loses the ability to *change* anything and keeps the ability to *see and retrieve* everything.

MDflow sends **no billing email of any kind**. The owner learns about a failure from Stripe's own emails and from the in-app banner. Only the owner can fix it — if the owner is unreachable, the escape hatch is transferring ownership, which works in every state except `deleting`.

### 4.4 Cancelling and restarting

Cancellation happens in the Stripe billing portal, never in MDflow.

- **Cancel at period end** changes nothing until the period actually ends — the Team stays fully writable, and an amber notice appears in settings.
- **Cancel immediately** stops writes at once and puts the account into grace.
- **Restarting** is offered once the subscription is genuinely cancelled. There is **no second trial** — the 7 days are once per Team, ever — and the quantity is today's headcount.
- A `past_due` or `unpaid` subscription is *not* restarted with a new checkout; it is recovered by updating the payment method in the portal.
- MDflow never issues refunds. Cancelling immediately forfeits the remainder of the paid period.

## 5. What Team documents can and cannot do

Team documents use the same Monaco editor, split preview, markdown syntax, PDF export, local drafts and conflict-aware saves as personal documents.

**Included:** nested folders with descriptions, inline rename, duplicate, move, copy path, download `.md`, PDF export, presentation mode, markdown import (including a dragged folder tree), `⌘K` quick open, `@mention` links between documents in the same workspace, autosave, version history, public share links, client-side encryption, workspace ZIP export, and importing any Team document into your own private workspace.

**Deliberately not there yet**, pending Team-specific authorization designs:

| Missing | Missing |
| --- | --- |
| Comments | Sidebar search (title + body) |
| Uploaded images | Sort views and the refresh menu |
| Collections | Favorites |
| Private sharing by email | Moving content between Team workspaces |
| GitHub and Google Drive integrations | Custom domains for Team share links |

A few details that behave differently from personal documents:

- **Encryption** works identically, and the password travels between people directly — the encrypted body carries everything needed to derive the key, so any teammate who knows the password unlocks it on their own device. MDflow never holds a key.
- **Public share links** are always served from the MDflow domain; personal custom domains do not apply to Team content. Links survive grace and read-only and go dark only if the account is deleted.
- **Importing a Team document into your own workspace** is a snapshot — editing the copy changes nothing in the Team — and it counts against *your* personal document limit, not the Team's.
- **Version history follows the Team subscription**, so it withdraws in grace and read-only along with the rest of the Pro feature set.

Every account, membership, invitation, workspace, folder, document, restore and export change is recorded in a **Team-only audit feed with 365-day retention**.

## 6. Team access tokens (`mdt_…`)

Team API and Team MCP both authenticate with a **Team access token**, created from the Team account's **API Access** page (`/teams/{accountId}/api-access`).

**Any member can create one.** There is no Pro gate — eligibility comes from the seat, not from the creator's personal plan.

When creating a token you choose:

- a **name**,
- the **workspaces** it may reach (checkboxes; only workspaces you can already read),
- a **permission cap**: read-only, or read and write.

| Property | Behaviour |
| --- | --- |
| Format | `mdt_` + 32 random bytes, base64url |
| Storage | SHA-256 hash only — MDflow never stores the plaintext |
| Reveal | Shown exactly once, at creation |
| Scope | One Team account, and only the workspaces selected at creation |
| Later workspaces | Never granted automatically |
| Expiration / rotation | Not yet — revoke and re-create |
| Visibility | Owners and admins see every token's metadata (creator, workspaces, permission, created and last used); members see their own |
| Revocation | Immediate. Members revoke their own; owners and admins revoke any |

### Effective access is recomputed on every request

The token only sets an **upper bound**. Each request recalculates:

```text
token's workspace grant
  ∩ the creator's current membership and role
  ∩ their current per-workspace permission
  ∩ the Team account's access state
```

So, without touching the token:

- Downgrading a member from write to read on a workspace **revokes that token's write access instantly**, and restoring it restores the token too.
- **Removing a member deletes their tokens outright.**
- A lapsed Team (grace or read-only) makes every token read-only, because writes require an `active` account.
- A `deleting` account refuses everything.
- Workspaces the token cannot reach return `404` — never a hint that they exist.

## 7. The Team API

A content-only JSON API under `/api/team/v1`. Authenticate with a bearer header:

```
Authorization: Bearer mdt_your_token_here
```

### Endpoints

| Method | Path | Purpose |
| --- | --- | --- |
| `GET` | `/api/team/v1/workspaces` | Reachable workspaces, each with your effective `access_level` and the account's state |
| `GET` | `/api/team/v1/folders` | List folders; optional `?workspace_id=` |
| `POST` | `/api/team/v1/folders` | Create a folder |
| `GET` | `/api/team/v1/folders/{id}` | Read one folder |
| `PATCH` | `/api/team/v1/folders/{id}` | Rename, re-describe, or reparent |
| `DELETE` | `/api/team/v1/folders/{id}` | Delete the folder and everything inside it |
| `GET` | `/api/team/v1/folders/{id}/documents` | Documents directly in a folder |
| `GET` | `/api/team/v1/documents` | List documents; optional `?workspace_id=`, or `?ids=` (up to 10, returns bodies) |
| `POST` | `/api/team/v1/documents` | Create a document |
| `GET` | `/api/team/v1/documents/{id}` | Read one document, body included |
| `PATCH` | `/api/team/v1/documents/{id}` | Rename |
| `DELETE` | `/api/team/v1/documents/{id}` | Delete |
| `PUT` | `/api/team/v1/documents/{id}/body` | Replace the entire body |
| `PUT` | `/api/team/v1/documents/{id}/folder` | Move to another folder, or to the workspace root |

That is 14 operations. **Membership, invitations, billing, sharing and workspace administration are deliberately not exposed** — the API touches content only.

### Rules that catch people out

- **`workspace_id` is never guessed.** A Team token can span several workspaces, so a top-level `POST /folders` or `POST /documents` without a `parent_id` / `folder_id` must pass `workspace_id`, or it returns `400`. Supplying both is fine as long as they agree.
- **Duplicate names are disambiguated automatically** with a numeric suffix, exactly as in the app.
- **Emptying a document requires intent.** `PUT .../body` with an empty string returns `400 Empty body requires confirmEmpty: true`.
- **A body replaces the whole document.** There is no patch or append.
- Documents may sit at the **workspace root** (`folder_id: null`), which personal documents cannot.
- **Rate limit: 60 requests per minute**, the same as the personal API.

### Errors

| Code | Meaning |
| --- | --- |
| `400` | Invalid body, invalid id, missing `workspace_id`, conflicting `workspace_id`, empty body without `confirmEmpty`, or the 10,000-document account limit |
| `401` | Missing, malformed, unknown, revoked, or personal (`mdf_`) token |
| `403` | Write attempted with a read-only token or on a lapsed account; or the account is deleting |
| `404` | Not found *or* not reachable by this token — the two are indistinguishable on purpose |
| `429` | Rate limit exceeded |

### Example

```bash
# 1. Discover what this token can reach
curl -s https://mdflow.cz/api/team/v1/workspaces \
  -H "Authorization: Bearer $MDFLOW_TEAM_TOKEN"

# 2. Create a document at the root of a workspace
curl -s -X POST https://mdflow.cz/api/team/v1/documents \
  -H "Authorization: Bearer $MDFLOW_TEAM_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"workspace_id":"<uuid>","title":"Release notes","body":"# Release notes\n"}'

# 3. Replace its body
curl -s -X PUT https://mdflow.cz/api/team/v1/documents/<uuid>/body \
  -H "Authorization: Bearer $MDFLOW_TEAM_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"body":"# Release notes\n\n- Shipped Team API\n"}'
```

Every write is attributed to the token's creator in the Team audit feed — including deletes.

## 8. The Team MCP server

A hosted MCP server at **`https://mdflow.cz/api/team/mcp`**, authenticated with the same `mdt_` token, exposing **16 tools**:

| Workspaces | Folders | Documents |
| --- | --- | --- |
| `mdflow_team_list_workspaces` | `mdflow_team_list_folders` | `mdflow_team_list_documents` |
| | `mdflow_team_get_folder` | `mdflow_team_get_document` |
| | `mdflow_team_create_folder` | `mdflow_team_create_document` |
| | `mdflow_team_rename_folder` | `mdflow_team_rename_document` |
| | `mdflow_team_update_folder_description` | `mdflow_team_update_document_body` |
| | `mdflow_team_move_folder` | `mdflow_team_move_document` |
| | `mdflow_team_delete_folder` | `mdflow_team_delete_document` |
| | | `mdflow_team_get_context` |

`mdflow_team_get_context` is the one to reach for first when an agent is looking for information about a *topic* rather than a known document: it ranks **folder descriptions above folder names and document titles**, then returns the best-matching bodies. Pass `workspaceId` to stay in one workspace, or omit it to rank across everything the token can read.

This is why **folder descriptions matter so much in a Team**. They are the primary signal an agent uses to decide what is relevant.

### Connecting a client

The Team MCP works with any client that can send a bearer header — Claude Code, Cursor, Codex, `mcp-remote`:

```json
{
  "mcpServers": {
    "mdflow-team": {
      "type": "http",
      "url": "https://mdflow.cz/api/team/mcp",
      "headers": { "Authorization": "Bearer mdt_your_token_here" }
    }
  }
}
```

> **Not yet supported: hosted connectors that require OAuth**, such as the Claude.ai web connector directory and ChatGPT. Those need per-user OAuth, which is a planned later phase for Teams. The endpoint is already built to accept it without any change to the tools. Until then, use a client that lets you set a header.

The personal MCP server at `/api/mcp` is unchanged and still uses personal tokens — see [03 MCP Servers](/doc/aad78a27-0654-4781-b4b7-6ced0b14dbfc).

## 9. Limits

| Limit | Value |
| --- | --- |
| Team accounts per person | 10 |
| Members per Team account | 50 |
| Pending invitations per account | 50 |
| Workspaces per Team account | 50 |
| Documents per Team account | 10,000 |
| Document body size | 500 KiB |
| API / MCP rate limit | 60 requests per minute |
| Audit feed retention | 365 days |
| Grace period after a failed payment | 30 days |

## 10. Leaving, transferring and deleting

- **Members and admins can leave** at any time, in any billing state. The owner cannot.
- **Ownership transfer** works in every state except `deleting` and requires a recent sign-in. This is the recovery path when an owner becomes unreachable.
- **Deleting a Team account** requires a recent sign-in and exact-name confirmation. It locks ordinary access first, cancels billing, and only then removes data — so data is never deleted while a subscription could still bill.
- **Deleting your personal MDflow account** first requires leaving every Team you are a member of and transferring or deleting every Team you own.

## See also

- [04 Free and Pro Plans](/doc/b55abc0c-ac5d-44cb-b5a5-22b3cc04a69f) — the personal plans, which are entirely separate from Team billing
- [01 Personal Access Tokens](/doc/bdefa552-e9b6-4382-a479-91615474cf69) — the `mdf_` tokens used for personal workspaces
- [02 HTTP API](/doc/bdb63d7f-857f-42ef-b13f-aa4d139e7cf8) — the personal `/api/v1` surface
- [03 MCP Servers](/doc/aad78a27-0654-4781-b4b7-6ced0b14dbfc) — the personal hosted and local MCP servers
- [04 Connecting Claude, ChatGPT and Other Clients](/doc/72aa97f1-fef9-4465-82fc-0a21ee327252) — client setup patterns
