Skip to content
For LLMsView as Markdown·

Team Management Reference

Member and invite endpoints that power the dashboard Team page (organisation-level logins). See the Team Management guide for the operator-facing walkthrough.

Base path: /functions/v1/onehazel-api

Authentication: the /operators/me/* endpoints use a Supabase session JWT (Authorization: Bearer <jwt>) — they're the same calls the dashboard makes while you're signed in. The acting user's role (owner / admin / member) is resolved from their profile and enforced server-side. The two /invites/* endpoints below are public and authenticated by the invite token itself.

Roles

  • Owner — full control; exactly one per org; can't be revoked or demoted here.
  • Admin — can invite, re-role, revoke, and reinstate Members only.
  • Member — no management rights.

Management endpoints return 403 FORBIDDEN when the caller isn't an Owner or Admin, and when an Admin tries to act on an Owner or another Admin.


List members {#list-members}

GET /operators/me/members

Returns the roster of everyone in the caller's organisation. Available to any member of the org.

Response

json
{
  "success": true,
  "data": {
    "members": [
      {
        "id": "0f8e…",
        "authUserId": "b2c3…",
        "email": "owner@acme.com",
        "displayName": "Alice Owner",
        "role": "owner",
        "status": "active",
        "createdAt": "2026-05-01T09:00:00.000Z",
        "isSelf": true,
        "isOwner": true
      },
      {
        "id": "1a2b…",
        "authUserId": "c3d4…",
        "email": "bob@acme.com",
        "displayName": "Bob Member",
        "role": "member",
        "status": "active",
        "createdAt": "2026-06-10T12:00:00.000Z",
        "isSelf": false,
        "isOwner": false
      }
    ],
    "count": 2
  }
}

status is active or suspended (a revoked member).


List pending invites {#list-invites}

GET /operators/me/invites

Lists pending email invites for the org. Owner/Admin only.

Response

json
{
  "success": true,
  "data": {
    "invites": [
      {
        "id": "inv_db_id",
        "email": "carol@acme.com",
        "role": "member",
        "status": "pending",
        "expiresAt": "2026-06-25T12:00:00.000Z",
        "expired": false,
        "createdAt": "2026-06-18T12:00:00.000Z"
      }
    ],
    "count": 1
  }
}

Invite a member {#invite}

POST /operators/me/invites

Creates an invite, emails the recipient a link, and returns the link once. Owner/Admin only. Admins may only invite the member role.

Request

FieldTypeRequiredDescription
emailstringYesThe invitee's email address
rolestringYesadmin or member (Admins can only assign member)

Response (201)

json
{
  "success": true,
  "data": {
    "id": "inv_db_id",
    "email": "carol@acme.com",
    "role": "member",
    "expiresAt": "2026-06-25T12:00:00.000Z",
    "acceptUrl": "https://app.onehazel.com/accept-invite?token=…",
    "token": "inv_…"
  }
}

The raw token and acceptUrl are returned only on creation — the dashboard surfaces them as a copy-able link in case the email is delayed. Invites expire 7 days after creation. Re-inviting the same email revokes the prior pending invite first.

Errors

HTTPCodeWhen
400INVALID_INPUTMissing/invalid email, or role not admin/member
403FORBIDDENCaller isn't Owner/Admin, or an Admin tried to invite an Admin
409ALREADY_MEMBERThat email already has a OneHazel account

Resend an invite {#resend}

POST /operators/me/invites/:id/resend

Issues a fresh token (invalidating the old link), updates the expiry, and re-emails it. Owner/Admin only. Returns the new acceptUrl + token.

json
{
  "success": true,
  "data": { "id": "inv_db_id", "expiresAt": "…", "acceptUrl": "…", "token": "inv_…" }
}

Returns 404 NOT_FOUND if there's no pending invite with that id in the org.


Revoke an invite {#revoke-invite}

DELETE /operators/me/invites/:id

Cancels a pending invite; its link stops working. Owner/Admin only.

json
{ "success": true, "data": { "id": "inv_db_id", "status": "revoked" } }

Change role / reinstate a member {#patch-member}

PATCH /operators/me/members/:id

Changes a member's role and/or reinstates a suspended member. Owner/Admin only, subject to the role rules (Admins act on Members only; nobody acts on the Owner or themselves).

Request

FieldTypeRequiredDescription
rolestringNoNew role: admin or member (Owner only may set admin)
statusstringNoOnly accepts "active" — reinstates a suspended member

At least one of role or status must be provided.

Response

json
{ "success": true, "data": { "id": "0f8e…", "role": "admin", "status": "active" } }

Errors

HTTPCodeWhen
400INVALID_INPUTActing on yourself, bad role, or status other than active
403FORBIDDENCaller can't manage this target (e.g. Admin acting on an Admin/Owner)
404NOT_FOUNDNo such member in the org

Revoke (suspend) a member {#revoke-member}

DELETE /operators/me/members/:id

Suspends a member — they're signed out and blocked from signing back in until reinstated (via PATCH with status: "active"). Owner/Admin only.

json
{ "success": true, "data": { "id": "0f8e…", "status": "suspended" } }

Errors

HTTPCodeWhen
400INVALID_INPUTYou can't revoke your own access
403OWNER_PROTECTEDThe Owner can't be revoked
403FORBIDDENCaller can't manage this target
404NOT_FOUNDNo such member in the org

API keys are not revoked

Suspending a member does not disable any API keys they created — keys belong to the organisation. Revoke those separately via DELETE /api-keys.


Look up an invite (public) {#lookup}

GET /invites/lookup?token=<token>

Public, token-authenticated. Used by the Accept Invite page to validate a link before showing the form. Always returns 200 with a valid flag (it never reveals whether a token "exists").

json
{
  "success": true,
  "data": { "valid": true, "email": "carol@acme.com", "role": "member", "orgName": "Acme" }
}

When not usable:

json
{ "success": true, "data": { "valid": false, "reason": "expired" } }

reason is one of invalid, expired, accepted, or revoked.


Accept an invite (public) {#accept}

POST /invites/accept

Public, token-authenticated. Creates the invitee's login inside the inviting organisation and marks the invite used. After a successful call the dashboard signs the new user in.

Request

FieldTypeRequiredDescription
tokenstringYesThe invite token from the link
passwordstringYesMinimum 8 characters
first_namestringYesThe new user's first name
last_namestringYesThe new user's last name

Response

json
{ "success": true, "data": { /* new member session */ } }

Errors

HTTPCodeWhen
400INVALID_TOKENToken malformed or no longer valid
400INVALID_INPUTPassword under 8 characters
409INVITE_USEDInvite already accepted
410INVITE_EXPIREDInvite past its 7-day window