Events & Activity Event search, activity polling, WebSocket realtime.

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

Events capture every action in the system — file operations, membership changes, comments, AI activity, billing, and more. Use the event search endpoints to query the log, activity polling for efficient change detection, and WebSocket for real-time delivery.

Search and filter the event log. Events capture every action in the system — file operations, membership changes, comments, AI activity, billing, and more.

Search Events

GET /current/events/search/

Search and filter events with comprehensive filtering options. Uses offset-based pagination.

Auth: Required (JWT). Subject to global rate limiting; see the rate-limit section in the main reference.

Query Parameters

ParameterTypeRequiredDefaultDescription
user_id string Conditional Filter by user profile ID. One of user_id, org_id, workspace_id, share_id, or parent_event_id is required.
org_id string Conditional Filter by organization profile ID
workspace_id string Conditional Filter by workspace profile ID
share_id string Conditional Filter by share profile ID
parent_event_id string Conditional Filter by parent event ID for serial/batch events. Cannot combine with filters other than acknowledged, limit, offset.
event string No Filter by specific event name (e.g., workspace_storage_file_added). Max 100 characters.
category string No Filter by event category. See Event Categories.
subcategory string No Filter by event subcategory. See Event Subcategories.
calling_user_id string No Filter by the user who triggered the event (19-digit numeric ID)
object_id string No Filter by related object ID (file, folder, etc.)
acknowledged string No Filter by acknowledgment status: "true" or "false"
visibility string No All non-internal "external_audit_log" or "external"
created-min string No Lower bound for event creation time. Accepts ISO 8601 (e.g., 2025-12-01T06:00:00Z) or YYYY-MM-DD HH:MM:SS
created-max string No Upper bound for event creation time. Same format as created-min; must be > created-min
limit integer No 100 Maximum number of results (1–250)
offset integer No 0 Number of results to skip for pagination
output string No Select the response shape. Comma-separated tokens (e.g. terse, standard, full). See the "Compact Responses" section below for the three detail levels and the fields returned at each level.

Profile filter priority: If multiple profile filters are supplied, priority is: user_id > org_id > workspace_id > share_id. Only the highest-priority filter is applied.

Example Request

curl -X GET "https://api.fast.io/current/events/search/?workspace_id=1234567890123456789&category=workspace&subcategory=storage&limit=50" \
  -H "Authorization: Bearer {jwt_token}"

Response (200 OK)

{
  "result": true,
  "events": [
    {
      "event_id": "ancouywgcxiff7kpbijpl4ysgn43j",
      "created": "2025-01-20 10:30:45 UTC",
      "acknowledged": false,
      "event": "workspace_storage_file_added",
      "category": "workspace",
      "sub_category": "storage",
      "object_id": "2lavr2y6uogubeyvfmqv2ur6k6ezt",
      "calling_user": "9876543210987654321",
      "org_id": "1111111111111111111",
      "workspace_id": "1234567890123456789",
      "filename": "quarterly_report.pdf",
      "file_size": 2485760
    }
  ]
}

Response Fields

FieldTypeDescription
resultbooleantrue on success
eventsarrayArray of event objects
events[].event_idstringUnique event identifier (alphanumeric OpaqueId)
events[].createdstringEvent timestamp (Y-m-d H:i:s UTC)
events[].acknowledgedbooleanWhether the current user has acknowledged this event
events[].eventstringEvent name identifier (e.g., workspace_storage_file_added)
events[].categorystringEvent category name
events[].sub_categorystringEvent subcategory name
events[].object_idstringRelated object OpaqueId (if applicable)
events[].calling_userstring19-digit numeric ID of the attributed user, when one was recorded
events[].org_idstringOrganization ID context (if applicable)
events[].workspace_idstringWorkspace ID context (if applicable)
events[].share_idstringShare ID context (if applicable)
events[].user_idstringTarget user ID for user-specific events (if applicable)

Additional event-specific fields (e.g., filename, file_size, member_name) vary by event type.

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 StatusDescription
1605 (Invalid Input)406No profile filter or parent_event_id provided
1605 (Invalid Input)406created-min is greater than created-max
1605 (Invalid Input)406parent_event_id combined with disallowed filters
1605 (Invalid Input)406Invalid datetime format for created-min or created-max
1680 (Access Denied)401Token scope does not include the requested profile, or the audit-log gate is not satisfied (see Notes)
1600 (Query Error)500Internal error during event search
1650 (Authentication Invalid)401Missing or invalid JWT token
1651 (Invalid Request Type)405Wrong HTTP method (only GET accepted)

Notes: Results may be slightly delayed due to caching. OAuth scoped tokens enforce entity-level access: events are filtered to only entities within the token's scope. Events the user cannot access are automatically excluded from results.

Audit-log queries are gated. Requesting visibility=external_audit_log requires (1) an org_id, workspace_id, or share_id filter — a user_id-only audit-log query is rejected — and (2) admin permission on that profile. Failing either returns 1680 (Access Denied).


Summarize Events (AI)

GET /current/events/search/summarize/

Search events and generate an AI-powered natural language summary. Accepts all parameters from /events/search/ plus user_context.

Auth: Required (JWT). Subject to global rate limiting; shares the events search rate limit bucket.

Additional Query Parameters

ParameterTypeRequiredDefaultDescription
user_context string No "" Focus guidance for the AI summary (e.g., "Focus on uploads"). Max 64 chars; letters, numbers, spaces, . , ! ? ' - only.

All other parameters are identical to Search Events.

Example Request

curl -X GET "https://api.fast.io/current/events/search/summarize/?workspace_id=1234567890123456789&user_context=Focus%20on%20file%20uploads&limit=100" \
  -H "Authorization: Bearer {jwt_token}"

Response (200 OK)

{
  "result": true,
  "summary": {
    "text": "@[user:9876543210987654321:Jane Smith] uploaded 12 files...",
    "metrics": {
      "total_events": 50,
      "unique_actors": 5,
      "date_range": {
        "start": "2025-01-01 00:00:00 UTC",
        "end": "2025-01-20 10:30:45 UTC"
      },
      "categories": {
        "storage": 30,
        "members": 20
      }
    }
  },
  "events": [ ... ]
}

Response Fields (additional to events search)

FieldTypeDescription
summaryobject|nullAI-generated summary, or null if no events or generation failed
summary.textstringNatural language summary with @[type:ID:name] mention pills
summary.metrics.total_eventsintegerTotal events summarized
summary.metrics.unique_actorsintegerDistinct users who triggered events
summary.metrics.date_range.startstringEarliest event timestamp (Y-m-d H:i:s UTC)
summary.metrics.date_range.endstringMost recent event timestamp (Y-m-d H:i:s UTC)
summary.metrics.categoriesobjectMap of category names to event counts

Summary Mention Pill Formats

EntityFormatExample
User@[user:USER_ID:Display Name]@[user:9876543210987654321:Jane Smith]
File@[file:FILE_ID:filename.ext]@[file:ancouywgcxiff7kpbijpl4ysgn43j:report.pdf]
Folder@[folder:FOLDER_ID:foldername]@[folder:2lavr2y6uogubeyvfmqv2ur6k6ezt:Projects]
Workspace@[workspace:WS_ID:name]@[workspace:1234567890123456789:Engineering]
Share@[share:SHARE_ID:name]@[share:5555555555555555555:Client Files]

Error Responses

All errors from /events/search/ apply, plus:

Error CodeHTTP StatusDescription
1680 (Access Denied)401The org_id, workspace_id, or share_id filter names a profile you are not a member of
1609 (Not Found)404The org_id, workspace_id, or share_id filter names a profile that does not exist
1605 (Invalid Input)406The id supplied is not of the type the parameter expects (e.g. a workspace id passed as org_id)

Notes: Summary generation failures are non-fatal: summary is null but events are still returned.

You must belong to the profile you filter on. This endpoint consumes AI tokens that are billed to the filtered profile's organization, so org_id, workspace_id, and share_id are authorized before any summary is generated: the profile must exist, the id must be of the matching type, and you must hold an active membership on it. /events/search/ (the same query without the summary) is unchanged.

The account billed is the one you filtered on. The summary is charged to the organization that owns the profile the query is scoped to — the same profile that selects which events are searched. A query scoped to yourself (user_id, or a parent_event_id query) is charged to your own billing account. Where more than one filter is supplied, the one that selects the events (in the order user_id, org_id, workspace_id, share_id) is the one authorized and billed; the others do not select or bill anything, but they are still validated, so a malformed or wrong-type value in any of them fails the request. user_id, org_id, workspace_id and share_id are type-checked on this endpoint: each must carry an id of the kind the parameter names. If the billing organization cannot be resolved, the request still succeeds and returns the events with summary set to null; it is never charged to a different account.

The same audit-log gate as /events/search/ applies: visibility=external_audit_log requires an org_id/workspace_id/share_id context filter AND admin permission on that profile, otherwise 1680 (Access Denied).


Event Details

GET /current/event/{event_id}/details/

Get full details for a single event.

Auth: Required (JWT). Default rate limiting. No credit consumption.

Path Parameters

ParameterTypeRequiredDescription
{event_id} string Yes Alphanumeric OpaqueId of the event

Example Request

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

Response (200 OK)

{
  "result": true,
  "event": {
    "event_id": "ancouywgcxiff7kpbijpl4ysgn43j",
    "created": "2025-01-20 10:30:45 UTC",
    "acknowledged": false,
    "event": "workspace_storage_file_added",
    "category": "workspace",
    "sub_category": "storage",
    "object_id": "2lavr2y6uogubeyvfmqv2ur6k6ezt",
    "calling_user": "9876543210987654321",
    "org_id": "1111111111111111111",
    "workspace_id": "1234567890123456789",
    "filename": "quarterly_report.pdf",
    "file_size": 2485760
  }
}

Access Rules

ConditionAccess
User is the calling_userGranted
User is the event_user (target)Granted
Event has targeted permission and user is neither target nor callerDenied
Event is internal visibilityAlways denied
User has appropriate profile-level permissionsGranted based on permission level

Error Responses

Error CodeHTTP StatusDescription
1605 (Invalid Input)406Event ID missing, empty, or not a valid OpaqueId
1609 (Not Found)404No event exists with the provided ID
1605 (Invalid Input)406Event has internal visibility
1605 (Invalid Input)406User lacks permission (targeted event)
1650 (Authentication Invalid)401Missing or invalid JWT token
1651 (Invalid Request Type)405Wrong HTTP method (only GET accepted)

Acknowledge Event

POST /current/event/{event_id}/ack/

Acknowledge (mark as read) an event for the current user. Idempotent.

Auth: Required (JWT). Default rate limiting.

Path Parameters

ParameterTypeRequiredDescription
{event_id} string Yes Alphanumeric OpaqueId of the event to acknowledge

Example Request

curl -X POST "https://api.fast.io/current/event/ancouywgcxiff7kpbijpl4ysgn43j/ack/" \
  -H "Authorization: Bearer {jwt_token}"

Response (200 OK)

{
  "result": true
}

Error Responses

Error CodeHTTP StatusDescription
1605 (Invalid Input)406Event ID missing or invalid
1609 (Not Found)404Event not found
1605 (Invalid Input)406Event has internal visibility
1605 (Invalid Input)406User lacks permission (targeted event)
1664 (Datastore Error)500Failed to persist the acknowledgment
1650 (Authentication Invalid)401Missing or invalid JWT token

Note: Acknowledgment is per-user. Acknowledging for one user does not affect others. Same access rules as event details apply.

Compact Responses (output=)

Every endpoint that returns event objects (search, details) 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 event (cumulative)
terseevent_id, event, category, object_id, created
standardterse + sub_category, calling_user, org_id, workspace_id, user_id, share_id, acknowledged, template (containing description + params), severity, visibility, notification
fullstandard + required_params, requires_parent_node, permission

Use terse for activity feed tickers and unread-count polling — it carries the event identity (event_id), name (event), category, target object, and a timestamp (created), which is the minimum a feed row needs to render without a follow-up fetch. Use standard for the most common event list/detail views — it adds subcategory, every profile-link id (calling user, owning org/workspace/share), the acknowledgment flag, the template object (which carries the human-readable description and any template params), and the render-hint enums: severity (drives feed-row color/icon), visibility (distinguishes the Activity, Audit-Log, and internal tabs), and notification (drives bell/notification rendering). Use full (or omit the parameter) for audit-log exports and event schema introspection. 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.

Event Categories

CategoryAPI ValueDescription
UploaduploadFile upload operations
UseruserUser account events
OrganizationorgOrganization events
WorkspaceworkspaceWorkspace operations — and workspace-scoped file/folder activity. See below
ShareshareShare operations — and share-scoped file/folder activity. See below
AIaiAI/ML operations
InvitationinvitationInvitation events
EmailemailEmail-related events
BillingbillingBilling and subscription events
MetadatametadataMetadata operations
AppsappsApplication/integration events
NodenodeAI indexing pipeline only — not file operations. See below
ServerserverServer/system-level events
ImportimportImport operations

Where File and Folder Activity Lives

File and folder activity is NOT category node. Despite the name, node covers only the AI indexing pipeline (for example node_ai_summary_created), and most of its events are internal and never returned by the API.

It lives in two categories, and filtering only one of them silently omits the other:

category=workspace — workspace-scoped storage:

workspace_storage_file_added       workspace_storage_folder_created
workspace_storage_file_updated     workspace_storage_folder_updated
workspace_storage_file_deleted     workspace_storage_folder_deleted
workspace_storage_file_moved       workspace_storage_folder_moved
workspace_storage_file_restored    workspace_storage_folder_restored
workspace_storage_file_copied      workspace_storage_folder_copied
workspace_storage_lock_overridden

category=share — the share-scoped counterpart, a full parallel set:

share_storage_file_added           share_storage_folder_created
share_storage_file_updated         share_storage_folder_updated
share_storage_file_deleted         share_storage_folder_deleted
share_storage_file_moved           share_storage_folder_moved
share_storage_file_restored        share_storage_folder_restored
share_storage_file_copied          share_storage_folder_copied
share_storage_lock_overridden

A consumer that filters only workspace sees no activity in any share. Query both categories for a complete picture of what happened to files.

calling_user_id vs user_id — Actor vs Subject

These two filters answer different questions and are not interchangeable:

FilterMatches OnAnswers
calling_user_idthe user who performed the action“what did this person do”
user_idthe user the event is about“what happened to / about this person”

calling_user_id matches the attributed actor. Usually that is the person who performed the action. Where a background workflow has a responsible party, it can be that party instead — a file that a scheduled sync pulls in is attributed to the user who connected the synced folder, even though nobody clicked anything at the time, so it appears in the activity feed under their name.

But an absent calling_user_id does NOT mean nobody was responsible. Attribution is recorded per event type, and several types that a user plainly caused still carry no actor:

So calling_user_id answers “what is attributed to this person” — right for an actor-scoped audit view, and wrong for “everything that happened in this scope”, where you should omit it. Do not assume it excludes all background activity, and do not read its absence as “the system did this on its own”. Filter on user_id when you want what happened to a person.

Event Subcategories

SubcategoryAPI ValueDescription
StoragestorageFile and folder operations
CommentscommentsComment activity
MembersmembersMembership changes
LifecyclelifecycleCreate, update, delete, archive events
SettingssettingsConfiguration changes
SecuritysecuritySecurity-related events
AuthenticationauthenticationLogin and auth events
AIaiAI processing events
InvitationsinvitationsInvitation management
BillingbillingSubscription and payment
AssetsassetsAsset (avatar, branding) updates
UploaduploadUpload events
TransfertransferOwnership transfer events
Import/Exportimport_exportImport/export operations
Quick SharequickshareQuick share events
MetadatametadataMetadata operations
APIapiAPI-related events
ArchivearchiveArchive operations
EmailemailEmail events
RenderrenderPreview/thumbnail rendering events
Cloud Importcloud_importCloud import operations

Event Visibility Levels

VisibilityAPI ValueDescription
InternalinternalSystem events. Never accessible via API.
Audit Logexternal_audit_logAudit/compliance events. Targeted permission checks bypassed for admins.
ExternalexternalStandard user-facing events.

Default (no visibility parameter): returns both external_audit_log and external events, excludes internal.

Event Permission Levels

PermissionDescription
memberAny member of the profile can view
adminOnly admins of the profile can view
targetedOnly the target user or the calling user can view

When querying with visibility=external_audit_log, targeted permission checks are bypassed — but the query itself is gated: it requires an org_id/workspace_id/share_id context filter AND admin permission on that profile (see the Audit-log note under Search Events).

Event History Retention

Event history is kept for a bounded window that depends on the organization’s plan — higher plans retain history for longer. Events older than the organization’s window are removed automatically, so a search over an older period returns fewer results rather than an error.

Two things follow for integrations:

An organization that has held a paid subscription keeps a longer minimum window than its current plan alone would give, so billing and dispute history stays available after a cancellation.

Event Names Reference

Workspace Storage

Share Storage

Comments

Membership

Workspace Lifecycle

Share Lifecycle

File Share Lifecycle

Durable single-file File Share events. Category share, external visibility, member permission. Anchored to the owning workspace; the affected File Share id travels in the event data as file_share_id.

Cloud Sync Import Events

Category import, sub-category cloud_import, external visibility. Permission is SPLIT, and the split is the useful part: the three import_source_sync_* events are member, because whether a graft synced is operational status a workspace member can already infer from files appearing. Everything else in this family — including both provider_identity_* events, which carry the connected account’s email address — is admin, because whose cloud account is attached is a disclosure question rather than a status one. These are the connect / configure / sync half of cloud sync: linking a provider account, creating a source against it, and running the jobs that pull provider content in. Anchored to the owning workspace via profile_id.

Provider identity (a connected cloud account):

Import sources (a configured sync connection — one remote folder synced into a workspace):

Files arriving from a sync (these are workspace events, not import ones — listed here because cloud sync is what produces them):

Category workspace, sub-category transfer, external visibility, member permission. A large first sync produces many — roughly one per file. They are attributed to the owner of the connection that grafted the folder, so a synced file reads like an upload by that user rather than appearing authorless.

Treat them as AT MOST ONE BEST-EFFORT emission per detected add or content change — not as a guarantee of one-per-change. Two edges make the stronger reading wrong in opposite directions. Emission is best-effort: if the workspace cannot be resolved at emit time nothing is sent and nothing retries, so a real change can produce ZERO events. And change detection is deliberately conservative: when the stored content fingerprint is missing or unreadable the file is assumed changed, so an event can arrive for a file whose content did not actually change. Do not use these as a ledger of what changed — use them as a prompt to re-read, and let the folder listing be the truth.

There is NO delete counterpart. Nothing is emitted when a sync removes a file that disappeared at the provider. Do not infer from silence that a file still exists — re-read the folder to establish that. This is the single most important line in this section: an absent event here means “no signal”, never “no change”.

Import & discovery jobs (the job record behind a sync run, or behind a pre-source folder listing):

source_name is MUTABLE, CLIENT-SETTABLE display text, and it is never scrubbed. It starts as the remote folder or library path inside the user’s connected cloud account (e.g. Imported to Fastio/Images), but the update endpoint lets a caller change it afterwards, so it is untrusted input rather than a reliable provider identifier — do not key anything on it. On discovery events, before a source exists, it carries the provider name instead of a folder name. It is interpolated directly into the description of every event above except the two provider_identity_* events. identity_email on those two is the connected account’s real address. Both can name something outside the workspace’s own content, and neither is redacted before the event is persisted or read. Summarise, don’t relay verbatim — same guidance as the diagnostic-field note under Cloud Sync Write-Back below.

Cloud Sync Write-Back

Category import, sub-category cloud_import, external visibility, admin permission. Emitted when a local change is pushed back to the connected cloud provider. Anchored to the owning workspace.

Event data carries profile_id, source_id, node_id and wb_id (plus error_message on _failed). wb_id identifies the write-back itself and is the same across all four events for one push, so it is what correlates a _started with its eventual _completed, _failed or _conflict. node_id cannot do that job: a node edited twice produces two write-backs sharing one node id.

These events do NOT wake a client. They raise no realtime signal — a client must READ the events feed to see them and will not be nudged. This is deliberate: they fire per FILE, so a large sync would otherwise notify once per file.

Treat diagnostic fields as sensitive and untrusted. error_message is a coarse failure class, but diagnostic text in this family can carry provider-supplied content, including the names of files inside a user’s connected cloud account. Summarise it; never relay it verbatim into a chat, ticket, or commit message, and never parse it for control flow.

AI

Metadata

Field vocabulary merges

Category metadata, sub-category metadata, external visibility, member permission. Emitted when a workspace’s field vocabulary changes shape: one field is folded into another, so the folded name stops being its own entry and resolves to the surviving field from then on.

The event data carries workspace plus both NAMES — alias_field_name, the field that was retired, and canonical_field_name, the field it now resolves to. That pair is the point of the event: a consumer holding persisted selections that named the retired field can REWRITE them to the surviving name instead of dropping them, and a cached copy of the vocabulary can be corrected without re-reading the whole listing.

Nothing reaches anyone on its own when this fires — no email, no notification, no webhook, no realtime nudge. The fold is recorded and nothing more, so a consumer that wants to react to one must POLL GET /current/events/search/ for it. Do not wait to be woken; you will not be.

It fires only when a fold actually WRITES. A pre-flight of a merge emits nothing, and neither does a merge that finds the two fields already folded together — so receiving this event always means the vocabulary really changed. The converse does NOT hold. Emission is best-effort and nothing retries, so a real fold can produce no event at all. Do not treat the stream as a ledger of vocabulary changes: treat an event as a prompt to re-read the field vocabulary, and let that listing be the truth.

Templates and saved views (retired — no longer emitted)

Metadata templates have been replaced by the workspace field vocabulary, and per-user saved views have been folded into metadata filters. Both sets of endpoints are gone. These event types are retained only so historical activity stays readable; none of them is ever emitted now, so do not build a subscription or a workflow that waits on one.

Quick Shares (deprecated — see File Share Lifecycle)

QuickShare creation is deprecated in favor of the durable File Share; these events fire only for the draining QuickShare population. New single-file sharing emits the file_share_* events above.

Invitations

User

Organization

Billing

Event Search Examples

Recent comments in a workspace

GET /current/events/search/?workspace_id={id}&subcategory=comments

File uploads to a share in a date range

GET /current/events/search/?share_id={id}&event=share_storage_file_added&created-min=2025-12-01T06:00:00Z

Membership changes in an org

GET /current/events/search/?org_id={id}&subcategory=members

AI activity in a workspace

GET /current/events/search/?workspace_id={id}&category=ai

Unacknowledged events for a user

GET /current/events/search/?user_id={id}&acknowledged=false

Audit log events only

GET /current/events/search/?workspace_id={id}&visibility=external_audit_log&limit=100

Child events of a batch operation

GET /current/events/search/?parent_event_id=ancouywgcxiff7kpbijpl4ysgn43j&limit=100

Activity Polling

Long-poll endpoints for efficient change detection. The server holds the connection open and returns immediately when something changes, avoiding expensive resource polling.

Poll User Activity

GET /current/activity/poll/

Poll for activity updates on the current user's profile.

Auth: Required (JWT). Subject to global rate limiting; see the rate-limit section in the main reference. No credit consumption.

Query Parameters

ParameterTypeRequiredDefaultDescription
wait integer No 0 Long-poll timeout in seconds (0–95). Server holds connection open until update or timeout.
lastactivity string No Current time Only return activity newer than this timestamp. Micro-precision datetime (e.g., 2025-01-20 10:30:45.123456 UTC).
updated any No If present, only return activity fields updated since lastactivity
fields string No All fields Comma-delimited activity field names to check. Max 30 fields. Supports ID qualifier via colon (e.g., storage:12345).

Example Request

curl -X GET "https://api.fast.io/current/activity/poll/?wait=30&lastactivity=2025-01-20%2010:30:45.123456" \
  -H "Authorization: Bearer {jwt_token}"

Response (200 OK — activity found)

{
  "result": true,
  "results": 3,
  "activity": {
    "storage": "2025-01-20 10:30:45.123456 UTC",
    "members": "2025-01-20 09:15:22.654321 UTC",
    "settings": "2025-01-19 14:00:00.000000 UTC"
  },
  "lastactivity": "2025-01-20 10:30:45.123456 UTC"
}

Response (200 OK — no activity)

{
  "result": true,
  "results": 0,
  "activity": []
}

Response Fields

FieldTypeDescription
resultbooleantrue on success
resultsintegerNumber of activity fields returned
activityobjectMap of activity field names to micro-precision UTC timestamps
lastactivitystringMost recent timestamp; pass as lastactivity in next poll

Error Responses

Error CodeHTTP StatusDescription
1605 (Invalid Input)406Invalid profile ID format
1605 (Invalid Input)406User lacks permissions for the specified profile
1605 (Invalid Input)406Invalid field names or more than 30 fields
1665 (Object Init Failed)500Internal error
1650 (Authentication Invalid)401Missing or invalid JWT token

Poll Profile Activity

GET /current/activity/poll/{profile_id}/

Poll for activity updates on a specific workspace, share, org, or File Share.

Auth: Required (JWT). Same rate limits and parameters as Poll User Activity.

Path Parameters

ParameterTypeRequiredDescription
{profile_id} string Yes Profile ID to subscribe to (org, workspace, share, or File Share) — a 19-digit numeric profile ID; a File Share may also be addressed by its opaque id_alt. For upload progress, use your user ID.

Access Requirements

Profile TypePermission Required
User (self)Authenticated
OrganizationPERM_VIEW on the org
WorkspacePERM_VIEW on the workspace
ShareShare view permissions (canViewShareDetails)
File ShareThe same access decision as the File Share's public read surface (access tier + password + named grant). A signed-in recipient granted view/download/edit receives a recipient-scoped feed (comment activity plus a bare content/lifecycle nudge — never the owner's internal activity); an anonymous anyone-with-link visitor cannot poll.

Example Request

curl -X GET "https://api.fast.io/current/activity/poll/1234567890123456789/?wait=30&fields=storage,members&updated=1&lastactivity=2025-01-20%2010:30:45.123456" \
  -H "Authorization: Bearer {jwt_token}"

Response format is identical to Poll User Activity.


Polling Workflow

  1. Make initial poll request (no lastactivity parameter)
  2. Receive response with activity fields and lastactivity timestamp
  3. Process changes by fetching updated resources based on activity field names
  4. Make next poll with lastactivity from previous response
  5. Repeat — server returns immediately on change, or after wait seconds timeout

Activity Key Patterns

Key PatternWhat Changed
storage:{fileId}File added, updated, or removed
preview:{fileId}File preview/thumbnail is ready
metadata:{fileId}File's extracted metadata fields were written (use to refresh a single row during a template extraction)
ai_chat:{chatId}AI chat message updated
comments:{nodeId}Comment added or updated
member:{userId}Membership changed

Anti-Patterns

WebSocket (Real-Time)

Optional real-time delivery (~300ms latency vs ~1s for polling). Sends both activity messages (field names for change detection) and enriched event messages (full event details) via WebSocket. Backwards compatible — existing clients continue to work without changes.

WebSocket Auth

GET /current/websocket/auth/{profile_id}

Generate a WebSocket authentication JWT for a specific profile. User, organization, and workspace tokens are valid for 24 hours. Share and File Share tokens use a shorter TTL (30 minutes) — always check the expires_in field on every response and refresh before it expires. A File Share realtime channel is gated by the same access decision as its public read surface (access tier + password + named grant), the same requirement as its activity poll. A signed-in recipient granted view/download/edit can mint a token and receives a recipient-scoped feed — comment activity plus a bare content/lifecycle nudge, never the owner's internal activity; an anonymous anyone-with-link visitor cannot mint a realtime token.

Auth: Required (JWT). No credit consumption.

Path Parameters

ParameterTypeRequiredDescription
{profile_id} string Yes ID of the user, org, workspace, share, or file share to subscribe to — a 19-digit numeric ID; a File Share may also be addressed by its opaque id_alt

Example Request

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

Response (200 OK)

{
  "result": true,
  "expires_in": 86400,
  "auth_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Response Fields

FieldTypeDescription
resultbooleantrue on success
expires_inintegerEffective token lifetime in seconds (86400 = 24 hours for user/org/workspace; 1800 = 30 minutes for share and file-share)
auth_tokenstringSigned JWT with websocket scope, bound to the requested profile

Access Requirements

Profile TypePermission Required
User (self)None beyond authentication
OrganizationPERM_VIEW on the org
WorkspacePERM_VIEW on the workspace
SharecanViewShareDetails
File ShareThe same access decision as the File Share's public read surface (access tier + password + named grant), the same gate as its activity poll. A signed-in view/download/edit recipient mints a recipient-scoped token (comment activity plus a bare content/lifecycle nudge only); an anonymous anyone-with-link visitor cannot mint one.

Token Lifetime

The default expires_in for User, Organization, and Workspace tokens is 86400 (24 hours). Share and File Share tokens are shorter-lived (1800 seconds / 30 minutes) — always inspect the expires_in field on the response and refresh the token before it expires. These channels use a short TTL because the recipient's standing can change during a session, and the token freezes that standing at mint time: a share guest can be removed or have their access level downgraded, and a File Share recipient's grant or the file's access tier/password can change. The short TTL bounds how long an open connection can keep delivering events under a now-stale authorization; on reconnect the freshly minted token reflects the current standing.

Error Responses

Error CodeHTTP StatusDescription
1605 (Invalid Input)406No profile ID provided or invalid format
1605 (Invalid Input)406Profile type unsupported, not found, or user lacks permissions
1650 (Authentication Invalid)401Missing or invalid JWT, or internal JWT generation failure

WebSocket Connection

Connect to: wss://{host}/api/websocket/?{auth_token}

Where {auth_token} is the JWT returned from the auth endpoint above — it is the entire query string (no token= key, no other query parameters).

Message Types

The server pushes two types of JSON messages:

activity Messages

Indicate which resource categories changed. Use these to know what to re-fetch:

{
  "response": "activity",
  "activity": ["storage:2abc...", "preview:2abc..."]
}

The activity array contains the same activity key patterns as the polling endpoint.

event Messages

Sent alongside activity messages when structured event data is available. Provide full event details for immediate UI updates without a follow-up API call:

{
  "result": true,
  "response": "event",
  "time": "2026-03-22 14:30:45.123456 UTC",
  "timestamp": "1711123456.1234",
  "event": "workspace_storage_file_added",
  "category": "workspace",
  "sub_category": "storage",
  "object_id": "23s5ktto3hoomtz3fbgrmhurl2mi6",
  "calling_user": "1234567890123456789",
  "activity_field": "storage",
  "data": {
    "name": "report.pdf",
    "parent_node_id": "xyz789...",
    "size": 1048576
  }
}
event Message Fields
FieldTypeDescription
resultbooleanAlways true for event messages
responsestringAlways "event" for this message type
timestringServer send time (Y-m-d H:i:s.uuuuuu UTC, e.g. "2026-03-22 14:30:45.123456 UTC"). Added when the message is dispatched.
timestampstringEvent trigger time as a microtime float string (e.g., "1711123456.1234").
eventstringEvent name identifier (e.g., workspace_storage_file_added)
categorystringEvent category (e.g., workspace, share)
sub_categorystringEvent subcategory (e.g., storage, members)
object_idstringAffected object OpaqueId (file, folder, etc.)
calling_userstring19-digit numeric ID of the user who triggered the event
activity_fieldstringCorresponding activity field name (e.g., storage)
dataobjectEvent-specific details; shape varies by event type
Backwards Compatibility

activity messages are sent for every change a recipient is permitted to observe. event messages are supplementary — sent alongside activity messages when structured event data is available. Existing clients need no changes. Event payloads are kept under ~4KB.

Permission gating: Enriched event messages are only sent for member-level events. Admin and targeted events receive only the activity message.

Per-recipient scoping (share channels): On a share channel, each outgoing activity/event frame is scoped to the receiving guest's share access before it is delivered. A guest receives only the changes their access level permits them to observe — frames they may not see are suppressed, and content detail (file names, node identifiers, enriched data) is collapsed to a bare category or dropped for guests with restricted file visibility. Members and all non-share channels (user / organization / workspace) are unaffected and receive the full frame. Do not assume a share guest will observe every change on the channel, or that a delivered activity frame will always carry an enriched event companion.

Fallback

If the WebSocket connection drops, fall back to long-polling (GET /current/activity/poll/{profile_id}/). Activity polling returns field names and timestamps. To retrieve full event details after reconnection, use GET /current/events/search/ with a created-min filter.

Realtime Auth (Collaborative Rooms)

Separate from WebSocket activity channels, these endpoints provide authentication for collaborative editing rooms.

Generate Realtime Token

GET /current/realtime/auth/{room_id}

Generate a realtime JWT for a workspace or share collaborative room. Tokens are valid for 24 hours.

Auth: Required (JWT). Subject to global rate limiting; see the rate-limit section in the main reference. No credit consumption.

Path Parameters

ParameterTypeRequiredDescription
{room_id} string Yes 19-digit numeric ID of the workspace or share to join

Example Request

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

Response (200 OK)

{
  "result": true,
  "expires_in": 86400,
  "auth_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Access Requirements

Profile TypePermission Required
WorkspaceAt least PERM_VIEW
SharecanViewShareDetails + multiplayer enabled

Error Responses

Error CodeHTTP StatusDescription
1605 (Invalid Input)406Missing room ID
1605 (Invalid Input)406Room ID is not numeric
1605 (Invalid Input)406Room ID does not correspond to a workspace or share
1680 (Access Denied)401User lacks permissions on the room
1650 (Authentication Invalid)401Missing or invalid JWT, or internal JWT generation failure

Note: Only workspace and share profile types are accepted as room IDs.


Generate Realtime Note Token

GET /current/realtime/note-auth/{profile_id}/{note_id}

Generate a realtime token for collaborative editing of a single Note. The token is bound to the calling user, the note's workspace, and the note itself, and carries the caller's edit/view standing for the note. Tokens are valid for 900 seconds (15 minutes); refresh before expiry by calling the endpoint again, which re-resolves your current standing on the note.

Auth: Required (JWT). Subject to global rate limiting; see the rate-limit section in the main reference. No credit consumption.

Path Parameters

ParameterTypeRequiredDescription
{profile_id} string Yes 19-digit numeric ID of the workspace the note lives in
{note_id} string Yes ID of the Note to edit

Example Request

curl -X GET "https://api.fast.io/current/realtime/note-auth/{profile_id}/{note_id}" \
  -H "Authorization: Bearer {jwt_token}"

Response (200 OK)

{
  "result": true,
  "expires_in": 900,
  "auth_token": "{realtime_note_token}"
}

Response Fields

FieldTypeDescription
resultbooleantrue on success
expires_inintegerToken lifetime in seconds (900 = 15 minutes)
auth_tokenstringSigned realtime-note token, bound to the user, workspace, and note

Access Requirements

The token's granted capabilities are frozen at mint time from your current permission on the workspace:

Workspace PermissionGranted Capability
Edit (or higher)Read and edit the note
ViewRead the note only
Below ViewDenied

Because the capabilities are frozen for the token's lifetime, a permission change made after a token is issued takes effect on the next token refresh (at most ~15 minutes later).

Error Responses

Error CodeHTTP StatusDescription
1605 (Invalid Input)406Missing or invalid note ID, or the node is not a note
1609 (Not Found)404No such note exists, or the note is in the trash
1680 (Access Denied)401You lack permission on the workspace
1650 (Authentication Invalid)401Missing or invalid JWT, or internal token generation failure

Validate Realtime Token

GET /current/realtime/auth/validate/

Validate a realtime JWT and extract the room ID. Intended for backend services to verify tokens.

Auth: Bearer token in Authorization header (the realtime JWT to validate, not a user JWT).

Example Request

curl -X GET "https://api.fast.io/current/realtime/auth/validate/" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
  "result": true,
  "room_id": "1234567890123456789"
}

Response Fields

FieldTypeDescription
resultbooleantrue on success
room_idstringThe workspace or share profile ID the token is bound to

Error Responses

Error CodeHTTP StatusDescription
1650 (Authentication Invalid)401Missing Authorization header
1605 (Invalid Input)406Malformed Authorization header
1605 (Invalid Input)406Authorization header does not use Bearer scheme
1605 (Invalid Input)406Bearer keyword present but no token follows
1605 (Invalid Input)406Token is not valid JWT format
1680 (Access Denied)401Token signature verification or expiration check failed
1610 (Internal Error)500Token payload is malformed or missing required fields
1605 (Invalid Input)406Token scope is not realtime

Notes: Only validates tokens with realtime scope. WebSocket-scoped tokens are rejected. Validation is performed using the JWT alone.

Validate Realtime Note Token

GET /current/realtime/note-auth/validate/

Validate a realtime-note token (minted by GET /current/realtime/note-auth/{profile_id}/{note_id}) and read back the note, workspace, and permission it is bound to. Intended for the collaborative-editing backend to confirm a token on connect.

Auth: Bearer token in Authorization header (the realtime-note token to validate, not a user JWT).

Example Request

curl -X GET "https://api.fast.io/current/realtime/note-auth/validate/" \
  -H "Authorization: Bearer {realtime_note_token}"

Response (200 OK)

{
  "result": true,
  "profile": "1234567890123456789",
  "node": "2ik5q-a43cm-uixi2-van5r-3eolo-7mue",
  "perm": "edit",
  "file_share_id": null
}

Response Fields

FieldTypeDescription
resultbooleantrue on success
profilestringThe workspace profile ID the token is bound to
nodestringThe Note node ID the token is bound to
permstringThe frozen permission: "edit" (read and edit) or "view" (read only)
file_share_idstring|nullThe File Share ID the token was minted for when it is a File Share-surface note token; null for a workspace-native token. The realtime backend uses this to route note reads/saves to the File Share-specific endpoints rather than the workspace-native ones.

Error Responses

Error CodeHTTP StatusDescription
1650 (Authentication Invalid)401Missing Authorization header, or the token is invalid, expired, wrong-scope, wrong-audience, or malformed

Notes: Only validates tokens with the realtime-note scope; any other token is rejected. Validation is performed using the token alone — signature, expiry, scope, audience, and the perm/capability binding are all checked.

↑ Back to top