> Part of the Fastio API Reference. Overview: https://api.fast.io/current/llms/ # Events, Activity & Realtime Base URL: `https://api.fast.io/current/` Auth: All endpoints require `Authorization: Bearer {jwt_token}` unless noted. Response format: JSON (standard envelope with `result`, `status`, and data fields). --- ## Events Search Search and filter the event log. Events capture every action in the system -- file operations, membership changes, comments, AI activity, billing, workflow, and more. --- ### `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:** | Parameter | Type | Required | Default | Constraints | Description | |-----------|------|----------|---------|-------------|-------------| | `user_id` | string | Conditional | - | 19-digit numeric ID | 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 | - | 19-digit numeric ID | Filter by organization profile ID | | `workspace_id` | string | Conditional | - | 19-digit numeric ID | Filter by workspace profile ID | | `share_id` | string | Conditional | - | 19-digit numeric ID | Filter by share profile ID | | `parent_event_id` | string | Conditional | - | Alphanumeric OpaqueId | Filter by parent event ID for serial/batch events. Cannot combine with filters other than `acknowledged`, `limit`, `offset`. | | `event` | string | No | - | Max 100 characters | Filter by specific event name (e.g., `workspace_storage_file_added`) | | `category` | string | No | - | See Event Categories | Filter by event category | | `subcategory` | string | No | - | See Event Subcategories | Filter by event subcategory | | `calling_user_id` | string | No | - | 19-digit numeric ID | Filter by the user who triggered the event | | `object_id` | string | No | - | Alphanumeric OpaqueId | Filter by related object ID (file, folder, etc.) | | `acknowledged` | string | No | - | `"true"` or `"false"` | Filter by acknowledgment status | | `visibility` | string | No | All non-internal | `"external_audit_log"` or `"external"` | Filter by event visibility level | | `created-min` | string | No | - | Accepts ISO 8601 (e.g., `2025-12-01T06:00:00Z`) or `YYYY-MM-DD HH:MM:SS` format | Lower bound for event creation time | | `created-max` | string | No | - | Same format as `created-min`; must be > `created-min` | Upper bound for event creation time | | `limit` | integer | No | `100` | 1-250 | Maximum number of results | | `offset` | integer | No | `0` | 0+ | Number of results to skip for pagination | | `output` | string | No | - | Comma-separated tokens (e.g. `terse`, `standard`, `full`) | Select the response shape. See "Compact Responses" below for the three detail levels and which event fields appear in each. | **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. **curl Example:** ```bash 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):** ```json { "result": true, "events": [ { "event_id": "abc123xyz789def456ghi789jkl01", "created": "2025-01-20 10:30:45 UTC", "acknowledged": false, "event": "workspace_storage_file_added", "category": "workspace", "sub_category": "storage", "object_id": "def456ghi789jkl012mno345pqr67", "calling_user": "9876543210987654321", "calling_user_name": "Jane Smith", "org_id": "1111111111111111111", "workspace_id": "1234567890123456789", "filename": "quarterly_report.pdf", "file_size": 2485760 } ] } ``` **Response Fields:** | Field | Type | Description | |-------|------|-------------| | `result` | boolean | `true` on success | | `events` | array | Array of event objects | | `events[].event_id` | string | Unique event identifier (alphanumeric OpaqueId) | | `events[].created` | string | Event timestamp (`Y-m-d H:i:s UTC`) | | `events[].acknowledged` | boolean | Whether the current user has acknowledged this event | | `events[].event` | string | Event name identifier (e.g., `workspace_storage_file_added`) | | `events[].category` | string | Event category name | | `events[].sub_category` | string | Event subcategory name | | `events[].object_id` | string | Related object OpaqueId (if applicable) | | `events[].calling_user` | string | 19-digit numeric ID of the triggering user | | `events[].calling_user_name` | string | Display name of the triggering user | | `events[].org_id` | string | Organization ID context (if applicable) | | `events[].workspace_id` | string | Workspace ID context (if applicable) | | `events[].share_id` | string | Share ID context (if applicable) | | `events[].user_id` | string | Target 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:** | Error Code | HTTP Status | Description | |------------|-------------|-------------| | `1605 (Invalid Input)` | 406 | No profile filter or `parent_event_id` provided ("Event profile was missing.") | | `1605 (Invalid Input)` | 406 | `created-min` is greater than `created-max` | | `1605 (Invalid Input)` | 406 | `parent_event_id` combined with disallowed filters | | `1605 (Invalid Input)` | 406 | Invalid datetime format for `created-min` or `created-max` | | `1680 (Access Denied)` | 401 | Token scope does not include the requested profile, or the audit-log gate is not satisfied (see Notes) | | `1600 (Query Error)` | 500 | Internal error during event search | | `1650 (Authentication Invalid)` | 401 | Missing or invalid JWT token | | `1651 (Invalid Request Type)` | 405 | Wrong 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. - **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)`. --- ### `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:** | Parameter | Type | Required | Default | Constraints | Description | |-----------|------|----------|---------|-------------|-------------| | `user_context` | string | No | `""` | Max 64 chars; letters, numbers, spaces, `. , ! ? ' -` only | Focus guidance for the AI summary (e.g., `"Focus on uploads"`) | All other parameters are identical to `GET /current/events/search/`. **curl Example:** ```bash 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):** ```json { "result": true, "summary": { "text": "@[user:9876543210987654321:Jane Smith] uploaded 12 files to the project folder, including quarterly reports and design assets. @[user:5555555555555555555:John Doe] added 3 new members to the workspace.", "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):** | Field | Type | Description | |-------|------|-------------| | `summary` | object or null | AI-generated summary, or `null` if no events or generation failed | | `summary.text` | string | Natural language summary with `@[type:ID:name]` mention pills | | `summary.metrics.total_events` | integer | Total events summarized | | `summary.metrics.unique_actors` | integer | Distinct users who triggered events | | `summary.metrics.date_range.start` | string | Earliest event timestamp (`Y-m-d H:i:s UTC`) | | `summary.metrics.date_range.end` | string | Most recent event timestamp (`Y-m-d H:i:s UTC`) | | `summary.metrics.categories` | object | Map of category names to event counts | **Summary Mention Pill Formats:** | Entity | Format | Example | |--------|--------|---------| | User | `@[user:USER_ID:Display Name]` | `@[user:9876543210987654321:Jane Smith]` | | File | `@[file:FILE_ID:filename.ext]` | `@[file:abc123xyz789def456ghi789jkl01:report.pdf]` | | Folder | `@[folder:FOLDER_ID:foldername]` | `@[folder:def456ghi789jkl012mno345pqr67: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. Summary generation failures are non-fatal: `summary` is `null` but events are still returned. **Notes:** - Requires a billable organization for AI token billing (resolved from profile context). - AI token consumption is billed to the associated organization. - 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)`. --- ### `GET /current/event/{event_id}/details/` Get full details for a single event. **Auth:** Required (JWT). Default rate limiting. **Path Parameters:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `{event_id}` | string | Yes | Alphanumeric OpaqueId of the event | **curl Example:** ```bash curl -X GET "https://api.fast.io/current/event/abc123xyz789def456ghi789jkl01/details/" \ -H "Authorization: Bearer {jwt_token}" ``` **Response (200 OK):** ```json { "result": true, "event": { "event_id": "abc123xyz789def456ghi789jkl01", "created": "2025-01-20 10:30:45 UTC", "acknowledged": false, "event": "workspace_storage_file_added", "category": "workspace", "sub_category": "storage", "object_id": "def456ghi789jkl012mno345pqr67", "calling_user": "9876543210987654321", "calling_user_name": "Jane Smith", "org_id": "1111111111111111111", "workspace_id": "1234567890123456789", "filename": "quarterly_report.pdf", "file_size": 2485760 } } ``` **Response Fields:** | Field | Type | Description | |-------|------|-------------| | `result` | boolean | `true` on success | | `event` | object | Full event object (same fields as event search results) | **Access Rules:** | Condition | Access | |-----------|--------| | User is the `calling_user` | Granted | | User is the `event_user` (target) | Granted | | Event has `targeted` permission and user is neither target nor caller | Denied | | Event is `internal` visibility | Always denied | | User has appropriate profile-level permissions | Granted based on permission level | **Error Responses:** | Error Code | HTTP Status | Description | |------------|-------------|-------------| | `1605 (Invalid Input)` | 406 | Event ID missing, empty, or not a valid OpaqueId | | `1609 (Not Found)` | 404 | No event exists with the provided ID | | `1605 (Invalid Input)` | 406 | Event has `internal` visibility | | `1605 (Invalid Input)` | 406 | User lacks permission (targeted event) | | `1650 (Authentication Invalid)` | 401 | Missing or invalid JWT token | | `1651 (Invalid Request Type)` | 405 | Wrong HTTP method (only GET accepted) | --- ### `POST /current/event/{event_id}/ack/` Acknowledge (mark as read) an event for the current user. Idempotent: acknowledging an already-acknowledged event succeeds silently. **Auth:** Required (JWT). Default rate limiting. **Path Parameters:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `{event_id}` | string | Yes | Alphanumeric OpaqueId of the event to acknowledge | **curl Example:** ```bash curl -X POST "https://api.fast.io/current/event/abc123xyz789def456ghi789jkl01/ack/" \ -H "Authorization: Bearer {jwt_token}" ``` **Response (200 OK):** ```json { "result": true } ``` **Error Responses:** | Error Code | HTTP Status | Description | |------------|-------------|-------------| | `1605 (Invalid Input)` | 406 | Event ID missing or invalid | | `1609 (Not Found)` | 404 | Event not found | | `1605 (Invalid Input)` | 406 | Event has `internal` visibility | | `1605 (Invalid Input)` | 406 | User lacks permission (targeted event) | | `1664 (Datastore Error)` | 500 | Failed to persist the acknowledgment | | `1650 (Authentication Invalid)` | 401 | Missing or invalid JWT token | **Notes:** - 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. | Level | Fields returned (cumulative) | |-------|------------------------------| | `terse` | `event_id`, `event`, `category`, `object_id`, `created` | | `standard` | terse + `sub_category`, `calling_user`, `org_id`, `workspace_id`, `user_id`, `share_id`, `acknowledged`, `template` (containing `description` + `params`), `severity`, `visibility`, `notification` | | `full` | standard + `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 in `llms.txt` for the full contract. --- ## Event Categories | Category | API Value | Description | |----------|-----------|-------------| | Upload | `upload` | File upload operations | | User | `user` | User account events | | Organization | `org` | Organization events | | Workspace | `workspace` | Workspace operations | | Share | `share` | Share operations | | AI | `ai` | AI/ML operations | | Invitation | `invitation` | Invitation events | | Email | `email` | Email-related events | | Billing | `billing` | Billing and subscription events | | Metadata | `metadata` | Metadata operations | | Apps | `apps` | Application/integration events | | Node | `node` | Node-level events (individual file/folder objects) | | Server | `server` | Server/system-level events | | Import | `import` | Import operations | --- ## Event Subcategories | Subcategory | API Value | Description | |-------------|-----------|-------------| | Storage | `storage` | File and folder operations | | Comments | `comments` | Comment activity | | Members | `members` | Membership changes | | Lifecycle | `lifecycle` | Create, update, delete, archive events | | Settings | `settings` | Configuration changes | | Security | `security` | Security-related events | | Authentication | `authentication` | Login and auth events | | AI | `ai` | AI processing events | | Invitations | `invitations` | Invitation management | | Billing | `billing` | Subscription and payment | | Assets | `assets` | Asset (avatar, branding) updates | | Upload | `upload` | Upload events | | Transfer | `transfer` | Ownership transfer events | | Import/Export | `import_export` | Import/export operations | | Quick Share | `quickshare` | Quick share events | | Metadata | `metadata` | Metadata operations | | API | `api` | API-related events | | Archive | `archive` | Archive operations | | Email | `email` | Email events | | Render | `render` | Preview/thumbnail rendering events | | Cloud Import | `cloud_import` | Cloud import operations | --- ## Event Visibility Levels | Visibility | API Value | Description | |------------|-----------|-------------| | Internal | `internal` | System events. Never accessible via API. | | Audit Log | `external_audit_log` | Audit/compliance events. Targeted permission checks bypassed for admins. | | External | `external` | Standard user-facing events. | Default (no `visibility` parameter): returns both `external_audit_log` and `external` events, excludes `internal`. --- ## Event Permission Levels | Permission | Description | |------------|-------------| | `member` | Any member of the profile can view | | `admin` | Only admins of the profile can view | | `targeted` | Only 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 `/events/search/`). --- ## Event Names Reference ### Workspace Storage - `workspace_storage_file_added` -- File uploaded - `workspace_storage_file_deleted` -- File trashed - `workspace_storage_file_moved` -- File moved - `workspace_storage_file_copied` -- File copied - `workspace_storage_file_updated` -- File metadata updated - `workspace_storage_file_restored` -- File restored from trash - `workspace_storage_file_version_restored` -- File version restored - `workspace_storage_folder_created` -- Folder created - `workspace_storage_folder_deleted` -- Folder trashed - `workspace_storage_folder_moved` -- Folder moved - `workspace_storage_download_token_created` -- Download token issued - `workspace_storage_zip_downloaded` -- ZIP download completed - `workspace_storage_link_added` -- Link added ### Share Storage - `share_storage_file_added` -- File uploaded - `share_storage_file_deleted` -- File trashed - `share_storage_file_moved` -- File moved - `share_storage_file_copied` -- File copied - `share_storage_file_updated` -- File metadata updated - `share_storage_file_restored` -- File restored - `share_storage_folder_created` -- Folder created - `share_storage_folder_deleted` -- Folder trashed - `share_storage_folder_moved` -- Folder moved - `share_storage_download_token_created` -- Download token issued - `share_storage_zip_downloaded` -- ZIP download completed ### Comments - `comment_created` -- Comment created - `comment_updated` -- Comment updated - `comment_deleted` -- Comment deleted - `comment_mentioned` -- User mentioned in comment (targeted permission) - `comment_replied` -- Reply to a comment - `comment_reaction` -- Reaction added ### Membership - `added_member_to_org` / `removed_member_from_org` -- Org membership - `added_member_to_workspace` / `removed_member_from_workspace` -- Workspace membership - `added_member_to_share` / `removed_member_from_share` -- Share membership - `membership_updated` -- Permission changes ### Workspace Lifecycle - `workspace_created` / `workspace_updated` / `workspace_deleted` - `workspace_archived` / `workspace_unarchived` ### Share Lifecycle - `share_created` / `share_updated` / `share_deleted` - `share_archived` / `share_unarchived` - `share_imported_to_workspace` -- Share imported into workspace ### 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`. - `file_share_created` -- A durable File Share was created - `file_share_updated` -- A File Share's settings (title / access tier / password) were updated - `file_share_content_updated` -- The shared file's content was replaced (external edit / write-back) - `file_share_deleted` -- A File Share was deleted - `file_share_access_granted` -- A per-user grant (view / download / edit) was added or raised - `file_share_access_revoked` -- A per-user grant was revoked ### AI - `ai_chat_created` / `ai_chat_updated` / `ai_chat_deleted` -- AI agent conversation lifecycle - `ai_chat_new_message` -- New message in an AI agent conversation - `ai_chat_published` -- AI agent conversation published (chat publish is currently disabled platform-wide -- `capabilities.can_publish_agent_chat` is `false`; fires only for chats published before the disable) - `node_ai_summary_created` -- AI summary generated for a file - `workspace_ai_share_created` -- AI share created in workspace ### Metadata - `metadata_kv_update` / `metadata_kv_delete` / `metadata_kv_extract` - `metadata_template_update` / `metadata_template_delete` / `metadata_template_select` - `metadata_view_update` / `metadata_view_delete` ### 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. - `workspace_quickshare_created` / `workspace_quickshare_updated` / `workspace_quickshare_deleted` - `workspace_quickshare_file_downloaded` / `workspace_quickshare_file_previewed` ### Invitations - `invitation_email_sent` / `invitation_accepted` / `invitation_declined` ### User - `user_created` / `user_updated` / `user_deleted` - `user_email_reset` / `user_asset_updated` ### Organization - `org_created` / `org_updated` / `org_closed` ### Billing - `subscription_created` — fires when a new subscription is initiated - `subscription_cancel_scheduled` — fires when a customer schedules cancellation; the subscription remains active until `cancel_at` - `subscription_cancelled` — fires when the subscription is actually terminated by the payment provider (at `cancel_at`) - `billing_free_trial_ended` --- ## 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=abc123xyz789def456ghi789jkl01&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. --- ### `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. **Query Parameters:** | Parameter | Type | Required | Default | Constraints | Description | |-----------|------|----------|---------|-------------|-------------| | `wait` | integer | No | `0` | 0-95 | Long-poll timeout in seconds. Server holds connection open until update or timeout. | | `lastactivity` | string | No | Current time | Micro-precision datetime (e.g., `2025-01-20 10:30:45.123456 UTC`) | Only return activity newer than this timestamp | | `updated` | any | No | - | Any value = enabled | If present, only return activity fields updated since `lastactivity` | | `fields` | string | No | All fields | Comma-delimited; max 30 fields | Activity field names to check. Supports ID qualifier via colon (e.g., `storage:12345`). | **curl Example:** ```bash 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):** ```json { "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):** ```json { "result": true, "results": 0, "activity": [] } ``` **Response Fields:** | Field | Type | Description | |-------|------|-------------| | `result` | boolean | `true` on success | | `results` | integer | Number of activity fields returned | | `activity` | object | Map of activity field names to micro-precision UTC timestamps | | `lastactivity` | string | Most recent timestamp; pass as `lastactivity` in next poll | **Error Responses:** | Error Code | HTTP Status | Description | |------------|-------------|-------------| | `1605 (Invalid Input)` | 406 | Invalid profile ID format | | `1605 (Invalid Input)` | 406 | User lacks permissions for the specified profile | | `1605 (Invalid Input)` | 406 | Invalid field names or more than 30 fields | | `1665 (Object Init Failed)` | 500 | Internal error | | `1650 (Authentication Invalid)` | 401 | Missing or invalid JWT token | --- ### `GET /current/activity/poll/{profile_id}/` Poll for activity updates on a specific workspace, share, org, File Share, or Sign Envelope. **Auth:** Required (JWT). Same rate limits and parameters as `GET /current/activity/poll/`. **Path Parameters:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `{profile_id}` | string | Yes | Profile ID to subscribe to (org, workspace, share, File Share, or Sign Envelope) — 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 Type | Permission Required | |--------------|-------------------| | User (self) | Authenticated | | Organization | `PERM_VIEW` on the org | | Workspace | `PERM_VIEW` on the workspace | | Share | Share view permissions (`canViewShareDetails`) | | File Share | The 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. | | Sign Envelope | The e-sign creator-side realtime channel. Requires view access to the sign envelope. | **curl Example:** ```bash 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 `GET /current/activity/poll/`. --- ### 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 Pattern | What 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 - **Do NOT** loop on resource detail endpoints to wait for previews or processing. - **Instead**, poll on the workspace/share and watch for the relevant activity key. - **For uploads**, use your user ID as the `profile_id` since upload progress is tied to the user, not a workspace. --- ## 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. --- ### `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:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `{profile_id}` | string | Yes | ID of the user, org, workspace, share, file share, or sign envelope to subscribe to — a 19-digit numeric ID; a **File Share may also be addressed by its opaque `id_alt`** | **curl Example:** ```bash curl -X GET "https://api.fast.io/current/websocket/auth/1234567890123456789" \ -H "Authorization: Bearer {jwt_token}" ``` **Response (200 OK):** ```json { "result": true, "expires_in": 86400, "auth_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." } ``` **Response Fields:** | Field | Type | Description | |-------|------|-------------| | `result` | boolean | `true` on success | | `expires_in` | integer | Effective token lifetime in seconds (86400 = 24 hours for user/org/workspace; 1800 = 30 minutes for share and file-share) | | `auth_token` | string | Signed JWT with `websocket` scope, bound to the requested profile | **Access Requirements:** | Profile Type | Permission Required | |--------------|-------------------| | User (self) | None beyond authentication | | Organization | `PERM_VIEW` on the org | | Workspace | `PERM_VIEW` on the workspace | | Share | `canViewShareDetails` | | File Share | The 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. | | Sign Envelope | View access to the sign envelope (the e-sign creator-side realtime channel) | **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 Code | HTTP Status | Description | |------------|-------------|-------------| | `1605 (Invalid Input)` | 406 | No profile ID provided or invalid format | | `1605 (Invalid Input)` | 406 | Profile type unsupported, not found, or user lacks permissions | | `1650 (Authentication Invalid)` | 401 | Missing 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). ### WebSocket 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: ```json { "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: ```json { "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": "abc123def456ghi789jkl012mno34", "calling_user": "1234567890123456789", "activity_field": "storage", "data": { "name": "report.pdf", "parent_node_id": "xyz789...", "size": 1048576 } } ``` **`event` message fields:** `result` (boolean, always true), `response` (always `"event"`), `time` (server send time, `YYYY-MM-DD HH:MM:SS.ffffff UTC`, e.g. `"2026-03-22 14:30:45.123456 UTC"`), `timestamp` (event trigger time as microtime float string, e.g. `"1711123456.1234"`), `event` (event name), `category`, `sub_category`, `object_id` (affected object OpaqueId), `calling_user` (actor's 19-digit ID), `activity_field` (corresponding activity field name), `data` (event-specific details, shape varies by event type). **Backwards compatible:** `activity` messages are sent for every change a recipient is permitted to observe. `event` messages are supplementary. Existing clients need no changes. Payload 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. ### WebSocket 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. --- ### `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:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `{room_id}` | string | Yes | 19-digit numeric ID of the workspace or share to join | **curl Example:** ```bash curl -X GET "https://api.fast.io/current/realtime/auth/1234567890123456789" \ -H "Authorization: Bearer {jwt_token}" ``` **Response (200 OK):** ```json { "result": true, "expires_in": 86400, "auth_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." } ``` **Response Fields:** | Field | Type | Description | |-------|------|-------------| | `result` | boolean | `true` on success | | `expires_in` | integer | Token lifetime in seconds (86400 = 24 hours) | | `auth_token` | string | Signed JWT with `realtime` scope, bound to the requested room | **Access Requirements:** | Profile Type | Permission Required | |--------------|-------------------| | Workspace | At least `PERM_VIEW` | | Share | `canViewShareDetails` + multiplayer enabled (multiplayer status is automatically determined based on share configuration -- not directly togglable via API) | **Error Responses:** | Error Code | HTTP Status | Description | |------------|-------------|-------------| | `1605 (Invalid Input)` | 406 | Missing room ID | | `1605 (Invalid Input)` | 406 | Room ID is not numeric | | `1605 (Invalid Input)` | 406 | Room ID does not correspond to a workspace or share | | `1680 (Access Denied)` | 401 | User lacks permissions on the room | | `1650 (Authentication Invalid)` | 401 | Missing or invalid JWT, or internal JWT generation failure | **Notes:** - Only workspace and share profile types are accepted as room IDs. --- ### `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:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `{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 | **curl Example:** ```bash curl -X GET "https://api.fast.io/current/realtime/note-auth/{profile_id}/{note_id}" \ -H "Authorization: Bearer {jwt_token}" ``` **Response (200 OK):** ```json { "result": true, "expires_in": 900, "auth_token": "{realtime_note_token}" } ``` **Response Fields:** | Field | Type | Description | |-------|------|-------------| | `result` | boolean | `true` on success | | `expires_in` | integer | Token lifetime in seconds (900 = 15 minutes) | | `auth_token` | string | Signed 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 permission | Granted capability | |----------------------|--------------------| | Edit (or higher) | Read and edit the note | | View | Read the note only | | Below View | Denied | 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 Code | HTTP Status | Description | |------------|-------------|-------------| | `1605 (Invalid Input)` | 406 | Missing or invalid note ID, or the node is not a note | | `1609 (Not Found)` | 404 | No such note exists, or the note is in the trash | | `1680 (Access Denied)` | 401 | You lack permission on the workspace | | `1650 (Authentication Invalid)` | 401 | Missing or invalid JWT, or internal token generation failure | --- ### `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). **curl Example:** ```bash curl -X GET "https://api.fast.io/current/realtime/auth/validate/" \ -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." ``` **Response (200 OK):** ```json { "result": true, "room_id": "1234567890123456789" } ``` **Response Fields:** | Field | Type | Description | |-------|------|-------------| | `result` | boolean | `true` on success | | `room_id` | string | The workspace or share profile ID the token is bound to | **Error Responses:** | Error Code | HTTP Status | Description | |------------|-------------|-------------| | `1650 (Authentication Invalid)` | 401 | Missing Authorization header | | `1605 (Invalid Input)` | 406 | Malformed Authorization header | | `1605 (Invalid Input)` | 406 | Authorization header does not use Bearer scheme | | `1605 (Invalid Input)` | 406 | Bearer keyword present but no token follows | | `1605 (Invalid Input)` | 406 | Token is not valid JWT format | | `1680 (Access Denied)` | 401 | Token signature verification or expiration check failed | | `1610 (Internal Error)` | 500 | Token payload is malformed or missing required fields | | `1605 (Invalid Input)` | 406 | Token scope is not `realtime` | **Notes:** - Only validates tokens with `realtime` scope. WebSocket-scoped tokens are rejected. - Validation is performed using the JWT alone. --- ### `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). **curl Example:** ```bash curl -X GET "https://api.fast.io/current/realtime/note-auth/validate/" \ -H "Authorization: Bearer {realtime_note_token}" ``` **Response (200 OK):** ```json { "result": true, "profile": "1234567890123456789", "node": "n5mno-pqrst-uvwxy-z1234-abcde-fghi", "perm": "edit", "file_share_id": null } ``` **Response Fields:** | Field | Type | Description | |-------|------|-------------| | `result` | boolean | `true` on success | | `profile` | string | The workspace profile ID the token is bound to | | `node` | string | The Note node ID the token is bound to | | `perm` | string | The frozen permission: `"edit"` (read and edit) or `"view"` (read only) | | `file_share_id` | string or null | The 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 Code | HTTP Status | Description | |------------|-------------|-------------| | `1650 (Authentication Invalid)` | 401 | Missing 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.