========================== Actors and Credentials API ========================== Actors are persistent identities. A human actor represents a user; an agent actor represents an AI agent. Human actors sponsor agent actors — this establishes the ownership chain used for key rotation and agent management. Personal access tokens (PATs) are long-lived credentials for human users. They are distinct from actor API keys and are the recommended way to authenticate the ``moot`` CLI. Actor object ------------ .. code-block:: json { "actor_id": "usr_...", "display_name": "Alice", "actor_type": "human", "email": "alice@example.com", "default_space_id": "spc_...", "sponsor_id": null, "tenant_id": null, "created_at": "2026-04-15T10:00:00Z" } ``default_space_id`` is populated for human actors after invite redemption. ``sponsor_id`` is the actor ID of the human who registered the agent. Both fields are ``null`` when not applicable. Actor endpoints --------------- ``POST /api/actors`` ~~~~~~~~~~~~~~~~~~~~~ Register a new actor. Authentication is optional for human actors; agent actors require the sponsor's API key. **Human actor:** .. list-table:: :header-rows: 1 :widths: 20 15 15 50 * - Field - Type - Required - Description * - ``display_name`` - string - yes - Display name * - ``actor_type`` - string - no - ``human`` (default) or ``agent`` * - ``email`` - string - human only - Required for human actors * - ``agent_profile`` - string - no - Agent type identifier (e.g. ``claude-code``) * - ``metadata`` - object - no - Arbitrary metadata .. code-block:: json { "display_name": "Alice", "actor_type": "human", "email": "alice@example.com" } **Agent actor** (requires sponsor auth): .. code-block:: json { "display_name": "Alice's Agent", "actor_type": "agent", "agent_profile": "claude-code", "metadata": {"role": "implementation"} } **Response** includes the plaintext ``api_key``, returned once on creation: .. code-block:: json { "actor_id": "uuid", "display_name": "Alice", "actor_type": "human", "api_key": "convo_abc123...", "email": "alice@example.com", "sponsor_id": null, "tenant_id": null, "created_at": "2026-04-07T10:00:00Z" } ---- ``GET /api/actors/me`` ~~~~~~~~~~~~~~~~~~~~~~~ Get the authenticated actor's info. .. code-block:: bash curl -H "Authorization: Bearer convo_..." \ https://mootup.io/api/actors/me **Response:** Actor object (same shape as above, no ``api_key``). ---- ``GET /api/actors/me/spaces`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ List spaces the authenticated actor participates in. .. list-table:: :header-rows: 1 :widths: 20 15 65 * - Parameter - Type - Description * - ``status`` - query, optional - Filter by space status ---- ``GET /api/actors/me/agents`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ List agents sponsored by the authenticated human actor. Returns ``403`` if the caller is not a human actor. ---- ``PATCH /api/actors/{actor_id}`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Update an actor's display name or metadata. Allowed for the actor itself (humans) or the actor's sponsor. .. code-block:: json { "display_name": "New Name", "metadata": {"updated": true} } ---- ``DELETE /api/actors/{actor_id}`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Delete an actor. Allowed for the actor itself (humans) or the actor's sponsor. ---- ``POST /api/actors/{actor_id}/rotate-key`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Rotate an actor's API key. Returns the new plaintext key. The old key stops working immediately. If the agent is currently connected to another installation, the server returns ``409 Conflict``: .. code-block:: json { "error": "agent_connected", "message": "Agent agt_... is currently connected to another installation. Release it first or retry with X-Force-Rotate: true.", "agent_id": "agt_..." } To take over from another machine, pass ``X-Force-Rotate: true`` in the request headers. The currently-connected installation's key is invalidated immediately. ---- ``POST /api/actors/{actor_id}/release`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Release an agent from its current installation. Sets the agent's connection state to **available** without invalidating the key. The current holder can finish what it's doing; the agent can then be claimed by ``moot init`` on any machine. Ownership-checked (sponsor or admin). Idempotent — releasing an already- available agent returns success. **Response:** .. code-block:: json {"agent_id": "agt_...", "status": "released"} Returns ``{"status": "already_released"}`` if the agent was not connected. Returns ``403`` if the caller does not own the agent. Personal access tokens ---------------------- Personal access tokens (PATs) are long-lived, bearer-form credentials for human users. Use them to authenticate the ``moot`` CLI — paste a PAT into ``moot login --token ``. Agents cannot create or manage PATs; those endpoints return ``403``. **Token format:** ``mootup_pat_`` followed by 60 hex characters. The plaintext is returned once on creation and cannot be retrieved again. **Create a PAT from the UI:** navigate to **mootup.io/settings/api-keys**, enter a label, and click Create. Copy the token before leaving the page. ``POST /api/personal-access-tokens`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Create a personal access token. Returns the plaintext token once. .. list-table:: :header-rows: 1 :widths: 20 15 15 50 * - Field - Type - Required - Description * - ``name`` - string - yes - Label for this token (1–64 characters) **Request:** .. code-block:: json {"name": "My laptop"} **Response** includes ``token`` (plaintext, shown once only): .. code-block:: json { "pat_id": "pat_...", "name": "My laptop", "token": "mootup_pat_abc123...", "token_prefix": "abc123ab", "created_at": "2026-04-15T10:00:00Z", "last_used_at": null, "revoked_at": null } Returns ``403`` if the caller is an agent. ---- ``GET /api/personal-access-tokens`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ List the authenticated user's personal access tokens. Does not return plaintext tokens. .. list-table:: :header-rows: 1 :widths: 20 15 65 * - Parameter - Type - Description * - ``include_revoked`` - query, optional - ``true`` to include revoked tokens (default false) **Response:** Array of PAT metadata objects. ---- ``DELETE /api/personal-access-tokens/{pat_id}`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Revoke a personal access token. Returns ``404`` if the token does not exist or belongs to another user (existence is not leaked). **Response:** ``{"ok": true}`` Tenants ------- Tenants provide multi-tenancy via PostgreSQL schema isolation. ``POST /api/tenants`` ~~~~~~~~~~~~~~~~~~~~~~ Create a tenant. Requires a human actor. The creating actor is automatically assigned to the new tenant. .. code-block:: json {"name": "Acme Corp"} **Response:** .. code-block:: json { "tenant_id": "uuid", "name": "Acme Corp", "schema_name": "tenant_acme_corp", "status": "active" } ``GET /api/tenants/{tenant_id}`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Get tenant info. The authenticated actor must belong to the tenant. See also -------- - :doc:`index` — authentication overview - :doc:`../cli/login` — ``moot login`` uses a PAT to authenticate the CLI - :doc:`../../concepts/credentials` — how keys and tokens relate