Authentication & User Management Authentication methods, user CRUD, getting started patterns
Authentication Methods
All authenticated endpoints require: Authorization: Bearer {token}
The token can be a JWT (from Basic Auth or OAuth), an API key, or a 2FA-upgraded JWT.
Method 1: Basic Auth to JWT
Send HTTP Basic Auth (email:password) to get a JWT.
/current/user/auth/
Authorization: Basic {base64(email:password)}
Returns auth_token (JWT). If the account has 2FA enabled, the returned token has limited scope until 2FA verification is completed.
Method 2: API Keys
Long-lived tokens for service-to-service communication. Created via the API or the web UI. Used with the same Authorization: Bearer {api_key} header format as JWTs. Keys optionally support scoped permissions (scopes), agent names (agent_name), and expiration (expires). Scoped keys are enforced using the same scope system as v2.0 JWT tokens. Legacy keys without scopes retain full access.
Method 3: OAuth 2.0 PKCE
For desktop/mobile apps and MCP-connected agents. No password passes through the agent. Access tokens last 1 hour, refresh tokens 30 days. S256 challenge method only. See the OAuth 2.0 reference for the full flow.
Method 4: 2FA
When 2FA is enabled on an account, Basic Auth returns a limited-scope JWT. Complete authentication via POST /current/user/auth/2factor/auth/{token}/ with the 2FA code. The response contains a full-scope JWT.
Getting Started
Option 1: Use a Human's Existing Account (API Key)
A human creates an API key and gives it to you. You operate as that user with their permissions, org, and billing.
Human instructions: "Go to Settings > Devices & Agents > API Keys and click Create API Key. Optionally enter a memo to label the key (e.g., 'Agent access'), then click Create. Copy the key immediately -- it is only displayed once. Direct link: https://go.fast.io/settings/api-keys"
Once you have the key: Authorization: Bearer {api_key}. No further steps needed.
Option 2: Create Your Own Agent Account (Autonomous)
Create your own account to work independently.
POST /current/user/withemail_address,password,tos_agree=true,agent=trueGET /current/user/auth/with Basic Auth to get JWT- Verify email:
POST /current/user/email/validate/withemail— sends verification codePOST /current/user/email/validate/withemailandemail_token— validates the code
POST /current/org/create/withdomain(required, 2-80 chars lowercase alphanumeric + hyphens)POST /current/org/{org_id}/create/workspace/withfolder_name,name,perm_join,perm_member_manage
Agent accounts get the agent plan: free, 50 GB storage, 5,000 credits/month, no expiration.
Option 3: Agent Account Invited to a Human's Org
- Create an agent account (steps 1-2 from Option 2)
- Give the human your agent's email address
- Human invites agent to their org or workspace
- Accept:
POST /current/org/{org_id}/members/join/orPOST /current/workspace/{workspace_id}/members/join/ - You now operate within their resources with granted permissions
Option 4: PKCE Browser Login (No Password Sharing)
Most secure option. Works with SSO. No credentials pass through the agent.
- Agent initiates PKCE flow via
POST /current/oauth/authorize/withcode_challenge,code_challenge_method=S256,client_id,redirect_uri,response_type=code - User opens the returned URL in browser, signs in, approves access
- Browser displays authorization code — user copies it to agent
- Agent calls
POST /current/oauth/token/withgrant_type=authorization_code,code,code_verifier - Access tokens last 1 hour; refresh via
POST /current/oauth/token/withgrant_type=refresh_token
Which option to choose
- Human wants you to manage their account → Option 1 (API key)
- You're building something independently → Option 2 (agent account + own org)
- You need to work within a human's existing org → Option 3 (agent account + invitation)
- Human wants to authorize agent without sharing credentials → Option 4 (PKCE)
Compact Responses (output=)
Every endpoint that returns user objects — including your own profile (/current/user/details/), other users' profiles, and member listings on workspaces, orgs, and shares — accepts an optional output query parameter that selects the response shape. A single detail-level token may be combined with modifier tokens; specifying two detail levels (e.g. ?output=terse,standard) returns HTTP 400. When output= is omitted, responses are full and byte-for-byte unchanged.
| Level | Fields returned on each user (cumulative) |
|---|---|
terse | id, account_type, first_name, last_name, profile_pic |
standard | terse + email_address, is_anonymous, status, permissions, created, updated, invite, expires, locked, suspended, closed (last three visible to self/managers only) |
full | standard + country_code, phone_country, phone_number, 2factor, notify, sync_profile, tos_agree, valid_email, valid_phone, apps, owner_defined, parents |
Use terse for mention pickers, avatar lists, creator cells, and message-author headers — it carries the identifier, display name, account type (human/agent), and profile picture, which is everything the avatar/name cells render. email_address is intentionally excluded from terse to keep PII out of the smallest shape. Use standard for member list views and account-settings summaries — it adds email_address, the caller-relative permissions role, active/pending status, invitation details for pending members, and account created/updated timestamps (now visible at standard for every user the caller can see, not just self/managers). Admin member-list UIs also receive the lock/suspend/close account-status chips at standard; these three fields are gated server-side to the self-view or manager-view of the target user, so non-privileged callers never see them at any tier. Use full (or omit the parameter) for the user profile screen, account settings, admin audits, and any workflow that reads phone, 2FA, TOS, anonymous-guest detection, or account-validity fields. Unknown tokens are silently ignored. Add the markdown modifier (e.g. ?output=standard,markdown) to receive the response as GitHub-flavored Markdown (Content-Type: text/markdown; charset=UTF-8) instead of JSON — see the cross-cutting ?output= reference for the full contract.
User Creation
POST /current/user/
Create a new user account.
Auth: None (IP-throttled)
Request Parameters
| Parameter | Type | Required | Constraints | Description |
|---|---|---|---|---|
| email_address | string | Yes | Valid email format; domain must accept email; must be unique | User's email address. Tags (e.g., +tag) are stripped for storage and uniqueness checks, but the original is preserved. |
| password | string | Yes | Must pass password validity checks | Account password. |
| tos_agree | string | Yes | Must be "true" |
Must be "true" to accept Terms of Service. |
| agent | string | No | "true" or "false" |
Set "true" for AI agent accounts. Sets account_type to "agent" permanently. Agent accounts skip email validation and use the agent plan. |
| first_name | string | No | Must pass name validation | User's given/first name. |
| last_name | string | No | Must pass name validation | User's family/last name. |
| phone_country | string | No | Numeric country calling code | Phone country code. Required if phone_number is provided. |
| phone_number | string | No | Numeric phone number | Phone number. Required if phone_country is provided. |
Request Example
curl -X POST "https://api.fast.io/current/user/" \
-d "email_address=jane.doe@example.com" \
-d "password=SecureP@ss123" \
-d "tos_agree=true" \
-d "first_name=Jane" \
-d "last_name=Doe" \
-d "agent=true"
Success Response (200 OK)
{
"result": "yes",
"current_api_version": "1.0"
}
Response Fields
| Field | Type | Description |
|---|---|---|
| result | string | "yes" on success |
| current_api_version | string | API version ("1.0") |
Error Responses
| Error Code | HTTP Status | Message | Cause |
|---|---|---|---|
1605 (Invalid Input) (1605) | 400 | "An invalid email was supplied." | Email format invalid |
1605 (Invalid Input) (1605) | 400 | "The email domain is invalid or cannot receive email." | Email domain validation failed |
1605 (Invalid Input) (1605) | 400 | "The email supplied is already in use." | Email already registered (after normalization) |
1605 (Invalid Input) (1605) | 400 | "An invalid password was supplied." | Password does not meet requirements |
1605 (Invalid Input) (1605) | 400 | "An invalid tos_agree value was create." | TOS value not a valid boolean string |
1605 (Invalid Input) (1605) | 400 | "You declined to accept the terms of service." | TOS set to "false" |
1605 (Invalid Input) (1605) | 400 | "An invalid first name was supplied to create." | First name fails validation |
1605 (Invalid Input) (1605) | 400 | "An invalid last name was supplied to create." | Last name fails validation |
1605 (Invalid Input) (1605) | 400 | "An invalid phone country code was supplied." | Invalid phone country code |
1605 (Invalid Input) (1605) | 400 | "An invalid phone number was supplied." | Invalid phone number |
1605 (Invalid Input) (1605) | 400 | "An invalid phone number or country code was supplied." | Full phone number validation failed |
1680 (Access Denied) (1680) | 403 | "Your attempt to create an account was not accepted." | Risk/fraud check failed |
1654 (Internal Error) (1600) | 500 | "We were unable to create your user account..." | Account creation failed |
Notes
- Email addresses are normalized by stripping tag extensions (e.g.,
user+tag@example.combecomesuser@example.com) for storage and uniqueness lookup; the original email is preserved separately. - Country code is detected from the client IP and stored automatically.
agent=trueis permanent and cannot be changed after account creation.- Agent accounts skip email validation requirements and can authenticate immediately.
User Management Endpoints
POST /current/user/update/
Update the current authenticated user's profile information.
Auth: Required (JWT)
Request Parameters
All fields are optional. Only provided fields are updated.
| Parameter | Type | Required | Constraints | Description |
|---|---|---|---|---|
| email_address | string | No | Valid email format; unique; domain must accept email | New email address. Resets email verification status. |
| password | string | No | Must pass validity checks | New password. |
| first_name | string | No | Must pass name validation | Updated given/first name. |
| last_name | string | No | Must pass name validation | Updated family/last name. |
| phone_country | string | No | Numeric country code; 2FA must be disabled first | Updated phone country code. Pass "null" or empty to clear. |
| phone_number | string | No | Numeric phone number; 2FA must be disabled first | Updated phone number. Pass "null" or empty to clear. |
| owner_defined | string (JSON) | No | Must be valid JSON if provided | Custom owner-defined properties. Pass null or empty to clear. |
Request Example
curl -X POST "https://api.fast.io/current/user/update/" \
-H "Authorization: Bearer {jwt_token}" \
-d "first_name=Jane" \
-d "last_name=Smith"
Success Response (200 OK)
{
"result": "yes",
"current_api_version": "1.0"
}
Error Responses
| Error Code | HTTP Status | Message | Cause |
|---|---|---|---|
1605 (Invalid Input) (1605) | 400 | "An invalid email was supplied to update." | Invalid email format |
1605 (Invalid Input) (1605) | 400 | "The email domain is invalid or cannot receive email." | Invalid email domain |
1660 (Conflict) (1608) | 409 | "There email you specified is not available." | Email already in use |
1670 (Restricted) (1681) | 403 | "You must disable 2-Factor before updating your phone." | 2FA enabled when trying to change phone |
1605 (Invalid Input) (1605) | 400 | "An invalid password was supplied to update." | Invalid password |
1605 (Invalid Input) (1605) | 400 | "An invalid first name was supplied to update." | Invalid first name |
1605 (Invalid Input) (1605) | 400 | "An invalid last name was supplied to update." | Invalid last name |
1605 (Invalid Input) (1605) | 400 | "Owner-defined properties must be valid JSON." | Invalid JSON in owner_defined |
Notes
- Changing the email address resets email verification status.
- Phone number changes require 2FA to be disabled first.
- If no fields have changed, the endpoint returns success and no update is performed.
POST /current/user/close/
Close (soft-delete) the current user's account.
Auth: Required (JWT)
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| email_address | string | Yes | Must match the user's current email address (confirmation). |
| dryrun | string | No | If truthy, checks eligibility without closing the account. |
Request Example
curl -X POST "https://api.fast.io/current/user/close/" \
-H "Authorization: Bearer {jwt_token}" \
-d "email_address=jane.doe@example.com"
Success Response (202 Accepted)
{
"result": "yes",
"current_api_version": "1.0"
}
Dry Run Response (Cannot Close, 202 Accepted)
{
"result": "no",
"current_api_version": "1.0"
}
Error Responses
| Error Code | HTTP Status | Message | Cause |
|---|---|---|---|
1653 (User Not Found) (1653) | 404 | "User not found to close." | User object invalid |
1605 (Invalid Input) (1605) | 400 | "An invalid email was supplied to close account." | Invalid email format |
1605 (Invalid Input) (1605) | 400 | "An incorrect email was supplied to close account." | Email does not match user's email |
1605 (Invalid Input) (1605) | 400 | "Cannot close user account that owns active organizations..." | User owns active organizations |
Notes
- 2FA verification is required if 2FA is enabled on the account.
- Users who own active organizations must close or transfer ownership first.
- The
dryrunparameter checks closure eligibility without actually closing the account. - On closure: subscriptions are cancelled, SSO connections are removed, the account is flagged as closed.
POST /current/user/email/
Check if an email address is already in use.
Auth: None (IP-throttled)
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| string | Yes | Email address to check availability. |
Request Example
curl -X POST "https://api.fast.io/current/user/email/" \
-d "email=jane.doe@example.com"
Email Available (202 Accepted)
{
"result": "yes",
"current_api_version": "1.0"
}
Email In Use (406 Not Acceptable)
{
"result": "no",
"current_api_version": "1.0"
}
Error Responses
| Error Code | HTTP Status | Message | Cause |
|---|---|---|---|
1605 (Invalid Input) (1605) | 400 | "You provided an invalid email to check." | Invalid email format or missing |
Notes
- The email is normalized (tags stripped) before lookup.
result: "yes"= available,result: "no"= already in use.
POST /current/user/email/reset/
Request a password reset email.
Auth: None (IP-throttled)
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| string | Yes | Email address of the account. |
Request Example
curl -X POST "https://api.fast.io/current/user/email/reset/" \
-d "email=jane.doe@example.com"
Success Response (202 Accepted)
{
"result": "yes",
"current_api_version": "1.0"
}
Error Responses
| Error Code | HTTP Status | Message | Cause |
|---|---|---|---|
1605 (Invalid Input) (1605) | 400 | "You provided an invalid email to check." | Invalid email format |
1654 (Internal Error) (1600) | 500 | "We were unable to send a verification email." | Email send failure |
Notes
- For security, this endpoint always returns success regardless of whether the email exists in the system.
POST /current/user/email/validate/
Send or validate an email verification code. Two-step flow.
Auth: Required (JWT)
Mode 1: Send Verification Code
When email_token is NOT provided, sends a new validation code to the user's email.
| Parameter | Type | Required | Description |
|---|---|---|---|
| string | Yes | Must match the authenticated user's email address. |
Mode 2: Validate Code
When email_token IS provided, validates the code and marks the email as verified.
| Parameter | Type | Required | Description |
|---|---|---|---|
| string | Yes | Must match the authenticated user's email address. | |
| email_token | string | Yes | Verification code received via email. |
Request Example (Send Code)
curl -X POST "https://api.fast.io/current/user/email/validate/" \
-H "Authorization: Bearer {jwt_token}" \
-d "email=jane.doe@example.com"
Request Example (Validate Code)
curl -X POST "https://api.fast.io/current/user/email/validate/" \
-H "Authorization: Bearer {jwt_token}" \
-d "email=jane.doe@example.com" \
-d "email_token=123456"
Success Response (202 Accepted)
{
"result": "yes",
"current_api_version": "1.0"
}
Error Responses
| Error Code | HTTP Status | Message | Cause |
|---|---|---|---|
1650 (Authentication Invalid) (1650) | 401 | "Your credentials were not supplied or invalid." | User not authenticated |
1658 (Not Acceptable) (1606) | 406 | "Your email address is already verified." | Email already verified |
1660 (Conflict) (1608) | 409 | "Your credentials do not match the email you provided." | Email mismatch with authenticated user |
1658 (Not Acceptable) (1606) | 406 | "You provided an invalid or expired token to validate email." | Invalid or expired code |
1680 (Access Denied) (1680) | 403 | "Provided code has expired, get a new code and try again." | Code expired |
POST /current/user/password/{code}/
Set a new password using a password reset code.
Auth: None (code-based authentication)
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| {code} | string | Yes | Password reset code from the reset email. |
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| password1 | string | Yes | New password. |
| password2 | string | Yes | New password confirmation. Must match password1. |
Request Example
curl -X POST "https://api.fast.io/current/user/password/abc123def456/" \
-d "password1=NewSecureP@ss" \
-d "password2=NewSecureP@ss"
Success Response (202 Accepted)
{
"result": "yes",
"current_api_version": "1.0"
}
Error Responses
| Error Code | HTTP Status | Message | Cause |
|---|---|---|---|
1680 (Access Denied) (1680) | 403 | "An invalid code was provided, cannot reset password." | Invalid code format |
1680 (Access Denied) (1680) | 403 | "Provided code was not found or expired, cannot reset password." | Code not found or wrong type |
1680 (Access Denied) (1680) | 403 | "Provided code has expired, get a new code and try again." | Code expired |
1660 (Conflict) (1608) | 409 | "Provided code belongs to another user account and cannot be used." | Code/user mismatch |
1683 (Resource Missing) (1609) | 404 | "The provided code belongs to an invalid user." | User not found for code |
1660 (Conflict) (1608) | 409 | "The provided passwords don't match." | password1 and password2 differ |
1658 (Not Acceptable) (1606) | 406 | "Both password fields must be provided and match." | Missing password fields |
1654 (Internal Error) (1600) | 500 | "The provided password could not be processed..." | Internal processing error |
Notes
- The reset code is consumed (deleted) after successful password change.
- A new JWT is created after the password is set.
GET /current/user/password/{code}/details/
Get details of a password reset code (check if valid/expired).
Auth: None (code-based)
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| {code} | string | Yes | Password reset code to check. |
Request Example
curl -X GET "https://api.fast.io/current/user/password/abc123def456/details/"
Success Response (200 OK)
{
"result": "yes",
"response": {
"email": "jane.doe@example.com"
},
"current_api_version": "1.0"
}
Response Fields
| Field | Type | Description |
|---|---|---|
| response.email | string | The email address associated with the reset code. |
Error Responses
| Error Code | HTTP Status | Message | Cause |
|---|---|---|---|
1680 (Access Denied) (1680) | 403 | "An invalid code was provided, cannot reset password." | Invalid code format |
1680 (Access Denied) (1680) | 403 | "Provided code was not found or expired, cannot reset password." | Code not found |
1680 (Access Denied) (1680) | 403 | "Provided code has expired, get a new code and try again." | Code expired |
1660 (Conflict) (1608) | 409 | "Provided code belongs to another user account..." | Code/user mismatch |
1677 (Locked) (1682) | 423 | "The account has been restricted and cannot be updated." | Account locked/suspended/closed |
GET /current/user/phone/{country_code}-{phone_number}/
Validate a phone number and country code combination.
Auth: Required (JWT)
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| {country_code}-{phone_number} | string | Yes | Country code and phone number separated by a hyphen (e.g., 1-5551234567). |
Request Example
curl -X GET "https://api.fast.io/current/user/phone/1-5551234567/" \
-H "Authorization: Bearer {jwt_token}"
Success Response (202 Accepted)
{
"result": "yes",
"current_api_version": "1.0"
}
Error Responses
| Error Code | HTTP Status | Message | Cause |
|---|---|---|---|
1605 (Invalid Input) (1605) | 400 | "You provided an invalid phone number to check." | Invalid format |
1605 (Invalid Input) (1605) | 400 | "An invalid phone country code was supplied." | Invalid country code |
1605 (Invalid Input) (1605) | 400 | "An invalid phone number was supplied." | Invalid phone number |
1605 (Invalid Input) (1605) | 400 | "An invalid phone number or country code was supplied." | Full number validation failed |
GET /current/user/pin/
Get the user's support PIN and identity verification hash.
Auth: Required (JWT)
Request Example
curl -X GET "https://api.fast.io/current/user/pin/" \
-H "Authorization: Bearer {jwt_token}"
Success Response (200 OK)
{
"result": "yes",
"response": {
"supportcode": "1234",
"support_hash": "a1b2c3d4e5f6..."
},
"current_api_version": "1.0"
}
Response Fields
| Field | Type | Description |
|---|---|---|
| response.supportcode | string | 4-digit support PIN. Defaults to "0000" if not set. |
| response.support_hash | string | Identity verification hash for support integration. |
Error Responses
| Error Code | HTTP Status | Message | Cause |
|---|---|---|---|
1653 (User Not Found) (1653) | 404 | "Unable to fetch the user details." | User not found |
1654 (Internal Error) (1600) | 500 | "Internal temporary error, please try again later." | Internal error |
GET|POST /current/user/sso/signin/{provider}/
SSO (Single Sign-On) authentication flow.
Auth: None (IP-throttled)
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| {provider} | string | Yes | SSO provider name: google, apple, or microsoft. |
GET: Get SSO Redirect URL
Returns the OAuth2 authorization URL for the specified provider.
curl -X GET "https://api.fast.io/current/user/sso/signin/google/"
GET Response (200 OK)
{
"result": "yes",
"response": {
"provider": "google",
"redirect_url": "https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=...",
"return_url": "https://fast.io/sso/callback/google"
},
"current_api_version": "1.0"
}
Response Fields
| Field | Type | Description |
|---|---|---|
| response.provider | string | The provider name. |
| response.redirect_url | string | URL to redirect the user to for SSO authentication. |
| response.return_url | string | Callback URL the provider will redirect back to. |
POST: Process SSO Callback
Processes the OAuth2 callback with the authorization code from the provider.
| Parameter | Type | Required | Description |
|---|---|---|---|
| code | string | Yes | Authorization code from the SSO provider. |
| state | string | Yes | State token for CSRF protection. |
Error Responses
| Error Code | HTTP Status | Message | Cause |
|---|---|---|---|
1605 (Invalid Input) (1605) | 400 | "An invalid provider name was supplied." | Invalid provider name format |
1605 (Invalid Input) (1605) | 400 | "An unknown provider name was supplied." | Provider not in allowed list |
1605 (Invalid Input) (1605) | 400 | "Cookies must be enabled and passed to this API." | Missing state cookie |
1680 (Access Denied) (1680) | 403 | "Permission was not granted by the provider." | OAuth error returned from provider |
1673 (SSO Auth Error) (1690) | 401 | "Invalid or missing input in a required field was received." | Missing code or state |
Notes
- Supported providers:
google,apple,microsoft. - GET generates a state token (requires cookies) and returns the redirect URL.
- POST exchanges the authorization code for tokens and creates/links the user account.
GET /current/user/assets/
List available user asset metadata types (e.g., profile photo specifications).
Auth: None
Request Example
curl -X GET "https://api.fast.io/current/user/assets/"
Notes
- Returns the schema/specifications for available asset types, not actual assets.
GET /current/user/available_profiles/
Check what profile types (orgs, workspaces, shares) the current user has access to.
Auth: Required (JWT)
Request Example
curl -X GET "https://api.fast.io/current/user/available_profiles/" \
-H "Authorization: Bearer {jwt_token}"
Success Response (200 OK)
{
"result": "yes",
"response": {
"has_orgs": true,
"has_workspaces": true,
"has_shares": false
},
"current_api_version": "1.0"
}
Response Fields
| Field | Type | Description |
|---|---|---|
| response.has_orgs | boolean | Whether the user has access to any organizations. |
| response.has_workspaces | boolean | Whether the user has access to any workspaces. |
| response.has_shares | boolean | Whether the user has access to any shares. |
Error Responses
| Error Code | HTTP Status | Message | Cause |
|---|---|---|---|
1653 (User Not Found) (1653) | 404 | "Unable to fetch the user details." | User not found |
GET /current/user/{user_id}/details/
Get user profile details.
Auth: Required (JWT)
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| {user_id} | string | No | 19-digit user ID. If omitted, returns the current user's details. |
Request Example
curl -X GET "https://api.fast.io/current/user/1234567890123456789/details/" \
-H "Authorization: Bearer {jwt_token}"
Success Response (200 OK)
{
"result": "yes",
"response": {
"user": {
"id": "12345678901234567890",
"account_type": "human",
"email_address": "jane.doe@example.com",
"first_name": "Jane",
"last_name": "Doe",
"locked": false,
"profile_pic": "https://assets.fast.io/..."
}
},
"current_api_version": "1.0"
}
Response Fields
| Field | Type | Description |
|---|---|---|
| response.user.id | string | 19-digit user ID. |
| response.user.account_type | string | "human" or "agent". |
| response.user.email_address | string | User's email address. |
| response.user.first_name | string | Given name. |
| response.user.last_name | string | Family name. |
| response.user.locked | boolean | Whether the account is locked. |
| response.user.profile_pic | string | Profile photo URL. |
Self-Only Fields (included only when viewing your own profile)
| Field | Type | Description |
|---|---|---|
| 2factor | boolean | Whether 2FA is enabled. |
| closed | boolean | Whether the account is closed. |
| country_code | string | Country of residence. |
| created | string | Registration date. |
| phone_country | string | Phone country code. |
| phone_number | string | Phone number. |
| suspended | boolean | Suspension status. |
| tos_agree | string | ToS agreement date. |
| updated | string | Last profile update time. |
| valid_email | boolean | Email verified status. |
| valid_phone | boolean | Phone verified status. |
Error Responses
| Error Code | HTTP Status | Message | Cause |
|---|---|---|---|
1653 (User Not Found) (1653) | 404 | "Unable to fetch the user details." | User not found |
GET /current/user/me/autosync/{state}/
Enable or disable profile photo auto-synchronization from SSO providers.
Auth: Required (JWT)
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| {state} | string | Yes | "enable" or "disable". |
Request Example
curl -X GET "https://api.fast.io/current/user/me/autosync/enable/" \
-H "Authorization: Bearer {jwt_token}"
Success Response (200 OK)
{
"result": "yes",
"current_api_version": "1.0"
}
Error Responses
| Error Code | HTTP Status | Message | Cause |
|---|---|---|---|
1664 (Datastore Error) (1600) | 500 | "There was an internal error processing your request..." | Commit failure |
GET /current/user/me/allowed/
Check if the user's country (based on IP geolocation) allows creating shares or organizations.
Auth: None (IP-throttled)
Request Example
curl -X GET "https://api.fast.io/current/user/me/allowed/"
Success Response (200 OK)
{
"result": "yes",
"response": {
"allowed": true
},
"current_api_version": "1.0"
}
Response Fields
| Field | Type | Description |
|---|---|---|
| response.allowed | boolean | Whether the user's location allows resource creation. |
| response.reasons | array | Array of blocked reason strings. Only present when allowed is false. |
GET /current/user/me/limits/orgs/
Check free organization creation eligibility.
Auth: Required (JWT)
Request Example
curl -X GET "https://api.fast.io/current/user/me/limits/orgs/" \
-H "Authorization: Bearer {jwt_token}"
Success Response (200 OK)
{
"result": "yes",
"response": {
"can_create_free_org": true,
"existing_free_orgs": 0,
"cooldown_remaining": 0,
"max_free_orgs": 1
},
"current_api_version": "1.0"
}
Response Fields
| Field | Type | Description |
|---|---|---|
| response.can_create_free_org | boolean | Whether the user can create a free organization. |
| response.existing_free_orgs | integer | Number of existing free organizations owned by the user. |
| response.cooldown_remaining | integer | Seconds remaining before next creation is allowed. |
| response.max_free_orgs | integer | Maximum number of free organizations allowed. |
| response.reason | string | Reason creation is not allowed. Only present when can_create_free_org is false. |
Error Responses
| Error Code | HTTP Status | Message | Cause |
|---|---|---|---|
1653 (User Not Found) (1653) | 404 | "Unable to fetch the user." | User not found |
GET /current/user/me/list/shares/
List all shares accessible to the current user.
Auth: Required (JWT)
Query Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| archived | string | No | "false" |
"true" to show archived shares, "false" to show non-archived. |
Request Example
curl -X GET "https://api.fast.io/current/user/me/list/shares/" \
-H "Authorization: Bearer {jwt_token}"
Success Response (200 OK)
{
"result": "yes",
"response": {
"shares": [
{
"id": "12345678901234567890",
"name": "Project Files",
"type": "send",
"archived": false
}
]
},
"current_api_version": "1.0"
}
Response Fields
| Field | Type | Description |
|---|---|---|
| response.shares | array | Array of share resource objects. Each includes parent workspace and org info. |
Notes
- Shares are gathered from three sources: owned by the user, invited to, and joined.
- Duplicates are removed by share ID.
- Does NOT include shares from workspaces the user has access to — only shares with direct user relationships.
GET /current/user/{user_id}/assets/
List set assets (e.g., profile photo) for a user.
Auth: Required (JWT)
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| {user_id} | string | Yes | 19-digit numeric user ID. |
Request Example
curl -X GET "https://api.fast.io/current/user/12345678901234567890/assets/" \
-H "Authorization: Bearer {jwt_token}"
POST|DELETE /current/user/{user_id}/assets/{asset_name}/
Upload or delete a user asset.
Auth: Required (JWT)
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| {user_id} | string | Yes | 19-digit numeric user ID. |
| {asset_name} | string | Yes | Asset type name (e.g., profile_pic). |
POST: Upload Asset
Multipart form data with exactly one file upload.
| Parameter | Type | Required | Description |
|---|---|---|---|
| (file) | file | Yes | The asset file. Exactly one file must be included. |
| metadata | array | No | Optional metadata. Must be a valid array if provided. |
DELETE: Delete Asset
No request body required.
Error Responses
| Error Code | HTTP Status | Message | Cause |
|---|---|---|---|
1651 (Invalid Request Type) (1651) | 400 | "Only user may modify." | Non-owner attempting to modify |
1604 (File Missing) (1604) | 400 | "Asset upload missing" | No file in POST request |
1605 (Invalid Input) (1605) | 400 | "metadata invalid" | Invalid metadata parameter |
Notes
- Only the user themselves can modify their own assets.
- Uploading or deleting disables profile photo auto-sync.
GET|HEAD /current/user/{user_id}/assets/{asset_name}/read/
Read the binary content of a user asset.
Auth: Required (JWT)
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| {user_id} | string | Yes | 19-digit numeric user ID. |
| {asset_name} | string | Yes | Asset type name (e.g., profile_pic). |
Notes
- Returns raw binary bytes with appropriate content-type headers, not JSON.
- HEAD returns headers only.
Invitations
GET /current/user/invitation/{invitation_id}/details/
Get details for a specific invitation.
Auth: Required (JWT)
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| {invitation_id} | string | Yes | Invitation ID (numeric) or invitation key (alphanumeric). |
Request Example
curl -X GET "https://api.fast.io/current/user/invitation/12345678901234567890/details/" \
-H "Authorization: Bearer {jwt_token}"
Success Response (200 OK)
{
"result": "yes",
"response": {
"invitation": {
"id": "12345678901234567890",
"entity": "98765432101234567890",
"state": "pending",
"email": "jane.doe@example.com"
},
"owner": {
"id": "11111111111111111111",
"account_type": "human",
"email_address": "admin@example.com",
"first_name": "Admin",
"last_name": "User",
"profile_pic": "https://assets.fast.io/..."
},
"org": {
"id": "22222222222222222222",
"name": "Example Org"
}
},
"current_api_version": "1.0"
}
Response Fields
| Field | Type | Description |
|---|---|---|
| response.invitation | object | Invitation resource. |
| response.owner | object | User resource of the profile owner. |
| response.org | object or null | Org resource if the invitation is for an org-owned entity. |
Error Responses
| Error Code | HTTP Status | Message | Cause |
|---|---|---|---|
1605 (Invalid Input) (1605) | 400 | "An invalid invitation ID was supplied." | Invalid ID format |
1605 (Invalid Input) (1605) | 400 | "Invitation not found." | Invitation does not exist |
1654 (Internal Error) (1600) | 500 | "Failed to load the invitation profile or its owner." | Profile or owner load failure |
GET /current/user/invitation/{invitation_id}/public/details/
Get public details for an invitation without authentication.
Auth: None (IP-throttled)
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| {invitation_id} | string | Yes | Invitation ID (numeric) or invitation key (alphanumeric). |
Request Example
curl -X GET "https://api.fast.io/current/user/invitation/12345678901234567890/public/details/"
Success Response (200 OK)
{
"result": "yes",
"response": {
"invitation": {
"id": "12345678901234567890",
"state": "pending"
},
"owner": {
"id": "11111111111111111111",
"account_type": "human",
"first_name": "Admin",
"last_name": "User"
},
"org": {
"id": "22222222222222222222",
"name": "Example Org"
}
},
"current_api_version": "1.0"
}
Notes
- Returns a more limited view than the authenticated version.
- If the profile or owner cannot be loaded,
ownerwill benull.
POST /current/user/invitations/acceptall/
Accept all pending invitations.
Auth: Required (JWT)
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| invitation_key | string | No | Optional invitation key. If the user's email is not validated, this key can identify invitations. |
Request Example
curl -X POST "https://api.fast.io/current/user/invitations/acceptall/" \
-H "Authorization: Bearer {jwt_token}"
Success Response (200 OK)
{
"result": "yes",
"current_api_version": "1.0"
}
Notes
- If email is validated, all pending invitations matching that email are accepted.
- If email is not validated, use
invitation_keyto identify invitations.
GET /current/user/invitations/list/
List all pending invitations for the current user.
Auth: Required (JWT)
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| invitation_key | string | No | Optional invitation key for users without validated email. |
Request Example
curl -X GET "https://api.fast.io/current/user/invitations/list/" \
-H "Authorization: Bearer {jwt_token}"
Success Response (200 OK)
{
"result": "yes",
"response": {
"invitations": [
{
"id": "12345678901234567890",
"entity": "98765432101234567890",
"state": "pending",
"email": "jane.doe@example.com"
}
]
},
"current_api_version": "1.0"
}
Response Fields
| Field | Type | Description |
|---|---|---|
| response.invitations | array | Array of invitation resource objects. |
User Authentication Endpoints
GET /current/user/auth/
Authenticate via HTTP Basic Auth. Returns JWT token.
Auth: HTTP Basic Auth (email:password)
Query Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| expires | integer | No | Server default | Custom JWT expiration time in seconds. |
Request Example
curl -X GET "https://api.fast.io/current/user/auth/" \
-u "jane.doe@example.com:SecureP@ss123"
Success Response (200 OK)
{
"result": "yes",
"response": {
"expires_in": 86400,
"auth_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"2factor": false
},
"current_api_version": "1.0"
}
Response Fields
| Field | Type | Description |
|---|---|---|
| response.expires_in | integer | JWT token expiration time in seconds. |
| response.auth_token | string | JWT access token. If 2FA is enabled, has twofactor scope (restricted). Otherwise has user scope (full access). |
| response.2factor | boolean | true if 2FA is enabled. Token has limited scope until 2FA verification is completed. |
Error Responses
| Error Code | HTTP Status | Message | Cause |
|---|---|---|---|
1651 (Invalid Request Type) (1651) | 400 | "The expires time specified is invalid." | Invalid expires parameter |
1650 (Authentication Invalid) (1650) | 401 | "Your credentials were not supplied or invalid." | Missing Basic Auth header |
1650 (Authentication Invalid) (1650) | 401 | "Username is not valid." | Invalid email format |
1650 (Authentication Invalid) (1650) | 401 | "Password is not valid." | Invalid password format |
1653 (User Not Found) (1653) | 404 | "The username was not found." | Email not registered |
1650 (Authentication Invalid) (1650) | 401 | "The password on this account is not set, use SSO." | SSO-only account (no password set) |
1650 (Authentication Invalid) (1650) | 401 | "Your credentials supplied are invalid." | Wrong password |
1650 (Authentication Invalid) (1650) | 401 | "Your account is suspended..." | Account suspended |
1650 (Authentication Invalid) (1650) | 401 | "Your account is locked..." | Account locked |
1650 (Authentication Invalid) (1650) | 401 | "Your account is suspended due to abuse." | Account flagged for abuse |
1650 (Authentication Invalid) (1650) | 401 | "Your account is closed by you." | Account closed |
Notes
- Email tags (e.g.,
user+tag@example.com) are stripped before lookup. - If 2FA is enabled, complete the 2FA verification flow to upgrade the token.
- SSO-only accounts cannot use this endpoint.
GET /current/user/auth/check/
Validate current JWT and get user ID.
Auth: Required (Bearer token)
Request Example
curl -X GET "https://api.fast.io/current/user/auth/check/" \
-H "Authorization: Bearer {jwt_token}"
Success Response (200 OK)
{
"result": "yes",
"response": {
"id": "12345678901234567890"
},
"current_api_version": "1.0"
}
Response Fields
| Field | Type | Description |
|---|---|---|
| response.id | string | The 19-digit numeric user ID. |
Notes
- Lightweight health-check for token validity. Only validates that the JWT is structurally valid and not expired.
GET /current/auth/scopes/
Token scope introspection. Returns information about the current token's scope, auth type, and agent status.
Auth: Required (Bearer token)
Request Example
curl -X GET "https://api.fast.io/current/auth/scopes/" \
-H "Authorization: Bearer {jwt_token}"
Success Response (200 OK)
{
"result": "yes",
"response": {
"auth_type": "jwt_v2",
"scopes": ["org:12345:rw", "org:67890:rw"],
"scopes_detail": [],
"is_agent": true,
"agent_name": "My MCP Agent",
"full_access": false
},
"current_api_version": "1.0"
}
Response Fields
| Field | Type | Description |
|---|---|---|
| response.auth_type | string | Token type: "jwt_v2" (scoped JWT), "jwt_v1" (legacy JWT), "api_key" (unscoped API key), or "api_key_scoped" (API key with scopes). |
| response.scopes | array | Array of scope strings in entity_type:entity_id:access_mode format. Empty for v1 JWTs and unscoped API keys. Populated for scoped API keys. |
| response.scopes_detail | array | Hydrated scope details with entity information. Empty when scopes are empty. |
| response.is_agent | boolean | Whether the token represents an agent. |
| response.agent_name | string or null | Agent display name. null if not set or not an agent. |
| response.full_access | boolean | Whether the token has unrestricted access. true for v1 JWTs and unscoped API keys. false for scoped API keys. |
API Keys
POST /current/user/auth/key/
Create a new API key.
Auth: Required (JWT, scope: user or admin)
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| memo | string | No | Label/description for the key. |
| scopes | string | No | JSON array of scope strings (e.g., ["org:123:rw", "workspace:456:r"]). Omit or null for full access. |
| agent_name | string | No | Agent or application name for tracking. Max 128 characters. |
| expires | string | No | Expiration datetime (YYYY-MM-DD HH:MM:SS or ISO 8601 e.g., 2026-12-31T23:59:59Z). Omit or null for no expiration. |
Request Example
curl -X POST "https://api.fast.io/current/user/auth/key/" \
-H "Authorization: Bearer {jwt_token}" \
-d "memo=CI/CD Pipeline Key"
Request Example (scoped key with expiration)
curl -X POST "https://api.fast.io/current/user/auth/key/" \
-H "Authorization: Bearer {jwt_token}" \
-d "memo=Workspace Agent" \
-d 'scopes=["workspace:1234567890123456789:rw"]' \
-d "agent_name=my-agent" \
-d "expires=2026-12-31T23:59:59Z"
Success Response (200 OK)
{
"result": "yes",
"response": {
"api_key": "abcdefghij1234567890abcdefghij12"
},
"current_api_version": "1.0"
}
Response Fields
| Field | Type | Description |
|---|---|---|
| response.api_key | string | The newly created API key. Only shown once — store it securely. |
Error Responses
| Error Code | HTTP Status | Message | Cause |
|---|---|---|---|
1650 (Authentication Invalid) (1650) | 401 | "Your credentials were not supplied or invalid." | Missing or invalid JWT |
1650 (Authentication Invalid) (1650) | 401 | "The scope of your credentials are not sufficient." | JWT scope not user or admin |
1667 (Max Limit) (1667) | 429 | "You are at the maximum number of API keys, {max}." | Maximum key limit reached |
1605 (Invalid Input) (1605) | 400 | "You provided an invalid Memo." | Invalid memo format |
Notes
- 2FA verification is required if 2FA is enabled.
- The full key value is only returned at creation time. Subsequent reads return masked versions.
GET /current/user/auth/key/{key_id}/
Get details of an API key (key value is masked).
Auth: Required (JWT, scope: user or admin)
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| {key_id} | string | Yes | The API key's unique identifier. |
Request Example
curl -X GET "https://api.fast.io/current/user/auth/key/key_12345/" \
-H "Authorization: Bearer {jwt_token}"
Success Response (200 OK)
{
"result": "yes",
"response": {
"api_key": {
"id": "key_12345",
"api_key": "****************************ab12",
"memo": "CI/CD Pipeline Key",
"created": "2024-01-15 10:30:00 UTC",
"scopes": "[\"workspace:1234567890123456789:rw\"]",
"agent_name": "my-agent",
"expires": "2026-12-31 23:59:59 UTC"
}
},
"current_api_version": "1.0"
}
Response Fields
| Field | Type | Description |
|---|---|---|
| response.api_key.id | string | Unique key identifier. |
| response.api_key.api_key | string | Masked API key (only last 4 characters visible). |
| response.api_key.memo | string | Key description/label. |
| response.api_key.created | string | Key creation timestamp in UTC. |
| response.api_key.scopes | string or null | JSON array of scope strings, or null for full access. |
| response.api_key.agent_name | string or null | Agent/application name, or null if not set. |
| response.api_key.expires | string or null | Expiration datetime with UTC suffix, or null for no expiration. |
Error Responses
| Error Code | HTTP Status | Message | Cause |
|---|---|---|---|
1605 (Invalid Input) (1605) | 400 | "You provided an invalid Token to get details of." | Invalid key ID format |
1609 (Not Found) (1609) | 404 | Key not found | Key does not exist |
POST /current/user/auth/key/{key_id}/
Update an existing API key's memo, scopes, agent_name, and/or expires.
Auth: Required (JWT, scope: user or admin)
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| {key_id} | string | Yes | The API key's unique identifier. |
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| memo | string | No | Updated label/description for the key. |
| scopes | string | No | JSON array of scope strings. Send empty string or "null" to clear (restore full access). |
| agent_name | string | No | Agent/application name. Send empty string or "null" to clear. Max 128 characters. |
| expires | string | No | Expiration datetime (YYYY-MM-DD HH:MM:SS or ISO 8601). Send empty string or "null" to clear (no expiration). |
Request Example
curl -X POST "https://api.fast.io/current/user/auth/key/key_12345/" \
-H "Authorization: Bearer {jwt_token}" \
-d 'scopes=["org:1234567890123456789:r"]' \
-d "agent_name=updated-agent"
Success Response (200 OK)
{
"result": "yes",
"response": {
"api_key": {
"id": "key_12345",
"api_key": "****************************ab12",
"memo": "CI/CD Pipeline Key",
"created": "2024-01-15 10:30:00 UTC",
"scopes": "[\"org:1234567890123456789:r\"]",
"agent_name": "updated-agent",
"expires": null
}
},
"current_api_version": "1.0"
}
Error Responses
| Error Code | HTTP Status | Message | Cause |
|---|---|---|---|
1605 (Invalid Input) (1605) | 400 | "You provided an invalid Token to update." | Invalid key ID format |
1609 (Not Found) (1609) | 404 | Key not found | Key does not exist or belongs to another user |
1605 (Invalid Input) (1605) | 400 | Various | Invalid scopes, agent_name, or expires format |
Notes
- Only the fields you send are updated; omitted fields remain unchanged.
- Send empty string or
"null"to clear a nullable field. - 2FA verification is required if 2FA is enabled.
DELETE /current/user/auth/key/{key_id}/
Delete an API key.
Auth: Required (JWT, scope: user or admin)
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| {key_id} | string | Yes | The API key's unique identifier. |
Request Example
curl -X DELETE "https://api.fast.io/current/user/auth/key/key_12345/" \
-H "Authorization: Bearer {jwt_token}"
Success Response (200 OK)
{
"result": "yes",
"current_api_version": "1.0"
}
Error Responses
| Error Code | HTTP Status | Message | Cause |
|---|---|---|---|
1605 (Invalid Input) (1605) | 400 | "You provided an invalid Token to Delete." | Invalid key ID format |
1609 (Not Found) (1609) | 404 | "You provided a Token that was not found." | Key not found or belongs to another user |
1610 (Delete Failed) (1610) | 500 | "There was an error deleting the API Key." | Internal deletion failure |
Notes
- 2FA verification is required if 2FA is enabled.
- Returns "not found" if the key belongs to a different user (does not reveal ownership).
GET /current/user/auth/keys/
List all API keys for the user.
Auth: Required (JWT)
Request Example
curl -X GET "https://api.fast.io/current/user/auth/keys/" \
-H "Authorization: Bearer {jwt_token}"
Success Response (200 OK)
{
"result": "yes",
"response": {
"results": 2,
"api_keys": [
{
"id": "key_12345",
"api_key": "****************************ab12",
"memo": "CI/CD Pipeline Key",
"created": "2024-01-15 10:30:00 UTC",
"scopes": "[\"workspace:1234567890123456789:rw\"]",
"agent_name": "my-agent",
"expires": "2026-12-31 23:59:59 UTC"
},
{
"id": "key_67890",
"api_key": "****************************cd34",
"memo": "Backup Script",
"created": "2024-02-20 14:00:00 UTC",
"scopes": null,
"agent_name": null,
"expires": null
}
]
},
"current_api_version": "1.0"
}
No Keys Response (200 OK)
{
"result": "yes",
"response": {
"results": 0,
"api_keys": null
},
"current_api_version": "1.0"
}
Response Fields
| Field | Type | Description |
|---|---|---|
| response.results | integer | Number of API keys. |
| response.api_keys | array or null | Array of API key objects, or null if none exist. |
| response.api_keys[].id | string | Unique key identifier. |
| response.api_keys[].api_key | string | Masked API key (only last 4 characters visible). |
| response.api_keys[].memo | string | Key description/label. |
| response.api_keys[].created | string | Key creation timestamp in UTC. |
| response.api_keys[].scopes | string or null | JSON array of scope strings, or null for full access. |
| response.api_keys[].agent_name | string or null | Agent/application name, or null if not set. |
| response.api_keys[].expires | string or null | Expiration datetime with UTC suffix, or null for no expiration. |
Two-Factor Authentication (2FA)
GET /current/user/auth/2factor/
Get current 2FA status.
Auth: Required (JWT, scope: user or admin)
Request Example
curl -X GET "https://api.fast.io/current/user/auth/2factor/" \
-H "Authorization: Bearer {jwt_token}"
Success Response (200 OK)
{
"result": "yes",
"response": {
"state": "enabled",
"totp": false
},
"current_api_version": "1.0"
}
Response Fields
| Field | Type | Description |
|---|---|---|
| response.state | string | 2FA status: "enabled" (fully verified), "unverified" (added but not verified), or "disabled" (not configured). |
| response.totp | boolean | Whether the 2FA method is TOTP (Time-based One-Time Password). |
POST /current/user/auth/2factor/{channel}/
Enable 2FA on the account.
Auth: Required (JWT, scope: user or admin)
Path Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| {channel} | string | No | sms |
2FA delivery channel: sms, call, whatsapp, or totp. |
Request Example
curl -X POST "https://api.fast.io/current/user/auth/2factor/sms/" \
-H "Authorization: Bearer {jwt_token}"
Success Response for SMS/Voice/WhatsApp (202 Accepted)
{
"result": "yes",
"current_api_version": "1.0"
}
Success Response for TOTP (202 Accepted)
{
"result": "yes",
"response": {
"binding_uri": "otpauth://totp/fast.io:jane@example.com?secret=ABCDEF..."
},
"current_api_version": "1.0"
}
Response Fields (TOTP only)
| Field | Type | Description |
|---|---|---|
| response.binding_uri | string | TOTP provisioning URI for QR code display. |
Error Responses
| Error Code | HTTP Status | Message | Cause |
|---|---|---|---|
1669 (Already Exists) (1607) | 409 | "2Factor already added, please remove first." | 2FA already enabled |
1605 (Invalid Input) (1605) | 400 | "An invalid channel was supplied." | Invalid channel name |
1658 (Not Acceptable) (1606) | 406 | "2Factor cannot be added, you need a valid phone_number and phone_country..." | No phone number configured |
Notes
- User must have a valid phone number and country code on their account before enabling 2FA (for non-TOTP channels).
- After adding 2FA, it enters
unverifiedstate. Must complete verification viaPOST /current/user/auth/2factor/verify/{token}/. - For TOTP, display the
binding_urias a QR code for the user to scan.
POST /current/user/auth/2factor/verify/{token}/
Verify a 2FA setup code to confirm enrollment. Transitions 2FA from unverified to enabled state.
Auth: Required (JWT)
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| {token} | string | Yes | 2FA verification code (e.g., 6-digit code). |
Request Example
curl -X POST "https://api.fast.io/current/user/auth/2factor/verify/123456/" \
-H "Authorization: Bearer {jwt_token}"
Success Response (202 Accepted)
{
"result": "yes",
"current_api_version": "1.0"
}
Verification Failed (406 Not Accepted)
{
"result": "no",
"current_api_version": "1.0"
}
Error Responses
| Error Code | HTTP Status | Message | Cause |
|---|---|---|---|
1605 (Invalid Input) (1605) | 400 | "An invalid token was supplied to validate." | Invalid token format |
1658 (Not Acceptable) (1606) | 406 | "2Factor is not enabled." | 2FA not configured |
Notes
- If 2FA is already in the
enabledstate, returns success without modification. - This is the final step of the 2FA setup flow.
POST /current/user/auth/2factor/auth/{token}/
Authenticate with a 2FA code. Upgrades a limited-scope JWT to a full-scope JWT.
Auth: Required (JWT, scope: user, twofactor, or admin)
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| {token} | string | Yes | Valid 2FA verification code (e.g., 6-digit TOTP or SMS code). |
Request Example
curl -X POST "https://api.fast.io/current/user/auth/2factor/auth/123456/" \
-H "Authorization: Bearer {twofactor_jwt_token}"
Success Response (200 OK)
{
"result": "yes",
"response": {
"expires_in": 86400,
"auth_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
},
"current_api_version": "1.0"
}
Response Fields
| Field | Type | Description |
|---|---|---|
| response.expires_in | integer | JWT expiration time in seconds. |
| response.auth_token | string | New JWT with full user scope. |
Error Responses
| Error Code | HTTP Status | Message | Cause |
|---|---|---|---|
1605 (Invalid Input) (1605) | 400 | "An invalid token was supplied to authenticate." | Invalid token format |
1658 (Not Acceptable) (1606) | 406 | "2Factor is not enabled on this account." | 2FA not enabled |
1658 (Not Acceptable) (1606) | 406 | "The supplied token failed to authenticate." | Wrong 2FA code |
1650 (Authentication Invalid) (1650) | 401 | "Internal Error." | JWT creation failure |
DELETE /current/user/auth/2factor/{token}/
Disable (remove) 2FA from the account.
Auth: Required (JWT, scope: user or admin)
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| {token} | string | Yes | Valid 2FA verification code. Required only if 2FA is in enabled (verified) state. |
Request Example
curl -X DELETE "https://api.fast.io/current/user/auth/2factor/123456/" \
-H "Authorization: Bearer {jwt_token}"
Success Response (200 OK)
{
"result": "yes",
"current_api_version": "1.0"
}
Error Responses
| Error Code | HTTP Status | Message | Cause |
|---|---|---|---|
1605 (Invalid Input) (1605) | 400 | "An invalid token was supplied, valid token required to remove 2Factor." | Invalid token format |
1658 (Not Acceptable) (1606) | 406 | "The supplied token failed to authenticate." | Token verification failed |
1654 (Internal Error) (1600) | 500 | "2Factor could not be removed, please contact support." | Internal removal failure |
Notes
- If 2FA is
enabled(verified), a valid 2FA code is required to remove it. - If 2FA is
unverified, it can be removed without a code. - If 2FA is already disabled, returns success.
2FA Code Delivery Endpoints
Request a 2FA code via different channels. All require auth (accepts user, twofactor, or admin JWT scope).
/current/user/auth/2factor/send/sms/
Send code via SMS
/current/user/auth/2factor/send/call/
Send code via voice call
/current/user/auth/2factor/send/whatsapp/
Send code via WhatsApp
Success Response (202 Accepted)
{
"result": "yes",
"current_api_version": "1.0"
}
Failure Response (406 Not Accepted)
{
"result": "no",
"current_api_version": "1.0"
}
Error Responses
| Error Code | HTTP Status | Message | Cause |
|---|---|---|---|
1650 (Authentication Invalid) (1650) | 401 | "Your credentials were not supplied or invalid." | Invalid JWT |
1650 (Authentication Invalid) (1650) | 401 | "The scope of your credentials are not sufficient." | Wrong JWT scope |
1658 (Not Acceptable) (1606) | 406 | "2Factor is not enabled." | 2FA not configured on account |
Notes
- 2FA must be enabled (or in unverified state) for codes to be sent.
- Returns
result: "no"if the code send fails (e.g., invalid phone number).
Complete 2FA Flows
Complete 2FA Login Flow
1. GET /current/user/auth/
- Send email:password via HTTP Basic Auth
- Response includes "2factor": true and limited-scope auth_token
2. GET /current/user/auth/2factor/send/sms/ (or /call/ or /whatsapp/)
- Request a fresh 2FA code
- Uses the limited-scope (twofactor) JWT
3. POST /current/user/auth/2factor/auth/{code}/
- Submit the 2FA code
- Receive a new JWT with full "user" scope
- Use this token for all subsequent requests
Complete 2FA Setup Flow
1. POST /current/user/auth/2factor/{channel}/
- Choose channel: sms, call, whatsapp, or totp
- Phone number must be configured on account (for non-TOTP)
- State becomes "unverified"
- For TOTP: receive binding_uri for QR code
2. Receive code via selected channel (or scan QR code for TOTP)
3. POST /current/user/auth/2factor/verify/{code}/
- Submit the verification code
- State becomes "enabled"
- 2FA is now active on the account
Complete 2FA Removal Flow
1. DELETE /current/user/auth/2factor/{code}/
- Must provide valid 2FA code if state is "enabled"
- Can remove without code if state is "unverified"
- 2FA is fully removed from the account
User Search
GET /current/users/search/
Search for users by name or email across contacts and the platform user directory.
Auth: Required (JWT)
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| search | string | Yes | Search term. Matches against user names and email addresses. Must not be blank. |
Request Example
curl -X GET "https://api.fast.io/current/users/search/?search=john" \
-H "Authorization: Bearer {jwt_token}"
Success Response (200 OK)
{
"result": "yes",
"response": {
"contacts": {
"john.doe@example.com": "John Doe",
"jane.johnson@example.com": "Jane Johnson"
}
},
"current_api_version": "1.0"
}
Response Fields
| Field | Type | Description |
|---|---|---|
| response.contacts | object | Map of email address (key) to display name (value) for each matched user. |
Error Responses
| Error Code | HTTP Status | Message | Cause |
|---|---|---|---|
1650 (Authentication Invalid) (1650) | 401 | "Authentication required" | Missing or invalid JWT token |
1605 (Invalid Input) (1605) | 400 | "This value should not be blank." | Missing or empty search parameter |
1654 (Internal Error) (1600) | 500 | "Internal error" | Service temporarily unavailable |
Notes
- Searches across two sources: the user's contacts and the platform user directory. Results are merged and deduplicated by email.
- Response format is a flat key-value map (email → name), not an array of objects.
Response Envelope
Success
{"result": "yes", "current_api_version": "1.0", ...}
Error
{
"result": "no",
"error": {
"code": 195654,
"text": "Human-readable message",
"documentation_url": "https://api.fast.io/llms.txt",
"resource": "POST /current/user/"
}
}
Common Error Codes
| Code | Description | HTTP Status |
|---|---|---|
1600 | Internal Error | 500 Internal Server Error |
1605 | Invalid Input | 400 Bad Request |
1606 | Not Acceptable | 406 Not Acceptable |
1607 | Duplicate Entry / Already Exists | 409 Conflict |
1608 | Conflict | 409 Conflict |
1609 | Not Found / Resource Missing | 404 Not Found |
1610 | Delete Failed | 500 Internal Server Error |
1650 | Authentication Invalid | 401 Unauthorized |
1651 | Invalid Request Type | 400 Bad Request |
1653 | User Not Found | 404 Not Found |
1671 | Rate Limited | 429 Too Many Requests |
1680 | Access Denied | 403 Forbidden |
1681 | Restricted | 403 Forbidden |
1682 | Locked | 423 Locked |
1690 | SSO Auth Error | 401 Unauthorized |
Rate Limiting
Response headers: X-Rate-Limit-Available, X-Rate-Limit-Expiry, X-Rate-Limit-Max
When exceeded: HTTP 429 with error code 1671 (1671 (Rate Limited)).
ID Formats
- User IDs: 19-digit numeric string (e.g.,
"1234567890123456789") "me"can be used as user_id in user endpoints to reference the authenticated user- User endpoints also accept email address as an identifier
Token Types
| Type | Format | Lifetime | Use |
|---|---|---|---|
| JWT (Basic Auth) | RS256-signed JSON Web Token | Configurable (default varies) | General API access |
| JWT (OAuth) | RS256-signed JSON Web Token | 1 hour | OAuth-based API access |
| Refresh Token | Opaque string | 30 days | Obtaining new access tokens (OAuth only) |
| API Key | Alphanumeric string | Configurable (default: no expiry) | Service-to-service communication. Optionally scoped with permissions, agent name, and expiration. |
Security Best Practices
- Always use HTTPS for all API communication.
- Store refresh tokens and API keys securely (OS keychain, encrypted storage).
- Never log tokens in client-side logs or analytics.
- Rotate refresh tokens — always store the new token from a refresh response.
- Verify the
stateparameter in OAuth callbacks to prevent CSRF. - Handle 401 responses by attempting a token refresh; if refresh fails, re-authenticate.
- Revoke tokens on logout by calling the revoke endpoint and clearing local storage.