Ingest keys
Every project in the dashboard has keys. Your app uses exactly one kind: the public ingest key.
- Public keys (
cs_live_pk_…,cs_test_pk_…) can only send events. They can't read any data, so they're safe to ship inside an APK or a JavaScript bundle. - Secret keys (
cs_…_sk_…) must never be in an app. Both SDKs refuse a secret key and simply stay switched off.
live and test keys belong to different environments. Use a test key in development and CI, and a live key in production builds, so test traffic never mixes with real users.
Events
Everything the SDK sends is an event: a small record with a type, a severity, a timestamp, and context about the device, app and user. These are the types you'll see:
| Type | Platforms | What it means |
|---|---|---|
| crash | Android | An uncaught exception that ended the app. |
| crash_native | Android | A native (C/C++) crash. |
| anr | Android | An Application Not Responding freeze. |
| exception | Android, Web | A handled error you reported, or an uncaught JavaScript error or rejection on the web. |
| api_error | Android, Web | A request that failed: a network error or an HTTP status of 400 or above. |
| api_timing | Android, Web | How long a request took. High-volume and low-severity. |
| heartbeat | Android, Web | A periodic "still here" signal sent while the app is in the foreground. |
Identity
Codeskop tells users apart in two layers:
- Install ID: a random, anonymous ID created the first time the SDK runs. It's stored on the device (in the browser's
localStorage, or a cookie as a fallback) and survives restarts. - User ID: your own ID for the signed-in user, set with
identify(userId). Call it on every login; calling it again with the same ID is expected and never creates a duplicate user.
Call reset() on logout. The user ID is cleared and events become anonymous again; the install ID stays.
On Android the user ID is kept in memory only, so call identify again on each app launch once you know who's signed in, not just at the moment of login.
Use an opaque ID
Pass the ID from your database or auth provider, not an email address, phone number or name.Sampling
Some events are high-volume and low-value one by one, mainly api_timing. Sampling keeps a percentage of those to control volume.
- Android: sample rates come from
CodeskopConfig.sampleRatesand can be overridden remotely. Crashes and ANRs are always kept. - Web: the web SDK currently keeps every event. The
sampleRatesoption is accepted but not applied yet.
The offline queue
Events are written to a queue on the device before anything is sent. The SDK sends them in compressed batches when there's a connection, and keeps anything that wasn't accepted to retry later. A crash on a train with no signal still reaches you.
- Android: a SQLite-backed queue (10 MB by default). WorkManager sends it about every 15 minutes while there's a connection, and right away when the device reconnects.
- Web: an IndexedDB queue (5 MB by default), falling back to memory where IndexedDB isn't available. When the tab is hidden, remaining events are sent with
sendBeaconso closing a tab doesn't lose them.
If the queue fills up, the oldest routine events (like timings and heartbeats) are dropped first; crashes and errors are protected. More in Offline & Background Sync.
Remote config and the kill-switch
When the SDK starts, it fetches a small configuration from Codeskop. You change it per project in the dashboard (Settings → SDK settings), without shipping a new release:
- turn capture off completely (the kill-switch),
- turn network monitoring and ANR detection on or off,
- lower the request timing sample rate and the offline queue size, within your plan's limits.
The config is fetched once each time the SDK starts, so a change (including the kill-switch) takes effect on the next app launch or page load. If the fetch fails, the SDK uses the last configuration it saw, or safe defaults on a first run. An inactive plan also arrives as "capture off", so an app on an expired plan sends nothing. See Remote Config.
Releases
Every event carries the version of your app that produced it, so you can see when an issue started.
- Android: the version name and code are read from your app automatically.
- Web: pass
releasetoinit, for example from your build's version.
Built to never hurt your app
Both SDKs make the same promises:
Never throws
Every public method is guarded. A failure inside the SDK is swallowed. It never reaches your code, so there's no need to wrap calls in try/catch.
Safe before init
Calls made before the SDK is initialized do nothing. You don't need to guard them.
Non-blocking
No method does network or heavy disk work on the calling thread. Initialization returns immediately.