Hanzo AI

Sentry

Sentry catches an exception off a running application and groups it into an issue. Here that is /v1/o11y (381 operations) to read and /v1/event (12) to ingest, and the ingest speaks Sentry's own wire.

Sentry takes a stack trace off a running application, groups it by fingerprint into an issue, and keeps the traces, logs and recordings around it. Two capabilities answer that: /v1/o11y (381 operations) holds the issues, traces, alert rules and dashboards, and /v1/event (12 operations) is the ingest endpoint, which accepts the Sentry envelope byte-for-byte — an application already instrumented reports here by changing its DSN and nothing else.

The structural difference is where the tenant comes from. Sentry names the organization in nearly every path (/api/0/projects/{org}/{project}/issues/). Here it never appears: on ingest the org is derived from the DSN key, on reads from the validated bearer. The only identifier you carry is the project, and on most reads it is optional.

Start here

One key and one project: the project's DSN drops into Sentry.init, and the issues read back with no org slug anywhere.

# 1. mint a key — sk- belongs on a server, pk- is safe in a browser
curl -sS -X POST https://api.hanzo.ai/v1/account/keys \
  -H "Authorization: Bearer $HANZO_SESSION" \
  -H 'Content-Type: application/json' \
  -d '{"type":"secret"}'

# 2. create the project — the DSN comes back with it
curl -sS -X POST https://api.hanzo.ai/v1/o11y/sentinel/projects \
  -H "Authorization: Bearer $HANZO_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"name":"backend","platform":"python"}'

# 3. read the grouped issues back
curl -sS "https://api.hanzo.ai/v1/o11y/sentinel/issues?status=unresolved&period=24h" \
  -H "Authorization: Bearer $HANZO_API_KEY"

Step 2 returns data.dsn. Paste it into Sentry.init and an already-instrumented application reports to POST /v1/event/{project}/envelope with nothing else changed — that endpoint is credentialed by the DSN key, not by the bearer above. Step 3 is the read side: the org came from the key, so project is an optional narrowing rather than a required path segment.

Core capabilities

CapabilityWhat it doesOperations
/v1/o11yIssues, traces, alert rules, dashboards and logs — everything you read381, across 287 paths
/v1/eventThe ingest endpoint. Takes the Sentry envelope byte-for-byte, keyed by the DSN12
/v1/webhookWhere an issue leaves the platform. Each endpoint carries its 7-day delivery and failure counts8

Nouns

Errors and issues

SentryHanzo
OrganizationYour org — from the DSN key on ingest, the bearer on reads. Never a path segment
ProjectPOST /v1/o11y/sentinel/projects — you set name and platform, the server assigns id and key
DSN, client keyThe dsn field on the project, derived fresh on every read of it
Rotating a client keyPOST /v1/o11y/sentinel/projects/{id}/keys/rotate
Envelope ingest, current SDKsPOST /v1/event/{project}/envelope
store ingest, pre-envelope SDKsPOST /v1/event/{project}/store
The /api/{id}/envelope/ path an SDK buildsPOST /v1/o11y/api/{project_id}/envelope/ — received verbatim, not published
Issue, grouped by fingerprintGET /v1/o11y/sentinel/issues, also GET /v1/o11y/errortracking/issues
Resolve, ignore, reopen, assignPUT /v1/o11y/sentinel/issues/{id} — one route, status and assignee
An issue's occurrencesGET /v1/o11y/sentinel/issues/{id}/events
One event by idGET /v1/o11y/sentinel/events/{id}
Discover queryPOST /v1/o11y/sentinel/discover — filters, groupings and aggregations in, columns and rows out
Event-rate graph for a projectGET /v1/o11y/sentinel/stats
Full-text over raw eventsGET /v1/o11y/sentinel/logs
Deleting a projectDELETE /v1/o11y/sentinel/projects/{id} — the DSN stops resolving at once, retained events are untouched

Performance

SentryHanzo
Transaction, traceGET /v1/o11y/traces, one at GET /v1/o11y/traces/{traceId}
Span waterfallPOST /v1/o11y/traces/{traceId}/waterfall
Flame graphPOST /v1/o11y/traces/{traceId}/flamegraph
Service latency and error rateGET /v1/o11y/services/list
Slowest operations in one servicePOST /v1/o11y/service/top_operations — p50, p95 and p99 each
Apdex thresholdGET /v1/o11y/settings/apdex, set with POST on the same path
Where one span sits among its peersPOST /v1/o11y/span_percentile
Outbound HTTP dependenciesPOST /v1/o11y/third-party-apis/overview/list
Which requests are failingGET /v1/o11y/sentinel/traces — traces a project's errors landed on, error count each
Every error on one traceGET /v1/o11y/sentinel/traces/{id}
Funnel across a traceGET /v1/o11y/trace-funnels/list

Releases, replay and the rest

SentryHanzo
ReleaseGET /v1/platform/releases — the versions that reached the cluster, observed rather than declared
Session Replay ingestPOST /v1/event/replay — an rrweb batch keyed by sessionId, stored before the call answers
Metric alert ruleGET /v1/o11y/rules, created with POST, dry-run at POST /v1/o11y/rules/test
Currently firingGET /v1/o11y/alerts, most recent at GET /v1/o11y/alerts/last
Where an alert is deliveredGET /v1/o11y/channels, checked with POST /v1/o11y/channels/test
DashboardGET /v1/o11y/dashboards, shared read-only at /v1/o11y/dashboards/{id}/public
Logs, and Live TailGET /v1/o11y/logs, GET /v1/o11y/logs/livetail
Retention and quotaPOST /v1/o11y/settings/ttl — default TTL in days, per-label rules, cold storage
Spike protection, per-key rate limitPOST /v1/o11y/gateway/ingestion_keys/{keyId}/limits
Cron monitors and check-ins/v1/tasks (5) — a durable engine, not a heartbeat beside an issue
Issue webhooks/v1/webhook (8) — every endpoint carries its 7-day delivery and failure counts
Installing a third-party integrationPOST /v1/o11y/integrations/install
Browser errors from a page with no bundlerGET /v1/event/tag.js — one script tag and a publishable pk- key
Data exportPOST /v1/o11y/export_raw_data
LLM spans, tokens and costGET /v1/o11y/llm/traces

The call

Sentry. The DSN comes out of the project settings page, and reads name the org and the project in the path:

curl -sS "https://sentry.io/api/0/projects/acme/backend/issues/?query=is%3Aunresolved&statsPeriod=24h" \
  -H "Authorization: Bearer $SENTRY_TOKEN"

curl -sS -X PUT "https://sentry.io/api/0/issues/5551212/" \
  -H "Authorization: Bearer $SENTRY_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"status":"resolved"}'

Hanzo. Create the project and the DSN comes back with it:

curl -sS -X POST https://api.hanzo.ai/v1/o11y/sentinel/projects \
  -H "Authorization: Bearer $HANZO_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"name":"backend","platform":"python"}'

Copy data.dsn into Sentry.init and the application is migrated. The SDK appends its own /api/<project>/envelope/ to whatever DSN it holds, so no code below that one string changes.

Reading back, and resolving:

curl -sS "https://api.hanzo.ai/v1/o11y/sentinel/issues?status=unresolved&period=24h" \
  -H "Authorization: Bearer $HANZO_API_KEY"

curl -sS -X PUT https://api.hanzo.ai/v1/o11y/sentinel/issues/5551212 \
  -H "Authorization: Bearer $HANZO_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"id":"5551212","status":"resolved","assignee":"[email protected]"}'

Three things follow from that shape. acme appears nowhere, so there is no org slug to keep in step with the token that reads it — and because the org is the key's, project is an optional narrowing rather than a required coordinate: drop it and you read every project's unresolved issues, which in Sentry is a second endpoint under a different scope. The DSN is derived from the project on each read rather than stored beside it, so there is no second record to keep current; DELETE on the project stops the DSN resolving in the same instant. And rotation bumps a watermark that keys below stop verifying against, so the old key is dead on the next request rather than once a revocation has reached every relay.

What does not carry

No source maps and no symbolication. Sentry uploads artifact bundles and debug files and un-minifies the stack server-side. Nothing here does — there is no upload route and no symbolicator, so a minified frame is stored and shown minified. If your frontend stacks are the reason you read Sentry, this is the one to weigh before anything else.

A release is observed, not declared. GET /v1/platform/releases lists the deployments that genuinely reached the cluster; one that failed or is still building is excluded. Error tracking never takes a release declaration: deploying through Hanzo's own PaaS does mint one (POST /v1/projects/{slug}/releases), but no route attaches commits to a release or ties one to an issue — so sentry-cli releases new, set-commits and finalize have nothing to call, and the suspect-commit attribution and regression detection built on that graph do not come with it.

No release health. Session tracking, crash-free rate and adoption per release are Sentry counting sessions the SDK opens. The envelope wire carries those session items and they are ingested, but no route reads a crash-free number back.

Grouping is the server's. Issues group by fingerprint and there is no route that edits a grouping rule, so Sentry's per-project stack-trace rules and fingerprint overrides have no destination. What the SDK sends is what it groups by.

Attachments and user feedback arrive but do not read back. The envelope is accepted whole, attachments included. There is no route that lists or downloads an attachment, and no crash-report or user-feedback endpoint to point a feedback widget at.

Replay ingests through the API and plays back in the console. POST /v1/event/replay takes the recording — 512 KiB per batch, so a recorder chunks a long session — and a player in console.hanzo.ai reads the derived summary. Pulling a raw recording out programmatically is not an API call. A reduced Hanzo Team workspace token is refused here with insufficient_capability, because a full-fidelity screen recording has no safe projected form for a guest to write into a host org.

How is this guide?