Your AI Connector Docs

Team API

Your team is everyone who works inside your account besides you — admins, agents and read-only viewers — plus the invitations you have sent and the departments you organise them into. The Team API is the programmatic version of Settings → Team: add and remove people, set what each of them can see and do, send and chase invitations, and manage departments.

All endpoints below are relative to the base URL https://api.youraiconnector.com/v1. For the dashboard version of everything on this page, see Team Management.


Authentication: these endpoints need a signed-in person

This is the one part of the API that an API key cannot use. Every /team endpoint except the department ones has to be called with a Firebase ID token from a signed-in session:

Authorization: Bearer <Firebase ID token>

Send an API key instead and the request is rejected with a 401:

{
  "success": false,
  "error_code": 401,
  "error": "This endpoint requires a Firebase ID token (Authorization: Bearer <token>)."
}

The reason is that these endpoints decide what to do based on who is signed in: your role, the ceiling on what you are allowed to grant someone else, and whether you are currently working inside another account. An API key is an integration, not a person, so there is nobody for those rules to apply to.

In practice that means the Team API is for a first-party app with a logged-in Your AI Connector user (see Authentication → Firebase ID token). A server-to-server integration cannot manage team members — there is no way to mint one of these tokens from outside the app.

The exception: the four department endpoints are ordinary API endpoints. They accept your API key exactly like the rest of the API, as well as a signed-in session.

Every response on this page follows the usual envelope: success: true plus the endpoint’s fields at the top level, or success: false with error and error_code when something goes wrong.


Roles and permissions

Every team member has one role, which sets their default access across 12 areas of the app. You can then override individual areas.

Role Value Summary
Admin admin Everything except the owner’s billing-level actions.
Editor editor Can create and change things. Shown as Agent in the app.
Viewer viewer Read-only.

Each area is set to one of four levels: none (hidden), view (read-only), edit (create and change), full (including deleting).

Area Admin Editor Viewer
campaigns full edit view
contacts full edit view
messages full edit view
appointments full edit view
settings edit view none
billing edit none none
team_management edit none none
analytics full view view
phone_numbers edit none none
integrations edit none none
faqs full edit view
daily_summaries full view view

To depart from the role’s defaults, send permission_overrides — an array of { "area": ..., "level": ... } objects. Each entry replaces the role’s default for that one area; everything you do not list keeps the role default.

"permission_overrides": [
  { "area": "analytics", "level": "full" },
  { "area": "billing", "level": "none" }
]

Who can call these endpoints

  • The account owner can always do everything.
  • A team member needs team_management at view to read the roster and the invite list, and at edit to add, change, suspend, remove, invite, cancel or resend. Admins have edit by default; editors and viewers have none, so by default only admins can manage the team.
  • Nobody can grant access above their own. If you try to give someone a level you do not hold yourself — or to edit, suspend or remove someone whose access is already broader than yours — the request is refused with 403 and a message naming the area.

The team member object

GET /team/members returns one of these per member:

Field Type Description
member_uid string The member’s own user ID. This is the {memberUid} in the paths below.
account_owner_uid string The account they are a member of.
member_email string Their email address.
member_display_name string The name shown for them in the app.
role string admin, editor or viewer.
permission_overrides array Their per-area exceptions. [] when they are purely on role defaults.
status string active or suspended.
auto_assign_enabled boolean | null Whether new contacts can be auto-assigned to them. null means never changed, which behaves as true.
created_by string Who added them.
created_at string | null ISO 8601 timestamp.
updated_at string | null ISO 8601 timestamp.

Removed members are not returned — the list is active and suspended members only.

Visibility limits are write-only here. contact_scope, contact_scope_axes and sub_account_access (see Limiting what a member can see) can be set on create, update and invite, but this endpoint does not return them.


List team members

GET /team/members

Returns the roster plus your plan’s seat counts, so you can show “3 of 5 seats” and know when inviting is about to be refused.

cURL

curl "https://api.youraiconnector.com/v1/team/members" \
  -H "Authorization: Bearer FIREBASE_ID_TOKEN"

JavaScript

const res = await fetch("https://api.youraiconnector.com/v1/team/members", {
  headers: { Authorization: `Bearer ${idToken}` },
});
const { members, seat_limit, seats_used } = await res.json();

Python

import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/team/members",
    headers={"Authorization": f"Bearer {id_token}"},
)
data = res.json()

Response

{
  "success": true,
  "members": [
    {
      "account_owner_uid": "owner_uid_123",
      "member_uid": "uid_alice",
      "member_email": "alice@example.com",
      "member_display_name": "Alice Chen",
      "role": "admin",
      "permission_overrides": [],
      "status": "active",
      "auto_assign_enabled": true,
      "created_by": "owner_uid_123",
      "created_at": "2026-05-01T10:00:00.000Z",
      "updated_at": "2026-06-02T09:15:00.000Z"
    }
  ],
  "seat_limit": 5,
  "seats_used": 3
}

seat_limit is null when your plan has no seat cap. seats_used counts active members only — suspending or removing someone frees their seat immediately.


Add a team member directly

POST /team/members

Puts someone on your team straight away, without an invitation.

This sends no email. Nobody is told they were added, and if they did not already have a Your AI Connector login, the account created for them has no password, so they cannot sign in until they reset it. Use Send an invitation unless you have your own way of telling the person and getting them signed in.

Request fields

Field Required Description
email Yes The teammate’s email address.
display_name Yes The name shown for them in the app.
role Yes admin, editor or viewer.
permission_overrides No Per-area exceptions to the role’s defaults.
contact_scope No all or assigned — see Limiting what a member can see.
contact_scope_unassigned No With assigned, also let them see contacts nobody owns yet.
contact_scope_axes No Limit them to named agents, channels or departments.
sub_account_access No Agencies only — which client sub-accounts they may open.

cURL

curl -X POST "https://api.youraiconnector.com/v1/team/members" \
  -H "Authorization: Bearer FIREBASE_ID_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "sam@example.com",
    "display_name": "Sam Rivera",
    "role": "editor"
  }'

JavaScript

const res = await fetch("https://api.youraiconnector.com/v1/team/members", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${idToken}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    email: "sam@example.com",
    display_name: "Sam Rivera",
    role: "editor",
  }),
});
const { member_uid } = await res.json();

Response201 Created

{
  "success": true,
  "team_member_id": "owner_uid_123_uid_sam",
  "member_uid": "uid_sam",
  "message": "Team member created successfully."
}
Status When
400 email, display_name or role is missing, the role is not one of the three, or you tried to add yourself.
403 You do not have permission to manage the team, or you tried to grant access above your own.
409 That person is already an active member of your team.
429 Your plan’s team seats are full.

Adding someone who was previously suspended or removed reinstates them rather than failing.


Update a team member

PATCH /team/members/{memberUid}

Changes a member’s role, permissions, visibility, client access, or whether they take part in automatic contact assignment. Send only the fields you want to change; anything you leave out keeps its current value.

Request fields

Field Description
role admin, editor or viewer.
permission_overrides Replaces their whole override list. Send [] to put them back on pure role defaults.
status Only active is accepted, to bring a suspended member back. To suspend someone, use the suspend endpoint.
auto_assign_enabled true or false.
contact_scope all or assigned.
contact_scope_unassigned true or false.
contact_scope_axes See Limiting what a member can see.
sub_account_access Agencies only.

This is the only endpoint where null means “clear”. Sending "contact_scope": null, "contact_scope_axes": null or "sub_account_access": null removes that limit entirely and puts the member back to seeing everything. On create and invite, null simply means “not supplied”.

cURL

curl -X PATCH "https://api.youraiconnector.com/v1/team/members/uid_sam" \
  -H "Authorization: Bearer FIREBASE_ID_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "role": "admin",
    "permission_overrides": [{ "area": "billing", "level": "none" }]
  }'

Response

{
  "success": true,
  "message": "Team member updated successfully."
}
Status When
400 An invalid status or auto_assign_enabled value, or you tried to reactivate a member who was removed (removed members must be re-invited).
403 You do not have permission, or the change would edit or create access broader than your own.
404 No such team member.

Suspend a team member

POST /team/members/{memberUid}/suspend

Suspends someone: they keep their place on the team but lose access. Use this instead of removing when the pause is temporary — bring them back with PATCH /team/members/{memberUid} and {"status": "active"}.

cURL

curl -X POST "https://api.youraiconnector.com/v1/team/members/uid_sam/suspend" \
  -H "Authorization: Bearer FIREBASE_ID_TOKEN"

Response

{
  "success": true,
  "message": "Team member suspended successfully."
}

A suspended member frees their seat, so you can invite someone else in their place. Their access ends when their current session token next refreshes, which can take up to an hour — remove them instead if you need it to be immediate.

Status When
400 You tried to suspend the account owner, or a member who is already suspended or removed.
403 Their access is broader than yours.
404 No such team member.

Remove a team member

DELETE /team/members/{memberUid}

Removes someone from your team and frees their seat. They are signed out and lose access to your account; their own login is untouched.

cURL

curl -X DELETE "https://api.youraiconnector.com/v1/team/members/uid_sam" \
  -H "Authorization: Bearer FIREBASE_ID_TOKEN"

Response

{
  "success": true,
  "message": "Team member removed successfully."
}

Removal is permanent from your side: a removed member cannot be reactivated with the update endpoint — invite them again if you change your mind. Their email is also taken off your account’s notification list.

Status When
400 You tried to remove the account owner.
403 Their access is broader than yours.
404 No such team member.

Limiting what a member can see

Three optional fields, accepted on add, update and invite, decide how much of the account a person sees. They stack: a member limited on more than one is limited by all of them.

contact_scopeall (the default: every contact and conversation) or assigned (only the ones assigned to them). With assigned, add "contact_scope_unassigned": true to also let them see contacts nobody owns yet.

contact_scope_axes — limits them to named agents, channels or departments:

Field Type Description
agents string[] Agent IDs. They only see chats routed to one of these agents. Max 200.
channels string[] Channel names — whatsapp, whatsapp_web, sms, instagram, instagram_private, messenger, facebook, chat_widget, telegram, line, viber, tiktok, imessage, email, linkedin, skool, custom, custom_channel. Max 200.
departments string[] Department IDs (see Departments). They only see leads filed under them. Max 200.
include_unrouted boolean With agents set, also show chats that no agent handles. Off by default. Ignored when agents is empty.
include_undepartmented boolean With departments set, also show chats that are in no department. Off by default. Ignored when departments is empty.

Agent and department IDs are not checked when you save them — an ID that does not exist simply matches nothing, which shows up as an empty inbox rather than an error. Channel names are checked: an unrecognised one is rejected with 400.

None of these three can be set on the account owner — that request is refused with 400.


List invitations

GET /team/invites

The invitations you have sent, newest first, so you can see who has not accepted yet.

Query parameters

Parameter Required Description
status No Only return invites in this state — pending, accepted, declined, cancelled or expired.

cURL

curl "https://api.youraiconnector.com/v1/team/invites?status=pending" \
  -H "Authorization: Bearer FIREBASE_ID_TOKEN"

Response

{
  "success": true,
  "invites": [
    {
      "id": "inv_abc123",
      "account_owner_uid": "owner_uid_123",
      "account_owner_display_name": "Acme Ltd",
      "invitee_email": "sam@example.com",
      "invitee_uid": null,
      "role": "editor",
      "permission_overrides": [],
      "status": "pending",
      "created_by": "owner_uid_123",
      "created_at": "2026-06-10T12:00:00.000Z",
      "expires_at": "2026-06-17T12:00:00.000Z",
      "responded_at": null
    }
  ]
}

The invitation token is never returned — it only ever exists in the email that was sent.


Send an invitation

POST /team/invites

Emails someone an invitation to join your team. This is the usual way to add a teammate: they click the link, sign in as themselves, and accept. If they do not have a Your AI Connector account yet, one is created for them and the email walks them through setting a password.

Request fields

Field Required Description
email Yes Where to send the invitation.
role Yes admin, editor or viewer.
permission_overrides No Per-area exceptions, applied the moment they accept.
contact_scope No Applied when they accept.
contact_scope_unassigned No Applied when they accept.
contact_scope_axes No Applied when they accept.
sub_account_access No Agencies only. Applied when they accept.

Setting the permissions up front means you do not have to edit the member afterwards — everything is copied onto their membership when they accept.

cURL

curl -X POST "https://api.youraiconnector.com/v1/team/invites" \
  -H "Authorization: Bearer FIREBASE_ID_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "email": "sam@example.com", "role": "editor" }'

JavaScript

const res = await fetch("https://api.youraiconnector.com/v1/team/invites", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${idToken}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ email: "sam@example.com", role: "editor" }),
});
const { invite_id } = await res.json();

Response201 Created

{
  "success": true,
  "invite_id": "inv_abc123",
  "message": "Team invite sent successfully."
}

Things to plan for

  • Invitations expire after 7 days. An expired one can be resent, which starts a fresh 7 days.
  • Pending invitations hold a seat. Unlike adding a member directly, the seat check here counts active members plus pending invitations, so an account with all seats spoken for is refused before the email goes out.
  • 20 invitations per day, counted per account across both sending and resending.
Status When
400 email is missing or the role is invalid.
403 You do not have permission to manage the team, or you tried to grant access above your own.
409 A pending invitation for that email already exists, or that person is already on your team.
429 Your plan’s team seats are full, or you have hit the 20-invitations-a-day limit. The error message says which.

Cancel an invitation

DELETE /team/invites/{inviteId}

Withdraws an invitation before it is accepted. The link in the email stops working.

cURL

curl -X DELETE "https://api.youraiconnector.com/v1/team/invites/inv_abc123" \
  -H "Authorization: Bearer FIREBASE_ID_TOKEN"

Response

{
  "success": true,
  "message": "Team invite cancelled."
}

Both pending and expired invitations can be cancelled. An invitation that was already accepted, declined or cancelled returns 400; one that is not yours returns 403; an unknown ID returns 404.


Resend an invitation

POST /team/invites/{inviteId}/resend

Sends the invitation email again — for when it was missed or went to spam. Works on pending and expired invitations, and resets the expiry to 7 days from now.

cURL

curl -X POST "https://api.youraiconnector.com/v1/team/invites/inv_abc123/resend" \
  -H "Authorization: Bearer FIREBASE_ID_TOKEN"

Response

{
  "success": true,
  "message": "Team invite resent successfully."
}

The new email carries a new link, and the old link keeps working too, so a person who finds the first email later is not stuck. Resending counts against the same 20-a-day limit as sending, and reviving an expired invitation re-checks your seats — a full plan is refused with 429.


Accept an invitation

POST /team/invites/accept

Accepts an invitation with the token from the invitation email, joining the signed-in person to that account’s team.

This is an act of your own identity. Sign in as yourself — it is deliberately refused with 403 while you are working inside somebody else’s account.

Request fields

Field Required Description
invite_token Yes The token from the invitation email link.

cURL

curl -X POST "https://api.youraiconnector.com/v1/team/invites/accept" \
  -H "Authorization: Bearer FIREBASE_ID_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "invite_token": "1f4c…" }'

Response

{
  "success": true,
  "team_member_id": "owner_uid_123_uid_sam",
  "account_owner_uid": "owner_uid_123",
  "message": "Team invite accepted successfully."
}
Status When
400 invite_token is missing, or the invitation is for your own account.
403 The session is working inside another account, or the invitation was sent to a different email address than the one you are signed in with.
404 The invitation does not exist or has already been used.
429 The account’s seats filled up between the invitation and your acceptance.
504 The invitation has expired. Ask the sender to resend it.

Decline an invitation

POST /team/invites/decline

Declines an invitation with the token from the email. Like accepting, this is an act of your own identity and is refused while you are working inside another account.

cURL

curl -X POST "https://api.youraiconnector.com/v1/team/invites/decline" \
  -H "Authorization: Bearer FIREBASE_ID_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "invite_token": "1f4c…" }'

Response

{
  "success": true,
  "message": "Team invite declined."
}

Departments

A department is a named group of your team — Sales, Customer support, HR. It gives a lead an owner team, can claim new conversations on its own, and can be used to limit what a member sees.

These four endpoints take an API key. Unlike the rest of this page, they authenticate like every other endpoint in the API (see Authentication). A signed-in session works too: reading needs contacts at view, and creating, changing or deleting needs team_management at edit.

The department object

Field Type Description
id string The department’s ID. Use it in contact_scope_axes.departments and in the paths below.
name string What the team is called. Up to 60 characters, unique on the account.
color string | null Accent colour as #rrggbb, or null.
member_uids string[] The team members in this department. May include the account owner.
auto_assign_enabled boolean Whether a lead filed under this department is also handed to someone on it. false means the department works from a shared queue.
routing_agents string[] New conversations handled by these AI Agents are filed under this department automatically. Empty means no agent rule.
routing_channels string[] New conversations on these channels are filed here automatically. Empty means no channel rule.
created_by string | null Who created it.

When both routing_agents and routing_channels are set, a conversation has to match both to be filed here — that is how you give one team “the support agent, but only on WhatsApp”.

An account can have up to 50 departments.

List departments

GET /team/departments

curl "https://api.youraiconnector.com/v1/team/departments?apiKey=YOUR_API_KEY"

Response

{
  "success": true,
  "departments": [
    {
      "id": "dep_abc123",
      "name": "Sales",
      "color": "#2f6fed",
      "member_uids": ["uid_alice", "uid_bob"],
      "auto_assign_enabled": true,
      "routing_agents": [],
      "routing_channels": ["whatsapp"],
      "created_by": "owner_uid_123"
    }
  ]
}

Create a department

POST /team/departments

Request fields

Field Required Description
name Yes Up to 60 characters. Must not match an existing department.
color No #rrggbb hex, or null.
member_uids No Who is on it. Every UID must be the account owner or an active team member.
auto_assign_enabled No Defaults to true.
routing_agents No Agent IDs whose new chats land here.
routing_channels No Channel names whose new chats land here — same vocabulary as contact_scope_axes.channels.

cURL

curl -X POST "https://api.youraiconnector.com/v1/team/departments?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Sales",
    "color": "#2f6fed",
    "member_uids": ["uid_alice", "uid_bob"],
    "routing_channels": ["whatsapp"]
  }'

JavaScript

const res = await fetch("https://api.youraiconnector.com/v1/team/departments", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "Sales",
    color: "#2f6fed",
    member_uids: ["uid_alice", "uid_bob"],
    routing_channels: ["whatsapp"],
  }),
});
const { department } = await res.json();

Python

import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/team/departments",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "name": "Sales",
        "color": "#2f6fed",
        "member_uids": ["uid_alice", "uid_bob"],
        "routing_channels": ["whatsapp"],
    },
)
department = res.json()["department"]

Response201 Created

{
  "success": true,
  "department": {
    "id": "dep_abc123",
    "name": "Sales",
    "color": "#2f6fed",
    "member_uids": ["uid_alice", "uid_bob"],
    "auto_assign_enabled": true,
    "routing_agents": [],
    "routing_channels": ["whatsapp"],
    "created_by": "owner_uid_123"
  }
}
Status When
400 name is missing or too long, color is not #rrggbb, a channel name is not recognised, a listed UID is not an active member of this team, or you already have 50 departments.
409 A department with that name already exists.

Update a department

PATCH /team/departments/{departmentId}

Changes a department. Only the fields you send are changed.

curl -X PATCH "https://api.youraiconnector.com/v1/team/departments/dep_abc123?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "member_uids": ["uid_alice"], "auto_assign_enabled": false }'

Response

{
  "success": true,
  "department": {
    "id": "dep_abc123",
    "name": "Sales",
    "color": "#2f6fed",
    "member_uids": ["uid_alice"],
    "auto_assign_enabled": false,
    "routing_agents": [],
    "routing_channels": ["whatsapp"],
    "created_by": "owner_uid_123"
  }
}

Sending no recognised fields returns 400; an unknown department returns 404; a name that clashes with another department returns 409.

Delete a department

DELETE /team/departments/{departmentId}

curl -X DELETE "https://api.youraiconnector.com/v1/team/departments/dep_abc123?apiKey=YOUR_API_KEY"

Response

{
  "success": true,
  "deleted": "dep_abc123"
}

Deleting a department that someone is limited to is refused. The 400 response names the members whose visibility is narrowed to it, so you can re-scope them first. That is deliberate: silently un-limiting them would hand them your whole customer base with nothing to show it happened.

Contacts filed under a deleted department are not rewritten — they simply stop showing a department, and the next time you file them it sticks.


Check your own permissions

GET /team/permissions

Returns what the signed-in person is allowed to do in the account they are currently working in. Use it to hide buttons a member cannot use, instead of letting them discover the limit through an error.

cURL

curl "https://api.youraiconnector.com/v1/team/permissions" \
  -H "Authorization: Bearer FIREBASE_ID_TOKEN"

Response — the account owner

{
  "success": true,
  "role": "owner",
  "is_team_mode": false,
  "permissions": {
    "campaigns": "full",
    "contacts": "full",
    "messages": "full",
    "appointments": "full",
    "settings": "full",
    "billing": "full",
    "team_management": "full",
    "analytics": "full",
    "phone_numbers": "full",
    "integrations": "full",
    "faqs": "full",
    "daily_summaries": "full"
  }
}

Response — a team member working inside an account

{
  "success": true,
  "role": "editor",
  "is_team_mode": true,
  "permissions": { "campaigns": "edit", "billing": "none", "…": "…" },
  "member": {
    "uid": "uid_sam",
    "email": "sam@example.com",
    "display_name": "Sam Rivera",
    "account_owner_uid": "owner_uid_123"
  }
}

role is owner when the signed-in person is the account owner; otherwise it is their team role. member is only present in team mode, and carries contact_scope, contact_scope_unassigned and contact_scope_axes when their membership has them.


Session tokens

Five endpoints mint a one-time sign-in token for switching between accounts. They all answer the same way:

{
  "success": true,
  "customToken": "eyJhbGciOi…"
}

The token is exchanged for a session with the Firebase client SDK. It is not an API key and cannot be sent as one, which is why these endpoints are only useful inside a first-party app.

Endpoint What it does Body
POST /team/tokens/team-member Lets a team member start working inside an account they belong to. account_owner_uid (required)
POST /team/tokens/return-from-team Takes them back out, to their own account.
POST /team/tokens/assist Lets Your AI Connector staff open a customer’s account to help. Staff only. customerUid
POST /team/tokens/return-to-admin Ends an assist session and returns staff to their own account.
POST /team/tokens/agency-assist Lets an agency open one of its client sub-accounts — or, called without one, return to the agency account. subAccountUid (optional)

Each refuses with 403 when the session is not entitled to it: not a member of that account, not staff, that sub-account is not on your agency or has not been granted to you, or the session is not currently in the mode the endpoint ends.


Assign a platform role

POST /team/users/{targetUid}/role

Sets a user’s platform role — User, Dev, Support or Agency. This is not team membership: it is what kind of Your AI Connector account somebody has.

This endpoint is restricted to Your AI Connector staff, and the last remaining Dev cannot be demoted. Listed for completeness; it is not part of managing your own team.

{
  "success": true,
  "targetUid": "uid_sam",
  "role": "Agency",
  "claimUpdated": true
}
Status When
400 role is missing or not one of the four, or this would remove the last Dev.
403 You are not staff, or the session is working inside another account.
404 No such user.

Team API errors

Team endpoints return the standard error envelope, always with error_code alongside the HTTP status:

{
  "success": false,
  "error_code": 403,
  "error": "Cannot grant \"full\" access to \"billing\" — exceeds your own permissions."
}
Status When it happens on a team endpoint
400 A required field is missing or invalid, or the action is not allowed in this state (reactivating a removed member, suspending the owner, deleting a department someone is limited to).
401 You sent an API key to an endpoint that needs a signed-in person — see Authentication.
403 You do not have team_management permission, the change exceeds your own access, or the action is refused while working inside another account.
404 No such member, invitation, department or user.
409 Already a team member, a pending invitation already exists, or a department by that name exists.
429 Team seats are full, the 20-invitations-a-day limit is reached, or you hit the API rate limit.
504 The invitation you tried to accept has expired.

The shared codes every endpoint can return — 429 (rate limit) and 500 — are listed with retry guidance in Errors & Pagination.


  • Team Management — the same features in the dashboard, with screenshots.
  • Authentication — how to send a Firebase ID token instead of an API key.
  • Contacts API — the contacts a member’s visibility limits apply to.