Accounts, Teams & Permissions
Every bot belongs to a team, not directly to a user. pywa login (see the
CLI Reference) covers day-to-day authentication; this page covers the
account/team model underneath it, which today is API-only — there’s no pywa cloud teams ...
command yet (see the CLI reference’s “Not built yet” section).
Signing up
Section titled “Signing up”curl -X POST http://127.0.0.1:8000/auth/signup \ -H 'Content-Type: application/json' \ -d '{"email": "you@example.com", "password": "at-least-8-characters"}'Returns a JWT access_token plus your default team id — signing up automatically creates a
single-seat “Personal” team you own. pywa login (no --token) drives this same account
system through its device-code flow instead of raw curl; either way you end up with the same
JWT. Use the token as Authorization: Bearer <access_token> on every other request.
curl -X POST http://127.0.0.1:8000/auth/login \ -H 'Content-Type: application/json' \ -d '{"email": "you@example.com", "password": "..."}'Default teams vs. real teams
Section titled “Default teams vs. real teams”Your default team is single-seat by design — it can’t invite anyone (POST /teams/{id}/members/invite rejects it with 400). It exists so a solo user never has to think about
teams at all: create a bot, it lands there, done. The moment you want a second person on a bot,
create a real team:
curl -X POST http://127.0.0.1:8000/teams \ -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \ -d '{"name": "Acme Bots"}'You’re automatically its Owner. Bots created afterward can be pointed at this team (or moved there later — see “Moving a bot between teams” below).
Three roles, each a strict superset of the one before it:
- Member — read access to the team’s bots, deployments, logs, and metrics.
- Admin — everything a Member can do, plus mutating actions: deploy, env vars, domains, rollback, connect/integrations, inviting and removing Members (not other Admins/Owners).
- Owner — everything an Admin can do, plus changing roles, deleting the team, and removing
Admins/other Owners. A team can never drop below one Owner — the API rejects any change that
would (
PATCH /teams/{id}/members/{user_id}/role,DELETE .../members/{user_id}).
Inviting members
Section titled “Inviting members”curl -X POST http://127.0.0.1:8000/teams/$TEAM_ID/members/invite \ -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \ -d '{"email": "teammate@example.com"}'Requires Admin or Owner. Returns a single-use, 24-hour invite token (also emailed — see
control-plane/README.md for the pluggable email adapter). The invited person accepts it once
they have an account of their own with that exact email:
curl -X POST http://127.0.0.1:8000/teams/$TEAM_ID/members/accept/$INVITE_TOKEN \ -H "Authorization: Bearer $THEIR_TOKEN"They join as a Member; an Owner can promote them afterward with PATCH /teams/{id}/members/{user_id}/role.
Moving a bot between teams
Section titled “Moving a bot between teams”curl -X POST http://127.0.0.1:8000/bots//transfer \ -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \ -d '{"destination_team_id": "'$DEST_TEAM_ID'", "confirm_slug": "my-bot"}'Team-to-team only (not user-to-user), and you must already be a member of the destination team.
Typing the bot’s own slug back as confirm_slug guards against transferring the wrong bot. The
bot’s slug and config are untouched — its running container is looked up by slug, not team, so
there’s no redeploy or downtime.
Deleting your account
Section titled “Deleting your account”curl -X POST http://127.0.0.1:8000/auth/account/delete \ -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \ -d '{"confirm_email": "you@example.com"}'Irreversible, hence typing your own email back rather than a single call with no confirmation. Blocked if you’re the sole Owner of a real (non-default) team that still has other members or bots — promote someone else or delete the team first. Your default team is removed automatically along with the account, as long as no bots remain on it.