agent-swarm.devagent-swarm.dev
Reference

Telemetry

What telemetry Agent Swarm sends, which identifiers it carries (org, org email domain, pseudonymous user reference), and how to opt out

Agent Swarm sends usage telemetry to the maintainers to help them understand usage patterns and improve the product. Telemetry is on by default and you can turn it off at any time (see How to opt out). Setup does not ask you to opt in, and it has no telemetry toggle.

This page lists every field that leaves your install.

What is collected

Telemetry tracks lifecycle and operational events. It does not include task descriptions, prompts, outputs, agent names, email addresses, user names, or error messages. The only free-text values it carries are ones you set yourself, such as SWARM_ORG_NAME, and the email domain of your admin (see Identity context).

IDs below (taskId, agentId, workflowId) are random UUIDs generated by your install.

The table below is generated from the event catalog that the telemetry endpoint enforces (src/telemetry-contract). An event or property that is not listed here is rejected by the endpoint, and a build that emits one fails to compile. Events with the ui emitter are sent by the dashboard through the API, as described under Dashboard events. Of those, page.viewed, feature.used and ui.session_started are reserved and not sent by this release, and so are skill.invoked and session.steered.

Every event the ingest accepts with schema_version: 2. A v2 event that is not listed here, or that sends a property not listed for it, is rejected with a typed 400. Emitter is the envelope source.

trigger_surface

Surface that started a chain of work: agent-swarm's AgentTaskSourceSchema values plus other.

Values: slack, ui, api, mcp, github, gitlab, linear, jira, agentmail, schedule, workflow, system, other.

agent-swarm

Common properties, allowed on every event of this product:

PropertyTypeDescription
has_email_channel(boolean)An email channel is configured.
has_embedding_key(boolean)An embedding API key is configured.
has_notification_channel(boolean)A notification channel is configured.
has_slack_channel(boolean)A Slack channel is configured.
install_method(string)Legacy key kept during the v1 window; v2 carries context.install_method.
is_cloud(boolean)Legacy key kept during the v1 window; v2 carries context.deployment.
is_e2b(boolean)Legacy key kept during the v1 window; v2 carries context.deployment.
swarmVersion(string)agent-swarm version. Legacy key kept during the v1 window; v2 carries context.swarm_version.
EventEmitterSinceDescriptionProperties
agent.registeredapi-server2026-09-30An agent registered or reconnected.capabilities (array, required)
isReconnect (boolean, required)
role (string)
compaction.triggeredworker2026-09-30The harness compacted a session context.compactTrigger (string, required)
contextTotalTokens (integer, required)
preCompactTokens (integer, required)
feature.usedui2026-09-30A dashboard feature was used.feature (string, required)
fleet_benchmark.unlockedui2026-10-18An admin supplied the company profile fields for the full benchmark.fields (array of website | team_size | use_case, required)
fleet_benchmark.viewedui2026-10-12The fleet benchmark card was shown.has_org_name (boolean, required)
level (basic | full)
identity.lookupui2026-10-18A dashboard identity lookup matched an existing user or found no match.matched (boolean, required)
identity.user_createdui2026-10-18A user created a dashboard identity. Email and personal names stay out of telemetry.email_kind (work | personal, required)
has_company (boolean, required)
has_function (boolean, required)
surface (identity_modal | setup, required)
integration.connectedapi-server2026-09-30An integration was connected.first_of_type (boolean, required)
provider (string)
type (string, required)
lead.capturedapi-server2026-09-30A consented lead (Teams waitlist or private-beta sign-up). The email stays in feedback_submissions.channel (string, required)
consent (boolean, required)
submission_id (string, required)
notification.dismissedui2026-09-30A notification-centre item was dismissed.key (string, required)
moment (task_failed | first_workflow | default)
notification.submittedui2026-09-30A notification-centre item was submitted.email_domain (string)
key (string, required)
moment (task_failed | first_workflow | default)
user_function (engineer | eng_lead | founder | product | ops | other)
notification.viewedui2026-09-30A notification-centre item was shown.key (string, required)
moment (task_failed | first_workflow | default)
onboarding.completedapi-server2026-09-30Onboarding was completed.seconds_since_install (integer)
seconds_since_start (integer, required)
onboarding.dismissedapi-server2026-09-30Onboarding was dismissed.seconds_since_install (integer)
seconds_since_start (integer, required)
onboarding.first_task_completedapi-server2026-09-30The first task after onboarding completed.method (string)
seconds_since_install (integer)
seconds_since_start (integer, required)
onboarding.startedapi-server2026-09-30Onboarding started.existing_install (boolean, required)
seconds_since_install (integer)
seconds_since_start (integer, required)
onboarding.step_completedapi-server2026-09-30An onboarding step was completed.derived (boolean, required)
method (string)
seconds_since_install (integer)
seconds_since_start (integer, required)
step (string, required)
onboarding.step_failedapi-server2026-09-30An onboarding step failed.error_class (string, required)
seconds_since_install (integer)
seconds_since_start (integer, required)
step (string, required)
onboarding.step_skippedapi-server2026-09-30An onboarding step was skipped.seconds_since_install (integer)
seconds_since_start (integer, required)
step (string, required)
onboarding.step_viewedapi-server2026-09-30An onboarding step was shown.seconds_since_install (integer)
seconds_since_start (integer, required)
step (string, required)
org.profile_updatedui2026-10-12An admin changed the org profile. Lists which fields changed, never their values.fields (array of name | website | team_size | use_case | acquisition_source, required)
surface (enum, required)
org.snapshotapi-server2026-09-30Daily per-install user and agent counts. Counts only: no names, emails or IDs.agents_total (integer ≥0, required)
users_active_7d (integer ≥0, required)
users_admin (integer ≥0, required)
users_linked_github (integer ≥0, required)
users_linked_gitlab (integer ≥0, required)
users_linked_jira (integer ≥0, required)
users_linked_linear (integer ≥0, required)
users_linked_other (integer ≥0, required)
users_linked_slack (integer ≥0, required)
users_suspended (integer ≥0, required)
users_total (integer ≥0, required)
users_with_login (integer ≥0, required)
page.viewedui2026-09-30A dashboard page was viewed.route_template (string, required)
popup.dismissedui2026-10-12A dashboard pop-up was closed without submitting.method (later | close | escape, required)
popup (feedback | org_name | used_by, required)
popup.shownui2026-10-12A dashboard pop-up was shown. Telemetry-gated: never sent while telemetry is off.popup (feedback | org_name | used_by, required)
trigger (task_failed | tasks_completed | workflow_completed | quarterly | missing_org_name, required)
popup.submittedui2026-10-12A dashboard pop-up was submitted. Content stays out of telemetry: only flags for what was filled.answered (integer ≥0)
has_contact (boolean, required)
has_message (boolean, required)
perks (array of used_by | slack_connect)
popup (feedback | org_name | used_by, required)
score (integer ≥0)
ranking.opted_inapi-server2026-10-18An admin opted the install into the public ranking, or the API rebuilt its listing after an org identity change.listing (string, required)
ranking.opted_outapi-server2026-10-18The install left the public ranking: the admin turned it off, or the org name or website was cleared or changed.domain (string, required)
ranking.usageworker2026-10-18Token usage of one session on an install opted into the public ranking. Sent next to session.cost.cacheReadTokens (integer ≥0)
cacheWriteTokens (integer ≥0)
inputTokens (integer ≥0, required)
model (string)
outputTokens (integer ≥0, required)
provider (string)
totalTokens (integer ≥0, required)
schedule.errorapi-server2026-09-30A schedule failed to fire.consecutiveErrors (integer, required)
scheduleType (string, required)
triggeredWorkflows (boolean, required)
wasRecovered (boolean, required)
schedule.executedapi-server2026-09-30A schedule fired.scheduleType (string, required)
triggeredWorkflows (boolean, required)
wasRecovered (boolean, required)
server.shutdownapi-server2026-09-30The API server shut down.sessionsProcessed (integer, required)
signal (string, required)
uptimeMs (integer, required)
server.startedapi-server2026-09-30The API server started.port (integer, required)
session.cancelledworker2026-09-30A session was cancelled.agentId (string, required)
durationMs (integer, required)
errorCategory (string, required)
model (string, required)
provider (string, required)
taskId (string, nullable, required)
trigger_surface (enum, nullable, required)
wasRateLimited (boolean, required)
session.costworker2026-09-30Token usage and cost of one session.agentId (string, required)
cacheReadTokens (integer)
cacheWriteTokens (integer)
durationMs (integer, required)
inputTokens (integer, required)
isError (boolean, required)
model (string, required)
numTurns (integer, nullable, required)
outputTokens (integer, required)
provider (string, required)
reasoningOutputTokens (integer)
taskId (string, nullable, required)
thinkingTokens (integer)
totalCostUsd (number, required)
trigger_surface (enum, nullable, required)
session.endedworker2026-09-30A worker or lead session ended.agentId (string, required)
durationMs (integer)
taskId (string, nullable, required)
tasksProcessed (integer, required)
trigger_surface (enum, nullable, required)
session.failureworker2026-09-30A session failed.agentId (string, required)
durationMs (integer, required)
errorCategory (string, required)
model (string, required)
provider (string, required)
taskId (string, nullable, required)
trigger_surface (enum, nullable, required)
wasRateLimited (boolean, required)
session.startedworker2026-09-30A worker or lead session started.agentId (string, required)
harnessProvider (string, required)
model (string)
role (string, required)
taskId (string, nullable, required)
trigger_surface (enum, nullable, required)
session.steeredapi-server2026-09-30A running session received a steering message.by_kind (string, required)
trigger_surface (enum, nullable, required)
setup.worker_command_copiedui2026-10-18An admin copied a worker command from the setup health popover.harness (enum, required)
target (compose | docker | kubernetes, required)
skill.invokedworker2026-09-30A skill was invoked in a session.skill_name (string, required)
skill_source (string, required)
trigger_surface (enum, nullable, required)
task.cancelledapi-server2026-09-30A task was cancelled.agentId (string)
durationMs (integer)
previousStatus (string, required)
taskId (string, required)
task_source (enum, required)
trigger_surface (enum, required)
task.claimedapi-server2026-09-30An agent claimed a task.agentId (string, required)
taskId (string, required)
task_source (enum, required)
trigger_surface (enum, required)
task.completedapi-server2026-09-30A task completed.agentId (string)
durationMs (integer)
harnessVariant (string)
harnessVersion (string)
provider (string)
taskId (string, required)
task_source (enum, required)
trigger_surface (enum, required)
task.createdapi-server2026-09-30A task was created.harnessVariant (string)
harnessVersion (string)
hasParent (boolean, required)
has_repo (boolean, required)
priority (integer, required)
provider (string)
taskId (string, required)
task_source (enum, required)
trigger_surface (enum, required)
task.failedapi-server2026-09-30A task failed.agentId (string)
durationMs (integer)
harnessVariant (string)
harnessVersion (string)
provider (string)
taskId (string, required)
task_source (enum, required)
trigger_surface (enum, required)
task.session_completedworker2026-09-30A worker session for a task finished.agentId (string, required)
durationMs (integer, required)
exitCode (integer, required)
harnessVariant (string)
harnessVersion (string)
isError (boolean, required)
model (string, required)
provider (string, required)
taskId (string, nullable, required)
trigger_surface (enum, nullable, required)
task.startedapi-server2026-09-30A task started running.agentId (string, required)
taskId (string, required)
task_source (enum, required)
trigger_surface (enum, required)
task.supersededapi-server2026-09-30A task was superseded by another task.agentId (string)
durationMs (integer)
harnessVariant (string)
harnessVersion (string)
provider (string)
reason (string, required)
taskId (string, required)
task_source (enum, required)
trigger_surface (enum, required)
telemetry.enabledapi-server2026-10-12Telemetry was turned on. Sent once, on the first boot or re-init after opting in.via (banner | settings | env | benchmark, required)
ui.session_startedui2026-09-30A dashboard session started.none
used_by.consent_changedui2026-10-12An admin granted or revoked the "used by" logo consent. The request itself goes to /v1/used-by.granted (boolean, required)
has_function (boolean)
has_quote (boolean)
workflow.completedapi-server2026-09-30A workflow run completed.durationMs (integer)
stepsCompleted (integer, required)
stepsFailed (integer, required)
workflowId (string, required)
workflow.createdapi-server2026-09-30A workflow was created.nodeCount (integer, required)
via (api | mcp)
workflowId (string, required)
workflow.deletedapi-server2026-09-30A workflow was deleted.via (api | mcp)
workflowId (string, required)
workflow.failedapi-server2026-09-30A workflow run failed.durationMs (integer)
stepsCompleted (integer, required)
stepsFailed (integer, required)
workflowId (string, required)
workflow.startedapi-server2026-09-30A workflow run started.nodeCount (integer, required)
triggerType (string, required)
workflowId (string, required)

Fields on every event

Each event is a JSON envelope with these top-level fields:

  • schema_version — always 2
  • product — always agent-swarm
  • event — the event name from the table above
  • occurred_at — timestamp
  • source — the emitter (api-server or worker)
  • actor_mode — always anonymous
  • actor_anonymous_id — the installation ID (see Identifiers)
  • context — the identity block described under Identity context

Every event also adds these to properties. They are the same facts context carries, kept under their older names while existing dashboards move over:

  • swarmVersion — the Agent Swarm version
  • is_cloud — boolean, whether the event came from a cloud-hosted swarm or a self-hosted install. True when MCP_BASE_URL points at a host we operate or SWARM_CLOUD is set. The hostname itself is never sent.
  • is_e2b — boolean, whether the process runs inside an E2B sandbox
  • has_embedding_key — boolean, whether an embedding-capable key (EMBEDDING_API_KEY or OPENAI_API_KEY) is configured, i.e. whether semantic memory search is likely enabled
  • has_slack_channel / has_email_channel / has_notification_channel — booleans indicating whether an outbound notification channel (Slack, AgentMail email, or either) is configured. Presence only — never a token, channel ID, or address.
  • install_method — enum: onboard_interactive, onboard_noninteractive, e2b, or manual. Which entry point produced this install.

And these to metadata:

  • transport (always https) and environment (development for git checkouts, test for NODE_ENV=test, otherwise production; override with DESPLEGA_TELEMETRY_ENV)
  • is_cloud — boolean, from SWARM_CLOUD
  • organization_id (optional) — your SWARM_ORG_ID, when set
  • organization_name (optional) — your SWARM_ORG_NAME, when set
  • install_preset (optional) — the onboard wizard preset used (dev, content, research, solo, or custom), when the install went through the wizard. Allow-listed against the wizard's known preset IDs — any other value (including free text accidentally placed in INSTALL_PRESET) is omitted rather than forwarded.
  • install_created_at (optional) — ISO timestamp of when this install's identity was first minted, persisted once alongside the installation ID. Only set when this process genuinely mints a new installation ID; an existing installation ID with no stored anchor emits this field as absent, not back-filled with the current time — absence means "pre-existing install, anchor unknown". Use min(occurred_at) per installation_id in ClickHouse as the real anchor for that population.

Identity context

context says which organization and which person an event belongs to, without carrying anything that identifies them directly.

FieldWhat it holdsHow it is derived
org_idAn organization IDSWARM_ORG_ID when it is set to a valid ID (cloud deployments). Otherwise a random org_ + 16 hex characters, minted once and stored as telemetry_org_id.
org_nameThe name you gave the organizationSWARM_ORG_NAME, when set. null otherwise.
org_domainThe email domain of your admin, such as example.comThe domain of the earliest-created active user who has an admin grant, falling back to the earliest active user with an email. Lowercased, stored as telemetry_org_domain, and recomputed when a user changes and hourly. The address itself is never sent.
org_websiteThe company website an admin declared, such as example.comSWARM_ORG_WEBSITE, a bare hostname. Left out when unset.
team_sizeThe organization's team size: 1, 2-10, 11-50 or 51+Not sent by this version. Reserved in the contract for the company profile.
use_caseThe organization's main use case: features, bugs, review, ops, docs or dataNot sent by this version. Reserved in the contract for the company profile.
user_refA pseudonymous reference to the person who caused the eventu_ plus the first 32 hex characters of sha256(installation ID + ":" + user ID). Stable inside your install, different on every other install, and not reversible to the user. null for events nobody caused (server start, schedules, heartbeat).
user_roleadmin or memberFrom the user's role grants. null when user_ref is null.
planself-host-free or cloudcloud for a cloud deployment, else self-host-free.
deploymentself-host, cloud or e2bWhere the process runs.
swarm_versionThe Agent Swarm versionpackage.json.
install_methodHow the install was producedSame as the property of the same name.
install_presetThe onboard wizard presetSame as the metadata field. null when there is none.
acquisition_sourceHow the organization discovered Agent SwarmSWARM_ORG_ACQUISITION_SOURCE: search, github, social, colleague, newsletter, event, ai_assistant, hacker_news, or other. null when unset or invalid.
team_sizeThe organization's team sizeSWARM_ORG_TEAM_SIZE: 1, 2-10, 11-50, or 51+. Omitted when unset or invalid.
use_caseThe organization's main use caseSWARM_ORG_USE_CASE: features, bugs, review, ops, docs, or data. Omitted when unset or invalid.
user_functionThe actor's job function, separate from RBAC roleusers.metadata.jobFunction: engineer, eng_lead, founder, product, ops, or other. Cached with the RBAC role and invalidated after user updates. Omitted without an actor or a valid function.

Workers receive company profile settings through the resolved configuration at boot and during reload. They read telemetry_org_domain through the API. Workers do not query users for job functions. The API resolves those functions for events with user actors.

What is never sent: email addresses (only the domain of one address), user names, raw user IDs, task content, prompts or outputs.

Which surface started the work

Task and session events carry trigger_surface: the surface that started the whole chain of work (slack, ui, api, mcp, github, gitlab, linear, jira, agentmail, schedule, workflow, system, or other). It is the source of the root task. A Slack request that the lead splits into five worker tasks counts as six Slack tasks, and every task.* and session.* event of the chain reports slack. Tasks the swarm creates for itself, such as the heartbeat checklist and boot triage, are system. task_source keeps the raw source of each task on its own, for debugging delegation.

Daily organization snapshot

Once a day each install sends one org.snapshot event from the API server. It carries counts only: active and suspended users, admins, users who can log in, users with a linked Slack, GitHub, GitLab, Linear, Jira or other identity, users who requested a task in the last seven days, and registered agents. It carries no names, email addresses or IDs. The send time is stored as telemetry_last_snapshot_at, so a restart does not send a second one the same day.

Dashboard events

The dashboard never sends telemetry to Desplega itself. It posts each event to its own API (POST /api/telemetry/ui-event), and the API sends it like any server event, with the same context. The API accepts only the catalogued dashboard events, checks their properties against the catalog, and drops them when telemetry is off. The dashboard also holds them back until GET /status has loaded and reports telemetry on.

EventWhenProperties
notification.viewedSomeone opens the notification bell with an item in itkey: the notification
notification.dismissedSomeone dismisses a notification cardkey
notification.submittedSomeone submits a notification cardkey, and email_domain when they typed an email: the domain only, never the address
popup.shownThe organization-name prompt or the feedback pop-up openspopup (org_name, feedback) and trigger: why it opened
popup.dismissedIt closes without being submittedpopup and method: later, close or escape
org.profile_updatedAn admin saves the organization name or websitefields (name, website: which ones changed, never their values) and surface: prompt, setup or settings
used_by.consent_changedAn admin grants or revokes the "used by" logo consentgranted
fleet_benchmark.viewedThe "Your swarm vs. similar swarms" card shows on Home, at most once per page loadhas_org_name
popup.submittedIt is submittedpopup, has_message and has_contact: whether a message or a name or email was filled in, never the text. The feedback card adds the 0-10 score

telemetry.enabled (via: banner, settings, benchmark or env) is sent by the API server once per opt-in: on the first boot of an install with telemetry on, or when someone turns telemetry back on from the dashboard banner, the Home benchmark card, or the Configuration page. Turning it on that way applies without a restart.

While telemetry is off, admins see a dashboard banner that explains what turning it on sends. It cannot report that it was shown, since telemetry is off. After "Dismiss" it never comes back.

Fleet benchmark

With telemetry on and an organization name set, Home compares this swarm with other swarms over the last 30 days. It compares tasks per week and task success rate. The API reads fleet percentiles from GET https://proxy.desplega.sh/v1/fleet/benchmarks at most once a day.

When the swarm stores a website, team size, and use case, the API sends team_size and use_case to the proxy. The proxy compares against that peer group only when it meets the same minimum of 20 installs as the whole fleet. Otherwise, it uses the whole fleet. Within the selected fleet, comparisons use the same agent-count bucket when at least 20 installs qualify, otherwise all qualifying installs.

The request carries no installation ID or organization name. The response contains aggregates over at least 20 installs with 10 or more finished tasks. With telemetry off, the API never makes the request. The API computes the comparison locally, so this swarm's own numbers never leave it for the benchmark.

GET /api/fleet-benchmark adds level and peerGroup to its existing response. Level is full when website, team size, and use case are set. Otherwise, level is basic. Full requests include team_size and use_case query parameters. The fleet cache key includes these peer filters.

peerGroup contains {teamSize, useCase, orgs} when the proxy returns a matching group. It is null when the proxy returns the whole fleet or omits peer_group. With telemetry off, local contains DB counts for the last 30 days, without a proxy request or ingest requirement. Existing comparisons still require a known proxy ingest date.

Public ranking

agent-swarm.dev/ranking ranks installs by tokens used, like OpenRouter's app rankings. It is off by default (PUBLIC_RANKING_ENABLED=false). Opting in is all it takes: no PR and no approval step. An install is listed when all of these hold:

  • Telemetry is on.
  • The organization name (SWARM_ORG_NAME) and company website (SWARM_ORG_WEBSITE) are set. The placeholders Swarm and Your Swarm do not count as a name.
  • An admin clicked Get ranked in Settings → Configuration → Branding.
  • The install sent telemetry in the last 7 days and used tokens in the period shown.

The install shows at the proxy's next hourly refresh. If two installs list the same website, the one that opted in first holds the entry. Desplega can hide a domain that abuses the ranking.

Exactly these fields leave the install for the ranking:

EventSent byFields
ranking.opted_inAPI, on Get ranked and when the organization name changeslisting: the organization name and website, the category and subcategory you picked, the optional description and source code URL, and the time it was built. The same JSON is stored as the telemetry_ranking config key.
ranking.opted_outAPI, when the setting turns offdomain
ranking.usageEach worker, after every session with cost datatotalTokens, inputTokens, outputTokens, cacheReadTokens, cacheWriteTokens, and the session's model and provider. No task, agent or cost.

Token totals are public and exact. The page shows your total tokens per day, week and month, and tokens per model, the same as OpenRouter.

To leave, turn the setting off, turn telemetry off, or clear the organization name or website. Changing the website also opts out; click Get ranked again to list the new domain. The entry disappears at the proxy's next hourly refresh.

A notification card that asks for your email, and the feedback dialog, are different. Those send what you type to the feedback endpoint (https://proxy.desplega.sh/v1/feedback, overridable with the feedback_endpoint config key), and only when you submit them. They are not telemetry, so the opt-out does not block them.

The feedback card asks self-hosted admins one 0-10 question. It shows once after a completed workflow run, after 10 completed tasks on an install at least 3 days old, or in the session after the one that first saw a failed task, and then at most once a quarter. A tap on a score sends it; a follow-up question and contact details are optional. With telemetry off, the card still works, but it sends no installation ID, and sends the organization name only when you tick "Include my organization name".

Identifiers

  • Installation ID. Each installation generates a random, opaque identifier (e.g. install_a1b2c3d4e5f6g7h8), stored in the swarm_config table. It is not derived from your hostname, domain, or account, and it stays the same across restarts so events from one install link together.
  • Organization identity. The installation ID is random, but events are not anonymous to your organization. Every event carries an organization ID (org_id), the organization name when you set SWARM_ORG_NAME, the company website when you set SWARM_ORG_WEBSITE (org_website), and the email domain of your admin (org_domain), so they are attributable to your organization. Cloud deployments set SWARM_ORG_ID. The setup wizard's "name your swarm" step and the dashboard's organization-name prompt set SWARM_ORG_NAME. Leave SWARM_ORG_NAME unset, or opt out, if you do not want to share an organization name.
  • People. Events caused by a person carry a pseudonymous user_ref, never an email address or a name. See Identity context.
  • No IPs logged. The telemetry endpoint (proxy.desplega.sh) does not store client IP addresses.
  • No auth required. Events are sent in anonymous actor mode with no authentication headers.

Installation ID mechanism

On first startup, the telemetry module checks swarm_config for a telemetry_installation_id key:

  1. If found, it reuses the existing ID for consistent tracking across restarts.
  2. If not found, it generates a new random ID (install_ + 16 hex characters) and persists it.
  3. If config access fails (e.g. worker without DB access), an ephemeral ID (ephemeral_ prefix) is generated for that session only.

Workers access the installation ID via HTTP (respecting the DB boundary invariant), while the API server reads it directly from the database.

The API server also stores the organization ID as telemetry_org_id (and the admin's email domain as telemetry_org_domain) next to the installation ID. Workers read both through the same HTTP config call, so events from workers and from the server carry the same organization.

A telemetry_installed_at timestamp is minted alongside the installation ID, but only when this process genuinely mints a new installation ID — it anchors "when this install came into being" independent of server restarts. An existing installation ID with no stored anchor (an install that predates this field) leaves it unset, and the field is omitted from events rather than back-filled with the current time; use min(occurred_at) per installation_id in ClickHouse as the real anchor for that population.

How to opt out

Set the ANONYMIZED_TELEMETRY environment variable to false:

.env
ANONYMIZED_TELEMETRY=false

For Docker workers, also add it to .env.docker:

.env.docker
ANONYMIZED_TELEMETRY=false

You can also set it in the dashboard under Settings → Configuration → Telemetry & observability. A value stored there takes precedence over the deployment environment.

Two things reach Desplega with telemetry off, because each is an explicit action: feedback you submit, and the "Show our logo on agent-swarm.dev" consent, which sends your organization name and website when you change it. A company website also points the dashboard logo at https://proxy.desplega.sh/v1/logo, so browsers that show the dashboard load the logo from Desplega's proxy; set your own SWARM_ORG_LOGO_URL to avoid that.

When disabled:

  • No events are sent
  • No installation ID is generated or stored
  • The telemetry module initializes as a no-op
  • The dashboard stops sending notification events. GET /status reports the effective state as telemetry.enabled, which the dashboard reads. A server that predates that field is treated as enabled.

Technical details

  • Endpoint: POST https://proxy.desplega.sh/v1/events
  • Timeout: 5 seconds per request
  • Failure mode: Fire-and-forget — telemetry never throws exceptions or blocks operations
  • Module: src/telemetry.ts (importable from both API server and workers)

On this page