Organization Management Org CRUD, members, billing, discovery

Base URL: https://api.fast.io/current/ Auth: Bearer {jwt_token}

An organization (org) is a collector of workspaces. It can represent a company, a business unit, a team, or simply a personal collection. Orgs are the billable entity — storage, credits, and member limits are tracked at the org level. Every workspace and share lives under an org.

Profile IDs are 19-digit numeric strings. Most endpoints also accept the org's domain name (e.g., acme) in place of the numeric ID.


Internal vs External Orgs

Agents must call both GET /current/orgs/list/ and GET /current/orgs/list/external/ to discover all orgs they can access.

External orgs are the most common pattern when a human invites an agent to help with a specific project — they add the agent to a workspace but not to the org itself.

If the human later invites the agent to the org itself, it moves from external to internal and gains org-level access.


Org Field Constraints

FieldTypeMinMaxRegex / RulesDefault
domainstring263^[a-z0-9]([-a-z0-9]{0,61}[a-z0-9])?$ Lowercase alphanumeric + hyphens. Must be unique. Must not be reserved.Required
namestring3100Free text display namenull
descriptionstring101000Free textnull
industrystringMust be one of the values from GET /current/orgs/industries/null
perm_member_managestring'Member or above', 'Admin or above', 'Owner only''Member or above'
perm_authorized_domainsstringEmail domain for auto-join (e.g., acme.com)null
billing_emailstring (email)Valid email with reachable domainUser's email
accent_colorstring (JSON)JSON-encoded color objectnull
background_colorstring (JSON)JSON-encoded color objectnull
background_modestringOne of the supported background display modesnull

Member Roles and Permissions

RoleLevelCan manage membersCan manage settingsCan manage billingCan close orgCan transfer ownership
OwnerHighestYesYesYesYesYes
AdminHighYes (if perm_member_manage allows)YesYesNoNo
MemberStandardIf perm_member_manage = 'Member or above'NoNoNoNo
ViewLowestNoNoNoNoNo

The perm_member_manage org setting controls the minimum role required to add, remove, or update members.


Compact Responses (output=)

Every endpoint that returns one or more org objects (details, list, discovery) 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 406. When output= is omitted, responses are full and byte-for-byte unchanged.

LevelFields returned on each org (cumulative)
terseid, domain, name, logo
standardterse + description, plan, user_permission (member-only), user_status, member, closed (member-only), locked (member-only), suspended (member-only), created (member-only), updated (member-only), parent (member-only), capabilities, accent_color, background_mode, background, use_background, background_color, homepage, subscriber (member-only), subscriber_cancel (admin-only), subscriber_trial_until (member-only), payment_state (member-only), payment_failed_at (member-only), access_ends_at (member-only)
fullstandard + subscriber_trial_credits, billing_email, social links (facebook, instagram, twitter, youtube), encryption_key, perm_* blocks (including perm_auth_domains, perm_member_manage), dmca, owner_defined, platform, storage

Use terse for org switchers and billing-entity pickers — it includes the ID, URL domain, display name, and logo so the org-switcher sidebar can render entries without falling back to initials. Use standard for org list views, most member-facing dashboards, and branding-aware surfaces — it adds plan, description, lifecycle flags (including the locked and suspended lifecycle/billing chips, emitted to members only), the caller's permission and status, timestamps, hierarchy pointer, plan-gated capabilities, the visual-identity bundle (accent color, background, homepage), and the subscription state fields (subscriber, subscriber_cancel, subscriber_trial_until, payment_state, payment_failed_at, access_ends_at) that list-view subscription chips render. Note that subscriber reports entitlement, not payment health — it stays true while a payment is failing — so read payment_state when you need to know whether billing is actually current. Use full (or omit the parameter) for the org settings screen, billing portal, auth-domain configuration, and any workflow that reads remaining trial credit, permission blocks, social links, or encryption metadata. 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.


Organization CRUD

Create Organization

POST /current/org/create/

Auth required. Creates a new organization. The authenticated user becomes the owner.

Request parameters

NameTypeRequiredDescription
domainstringYes2-63 chars, lowercase alphanumeric + hyphens, must be unique and not reserved. Used as the org identifier in URLs.
namestringNo3-100 chars. Display name for the org.
descriptionstringNoOrganization description.
industrystringNoIndustry type from predefined list (see GET /current/orgs/industries/).
accent_colorstring (JSON)NoBrand accent color as JSON.
background_colorstring (JSON)NoBackground color as JSON.
background_modestringNoBackground display mode.
facebook_urlstring (URL)NoFacebook page URL. Must be valid URL.
twitter_urlstring (URL)NoTwitter profile URL. Must be valid URL.
instagram_urlstring (URL)NoInstagram profile URL. Must be valid URL.
youtube_urlstring (URL)NoYouTube channel URL. Must be valid URL.
homepage_urlstring (URL)NoOrganization website URL. Must be valid URL.
perm_member_managestringNoWho can manage members. See Org Field Constraints above.
perm_authorized_domainsstringNoAuthorized email domain for auto-join.
billing_emailstring (email)NoBilling contact email. Defaults to user's email.

A newly created organization must select a paid plan (Starter, Business, or Growth) before it can be used; until then it is in an upgrade-only state — the same state as an org that has exhausted its credits. This applies to agent and human accounts alike: agent accounts are ordinary accounts tagged account_type=agent and follow the same paid-plan flow. The legacy free plan is no longer available for new organizations.

New orgs on a monthly plan begin a 14-day free trial. Annual plans have no trial — an annual subscription is billed for the full term at signup. A free trial is only available on a user's first organization — if the user owns, or has ever owned, any other organization (including one they later closed), no free trial is offered on a later org, no matter how much time has passed. A secondary per-user cooldown (60 days on modern plans, anchored at trial start) also applies on top of this, though in practice it only matters on the first org itself. When blocked by either rule, a new org subscribes with immediate payment instead of a trial.

Read the trial length from the plan itself rather than assuming 14: each plan reports free_days in its pricing, and free_days: 0 means the plan has no trial and payment is due at checkout.

curl example

curl -X POST "https://api.fast.io/current/org/create/" \
  -H "Authorization: Bearer {jwt_token}" \
  -d "domain=acme-corp" \
  -d "name=Acme Corporation" \
  -d "industry=technology"

Response (200 OK) — free trial available

{
  "result": true,
  "org": {
    "id": "1234567890123456789",
    "domain": "acme-corp",
    "name": "Acme Corporation",
    "description": null,
    "logo": null,
    "accent_color": null,
    "closed": false,
    "suspended": false
  },
  "has_free_trial": true,
  "requires_payment": true,
  "is_agent": false
}

When the owner already owns, or has ever owned, another organization, no free trial is offered — the org still subscribes but bills immediately. This is the primary block and is permanent, so there is no trial_available_at:

{
  "result": true,
  "org": { "...": "..." },
  "has_free_trial": false,
  "requires_payment": true,
  "is_agent": false,
  "no_trial_reason": "Free trials are only available on your first organization."
}

A free trial can also be blocked by the secondary per-user cooldown (60 days on modern plans, anchored at trial start) — in practice this only matters on the first org itself. That case does include trial_available_at:

{
  "result": true,
  "org": { "...": "..." },
  "has_free_trial": false,
  "requires_payment": true,
  "is_agent": false,
  "no_trial_reason": "A free trial was started recently. A new free trial is available in 43 days.",
  "trial_available_at": "2026-08-05 12:00:00 UTC"
}

requires_payment is always true for new orgs (the legacy free tier is closed). Until a paid plan is selected the org is in an upgrade-only state (gated endpoints return 402). This applies to all accounts, including agent accounts (is_agent: true).

Response fields

FieldTypeDescription
orgobjectOrganization resource object
org.idstring19-digit numeric organization ID
org.domainstringURL-safe subdomain
org.namestring/nullDisplay name
org.descriptionstring/nullDescription
org.logostring/nullLogo asset URL
org.accent_colorstring/nullBrand color
org.closedbooleanWhether org is closed
org.suspendedbooleanWhether org is suspended
has_free_trialbooleanWhether a free trial is available at checkout. true only when this is the owner's first organization and the per-user cooldown has also cleared; false for any org after the first (permanent), or when within the per-user cooldown.
requires_paymentbooleanWhether a paid plan is required before the org can be used. true for new orgs.
is_agentbooleanWhether the creating user is an agent account
no_trial_reasonstringHuman-readable reason a free trial is unavailable (only when has_free_trial is false)
trial_available_atstringUTC timestamp (Y-m-d H:i:s UTC) when the user's next free trial becomes available. Present only when blocked by the per-user cooldown — absent when blocked by the first-organization rule, since that block is permanent and has no future date.

Error responses

Reading the error tables: the four-digit 16xx/17xx values below are HTTP-status classes, not error.code. The error.code a client actually receives is assigned per endpoint, so use the HTTP status as the gate and a documented error.code — five or six digits, plus the 9661-9669 family — only as a refinement. A 16xx value identifies the status class — useful for telling which kind of failure occurred — but comparing one against error.code will never match. Five- and six-digit codes (and the 9661-9669 family) are real error.code values. If you widen a check from a specific code to a status, widen what you assert with it — a status covers failures the narrower code did not, so a message written for that one code becomes a confident falsehood on the rest.

Error CodeHTTP StatusMessageCause
1605 (Invalid Input)406"An invalid org domain was supplied."Invalid domain format
1605 (Invalid Input)406"The supplied org domain name is restricted."Domain is reserved
1605 (Invalid Input)406"The supplied org domain name is already in use."Domain already taken
1605 (Invalid Input)406"An invalid configuration was supplied..."Metadata validation failed
1605 (Invalid Input)406"Invalid JSON provided for {key}."Malformed JSON in color fields
1663 (Update Failed)500"There was an internal error processing your create request."Org creation failed
1654 (Internal Error)500"There was an internal error processing your create request."Internal error during creation
1654 (Internal Error)500"We were unable to create your organization..."Internal error
1680 (Access Denied)401GEO/risk restriction messageRequest blocked by geo/risk check

Get Org Details

GET /current/org/{org_id}/details/

Auth required. Returns full org details. Fields vary by the requesting user's permission level.

{org_id} accepts a 19-digit numeric ID or the org's domain name.

Access levels

RoleAccessNotes
OwnerFull accessFull access to all organization settings and security configuration
AdminExtended accessIncludes billing info, permissions, subscriber status, credit balance
MemberStandard accessBasic org info, plan, subscriber status (boolean only — no credit balance)
ViewLimited accessPublic fields only

curl example

curl -X GET "https://api.fast.io/current/org/1234567890123456789/details/" \
  -H "Authorization: Bearer {jwt_token}"

Response (200 OK)

{
  "result": true,
  "org": {
    "id": "1234567890123456789",
    "domain": "acme-corp",
    "name": "Acme Corporation",
    "description": "Leading provider of innovation",
    "logo": "https://assets.fast.io/org/logo.png",
    "accent_color": "#0066CC",
    "closed": false,
    "locked": false,
    "suspended": false,
    "created": "2024-01-15 10:30:00 UTC",
    "updated": "2024-06-20 14:45:00 UTC"
  }
}

Response fields

FieldTypeDescription
org.idstring19-digit numeric organization ID
org.domainstringURL-safe subdomain
org.namestring/nullDisplay name
org.descriptionstring/nullDescription
org.logostring/nullLogo asset URL
org.accent_colorstring/nullBrand color
org.closedbooleanWhether org is closed
org.lockedbooleanWhether org is locked
org.suspendedbooleanWhether org is suspended
org.createdstringCreation timestamp
org.updatedstringLast update timestamp
org.planstringBilling plan identifier (e.g., "solo_monthly", "business_v2_monthly", "growth_monthly"). Member+ only.
org.subscriberbooleanWhether the org has an active subscription (includes credit availability for free-tier orgs). Member+ only.
org.subscriber_trial_untilinteger/nullUnix timestamp when the trial period ends. null for paid plans or if no trial. Member+ only.
org.payment_statestringPayment health: current, past_due, or unpaid. unpaid is terminal — retries have stopped. Read this rather than subscriber to tell whether billing is current: subscriber stays true throughout a failed payment. Unrecognised states report current. Member+ only.
org.payment_failed_atstring/nullStart of the billing period whose payment failed, Y-m-d H:i:s UTC. null unless payment_state is past_due or unpaid. Read this as “the period that is unpaid”, NOT as the instant the charge was declined. For a cycle-renewal failure the two coincide, because a past-due subscription's period does not advance while its invoice is unpaid. For a failure on a mid-cycle invoice — an immediate upgrade charge, for instance — the period start can be materially earlier than the failure. Do not render it as “payment failed on {date}”. A formatted string, not a Unix timestamp — unlike subscriber_trial_until above. Member+ only.
org.access_ends_atnullReserved. Always null: the date access ends is not currently knowable, so none is reported rather than an estimate. Member+ only.
org.subscriber_trial_creditsinteger/nullCredits remaining in the current billing period. Admin+ only.

Error responses

Error CodeHTTP StatusMessageCause
1680 (Access Denied)401"You have not been granted access to this Org."Insufficient permission
1688 (Subscription Required)402"The organization does not have an active subscription."Org has no active subscription or free-tier credits exhausted
1696 (Credit Limit Exceeded)402"You have exceeded your credit limit."Free-tier credit limit exceeded

Get Public Org Details

GET /current/org/{org_id}/public/details/

No authentication required. Returns limited public info about an org (name, domain, assets). IP-rate-limited.

{org_id} accepts a 19-digit numeric ID or the org's domain name.

curl example

curl -X GET "https://api.fast.io/current/org/1234567890123456789/public/details/"

Response (200 OK)

{
  "result": true,
  "org": {
    "id": "1234567890123456789",
    "domain": "acme-corp",
    "name": "Acme Corporation",
    "description": "Leading provider of innovation",
    "logo": "https://assets.fast.io/org/logo.png",
    "accent_color": "#0066CC"
  }
}

Response fields

FieldTypeDescription
org.idstring19-digit numeric organization ID
org.domainstringURL-safe subdomain
org.namestring/nullDisplay name
org.descriptionstring/nullDescription
org.logostring/nullLogo asset URL
org.accent_colorstring/nullBrand color

Update Organization

POST /current/org/{org_id}/update/

Auth required. Admin or above. Updates org details. Only provided fields are modified.

Access levels

RoleAccess
OwnerFull access
AdminFull access
MemberDenied

Request parameters (all optional)

NameTypeDescription
domainstringNew URL-safe subdomain (2-63 chars, lowercase alphanumeric + hyphens).
namestringDisplay name (3-100 chars). Send "null" to clear.
descriptionstringDescription. Send "null" or "" to clear.
industrystringIndustry type from predefined list.
accent_colorstring (JSON)Brand accent color as JSON. Send "null" to clear.
background_colorstring (JSON)Background color as JSON. Send "null" to clear.
background_modestringBackground display mode.
use_backgroundstringEnable/disable background ("true"/"false").
facebook_urlstring (URL)Facebook URL.
twitter_urlstring (URL)Twitter URL.
instagram_urlstring (URL)Instagram URL.
youtube_urlstring (URL)YouTube URL.
homepage_urlstring (URL)Organization website URL.
perm_member_managestringMember management permission level.
perm_authorized_domainsstringAuthorized email domain for auto-join.
billing_emailstring (email)Billing contact email. Domain must be reachable.
owner_definedstring (JSON)Custom owner-defined properties. Send "null" or "" to clear.

curl example

curl -X POST "https://api.fast.io/current/org/1234567890123456789/update/" \
  -H "Authorization: Bearer {jwt_token}" \
  -d "name=Acme Corp Updated" \
  -d "description=Updated description" \
  -d "industry=technology"

Response (200 OK)

{
  "result": true
}

If no actual changes are detected, returns success immediately.

Error responses

Error CodeHTTP StatusMessageCause
1605 (Invalid Input)406"An invalid org domain was supplied."Invalid domain format
1605 (Invalid Input)406"The supplied org domain name is restricted."Domain is reserved
1605 (Invalid Input)406"The supplied org domain name is already in use."Domain taken by another org
1605 (Invalid Input)406"An invalid configuration was supplied..."Metadata validation failed
1605 (Invalid Input)406"Invalid JSON provided for {key}."Malformed JSON
1605 (Invalid Input)406"The email domain is invalid or cannot receive email."Bad billing email domain
1663 (Update Failed)500"There was an internal error processing your update request."Internal error

Close Organization

POST /current/org/{org_id}/close/

Auth required. Owner only. Soft-deletes the organization. Active subscriptions are automatically cancelled.

Request parameters

NameTypeRequiredDescription
confirmstringYesMust match the org domain name or org numeric ID as confirmation.

curl example

curl -X POST "https://api.fast.io/current/org/1234567890123456789/close/" \
  -H "Authorization: Bearer {jwt_token}" \
  -d "confirm=acme-corp"

This response shape is the same whether or not a free trial applies. Confirm the setup_intent.client_secret with the customer's card. No money moves at that moment — the SetupIntent is a $0 card capture that also performs any 3-D Secure authentication up front.

The card is inspected before any charge: a card refused by the funding rules does not produce a subscription, and the customer's billing address is taken from the card, so the first invoice is taxed correctly.

Response (202 Accepted)

{
  "result": true
}

Error responses

Error CodeHTTP StatusMessageCause
120445406"The confirm field is required. Pass the org's domain or numeric id as confirm."confirm was not provided
10549406"The confirm field provided does not match the org's domain or id."Confirmation does not match domain or ID
1663 (Update Failed)500"There was an internal error processing your request."Failed to close org

Storage deletion is deferred to the deletion system after a retention period.


Organization Assets

List Available Asset Types

GET /current/org/assets/

Auth required. Returns available org asset metadata types (e.g., logo, background images).

curl example

curl -X GET "https://api.fast.io/current/org/assets/" \
  -H "Authorization: Bearer {jwt_token}"

Response (200 OK)

{
  "result": true,
  "assets": [
    {
      "name": "logo",
      "mime_types": ["image/png", "image/jpeg"],
      "max_size": 5242880
    }
  ]
}

List Org Assets

GET /current/org/{org_id}/assets/

Auth required. Any member with at least View permission. Returns assets currently set on the org.

curl example

curl -X GET "https://api.fast.io/current/org/1234567890123456789/assets/" \
  -H "Authorization: Bearer {jwt_token}"

Response (200 OK)

{
  "result": true,
  "assets": {
    "logo": {
      "url": "https://assets.fast.io/org/logo.png",
      "mime_type": "image/png",
      "size": 45678
    }
  }
}

Upload Org Asset

POST /current/org/{org_id}/assets/{asset_name}/

Auth required. Admin or above. Upload as multipart/form-data.

Request parameters

NameTypeRequiredDescription
filefile (multipart)YesThe asset file to upload.
metadatastring (JSON array)NoAdditional metadata for the asset.

curl example

curl -X POST "https://api.fast.io/current/org/1234567890123456789/assets/logo/" \
  -H "Authorization: Bearer {jwt_token}" \
  -F "file=@logo.png"

Error responses

Error CodeHTTP StatusMessageCause
1691 (File Missing)412"Asset upload missing"No file in the request
1605 (Invalid Input)406"metadata invalid"Metadata is not a valid array

Delete Org Asset

DELETE /current/org/{org_id}/assets/{asset_name}/

Auth required. Admin or above.

curl example

curl -X DELETE "https://api.fast.io/current/org/1234567890123456789/assets/logo/" \
  -H "Authorization: Bearer {jwt_token}"

Response (200 OK)

{
  "result": true
}

Read Org Asset (Raw)

GET /current/org/{org_id}/assets/{asset_name}/read/

No authentication required. Returns the raw binary content of an org asset with appropriate Content-Type header. Useful for displaying logos and images directly.

HEAD requests return headers only.


Organization Members

Add or Invite a Member

POST /current/org/{org_id}/members/{email_or_user_id}/

Auth required. Permission governed by org's perm_member_manage setting.

The target is specified as a path parameter:

Request parameters (adding existing user by ID)

NameTypeRequiredDescription
permissionsstringYesPermission level: "member", "admin". Cannot add as "owner".
expiresstring (datetime)NoMembership expiration date.
notifystringNoNotification preference.
force_notificationbooleanNoForce send a notification email.

Request parameters (inviting by email)

NameTypeRequiredDescription
permissionsstringYesPermission level for the invitation: "member", "admin".
messagestringNoCustom invitation message.
expiresstring (datetime)NoInvitation expiration.

curl example (add existing user)

curl -X POST "https://api.fast.io/current/org/1234567890123456789/members/9876543210987654321/" \
  -H "Authorization: Bearer {jwt_token}" \
  -d "permissions=member"

curl example (invite by email)

curl -X POST "https://api.fast.io/current/org/1234567890123456789/members/jane@example.com/" \
  -H "Authorization: Bearer {jwt_token}" \
  -d "permissions=member" \
  -d "message=Welcome to the team!"

Response (200 OK) — direct add

{
  "result": true
}

Response (200 OK) — invitation created

{
  "result": true,
  "invitation": {
    "id": "eA1B2C3D4E5F6G7H8J9K0L1M2N3O4",
    "invitee_email": "jane@example.com",
    "entity_type": "org",
    "state": "pending",
    "created": "2024-01-15 10:30:00 UTC"
  }
}

Error responses

Error CodeHTTP StatusMessageCause
1605 (Invalid Input)406"Invalid permission specified."Invalid permission value
1692 (Cannot Add As Owner)500"Adding a member as an owner is not allowed"Tried to add as owner (use transfer_ownership)
1656 (Limit Exceeded)413Limit messageMember limit exceeded

Remove a Member

DELETE /current/org/{org_id}/members/{user_id}/

Auth required. Permission governed by org's perm_member_manage setting.

The target user ID (19-digit numeric) is a path parameter.

curl example

curl -X DELETE "https://api.fast.io/current/org/1234567890123456789/members/9876543210987654321/" \
  -H "Authorization: Bearer {jwt_token}"

Response (200 OK)

{
  "result": true
}

List Org Members

GET /current/org/{org_id}/members/list/

Auth required. Any org member. Paginated.

Query parameters

NameTypeDefaultDescription
limitinteger1001-500, number of items to return
offsetinteger0Number of items to skip

curl example

curl -X GET "https://api.fast.io/current/org/1234567890123456789/members/list/?limit=50&offset=0" \
  -H "Authorization: Bearer {jwt_token}"

Response (200 OK)

{
  "result": true,
  "users": [
    {
      "id": "1234567890123456789",
      "account_type": "human",
      "email_address": "owner@example.com",
      "first_name": "John",
      "last_name": "Doe",
      "permissions": "owner"
    },
    {
      "id": "1234567890123456780",
      "account_type": "agent",
      "email_address": "bot@example.com",
      "first_name": "Service",
      "last_name": "Bot",
      "permissions": "admin"
    }
  ],
  "pagination": {
    "total": 2,
    "limit": 50,
    "offset": 0,
    "has_more": false
  }
}

Response fields

FieldTypeDescription
usersarrayArray of member objects
users[].idstring19-digit numeric user ID
users[].account_typestring"human" or "agent"
users[].email_addressstringUser's email
users[].first_namestringFirst name
users[].last_namestringLast name
users[].permissionsstringRole: "owner", "admin", "member"
pagination.totalintegerTotal number of members
pagination.limitintegerRequested page size
pagination.offsetintegerCurrent offset
pagination.has_morebooleanWhether more results exist

Leave Organization (Self)

DELETE /current/org/{org_id}/member/

Auth required. Removes the authenticated user from the org. Owners cannot leave — they must transfer ownership or close the org first.

curl example

curl -X DELETE "https://api.fast.io/current/org/1234567890123456789/member/" \
  -H "Authorization: Bearer {jwt_token}"

Response (200 OK)

{
  "result": true
}

Error responses

Error CodeHTTP StatusMessageCause
1605 (Invalid Input)406"You cannot leave an org you are the owner of..."User is the org owner
1605 (Invalid Input)406"You cannot leave an org you are not a member of."User is not a member

Get Member Details

GET /current/org/{org_id}/member/{user_id}/details/

Auth required. Any org member.

curl example

curl -X GET "https://api.fast.io/current/org/1234567890123456789/member/9876543210987654321/details/" \
  -H "Authorization: Bearer {jwt_token}"

Response (200 OK)

{
  "result": true,
  "member": {
    "id": "9876543210987654321",
    "account_type": "human",
    "email_address": "jane@example.com",
    "first_name": "Jane",
    "last_name": "Smith",
    "permissions": "admin",
    "invite": "accepted",
    "notify": "Email me",
    "expires": null,
    "member_added_at": "2026-08-29 15:48:29 UTC"
  }
}

Response fields

FieldTypeDescription
member.idstring19-digit numeric user ID
member.account_typestring"human" or "agent"
member.email_addressstringUser's email
member.first_namestringFirst name
member.last_namestringLast name
member.permissionsstringRole: "owner", "admin", "member"
member.invitestringInvitation status
member.notifystringNotification preference
member.expiresstring/nullMembership expiration (YYYY-MM-DD HH:MM:SS UTC) or null for no expiry
member.member_added_atstringWhen this membership was created (YYYY-MM-DD HH:MM:SS UTC). Absent — not null — when you are not allowed to see it: emitted only to the member themselves or to an admin-or-above of this org, so a peer member gets no key at all. Read it with a presence check on the key

Error responses

Error CodeHTTP StatusMessageCause
1605 (Invalid Input)406"The membership you specified does not exist."User is not a member

Update Member Permissions

POST /current/org/{org_id}/member/{user_id}/update/

Auth required. Permission governed by org's perm_member_manage setting.

Request parameters (all optional)

NameTypeDescription
permissionsstringNew permission level ("member", "admin")
expiresstring (datetime)Membership expiration date
notifystringNotification preference

curl example

curl -X POST "https://api.fast.io/current/org/1234567890123456789/member/9876543210987654321/update/" \
  -H "Authorization: Bearer {jwt_token}" \
  -d "permissions=admin"

Response (200 OK)

{
  "result": true
}

Error responses

Error CodeHTTP StatusMessageCause
1605 (Invalid Input)406"The membership you specified does not exist."User is not a member

Transfer Org Ownership

POST /current/org/{org_id}/member/{user_id}/transfer_ownership/

Auth required. Owner only. Transfers ownership of the org to the specified member. The current owner is demoted to admin.

curl example

curl -X POST "https://api.fast.io/current/org/1234567890123456789/member/9876543210987654321/transfer_ownership/" \
  -H "Authorization: Bearer {jwt_token}"

Response (200 OK)

{
  "result": true
}

Error responses

Error CodeHTTP StatusMessageCause
1605 (Invalid Input)406"You cannot transfer ownership to yourself."Target is the current user
1605 (Invalid Input)406"The membership you specified does not exist."User is not an org member
1605 (Invalid Input)406"Member is already an owner."Target is already the owner
1663 (Update Failed)500"Failed to update owner of the org."Internal error

Join Organization

POST /current/org/{org_id}/members/join/

Auth required. Join an org via invite or domain-based auto-join.

Join methods

  1. Via invitation: Append the invitation key to the URL path: .../join/{invitation_key}/ optionally followed by accept or decline. Default is accept.
  2. Via authorized domain: The org must have perm_authorized_domains set and the user's email domain must match. User is added as a Member.

curl example (invitation)

curl -X POST "https://api.fast.io/current/org/1234567890123456789/members/join/abc123def456/accept/" \
  -H "Authorization: Bearer {jwt_token}"

Response (200 OK)

{
  "result": true
}

Error responses

Error CodeHTTP StatusMessageCause
1680 (Access Denied)401"This org does not allow you to join automatically..."Domain auto-join not enabled
1680 (Access Denied)401"You are not allowed to join this org automatically..."User's email domain does not match
1656 (Limit Exceeded)413Limit messageMember limit exceeded

List Org Invitations

GET /current/org/{org_id}/members/invitations/list/

Auth required. Any org member. An optional state filter can be appended: .../list/pending/.

curl example

curl -X GET "https://api.fast.io/current/org/1234567890123456789/members/invitations/list/" \
  -H "Authorization: Bearer {jwt_token}"

Response (200 OK)

{
  "result": true,
  "invitations": [
    {
      "id": "eA1B2C3D4E5F6G7H8J9K0L1M2N3O4",
      "inviter": "John Doe",
      "invitee_email": "jane@example.com",
      "entity_type": "org",
      "state": "pending",
      "created": "2024-01-15 10:30:00 UTC",
      "expires": "2024-02-15 10:30:00 UTC"
    }
  ]
}

Response fields

FieldTypeDescription
invitationsarrayArray of invitation objects
invitations[].idstringInvitation identifier
invitations[].inviterstringName of the user who sent the invitation
invitations[].invitee_emailstringEmail address of the invitee
invitations[].entity_typestringAlways "org" for org invitations
invitations[].statestringInvitation state: "pending", "accepted", "declined"
invitations[].createdstringCreation timestamp
invitations[].expiresstring/nullExpiration timestamp

Error responses

Error CodeHTTP StatusMessageCause
1605 (Invalid Input)406"An invalid invitation state was supplied."Invalid state filter

Update an Invitation

POST /current/org/{org_id}/members/invitation/{invitation_id}/

Auth required. Permission governed by org's perm_member_manage setting.

{invitation_id} can be the invitation ID or the invitee email address.

Request parameters (all optional)

NameTypeDescription
statestringNew invitation state
permissionsstringUpdated permission level
expiresstring (datetime)Updated expiration date

curl example

curl -X POST "https://api.fast.io/current/org/1234567890123456789/members/invitation/eA1B2C3D4E5F6G7H8J9K0L1M2N3O4/" \
  -H "Authorization: Bearer {jwt_token}" \
  -d "permissions=admin"

Response (200 OK)

{
  "result": true
}

Error responses

Error CodeHTTP StatusMessageCause
1605 (Invalid Input)406"An invalid invitation ID or email was supplied."Invalid identifier
1605 (Invalid Input)406"Invitation not found."Invitation does not exist
1605 (Invalid Input)406"Invitation is not for an Org"Wrong entity type
1605 (Invalid Input)406"An invalid state was supplied."Invalid state value
1679 (Update Failed)500"Failed to update invitation."Internal error

Delete an Invitation

DELETE /current/org/{org_id}/members/invitation/{invitation_id}/

Auth required. Permission governed by org's perm_member_manage setting.

{invitation_id} can be the invitation ID or the invitee email address.

curl example

curl -X DELETE "https://api.fast.io/current/org/1234567890123456789/members/invitation/eA1B2C3D4E5F6G7H8J9K0L1M2N3O4/" \
  -H "Authorization: Bearer {jwt_token}"

Response (200 OK)

{
  "result": true
}

Error responses

Error CodeHTTP StatusMessageCause
1666 (Delete Failed)500"Failed to delete invitation."Deletion failed

Ownership Transfer / Claim (Agent-Owned Orgs)

When an agent account creates and owns an org, this flow lets a human user take over ownership. The agent (owner of an org on the agent plan) mints a short-lived transfer token, shares it with the human, and the human claims the org with that token. On a successful claim the human becomes the new owner of the org and every workspace under it, and the agent that created the org is demoted to admin (on the org and on each workspace). The org is also moved off the agent plan onto the transfer target plan.

This is distinct from Transfer Org Ownership (above), which reassigns ownership between two existing members of the same org. The token flow crosses the agent-to-human boundary and does not require the human to already be a member.

Token lifecycle: tokens are pending when created, become claimed once a human claims them, expired after their expiry (default 72 hours from creation), or deleted if revoked. Only a pending, non-expired token is claimable. An org may have at most 5 active (pending) tokens at a time. The raw 64-character token secret is returned by the create, list, and revoke endpoints (so the agent can share it); the public details endpoint deliberately omits it.

Create Transfer Token

POST /current/org/{org_id}/transfer/token/create/

Auth required. Owner only, and the caller must be an agent account whose org is on the agent plan. Mints a new pending transfer token.

curl example

curl -X POST "https://api.fast.io/current/org/1234567890123456789/transfer/token/create/" \
  -H "Authorization: Bearer {jwt_token}"

Response (200 OK)

{
  "result": true,
  "transfer_token": {
    "id": "eA1B2C3D4E5F6G7H8J9K0L1M2N3O4",
    "token": "a1b2c3d4e5f6...{64-char secret}",
    "org_id": 1234567890123456789,
    "state": "pending",
    "expires": "2026-07-10 16:37:29 UTC",
    "created": "2026-07-07 16:37:29 UTC"
  }
}

Response fields

FieldTypeDescription
transfer_token.idstringToken identifier (opaque). Use this to revoke the token.
transfer_token.tokenstringThe 64-character secret. Share this with the human who will claim the org.
transfer_token.org_idintegerNumeric org ID the token transfers
transfer_token.statestring"pending", "claimed", "expired", or "deleted"
transfer_token.expiresstringExpiration timestamp (YYYY-MM-DD HH:MM:SS UTC)
transfer_token.createdstringCreation timestamp (YYYY-MM-DD HH:MM:SS UTC)

Error responses

Error CodeHTTP StatusMessageCause
1680 (Access Denied)401"Only agent accounts can create org transfer tokens."Caller is not an agent account
1680 (Access Denied)401"Transfer tokens can only be created for organizations on the agent plan."Org is not on the agent plan
1658 (Not Acceptable)406"Maximum of 5 active transfer tokens per organization. Delete existing tokens first."Active token limit reached
1654 (Internal Error)500"Failed to create transfer token."Token generation or storage failed

List Transfer Tokens

GET /current/org/{org_id}/transfer/token/list/

Auth required. Owner only, and the caller must be an agent account. Lists the org's active (pending) transfer tokens so an agent can track outstanding handoff requests.

curl example

curl -X GET "https://api.fast.io/current/org/1234567890123456789/transfer/token/list/" \
  -H "Authorization: Bearer {jwt_token}"

Response (200 OK)

{
  "result": true,
  "results": 1,
  "transfer_tokens": [
    {
      "id": "eA1B2C3D4E5F6G7H8J9K0L1M2N3O4",
      "token": "a1b2c3d4e5f6...{64-char secret}",
      "org_id": 1234567890123456789,
      "state": "pending",
      "expires": "2026-07-10 16:37:29 UTC",
      "created": "2026-07-07 16:37:29 UTC"
    }
  ]
}

Each token object has the same shape as Create Transfer Token; results is the number of active tokens returned. Returns 1680 (Access Denied) (401) if the caller is not an agent account.


Revoke Transfer Token

DELETE /current/org/{org_id}/transfer/token/{token_id}/

Auth required. Owner only, and the caller must be an agent account. Soft-deletes a pending transfer token so it can no longer be claimed. {token_id} is the token's opaque id (not the raw secret), and it must belong to the org in the path.

curl example

curl -X DELETE "https://api.fast.io/current/org/1234567890123456789/transfer/token/eA1B2C3D4E5F6G7H8J9K0L1M2N3O4/" \
  -H "Authorization: Bearer {jwt_token}"

Response (200 OK)

{
  "result": true,
  "transfer_token": {
    "id": "eA1B2C3D4E5F6G7H8J9K0L1M2N3O4",
    "token": "a1b2c3d4e5f6...{64-char secret}",
    "org_id": 1234567890123456789,
    "state": "deleted",
    "expires": "2026-07-10 16:37:29 UTC",
    "created": "2026-07-07 16:37:29 UTC"
  }
}

Error responses

Error CodeHTTP StatusMessageCause
1680 (Access Denied)401"Only agent accounts can delete org transfer tokens."Caller is not an agent account
1680 (Access Denied)401"Transfer token does not belong to this organization."Token's org does not match the path org
1609 (Not Found)404"Transfer token not found."No token with that ID
1658 (Not Acceptable)406"Token cannot be deleted. It may have already been claimed, expired, or deleted."Token is not in a pending state
1654 (Internal Error)500"Failed to delete transfer token."Persisting the state change failed

Get Public Token Details

GET /current/org/transfer/claim/public/details/?token={token}

No authentication required. IP-rate-limited. Returns the token state plus the target org and agent-creator details so a human can review a handoff before claiming it. The raw token string is not echoed back.

Query parameters

NameTypeRequiredDescription
tokenstringYesThe 64-character transfer token secret.

curl example

curl -X GET "https://api.fast.io/current/org/transfer/claim/public/details/?token=a1b2c3d4e5f6...{64-char secret}"

Response (200 OK)

{
  "result": true,
  "transfer_token": {
    "id": "eA1B2C3D4E5F6G7H8J9K0L1M2N3O4",
    "state": "pending",
    "is_claimable": true,
    "expires": "2026-07-10 16:37:29 UTC",
    "created": "2026-07-07 16:37:29 UTC"
  },
  "org": {
    "id": "1234567890123456789",
    "domain": "acme-corp",
    "name": "Acme Corporation",
    "description": "Leading provider of innovation",
    "logo": "https://assets.fast.io/org/logo.png",
    "accent_color": "#0066CC",
    "plan": "agent"
  },
  "created_by": {
    "id": "1234567890123456780",
    "account_type": "agent",
    "first_name": "Service",
    "last_name": "Bot",
    "email_address": "bot@example.com",
    "profile_pic": null
  }
}

The transfer_token.is_claimable boolean reports whether the token can currently be claimed (pending and not expired). The org and created_by objects are omitted if the org or its agent creator can no longer be loaded.

Error responses

Error CodeHTTP StatusMessageCause
1605 (Invalid Input)406Token validation messagetoken missing, blank, or not the expected length
1609 (Not Found)404"Transfer token not found or invalid."No token matches the supplied secret

Claim Org (Human User)

POST /current/org/transfer/claim/

Auth required. The caller must be a human user (agent accounts cannot claim). Consumes a pending transfer token and takes ownership of the agent-owned org.

On success: the human becomes owner of the org and of every workspace under it; the agent that created the org is demoted to admin on the org and on each workspace; the org is moved off the agent plan onto its transfer target plan; and the token is marked claimed.

Request parameters

NameTypeRequiredDescription
tokenstringYesThe transfer token secret (accepted as a POST body field or a query parameter).

curl example

curl -X POST "https://api.fast.io/current/org/transfer/claim/" \
  -H "Authorization: Bearer {jwt_token}" \
  -d "token=a1b2c3d4e5f6...{64-char secret}"

Response (200 OK)

{
  "result": true,
  "org": {
    "id": "1234567890123456789",
    "domain": "acme-corp",
    "plan": "solo_monthly"
  },
  "previous_owner": {
    "id": "1234567890123456780",
    "account_type": "agent"
  },
  "workspaces_transferred": 3
}

Response fields

FieldTypeDescription
org.idstring19-digit numeric org ID now owned by the caller
org.domainstringOrg URL-safe subdomain
org.planstringThe plan the org was moved onto after transfer
previous_owner.idstring19-digit numeric ID of the agent that previously owned the org (now an admin)
previous_owner.account_typestringAlways "agent"
workspaces_transferredintegerNumber of workspaces re-owned to the caller

Error responses

Error CodeHTTP StatusMessageCause
1680 (Access Denied)401"Agent accounts cannot claim organizations. Only human users can claim ownership."Caller is an agent account
1609 (Not Found)404"Transfer token not found or invalid."No token matches the supplied secret
1609 (Not Found)404"The organization associated with this token was not found."The org could not be loaded
1658 (Not Acceptable)406"This transfer token has expired."Token is past its expiry
1658 (Not Acceptable)406"This transfer token has already been used or is no longer available."Token is not pending
1658 (Not Acceptable)406"This organization is no longer eligible for transfer."Org is no longer on the agent plan
1654 (Internal Error)500"Failed to complete organization transfer."Ownership or plan change could not be persisted

Organization Discovery

List Internal Orgs

GET /current/orgs/list/

Auth required. Lists orgs where the user is a direct member (member: true).

Non-admin/non-owner members only see orgs with active subscriptions; admins and owners always see their orgs.

curl example

curl -X GET "https://api.fast.io/current/orgs/list/" \
  -H "Authorization: Bearer {jwt_token}"

Response (200 OK)

{
  "result": true,
  "orgs": [
    {
      "id": "1234567890123456789",
      "domain": "acme-corp",
      "name": "Acme Corporation",
      "description": "Leading provider of innovation",
      "logo": "https://assets.fast.io/org/logo.png",
      "accent_color": "#0066CC",
      "closed": false,
      "suspended": false,
      "subscriber": true,
      "user_status": "joined",
      "member": true
    }
  ]
}

Response fields

FieldTypeDescription
orgsarrayArray of organization objects
orgs[].idstring19-digit numeric organization ID
orgs[].domainstringURL-safe subdomain
orgs[].namestring/nullDisplay name
orgs[].descriptionstring/nullDescription
orgs[].logostring/nullLogo asset URL
orgs[].accent_colorstring/nullBrand color
orgs[].closedbooleanWhether org is closed
orgs[].suspendedbooleanWhether org is suspended
orgs[].subscriberbooleanWhether org has an active subscription
orgs[].user_statusstring"joined" or "available"
orgs[].memberbooleanAlways true for this endpoint

Subscription filtering

User RoleBehavior
OwnerAlways sees the org
AdminAlways sees the org
MemberOnly sees the org if it has an active subscription

List External Orgs

GET /current/orgs/list/external/

Auth required. Lists orgs where the user has access only through workspace membership (member: false).

curl example

curl -X GET "https://api.fast.io/current/orgs/list/external/" \
  -H "Authorization: Bearer {jwt_token}"

Response (200 OK)

{
  "result": true,
  "orgs": [
    {
      "id": "1234567890123456780",
      "domain": "partner-corp",
      "name": "Partner Corporation",
      "description": "External partner organization",
      "logo": null,
      "accent_color": "#FF6600",
      "closed": false,
      "suspended": false,
      "subscriber": true,
      "user_status": "available",
      "member": false
    }
  ]
}

Response fields

FieldTypeDescription
orgsarrayArray of external organization objects
orgs[].user_statusstringAlways "available" for external orgs
orgs[].memberbooleanAlways false for this endpoint

List All Orgs

GET /current/orgs/all/

Auth required. Lists all accessible orgs (joined + invited).

curl example

curl -X GET "https://api.fast.io/current/orgs/all/" \
  -H "Authorization: Bearer {jwt_token}"

Response (200 OK)

{
  "result": true,
  "orgs": [
    {
      "id": "1234567890123456789",
      "domain": "acme-corp",
      "name": "Acme Corporation",
      "description": "Leading provider of innovation",
      "logo": "https://assets.fast.io/org/logo.png",
      "accent_color": "#0066CC",
      "closed": false,
      "suspended": false,
      "user_status": "joined"
    }
  ]
}

Response fields

FieldTypeDescription
orgs[].user_statusstring"joined" (already a member) or "available" (pending invitation)

List Available Orgs

GET /current/orgs/available/

Auth required. Lists orgs available to join (not yet joined). Excludes orgs the user is already a member of.

curl example

curl -X GET "https://api.fast.io/current/orgs/available/" \
  -H "Authorization: Bearer {jwt_token}"

Response (200 OK)

{
  "result": true,
  "orgs": [
    {
      "id": "1234567890123456782",
      "domain": "new-company",
      "name": "New Company",
      "description": "An org you can join",
      "logo": null,
      "accent_color": "#FF6600",
      "closed": false,
      "suspended": false
    }
  ]
}

Check Domain Availability

GET /current/orgs/check/domain/{domain_name}

Auth required. Checks if an org domain name is available for use.

Path parameters

NameTypeRequiredDescription
{domain_name}stringYesThe domain name to check for availability.

curl example

curl -X GET "https://api.fast.io/current/orgs/check/domain/acme-corp" \
  -H "Authorization: Bearer {jwt_token}"

Response (202 Accepted) — domain available

{
  "result": true
}

Error responses

Error CodeHTTP StatusMessageCause
1605 (Invalid Input)406"An invalid name was supplied."Domain format is invalid
1658 (Not Acceptable)406"The supplied name is restricted."Domain is reserved
1658 (Not Acceptable)406"The supplied name is already in use."Domain is taken

List Industries

GET /current/orgs/industries/

Auth required. Returns available industry types for org profiles.

curl example

curl -X GET "https://api.fast.io/current/orgs/industries/" \
  -H "Authorization: Bearer {jwt_token}"

Response (200 OK)

{
  "result": true,
  "technology": {
    "title": "Technology",
    "description": "Software, hardware, and IT services"
  },
  "healthcare": {
    "title": "Healthcare",
    "description": "Medical, pharmaceutical, and health services"
  },
  "finance": {
    "title": "Finance",
    "description": "Banking, investment, and financial services"
  },
  "education": {
    "title": "Education",
    "description": "Schools, universities, and training providers"
  }
}

Response fields

FieldTypeDescription
{key}stringMachine-readable industry identifier (use this value in create/update requests)
{key}.titlestringHuman-readable display name
{key}.descriptionstringBrief description of the industry category

Billing

The unsubscribed tier is identified as unpaid. It was previously reported as free; clients should accept both for now and treat them as the same tier. Where a plan identifier is accepted as INPUT, free is still accepted and resolves to unpaid. unpaid is not a purchasable plan: it never appears in GET /current/org/billing/plan/list/, and subscribing to it is refused.

Where the identifier appears for an org with no active subscription, and where it does not:

SurfaceWhat an unsubscribed org returns
The org's raw plan field (org details and the org list)"unpaid"
POST /current/org/{org_id}/billing/billing_status.current_planThe full plan object, with name: "unpaid", title: "Unpaid", category: "unpaid"
GET /current/org/{org_id}/billing/details/billing_status.current_plan{} — an empty object, not a plan named unpaid. This endpoint fills current_plan only for an org whose subscription is currently active
billing_status.previous_planThe full plan object, with name: "unpaid", when the previous plan was the unsubscribed tier

Do not read an empty current_plan as "no plan" or as an error. On GET .../billing/details/ it is the normal shape for an org that is not a current subscriber. To learn which tier such an org is on, read the org's plan field rather than this object.

Preview a Plan Change

GET /current/org/{org_id}/billing/preview/?billing_plan={plan}

Auth required. Admin or above. Returns what a plan change will cost before it is made. Read-only: creates no invoice and changes no subscription.

Use this before POST /current/org/{org_id}/billing/ whenever the org already has a subscription. A change that increases committed spend — a higher tier, or monthly to annual on the same tier — is invoiced immediately rather than at the next cycle, so the card is charged in the same interaction the customer confirms. Show them this figure first.

Request parameters

NameTypeRequiredDescription
billing_planstringYesTarget plan ID (a valid, currently-offered paid plan)
proration_dateintegerNoThe preview.proration_date from a prior GET .../billing/preview/ call. Pass it whenever you showed the customer a figure, so the invoice prices the instant they were quoted rather than drifting by however long they spent on the confirm dialog — a spend-increasing change collects immediately, so that drift is a real charge. Validated server-side. A quote in the future, or older than one billing period, is REFUSED with error 10765 rather than silently ignored — you showed the customer a figure, so repricing it quietly would charge them something they never agreed to. Recovery: request a fresh preview and confirm again, which shows them the correct current amount.

curl example

curl -X GET "https://api.fast.io/current/org/1234567890123456789/billing/preview/?billing_plan=growth_monthly" \
  -H "Authorization: Bearer {jwt_token}"

Response (200 OK)

{
  "result": true,
  "preview": {
    "amount_due_cents": 20132,
    "currency": "usd",
    "proration_date": 1756670400,
    "source_plan": "business_v2_monthly",
    "target_plan": "growth_monthly",
    "spend_increasing": true,
    "ends_trial": false
  }
}
FieldTypeDescription
preview.amount_due_centsintegerTotal due today, in cents, including tax. Divide by 100 before display.
preview.currencystringISO currency code
preview.proration_dateintegerUnix timestamp this quote was priced at. Pass it back on the change request so the swap prices the same instant — otherwise the customer can be charged a different figure from the one they agreed to, simply because time passed between the two calls.
preview.source_planstringPlan the subscription currently holds
preview.target_planstringPlan being previewed
preview.spend_increasingbooleantrue when the change increases committed spend and will therefore invoice immediately
preview.ends_trialbooleantrue when confirming also ends a running free trial. The copy must say so — “this ends your trial and charges $X today” reads very differently from “this charges $X today”.

Errors

Error CodeHTTP StatusCause
1605400billing_plan missing or not a currently-offered plan
10764406No preview is available — most commonly because the org has no subscription yet, and a first subscription is not a “change” to price. ⚠ Do NOT assume money is due. A first subscription charges $0 today when the plan offers a trial (pricing.free_days > 0) and the org is eligible (billing_status.free_trial_eligible); it charges the plan's list price only when neither holds. Decide from those two fields — not from the absence of a preview, and not from a local upgrade/downgrade classifier, which will read freepaid as a spend increase and tell a customer starting a free trial that they are being charged.

Create or Update Subscription

POST /current/org/{org_id}/billing/

Auth required. Admin or above. Creates or updates the org's billing subscription.

Request parameters

NameTypeRequiredDescription
billing_planstringNoTarget plan ID (must be a valid, currently-offered paid plan, e.g., "solo_monthly", "business_v2_monthly", "growth_monthly"). Each plan also has an annual variant (e.g., "business_v2_annual"). Plan IDs that are not currently offered are rejected here (existing orgs on them are unaffected).

curl example

curl -X POST "https://api.fast.io/current/org/1234567890123456789/billing/" \
  -H "Authorization: Bearer {jwt_token}" \
  -d "billing_plan=business_v2_monthly"

Response (201 Created) — new subscription

{
  "result": true,
  "billing_status": {
    "active": false,
    "free_trial_eligible": true,
    "current_plan": { "...": "..." },
    "customer": { "...": "..." },
    "subscription": { "...": "..." },
    "setup_intent": {
      "id": "{setup_id}",
      "client_secret": "{setup_id}_secret",
      "status": "requires_payment_method"
    },
    "payment_intent": null,
    "payment_recovery": {},
    "public_key": "{public_key}"
  }
}

Every field on this response is nested under billing_status — there is no root-level subscription, is_active, or is_trial_eligible.

Response (202 Accepted) — subscription updated.

Response (200 OK) — payment recovery: if the org already has an UNPAID subscription (status incomplete, past_due, or unpaid — e.g. a prior card was declined), no new subscription or setup intent is created. The response instead carries a non-empty billing_status.payment_recovery object (see Get Billing Details below for its shape); confirm its nested payment_intent.client_secret with a (new) card to pay the existing open invoice. While a subscription is in this state a plan switch is not applied — the outstanding invoice must be settled first, after which the plan can be changed once the subscription is active.

Error responses

Error CodeHTTP StatusMessageCause
1605 (Invalid Input)406"An invalid plan was supplied."Plan ID not recognized
1605 (Invalid Input)406"Cannot create subscription for the unpaid plan. Please select a paid plan."Tried to subscribe to the unpaid plan (an org with no active subscription)
1658 (Not Acceptable)406"An error occurred updating your subscription..."Subscription update failed
1658 (Not Acceptable)406"An error occurred creating the payment intent..."Intent creation failed

Schedule Subscription Cancellation

DELETE /current/org/{org_id}/billing/

Auth required. Owner only. Schedules the org's subscription to cancel at the end of the current billing period. The customer keeps full access until cancel_at. Use PUT (below) to reverse the schedule before cancel_at is reached.

curl example

curl -X DELETE "https://api.fast.io/current/org/1234567890123456789/billing/" \
  -H "Authorization: Bearer {jwt_token}"

Response (202 Accepted)

{
  "result": true,
  "status": "scheduled_cancellation",
  "message": "Your subscription is scheduled to end at the close of the current billing period.",
  "cancel_at": 1735689600,
  "cancel_at_period_end": true,
  "closed": false
}

If a cancellation has already been scheduled (or already executed):

{
  "result": true,
  "status": "already_cancelled",
  "message": "Subscription is already cancelled"
}

Response fields

FieldTypeDescription
statusstring"scheduled_cancellation" or "already_cancelled"
messagestringHuman-readable status message
cancel_atinteger/nullUnix timestamp when access will end. null only if the subscription record could not be re-read after scheduling.
cancel_at_period_endbooleanAlways true on a successful schedule
closedbooleanAlways false for the scheduled-cancel flow — the org remains open until cancel_at

Notes

Error responses

Error CodeHTTP StatusMessageCause
1683 (Resource Missing)404"No subscription was found to cancel."Org is not a subscriber
1654 (Internal Error)500"Your subscription failed to be canceled..."Cancellation failed

Reactivate Subscription

PUT /current/org/{org_id}/billing/

Auth required. Owner only. Reactivates a subscription whose cancellation was scheduled via DELETE /current/org/{org_id}/billing/ but has not yet executed. Clears cancel_at_period_end so the subscription renews normally.

curl example

curl -X PUT "https://api.fast.io/current/org/1234567890123456789/billing/" \
  -H "Authorization: Bearer {jwt_token}"

Response (200 OK)

{
  "result": true,
  "status": "reactivated",
  "message": "Your subscription has been reactivated and will renew at the end of the current billing period.",
  "current_period_end": 1735689600,
  "cancel_at_period_end": false
}

Response fields

FieldTypeDescription
statusstringAlways "reactivated" on success
messagestringHuman-readable status message
current_period_endinteger/nullUnix timestamp of the next renewal
cancel_at_period_endbooleanAlways false on success

Notes

Error responses

Error CodeHTTP StatusMessageCause
1683 (Resource Missing)404"No active subscription was found to reactivate."Org is not currently a subscriber
1654 (Internal Error)500"Your subscription could not be reactivated, please contact support."Reactivation failed

Get Billing Details

GET /current/org/{org_id}/billing/details/

Auth required. Admin or above. Returns subscription/billing details.

curl example

curl -X GET "https://api.fast.io/current/org/1234567890123456789/billing/details/" \
  -H "Authorization: Bearer {jwt_token}"

Response (200 OK)

{
  "result": true,
  "billing_status": {
    "active": true,
    "free_trial_eligible": false,
    "current_plan": { "...": "..." },
    "customer": { "...": "..." },
    "subscription": { "...": "..." },
    "setup_intent": { "...": "..." },
    "payment_intent": { "...": "..." },
    "payment_recovery": { "...": "..." },
    "public_key": "{public_key}"
  }
}

Every field on this response is nested under billing_status — there is no root-level subscription, is_active, or is_trial_eligible. billing_status.previous_plan is present only when the subscription is cancelled (see below).

Response fields

FieldTypeDescription
billing_status.subscriptionobjectPayment provider subscription details
billing_status.customerobjectPayment provider customer details
billing_status.setup_intentobject/nullActive setup intent if exists
billing_status.setup_intent.trialbooleanWhether the intent was minted with a free trial. Capped to false whenever billing_status.free_trial_eligible is false — including an intent minted earlier while the org was still eligible — so it never advertises a trial the org is not currently eligible for.
billing_status.payment_intentobject/nullActive payment intent if exists
billing_status.payment_recoveryobjectPresent (non-empty) when an existing subscription's invoice is unpaid — status incomplete, past_due, or unpaid (e.g. a declined first payment). Contains recoverable (bool), status, plan, subscription {id,status}, invoice {id,status,amount_due,currency,hosted_invoice_url}, and payment_intent {id,client_secret,status,requires_action}. The client completes payment by confirming that PaymentIntent's client_secret with a (new) card. Empty when there is nothing to recover.
billing_status.activebooleanWhether subscription is currently active
billing_status.free_trial_eligiblebooleanWhether a free trial is available for this org. false once this org has ever subscribed, once this org is not the owner's first organization (permanent — a free trial is only ever available on a user's first org, so an owner can still be true on that first org even while owning others), or when the owner is within the per-user 60-day trial cooldown.
billing_status.current_planobjectFull plan-details object for the org's current plan. Filled only while the subscription is ACTIVE — an org that is not a current subscriber returns {} here, NOT a plan named unpaid. That is the normal shape for an unsubscribed org, not an error; read the org's plan field to learn its tier. (The POST /current/org/{org_id}/billing/ response differs: it returns the full object with name: "unpaid".)
billing_status.previous_planobjectFull plan-details object for the org's previous plan. Present only when the subscription is cancelled. Reports name: "unpaid" when the previous plan was the unsubscribed tier.
billing_status.public_keystringPayment provider publishable key

Get Credit Usage

GET /current/org/{org_id}/billing/usage/limits/credits/

Auth required. Admin or above. Returns credit consumption and limits.

curl example

curl -X GET "https://api.fast.io/current/org/1234567890123456789/billing/usage/limits/credits/" \
  -H "Authorization: Bearer {jwt_token}"

Response (200 OK)

{
  "result": true,
  "credit_limits_enabled": true,
  "free_org_mode": false,
  "org_id": "1234567890123456789",
  "plan": "solo_monthly",
  "over_limit": false,
  "usage": {
    "credits_used": 1200,
    "credit_limit": 300000,
    "credits_remaining": 298800,
    "usage_percentage": 0.4
  },
  "period": {
    "start": "2025-01-15 10:00:00 UTC",
    "end": "2025-02-14 10:00:00 UTC",
    "days_total": 30,
    "days_elapsed": 10,
    "days_remaining": 20
  },
  "renewal": {
    "interval_days": 30,
    "next_renewal": "2025-02-14 10:00:00 UTC"
  },
  "trial": null
}

Response fields

FieldTypeDescription
credit_limits_enabledbooleanWhether the plan enforces credit limits
free_org_modebooleantrue for free orgs (reductive model)
over_limitbooleanWhether the org has exceeded its credit limit
usage.credits_usedintegerCredits consumed in the current period
usage.credit_limitintegerTotal credits available per period
usage.credits_remainingintegerCredits remaining in the current period
usage.usage_percentagenumberPercentage of credits used
period.startstringStart of the current billing period (YYYY-MM-DD HH:MM:SS UTC)
period.endstringEnd of the current billing period (YYYY-MM-DD HH:MM:SS UTC)
period.days_totalintegerTotal days in the period
period.days_elapsedintegerDays elapsed since period start
period.days_remainingintegerDays remaining until renewal
renewal.interval_daysintegerDays between credit renewals
renewal.next_renewalstring/nullNext credit renewal timestamp (YYYY-MM-DD HH:MM:SS UTC), or null
run_rateobject/nullUsage rate projections (shown after 25% of period or credits used)
trialobject/nullTrial info if applicable

Credit costs: storage (150/GB), bandwidth (400/GB), AI tokens (1/100 tokens), document ingestion (10/page), video ingestion (5/sec), image ingestion (5/image), file conversions (25/each).


List Billable Members

GET /current/org/{org_id}/billing/usage/members/list/

Auth required. Admin or above. Paginated.

Query parameters

NameTypeDefaultDescription
limitinteger1001-500
offsetinteger0Items to skip

curl example

curl -X GET "https://api.fast.io/current/org/1234567890123456789/billing/usage/members/list/" \
  -H "Authorization: Bearer {jwt_token}"

Response (200 OK)

{
  "result": true,
  "billable_members": [
    {
      "id": "1234567890123456789",
      "account_type": "human",
      "email_address": "user@example.com",
      "parents": {
        "9876543210987654321": {
          "permission": "member",
          "date_joined": "2024-01-15 10:30:00 UTC"
        }
      }
    }
  ]
}

Response fields

FieldTypeDescription
billable_membersarrayArray of billable member objects
billable_members[].idstring19-digit user ID
billable_members[].account_typestring"human" or "agent"
billable_members[].email_addressstringUser's email
billable_members[].parentsobjectMap of workspace IDs to membership details

Get Usage Meters

GET /current/org/{org_id}/billing/usage/meters/list/

Auth required. Admin or above. Returns detailed usage breakdown by meter.

Query parameters

NameTypeRequiredDefaultDescription
meterstringYesMeter type (e.g., "storage_bytes", "bandwidth_bytes", "ai_tokens")
start_timestring (datetime)No30 days agoStart of time range
end_timestring (datetime)NoNowEnd of time range
workspace_idstringNoFilter by workspace (19-digit ID)
share_idstringNoFilter by share (19-digit ID)

Only one of workspace_id or share_id can be specified at a time.

Usage history retention. Detailed usage history is kept for a bounded window; detail older than that window is removed automatically. A request whose range reaches past the window returns zeros for the part that is no longer retained rather than an error, so a flat or empty early portion of a long range means the detail has aged out, not that there was no usage. Invoices and billed totals for closed periods are unaffected — they are kept independently of this detail. Pull and store anything you need to keep for longer.

curl example

curl -X GET "https://api.fast.io/current/org/1234567890123456789/billing/usage/meters/list/?meter=storage_bytes&start_time=2026-08-01+00:00:00&end_time=2026-08-31+23:59:59" \
  -H "Authorization: Bearer {jwt_token}"

Response (200 OK)

{
  "result": true,
  "usage": {
    "meter": "storage_bytes",
    "total": 1073741824,
    "cost": 0.50,
    "credits": 500,
    "start_time": "2026-08-01 00:00:00 UTC",
    "end_time": "2026-08-31 23:59:59 UTC",
    "interval_hours": 24,
    "workspace_id": null,
    "share_id": null,
    "data_points": [
      {
        "start_time": "2026-08-01 00:00:00 UTC",
        "end_time": "2026-08-02 00:00:00 UTC",
        "value": 536870912,
        "cost": 0.25,
        "credits": 250
      }
    ]
  }
}

Response fields

FieldTypeDescription
usage.meterstringThe meter type queried
usage.totalnumberTotal usage value for the period
usage.costnumberTotal cost in USD
usage.creditsnumber/nullTotal credits consumed (null for direct-billed meters)
usage.start_timestringStart of the queried range
usage.end_timestringEnd of the queried range
usage.interval_hoursintegerHours per data point (auto-calculated, max 30 points)
usage.data_pointsarrayTime-series data with value, cost, and credits per interval

Error responses

Error CodeHTTP StatusMessageCause
1605 (Invalid Input)406"Must be one of the valid meter types"Invalid meter type
1605 (Invalid Input)406"Only one of workspace_id or share_id can be specified."Both filters provided
1605 (Invalid Input)406"Start time must be before end time."Invalid time range
1605 (Invalid Input)406"Time range must be at least 1 day."Range too short
1654 (Internal Error)500"Failed to retrieve usage data."Internal error

List Available Plans

GET /current/org/billing/plan/list/

Auth required. Returns the paid plans available to select when activating or upgrading an organization. Only these currently-offered paid plans are returned for selection.

New organizations choose a paid plan to activate; until then the org is in an upgrade-only state.

curl example

curl -X GET "https://api.fast.io/current/org/billing/plan/list/" \
  -H "Authorization: Bearer {jwt_token}"

Response (200 OK)

{
  "result": true,
  "results": 3,
  "defaults": {
    "pro": "solo_monthly",
    "business": "business_v2_monthly"
  },
  "plans": [
    {
      "id": "solo_monthly",
      "name": "Starter",
      "category": "pro",
      "amount": 2900
    },
    {
      "id": "business_v2_monthly",
      "name": "Business",
      "category": "business",
      "amount": 9900
    },
    {
      "id": "growth_monthly",
      "name": "Growth",
      "category": "business",
      "amount": 29900
    }
  ]
}

Response fields

FieldTypeDescription
resultsintegerNumber of available plans
defaultsobjectDefault plan IDs per category (pro, business)
plansarrayArray of plan detail objects
plans[].idstringPlan identifier (use in subscription requests)
plans[].namestringDisplay name
plans[].categorystringPlan category: "pro" (Starter), "business" (Business or Growth)
plans[].amountintegerPrice in cents (e.g., 2900 = $29.00)

List Invoices

GET /current/org/{org_id}/billing/invoices/

Auth required. Admin or above. Returns a paginated list of invoices with hosted payment links.

Query parameters

NameTypeDefaultDescription
limitinteger10Number of invoices to return (1-100)
starting_afterstringInvoice ID cursor for pagination

curl example

curl -X GET "https://api.fast.io/current/org/1234567890123456789/billing/invoices/?limit=10" \
  -H "Authorization: Bearer {jwt_token}"

Response (200 OK)

{
  "result": true,
  "invoices": [
    {
      "id": "in_1234567890",
      "status": "paid",
      "currency": "usd",
      "amount_due": 2900,
      "amount_paid": 2900,
      "subtotal": 2900,
      "total": 2900,
      "paid": true,
      "description": "Subscription creation",
      "hosted_invoice_url": "https://{payment_provider_host}/i/.../{invoice_id}",
      "invoice_pdf": "https://{payment_provider_host}/invoice/.../{invoice_id}.pdf",
      "period_start": "2026-03-01 00:00:00 UTC",
      "period_end": "2026-04-01 00:00:00 UTC",
      "created": "2026-03-01 00:00:00 UTC"
    }
  ],
  "has_more": false
}

Response fields

FieldTypeDescription
invoicesarrayArray of invoice objects
invoices[].idstringInvoice identifier (use as starting_after cursor)
invoices[].statusstring"draft", "open", "paid", "void", "uncollectible"
invoices[].currencystringThree-letter ISO currency code (e.g., "usd")
invoices[].amount_dueintegerAmount due in cents
invoices[].amount_paidintegerAmount paid in cents
invoices[].subtotalintegerSubtotal before tax in cents
invoices[].totalintegerTotal after tax in cents
invoices[].paidbooleanWhether the invoice has been paid
invoices[].descriptionstring/nullInvoice description
invoices[].hosted_invoice_urlstring/nullURL to view and pay the invoice
invoices[].invoice_pdfstring/nullDirect PDF download URL
invoices[].period_startstring/nullBilling period start (YYYY-MM-DD HH:MM:SS UTC)
invoices[].period_endstring/nullBilling period end (YYYY-MM-DD HH:MM:SS UTC)
invoices[].createdstring/nullInvoice creation timestamp (YYYY-MM-DD HH:MM:SS UTC)
has_morebooleanWhether more invoices are available for pagination

Amounts are in the smallest currency unit (cents for USD). Use hosted_invoice_url to link users to their invoices. Use starting_after with the last invoice id for pagination.


Create Workspace (from Org)

POST /current/org/{org_id}/create/workspace/

Auth required. Member or above. Creates a workspace within the org. Subject to plan feature availability and workspace creation limits.

Request parameters

NameTypeRequiredDescription
folder_namestringYesURL-safe folder name for the workspace. Must be globally unique across all workspaces.
namestringYesDisplay name.
descriptionstringNoWorkspace description.
perm_joinstringYesWho can auto-join from the org. Values: 'Member or above' (default), 'Admin or above', 'Only Org Owners'.
perm_member_managestringYesWho can manage workspace members. Values: 'Member or above' (default), 'Admin or above'.
intelligencestringNoEnable AI features ("true"/"false"). Defaults to "true" when omitted. Forced off on plans lacking content_ai + ai_agent, which never fails the create.
accent_colorstring (JSON)NoAccent color as JSON.
background_color1string (JSON)NoPrimary background color as JSON.
background_color2string (JSON)NoSecondary background color as JSON.

curl example

curl -X POST "https://api.fast.io/current/org/1234567890123456789/create/workspace/" \
  -H "Authorization: Bearer {jwt_token}" \
  -d "folder_name=project-alpha" \
  -d "name=Project Alpha" \
  -d "perm_join=Member or above" \
  -d "perm_member_manage=Admin or above"

Response (200 OK)

{
  "result": true,
  "workspace": {
    "id": "1234567890123456780",
    "folder_name": "project-alpha"
  }
}

Response fields

FieldTypeDescription
workspace.idstring19-digit numeric workspace ID
workspace.folder_namestringURL-safe folder name

Error responses

Error CodeHTTP StatusMessageCause
1685 (Feature Limit)412"Workspace creation is not available on your current plan."Feature disabled
1685 (Feature Limit)412"You have reached your workspace creation limit."Limit exceeded
1658 (Not Acceptable)406"The supplied workspace folder name is already in use."Duplicate folder name
1605 (Invalid Input)406"An invalid workspace folder name was supplied."Invalid folder name
1605 (Invalid Input)406"An invalid configuration was supplied..."Metadata validation failed

List Workspaces in Org

GET /current/org/{org_id}/list/workspaces/

Auth required. Lists accessible workspaces within the org.

Query parameters

NameTypeDefaultDescription
archivedstring"false""true" to show archived workspaces, "false" for active

Access levels

RoleAccessNotes
OwnerFull accessSees all workspaces
AdminFull accessSees all workspaces except those restricted to perm_join = 'Only Org Owners' (unless directly a member)
MemberFilteredSees workspaces matching join permission level
ExternalFilteredSees only workspaces where they are a direct member

curl example

curl -X GET "https://api.fast.io/current/org/1234567890123456789/list/workspaces/" \
  -H "Authorization: Bearer {jwt_token}"

Response (200 OK)

{
  "result": true,
  "workspaces": [
    {
      "id": "1234567890123456780",
      "folder_name": "project-alpha",
      "name": "Project Alpha",
      "description": "Main project workspace"
    }
  ]
}

Response fields

FieldTypeDescription
workspacesarrayArray of workspace objects
workspaces[].idstring19-digit numeric workspace ID
workspaces[].folder_namestringURL-safe folder name
workspaces[].namestringDisplay name
workspaces[].descriptionstring/nullWorkspace description
↑ Back to top