API docs

mind-mapper API

mount /mindmap · base https://zpe.vc/mindmap · v1.0.0 · 23 operations · 13 write · 6 need no account · beta

the document this app serves now. Contract: https://zpe.vc/mindmap/_/api/v1/openapi.json · sha256 0995a44fc0d1

GET /api/health Liveness, and whether the gate, the database, the assets and the human check are wired. getHealth no account

The one route a monitor can call without a credential. `gateConfigured` is false when the door's address (`DOOR_INTERNAL_URL`) or its bearer secret (`DOOR_INTERNAL_TOKEN`) is unbound, which is the difference between a worker that refuses everybody and one that is running. This Worker holds no identity database of its own; it asks the door. `humanCheck` names whether the Turnstile secret is bound: an unarmed production mint is visible here rather than silent.

Parameters

No parameters.

Request body

None: this operation takes its arguments in the path and the query.

Responses

statusmeaningbody
200The worker is up, and each boolean says whether that binding is present.object

Example

Liveness, and whether the gate, the database, the assets and the human check are wired..

GET /api/health
curl -X GET 'https://zpe.vc/mindmap/api/health'
200 application/json
{
  "ok": false,
  "gateConfigured": false,
  "dbConfigured": false,
  "assetsBound": false,
  "humanCheck": false
}

The example is generated from the schema in the contract: the shape is the document's, the values are placeholders.

GET /api/session Is there a live fleet sign-in on this request? The passkey door, read. getSession no account

Answers before a principal exists, so a person signed in at the door with a passkey and no number on the device is not shown Sign In. `linked` is the second half of the truth: signed in at the door but no principal in the gate yet. No cookie, an unreachable door and a refused session are one answer, signed out. Nothing here creates or changes a row.

Parameters

No parameters.

Request body

None: this operation takes its arguments in the path and the query.

Responses

statusmeaningbody
200Signed in or not, and whether the gate already holds a principal for that person.object

Example

Is there a live fleet sign-in on this request? The passkey door, read..

GET /api/session
curl -X GET 'https://zpe.vc/mindmap/api/session'
200 application/json
{
  "signedIn": false,
  "linked": false,
  "name": null
}

The example is generated from the schema in the contract: the shape is the document's, the values are placeholders.

POST /api/signout End the door's session, carrying the request's cookies to the door and the cleared ones back. postSignout no account

One gate, so the session is the door's to end and this app only carries the act. A door that does not answer is named in the reply (`door: "did_not_answer"`) rather than hidden: a browser whose cookie was not cleared still looks signed in on the next load, and a silent success would be a lie. GET on this path is not a route and answers the router's 404.

Parameters

No parameters.

Request body

None: this operation takes its arguments in the path and the query.

Responses

statusmeaningbody
200Whether the door answered. `set-cookie` carries the door's own clearing headers.object

Example

End the door's session, carrying the request's cookies to the door and the cleared ones back..

POST /api/signout
curl -X POST 'https://zpe.vc/mindmap/api/signout'
200 application/json
{
  "ok": false,
  "door": "ended"
}

The example is generated from the schema in the contract: the shape is the document's, the values are placeholders.

GET /api/me Resolve the presented credential to one principal, and summarise what this app holds for it. getMindMapperMe no account

Null is a clean answer here, not an error: a request with no credential and one whose credential resolves to nobody both answer `principal: null`, so this route never tells a stranger whether a number exists. `via` is the resolver's own word for which credential answered (`account` for a number, `identity` for a door session).

Parameters

No parameters.

Request body

None: this operation takes its arguments in the path and the query.

Responses

statusmeaningbody
200The principal, or null; with the counts and the profile's update time when there is one.object

Example

Resolve the presented credential to one principal, and summarise what this app holds for it..

GET /api/me
curl -X GET 'https://zpe.vc/mindmap/api/me'
200 application/json
{
  "principal": {
    "principalId": "string",
    "kind": "anon",
    "last4": null,
    "claimed": false
  },
  "via": "account",
  "results": {
    "count": 0,
    "lastUpdatedAt": null
  },
  "profile": {
    "updatedAt": "2026-10-31T09:00:00Z"
  }
}

The example is generated from the schema in the contract: the shape is the document's, the values are placeholders.

POST /api/account/start Mint a new anonymous number. The number is returned once and never again. postAccountStart no account

This route is retired as the way a person gets a number (DECISIONS.md D-005: a number is minted at the door), but the worker still answers it, and this contract describes what it answers rather than what the app recommends. The number is returned ONCE: the identity database stores only a peppered hash, so this response is the only copy that will exist. Rate limited by counting rows minted in the last hour; no IP, no fingerprint, no identifier is stored. When `TURNSTILE_SECRET` is bound the call needs a `turnstileToken` in the body or the maintenance key in `x-mm-mint-key`, and the check FAILS CLOSED: a siteverify outage refuses the mint rather than opening it.

Parameters

nameinrequiredtypenotes
x-mm-mint-keyheadernostringThe maintenance key, for scripts that cannot solve a Turnstile widget. A worker secret; nothing else uses it.

Request body application/json · optional

fieldtyperequirednotes
turnstileTokenstringnoThe widget's token, verified against Cloudflare's siteverify.

Responses

statusmeaningbody
201The only copy of the number that will ever exist.object
403The human check refused, so nothing was created. `reason` is the check's own word.object
429The hourly mint cap was reached. The cap is counted from rows, never from an address.object
503The gate is not wired on this deployment (`DOOR_INTERNAL_URL` or the `DOOR_INTERNAL_TOKEN` secret unbound).object

Example

Mint a new anonymous number. The number is returned once and never again..

POST /api/account/start
curl -X POST 'https://zpe.vc/mindmap/api/account/start' \
  -H 'content-type: application/json' \
  -d '{
  "turnstileToken": "string"
}'
201 application/json
{
  "number": "string",
  "last4": "string",
  "principalId": "string",
  "created": false,
  "note": "string"
}

The example is generated from the schema in the contract: the shape is the document's, the values are placeholders.

POST /api/account/attach Adopt an existing number on this device. A wrong number and an unknown one answer alike. postAccountAttach no account

The one route that takes a number in a BODY rather than a header. A number that does not normalise, an unknown number, a revoked number and a claimed one all answer the same 401 `unknown_account`, because telling them apart is what would let a stranger probe the space.

Parameters

No parameters.

Request body application/json · required

fieldtyperequirednotes
numberstringyesSixteen digits, with or without the dashes.

Responses

statusmeaningbody
200The principal that number resolves to, and its last four.object
400The body is not JSON, is larger than 1 MB, or carries the wrong shape. `bad_request` for a body that will not parse, `body_too_large` past 1000000 bytes.object
401No credential, or one that resolves to nobody. A wrong number, an unknown number and a revoked number all answer this same body, so a stranger cannot probe the number space.object
413The request body is larger than 1000000 bytes.object

Example

Adopt an existing number on this device. A wrong number and an unknown one answer alike..

POST /api/account/attach
curl -X POST 'https://zpe.vc/mindmap/api/account/attach' \
  -H 'content-type: application/json' \
  -d '{
  "number": "string"
}'
200 application/json
{
  "principal": {
    "principalId": "string",
    "last4": null
  }
}

The example is generated from the schema in the contract: the shape is the document's, the values are placeholders.

DELETE /api/account Delete what this app holds for the resolved principal, and nothing else. deleteAccount

The only destructive route in the app, and its scope is narrow: groups the principal owns (ownership passes to the earliest-joined remaining member, or the group goes), invite codes they minted, their membership rows, their results and their profile. The central identity database is NOT touched: deleting a principal there is the gate's business, not this app's. Idempotent, so a second call reports zero deletions.

Parameters

No parameters.

Request body

None: this operation takes its arguments in the path and the query.

Responses

statusmeaningbody
200What went, counted. `gate` is always `untouched`. On a deployment with no `DB` bound the same 200 carries `{error: "not_configured"}` instead.object
401No credential, or one that resolves to nobody. A wrong number, an unknown number and a revoked number all answer this same body, so a stranger cannot probe the number space.object

Example

Delete what this app holds for the resolved principal, and nothing else., with a token from the door.

DELETE /api/account
curl -X DELETE 'https://zpe.vc/mindmap/api/account' \
  -H 'Authorization: Bearer $ZPE_TOKEN'
200 application/json
{
  "ok": false,
  "deleted": {
    "results": 0,
    "profile": 0,
    "memberships": 0,
    "invites": 0,
    "groupsDeleted": 0,
    "groupsTransferred": 0
  },
  "gate": "string",
  "error": "string"
}

The example is generated from the schema in the contract: the shape is the document's, the values are placeholders.

GET /api/results Every stored result for the resolved principal. getResults

Scoped to the principal the credential resolves to; there is no parameter that names somebody else. `results` is a map keyed by test id, and each payload is whatever the app saved, so this contract does not shape it.

Parameters

No parameters.

Request body

None: this operation takes its arguments in the path and the query.

Responses

statusmeaningbody
200The caller's own rows, keyed by test id.object
401No credential, or one that resolves to nobody. A wrong number, an unknown number and a revoked number all answer this same body, so a stranger cannot probe the number space.object

Example

Every stored result for the resolved principal., with a token from the door.

GET /api/results
curl -X GET 'https://zpe.vc/mindmap/api/results' \
  -H 'Authorization: Bearer $ZPE_TOKEN'
200 application/json
{
  "results": {},
  "count": 0,
  "lastUpdatedAt": null
}

The example is generated from the schema in the contract: the shape is the document's, the values are placeholders.

PUT /api/results Upsert results, scoped to the resolved principal. putResults

A per-test upsert: a key present in `results` replaces that row's payload, a key absent is left alone (this is not a replace-all). A test id is truncated to 64 characters and a payload larger than 1 MB is skipped rather than refused, so one oversized value does not cost the caller the rest of the write. The answer is the same shape a read would give.

Parameters

No parameters.

Request body application/json · required

fieldtyperequirednotes
resultsobjectyesThe tests to write. A key present replaces that row; a key absent is left alone.

Responses

statusmeaningbody
200The write landed; the counts are what the store holds now.object
400`results` is missing, or is not an object, or the body is not JSON.object
401No credential, or one that resolves to nobody. A wrong number, an unknown number and a revoked number all answer this same body, so a stranger cannot probe the number space.object
413The request body is larger than 1000000 bytes.object

Example

Upsert results, scoped to the resolved principal., with a token from the door.

PUT /api/results
curl -X PUT 'https://zpe.vc/mindmap/api/results' \
  -H 'Authorization: Bearer $ZPE_TOKEN' \
  -H 'content-type: application/json' \
  -d '{
  "results": {}
}'
200 application/json
{
  "ok": false,
  "count": 0,
  "lastUpdatedAt": null
}

The example is generated from the schema in the contract: the shape is the document's, the values are placeholders.

GET /api/profile This app's profile row for the resolved principal. getProfile

`null` when the principal has never saved one, which is not an error.

Parameters

No parameters.

Request body

None: this operation takes its arguments in the path and the query.

Responses

statusmeaningbody
200The profile, or null.object
401No credential, or one that resolves to nobody. A wrong number, an unknown number and a revoked number all answer this same body, so a stranger cannot probe the number space.object

Example

This app's profile row for the resolved principal., with a token from the door.

GET /api/profile
curl -X GET 'https://zpe.vc/mindmap/api/profile' \
  -H 'Authorization: Bearer $ZPE_TOKEN'
200 application/json
{
  "profile": {
    "displayName": null,
    "dateOfBirth": null,
    "timeOfBirth": null,
    "placeOfBirth": null,
    "updatedAt": "2026-10-31T09:00:00Z"
  }
}

The example is generated from the schema in the contract: the shape is the document's, the values are placeholders.

PUT /api/profile Upsert the resolved principal's profile row. putProfile

Every field is trimmed and capped at 200 characters; an empty or non-string field is stored as null rather than refused, so a person can clear a field by sending nothing in it.

Parameters

No parameters.

Request body application/json · required

fieldtyperequirednotes
displayNamestringno
dateOfBirthstringno
timeOfBirthstringno
placeOfBirthstringno

Responses

statusmeaningbody
200The row as stored.object
400The body is not JSON, is larger than 1 MB, or carries the wrong shape. `bad_request` for a body that will not parse, `body_too_large` past 1000000 bytes.object
401No credential, or one that resolves to nobody. A wrong number, an unknown number and a revoked number all answer this same body, so a stranger cannot probe the number space.object
413The request body is larger than 1000000 bytes.object

Example

Upsert the resolved principal's profile row., with a token from the door.

PUT /api/profile
curl -X PUT 'https://zpe.vc/mindmap/api/profile' \
  -H 'Authorization: Bearer $ZPE_TOKEN' \
  -H 'content-type: application/json' \
  -d '{
  "displayName": "string",
  "dateOfBirth": "string",
  "timeOfBirth": "string",
  "placeOfBirth": "string"
}'
200 application/json
{
  "ok": false,
  "profile": {
    "displayName": null,
    "dateOfBirth": null,
    "timeOfBirth": null,
    "placeOfBirth": null,
    "updatedAt": "2026-10-31T09:00:00Z"
  }
}

The example is generated from the schema in the contract: the shape is the document's, the values are placeholders.

GET /api/groups Every group the resolved principal is a member of, with their own share level. getGroups

Newest group first. `memberCount` counts members, never results.

Parameters

No parameters.

Request body

None: this operation takes its arguments in the path and the query.

Responses

statusmeaningbody
200The caller's groups.object
401No credential, or one that resolves to nobody. A wrong number, an unknown number and a revoked number all answer this same body, so a stranger cannot probe the number space.object

Example

Every group the resolved principal is a member of, with their own share level., with a token from the door.

GET /api/groups
curl -X GET 'https://zpe.vc/mindmap/api/groups' \
  -H 'Authorization: Bearer $ZPE_TOKEN'
200 application/json
{
  "groups": [
    {
      "id": "string",
      "name": "string",
      "ownerPrincipalId": "string",
      "createdAt": "2026-10-31T09:00:00Z",
      "isOwner": false,
      "myShareLevel": "none",
      "joinedAt": "2026-10-31T09:00:00Z",
      "memberCount": 0
    }
  ]
}

The example is generated from the schema in the contract: the shape is the document's, the values are placeholders.

POST /api/groups Create a group. The creator is owner and a member, and an invite is returned once. postGroups

The creator joins at share level `none`, so creating a group shares nothing by itself. The invite code is returned in this response and stored only as a hash, so it cannot be handed back a second time: only its last four are readable later.

Parameters

No parameters.

Request body application/json · required

fieldtyperequirednotes
namestringyesTrimmed and capped at 80 characters.

Responses

statusmeaningbody
200The group, and the one-time invite.object
400The name is missing or blank.object
401No credential, or one that resolves to nobody. A wrong number, an unknown number and a revoked number all answer this same body, so a stranger cannot probe the number space.object
413The request body is larger than 1000000 bytes.object

Example

Create a group. The creator is owner and a member, and an invite is returned once., with a token from the door.

POST /api/groups
curl -X POST 'https://zpe.vc/mindmap/api/groups' \
  -H 'Authorization: Bearer $ZPE_TOKEN' \
  -H 'content-type: application/json' \
  -d '{
  "name": "string"
}'
200 application/json
{
  "group": {
    "id": "string",
    "name": "string",
    "ownerPrincipalId": "string",
    "createdAt": "2026-10-31T09:00:00Z",
    "isOwner": false
  },
  "invite": {
    "code": "string",
    "last4": "string",
    "createdAt": "2026-10-31T09:00:00Z",
    "expiresAt": null,
    "maxUses": 0
  }
}

The example is generated from the schema in the contract: the shape is the document's, the values are placeholders.

POST /api/groups/join Join a group by its invite code. postGroupsJoin

An unknown, expired, exhausted and foreign code all answer the same 404 `invalid_code`: the four causes are indistinguishable, so a code cannot be probed. A member who presents a live code again answers `alreadyMember: true` rather than an error.

Parameters

No parameters.

Request body application/json · required

fieldtyperequirednotes
codestringyes

Responses

statusmeaningbody
200The group, whether the caller was already in it, and the code's last four.object
400The body is not JSON, is larger than 1 MB, or carries the wrong shape. `bad_request` for a body that will not parse, `body_too_large` past 1000000 bytes.object
401No credential, or one that resolves to nobody. A wrong number, an unknown number and a revoked number all answer this same body, so a stranger cannot probe the number space.object
404The code is unknown, expired, exhausted or belongs to another account.object
413The request body is larger than 1000000 bytes.object

Example

Join a group by its invite code., with a token from the door.

POST /api/groups/join
curl -X POST 'https://zpe.vc/mindmap/api/groups/join' \
  -H 'Authorization: Bearer $ZPE_TOKEN' \
  -H 'content-type: application/json' \
  -d '{
  "code": "string"
}'
200 application/json
{
  "group": {
    "id": "string",
    "name": "string",
    "ownerPrincipalId": "string",
    "createdAt": "2026-10-31T09:00:00Z",
    "isOwner": false
  },
  "alreadyMember": false,
  "inviteLast4": "string"
}

The example is generated from the schema in the contract: the shape is the document's, the values are placeholders.

POST /api/groups/leave Leave a group the resolved principal is a member of. postGroupsLeave

An unknown group and a group the caller is not in answer the same 404 `not_found`.

Parameters

No parameters.

Request body application/json · required

fieldtyperequirednotes
groupIdstringyes

Responses

statusmeaningbody
200The membership row is gone.object
400The body is not JSON, is larger than 1 MB, or carries the wrong shape. `bad_request` for a body that will not parse, `body_too_large` past 1000000 bytes.object
401No credential, or one that resolves to nobody. A wrong number, an unknown number and a revoked number all answer this same body, so a stranger cannot probe the number space.object
404No such group, or the caller is not a member of it.object
413The request body is larger than 1000000 bytes.object

Example

Leave a group the resolved principal is a member of., with a token from the door.

POST /api/groups/leave
curl -X POST 'https://zpe.vc/mindmap/api/groups/leave' \
  -H 'Authorization: Bearer $ZPE_TOKEN' \
  -H 'content-type: application/json' \
  -d '{
  "groupId": "string"
}'
200 application/json
{
  "ok": false,
  "left": "string"
}

The example is generated from the schema in the contract: the shape is the document's, the values are placeholders.

GET /api/groups/{id} A group's detail: membership, the caller's share level, their own per-test switches. getGroupsById

Membership and each member's sharing state are visible to any member; the invite metadata is the owner's alone (`invites` is null for everybody else). No member's results are ever included here. `shareableTests` is what the CALLER could switch on, not what anybody else holds.

Parameters

nameinrequiredtypenotes
idpathyesstringThe group id, a UUID.

Request body

None: this operation takes its arguments in the path and the query.

Responses

statusmeaningbody
200The group as this caller may read it.object
401No credential, or one that resolves to nobody. A wrong number, an unknown number and a revoked number all answer this same body, so a stranger cannot probe the number space.object
404No such group, or the caller is not a member of it.object

Example

A group's detail: membership, the caller's share level, their own per-test switches., with a token from the door.

GET /api/groups/{id}
curl -X GET 'https://zpe.vc/mindmap/api/groups/string' \
  -H 'Authorization: Bearer $ZPE_TOKEN'
200 application/json
{
  "group": {
    "id": "string",
    "name": "string",
    "ownerPrincipalId": "string",
    "createdAt": "2026-10-31T09:00:00Z",
    "isOwner": false
  },
  "myShareLevel": "none",
  "myShowDots": false,
  "myExcludedTests": [
    "string"
  ],
  "shareableTests": [
    {
      "testId": "string",
      "label": "string"
    }
  ],
  "members": [
    {
      "principalId": "string",
      "displayName": null,
      "shareLevel": "none",
      "showDots": false,
      "joinedAt": "2026-10-31T09:00:00Z",
      "isOwner": false,
      "isMe": false
    }
  ],
  "invites": [
    {
      "last4": "string",
      "createdAt": "2026-10-31T09:00:00Z",
      "expiresAt": null,
      "maxUses": 0,
      "uses": 0,
      "addressed": false
    }
  ]
}

The example is generated from the schema in the contract: the shape is the document's, the values are placeholders.

PUT /api/groups/{id}/share Set the caller's OWN share level in this group. putGroupsByIdShare

A member can only change their own level; there is no field for somebody else's. `group` puts their values into the group profile, `pair` additionally allows a pairwise read with another member who is also at `pair`, and `none` shares nothing.

Parameters

nameinrequiredtypenotes
idpathyesstringThe group id, a UUID.

Request body application/json · required

fieldtyperequirednotes
share_level"none" | "group" | "pair"yes`none` shares nothing, `group` puts the member's values into the group profile, `pair` additionally allows a pairwise read.

Responses

statusmeaningbody
200The level as stored.object
400The level is not one of `none`, `group`, `pair`.object
401No credential, or one that resolves to nobody. A wrong number, an unknown number and a revoked number all answer this same body, so a stranger cannot probe the number space.object
404No such group, or the caller is not a member of it.object
413The request body is larger than 1000000 bytes.object

Example

Set the caller's OWN share level in this group., with a token from the door.

PUT /api/groups/{id}/share
curl -X PUT 'https://zpe.vc/mindmap/api/groups/string/share' \
  -H 'Authorization: Bearer $ZPE_TOKEN' \
  -H 'content-type: application/json' \
  -d '{
  "share_level": "none"
}'
200 application/json
{
  "ok": false,
  "groupId": "string",
  "shareLevel": "none"
}

The example is generated from the schema in the contract: the shape is the document's, the values are placeholders.

PUT /api/groups/{id}/dots Show the caller's own value as a dot in this group's profile, or stop showing it. putGroupsByIdDots

The choice is the caller's alone. A reader always sees their own value; this switch decides whether OTHER members do. `show_dots`, `showDots` and `show` are all read, and anything not true is stored as false.

Parameters

nameinrequiredtypenotes
idpathyesstringThe group id, a UUID.

Request body application/json · required

fieldtyperequirednotes
show_dotsbooleanyes

Responses

statusmeaningbody
200The switch as stored.object
400The body is not JSON, is larger than 1 MB, or carries the wrong shape. `bad_request` for a body that will not parse, `body_too_large` past 1000000 bytes.object
401No credential, or one that resolves to nobody. A wrong number, an unknown number and a revoked number all answer this same body, so a stranger cannot probe the number space.object
404No such group, or the caller is not a member of it.object
413The request body is larger than 1000000 bytes.object

Example

Show the caller's own value as a dot in this group's profile, or stop showing it., with a token from the door.

PUT /api/groups/{id}/dots
curl -X PUT 'https://zpe.vc/mindmap/api/groups/string/dots' \
  -H 'Authorization: Bearer $ZPE_TOKEN' \
  -H 'content-type: application/json' \
  -d '{
  "show_dots": false
}'
200 application/json
{
  "ok": false,
  "groupId": "string",
  "showDots": false
}

The example is generated from the schema in the contract: the shape is the document's, the values are placeholders.

PUT /api/groups/{id}/tests Set the caller's OWN per-test exclusions in this group. putGroupsByIdTests

An excluded test is filtered in the query before anything is loaded, so its values cannot reach a dimension, a median or a payload: it lowers n rather than contributing a zero. A test id the caller holds no result for is refused (`unknown_test`) rather than stored.

Parameters

nameinrequiredtypenotes
idpathyesstringThe group id, a UUID.

Request body application/json · required

fieldtyperequirednotes
excludedarray of stringyesThe test ids to switch OFF for this group.

Responses

statusmeaningbody
200The exclusions as stored.object
400`excluded` is not an array, or names a test the caller holds no result for.object
401No credential, or one that resolves to nobody. A wrong number, an unknown number and a revoked number all answer this same body, so a stranger cannot probe the number space.object
404No such group, or the caller is not a member of it.object
413The request body is larger than 1000000 bytes.object

Example

Set the caller's OWN per-test exclusions in this group., with a token from the door.

PUT /api/groups/{id}/tests
curl -X PUT 'https://zpe.vc/mindmap/api/groups/string/tests' \
  -H 'Authorization: Bearer $ZPE_TOKEN' \
  -H 'content-type: application/json' \
  -d '{
  "excluded": [
    "string"
  ]
}'
200 application/json
{
  "ok": false,
  "groupId": "string",
  "excludedTests": [
    "string"
  ]
}

The example is generated from the schema in the contract: the shape is the document's, the values are placeholders.

POST /api/groups/{id}/invites The owner mints another invite. The code is returned once, here. postGroupsByIdInvites

The owner's alone: a member who is not the owner, and a group that does not exist, answer the same 404 `not_found`. The code is stored only as a sha256, so this response is the only copy that will exist; later reads show its last four, its uses and its expiry.

Parameters

nameinrequiredtypenotes
idpathyesstringThe group id, a UUID.

Request body application/json · optional

fieldtyperequirednotes
maxUsesintegernoHow many joins the code allows. Defaults to 25.
expiresAtstring (date-time)noWhen the code stops working. Absent means it does not expire.
principalIdstringnoAddress the code to one principal.

Responses

statusmeaningbody
200The one-time code, and what it allows.object
400The body is not JSON, is larger than 1 MB, or carries the wrong shape. `bad_request` for a body that will not parse, `body_too_large` past 1000000 bytes.object
401No credential, or one that resolves to nobody. A wrong number, an unknown number and a revoked number all answer this same body, so a stranger cannot probe the number space.object
404No such group, or the caller is not its owner.object
413The request body is larger than 1000000 bytes.object

Example

The owner mints another invite. The code is returned once, here., with a token from the door.

POST /api/groups/{id}/invites
curl -X POST 'https://zpe.vc/mindmap/api/groups/string/invites' \
  -H 'Authorization: Bearer $ZPE_TOKEN' \
  -H 'content-type: application/json' \
  -d '{
  "maxUses": 0,
  "expiresAt": "2026-10-31T09:00:00Z",
  "principalId": "string"
}'
200 application/json
{
  "invite": {
    "code": "string",
    "last4": "string",
    "createdAt": "2026-10-31T09:00:00Z",
    "expiresAt": null,
    "maxUses": 0
  }
}

The example is generated from the schema in the contract: the shape is the document's, the values are placeholders.

GET /api/groups/{id}/stats The group profile: a median per dimension, with n, and the dots members chose to show. getGroupsByIdStats

Median, never mean. A dimension with fewer than `minSharers` contributors answers `status: "insufficient"` with its n and no median, range or spread: the gap is named and nothing is invented. Members who are not sharing at `group` or `pair` appear in `absent` with their level, never as a zero.

Parameters

nameinrequiredtypenotes
idpathyesstringThe group id, a UUID.

Request body

None: this operation takes its arguments in the path and the query.

Responses

statusmeaningbody
200The group's profile as this caller may read it.object
401No credential, or one that resolves to nobody. A wrong number, an unknown number and a revoked number all answer this same body, so a stranger cannot probe the number space.object
404No such group, or the caller is not a member of it.object

Example

The group profile: a median per dimension, with n, and the dots members chose to show., with a token from the door.

GET /api/groups/{id}/stats
curl -X GET 'https://zpe.vc/mindmap/api/groups/string/stats' \
  -H 'Authorization: Bearer $ZPE_TOKEN'
200 application/json
{
  "groupId": "string",
  "name": "string",
  "memberCount": 0,
  "rule": "string",
  "minSharers": 0,
  "contributors": 0,
  "dimensions": [
    {
      "dimension": "string",
      "testId": "string",
      "testLabel": "string",
      "label": "string",
      "n": 0,
      "status": "ok",
      "dots": [
        {}
      ],
      "dotsHidden": 0
    }
  ],
  "absent": [
    {
      "principalId": "string",
      "shareLevel": "none"
    }
  ]
}

The example is generated from the schema in the contract: the shape is the document's, the values are placeholders.

GET /api/groups/{id}/pair/{principalId} Pairwise divergence with one other member. getGroupsByIdPairByPrincipalId

Refused uniformly unless BOTH principals are members of this group AND sharing at `pair`: an unknown member, a non-member, a member at `group` and a request naming the caller themselves all answer the same 404 `pair_unavailable`. Only dimensions both members hold a value for are returned; each side's per-test exclusions are applied before the read.

Parameters

nameinrequiredtypenotes
idpathyesstringThe group id, a UUID.
principalIdpathyesstringThe other member's principal id.

Request body

None: this operation takes its arguments in the path and the query.

Responses

statusmeaningbody
200The dimensions both share, with the absolute difference and who is higher.object
401No credential, or one that resolves to nobody. A wrong number, an unknown number and a revoked number all answer this same body, so a stranger cannot probe the number space.object
404Both principals are not members sharing at `pair`, or the request names the caller.object

Example

Pairwise divergence with one other member., with a token from the door.

GET /api/groups/{id}/pair/{principalId}
curl -X GET 'https://zpe.vc/mindmap/api/groups/string/pair/string' \
  -H 'Authorization: Bearer $ZPE_TOKEN'
200 application/json
{
  "groupId": "string",
  "you": {
    "principalId": "string"
  },
  "them": {
    "principalId": "string",
    "displayName": null
  },
  "rule": "string",
  "shared": 0,
  "dimensions": [
    {
      "dimension": "string",
      "testLabel": "string",
      "label": "string",
      "you": 0,
      "them": 0,
      "diff": 0,
      "direction": "you"
    }
  ]
}

The example is generated from the schema in the contract: the shape is the document's, the values are placeholders.

GET /_/api/v1/results The estate's machine address for this app: the same read as /api/results, keyed by the number. getMachineResults

`<mount>/_/api/v1/<tail>` is the address a caller with no browser uses. The guard is the same resolver the browser routes use, and its verdict is final: no principal reads nothing here. A request that presented no credential gets the estate's RFC 9457 `unauthenticated` document; one that presented a number which resolves to nobody gets the app's uniform 401 `unknown_account`, so unknown, revoked and claimed numbers stay indistinguishable. The method is checked BEFORE the credential, so a caller learns the method without being asked for a key. Any other path under `/_/` is a `not-found` problem document, never the SPA shell.

Parameters

No parameters.

Request body

None: this operation takes its arguments in the path and the query.

Responses

statusmeaningbody
200The caller's own rows, keyed by test id.object
401One of two documents, by whether a credential was presented at all. No credential: the estate's problem document. A credential that resolves to nobody: the app's uniform `unknown_account`.object
405A method this path does not answer. The `allow` header names GET.object

Example

The estate's machine address for this app: the same read as /api/results, keyed by the number., with a token from the door.

GET /_/api/v1/results
curl -X GET 'https://zpe.vc/mindmap/_/api/v1/results' \
  -H 'Authorization: Bearer $ZPE_TOKEN'
200 application/json
{
  "results": {},
  "count": 0,
  "lastUpdatedAt": null
}

The example is generated from the schema in the contract: the shape is the document's, the values are placeholders.