Workspaces API

Manage workspaces, members, and invites. Workspaces are called teams in the API: endpoints live under /teams and requests carry team_id. Every API key belongs to a workspace, and apps, tasks, secrets, and billing are scoped to it.

Requires API key scopes teams:read (list, get, members, invites) or teams:write (create, update, delete, member changes, invite management). Create keys in settings → workspace → api keys.

→ Authentication · Entitlements: plan limits per workspace


Get current user

GET /me

Returns the authenticated user and the current workspace (team) resolved from the API key (or guest context). Requires a valid API key.

Response

json
1{2  "user": {3    "id": "usr_abc123",4    "email": "[email protected]",5    "name": "you",6    "full_name": "Alex Example",7    "default_team_id": "team_personal",8    "avatar_url": "https://...",9    "metadata": {10      "completed_onboarding": true,11      "use_case": "agents",12      "signup_source": "github",13      "terms_accepted_at": "2026-01-15T10:30:00Z",14      "terms_version": "2026-01"15    }16  },17  "team": {18    "id": "team_xyz",19    "type": "team",20    "name": "Acme",21    "username": "acme",22    "setup_completed": true,23    "status": "active"24  },25  "org": {26    "id": "org_abc",27    "slug": "acme",28    "name": "Acme Inc",29    "avatar_url": "https://..."30  },31  "team_view": {32    "team_id": "team_xyz",33    "kind": "org_member",34    "governance": {35      "billing": { "by": "org", "team_id": "org_abc" },36      "policy": { "by": "org", "team_id": "org_abc" }37    },38    "org": {39      "id": "org_abc",40      "name": "Acme Inc",41      "slug": "acme",42      "avatar_url": "https://...",43      "can": ["manage_org", "manage_billing", "view_billing"]44    },45    "can": ["edit_profile", "manage_members", "view_billing"]46  },47  "personal_team_id": "team_personal",48  "needs_username": false49}

team is omitted when no workspace context is attached to the request.

org is present when the current workspace belongs to an organization. org.is_admin is not set on /me — use team_view (or GET /teams/{id}/view) for what the caller may do.

team_view is the same object as GET /teams/{id}/view: workspace kind, who governs billing and usage policy, and the capability strings the API enforces on settings routes. Omitted when there is no workspace context.

The top-level JSON object is typed as MeResponse in the JavaScript, Python, and Swift SDKs (team is a full TeamDTO, not the slimmer relation object from GET /teams). See Python SDK — GET /me and JavaScript SDK — generated types.

FieldDescription
personal_team_idThe caller's personal workspace ID. Omitted or empty for managed accounts, which have no personal workspace.
needs_usernametrue when the personal workspace has not finished setup (username not chosen yet). Always false for managed accounts. These fields describe the personal workspace, not the session workspace in team — for example, someone who accepts an invite may land in the inviter's workspace while needs_username is still true until they complete setup on their personal workspace.

When needs_username is true, pick a username with GET /teams/check-username, then call POST /teams/{personal_team_id}/complete-setup (use the personal_team_id value from this response as {id}).

The nested user.metadata object (UserMetadataDTO) tracks onboarding and consent state:

FieldDescription
completed_onboardingWhether the user finished workspace onboarding
use_case, use_case_reason, use_case_privacyOnboarding survey answers
signup_sourceHow the account was created (e.g. github, google)
terms_accepted_atISO timestamp when the user accepted the current terms (optional)
terms_versionTerms version string the user accepted

JavaScript SDK:

typescript
1// me() returns Response<MeResponse>; the body is on `data`2const { data: { user, team, team_view, org, personal_team_id, needs_username } } =3  await client.teams.me();4// team is TeamDTO; use team_view.can for settings gates

Example:

bash
1curl https://api.inference.sh/me \2  -H "Authorization: Bearer inf_your_key"

Survey

Customer-discovery and open feedback submitted from the CLI (belt feedback) or other clients. Structured prompts are driven by active bounty programs named survey-<question_id>; the CLI reads question text and options from the platform form infsh/<question_id> (first question in the schema). When programs cannot be loaded, the CLI uses built-in fallback wording for the classic IDs below.

List your responses

GET /me/survey

Returns survey answers for the authenticated user. The response lists every structured answer first (discovery, use_case, need, testimonial, and legacy feedback), then the most recent freeform open:* rows, up to 100 rows in total. Open feedback never pushes structured answers off the list. The CLI uses this list to decide which onboarding questions are still pending.

Response:

json
1{2  "responses": [3    {4      "id": "srv_abc123",5      "question_id": "discovery",6      "response": "github",7      "agent": "cursor",8      "source": "cli",9      "context": "belt login, belt app run, belt task get abc123"10    }11  ]12}

Submit a response

POST /me/survey

FieldTypeRequiredDescription
question_idstringYesdiscovery, use_case, need, testimonial, feedback (legacy), or open (freeform)
responsestringYesAnswer text
agentstringNoAgent runtime that submitted the answer (e.g. cursor, claude-code)
sourcestringNoClient identifier (CLI sends cli)
contextstringNoRecent commands that led to submission. The CLI sends the last five belt commands (comma-separated) from ~/.belt/history. Other clients may send a page path or other freeform string.

Each question_id is the platform form infsh/<question_id>, and the form's submit policy decides what a repeat answer does. The structured questions (discovery, use_case, need, testimonial) take one answer per user: a second answer returns 409 already_submitted. The legacy feedback question is closed and returns 409 form_closed. Open feedback (open) takes any number of answers. Which questions the CLI asks depends on bounty configuration (survey-<id> programs); it walks them in program order.

Under the hood, each structured answer is also stored as a form submission to infsh/<question_id>. For general form APIs (create forms, submit JSON answers, list submissions), see Forms API.

Rewards: POST /me/survey records the answer only; it grants nothing, and neither does belt feedback. The response may still include granted_amount and reward_blocked_reason for older clients; they are always 0 and empty. Credits come from the matching survey-<question_id> bounty program through POST /me/bounty. The seeded survey-* programs use proof_type: text, which belt bounty claim does not handle, so claim them with a direct POST /me/bounty call. A program with proof_type: form and proof_form: infsh/<question_id> takes the response.id from this call as proof_id.

Fill-in placeholder text (for example your answer here or <your answer>, copied from CLI help) is rejected with 422 validation_error and nothing is stored.

Example:

bash
1curl -X POST https://api.inference.sh/me/survey \2  -H "Authorization: Bearer inf_your_key" \3  -H "Content-Type: application/json" \4  -d '{5    "question_id": "discovery",6    "response": "github",7    "source": "cli",8    "context": "belt login, belt app run, belt task get abc123"9  }'

Response:

json
1{2  "response": {3    "id": "srv_abc123",4    "question_id": "discovery",5    "response": "github",6    "source": "cli",7    "context": "belt login, belt app run, belt task get abc123"8  }9}

Claim the matching bounty (when configured):

bash
1curl -X POST https://api.inference.sh/me/bounty \2  -H "Authorization: Bearer inf_your_key" \3  -H "Content-Type: application/json" \4  -d '{5    "bounty_id": "survey-discovery",6    "proof_id": "discovery",7    "source": "cli"8  }'

For a proof_type: text program, proof_id is a key you choose (one claim per key and program). For a proof_type: form program, use the response.id from POST /me/survey.

Errors:

HTTPCodeCause
400validation_errorunknown question: <id>: no infsh/<id> form
409already_submittedYou already answered this one-answer question
409form_closedThe question no longer takes answers (legacy feedback)
422validation_errorThe answer fails the question's schema (empty or too short), or is fill-in placeholder text

→ CLI setup — Feedback


Bounties

Incentive programs that grant credits when you complete qualifying actions (for example, deploying a useful app or answering survey questions). Requires authentication.

List active programs

GET /bounties

Returns active bounty programs sorted by notice_priority (ascending). Each program includes its stable name (used as bounty_id when claiming), reward amount, proof requirements, and CLI notice configuration.

The belt CLI calls this endpoint to populate stderr bounty notices. Responses are cached locally for 6 hours (~/.belt/cache/bounty-programs.json); admin edits to notice fields propagate on the next cache refresh without a CLI release. Programs with proof_type: form surface in the notice block and in belt feedback as belt bounty claim <program> prompts until the bounty is claimed; survey-<id> programs stay on the single-question belt feedback path. Reward amounts are per program and may change; belt bounty list shows the live values. See Forms API and CLI setup — Feedback.

Response — array of BountyProgramDTO objects:

FieldDescription
nameProgram ID (for example app-builder, survey-discovery)
descriptionRules shown to users and agents
amount_microcentsReward per claim, in microcents
grant_typeCredit grant type (bounty or survey)
expiry_daysDays until granted credits expire
max_per_userTotal claims per user (0 = unlimited). Independent of max_per_day — surveys default to 1.
max_per_dayDaily claim cap (0 = no daily cap, unique proof only)
proof_typeRequired proof: app, form, text, or none
proof_formWhen proof_type is form, the form ref (namespace/name) the submission must answer
requires_payment_methodWhen true, claims return 402 until the team saves a payment method (no claim row is stored, so you can retry after adding a card)
statusactive, paused, or ended
notice_textMessage shown in CLI stderr notices
notice_cooldown_hoursHours between notice displays
notice_priorityLower values surface first when multiple notices are due

The default app-builder program (seeded on new deployments) pays $2 per claim (amount_microcents: 200000000), with max_per_day: 1 (one successful claim per calendar day) and unlimited total claims across different apps (max_per_user: 0). CLI stderr notices use the program's notice_text (for example, pointing at belt bounty claim app-builder <namespace/app>).

Claim a bounty

POST /me/bounty

FieldTypeRequiredDescription
bounty_idstringYesProgram name (for example app-builder)
proof_idstringYesProof identifier — see below by proof_type
agentstringNoAgent runtime that submitted the claim (CLI sends detected runtime)
sourcestringNoClient identifier (CLI sends cli)
proof_typeproof_id meaningStored proof_ref
appApp ID you own with a deployed versionApp namespace/name
formYour submission id to the program's proof_formThat form's namespace/name
text, noneFreeform proof key (for example a question slug)Same as proof_id

For proof_type: app, resource_id in the response is the app ID. For proof_type: form, resource_id is the form ID and proof_id is the submission ID.

Response:

json
1{2  "submission": {3    "id": "bsm_abc123",4    "bounty_id": "app-builder",5    "resource_id": "01abc123def456",6    "proof_id": "01abc123def456",7    "proof_ref": "you/my-app",8    "agent": "cursor",9    "source": "cli"10  },11  "granted_amount": 20000000012}
submission fieldDescription
resource_idLinked resource ID when applicable — for proof_type: app, the app ID
proof_idStable proof key — app ID, form submission ID, or text key
proof_refHuman-readable reference (namespace/name for apps and form proofs)

granted_amount is in microcents (200000000 = $2.00 for the default app-builder reward). Omitted or 0 when no credits were granted (for example, duplicate claim).

Errors:

HTTPCodeMessageCause
400validation_errorproof_id is requiredMissing proof_id
400validation_errorapp must be deployed before claiming a bountyApp has no deployed version
400validation_errorsubmission … answers …; bounty … takes a submission to …Form submission is not for this program's proof_form
402payment_method_requiredThe program sets requires_payment_method and the team has no saved card; nothing is recorded
403forbiddenform submission … is not yoursproof_id names another user's form submission
404not_foundunknown bounty: <id>No program with that name
404not_foundapp not found: <id>App does not exist or is not yours
404not_foundform submission not found: …Unknown submission id
409conflictbounty program <id> is not activeProgram paused or ended
409conflictdaily bounty limit reached — try again tomorrowmax_per_day reached
409already_claimedbounty already claimed for this proofSame proof_id already claimed for this program
409already_claimedbounty limit reached — you've already claimed this N time(s)max_per_user reached

When the team has no saved payment method and the program sets requires_payment_method, the API returns 402 with code payment_method_required and meta.billing_page set to /settings/team/billing/payments. The claim is rejected and not stored, so you can call POST /me/bounty again with the same proof_id after adding a card.

Example:

bash
1curl -X POST https://api.inference.sh/me/bounty \2  -H "Authorization: Bearer inf_your_key" \3  -H "Content-Type: application/json" \4  -d '{5    "bounty_id": "app-builder",6    "proof_id": "01abc123def456",7    "source": "cli"8  }'

Survey rewards use the same bounty system (bounty_id = survey-<question_id>) but are claimed explicitly via POST /me/bounty after the answer is stored — see Survey and Forms API.

Bounty program administration (creating programs, pausing, viewing claims) is internal and not exposed on the public REST API.

SDKs: No client.bounties namespace yet in either SDK. Import BountyProgramDTO, SubmitBountyRequest, and SubmitBountyResponse from inferencesh.types (Python) or @inferencesh/sdk (JavaScript) when calling these endpoints directly — see Python SDK — generated bounty types and JavaScript SDK — generated bounty types.

→ CLI setup — App bounties · Billing — credit grants


Data export

Request a ZIP archive of your account and workspace data (GDPR Articles 15 and 20: right of access and data portability). Requires authentication as the account owner (session token or API key).

Request export

POST /me/export

Starts an asynchronous export job. Returns 202 Accepted with the export ID and initial status.

Response:

json
1{2  "id": "uex_abc123",3  "status": "pending"4}

If you already have a pending or processing export from the last 24 hours, the API returns that existing job instead of starting a duplicate.

Get export status

GET /me/export/{id}

Poll until status is completed or failed. When complete, the response includes a presigned download_url (valid for one hour).

FieldDescription
idExport job ID
statuspending, processing, completed, or failed
download_urlPresigned URL to the ZIP (when completed)
completed_atISO timestamp when the archive was ready
expires_atWhen the download link expires (seven days after completion)
errorFailure message (when failed)

Example:

bash
1# Start export2curl -X POST https://api.inference.sh/me/export \3  -H "Authorization: Bearer $(belt auth token)"45# Poll for download URL6curl https://api.inference.sh/me/export/uex_abc123 \7  -H "Authorization: Bearer $(belt auth token)"

The ZIP contains JSON files for your user profile, sessions (tokens redacted), API keys (masked), workspace resources (apps, agents, flows, knowledge, projects, chats, tasks, files, secrets, integrations, triggers), billing, notifications, and survey responses. Processing may take several minutes; the server enforces a 10-minute timeout per job.


List workspaces

GET /teams

Returns all workspaces the authenticated user belongs to (personal workspace, shared workspaces, and organization workspaces you administer).

Requires teams:read.

Response

Array of workspace objects:

FieldDescription
idWorkspace ID
typepersonal, team, org, or system. See Workspace types.
nameDisplay name
usernameUnique workspace username (used in namespaces and belt auth switch)
avatar_urlAvatar URL
setup_completedWhether the workspace finished onboarding (username chosen)
statusWorkspace status
emailContact email (when set)
org_idOrg this workspace belongs to. An org workspace points at itself. Omitted for standalone workspaces.
max_concurrencyPlatform-managed field (usually 0). Your workspace's concurrent task limit comes from the concurrency entitlement on your subscription plan. See Entitlements: concurrent task limits

Workspace types

typeDescription
personalPersonal workspace, one per account
teamShared workspace for collaborators. When attached to an org, org_id is the org's ID.
orgAn organization's own workspace. Created with the org, with the same ID as the org, and org_id points at itself. Its members are the org's admins, so it has owners and admins only. Holds org billing and org-scope credentials.
systemInternal platform workspace

Example:

bash
1curl https://api.inference.sh/teams \2  -H "Authorization: Bearer inf_your_key"

JavaScript SDK: client.teams.list()


Get workspace

GET /teams/{id}

Returns a single workspace by ID. Requires teams:read and membership (or admin access).

JavaScript SDK: client.teams.get(teamId)


Workspace settings view

GET /teams/{id}/view

Returns how the authenticated caller sees a workspace in settings: workspace kind, billing/policy governance, optional parent organization summary, and the can capability list the API uses to gate POST/PUT/DELETE routes under /teams/{id} and related org routes.

Requires teams:read. A caller with no role on the workspace (directly or through org standing) gets 404. This is the source of truth for UI permission checks — clients should not re-derive capabilities from roles alone.

Response (TeamViewDTO)

FieldDescription
team_idWorkspace ID (same as path parameter)
kindCaller-relative kind: personal, team (standalone shared workspace), org_member (workspace inside an org), or org (the organization's own workspace)
governance.billingWho decides billing: by is self or org; team_id is the workspace that holds the balance
governance.policyWho decides usage policy: same shape as governance.billing
orgWhen the workspace belongs to an org: id, name, slug, avatar_url, and can — capabilities on the organization workspace (billing, org admin actions, org credentials). Omitted for standalone workspaces
canCapability strings the caller holds on this workspace (for example manage_members, manage_billing, manage_org)

Capability strings

The can arrays use the same identifiers the API evaluates on settings routes:

CapabilityTypical meaning
edit_profileRename workspace, avatar, username setup
manage_membersInvite, remove, and change roles for plain members (admins and owners)
manage_adminsGrant, change, or remove admin and owner roles (owners only; omitted on personal workspaces)
view_membersRead the member roster
manage_keysWorkspace API keys: create workspace keys, list and revoke every key of the workspace
create_keysCreate, list, and revoke your own personal keys. Absent for service accounts
manage_vaultVault membership and secrets layout
view_billingRead balance, usage, invoices
manage_billingTop-ups, payment methods, subscription checkout
view_policyRead effective usage policy
manage_policyEdit usage policy (org-wide on the org workspace; per-workspace on standalone teams)
manage_orgOrg admin actions (list workspaces, attach/create teams, managed users, org SSO)
manage_ssoTeam SAML connection
archiveDelete/archive workspace
create_teamPOST /teams — create a standalone workspace. Absent on managed accounts (use POST /orgs/{id}/teams/create instead)
create_orgPOST /orgs — create an organization from a standalone workspace you own
connect_org_credentialConnect org-scoped integrations
connect_platform_credentialPlatform admin: platform-scoped credentials

On an org member workspace, can is the capability table for this workspace: your roster role, or — when you administer the org — your role on the organization workspace raised to that level (org owners act as owners on every member team; org admins as admins, without joining the roster). org.can carries rights on the organization workspace itself (for example manage_billing when you may change org payment settings). Do not use org.can to decide admin invites or role changes on a member workspace. Member-team owners and org owners acting through standing see view_billing on the member team but not manage_billing there when the org pays. Clients should use can from this endpoint (or from GET /orgs/{id}/teams per workspace) instead of inferring permissions from membership alone. Last-owner checks still count only listed owners on the workspace, not org standing.

Example:

bash
1curl "https://api.inference.sh/teams/team_xyz/view" \2  -H "Authorization: Bearer inf_your_key" \3  -H "X-Team-ID: team_xyz"

GET /me embeds the same payload as team_view for the API key's current workspace.

→ Organizations API: create orgs, list member workspaces, org admins


Workspace governance rules (usage policy)

A workspace's usage policy is its governance layer in the rules API: GET, PUT, and DELETE /teams/{id}/rules, single-rule routes under /teams/{id}/rules/{ruleId}, member policies under /teams/{id}/members/{userId}/rules, the blocked-attempts feed, and effective access on /teams/{id}/rules/access. Reads need view_policy; writes need manage_policy, which in an organization belongs to the org admins. See the Usage policy API.


Create workspace

POST /teams

Requires teams:write and the create_team capability on your current workspace context (see GET /teams/{id}/view). Managed accounts do not receive create_team; org admins create workspaces inside an org with POST /orgs/{id}/teams/create.

Request

FieldRequiredDescription
nameyesDisplay name
usernameyesUnique workspace username (kebab-case)
emailnoContact email
json
1{2  "name": "Acme Engineering",3  "username": "acme-eng",4  "email": "[email protected]"5}

Returns the created workspace object. New workspaces may need Complete setup before all features are available.

JavaScript SDK: client.teams.create({ name, username, email })


Update workspace

POST /teams/{id}

Updates workspace metadata. Requires teams:write and appropriate role (owner or admin).

JavaScript SDK: client.teams.update(teamId, { name, username, email })


Delete workspace

DELETE /teams/{id}

Permanently deletes a workspace. Requires teams:write and owner role. Personal workspaces cannot be deleted.

JavaScript SDK: client.teams.delete(teamId)


Check username availability

GET /teams/check-username?username={name}

Returns an AvailabilityResponse. Use before create or rename.

QueryRequiredDescription
usernameYesProposed username (normalized to lowercase server-side)
exclude_team_idNoWorkspace ID to ignore. Use when updating so the workspace's current username does not count as a conflict

Requires teams:read.

Response:

FieldTypeDescription
valuestringNormalized username (lowercased)
availablebooleantrue if the username can be used
reasonstringWhen available is false: taken (username in use) or reserved (on the platform reserved-name list). Omitted when available.

Use reason for user-facing messages instead of maintaining a client-side reserved-name list — the server owns the full reserved namespace.

json
1{2  "value": "acme-eng",3  "available": false,4  "reason": "taken"5}

JavaScript SDK: client.teams.checkUsername('acme-eng')


Complete setup

POST /teams/{id}/complete-setup

Chooses the workspace username during onboarding. For a new self-serve account, {id} is personal_team_id from GET /me while needs_username is true.

Request

json
1{2  "username": "acme"3}

Requires teams:write.


Members

List members

GET /teams/{id}/members

Returns workspace members with roles. Requires teams:read.

FieldDescription
idMembership row ID
user_idMember user ID
team_idTeam ID
roleowner, admin, or member
userNested user profile (email, name, avatar_url)
assignable_rolesRoles the authenticated caller may set on this member, including their current role. Populated only when the caller may manage members. Empty or omitted when the caller cannot change anyone (for example, a member), when an admin views an owner they cannot demote, or when the sole owner cannot step down until another owner exists. Only owners may include owner in this list.
removableWhether the caller may remove this member. true only when removal is allowed (the last owner on a team is never removable). Omitted when the caller cannot manage members.

These fields mirror the same checks enforced by POST /teams/{id}/members/{userId}/role and DELETE /teams/{id}/members/{userId}.

Use them to drive member-management UIs without reimplementing server rules:

  • Admins may add, change, and remove plain members (manage_members). Granting, changing, or removing admin or owner requires manage_admins (owners only).
  • A team must keep at least one owner. Demoting or removing the last owner returns 400 with code last_owner.
  • The organization workspace accepts owner and admin only — member is not assignable there.

Member mutations require teams:write and route-level capability checks (manage_members for roster changes; the service enforces manage_admins when the target role is admin or owner). Refusals return 403 with code team_role_required (often with meta.capability set). See Workspace settings view instead of inferring rights from role names alone.

JavaScript SDK: client.teams.getMembers(teamId) — typed as TeamMemberDTO with assignable_roles and removable.

Python SDK: import TeamMemberDTO from inferencesh.types when calling this endpoint with httpx or requests (no client.teams namespace yet).

Add member

POST /teams/{id}/members

json
1{2  "email": "[email protected]",3  "role": "member"4}

Requires teams:write and manage_members (and manage_admins when role is admin or owner). Adds an existing platform user by email, or creates a pending invite when the email is not registered. The email value is trimmed and lowercased before lookup; matching is case-insensitive. For Gmail and Googlemail, the API also treats addresses that deliver to the same mailbox as one address when checking for an existing member (see REST overview: Signup email restrictions).

JavaScript SDK: client.teams.addMember(teamId, { email, role })

Remove member

DELETE /teams/{id}/members/{userId}

Requires teams:write and manage_members (or manage_admins when removing an admin or owner). Also revokes the removed user's personal API keys on that workspace; workspace keys they created keep working. See API keys — when someone leaves.

JavaScript SDK: client.teams.removeMember(teamId, userId)

Update member role

POST /teams/{id}/members/{userId}/role

json
1{2  "role": "admin"3}

Requires teams:write and manage_members / manage_admins as for Add member.

JavaScript SDK: client.teams.updateMemberRole(teamId, userId, 'admin')


Invites

List workspace invites

GET /teams/{id}/invites

Returns pending invites for a workspace. Requires teams:read.

JavaScript SDK: client.teams.listInvites(teamId)

Create invite

POST /teams/{id}/invites

json
1{2  "email": "[email protected]",3  "role": "member"4}

Requires teams:write. Pending invites store the invited address trimmed and lowercased. A second invite to the same address replaces the earlier pending invite for that workspace. Invite emails are limited to 3 per hour per address (letter case and + aliases count as one address) and 100 per workspace per day; past either limit the API returns 429 rate_limited.

JavaScript SDK: client.teams.createInvite(teamId, { email, role })

Revoke invite

DELETE /teams/{id}/invites/{inviteId}

Requires teams:write.

JavaScript SDK: client.teams.revokeInvite(teamId, inviteId)

List my pending invites

GET /invites/me

Returns invites addressed to the authenticated user's email. Requires teams:read.

Get invite info (public)

GET /invites/info?token={token}

Returns a TeamInviteInfoDTO for the accept page. No API key required — the invite token is the credential.

Response is a TeamInviteInfoDTO:

FieldDescription
idInvite ID
emailAddress the invite was sent to
roleRole the invitee receives on accept
statusStanding now: a pending invite past expires_at reads expired; an invite to a workspace that no longer admits members reads revoked
expires_atISO timestamp when a pending invite stops being valid
teamWorkspace summary: id, type (personal, team, or org), name, avatar_url
org_nameOrganization name when the workspace is a member team inside an org. Omitted for standalone workspaces and for the org's own workspace
invited_byDisplay name of the inviter, as the invite email names them
HTTP statusWhenClient behavior
400token query parameter is missingFix the link — it must include ?token=…
404No invite matches the tokenTreat the link as invalid (truncated URL, revoked invite, or wrong token)
5xxServer error while loading invite infoThe token may still be valid; retry the lookup

Only 404 means the link does not work; other failures say nothing about whether the invite stands, so the workspace invite page offers try again for them.

Accept invite

POST /invites/accept

Requires authentication. Pass the invite token in the JSON body or token query parameter:

json
1{2  "token": "invite_token_from_email"3}

Returns the new TeamMemberDTO.

Decline invite

POST /invites/decline

Pass the same token shape. Does not require the invitee to be logged in.


Roles

RoleTypical permissions
ownerFull workspace control, billing, delete workspace
adminManage members, settings, and workspace resources
memberUse workspace resources within plan limits

Owners and admins can list and manage other members' private resources, for example after a member leaves. Pass include_private: true on cursor-list requests, or access an individual resource by ID. Both paths are audited. See REST overview: Workspace admin visibility.


we use cookies

we use cookies to ensure you get the best experience on our website. for more information on how we use cookies, please see our cookie policy.

by clicking "accept", you agree to our use of cookies.
learn more.