Admin API
Administrative operations scoped to one tenant: creating and maintaining accounts, controlling activation, resetting MFA, managing the license, and configuring how the tenant behaves. For operations across tenants, see Super Admin. The tenant's audit trail has its own page, Audit logs.
Base URL and authorization
/api/external/v1/admins
These endpoints require an API Key issued to an account holding the ADMIN role. A super administrator's key also
passes. Two endpoints are exceptions, noted where they appear.
Authorization for /admins/** is by role alone. The API Key's scope is not examined, so a key holding only api:read
can create accounts, change quotas, deactivate users, import CSV, and register a license. Treat any admin key as a
full-privilege credential regardless of the scope it was issued with.
POST /admins authenticated by an external API Key may only create accounts with rt of USER. Asking for ADMIN is
rejected with USER_013. Note that bulk account import does not apply the same
restriction - see the warning there.
Every operation is limited to the caller's own tenant. An endpoint that takes a userSeq returns USER_005 for a
userSeq in another tenant, the same response as for one that does not exist, so account existence is not disclosed.
Pagination
Admin listings use cursor pagination: send ps for the page size and ci as the last userSeq of the previous page.
nextCursor and hasNext are omitted from the response when they have no value.
Create an account
POST /api/external/v1/admins
Send as multipart/form-data.
| Parameter | Type | Required | Description |
|---|---|---|---|
ui | string | Yes | Account id, an email address, up to 25 characters |
encPwd | string | Yes | Password, 8 to 64 characters, with at least one uppercase letter, lowercase letter, digit, and symbol |
un | string | Yes | Display name, up to 20 characters |
tq | long | Yes | Storage allowance in bytes |
rt | string | Yes | Role; an external key may only send USER |
pif | file | No | Profile image |
ct | string | No | Country dialing code without the plus sign, up to 4 characters |
pn | string | No | Phone number without separators, up to 20 characters |
ct and pn must be sent together or both left out; sending one alone returns USER_027.
curl -X POST "https://drive.example.com/api/external/v1/admins" \
-H "Authorization: Bearer replace-with-your-api-key" \
-F "ui=user1@example.com" \
-F "encPwd=User1234!" \
-F "un=First user" \
-F "tq=1000000000" \
-F "rt=USER"
Returns the new userSeq. A malformed email returns USER_002, a duplicate returns USER_004, and a request that
exceeds the licensed seats or available storage returns USER_013 or QUOTA_004.
Read an account
GET /api/external/v1/admins/{userSeq}
Returns the account with its quota, role, status, phone, MFA state, and registration and update history. Dates are formatted strings rather than ISO instants.
isActive is deprecatedIt is kept for backward compatibility. Read status instead.
Update an account
PATCH /api/external/v1/admins/{userSeq}
Send as multipart/form-data.
| Parameter | Type | Required | Description |
|---|---|---|---|
un | string | Yes | Display name, up to 20 characters |
tq | long | Yes | Storage allowance in bytes |
ct | string | No | Country dialing code; send with pn or omit both |
pn | string | No | Phone number; send with ct or omit both |
pif | file | No | Profile image |
dpi | boolean | No | Delete the existing profile image; defaults to false |
sk | string | No | Storage key |
A quota below what the account already uses is rejected with USER_014.
List administrators
GET /api/external/v1/admins
Lists administrator accounts in the caller's tenant, excluding ordinary users.
| Parameter | Type | Required | Description |
|---|---|---|---|
ps | integer | Yes | Page size, minimum 1 |
ci | long | No | Cursor - the last userSeq of the previous page |
s | enum | No | Filter by state - active or inactive, case-insensitive |
Any other value for s returns USER_019.
Set an account password
PATCH /api/external/v1/admins/{userSeq}/password
| Parameter | Type | Required | Description |
|---|---|---|---|
newPwd | string | Yes | New password, 8 to 64 characters, with at least one uppercase letter, lowercase letter, digit, and symbol |
The current password is not required, so this sets a password rather than changing one.
Activate and deactivate
PATCH /api/external/v1/admins/{userSeq}/activate
PATCH /api/external/v1/admins/{userSeq}/deactivate
A failed activation returns USER_017 and a failed deactivation returns USER_018. Neither your own account nor the
tenant's default administrator can be deactivated; attempting it returns USER_011.
Mark an account for deletion
PATCH /api/external/v1/admins/{userSeq}/withdraw
Moves the account to a pending-delete state rather than removing it. Your own account and the default administrator
cannot be targeted, which returns USER_012. A failure to queue the deletion returns USER_016. See
accounts pending deletion for what happens next.
Reset MFA
PATCH /api/external/v1/admins/users/{userSeq}/mfa/reset
Clears the target account's multi-factor setup so the user can enrol again. The action is written to the audit log as
USER_TWOFACTOR_RESET. A failure returns USER_022.
Import accounts from CSV
GET /api/external/v1/admins/import/csv/sample
POST /api/external/v1/admins/import/csv
Download the sample first; it defines the column layout the import expects.
| Parameter | Type | Required | Description |
|---|---|---|---|
file | file | Yes | CSV file in the sample's format |
se | boolean | No | Send a welcome email to each created account |
A fully successful import returns insertCount.
If any row fails - a duplicate account id, a seat or quota overrun, or a parse error - the response is HTTP 400 with a
text/csv body rather than the JSON envelope. The returned file repeats the original columns and adds the reason each
row failed. Check Content-Type before parsing the response as JSON.
Read a user's profile image
GET /api/external/v1/users/{userSeq}/profile-image
This one endpoint sits under /users rather than /admins, so the role rule does not apply to it and the ordinary
scope check does instead. A key with api:read or api:write and no administrator role can call it.
Unlike every other endpoint on this page, this one does not restrict the lookup to the caller's tenant. A userSeq
belonging to a different tenant returns that user's profile image rather than USER_005. Do not rely on it to confirm
tenant membership, and consider it when deciding who may reach it.
An account with no profile image returns USER_008.
List user accounts
GET /api/external/v1/admins/users
Lists accounts with the USER role in the caller's tenant. Administrator and super administrator accounts are always
excluded; use List administrators for those.
| Parameter | Type | Required | Description |
|---|---|---|---|
ps | integer | Yes | Page size, minimum 1 |
ci | long | No | Cursor - the last userSeq of the previous page |
s | enum | No | Filter by state - ACTIVE or INACTIVE; any other value returns USER_019 |
u | string | No | Partial match against the account id |
Omitting s returns the default visible states, leaving out deleted and pending-delete accounts.
Search accounts for API Key issuance
GET /api/external/v1/admins/users/search
| Parameter | Type | Required | Description |
|---|---|---|---|
sk | string | Yes | Search keyword matched against account id or display name |
Only accounts with the USER role and ACTIVE status are returned. Other roles and states never appear, which is why
this endpoint suits choosing the account an API Key will be issued to.
Read storage usage
GET /api/external/v1/admins/users/{userSeq}/storage-usage
Breaks an account's consumption into its parts.
| Field | Type | Meaning |
|---|---|---|
totalQuotaBytes | long | Storage allowance |
totalUsedBytes | long | Total consumed |
liveUsedBytes | long | Consumed by current files |
versionUsedBytes | long | Consumed by version history |
trashUsedBytes | long | Consumed by items in the trash |
Emptying the trash or deleting version history reclaims the last two.
Read an account's security log
GET /api/external/v1/admins/users/{userSeq}/log
| Parameter | Type | Required | Description |
|---|---|---|---|
ps | integer | Yes | Number of entries to return |
ci | long | No | Cursor - the last userLogSeq of the previous page |
Entries carry activityType, ipAddress, region, registerDate, and actorType, which says whether the user
performed the action themselves or an administrator did it on their behalf.
Check the status of several accounts
POST /api/external/v1/admins/users/bulk-status
| Parameter | Type | Required | Description |
|---|---|---|---|
userIds | array | Yes | Account ids to look up; must not be empty |
Returns userSeq, userId, and status for each match.
An id that does not exist in the caller's tenant is left out of the result with no per-item error. Compare the returned list against what you sent rather than assuming every id was resolved.
Create and update accounts in bulk
POST /api/external/v1/admins/users/bulk
Creates and updates accounts in one request. Each entry with no us is created; each entry with a us is updated. The
HTTP status is always 200 whether or not anything was created.
| Parameter | Type | Required | Description |
|---|---|---|---|
users | array | Yes | Accounts to create or update; must not be empty |
Each entry takes:
| Field | Type | Required | Description |
|---|---|---|---|
us | integer | No | Account seq; omit to create, supply to update |
ui | string | No | Account id, up to 25 characters; required when creating |
encPwd | string | No | Password, 8 to 64 characters with at least one uppercase letter, lowercase letter, digit, and symbol; required when creating |
un | string | Yes | Display name, up to 20 characters |
tq | long | Yes | Storage allowance in bytes; cannot be reduced below current usage |
rt | string | No | Role; required when creating |
ct | string | No | Country dialing code; send with pn or omit both |
pn | string | No | Phone number; send with ct or omit both |
The response reports insertedCount, updatedCount, and the userId and userSeq of each created account.
Unlike POST /admins, this one does not restrict rt. ADMIN and SUPER_ADMIN are accepted and the accounts are
created with those roles, so any admin API Key that reaches this endpoint can mint new administrator accounts. Restrict
which of your callers can use it, and validate rt yourself before forwarding a request. A value that is not a known
role is not rejected cleanly either - it fails with HTTP 500.
A missing required field on create returns USER_026, a password that fails the pattern returns USER_001, a
duplicate account id returns USER_004, and reducing a quota below current usage returns USER_014.
Delete an account immediately
DELETE /api/external/v1/admins/users/{userSeq}
This is a hard delete. It bypasses the pending-delete grace period entirely and cannot be undone. There is no guard preventing you from deleting your own account or the tenant's only administrator. Use withdraw unless you specifically intend an unrecoverable deletion.
Accounts pending deletion
Accounts marked for deletion wait for a scheduled date before being removed, and can be restored until then.
GET /api/external/v1/admins/users/pending-delete
PATCH /api/external/v1/admins/users/pending-delete/{userSeq}/restore
PATCH /api/external/v1/admins/users/pending-delete/permanent-delete
The listing accepts:
| Parameter | Type | Required | Description |
|---|---|---|---|
ps | integer | Yes | Page size, minimum 1 |
pdSd | date | No | Start of the range for when the account was marked; send with pdEd |
pdEd | date | No | End of the range for when the account was marked; send with pdSd |
dsSd | date | No | Start of the scheduled-deletion range; send with dsEd |
dsEd | date | No | End of the scheduled-deletion range; send with dsSd |
ci | long | No | Cursor - the last userSeq; send with cv |
cv | date | No | Cursor - the last pendingDeleteDate; send with ci |
Each entry reports pendingDeleteDate, deleteScheduleTime, and who marked the account. status is always
PENDING_DELETE.
Restoring returns the account to normal use. An account that is not in the pending-delete state returns
ADMIN_USER_001.
Permanent deletion takes ul, a non-empty array of userSeq values, and removes them all. An empty list returns
REQUEST_001. If any listed account is not actually pending deletion, the whole request fails with ADMIN_USER_002
rather than partially applying - re-read the list and retry with only valid entries.
License
Three endpoints manage the Thinkfree Drive license.
POST /api/external/v1/admins/register/license
GET /api/external/v1/admins/get/license
GET /api/external/v1/admins/get/license/status
The license endpoints do not use the common envelope. A successful registration returns HTTP 200 with no body, and the
two read endpoints return the license fields at the top level. Failures return {"code": number, "error": "ENUM_NAME"}
where error is a constant name, not a message. Do not parse these responses with your common envelope handler.
GET /admins/get/license/status is authorized for USER rather than ADMIN, so an ordinary account's key can read
license status.
Register a license
Send the license file as multipart/form-data under licenseFile. A malformed file returns HTTP 400 with the matching
code, a processing failure returns HTTP 500 with 6999, and a failure reading the upload returns HTTP 500 with 4999.
Read the license
GET /admins/get/license returns the issued license.
| Field | Type | Meaning |
|---|---|---|
publisher | string | Issuer; always TF |
CATEGORY | string | TRIAL or PRODUCTION |
LICENSE_TYPE | string | SITE or PERSEAT |
LICENSE_ID | string | License identifier |
CLIENT_NAME | string | Customer name |
issuedOn | string | Issue date |
expiresOn | string | Expiry date |
updatedAt | string | When the license record was last refreshed |
spents | integer | Days elapsed since issue |
days | integer | Total validity in days |
state | boolean | Whether the license is currently valid |
gracePeriod | boolean | Present and true only while in the post-expiry grace period of up to eight days |
paidWhiteLabel | boolean | Whether the paid white-label option is included |
constrainValue | string | Seat limit; present only for PERSEAT |
currentSeatNum | integer | Active accounts; present only for PERSEAT |
availableSeatNum | integer | Remaining seats; present only for PERSEAT |
Read only the fields above. The response can carry additional internal values that are not part of this contract.
A tenant with no license registered returns HTTP 404 with 6998.
Read license status
GET /admins/get/license/status returns state, days, spents, expiresOn, and paidWhiteLabel. When the license
needs attention it adds code and resultMessage to the same HTTP 200 response - a warning signal inside a successful
response, not an HTTP error.
| Condition | code | resultMessage |
|---|---|---|
| Expires within ten days | 6994 | LICENSE_UNDER_LIMIT |
| Already expired | 6995 | LICENSE_FILE_EXPIRATION |
| Seat limit exceeded, which takes precedence over both rows above | 6993 | LICENSE_EXCEED_LIMIT |
| Healthy | absent | absent |
When the seat limit is exceeded the response also carries currentSeatNum, constrainValue, and availableSeatNum,
which can be negative, and state is forced to false.
Sign-in settings
Controls how users authenticate into the tenant.
GET /api/external/v1/admins/login-settings
PATCH /api/external/v1/admins/login-settings
| Parameter | Type | Required | Description |
|---|---|---|---|
loginType | enum | Yes | local, oidc, or jwt |
providers | array | No | Identity providers; omit or send null for local |
A jwt configuration takes exactly one provider. An oidc configuration may hold several but only one may be
ACTIVE. Each provider carries ssoProviderSeq when updating, displayName, status, and a config object whose
type must equal loginType - a mismatch returns SSO_PROTOCOL_002, and an unsupported protocol returns
SSO_PROTOCOL_001.
An oidc config takes issuerUrl, clientId, and clientSecret. A jwt config takes issuer, an optional
audience, and keySourceType of public_key or jwks - supply publicKey for the first and jwksUri for the
second. emailClaimName names an alternative claim to read the email from, and errorRedirectUrl is where a failed
sign-in lands.
The read response reports redirectUri for the callback and superAdminEnforced, which is true when a super
administrator is imposing the setting across tenants.
GET /admins/login-settings includes clientSecret and publicKey as stored, unmasked. Treat the response as
sensitive: do not log it, cache it in a browser, or pass it to a client application.
Branding
GET /api/external/v1/admins/branding
PUT /api/external/v1/admins/branding
Branding applies to the API Key's tenant; there is no identifier in the path. Send the update as
multipart/form-data.
| Parameter | Type | Required | Description |
|---|---|---|---|
tabTitle | string | Yes | Browser tab title, 2 to 30 characters |
serviceName | string | Yes | Service name, 2 to 30 characters |
showPoweredBy | boolean | Yes | Whether to show the provider attribution |
useDefault | boolean | Yes | true to use the stock images, false to supply all four |
favicon | file | Conditional | ICO or PNG, up to 500 KB; required when useDefault is false |
logoIcon | file | Conditional | PNG or SVG, up to 1 MB; required when useDefault is false |
logoImage | file | Conditional | PNG or SVG, up to 2 MB; required when useDefault is false |
emailLogo | file | Conditional | PNG or SVG, up to 2 MB; required when useDefault is false |
When useDefault is true the four image fields are ignored without validation. When it is false all four are
required; sending fewer returns REQUEST_001.
GET /api/external/v1/admins/branding/images/{type}
type is favicon, logo-icon, logo-image, or email-logo, and the required source is custom or default.
A tenant without it always receives the stock image regardless of source, so source=custom is only meaningful on a
white-label tenant. A missing custom image returns BRANDING_001.
Access restriction
Limits where users may sign in from.
PUT /api/external/v1/admins/access-restriction/countries
PUT /api/external/v1/admins/access-restriction/ips
| Parameter | Type | Required | Description |
|---|---|---|---|
countries | array | Yes | Allowed ISO alpha-2 country codes |
ips | array | Yes | Allowed IPv4 addresses or CIDR ranges |
CIDR host bits must be zero, and IPv6 is not supported. An invalid country code returns ACCESS_002 and an invalid
address returns ACCESS_004.
Neither adds to what is already stored. Sending an empty array deletes every entry. Read the current list, modify it, and send the complete result.
Turn the restriction on and off through
system-config/access-restriction-status. Enabling it with no countries and no addresses
registered returns ACCESS_003.
Share settings
GET /api/external/v1/admins/share-settings
PATCH /api/external/v1/admins/share-settings
| Parameter | Type | Required | Description |
|---|---|---|---|
se | boolean | Yes | Whether sharing is available at all |
esa | boolean | Conditional | Allow sharing outside the organization; required when se is true |
sua | boolean | Conditional | Allow the SPECIFIC_USERS share type; required when se is true |
ola | boolean | Conditional | Allow the ORG_LINK share type; required when se is true |
pa | boolean | Conditional | Allow the PUBLIC_LINK share type; required when se is true |
When se is true, at least one of sua, ola, and pa must also be true. Enabling pa requires esa to be
true as well.
The read response adds isEnforced. When it is true, a super administrator is applying one setting to every tenant
and the values returned are the global ones rather than this tenant's own.
SMTP
One SMTP configuration exists per tenant. Sending smtpConfigSeq updates the existing record; omitting it creates one.
GET /api/external/v1/admins/smtp-config
POST /api/external/v1/admins/smtp-config
POST /api/external/v1/admins/smtp-config/test
| Parameter | Type | Required | Description |
|---|---|---|---|
clientName | string | Yes | Customer name for the configuration |
host | string | Yes | SMTP server address |
port | integer | Yes | SMTP port |
encryptionType | enum | Yes | SSL, TLS, or NONE |
authType | enum | Yes | IDPW or OAUTH |
username | string | Yes | SMTP account |
encPwd | string | Conditional | Password; used when authType is IDPW |
oauthInfo | string | Conditional | OAuth access token; used when authType is OAUTH |
fromAddress | string | Yes | Sender address |
testReceiverAddress | string | Yes | Where the test message is sent |
smtpConfigSeq | integer | Conditional | The configuration to update; required by the test endpoint |
encPwd and oauthInfo are not enforced by request validation even though one of them is needed in practice, so a
configuration can be saved that cannot actually send. Use the test endpoint to confirm; a failed send returns
SMTP_CONFIG_002, and reading a configuration that does not exist returns SMTP_CONFIG_001.
GET /admins/smtp-config returns encPwd decrypted. Treat the response as sensitive and keep it server-side.
With authType of OAUTH, oauthInfo is used directly as the access token and username as the account. The server
does not check that the token was issued for that account or for fromAddress, and it does not refresh an expired
token. Verify the pairing yourself and plan for manual token rotation.
System configuration
GET /api/external/v1/admins/system-config/general
PUT /api/external/v1/admins/system-config/general
GET /api/external/v1/admins/system-config/office
PUT /api/external/v1/admins/system-config/office
GET /api/external/v1/admins/system-config/storage
GET /api/external/v1/admins/system-config/intellect
PUT /api/external/v1/admins/system-config/intellect
GET /api/external/v1/admins/system-config/mfa-status
PUT /api/external/v1/admins/system-config/mfa-status
GET /api/external/v1/admins/system-config/file-size-limit
PUT /api/external/v1/admins/system-config/file-size-limit
GET /api/external/v1/admins/system-config/access-restriction-status
PUT /api/external/v1/admins/system-config/access-restriction-status
GET /api/external/v1/admins/system-config/shares/exists
A PUT that creates a setting for the first time returns 201; updating an existing one returns 200.
General
clientDomain is the tenant's client domain, returned as an empty string when unset.
When more than one tenant is active, only a super administrator may change clientDomain; an ordinary administrator
receives SYSTEM_CONFIG_008. When a base domain is registered, the value must end with it, otherwise the call fails
with BASE_DOMAIN_004.
Office
officeDomain and officeAdapterName, both required on update. officeDomain is validated as a URL.
Storage
Read-only. Returns storageType of CEPH or S3, plus bucketName, region, endPoint, and status.
The response includes accessKey and secretKey in clear text. Any administrator key that can call this endpoint can
read the tenant's object-storage credentials. Restrict who can reach it and never forward the response to a client.
Feature toggles
intellect, mfa-status, and access-restriction-status each read and write a single enabled boolean. There is no
intellectStatus or status field; use enabled.
access-restriction-status also returns allowedCountries and allowedIps. Setting enabled to true requires at
least one country or address to be registered already, otherwise it fails with ACCESS_003.
File size limit
maxFileUploadSize is the upload ceiling in bytes. Send null for no limit; any value you do send must be positive.
Share data check
shares/exists reports whether the tenant holds any share records, as a single exists boolean. Use it before turning
sharing off to find out whether doing so would affect existing shares.
Errors
See Errors for the full list of codes these endpoints return.