Events & Activity Event search, activity polling, WebSocket realtime.
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.
Events Search
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
/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 | Description |
|---|---|---|---|---|
| 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
| 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 attributed user, when one was recorded |
| 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
Reading the error tables: the four-digit
16xx/17xxvalues below are HTTP-status classes, noterror.code. Theerror.codea client actually receives is assigned per endpoint, so use the HTTP status as the gate and a documentederror.code— five or six digits, plus the9661-9669family — only as a refinement. A16xxvalue identifies the status class — useful for telling which kind of failure occurred — but comparing one againsterror.codewill never match. Five- and six-digit codes (and the9661-9669family) are realerror.codevalues. 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 Code | HTTP Status | Description |
|---|---|---|
1605 (Invalid Input) | 406 | No profile filter or parent_event_id provided |
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 from results.
Audit-log queries are gated. Requesting
visibility=external_audit_logrequires (1) anorg_id,workspace_id, orshare_idfilter — auser_id-only audit-log query is rejected — and (2) admin permission on that profile. Failing either returns1680 (Access Denied).
Summarize Events (AI)
/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 | Description |
|---|---|---|---|---|
| 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)
| Field | Type | Description |
|---|---|---|
| summary | object|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: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 Code | HTTP Status | Description |
|---|---|---|
1680 (Access Denied) | 401 | The org_id, workspace_id, or share_id filter names a profile you are not a member of |
1609 (Not Found) | 404 | The org_id, workspace_id, or share_id filter names a profile that does not exist |
1605 (Invalid Input) | 406 | The 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:
summaryisnullbut 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, andshare_idare 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 aparent_event_idquery) is charged to your own billing account. Where more than one filter is supplied, the one that selects the events (in the orderuser_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_idandshare_idare 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 withsummaryset tonull; it is never charged to a different account.The same audit-log gate as
/events/search/applies:visibility=external_audit_logrequires anorg_id/workspace_id/share_idcontext filter AND admin permission on that profile, otherwise1680 (Access Denied).
Event Details
/current/event/{event_id}/details/
Get full details for a single event.
Auth: Required (JWT). Default rate limiting. No credit consumption.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| {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
| 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) |
Acknowledge Event
/current/event/{event_id}/ack/
Acknowledge (mark as read) an event for the current user. Idempotent.
Auth: Required (JWT). Default rate limiting.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| {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 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 |
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.
| Level | Fields returned on each event (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 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 — and workspace-scoped file/folder activity. See below |
| Share | share | Share operations — and share-scoped file/folder activity. See below |
| AI | ai | AI/ML operations |
| Invitation | invitation | Invitation events |
email | Email-related events | |
| Billing | billing | Billing and subscription events |
| Metadata | metadata | Metadata operations |
| Apps | apps | Application/integration events |
| Node | node | AI indexing pipeline only — not file operations. See below |
| Server | server | Server/system-level events |
| Import | import | Import 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:
| Filter | Matches On | Answers |
|---|---|---|
| calling_user_id | the user who performed the action | “what did this person do” |
| user_id | the 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:
- Usually attributed — file and folder activity, including files landed by a scheduled sync, and connecting, updating or disconnecting a synced folder. Treat this as the common case, not a guarantee: the actor is omitted whenever it cannot be resolved — for instance once the connecting member has left the workspace, or when the same action completes from a background job rather than the request that asked for it.
- Never attributed — the lifecycle events for syncs, imports and write-backs (a sync starting, completing or failing; an import job; a write-back push). These carry no
calling_user_idat all, even though a specific user connected the folder or triggered the edit. - Connecting or disconnecting a storage provider is attributed from 2026-08-24, and the two differ:
provider_identity_created— always the person who connected the account, which is always its owner. No admin-on-behalf-of path exists here.provider_identity_revoked— the person who initiated it, which may be a workspace admin acting on another member’s connected account.user_idis the account’s owner;calling_useris whoever acted. Omitted entirely when the platform disconnects the account automatically because the owning member was removed from the workspace.
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
| 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 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 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:
- Export anything you need to keep. If your compliance process requires a longer archive than your plan retains, pull the events you care about through
/current/events/search/and store them yourself. - An empty result is not proof that nothing happened. A search over a period older than the retention window returns no events because the records are gone, not because there was no activity. Check
created-minagainst your plan’s window before treating a gap as meaningful.
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
workspace_storage_file_added— File uploadedworkspace_storage_file_deleted— File trashedworkspace_storage_file_moved— File movedworkspace_storage_file_copied— File copiedworkspace_storage_file_updated— File metadata updatedworkspace_storage_file_restored— File restored from trashworkspace_storage_file_version_restored— File version restoredworkspace_storage_folder_created— Folder createdworkspace_storage_folder_deleted— Folder trashedworkspace_storage_folder_moved— Folder movedworkspace_storage_download_token_created— Download token issuedworkspace_storage_zip_downloaded— ZIP download completedworkspace_storage_link_added— Link addedworkspace_storage_lock_overridden— A file's edit lock was taken over by another user with write access. Does not name the displaced holder
Share Storage
share_storage_file_added— File uploadedshare_storage_file_deleted— File trashedshare_storage_file_moved— File movedshare_storage_file_copied— File copiedshare_storage_file_updated— File metadata updatedshare_storage_file_restored— File restoredshare_storage_folder_created— Folder createdshare_storage_folder_deleted— Folder trashedshare_storage_folder_moved— Folder movedshare_storage_download_token_created— Download token issuedshare_storage_lock_overridden— A file's edit lock was taken over by another user with write access. Does not name the displaced holdershare_storage_zip_downloaded— ZIP download completed
Comments
comment_created— Comment createdcomment_updated— Comment updatedcomment_deleted— Comment deletedcomment_mentioned— User mentioned in comment (targeted permission)comment_replied— Reply to a commentcomment_reaction— Reaction added
Membership
added_member_to_org/removed_member_from_org— Org membershipadded_member_to_workspace/removed_member_from_workspace— Workspace membershipadded_member_to_share/removed_member_from_share— Share membershipmembership_updated— Permission changes
Workspace Lifecycle
workspace_created/workspace_updated/workspace_deletedworkspace_archived/workspace_unarchived
Share Lifecycle
share_created/share_updated/share_deletedshare_archived/share_unarchivedshare_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 createdfile_share_updated— A File Share's settings (title / access tier / password) were updatedfile_share_content_updated— The shared file's content was replaced (external edit / write-back)file_share_deleted— A File Share was deletedfile_share_access_granted— A per-user grant (view / download / edit) was added or raisedfile_share_access_revoked— A per-user grant was revoked
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):
provider_identity_created— A provider account (Dropbox / Box / OneDrive / Google Drive) finished OAuth connect, or was provisioned directly.identity_emailis the connected account’s own email address, interpolated into the description. From 2026-08-24 this event carriescalling_user. Unlike revocation there is no admin-on-behalf-of path here — a connection is always made by the person who will own it — socalling_useranduser_idare the same person. There is no automatic creation path either, so an absent actor on this event always means it predates that date.provider_identity_revoked— Revocation of a connected provider account was INITIATED — by its owner or a workspace admin, or automatically when the connecting member is removed from the workspace. From 2026-08-24 this event carriescalling_userwhen a person initiated it, and OMITS it for the automatic membership-cleanup path. Because the endpoint admits a workspace admin as well as the owner, the actor may be someone OTHER than the account’s owner —user_idis the owner,calling_useris whoever acted. Nothing in the description names an actor, so read the field, not the text. ⚠ Events recorded BEFORE that date carry no actor regardless of who acted, so on an older row an absent actor means “not recorded”, not “automatic”. Treat it as the START of revocation, not proof it finished. Teardown at the provider is ASYNCHRONOUS and its ordering against this event is not fixed — it may still be pending, or may already have completed, when you see this. The identity’s own status is the authoritative signal; do not infer completion either way from the event.
Import sources (a configured sync connection — one remote folder synced into a workspace):
import_source_created— A source was created against a connected identity.import_source_updated— A source’s settings changed — from the update endpoint, or automatically when its identity is revoked or its connecting member’s workspace membership ends. The two automatic paths differ in actor and you cannot treat them alike: an identity revocation carries the acting user through, while membership cleanup is system-initiated and sends an EMPTYcalling_user. So an empty actor means “the platform did this”, not “nobody did this”.import_source_deleted— A source was deleted.import_source_sync_started— A sync run started for a source.import_source_sync_completed— A sync run finished;file_countandtotal_sizedescribe the source’s post-run state.import_source_sync_failed— A source entered the error state. Usually a sync run failed, but it also fires when the platform’s periodic recovery moves a source that was stranded mid-operation — so it means “this source is now in error”, not necessarily “a run was attempted and failed”.error_messageis filtered before it is stored, but the filter is not a guarantee: text supplied by the cloud provider can reach it verbatim.import_source_disconnected— A source was disconnected. For a source with ≤ 1000 files this fires synchronously from the disconnect endpoint with the requestingcalling_user; for a larger source it fires later from the async disconnect job withcalling_user: ''.
Files arriving from a sync (these are workspace events, not import ones — listed here because cloud sync is what produces them):
workspace_storage_file_sync_added— A file was added to the workspace by a cloud-folder sync.workspace_storage_file_sync_updated— A synced file’s content changed at the provider and the workspace copy was refreshed.
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):
import_job_started— A job started.job_typeisfull_sync,incremental, ordiscovery(listing a connected identity’s shared folders before any source exists — no source row yet, sosource_nameis the provider name rather than a folder name, and there is no pairedimport_source_sync_started). Does not fire fordisconnect-type jobs.import_job_completed— A job completed;files_added/files_updated/files_deletedare per-job counts.import_job_failed— A job failed;error_messagecarries the same caveat asimport_source_sync_failed— filtered, but able to carry provider-supplied text.
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.
import_writeback_started— A write-back to the provider was queued for a nodeimport_writeback_completed— The local change was successfully written back to the providerimport_writeback_failed— The write-back failed permanently;error_messagecarries a coarse failure classimport_writeback_conflict— The remote object changed since the local edit began, so the push was not applied
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
ai_chat_created/ai_chat_updated/ai_chat_deleted— AI agent conversation lifecycleai_chat_new_message— New message in an AI agent conversationai_chat_published— AI agent conversation published (chat publish is currently disabled platform-wide —capabilities.can_publish_agent_chatisfalse; fires only for chats published before the disable)node_ai_summary_created— AI summary generated for a fileworkspace_ai_share_created— AI share created in workspace
Metadata
metadata_kv_update/metadata_kv_delete/metadata_kv_extractmetadata_fact_update— A file’s extracted metadata was updated. Emitted by the machine-extraction write path, so it fires per file across an extraction runmetadata_field_merged— One field was folded into another in a workspace’s field vocabulary
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.
metadata_template_update/metadata_template_delete/metadata_template_selectmetadata_view_create/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_deletedworkspace_quickshare_file_downloaded/workspace_quickshare_file_previewed
Invitations
invitation_email_sent/invitation_accepted/invitation_declined
User
user_created/user_updated/user_deleteduser_email_reset/user_asset_updated
Organization
org_created/org_updated/org_closed
Billing
subscription_created— fires when a new subscription is initiatedsubscription_cancel_scheduled— fires when a customer schedules cancellation; the subscription remains active untilcancel_atsubscription_cancelled— fires when the subscription is actually terminated by the payment provider (atcancel_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=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
/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
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| 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
| 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 |
Poll Profile Activity
/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
| Parameter | Type | Required | Description |
|---|---|---|---|
| {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 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. |
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
- Make initial poll request (no
lastactivityparameter) - Receive response with
activityfields andlastactivitytimestamp - Process changes by fetching updated resources based on activity field names
- Make next poll with
lastactivityfrom previous response - Repeat — server returns immediately on change, or after
waitseconds 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_idsince 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.
WebSocket Auth
/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, 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
| 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. |
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).
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
| Field | Type | Description |
|---|---|---|
| result | boolean | Always true for event messages |
| response | string | Always "event" for this message type |
| time | string | Server 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. |
| timestamp | string | Event trigger time as a microtime float string (e.g., "1711123456.1234"). |
| event | string | Event name identifier (e.g., workspace_storage_file_added) |
| category | string | Event category (e.g., workspace, share) |
| sub_category | string | Event subcategory (e.g., storage, members) |
| object_id | string | Affected object OpaqueId (file, folder, etc.) |
| calling_user | string | 19-digit numeric ID of the user who triggered the event |
| activity_field | string | Corresponding activity field name (e.g., storage) |
| data | object | Event-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
/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 |
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 Type | Permission Required |
|---|---|
| Workspace | At least PERM_VIEW |
| Share | canViewShareDetails + multiplayer enabled |
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 |
Note: Only workspace and share profile types are accepted as room IDs.
Generate Realtime Note Token
/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 |
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
| 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 |
Validate Realtime Token
/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
| 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
realtimescope. WebSocket-scoped tokens are rejected. Validation is performed using the JWT alone.
Validate Realtime Note Token
/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
| 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|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 |
↑ Back to topNotes: Only validates tokens with the
realtime-notescope; any other token is rejected. Validation is performed using the token alone — signature, expiry, scope, audience, and the perm/capability binding are all checked.