Skip to main content

Team

Invite somebody

Send an invitation, take a seat, and what the owner-only rule means for API keys.

POST/team/invites
Invite somebody
curl --request POST \
  --url 'https://truestat.io/api/v1/team/invites' \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '{
  "role": "admin",
  "email": "<string>"
}'
{
  "data": {
    "role": "member",
    "email": "dana@example.com",
    "pending": true,
    "email_sent": true
  },
  "meta": {}
}

Sends an invitation e-mail and takes a seat immediately.

Owner only — and that is not the scope

This is the one place in the API where carrying the right scope is not the whole answer.

team:write says the key was issued for team management. It does not say the person behind the key is allowed to manage the team. An organisation admin can create an API key, and admin is not owner — so a key minted by an admin gets 403 owner_only here, exactly as that admin would find no team controls in the dashboard.

The role is read from whoever created the key, and it is read fresh on every call. A key whose creator is later demoted stops being able to manage the team from that moment — the same way the screen disappears for them.

Everywhere else in the API the scope is sufficient, because creating a key at all requires an admin, and admin already outranks what those endpoints need. Team management is the exception because the bar above it is higher.

The seat is taken before they accept

The membership row is written first, then the mail goes out. So the seat counts against your plan from this call, not from the moment somebody clicks.

If the plan has no seats left you get 402 with a sentence naming the tier that has more and what it costs. If the account has no active plan at all you get 402 subscription_required — that check happens before anything is created, so a failed invitation leaves nothing behind.

email_sent: false is not a failure

{
  "data": {
    "email": "dana@example.com",
    "role": "member",
    "pending": true,
    "email_sent": false
  }
}

The membership exists and holds its seat. Only delivery did not happen.

Do not retry this call — it would answer 409 already_member, which is correct and unhelpful. Re-send the invitation from the team screen instead.

Which roles can be invited

admin and member. Nothing else is accepted.

owner is not a value you can hand out through a request body; transferring an account is deliberate and happens in the dashboard. viewer is left out because nothing in the permission rules distinguishes it from member today — offering it would sell a restriction that is not enforced.

Already on the team

A 409 covers both cases, with a message that says which:

  • the address has already accepted — they are a member

  • the address has an invitation outstanding — it was already sent

Authorizations

Authorizationstringheaderrequired

An API key from Settings → API keys. Scopes available: analytics:read, sites:read, sites:write, settings:read, settings:write, goals:read, goals:write, funnels:read, funnels:write, notes:read, notes:write, alerts:read, alerts:write, team:read, team:write, revenue:read, revenue:write.

Bearer authentication header of the form Bearer <token>, where <token> is your auth token.

Request bodyapplication/json

roleenum<string>bodyrequired
`owner` and `viewer` are deliberately not offered.
options:adminmember
emailstring (email)bodyrequired
Where to send the invitation. Compared case-insensitively.

Response

application/json

The answer.

dataobject
metaobject