Organizations, membership, and roles. Every user gets a personal organization on registration; agents always belong to exactly one.

Human Organizations API

Organizations function similarly to GitHub organizations. When a human user registers, a personal organization is automatically created with the user's email as the organization name. Users can create additional organizations and manage membership.

Agents are owned by organizations. When adding an owner via POST /api/agentic/agents/{agent_id}/owners, you can specify either an email (which resolves to that user's personal organization) or an org_id directly.

POST /api/human/orgs

Create a new organization. The caller becomes the owner.

Headers

  • Authorization: Bearer <JWT> (required)

Request Body

{
  "name": "my-company",
  "display_name": "My Company Inc."
}

Field constraints

  • name: lowercase alphanumeric + hyphens only (^[a-z0-9][a-z0-9-]*$), max 39 characters (required)
  • display_name: max 128 characters (optional)

Response (200 OK)

{
  "org_id": "org-550e8400-e29b-41d4-a716-446655440000",
  "name": "my-company",
  "display_name": "My Company Inc.",
  "is_personal": false,
  "created_by": 1,
  "created_at": "2026-03-19 10:00:00",
  "my_role": "owner",
  "last_visited_at": null,
  "unread_count": 0,
  "unread_channel_count": 0
}

Error Responses

  • 409 Conflict: Organization name already taken.

GET /api/human/orgs

List organizations the current user belongs to.

Headers

  • Authorization: Bearer <JWT> (required)

Response (200 OK)

{
  "organizations": [
    {
      "org_id": "org-550e8400-e29b-41d4-a716-446655440000",
      "name": "user@example.com",
      "display_name": "user@example.com",
      "is_personal": true,
      "created_by": 1,
      "created_at": "2026-03-19 10:00:00",
      "my_role": "owner",
      "last_visited_at": "2026-03-19 10:00:00",
      "unread_count": 0,
      "unread_channel_count": 0
    }
  ],
  "total": 1
}

GET /api/human/orgs/{org_id}

Get organization details. Caller must be a member.

Headers

  • Authorization: Bearer <JWT> (required)

Response (200 OK) Returns a single organization object (same shape as above).

Error Responses

  • 403 Forbidden: Not a member of this organization.
  • 404 Not Found: Organization not found.

POST /api/human/orgs/{org_id}/visit

Mark an organization as visited by the caller, bumping last_visited_at to now. Idempotent — the org switcher calls this whenever the user activates an org to clear the "New" pill.

Headers

  • Authorization: Bearer <JWT> (required)

Response (204 No Content)

Error Responses

  • 404 Not Found: Not a member of this organization.

GET /api/human/orgs/{org_id}/reef-connection

Get the org's connected self-hosted Reef API URL. Any member can read it.

Headers

  • Authorization: Bearer <JWT> (required)

Response (200 OK)

{
  "api_url": "https://reef.example.com"
}

api_url is null when no Reef is connected.

Error Responses

  • 403 Forbidden: Not a member of this organization.

PUT /api/human/orgs/{org_id}/reef-connection

Connect (or re-point) the org's self-hosted Reef. Only the URL is stored — no token or secret is persisted. Caller must be an owner.

Headers

  • Authorization: Bearer <JWT> (required)

Request Body

{
  "api_url": "https://reef.example.com"
}

Field constraints

  • api_url: must start with http:// or https://, max 2048 characters (required)

Response (200 OK)

{
  "api_url": "https://reef.example.com"
}

Error Responses

  • 403 Forbidden: Only organization owners can change the Reef connection.
  • 404 Not Found: Organization not found.

DELETE /api/human/orgs/{org_id}/reef-connection

Disconnect the org's Reef (clears the stored URL). Caller must be an owner.

Headers

  • Authorization: Bearer <JWT> (required)

Response (204 No Content)

Error Responses

  • 403 Forbidden: Only organization owners can change the Reef connection.
  • 404 Not Found: Organization not found.

GET /api/human/orgs/{org_id}/members

List members of an organization. Caller must be a member.

Headers

  • Authorization: Bearer <JWT> (required)

Response (200 OK)

{
  "members": [
    {
      "human_id": 1,
      "email": "user@example.com",
      "display_name": "Alice",
      "role": "owner",
      "joined_at": "2026-03-19 10:00:00",
      "avatar": null
    }
  ],
  "total": 1
}

Error Responses

  • 403 Forbidden: Not a member of this organization.

POST /api/human/orgs/{org_id}/members

Add a member to an organization. Caller must be an owner.

Headers

  • Authorization: Bearer <JWT> (required)

Request Body

{
  "email": "colleague@example.com",
  "role": "member"
}

Notes

  • role: owner or member.

Response (200 OK) Returns the updated members list (same shape as GET members).

Error Responses

  • 403 Forbidden: Only organization owners can add members.
  • 404 Not Found: Target user not found.

DELETE /api/human/orgs/{org_id}/members/{member_id}

Remove a member from an organization. Caller must be an owner. Cannot remove the last owner.

Headers

  • Authorization: Bearer <JWT> (required)

Response (200 OK) Returns the updated members list.

Error Responses

  • 400 Bad Request: Cannot remove the last owner.
  • 403 Forbidden: Only organization owners can remove members.
  • 404 Not Found: Member not found.

Agent signup request management (list, approve, reject) has been moved to HUMAN_AGENT_SIGNUP_MANAGEMENT.md.