Team & Agent management
The team (alias: agents, members) and member (alias: agent) commands
mirror the Settings → Agents page in the app. You can list members, invite
a new agent, verify the owner OTP, update roles or per-page permissions, resend
the invite email, and remove a member — all from the terminal.
Backed by /api/v1/app/team/*. Every call uses your stored oc_live_ token
and acts as you (the owner) — only the account owner can manage team members.
Quick start
# 1. Invite an agent with full access — prompts for the OTP afterwards
splashify team add \
--name "Alice Smith" \
--email alice@example.com \
--country-code +91 \
--phone 9876543210 \
--role agent \
--all read_write
# 2. Type the OTP that lands on your owner email + WhatsApp at the prompt
# The CLI calls /verify-otps for you, then the backend emails Alice a
# "Set your password" link.
# 3. Confirm — Alice now appears on the team list
splashify teamHow adding a member actually works
The flow is two-step on the backend and the CLI wraps both into a single command by default:
POST /app/teamcreates the pending member and sends a single 6-digit OTP to YOUR (the owner’s) email and WhatsApp. The new member does not receive anything yet.POST /app/team/verify-otpsconfirms the OTP. Only after this step does the backend email the new member their “Set your password” link.
splashify team add runs step 1 immediately, then prompts you for the OTP and
runs step 2 inline. Use --otp <code> to skip the prompt (e.g. in a script
that read the code from elsewhere) or --no-verify to stop after step 1 and
verify later from a different machine.
Important — the OTP goes to the owner, not the invitee. Make sure you have access to the owner email and the owner’s WhatsApp number before running
team add, otherwise you will not see the code.
Member-limit gate
Every account starts with 5 team-member slots on an active paid plan plus
+1 per Extra Team Member addon unit. The limit is reported on every
splashify team call:
{
"success": true,
"members": [ ... ],
"count": 3,
"limit": 5
}team add returns HTTP 403 when count >= limit — purchase the addon from
the app (Settings → Subscriptions) to raise the limit. Members beyond the
limit are locked (read-only on the backend); the CLI surfaces their
is_locked: true flag.
Roles
Only two role values are accepted:
| Role | What it does |
|---|---|
agent (default) | Standard team member — permissions are enforced per page key |
manager | Higher-tier role — still enforced per page key, but typically granted broader permissions |
Anything else is rejected with a 400. The CLI validates the role client-side before the round-trip.
Per-page permissions
A member’s permissions field is a JSON object mapping page keys to
access levels. The same keys the app’s Settings → Agents → Edit
Permissions dialog uses:
| Group | Keys |
|---|---|
| Core | dashboard, messages, contacts |
| Marketing | broadcasts, templates, media |
| Intelligence | analytics, ai-agents, ai-credits |
| Account | integrations, wallet, settings, activity-logs |
Access levels:
| Level | Meaning |
|---|---|
none | No access — the page is hidden from the member |
read | View only — the member can read but cannot write |
read_write | Full access — view and modify |
Four ways to build a permissions map
The same flags are accepted on team add, team update, and team set-permissions. They are evaluated in this fixed order, so later flags
override earlier ones:
--all <level>— seed every known page key with one level (great for “grant everything, then remove a couple”).--permissions '<json>'— explicit JSON map; can include any keys (even ones the CLI does not know about).--rw <keys>/--read <keys>/--none <keys>— comma-separated page keys to set to that level. Use these to fine-tune what--allproduced.
# Full access everywhere
--all read_write
# Read-only by default, but full write on the two pages the agent needs
--all read --rw messages,contacts
# Explicit JSON — useful if you want a stored config file you can diff
--permissions '{"dashboard":"read","messages":"read_write","wallet":"none"}'
# Combine: start permissive, lock down billing surfaces
--all read_write --none wallet,settings,activity-logsCustom keys.
--permissionsaccepts arbitrary keys for forward compatibility — if the app adds a new page in the future, you can grant permission to it without waiting for a CLI release. The--rw/--read/--noneshortcuts only target the known list above.
How updates merge
team update (and team set-permissions) replace the entire permissions
map on the backend. That means an update like:
splashify team set-permissions <id> --rw messageswould send {"messages":"read_write"} and clear every other page back to
the default (no entry → no access). When you only want to change a few keys
while preserving the rest, fetch the current permissions first and merge them
yourself:
CURRENT=$(splashify member <id> | jq -r '.permissions')
splashify team set-permissions <id> \
--permissions "$(echo "$CURRENT" | jq '. + {"messages":"read_write"}')"Or use --all as the baseline (it seeds every known key) before overlaying
specific changes:
splashify team set-permissions <id> --all read --rw messages,contactsCommand reference
splashify team — list
splashify team # default: list all members + limit + count
splashify team list # alias| Backed by | GET /api/v1/app/team |
|---|
Response shape:
{
"success": true,
"members": [
{
"member_id": "uuid",
"owner_id": "uuid",
"name": "Alice Smith",
"email": "alice@example.com",
"country_code": "+91",
"phone": "+919876543210",
"role": "agent",
"email_verified": true,
"phone_verified": true,
"status": "active",
"is_locked": false,
"permissions": "{\"messages\":\"read_write\",\"contacts\":\"read\"}",
"created_at": "...",
"updated_at": "..."
}
],
"count": 3,
"limit": 5
}
permissionscomes back as a JSON-encoded string, not a nested object — the storage format on the backend. Usejq -r '.permissions | fromjson'to expand it.
splashify member <id> — show one
splashify member <member_id>No dedicated backend endpoint — the CLI fetches the list and filters
client-side, same pattern as splashify attribute <id>.
splashify team add — invite a new member
splashify team add \
--name "Bob Roe" \
--email bob@example.com \
--country-code +91 \
--phone 9876543210 \
[--role agent|manager] \
[--all read_write | --permissions '{…}' | --rw … --read … --none …] \
[--otp 123456 | --no-verify]| Flag | Required? | Notes |
|---|---|---|
--name | yes | Display name |
--email | yes | Will be lowercased + checked for duplicates |
--country-code | yes | +91, +1, etc. |
--phone | yes | Digits only — the country code is concatenated server-side |
--role | no | agent (default) or manager |
--all | no | Seed every page with one level |
--permissions | no | Explicit JSON map |
--rw / --read / --none | no | Overlay specific keys |
--otp | no | Pass the OTP inline — skips the prompt |
--no-verify | no | Stop after create; verify later from team verify |
Backed by POST /api/v1/app/team, then (by default) POST /api/v1/app/team/verify-otps.
The new member only receives their “Set Password” email after verify succeeds.
splashify team verify — confirm the OTP separately
splashify team verify <member_id> --otp <code>| Backed by | POST /api/v1/app/team/verify-otps |
|---|
Use this when you ran team add --no-verify earlier, or when the OTP arrives
on a different device. The --otp flag is interactive-friendly: if omitted,
the CLI prompts.
splashify team resend-otp — get a fresh code
splashify team resend-otp <member_id>| Backed by | POST /api/v1/app/team/resend-otps |
|---|
Generates a new 6-digit OTP and re-sends it to the owner’s email + WhatsApp. The previous OTP for that member is invalidated.
splashify team resend-invite — re-send the Set Password email
splashify team resend-invite <member_id>| Backed by | POST /api/v1/app/team/:member_id/resend-password-email |
|---|
Useful when the original “Set Password” email never arrived (or expired —
the link is valid 24h). Only works while the member is status = "pending";
once they’ve set their password and become active, the backend refuses with
HTTP 400.
splashify team update — change role and/or permissions
splashify team update <member_id> [--role agent|manager] \
[--all level | --permissions '{…}' \
| --rw … | --read … | --none …]| Backed by | PUT /api/v1/app/team/:member_id |
|---|
Only the flags you pass are sent. Pass at least one — calling update with no
flags returns a usage error.
splashify team set-role — convenience
splashify team set-role <member_id> agent | managerSame as team update --role …. Refuses any other flag — use this when you
specifically want to flip a role without risking accidental permission changes.
splashify team set-permissions — convenience
splashify team set-permissions <member_id> --all read_write
splashify team set-permissions <member_id> --rw messages,contacts --read analytics
splashify team set-permissions <member_id> --permissions '{"wallet":"none"}'Same as team update with only permission flags. Cannot change the role.
splashify team delete — remove a member
splashify team delete <member_id>
splashify team rm <member_id> # alias
splashify team remove <member_id> # alias| Backed by | DELETE /api/v1/app/team/:member_id |
|---|
Permanent. The member loses access immediately. Locked members
(is_locked: true, beyond your current addon-purchased limit) cannot be
deleted — renew the Extra Team Member addon first, or wait for the lock to
clear naturally.
Common workflows
Invite five agents from a CSV
# csv format: name,email,country_code,phone
while IFS=, read -r name email cc phone; do
splashify team add \
--name "$name" --email "$email" \
--country-code "$cc" --phone "$phone" \
--role agent \
--all read \
--no-verify
done < new-agents.csv
# Then verify each one as the OTPs arrive on your phone/email.List every member who can write to broadcasts
splashify team | jq -r '
.members[]
| (.permissions | fromjson) as $p
| select($p.broadcasts == "read_write")
| "\(.member_id)\t\(.name)\t\(.email)"
'Lock down a member to read-only across the board
splashify team set-permissions <id> --all readPromote an agent to a manager without changing permissions
splashify team set-role <id> managerBulk-remove pending members who never accepted the invite
splashify team | jq -r '.members[] | select(.status == "pending") | .member_id' \
| xargs -I{} splashify team delete {}Re-send the welcome email to every pending member
splashify team | jq -r '.members[] | select(.status == "pending") | .member_id' \
| xargs -I{} splashify team resend-invite {}Troubleshooting
“An active plan is required to add team members” — the owner account has
no active paid plan. The base limit of 5 is gated on an active subscription;
upgrade or renew at Settings → Subscriptions and try again.
“Team member limit reached (N/M)” — purchase the Extra Team Member addon
(Settings → Subscriptions → Add-ons) to raise the limit. Each unit grants
one slot.
“A team member with this email already exists” — the email is taken across the owner’s team (and globally — emails are unique across all team members). Pick a different address or delete the existing record first.
OTP expired or not found — the OTP is valid for 10 minutes and only 3
verification attempts. Use splashify team resend-otp <id> to start fresh.
“Can only resend invite to pending members” — the member already set
their password and is now active. They can log in directly at
app.splashifypro.com; the password-reset flow is a separate one they can
trigger themselves from the login page.
Member shows is_locked: true — they were added before an Extra Team
Member addon expired. Renew the addon to unlock and manage them.
See also
- Settings → Agents page in the app — the visual equivalent of these commands.
splashify account orgs— the orgs you were invited into (the inverse view: where you are a member, not a manager).