05 Team Accounts
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
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.
- On the first failed payment the account moves to grace and Team content becomes read-only immediately. There is no tolerance window.
- The 30-day clock starts then and does not restart on later retry failures.
- Invitations cannot be sent or accepted while lapsed, so the Team cannot grow.
- 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_dueorunpaidsubscription 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:
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
activeaccount. - A
deletingaccount 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_idis never guessed. A Team token can span several workspaces, so a top-levelPOST /foldersorPOST /documentswithout aparent_id/folder_idmust passworkspace_id, or it returns400. 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 .../bodywith an empty string returns400 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
# 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:
{
"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.
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
deletingand 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 — the personal plans, which are entirely separate from Team billing
- 01 Personal Access Tokens — the
mdf_tokens used for personal workspaces - 02 HTTP API — the personal
/api/v1surface - 03 MCP Servers — the personal hosted and local MCP servers
- 04 Connecting Claude, ChatGPT and Other Clients — client setup patterns