Team & roles
Textmast is a shared inbox. Conversations are assigned to people, agents leave notes for each other, and the mobile app exists for whoever is not at a desk.
Roles
| Role | Can |
|---|---|
owner | Everything, including billing and removing other owners. Created by signing up. |
admin | Everything an owner can, including managing the team. |
agent | The inbox, contacts and campaigns. Not settings, billing or the team. |
api_only | No dashboard. For a person who exists on the account to hold an integration. |
An account always has at least one owner. Removing or demoting the last one is refused with last_owner, because the result is an account that neither you nor we can administer without editing a database by hand. Promote somebody else first.
Inviting
POST /v1/team/invites
{ "email": "kim@yourcompany.co.uk", "role": "agent" }{
"id": "01j8xk2p7n4qc3m9v0wz5bt6ay",
"object": "invite",
"email": "kim@yourcompany.co.uk",
"role": "agent",
"status": "pending",
"expires_at": "2026-09-24T09:14:22+00:00",
"invite_url": "https://app.textmast.com/accept-invite?token=inv_…",
"invite_notice": "This link is shown once…"
}The link is the credential
Whoever holds it can join your account and read your entire message history. Treat it the way you would a password: not in a public channel, not in a shared document.
It is returned once and we cannot show it again — we store a hash, exactly as we do for API keys. If it goes astray, revoke the invite and create another.
We hand you the link rather than emailing it. Email delivery is not something we can guarantee, and an endpoint that answers "sent" when nothing arrived is worse than one that answers honestly. Send it however suits you — your own mail, chat, or reading it out over the phone.
Invitations expire
After seven days. An invitation emailed to a colleague who has since left should not still work next year.
Accepting
POST /v1/auth/accept-invite
{ "token": "inv_…" }No authentication — the person accepting does not have a login yet, and may never have had one. Two shapes come back:
| Response | Means |
|---|---|
{ "needs_account": true, … } | No login for that address. Post again with name and password. |
{ "accepted": true, … } | They already had a login and are now on the account too. |
Asking first is deliberate: showing a password field to somebody who already has an account is how people end up with two.
The address is not negotiable
A new login is created with the address the invitation was sent to, never one supplied in the request. Otherwise an invitation meant for one person could be redeemed into an account for another, and every audit line afterwards would name the wrong human.
One answer for every bad token
Used, revoked, expired, or never existed — all four return the sameinvite_not_valid. Telling them apart would tell somebody guessing at tokens that they had guessed a real one.
Managing
GET /v1/team
DELETE /v1/team/invites/{id}
PATCH /v1/team/members/{userId} { "role": "admin" }
DELETE /v1/team/members/{userId}Removing somebody ends their access to this account. It does not delete their login — they keep it for any other account they belong to.