---
title: "Read Visitor"
method: GET
path: "/api/demo/admin/visitors/{user_id}"
tags: ["demo-observability"]
---

# Read Visitor

`GET /api/demo/admin/visitors/{user_id}`

## Request

- Base URL: `https://api.quadrillion.io` (the document declares no server; this is the origin it was published from)
- URL: `https://api.quadrillion.io/api/demo/admin/visitors/{user_id}`
- Auth: none declared

## Path parameters

- `user_id` integer, required

## Query parameters

- `hours` integer

## Response `200`

Successful Response

- VisitorDetail
  - `queriedAt` string, required
  - `windowStart` string, required
  - `visitor` VisitorSummary, required: One demo visitor and what they consumed in the window.
    - `userId` string, required
    - `email` string, required
    - `name` string, required
    - `standbyState` string, nullable, required
    - `joinedAt` string, required
    - `lastActiveAt` string, nullable, required
    - `workspaceIds` string[], required
    - `requests` integer, required
    - `modelCostUsd` string, required
    - `compute` ComputeTotals, required: What a set of ledger boxes cost at on-demand rates. ``unpriced_boxes`` counts boxes the rate card could not price, so a total that excludes them says so instead of reading as complete.
      - `boxes` integer, required
      - `openBoxes` integer, required
      - `onDemandUsd` string, required
      - `unpricedBoxes` integer, required
    - `furthestStage` string, nullable, required
    - `tasks` integer, required
    - `tasksFailed` integer, required
  - `workspaces` WorkspaceSummary[], required
    - `workspaceId` string, required
    - `name` string, nullable, required
    - `createdAt` string, required
  - `boxes` SandboxRow[], required
    - `provider` 'modal' | 'aws' | 'gcp' | 'sail', required: The compute backend that owns a sandbox.
    - `sandboxId` string, required
    - `sandboxAlias` string, required
    - `lifecyclePhase` 'pending' | 'running' | 'stopped' | 'deleted', required: Current phase of a sandbox in the ledger's state table. ``STOPPED`` is not ``DELETED``. ``checkpoint_and_stop`` retains the disk and is the normal cleanup path on GCP and Sail, so a stopped sandbox is still billing for storage. ``RUNNING`` and ``STOPPED`` are therefore the two "open" phases: a sandbox in either one still exists and still costs money. The state table's partial unique index on ``sandbox_alias`` is scoped to exactly this pair, so that an alias resolves to at most one open sandbox. Adding or removing an open phase means changing that index in the same migration.
    - `warmth` 'cold' | 'warm_provider' | 'warm_gateway', required: How much of a sandbox acquisition was served from something existing. Three values, not two. Provider-level reuse and gateway session reuse are separate events raised from separate call sites, and they have different latency profiles; collapsing them into one "warm" value loses the distinction the cold-start work depends on.
    - `resourceClass` string, required
    - `gpuType` string, nullable, required
    - `gpuCount` integer, nullable, required
    - `isPacked` boolean, required
    - `isIsolated` boolean, required
    - `commitmentBasis` 'on_demand' | 'reservation' | 'capacity_block', required: Which capacity pool a sandbox's launch actually consumed. A property of the individual launch, not of the resource class. The same shape lands differently from one start to the next: GCP GPU hosts launch into ``GCP_GPU_RESERVATION`` "only while the reservation has an idle slot" (``gcp_client.py:32-35``) and take the on-demand path when it is unset (``:166``), and an AWS capacity-block launch "falls back to the on-demand path instead of failing the kernel start" when the slot is raced away (``ec2_client.py:1268``). So two identical ``p5.48xlarge`` rows can carry completely different real cost, and nothing at read time can tell them apart — the pool a launch consumed is unrecoverable once the box is gone. That is what makes this a stored column rather than something the rate card derives. ``RESERVATION`` and ``CAPACITY_BLOCK`` are separate because the mechanisms are: a GCE specific reservation holds ``RESERVATION_BOUND`` VMs, while AWS capacity blocks are bought on a rolling cadence and reclaimed at expiry (QUA-2813 and QUA-2770 respectively). Neither is marginal spend.
    - `deploymentEnvironment` string, required
    - `notebookId` string, nullable, required
    - `kernelId` string, nullable, required
    - `orgId` string, nullable, required
    - `orgName` string, nullable
    - `workspaceId` string, nullable, required
    - `userId` string, nullable, required
    - `userEmail` string, nullable
    - `runScope` string, nullable, required
    - `createdAt` string, required
    - `lastTransitionAt` string, required
    - `causeOfDeath` 'idle_reap' | 'preemption' | 'health_check_kill' | 'readiness_failed' | 'lease_released' | 'lease_expired' | 'age_limit' | 'explicit_teardown' | 'superseded' | 'unknown_reconciled', required: Why a sandbox left its last open phase. ``UNKNOWN_RECONCILED`` is first-class and load-bearing. A hard preemption leaves nobody running to write a truthful cause, so the reconciler has to be able to say it does not know. Without this value the reconciler is forced to pick a plausible cause instead, and every other cause in the ledger stops being trustworthy.
    - `closedAt` string, nullable, required
    - `runningSeconds` integer, nullable, required
    - `stoppedSeconds` integer, nullable, required
    - `currentRunSeconds` integer, nullable, required
    - `cost` union, required
      - CostAvailable: A derived cost floor for one box, split by the kind of money it is. Three figures in three different units, and **none may be added to another**. The rate module refuses to produce a total and this shape keeps that refusal, because summing prepaid capacity with marginal spend produces a figure no invoice will ever show. - ``on_demand_usd_floor`` is money that left the account, underestimated. - ``committed_capacity_list_value_usd`` is what the capacity this box drew would have cost at list price had it not been prepaid. Not spend: the slot was paid for whether or not anything ran in it. - ``consumed_capacity_hours`` is slot-hours drawn from a commitment. It needs no rate card, so it is the one figure here that cannot be wrong. ``basis`` names the pool the box's *compute* drew on, which is what a dashboard groups by. It does not promise every dollar below it is that kind; the fields do that.
        - `available` true
        - `basis` 'on_demand' | 'committed', required
        - `onDemandUsdFloor` string, nullable, required
        - `committedCapacityListValueUsd` string, nullable, required
        - `consumedCapacityHours` string, nullable, required
        - `billingShape` string, required
        - `rateProvenance` 'vendor_list' | 'vendor_reserved', required
        - `rateRegion` string, nullable, required
        - `rateRevisedOn` string, required
        - `overheadMultiplier` string, required
      - CostUnavailable: No derivable figure, and the reason there is none. Every reason is a property of one box rather than of the deployment, and every one of them yields no dollars at all. None is a zero: a zero renders as free, and nothing in the ledger is free. Three of the four originate in ``compute.sandbox_cost`` and are passed through rather than collapsed. A Sail box (``usage_billed_provider``) and a box whose machine type the card has no entry for (``unpriced_shape``) are different problems with different fixes, and a reader that has to act on one should not have to guess which it is looking at. The fourth, ``no_duration_evidence``, is this module's own: it is about the event log rather than the rate card. ``unknown_resource_class`` arrives by both routes. The rate module returns it for a shape it cannot resolve; the read layer returns it as well, for a dedicated box whose ``gpuCount`` is null -- unresolvable for the different reason that the hardware was never learned. Those have different fixes, a rate-card entry against a reconciler backfill, but they do not need separate reasons, because the row already carries the discriminator: this reason beside a null ``gpuCount`` is the second case, and every other occurrence is the first.
        - `available` false
        - `reason` 'usage_billed_provider' | 'unpriced_shape' | 'unknown_resource_class' | 'no_duration_evidence', required
  - `models` ModelRow[], required
    - `model` string, nullable, required
    - `provider` string, required
    - `servedVia` string, nullable, required
    - `requests` integer, required
    - `visitors` integer, required
    - `inputTokens` integer, required
    - `outputTokens` integer, required
    - `cachedTokens` integer, required
    - `costUsd` string, required
    - `ttft` LatencyStats, required: Percentiles over the samples that carried a measurement. ``samples`` is reported beside the percentiles so a figure drawn from a handful of calls, or from none, reads as such.
      - `samples` integer, required
      - `p50Ms` number, nullable, required
      - `p95Ms` number, nullable, required
    - `duration` LatencyStats, required: Percentiles over the samples that carried a measurement. ``samples`` is reported beside the percentiles so a figure drawn from a handful of calls, or from none, reads as such.
      - `samples` integer, required
      - `p50Ms` number, nullable, required
      - `p95Ms` number, nullable, required
    - `lastRequestAt` string, required
  - `agentTools` AgentToolRow[], required
    - `name` string, required
    - `calls` integer, required
    - `failed` integer, required
    - `workspaces` integer, required
    - `duration` LatencyStats, required: Percentiles over the samples that carried a measurement. ``samples`` is reported beside the percentiles so a figure drawn from a handful of calls, or from none, reads as such.
      - `samples` integer, required
      - `p50Ms` number, nullable, required
      - `p95Ms` number, nullable, required
  - `stages` StageProgress[], required
    - `stage` string, required
    - `tasks` integer, required
    - `completed` integer, required
    - `failed` integer, required
    - `cancelled` integer, required
    - `firstTaskAt` string, required
    - `lastCompletedAt` string, nullable, required
  - `warnings` string[], required

## Other responses

- `422`: Validation Error

## Changes

- **2026-09-29** `24240445b715`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/quadrillion/apis/quadrillion-cloud-api/changes/api/demo/admin/visitors/:user_id/get.md)

---

[API](https://skmtc.dev/quadrillion/apis/quadrillion-cloud-api.md) - [All operations](https://skmtc.dev/quadrillion/apis/quadrillion-cloud-api/llms.txt) - [OpenAPI document](https://skmtc.dev/quadrillion/apis/quadrillion-cloud-api/revisions/2e1425e0e054?raw)
