SendFlo

Teams & workspaces

A plan is a purchased Team subscription — the one multi-user plan. The organization owns every seat workspace: managers see the full file history, the org sets link policy, and the audit trail is exportable. Storage is pooled for the whole team (a base pool at the tier’s minimum size plus a per-member increment), and pricing is flat per member at fixed team sizes — Lite 3–25, Plus 5–50, Pro 10–100.

Every seat provisions a workspace — the isolation boundary for files, links, contacts, and quota. Your session is scoped to exactly one workspace at a time; switch with POST /v1/auth/workspace. Your personal workspace stays readable from any session via the /v1/personal/* read-only layer.

Roles

Owner is the account holder — structural, not grantable. The owner can grant admin (one step below: billing, link policy, managers). Owner or admin can grant manager (seats, invites, stats). Everyone else on a seat is a member.

curl -X PUT https://api.sendflo.app/v1/plans/<plan id>/roles/<user id> \
  -H "authorization: Bearer $TOKEN" -H "content-type: application/json" \
  -d '{"role":"admin","grant":true}'

Purchase and expand

Pricing is flat per member; a tier is purchasable only at its supported team sizes. Expansion is a new purchase that lands on the next supported size, never a mid-cycle mutation: one renewal date, the pool re-derived from the new size, and a prorated credit from the old term applied automatically. Seats, workspaces, roles, and live invitations follow to the new subscription; the old one becomes merged — clients must follow the returned subscription_id.

curl -X POST https://api.sendflo.app/v1/plans/<plan id>/expand \
  -H "authorization: Bearer $TOKEN" -H "content-type: application/json" \
  -d '{"add_seats":5}'
# → {"subscription_id":"<NEW id>","seat_count":10,"credit_cents":9500,...}

Seats, invites, rotation limits

Invites consume seats; each seat can be reassigned a limited number of times per subscription year, so a seat is a person’s, not a shared login. 409 seats_exhausted and 409 rotation_limit are the two honest refusals.

curl -X POST https://api.sendflo.app/v1/plans/<plan id>/invites \
  -H "authorization: Bearer $TOKEN" -H "content-type: application/json" \
  -d '{"email":"new.teammate@example.com","role":"member"}'

Departure = containment

Removing a member (DELETE /v1/plans/<id>/members/<user>) vacates the seat and triggers the leakage controls: every session the member held on that workspace is revoked at the refresh-family level, and the workspace’s live links are flagged for a human decision — keep or revoke, on the record.

Team administration runs in the signed-in app (it isn’t open to API keys), so there’s no key example here; the routes are listed for first-party clients in spec/internal.yaml.

Org link policy

Per purchased plan — two plans under one account can differ. Enforced when member workspaces create links: require passwords, cap lifetime, or disable public links entirely. The org owns every seat workspace, so every plan may constrain its links.

curl -X PUT https://api.sendflo.app/v1/plans/<plan id>/policy \
  -H "authorization: Bearer $TOKEN" -H "content-type: application/json" \
  -d '{"require_password":true,"max_link_ttl_seconds":604800}'

Audit log

Every member's sign-ins and security changes, links, each recipient's open, preview and download (with the IP address and its network), requests, collections, devices, team changes, billing and API use — who, what, when and from where. Owners and admins read the whole team's log on Team Pro and Team Enterprise (kept 365 and 730 days), filter it, and export exactly what's filtered as CSV or JSON. With an API key, use the audit.read scope.

curl "https://api.sendflo.app/v1/audit?plan_id=<plan id>&action=FILE_DOWNLOAD&since=2026-10-01T00:00:00Z" \
  -H "authorization: Bearer $SENDFLO_API_KEY"

curl "https://api.sendflo.app/v1/audit/export?plan_id=<plan id>&format=csv" \
  -H "authorization: Bearer $SENDFLO_API_KEY" -o audit.csv

The personal layer

Readable from any workspace session, write-nothing: your contacts, links, and transfer activity from your personal workspace, embedded view-only inside every active workspace. Nothing syncs the other way — team workspaces are org-owned, so their content never flows into your personal workspace.

One-click send & shared destinations

POST /v1/workspaces/<id>/send delivers a file to address-book contacts (seated members receive it in their workspace inbox); /request mints a magic upload link per contact. Destinations are named receive endpoints with allowlisted on-arrival hooks (webhook or notify) — point people at POST /v1/destinations/<slug>/deliver.