Summary and recommendation
The Dropbox Business API uses OAuth 2.0 with team-scoped tokens; the authorizing user must be a team admin. All endpoints - including reads - use HTTP POST with JSON bodies. Tagged unions (.tag) are pervasive in both requests and responses: always branch on .tag before accessing nested fields or you will encounter silent type errors.
Write operations (add, remove) are frequently asynchronous and return an async_job_id that must be polled against a separate job_status endpoint. SCIM tokens are issued independently in the Admin Console and are not interchangeable with OAuth tokens.
For teams building against a broader identity graph, the Dropbox API integrates cleanly into an MCP server with 60+ deep IT/identity integrations, but the async job model and cursor-based pagination require explicit handling at the orchestration layer.
API quick reference
| Has user API | Yes |
| Auth method | OAuth 2.0 |
| Base URL | Official docs |
| SCIM available | Yes |
| SCIM plan required | Standard (formerly Business), Advanced, or Enterprise |
Authentication
Auth method: OAuth 2.0
Setup steps
- Register an app at https://www.dropbox.com/developers/apps and select 'Dropbox Business API' app type.
- Choose required permission scopes (e.g., members.read, members.write) during app creation.
- Implement OAuth 2.0 authorization code flow: redirect admin to https://www.dropbox.com/oauth2/authorize with client_id, response_type=code, and token_access_type=offline.
- Exchange the returned authorization code for an access token and refresh token via POST https://api.dropbox.com/oauth2/token.
- Use the access token in the Authorization header as 'Bearer
' for all API requests. - For team-level operations, the authorizing user must be a Dropbox Business team admin.
Required scopes
| Scope | Description | Required for |
|---|---|---|
| members.read | Read team member profiles, status, and metadata. | List members, get member info |
| members.write | Add, remove, suspend, and modify team members. | Add member, remove member, suspend/unsuspend member, set admin role |
| members.delete | Permanently delete team members. | Remove member with data deletion |
| groups.read | Read team group membership and metadata. | List groups, get group members |
| groups.write | Create, modify, and delete team groups and their memberships. | Create group, add/remove group members |
| team_info.read | Read team-level metadata and settings. | Get team info |
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| team_member_id | string | Unique Dropbox-assigned identifier for the team member. | auto-generated | immutable | Use this ID for all subsequent API operations on the member. |
| string | Primary email address of the team member. | required | updatable via members/move_former_member_files or admin console | Must be unique within the team. | |
| given_name | string | Member's first name. | optional | updatable via members/set_profile | |
| surname | string | Member's last name. | optional | updatable via members/set_profile | |
| display_name | string | Full display name as shown in Dropbox UI. | derived | read-only via API; set via given_name + surname | |
| external_id | string | Optional external identifier (e.g., HR system ID) set by the team admin. | optional | updatable via members/set_profile | Useful for correlating Dropbox members with external systems. |
| account_id | string | Global Dropbox account identifier (cross-team). | auto-generated | immutable | Distinct from team_member_id. |
| membership_type | enum | Type of membership: 'full' or 'limited'. | optional (defaults to full) | updatable | Limited members have restricted storage and features. |
| role | enum | Admin role: 'member_only', 'support_admin', 'user_management_admin', 'team_admin'. | optional (defaults to member_only) | updatable via members/set_admin_permissions | |
| status | enum | Member status: 'active', 'invited', 'suspended', 'removed'. | set to 'invited' until accepted | changed via suspend/unsuspend/remove endpoints | Suspended members cannot access Dropbox but data is retained. |
| joined_on | timestamp | Date and time the member joined the team. | auto-set on acceptance | immutable | |
| persistent_id | string | SAML persistent ID used for SSO identity linking. | optional | updatable via members/set_profile | Required when using SSO with SAML persistent NameID format. |
| send_welcome_email | boolean | Whether to send a welcome email upon member invitation. | optional (defaults to true) | not applicable | Set to false for silent provisioning. |
| is_directory_restricted | boolean | Whether the member is restricted from the team directory. | optional | updatable |
Core endpoints
List team members
- Method: POST
- URL:
https://api.dropboxapi.com/2/team/members/list/v2 - Watch out for: Use /team/members/list/continue/v2 with the returned cursor to paginate. The v2 endpoint is preferred over the deprecated v1.
Request example
POST /2/team/members/list/v2
Authorization: Bearer <team_token>
Content-Type: application/json
{"limit": 100}
Response example
{
"members": [{"profile": {"team_member_id": "dbmid:abc", "email": "user@example.com", "status": {".tag": "active"}}}],
"cursor": "ZtkX9...",
"has_more": true
}
Get member info
- Method: POST
- URL:
https://api.dropboxapi.com/2/team/members/get_info/v2 - Watch out for: Accepts lookup by email, team_member_id, or external_id. Returns a tagged union; check the .tag field for 'member_info' vs 'id_not_found'.
Request example
POST /2/team/members/get_info/v2
Authorization: Bearer <team_token>
Content-Type: application/json
{"members": [{"email": "user@example.com", ".tag": "email"}]}
Response example
[
{".tag": "member_info", "profile": {"team_member_id": "dbmid:abc", "email": "user@example.com", "given_name": "Jane", "surname": "Doe", "status": {".tag": "active"}}}
]
Add team member(s)
- Method: POST
- URL:
https://api.dropboxapi.com/2/team/members/add/v2 - Watch out for: Supports batch add of up to 20 members per call. Response is async if the team is large; poll /team/members/add/job_status/check/v2 when response tag is 'async_job_id'.
Request example
POST /2/team/members/add/v2
Authorization: Bearer <team_token>
Content-Type: application/json
{"new_members": [{"member_email": "newuser@example.com", "send_welcome_email": false, "role": {".tag": "member_only"}}]}
Response example
{
"complete": [
{".tag": "success", "profile": {"team_member_id": "dbmid:xyz", "email": "newuser@example.com", "status": {".tag": "invited"}}}
]
}
Update member profile
- Method: POST
- URL:
https://api.dropboxapi.com/2/team/members/set_profile/v2 - Watch out for: Cannot change email via this endpoint. Email changes must be initiated by the member or via admin console.
Request example
POST /2/team/members/set_profile/v2
Authorization: Bearer <team_token>
Content-Type: application/json
{"user": {"team_member_id": "dbmid:abc", ".tag": "team_member_id"}, "new_given_name": "Jane", "new_external_id": "EMP-001"}
Response example
{
"profile": {"team_member_id": "dbmid:abc", "email": "user@example.com", "given_name": "Jane", "external_id": "EMP-001"}
}
Suspend member
- Method: POST
- URL:
https://api.dropboxapi.com/2/team/members/suspend - Watch out for: Setting wipe_data=true immediately deletes the member's Dropbox data from their devices. This is irreversible for local device data.
Request example
POST /2/team/members/suspend
Authorization: Bearer <team_token>
Content-Type: application/json
{"user": {"team_member_id": "dbmid:abc", ".tag": "team_member_id"}, "wipe_data": false}
Response example
{}
Unsuspend member
- Method: POST
- URL:
https://api.dropboxapi.com/2/team/members/unsuspend - Watch out for: Member must be in 'suspended' status; calling on an active member returns an error.
Request example
POST /2/team/members/unsuspend
Authorization: Bearer <team_token>
Content-Type: application/json
{"user": {"team_member_id": "dbmid:abc", ".tag": "team_member_id"}}
Response example
{}
Remove member
- Method: POST
- URL:
https://api.dropboxapi.com/2/team/members/remove - Watch out for: Remove is async; poll /team/members/remove/job_status/check with the returned async_job_id. If transfer_dest_id is omitted, files remain in a deactivated account accessible only to admins.
Request example
POST /2/team/members/remove
Authorization: Bearer <team_token>
Content-Type: application/json
{"user": {"team_member_id": "dbmid:abc", ".tag": "team_member_id"}, "wipe_data": false, "transfer_dest_id": {"team_member_id": "dbmid:mgr", ".tag": "team_member_id"}}
Response example
{".tag": "async_job_id", "async_job_id": "34g93opqe..."}
Set admin permissions
- Method: POST
- URL:
https://api.dropboxapi.com/2/team/members/set_admin_permissions/v2 - Watch out for: Only a team_admin token can elevate another member to team_admin role. user_management_admin tokens cannot grant team_admin.
Request example
POST /2/team/members/set_admin_permissions/v2
Authorization: Bearer <team_token>
Content-Type: application/json
{"user": {"team_member_id": "dbmid:abc", ".tag": "team_member_id"}, "new_role": {".tag": "user_management_admin"}}
Response example
{
"team_member_id": "dbmid:abc",
"role": {".tag": "user_management_admin"}
}
Rate limits, pagination, and events
- Rate limits: Dropbox enforces per-route rate limits. Most Business API endpoints allow up to 1,000 requests per minute per team token. Exceeding limits returns HTTP 429. Dropbox recommends exponential backoff.
- Rate-limit headers: Yes
- Retry-After header: Yes
- Rate-limit notes: HTTP 429 responses include a Retry-After header indicating seconds to wait. Dropbox does not publish exact per-route limits publicly; limits may vary by endpoint. Batch endpoints (e.g., members/add/v2) are preferred to reduce request volume.
- Pagination method: cursor
- Default page size: 1000
- Max page size: 1000
- Pagination pointer: cursor
| Plan | Limit | Concurrent |
|---|---|---|
| All Dropbox Business plans | ~1,000 requests/minute per team token (route-dependent) | 0 |
- Webhooks available: Yes
- Webhook notes: Dropbox supports webhooks for file/folder change notifications via the Dropbox API. For team-level user management events (member added, removed, suspended), Dropbox does not provide native push webhooks; instead, team activity is available via the Team Log API.
- Alternative event strategy: Poll /team/log/get_events for user management audit events (member_add, member_remove, member_suspend, member_change_admin_role, etc.). Supports filtering by event category and cursor-based pagination.
- Webhook events: file/folder change notifications (via webhook endpoint registered in app console)
SCIM API status
SCIM available: Yes
SCIM version: 2.0
Plan required: Standard (formerly Business), Advanced, or Enterprise
Endpoint: https://api.dropbox.com/2/team/scim/v2
Supported operations: GET /Users – list users, GET /Users/{id} – get user by SCIM ID, POST /Users – provision new user, PUT /Users/{id} – replace user attributes, PATCH /Users/{id} – update user attributes (activate/deactivate), DELETE /Users/{id} – deprovision user, GET /Groups – list groups, POST /Groups – create group, PATCH /Groups/{id} – update group membership, DELETE /Groups/{id} – delete group
Limitations:
- SSO must be enabled on the Dropbox team before SCIM provisioning can be activated.
- SCIM is supported via Azure AD (Entra ID), Google Cloud Identity, Okta, and OneLogin; direct SCIM token issuance is available for other IdPs.
- Dropbox Sign (formerly HelloSign) does NOT support SCIM provisioning.
- SCIM provisioning requires Standard plan or above; Essentials/Professional plans do not include SCIM.
- Group provisioning via SCIM is supported but group-based folder permissions must still be configured in Dropbox admin console.
- SCIM token is generated in the Dropbox Admin Console under Authentication > SCIM; it does not use OAuth 2.0.
Common scenarios
Three scenarios cover the majority of lifecycle automation needs. For provisioning, POST /team/members/add/v2 supports batch adds of up to 20 members per call; poll /team/members/add/job_status/check/v2 when the response tag is async_job_id, then extract team_member_id for downstream correlation.
Set external_id via /team/members/set_profile/v2 to anchor the Dropbox identity to your HR system record in the identity graph.
For offboarding, suspend first with wipe_data=false to preserve data, then POST /team/members/remove with transfer_dest_id pointing to the departing member's manager; removal is also async - poll /team/members/remove/job_status/check until complete.
For auditing, POST /team/members/list/v2 with limit=1000 and paginate via /team/members/list/continue/v2 using the returned cursor; filter client-side on status. tag == 'active'.
Do not cache cursors across sessions - they expire on inactivity.
Provision a new employee
- POST /team/members/add/v2 with member_email, given_name, surname, send_welcome_email=false, and role=member_only.
- Check response: if .tag is 'async_job_id', poll POST /team/members/add/job_status/check/v2 until .tag is 'complete'.
- Extract team_member_id from the completed response for future operations.
- Optionally POST /team/members/set_profile/v2 to set external_id for HR system correlation.
Watch out for: Member status will be 'invited' until the user accepts the invitation. Some profile fields (e.g., display_name) are not settable until the account is active.
Offboard a departing employee
- POST /team/members/suspend with wipe_data=false to immediately revoke access while preserving data.
- POST /team/members/remove with transfer_dest_id set to the departing member's manager team_member_id and wipe_data=false.
- Poll POST /team/members/remove/job_status/check with the returned async_job_id until status is 'complete'.
- Verify file transfer completion in the Dropbox Admin Console or via /team/log/get_events filtered to member_transfer_account_contents.
Watch out for: Suspension and removal are separate steps; skipping suspension and going directly to removal still revokes access immediately, but suspension allows a grace period for data review before permanent removal.
Audit all active team members
- POST /team/members/list/v2 with limit=1000 to retrieve the first page of members.
- While has_more is true, POST /team/members/list/continue/v2 with the returned cursor.
- Filter results client-side for members where status.tag == 'active'.
- For each active member, extract team_member_id, email, role, joined_on, and external_id for the audit report.
Watch out for: The cursor expires after a period of inactivity; complete the full pagination in a single session. Do not cache cursors across days for incremental sync without verifying cursor validity.
Why building this yourself is a trap
Three API behaviors are the most common sources of integration bugs. First, the async job pattern: callers who treat add and remove as synchronous will miss failures silently - always implement the poll loop.
Second, email is not updatable via /team/members/set_profile/v2; email changes must go through the admin console or be member-initiated, which breaks any automated identity reconciliation flow that assumes email is a mutable field.
Third, Dropbox Sign does not expose a SCIM or API provisioning surface, so any identity graph that includes Dropbox Sign users requires a separate, manual management path - there is no API workaround available at this time.
Rate limits return HTTP 429 with a Retry-After header; Dropbox does not publish exact per-route numeric limits, so implement exponential backoff unconditionally rather than tuning to a specific threshold.
Automate Dropbox workflows without one-off scripts
Stitchflow builds and maintains end-to-end IT automation across your SaaS stack, including apps without APIs. Built for exactly how your company works, with human approvals where they matter.