Getting Started
Add an app in the dashboard, install the SDK with the key it gives you, and watch the first event arrive.
Add an app
In the dashboard sidebar, open App Telemetry and click Add app (Add your first app when you have none). It takes four steps: Platform, Name, Install and Verify.
- 1Platform
Under What are you instrumenting?, pick the platform: Browser, Flutter, Android or iOS for apps people use; Node.js, Python, Go or PHP for servers and APIs; OpenTelemetry for anything else. One app per codebase — a front end and its API are two apps. Click Continue.
- 2Name
Enter an App name such as
Checkout weband choose the Environment to fill into the setup code. Under Advanced settings you can set the Service name (by default the name in lower case with dashes,checkout-web) and, for a browser app, Allowed origins. Click Create app. - 3Install
The page shows the setup for your platform with the key, service name and environment already filled in, with a tab per framework where it makes a difference. Follow it, then click I've added it. The steps are the same as on each SDK page.
- 4Verify
Zharp waits for the first event. Load a page in your app, or send your service a request; it shows up a few seconds later. Send a test event sends one log line with the app's key, which tells you the key works before the SDK is in. If nothing comes after a minute, the page lists the usual causes.
Once connected, Get the most out of it ticks off each item as its data arrives: Errors, Traces, Logs, Releases, User context, Linked to a backend and, for browser apps, Session replay.
You can leave halfway: the app is created with its key at the second step, and Finish setup next to its name takes you back. Later, the setup is under the app's settings, Setup instructions.
SDK pages
Each SDK page has the same sections — Install, Initialize, Logs, Errors, Readable stack traces, Tracing, User context, Options and Troubleshooting — so once you know one, you know them all.
| Platform | Package | Covers |
|---|---|---|
| Browser | @zharp/browser | React, Vue, Next.js, Angular, plain JavaScript, script tag |
| Node.js | @zharp/node | Express, NestJS, Fastify, Koa, Next.js server |
| Python | zharp | Django, FastAPI, Flask, scripts and workers |
| Go | github.com/zharp-io/zharp-go-lib | net/http, Gin, Echo, workers |
| PHP | zharp/zharp | Laravel, Symfony, plain PHP |
| Flutter | zharp_flutter | iOS and Android from one codebase; Dio with zharp_dio |
| Android | io.zharp:android | Kotlin and Java, OkHttp |
| iOS | Zharp (Swift package) | Swift, SwiftUI and UIKit, URLSession |
| OpenTelemetry | Any OTel SDK or Collector | Every other language |
The SDKs are at 0.1 (Android at 1.0.0). They are built on the official OpenTelemetry SDK of each language, so what they send is standard OpenTelemetry — but expect their own options to change as they settle.
The same SDKs send logs and errors without tracing. See Logs from your code.
Keys
Every app has its own ingest key, zpk_ followed by 32 characters. The setup page fills it in; it is also in the app's settings (the gear next to the time window) with a copy button.
- It can only send. It reads nothing and changes nothing, so it is safe in a web page or a mobile app, where anyone can find it. On a server, keep it in the environment rather than in the repository.
- Everything it sends belongs to its app, whatever
service.namethe data carries. - Allowed origins (browser apps, in Advanced settings or the app's settings): only pages on these origins may send with the key, one per line, such as
https://shop.example.com. Empty allows any. Ingest answers403to any other origin. Servers and mobile apps send no origin, so the list doesn't apply to them. - Roll key in the app's settings issues a new key. The old one stops working within a minute, and data stops arriving until every SDK has the new one.
You can also send with a workspace API key that can ingest — create one under Integrations & API → Main API keys with Ingest only ticked. Then each record goes to the app whose service name matches its service.name, and an unknown service name creates a discovered app. That suits a Collector or agent sending for many services at once; for a single app, use its own key.
Environments and releases
environment (production, staging…) is recorded on every event and shown on each error. One app can send from several environments with the same key — the environment you chose when adding it only fills in the setup code.
release is the version of the build. With it, the Overview marks when each release first appeared and shows its error rate, and an issue first seen in the newest release is marked New in <release>. Set it from your build — a version number or the commit SHA:
// The same value everywhere this build runs — a version or a commit SHA
Zharp.init({ key: "zpk_…", service: "checkout-web", environment: "production", release: "web@2.3.0" });Flutter, Android and iOS default to the app's version and build number, so mobile apps have a release without setting one.
Linking a front end to its API
Add the front end and its API as two apps. Then tell the front-end SDK which hosts are yours with tracingOrigins:
import { Zharp } from "@zharp/browser";
Zharp.init({
key: "zpk_…",
service: "checkout-web",
environment: "production",
release: "1.0.0", // optional: see which release broke
tracingOrigins: ["api.example.com"], // your API, so traces continue into it
replay: {
sampleRate: 0.1, // record 10% of sessions from start to end
onErrorSampleRate: 1.0, // and every session that hits an error
},
});Requests to those hosts — and, in the browser, to the page's own origin — carry two W3C headers: traceparent, so the API continues the same trace, and baggage with the session and user. The server SDKs read both, so the API's spans, logs and errors are one trace with the click that caused them and are filed under the same user and session. No other host gets the headers: an unexpected header can make a third-party request fail its CORS check.
A cross-origin API has to allow the two headers, or the browser blocks the request:
# alongside the headers your API already allows Access-Control-Allow-Headers: content-type, traceparent, baggage
Linked to a backend on the setup checklist ticks once a trace continues from one app into another.
Who the user is
Without it, errors and sessions are anonymous. Tell the SDK who is signed in, and every error, trace, log and session after that carries their ID — and the Users tab lists them with everything they did:
// Once someone signs in — and on every page load while they are signed in
Zharp.setUser({ id: user.id, email: user.email });
// On sign-out: forgets them and starts a new session
Zharp.clearUser();idis required and becomesenduser.id; use whatever identifies a person in your app.emailandnameare optional.- In apps people use (browser, Flutter, Android, iOS), the user is sent to your API in
baggage, so a server doesn't need to callsetUser. On a server,setUsersets the user for the current request — for when the server knows better, or the request didn't come from a Zharp front end. clearUser()on sign-out forgets the user and, on clients, starts a new session.
What else is sent
The client SDKs add the session ID, the browser or device model, and the operating system and its version. Mobile SDKs add a random install ID they create themselves — not an advertising or vendor ID, and gone when the app is uninstalled. Anything else is only what you add with setTag or as attributes on a log or error.