Skip to main content

Workspaces lakehouse

The lakehouse collects usage information, document audit trails, Logic Pilot traces, and feedback from every workspace. This page shows where that data is saved, and which program reads it.

Follow a batch of data from the workspaces to the place that keeps it. Choose a type of data. The glowing cubes show the route.

Workspace AWorkspace BWorkspace CCloudflare workerCloudflare PipelinesD1 databaseSTOREDNot used for this dataIceberg on R2STOREDdocument_audit.v1R2 workspace-events
Step 1 of 4

About 7 seconds after a burst of changes, each workspace sends them to the worker.

Document audit

Document changes are sent when events happen. About 7 seconds after a burst of changes, the workspace sends them to the Cloudflare worker. A poll every 5 minutes is a safety net, in case an event is missed. There is no D1 for this save.

The worker sends the batch into Cloudflare Pipelines. About a minute later the rows land in Iceberg document_audit.v1, in the R2 bucket workspace-events. That send also clears the KV cache for the document.

Saved in the lakehouse

Terms used on this page

Programs and places

TermMeaning
WorkspaceOne customer system. It has its own database.
Workspace backendThe program that sends data from each workspace. See logic-bee.
WorkerThe program that receives the data and saves it. See bob-workspace-orchestration.
BuilderThe program that reads the lakehouse data for charts. See bob-api.
D1The hot store. A fast database. It answers queries about recent data.
IcebergThe cold store. Files kept in the R2 bucket workspace-events. It keeps the long history.
R2Cloudflare's file storage. It holds the Iceberg files and the workspace file buckets.
Cloudflare PipelinesThe Cloudflare service that writes the Iceberg files. Rows are ready about 1 minute after the worker sends them.

Lakehouse concepts

TermMeaning
Hot storeA store built for fast, recent queries. On this page, that is D1.
Cold storeA store built to hold the long history. On this page, that is Iceberg.
Cleanup sweepThe job that runs every 6 hours and deletes D1 rows older than 28 days. Iceberg keeps its own copy.
DeploymentAnother name for one workspace. Its id is the workspace id.
CompanyOne company inside a workspace. A workspace can hold more than one.
Business unitA group inside a company.

Logic Pilot terms

TermMeaning
Logic PilotThe platform's AI agent. People talk with it in conversations, and each run can be traced as spans.
ConversationOne Logic Pilot chat thread. It has a mode, a model, and a message count.
SessionThe document that holds a person's ratings on Logic Pilot messages, identified by sessionDocId.
TraceOne Logic Pilot run, from start to end.
SpanOne step inside a trace, for example one model call.
Root spanThe first span in a trace.

Other terms

TermMeaning
BurstA short period of rapid changes to one document.
BucketOne Cloudflare R2 container for files. Each workspace has a public (cdn) bucket and a private (storage) bucket.
Class A / Class B operationsCloudflare's two cost categories for R2 operations. Class A covers writes and lists. Class B covers reads.
JSON PatchThe format used in jsonPatch to describe one document change, as a list of operations.

The three programs​

Three programs move this data.

ProgramWhat it doesRepository
Workspace backendSends data from each workspacelogic-bee
WorkerReceives the data and saves itbob-workspace-orchestration
Builder backendReads the data for chartsbob-api

A workspace is one customer system. It has its own database. There are many workspaces.

The workspace id in this store is the deployment id.

Local workspaces​

A workspace on a developer machine does not push lakehouse data. NODE_ENV is not production, so the workspace returns before it calls the worker.

The scheduled sends still start. They stop before they read companies or open a request. A document change, a document count, a Logic Pilot span, a stats snapshot, a bug report, a feedback note, and a Logic Pilot rating stay on the local machine. They are not copied to D1 or Iceberg. An audit-trail read on that machine does not call the worker.

A production workspace does push this data. NODE_ENV is production.

File storage is separate. The worker collects those totals. The workspace does not send them in either environment.

What the lakehouse is​

The lakehouse is one shared store. The business documents stay in the workspace. The lakehouse keeps copies of history, counts, reports, and file totals.

Two places save data:

  • D1 is a database. It answers recent queries.
  • Iceberg is files in the R2 bucket workspace-events. It keeps the long history.

Some types go to both places. Then D1 is the hot store. Iceberg is the long copy. D1 keeps that copy for 28 days. A query about those 28 days reads D1. An older query reads Iceberg. Every 6 hours, the worker deletes D1 rows that are older than 28 days.

Cloudflare Pipelines writes the Iceberg files. The rows are ready after about 1 minute.

Document telemetry and Logic Pilot traces use both places. Document audit uses Iceberg only. The other types use D1 only.

Fields in every row​

FieldMeaning
eventIdThe unique id of this row.
workspaceIdThe id of the workspace that sent the row. This is the deployment id.
occurredAtThe time the event happened.

Most rows also carry companyId, the id of the company inside the workspace. Rows about one person also carry userId, and some also carry userEmail. Each data-type section below lists every field from the worker schema for that row.

See the route​

This chart shows the same route, one type at a time. Choose a type. Drag the chart to move it. Use the controls to zoom.

Loading this route.
Document audit goes to Iceberg document_audit.v1. The workspace reads it back for that document. This save has no D1.

Document audit​

Document audit is the history of one document. It records who changed the document, and what changed.

The workspace sends the changes about 7 seconds after a burst of changes stops. A check every 5 minutes sends a change that the first listen missed.

The worker saves the row in Iceberg document_audit.v1 only.

The workspace reads this history when a person opens the audit trail or the activity of that document. Builder does not chart this type.

Example row (click to expand)

This is the full row from the worker schema.

{
"workspaceId": "d00232",
"eventId": "evt_audit_001",
"occurredAt": "2026-10-07T08:15:00.000Z",
"auditId": "audit_001",
"timestamp": "2026-10-07T08:15:00.000Z",
"documentTimestamp": "2026-10-07T08:14:58.000Z",
"serviceName": "logic-bee",
"type": "document",
"appId": "app_1",
"appSlug": "finance",
"appType": "workspace",
"projectId": "proj_1",
"deploymentId": "d00232",
"bobPackage": "finance",
"bobPackageVersion": "1.0.0",
"environment": "production",
"deployment": "d00232",
"licenseId": "lic_1",
"userId": "user_1",
"userEmail": "ada@example.com",
"roleType": "admin",
"userRoleId": "role_1",
"isSuperAdmin": "false",
"userType": "user",
"companyId": "co_1",
"businessUnitId": "bu_1",
"sessionId": "sess_1",
"ipAddress": "203.0.113.10",
"requestType": "http",
"userAgent": "Mozilla/5.0",
"complianceFlags": "[]",
"retentionPolicy": "default",
"retentionYears": 7,
"dataClassification": "internal",
"docId": "inv_1001",
"mongoId": "6640a1",
"docType": "invoice",
"namespace": "finance",
"docName": "invoice",
"cfpPath": "finance/invoice",
"status": "paid",
"version": 3,
"operationType": "update",
"operation": "update",
"jsonPatch": "[{\"op\":\"replace\",\"path\":\"/data/status\",\"value\":\"paid\"}]",
"patchOperationsCount": 1,
"fieldsAffected": "[\"/data/status\"]",
"updatedFields": "[\"/data/status\"]",
"removedFields": "[]",
"truncatedArrays": "[]",
"data": "{}",
"previousDocument": "{\"status\":\"unpaid\"}",
"currentDocument": "{\"status\":\"paid\"}",
"currentDocumentHash": "abc123",
"previousDocumentHash": "def456",
"message": "Status changed from unpaid to paid",
"activityMessage": "Ada marked the invoice paid",
"isActivity": true,
"userRequestId": "req_1",
"transactionId": "txn_1",
"podName": "logic-bee-1"
}
View all fields
FieldMeaning
workspaceIdThe id of the workspace that sent the row. This is the deployment id.
eventIdThe unique id of this row.
occurredAtThe time the event happened.
auditIdThe id of the audit record. May be empty.
timestampThe time the audit record was written.
documentTimestampThe time stored on the document.
serviceNameThe service that wrote the change.
typeThe audit type.
appIdThe id of the app.
appSlugThe slug of the app.
appTypeThe type of the app.
projectIdThe id of the project.
deploymentIdThe id of the deployment.
bobPackageThe package name.
bobPackageVersionThe package version.
environmentThe environment, for example production.
deploymentThe deployment name.
licenseIdThe license id.
userIdThe id of the person who made the change.
userEmailThe email of that person.
roleTypeThe role type of that person.
userRoleIdThe id of that person's role.
isSuperAdminWhether that person is a super admin, as a string.
userTypeThe kind of user.
companyIdThe id of the company inside the workspace.
businessUnitIdThe id of the business unit.
sessionIdThe id of the session.
ipAddressThe IP address of the request.
requestTypeThe kind of request.
userAgentThe user agent of the request.
complianceFlagsA JSON string of compliance flags.
retentionPolicyThe retention policy name.
retentionYearsHow many years the row is kept.
dataClassificationThe data classification label.
docIdThe id of the document that changed.
mongoIdThe database id of the document.
docTypeThe type of the document.
namespaceThe namespace of the document.
docNameThe name of the document, for display.
cfpPathThe CFP path of the document.
statusThe document status after the change.
versionThe document version after the change.
operationTypeThe kind of operation.
operationThe kind of change: create, update, or delete.
jsonPatchThe exact change, as a JSON Patch array stored in a string.
patchOperationsCountThe number of operations in jsonPatch.
fieldsAffectedA JSON string of the paths the change touched.
updatedFieldsA JSON string of the paths that were updated.
removedFieldsA JSON string of the paths that were removed.
truncatedArraysA JSON string of arrays that were truncated.
dataA JSON string of the change data.
previousDocumentA JSON string of the document before the change.
currentDocumentA JSON string of the document after the change.
currentDocumentHashA hash of the document after the change.
previousDocumentHashA hash of the document before the change.
messageA short, human-readable summary of the change.
activityMessageThe text shown in the activity list.
isActivitytrue when this row is also an activity item.
userRequestIdThe id of the user request.
transactionIdThe id of the transaction.
podNameThe name of the pod that wrote the change.
If a copy does not arrive

The 5-minute check sends an audit change that the first listen missed. After the workspace prepares the history row, it does not send that change again.

Document telemetry​

Document telemetry is a count of documents. It is not the documents.

Twice a day, each workspace sends the counts. Each row is one collection. count is the number of documents. countDeleted is the number of deleted documents.

The worker saves the same row in two places:

  • D1 document_telemetry, table v1, for 28 days
  • Iceberg document_telemetry.v1

Builder reads these counts for the document charts. The overview of one deployment uses them too.

Example row (click to expand)

This is the full row from the worker schema.

{
"workspaceId": "d00232",
"eventId": "evt_tel_001",
"occurredAt": "2026-10-07T08:18:00.000Z",
"auditId": "",
"timestamp": "2026-10-07T08:18:00.000Z",
"serviceName": "logic-bee",
"type": "document-telemetry",
"appId": "app_1",
"appSlug": "finance",
"appType": "workspace",
"projectId": "proj_1",
"deploymentId": "d00232",
"bobPackage": "finance",
"bobPackageVersion": "1.0.0",
"environment": "production",
"deployment": "d00232",
"licenseId": "lic_1",
"userId": "user_1",
"userEmail": "ada@example.com",
"roleType": "admin",
"userRoleId": "role_1",
"isSuperAdmin": "false",
"userType": "user",
"companyId": "co_1",
"businessUnitId": "bu_1",
"sessionId": "sess_1",
"ipAddress": "203.0.113.10",
"requestType": "cron",
"userAgent": "",
"complianceFlags": "[]",
"retentionPolicy": "default",
"retentionYears": 7,
"dataClassification": "internal",
"collectionName": "finance",
"docType": "invoice",
"namespace": "finance",
"docName": "invoice",
"cfpPath": "finance/invoice",
"count": 1204,
"countDeleted": 16,
"version": 1
}
View all fields
FieldMeaning
workspaceIdThe id of the workspace that sent the row. This is the deployment id.
eventIdThe unique id of this row.
occurredAtThe time the count was taken.
auditIdMay be empty.
timestampThe time the row was written.
serviceNameThe service that sent the count.
typeThe telemetry type.
appIdThe id of the app.
appSlugThe slug of the app.
appTypeThe type of the app.
projectIdThe id of the project.
deploymentIdThe id of the deployment.
bobPackageThe package name.
bobPackageVersionThe package version.
environmentThe environment, for example production.
deploymentThe deployment name.
licenseIdThe license id.
userIdThe id of the person, when the row has one.
userEmailThe email of that person.
roleTypeThe role type.
userRoleIdThe id of the role.
isSuperAdminWhether the person is a super admin, as a string.
userTypeThe kind of user.
companyIdThe id of the company inside the workspace.
businessUnitIdThe id of the business unit.
sessionIdThe id of the session.
ipAddressThe IP address, when the row has one.
requestTypeThe kind of request.
userAgentThe user agent, when the row has one.
complianceFlagsA JSON string of compliance flags.
retentionPolicyThe retention policy name.
retentionYearsHow many years the row is kept.
dataClassificationThe data classification label.
collectionNameThe collection the count is for.
docTypeThe type of document counted.
namespaceThe namespace of the collection.
docNameThe display name for the document type.
cfpPathThe CFP path of the document type.
countThe number of documents in the collection.
countDeletedThe number of deleted documents in the collection.
versionThe version of this count row.
If a copy does not arrive

The next twice-daily send sends a new snapshot.

Logic Pilot traces​

A trace is one Logic Pilot run. A span is one step in that run.

The workspace waits until 512 spans are ready, or until 5 seconds pass. Then it sends the group. One group can contain spans from more than one run.

The worker saves the same row in two places:

  • D1 mastra_spans, table v2, for 28 days
  • Iceberg mastra_spans.v2

Builder reads recent lists from D1. Builder reads one trace from Iceberg. A short cache holds the latest trace. A new save clears that cache.

Example row (click to expand)

This is the full row from the worker schema. endedAt is left out when the span has no end time. Do not send an empty string for it.

{
"workspaceId": "d00232",
"eventId": "evt_span_001",
"occurredAt": "2026-10-07T09:01:05.000Z",
"startAt": "2026-10-07T09:01:04.160Z",
"endedAt": "2026-10-07T09:01:05.000Z",
"traceId": "trace_88",
"spanId": "span_3",
"parentSpanId": "span_1",
"spanType": "model",
"name": "answer",
"model": "gpt-4.1",
"provider": "openai",
"durationMs": 840,
"promptTokens": 120,
"completionTokens": 40,
"status": "ok",
"companyId": "co_1",
"userId": "user_1",
"environment": "production",
"conversationId": "conv_9",
"messageId": "msg_9",
"mode": "text",
"inputJson": "{}",
"outputJson": "{}",
"attributesJson": "{}",
"metadataJson": "{}",
"errorJson": "",
"entityType": "conversation",
"entityId": "conv_9",
"entityName": "Invoice help",
"isEvent": false,
"isRootSpan": false,
"runId": "run_12",
"tags": "[]"
}
View all fields
FieldMeaning
workspaceIdThe id of the workspace that sent the row. This is the deployment id.
eventIdThe unique id of this row.
occurredAtThe time the span was recorded.
startAtThe time the span started.
endedAtThe time the span ended. Omitted when there is no end time.
traceIdThe id of the run this span belongs to.
spanIdThe id of this step.
parentSpanIdThe id of the parent span. May be empty.
spanTypeThe kind of step, for example model.
nameA short label for the step.
modelThe AI model used in this step. May be empty.
providerThe provider of that model, for example openai. May be empty.
durationMsHow long the step took, in milliseconds.
promptTokensInput tokens used by this step.
completionTokensOutput tokens used by this step.
statusok, or an error state. May be empty.
companyIdThe id of the company. May be empty.
userIdThe id of the person. May be empty.
environmentThe environment. May be empty.
conversationIdThe Logic Pilot conversation. May be empty.
messageIdThe message this span belongs to. May be empty.
modeThe conversation mode. May be empty.
inputJsonA JSON string of the span input.
outputJsonA JSON string of the span output.
attributesJsonA JSON string of span attributes.
metadataJsonA JSON string of span metadata.
errorJsonA JSON string of the error. Empty when there is no error.
entityTypeThe kind of entity this span is about. May be empty.
entityIdThe id of that entity. May be empty.
entityNameThe name of that entity. May be empty.
isEventtrue when this span is an event.
isRootSpantrue for the first span in the run.
runIdThe id of the broader run that contains this trace. May be empty.
tagsA JSON string of tags.
If a copy does not arrive

A span group that fails to leave the workspace is dropped. Later spans go out in a new group.

Workspace stats​

Workspace stats is a count of one company at one time. The count includes people, roles, companies, business units, invitations, API keys, two-factor use, and the autonomy level.

Twice a day, each workspace sends one row for each company. The worker saves it in D1 workspace_stats, table workspace_stats. If the same row is already saved, the worker leaves it.

Builder reads these rows to show change over time, and to show the latest counts.

Example row (click to expand)

This is the full row from the worker schema. eventId is workspaceId, companyId, and occurredAt.

{
"eventId": "d00232:co_1:2026-10-07T07:26:00.000Z",
"workspaceId": "d00232",
"occurredAt": "2026-10-07T07:26:00.000Z",
"companyId": "co_1",
"users": 42,
"deletedUsers": 1,
"guests": 3,
"deletedGuests": 0,
"roles": 8,
"deletedRoles": 0,
"companies": 1,
"deletedCompanies": 0,
"businessUnits": 3,
"superAdminUsers": 1,
"adminUsers": 4,
"otherUsers": 37,
"apiKeys": 2,
"deletedApiKeys": 0,
"apiCredentials": 1,
"invitesJson": "{\"pending\":2}",
"guestInvitesJson": "{}",
"pendingInvites": 2,
"oldestPendingInviteAt": "2026-10-01T09:00:00.000Z",
"invitesTotal": 10,
"twoFactorRequired": 1,
"usersWith2fa": 30,
"rolesInUse": 6,
"autonomyLevel": 2
}
View all fields
FieldMeaning
eventIdworkspaceId, companyId, and occurredAt. A repeat of the same row is left as it is.
workspaceIdThe id of the workspace that sent the row. This is the deployment id.
occurredAtThe time the snapshot was taken.
companyIdThe id of the company. May be empty.
usersThe number of people in the company.
deletedUsersThe number of deleted people.
guestsThe number of guests.
deletedGuestsThe number of deleted guests.
rolesThe number of roles defined.
deletedRolesThe number of deleted roles.
companiesThe number of companies in the workspace.
deletedCompaniesThe number of deleted companies.
businessUnitsThe number of business units.
superAdminUsersThe number of super admins.
adminUsersThe number of admins.
otherUsersThe number of people who are not admins.
apiKeysThe number of API keys.
deletedApiKeysThe number of deleted API keys.
apiCredentialsThe number of API credentials.
invitesJsonA JSON string of invite counts by status.
guestInvitesJsonA JSON string of guest invite counts by status.
pendingInvitesThe number of invitations not yet accepted.
oldestPendingInviteAtThe time of the oldest pending invitation. May be empty.
invitesTotalThe total number of invitations.
twoFactorRequired1 if two-factor login is required, 0 if not.
usersWith2faThe number of people with two-factor login on.
rolesInUseThe number of roles that are assigned.
autonomyLevelThe autonomy level set for the company.
If a copy does not arrive

The next twice-daily send sends a new row.

Logic Pilot stats​

Logic Pilot stats is three counts. The workspace sends them twice a day.

  1. One row for each conversation that changed in the last 72 hours.
  2. One row of folder counts for each company.
  3. One row of token totals for each model.

The 72 hours are longer than the time between the two daily sends. If one send fails, the next send still includes that conversation.

The worker saves the rows in D1 logic_pilot_stats. The tables are logic_pilot_conversation_stat, logic_pilot_folder_stat, and token_telemetry_stat.

Builder reads them for the conversation charts and the token charts.

Conversation rows​

One row for each conversation that changed in the last 72 hours.

Example row (click to expand)

This is the full row from the worker schema. eventId is workspaceId, conversationId, and updatedAt. hasContext, isBookmarked, and inFolder are stored as true or false.

{
"eventId": "d00232:conv_9:2026-10-06T18:00:00.000Z",
"workspaceId": "d00232",
"occurredAt": "2026-10-07T10:43:00.000Z",
"updatedAt": "2026-10-06T18:00:00.000Z",
"createdAt": "2026-10-01T09:00:00.000Z",
"lastMessageAt": "2026-10-06T18:00:00.000Z",
"firstMessageAt": "2026-10-01T09:05:00.000Z",
"durationMs": 432000000,
"companyId": "co_1",
"userId": "user_1",
"roleType": "admin",
"conversationId": "conv_9",
"name": "Invoice help",
"nameLength": 12,
"status": "active",
"isBookmarked": false,
"inFolder": true,
"conversationType": "chat",
"mode": "text",
"modelId": "gpt-4.1",
"agentId": "agent_1",
"hasContext": true,
"contextItemCount": 2,
"eventsCount": 3,
"messageCount": 14,
"userMessageCount": 7,
"assistantMessageCount": 7,
"totalUserCharCount": 900,
"totalUserWordCount": 160,
"maxUserWordCount": 40,
"voiceMessageCount": 0,
"totalVoiceDurationMs": 0,
"avgVoiceDurationMs": 0,
"maxVoiceDurationMs": 0,
"documentCount": 1,
"spreadsheetCount": 0,
"chartCount": 0
}
View all fields
FieldMeaning
eventIdworkspaceId, conversationId, and updatedAt. A repeat send is ignored.
workspaceIdThe id of the workspace that sent the row. This is the deployment id.
occurredAtThe time the snapshot was taken.
updatedAtThe time the conversation was last updated. May be empty.
createdAtThe time the conversation was created. May be empty.
lastMessageAtThe time of the last message. May be empty.
firstMessageAtThe time of the first message. May be empty.
durationMsThe length of the conversation, in milliseconds.
companyIdThe id of the company. May be empty.
userIdThe id of the person. May be empty.
roleTypeThe role type of that person. May be empty.
conversationIdThe id of the conversation.
nameThe conversation name. May be empty.
nameLengthThe length of the name.
statusactive, or another conversation state. May be empty.
isBookmarkedtrue when the conversation is bookmarked.
inFoldertrue when the conversation is in a folder.
conversationTypeThe kind of conversation. May be empty.
modetext, or another conversation mode. May be empty.
modelIdThe AI model used in the conversation. May be empty.
agentIdThe id of the agent. May be empty.
hasContexttrue when the conversation has context items.
contextItemCountThe number of context items.
eventsCountThe number of events.
messageCountThe number of messages in the conversation.
userMessageCountThe number of messages from the person.
assistantMessageCountThe number of messages from the assistant.
totalUserCharCountThe total characters in the person's messages.
totalUserWordCountThe total words in the person's messages.
maxUserWordCountThe longest person message, in words.
voiceMessageCountThe number of voice messages.
totalVoiceDurationMsThe total voice length, in milliseconds.
avgVoiceDurationMsThe average voice length, in milliseconds.
maxVoiceDurationMsThe longest voice message, in milliseconds.
documentCountThe number of documents produced.
spreadsheetCountThe number of spreadsheets produced.
chartCountThe number of charts produced.

Folder rows​

One row of folder counts for each company.

Example row (click to expand)

This is the full row from the worker schema. eventId is workspaceId, companyId, and occurredAt. One row is for one company, not for one folder.

{
"eventId": "d00232:co_1:2026-10-07T10:43:00.000Z",
"workspaceId": "d00232",
"occurredAt": "2026-10-07T10:43:00.000Z",
"companyId": "co_1",
"folderCount": 6,
"emptyFolderCount": 1,
"usersWithFoldersCount": 4,
"maxFoldersPerUser": 3,
"avgFoldersPerUser": 1.5,
"conversationsInFoldersCount": 20,
"conversationsNotInFolderCount": 5,
"maxConversationsInFolder": 8,
"avgConversationsPerFolder": 3.3
}
View all fields
FieldMeaning
eventIdworkspaceId, companyId, and occurredAt.
workspaceIdThe id of the workspace that sent the row. This is the deployment id.
occurredAtThe time the snapshot was taken.
companyIdThe id of the company. May be empty.
folderCountThe number of folders in the company.
emptyFolderCountThe number of folders with no conversations.
usersWithFoldersCountThe number of people who have at least one folder.
maxFoldersPerUserThe most folders one person has.
avgFoldersPerUserThe average number of folders per person.
conversationsInFoldersCountThe number of conversations placed in a folder.
conversationsNotInFolderCountThe number of conversations that are not in a folder.
maxConversationsInFolderThe most conversations in one folder.
avgConversationsPerFolderThe average number of conversations per folder.

Token rows​

One row of token totals for each model. These numbers are totals since the counter started, not just for that day.

Example row (click to expand)

This is the full row from the worker schema.

{
"eventId": "d00232:co_1:gpt-4.1:2026-10-07T10:43:00.000Z",
"workspaceId": "d00232",
"occurredAt": "2026-10-07T10:43:00.000Z",
"companyId": "co_1",
"model": "gpt-4.1",
"input": 120000,
"output": 40000,
"cachedInput": 8000,
"total": 160000
}
View all fields
FieldMeaning
eventIdThe unique id of this row.
workspaceIdThe id of the workspace that sent the row. This is the deployment id.
occurredAtThe time the snapshot was taken.
companyIdThe id of the company. May be empty.
modelThe AI model these totals are for.
inputTotal input tokens sent to the model.
outputTotal output tokens returned by the model.
cachedInputTotal input tokens served from cache.
totalinput plus output.
If a copy does not arrive

The next twice-daily send sends a new snapshot. Conversation rows still cover the last 72 hours, so a missed send is not lost.

Bug reports​

A person files a bug in the workspace. The workspace saves the bug first. Then it sends a copy to the worker.

The worker saves the copy in D1 feedbacks, table bug. Builder reads the copy.

Example row (click to expand)

This is the full row from the worker schema. A repeat uses bugDocId.

{
"eventId": "evt_bug_001",
"workspaceId": "d00232",
"occurredAt": "2026-10-07T11:02:00.000Z",
"environment": "production",
"deploymentId": "d00232",
"licenseId": "lic_1",
"companyId": "co_1",
"companyName": "Northwind",
"userId": "user_1",
"userEmail": "ada@example.com",
"userName": "Ada",
"roleType": "admin",
"bugDocId": "bug_44",
"pageUrl": "https://workspace.example/invoices",
"screenshotUrl": "https://workspace.example/files/bug_44.png",
"reportType": "something-doesnt-work",
"description": "The invoice total does not update after a line change.",
"userTimezone": "Europe/Chisinau",
"date": "2026-10-07",
"userLanguage": "en"
}
View all fields
FieldMeaning
eventIdThe unique id of this row.
workspaceIdThe id of the workspace that sent the row. This is the deployment id.
occurredAtThe time the bug was filed.
environmentThe environment, for example production.
deploymentIdThe id of the deployment.
licenseIdThe license id.
companyIdThe id of the company.
companyNameThe name of the company. May be empty.
userIdThe id of the person who filed the bug.
userEmailThe email of that person.
userNameThe name of that person. May be empty.
roleTypeThe role type of that person. May be empty.
bugDocIdThe id of the bug document in the workspace. A repeat of the same bug uses this id.
pageUrlThe page the person was on when they filed the bug. May be empty.
screenshotUrlThe URL of the screenshot. May be empty.
reportTypeThe kind of problem, for example something-doesnt-work. May be empty.
descriptionThe person's own description of the problem. May be empty.
userTimezoneThe person's time zone. May be empty.
dateThe date the person entered. May be empty.
userLanguageThe person's language. May be empty.
If a copy does not arrive

The bug report stays in the workspace. Only the Builder copy is missing.

Workspace feedback​

A person sends feedback in the workspace. The workspace saves it first. Then it sends a copy to the worker.

The worker saves the copy in D1 feedbacks, table workspace_feedback. Builder reads the copy.

Example row (click to expand)

This is the full row from the worker schema. A repeat uses feedbackDocId.

{
"eventId": "evt_fb_001",
"workspaceId": "d00232",
"occurredAt": "2026-10-07T11:10:00.000Z",
"environment": "production",
"deploymentId": "d00232",
"licenseId": "lic_1",
"companyId": "co_1",
"companyName": "Northwind",
"userId": "user_1",
"userEmail": "ada@example.com",
"userName": "Ada",
"roleType": "admin",
"feedbackDocId": "fb_12",
"workspaceUrl": "https://workspace.example",
"description": "The home page loads slowly in the morning.",
"date": "2026-10-07",
"userLanguage": "en",
"userTimezone": "Europe/Chisinau"
}
View all fields
FieldMeaning
eventIdThe unique id of this row.
workspaceIdThe id of the workspace that sent the row. This is the deployment id.
occurredAtThe time the feedback was sent.
environmentThe environment, for example production.
deploymentIdThe id of the deployment.
licenseIdThe license id.
companyIdThe id of the company.
companyNameThe name of the company. May be empty.
userIdThe id of the person who sent the feedback.
userEmailThe email of that person.
userNameThe name of that person. May be empty.
roleTypeThe role type of that person. May be empty.
feedbackDocIdThe id of the feedback document in the workspace. A repeat uses this id.
workspaceUrlThe workspace the feedback came from. May be empty.
descriptionThe person's own description. May be empty.
dateThe date the person entered. May be empty.
userLanguageThe person's language. May be empty.
userTimezoneThe person's time zone. May be empty.
If a copy does not arrive

The feedback stays in the workspace. Only the Builder copy is missing.

Logic Pilot feedback​

A person rates a Logic Pilot message. The rating stays on the session. When the session is saved, the workspace sends only new ratings.

The worker saves each new rating in D1 feedbacks, table logic_pilot_message. Builder reads the copy.

Example row (click to expand)

This is the full row from the worker schema. A repeat uses feedbackId. polarity is positive or negative.

{
"eventId": "evt_lp_001",
"workspaceId": "d00232",
"occurredAt": "2026-10-07T11:20:00.000Z",
"environment": "production",
"deploymentId": "d00232",
"licenseId": "lic_1",
"companyId": "co_1",
"companyName": "Northwind",
"userId": "user_1",
"userEmail": "ada@example.com",
"userName": "Ada",
"roleType": "admin",
"feedbackId": "rate_7",
"polarity": "negative",
"typeOfIssue": "wrong_data",
"details": "The answer used the wrong invoice.",
"sessionDocId": "sess_3",
"sessionName": "Invoice help",
"messageId": "msg_9",
"agentId": "agent_1",
"mode": "text",
"modelId": "gpt-4.1",
"messageContents": "[]",
"assistantVisualContents": "[]",
"conversationId": "conv_9"
}
View all fields
FieldMeaning
eventIdThe unique id of this row.
workspaceIdThe id of the workspace that sent the row. This is the deployment id.
occurredAtThe time the rating was saved.
environmentThe environment, for example production.
deploymentIdThe id of the deployment.
licenseIdThe license id.
companyIdThe id of the company.
companyNameThe name of the company. May be empty.
userIdThe id of the person who rated the message.
userEmailThe email of that person.
userNameThe name of that person. May be empty.
roleTypeThe role type of that person. May be empty.
feedbackIdThe id of this rating. A repeat uses this id.
polaritypositive or negative.
typeOfIssueThe kind of problem, for example wrong_data. May be empty.
detailsThe person's own note about the rating. May be empty.
sessionDocIdThe id of the session document.
sessionNameThe name of the session. May be empty.
messageIdThe id of the rated message.
agentIdThe id of the agent. May be empty.
modeThe conversation mode. May be empty.
modelIdThe AI model that produced the rated message. May be empty.
messageContentsA JSON string of the message contents.
assistantVisualContentsA JSON string of the assistant's visual contents.
conversationIdThe id of the conversation. May be empty.
If a copy does not arrive

The rating stays on the session. Only the Builder copy is missing.

File storage​

File storage is the size and the use of the workspace file buckets. The worker collects this data. The workspace does not send it.

Once a day, the worker asks Builder for deployments with status started or terminated. Then the worker reads the previous day's totals from Cloudflare. Each workspace has two buckets:

  • {workspaceId}-cdn is the public bucket
  • {workspaceId}-storage is the private bucket

The worker saves one row for each bucket for that day in D1 workspace_stats, table workspace_r2_stat. A later save for the same day replaces that row. The worker reads the totals. It does not open the files.

Builder reads these rows for the storage charts.

Loading this route.
Every workspace has two R2 buckets: one public, one private.
Example row (click to expand)

This is the full row from the worker schema. eventId is bucketName, then |, then the UTC day. occurredAt is the start of that day. Storage fields are the highest value of the day. Operation and bandwidth fields are totals for the day.

{
"eventId": "d00232-cdn|2026-10-06",
"workspaceId": "d00232",
"occurredAt": "2026-10-06T00:00:00.000Z",
"bucketName": "d00232-cdn",
"bucketKind": "cdn",
"payloadSize": 1073741824,
"metadataSize": 1048576,
"objectCount": 540,
"uploadCount": 12,
"classAOps": 120,
"classBOps": 8000,
"freeOps": 10,
"errorOps": 2,
"clientErrorOps": 1,
"serverErrorOps": 1,
"opsByTypeJson": "{\"PutObject\":12}",
"bytesDownload": 524288000,
"bytesUpload": 10485760
}
View all fields
FieldMeaning
eventIdbucketName, then |, then the UTC day YYYY-MM-DD.
workspaceIdThe id of the workspace. This is the deployment id.
occurredAtThe start of that UTC day.
bucketNameThe bucket this row is for.
bucketKindcdn for the public bucket, storage for the private bucket.
payloadSizeThe highest total bytes stored in the bucket that day.
metadataSizeThe highest metadata bytes stored that day.
objectCountThe highest number of files in the bucket that day.
uploadCountThe number of uploads that day.
classAOpsCount of write-type operations that day.
classBOpsCount of read-type operations that day.
freeOpsCount of free operations that day.
errorOpsCount of error operations that day.
clientErrorOpsCount of client error operations that day.
serverErrorOpsCount of server error operations that day.
opsByTypeJsonA JSON string of request counts by Cloudflare action type.
bytesDownloadTotal bytes downloaded that day.
bytesUploadTotal bytes uploaded that day.

This type carries no companyId or userId.

If a copy does not arrive

That day's group is skipped. A later save for the same day replaces the row.

Quick map​

DataSentWhere it is savedKept forWho reads it
Document auditOn each change, about 7 seconds after a burst stopsIceberg document_audit.v1Kept in Iceberg; no deletion rule givenThe workspace
Document telemetryTwice a dayD1 for 28 days, and Iceberg document_telemetry.v128 days in D1; kept in Iceberg after thatBuilder
Logic Pilot tracesIn groups, as spans are readyD1 for 28 days, and Iceberg mastra_spans.v228 days in D1; kept in Iceberg after thatBuilder
Workspace statsTwice a dayD1 workspace_statsKept in D1; no deletion rule givenBuilder
Logic Pilot statsTwice a dayD1 logic_pilot_statsKept in D1; no deletion rule givenBuilder
Bug reportsWhen a person files a bugD1 feedbacks, table bugKept in D1; no deletion rule givenBuilder
Workspace feedbackWhen a person sends feedbackD1 feedbacks, table workspace_feedbackKept in D1; no deletion rule givenBuilder
Logic Pilot feedbackWhen a person rates a messageD1 feedbacks, table logic_pilot_messageKept in D1; no deletion rule givenBuilder
File storageOnce a day (the worker reads it)D1 workspace_r2_statKept in D1; no deletion rule givenBuilder

Data that is not in the lakehouse​

Builder activity, build plans, and CFPs stay in Builder. Their charts read Builder.

The documents, sessions, and files that a person works on stay in the workspace.

Resources​

This architecture needs three programs and the Cloudflare resources below.

The workspace backend already exists. It is logic-bee. It runs in each workspace and sends the data.

bob-api already exists. It is the Builder backend. It reads the lakehouse for charts.

The worker is bob-workspace-orchestration. It receives the data and saves it. Create that worker with the Cloudflare resources in the table below.

Programs​

ProgramRepositoryWhat it does
Workspace backendlogic-beeSends data from each workspace. This program already exists.
bob-apibob-apiReads the lakehouse for Builder charts. This program already exists.
Workerbob-workspace-orchestrationReceives the data and saves it. Its Cloudflare resources are in the next table.

Cloudflare​

This table is the Cloudflare resources. Use these names. Do not invent new ones.

Iceberg types use a namespace and a version. The Iceberg table is {namespace}.{version}. The stream is {namespace}_{version}. The sink is {namespace}_{version}_sink. The pipeline is {namespace}_{version}_pipeline.

When that type also has a hot store, the D1 database name is the namespace and the D1 table name is the version (v1 or v2).

A D1-only type uses a database name and a table name that describe the data. Several tables can share one database.

NameProviderTypeBindingParentDataDocumentation
bob-workspace-orchestrationCloudflareWorker——All lakehouse typesWorkers
workspace-eventsCloudflareR2 bucketWORKSPACE_EVENTS—Iceberg filesR2
workspace_events_cacheCloudflareKV namespaceWORKSPACE_EVENTS_CACHEbob-workspace-orchestrationDocument audit and Logic Pilot tracesKV
document_telemetryCloudflareD1 databaseDOCUMENT_TELEMETRYbob-workspace-orchestrationDocument telemetryD1
mastra_spansCloudflareD1 databaseMASTRA_SPANSbob-workspace-orchestrationLogic Pilot tracesD1
feedbacksCloudflareD1 databaseFEEDBACKSbob-workspace-orchestrationBug reports, workspace feedback, and Logic Pilot feedbackD1
logic_pilot_statsCloudflareD1 databaseLOGIC_PILOT_STATSbob-workspace-orchestrationLogic Pilot statsD1
workspace_statsCloudflareD1 databaseWORKSPACE_STATSbob-workspace-orchestrationWorkspace stats and file storageD1
v1CloudflareD1 table—document_telemetryDocument telemetryD1
v2CloudflareD1 table—mastra_spansLogic Pilot tracesD1
bugCloudflareD1 table—feedbacksBug reportsD1
workspace_feedbackCloudflareD1 table—feedbacksWorkspace feedbackD1
logic_pilot_messageCloudflareD1 table—feedbacksLogic Pilot feedbackD1
logic_pilot_conversation_statCloudflareD1 table—logic_pilot_statsLogic Pilot statsD1
logic_pilot_folder_statCloudflareD1 table—logic_pilot_statsLogic Pilot statsD1
token_telemetry_statCloudflareD1 table—logic_pilot_statsLogic Pilot statsD1
workspace_statsCloudflareD1 table—workspace_statsWorkspace statsD1
workspace_r2_statCloudflareD1 table—workspace_statsFile storageD1
document_audit.v1CloudflareIceberg table—workspace-eventsDocument auditR2 Data Catalog
document_telemetry.v1CloudflareIceberg table—workspace-eventsDocument telemetryR2 Data Catalog
mastra_spans.v2CloudflareIceberg table—workspace-eventsLogic Pilot tracesR2 Data Catalog
document_audit_v1CloudflarePipeline streamDOCUMENT_AUDIT_STREAMbob-workspace-orchestrationDocument auditPipelines
document_audit_v1_sinkCloudflarePipeline sink—workspace-eventsDocument auditPipelines
document_audit_v1_pipelineCloudflarePipeline——Document auditPipelines
document_telemetry_v1CloudflarePipeline streamDOCUMENT_TELEMETRY_STREAMbob-workspace-orchestrationDocument telemetryPipelines
document_telemetry_v1_sinkCloudflarePipeline sink—workspace-eventsDocument telemetryPipelines
document_telemetry_v1_pipelineCloudflarePipeline——Document telemetryPipelines
mastra_spans_v2CloudflarePipeline streamMASTRA_SPANS_STREAMbob-workspace-orchestrationLogic Pilot tracesPipelines
mastra_spans_v2_sinkCloudflarePipeline sink—workspace-eventsLogic Pilot tracesPipelines
mastra_spans_v2_pipelineCloudflarePipeline——Logic Pilot tracesPipelines
0 2 * * *CloudflareCron—bob-workspace-orchestrationFile storageCron Triggers
0 */6 * * *CloudflareCron—bob-workspace-orchestrationDocument telemetry and Logic Pilot tracesCron Triggers

Each workspace has its own file buckets. The worker only reads their totals. Those buckets are not in this list.

The worker vars DOCUMENT_AUDIT_ICEBERG_TABLE, DOCUMENT_TELEMETRY_ICEBERG_TABLE, and MASTRA_SPANS_ICEBERG_TABLE must match the three Iceberg table names.

Cloudflare limits​

These limits are set by Cloudflare. They apply to the worker, the stores, the pipelines, and the reads on this page. The numbers were taken from the Cloudflare docs on 7 October 2026. Where a doc lists a Free plan and a Paid plan, the table below uses the Paid plan. The Free plan is lower. Pipelines and R2 SQL are in open beta, so those limits can change.

Worker​

The worker receives every ingest and runs both crons. Workers limits.

LimitPaid planWhat it means here
Memory128 MB per isolateA large ingest batch or a large query result must not be held in memory all at once.
CPU time, HTTP30 seconds by default, up to 5 minutesWaiting on D1, KV, R2, or fetch does not count as CPU time.
CPU time, cron shorter than 1 hour30 secondsThis page has no cron that runs that often.
CPU time, cron of 1 hour or longer15 minutesThe file-storage cron and the 28-day cleanup use this limit.
Cron wall time15 minutesA cron that is still running after 15 minutes is stopped.
Subrequests10,000 per invocationEach call to D1, KV, R2, or fetch counts.
Connections waiting for headers6 at one timeA seventh call waits until one of the six receives headers.
Request body100 MB on Free and Pro, 200 MB on Business, up to 5 GB on EnterpriseA body over the zone limit is rejected with HTTP 413.
waitUntil30 seconds after the responseWork left running after the response can be stopped after 30 seconds.

An account can have 250 cron triggers on the Paid plan, and 5 on the Free plan. A cron change can take up to 15 minutes to reach the whole network. Cron time is UTC. Cron Triggers.

D1​

D1 is the hot store. Each database is separate. D1 limits.

LimitPaid planWhat it means here
Database size10 GBCloudflare does not raise this. Five databases are used: document_telemetry, mastra_spans, feedbacks, logic_pilot_stats, and workspace_stats.
Queries per invocation1,000One cron run shares this cap across the databases it touches.
SQL duration30 secondsA delete of a huge set of rows in one statement can hit this. The 28-day cleanup deletes in batches.
Row size2 MBOne row, including JSON strings, must stay under 2 MB.
SQL statement100 KB
Bound parameters100 per query
Columns per table100
ConcurrencyOne query at a time per databaseExtra queries wait. A full queue returns an overloaded error.

Pipelines​

Pipelines carry document audit, document telemetry, and Logic Pilot traces into Iceberg. The product is in open beta. Pipelines limits.

LimitValueWhat it means here
Streams, sinks, and pipelines20 of each per accountThis page uses 3 of each.
Ingest request5 MBOne send to a stream must stay under 5 MB.
Ingest rate5 MB per second per stream
Stream schemaFixed after creationA new column needs a new stream, sink, pipeline, and Iceberg table. Logic Pilot traces did this for v2.
Bad eventsDropped while processingAn event that does not match the schema is accepted, then dropped.
Iceberg sinkParquet onlyA sink cannot be attached to an Iceberg table that already exists.
Roll interval60 seconds minimumRows are ready about 1 minute after the worker sends them.

Streams. R2 Data Catalog sink.

R2 and Iceberg​

Iceberg files live in the R2 bucket workspace-events. R2 limits. Table maintenance.

LimitValueWhat it means here
Bucket storage and object countNo stated maximum
One object5 TiB
Writes to the same object name1 per second
Catalog jurisdictionDefault jurisdiction onlyA bucket in the EU or FedRAMP jurisdiction cannot use the catalog.
Compaction file size64 MB to 512 MBCompaction reads Parquet files only. Files that no snapshot points at are not deleted.
Snapshot expirationOff unless enabledIf it is enabled, the default is to drop snapshots older than 30 days and keep the last 5.

R2 SQL​

Builder and the workspace read Iceberg through R2 SQL. R2 SQL is read-only and in open beta. It reads Parquet only. R2 SQL limitations. R2 SQL troubleshooting.

LimitValueWhat it means here
WritesNot allowedNew rows arrive through Pipelines, not through R2 SQL.
LIMIT10,000 rows maximum
OFFSETNot supportedA later page uses WHERE and ORDER BY, not OFFSET.
JSON inside a string columnNo JSON path filterjsonPatch and the other JSON strings are filtered in the application, or stored as their own columns.
Heavy queriesCan time out or return HTTP 400A large COUNT(DISTINCT), a large sort, or a join of three big tables can be rejected.

KV​

workspace_events_cache holds the short cache for a document audit and for one Logic Pilot trace. KV limits.

LimitPaid planWhat it means here
Writes to the same key1 per secondA burst of saves for one trace can hit this.
Operations per invocation1,000
Value size25 MiB
Key size512 bytes

GraphQL Analytics​

The file-storage cron reads bucket totals from the Cloudflare GraphQL Analytics API. It does not read the files. GraphQL API limits.

LimitValueWhat it means here
Requests300 per 5 minutes for one user or tokenThe cron runs once a day, so this cap is wide for that job.
Accounts in one query1