> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stackshift.cloud/llms.txt
> Use this file to discover all available pages before exploring further.

# Operations, events, webhooks, usage, and audit

> Observe durable mutations, consume resumable events, verify at-least-once webhooks, inspect usage, and query immutable audit history.

Use the right record for the question: an operation tracks long-running work, an event reports a domain change, a webhook is an at-least-once delivery of an immutable event, usage is append-only metering, and audit records who attempted or completed a governed action.

## Follow a durable operation

Every long mutation returns an operation with ID, sandbox ID, type, status, step, generation, attempt counters, deadline, retryability, compensation requirement, timestamps, and optional failure/result data.

```bash theme={null}
stackshift --output json sandbox operation <operation-id>
stackshift --timeout 15m sandbox operation <operation-id> --wait
```

Store the ID before waiting. A client timeout does not cancel or roll back server work. Refetch the operation, then the affected resource. If terminal status is not `succeeded`, surface the failure rather than returning a partial resource as success.

## Consume sandbox events

Events use a versioned envelope:

```json theme={null}
{
  "id": "<event-uuid>",
  "api_version": "2026-08-01",
  "type": "sandbox.ready",
  "created_at": "2026-08-13T12:00:00Z",
  "account_id": "<account-uuid>",
  "sandbox_id": "<sandbox-uuid>",
  "sequence": 17,
  "data": {
    "object": {},
    "previous_attributes": {"observed_state": "starting"}
  }
}
```

The per-sandbox SSE stream is monotonic by `sequence`. Delivery is at least once, so consumers must deduplicate by event ID and commit their last fully processed sequence. On reconnect, pass `after_sequence`; after a gap, refetch current resource state.

The catalog covers lifecycle/readiness, executions/processes, snapshots/volumes, policy application, and quota warnings/exceeded events. Do not infer a global ordering across sandboxes or endpoints.

## Create and verify a webhook

Create endpoints only at HTTPS destinations. Subscribe to exact event types or supported prefixes such as `sandbox.snapshot.*`:

```ts theme={null}
const created = await client.webhooks.create(
  'https://hooks.example.com/stackshift',
  ['sandbox.ready', 'sandbox.failed', 'sandbox.snapshot.*'],
  'customer-hooks-v1',
)

// shown once: store outside logs and application tables
await secretManager.put('stackshift-webhook-secret', created.signing_secret)
```

The signing secret is returned only at endpoint creation or explicit rotation. Verification must use the exact raw request bytes before JSON parsing. Official helpers validate `HMAC-SHA256(timestamp + "." + rawBody)`, the `v1=` signature, constant-time equality, and a timestamp tolerance.

```ts theme={null}
import { verifyWebhook } from '@stackshift-cloud/sandbox'

const valid = await verifyWebhook(
  rawBody,
  timestampHeader,
  signatureHeader,
  process.env.STACKSHIFT_WEBHOOK_SECRET!,
)
if (!valid) return new Response('invalid signature', { status: 401 })
```

After verification:

1. Reject events outside your accepted API versions/types.
2. Deduplicate by event ID or delivery ID in durable storage.
3. Enqueue work, then return a success response quickly.
4. Make handlers idempotent because retries and manual replay can duplicate delivery.
5. Never fetch a URL from event data without applying your own validation.

## Delivery history and replay

`GET /api/v1/webhook-deliveries` filters by endpoint or sandbox. Detail includes status, attempt count, next retry, safe response excerpt, error code, and each attempt's timing/status. States are `pending`, `delivering`, `succeeded`, and `dead_letter`.

`POST /api/v1/webhook-deliveries/{deliveryId}/replay` requires an idempotency key and creates a new signed delivery attempt from the original immutable event. Replay is permission-controlled, rate-limited, and audited. It does not create a new domain event.

Repeated failures may disable an endpoint. Fix TLS/DNS/response behavior, rotate a compromised signing secret, then explicitly re-enable according to policy.

## Usage and quotas

`GET /api/v1/usage` accepts `start`, `end`, `timezone`, `sandbox_id`, and `group_by` (`sandbox`, `project`, `team`, `profile`, `tag`, or `product_source`). Records contain metric, quantity, unit, exact window, and safe metadata. `GET /api/v1/usage/export` returns server-generated CSV for the same filters.

Billable meters can include reserved CPU seconds, reserved memory GB-seconds, active/sleeping disk GB-hours, snapshot/artifact storage, public egress, and optional execution/browser units. Price is separate from raw usage. Show an estimate only when the response provides an authoritative complete catalog, currency, and qualification; otherwise show units and quotas without inventing cost.

## Audit history

`GET /api/v1/audit-events` is permission-controlled and cursor-paginated. Filter by actor, action, target type/ID, outcome, source, and time range. Audit entries record actor, action, target, outcome, source, request/correlation ID, and safe change metadata.

Events and audit are not interchangeable: events drive resource integrations, while audit establishes accountable activity including denied or failed actions. Neither may contain secret plaintext, terminal tickets, signed URLs, or internal worker credentials.

<CardGroup cols={2}>
  <Card title="API and SDKs" href="/ai-sandboxes/api-and-sdks">Review endpoint and client conventions.</Card>
  <Card title="Troubleshooting" href="/ai-sandboxes/troubleshooting">Build a safe diagnostic bundle.</Card>
</CardGroup>
