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

# On the device

> The SDKs record every signal on the device; your policy decides what is sent, when, and how long the rest stays before it is deleted.

The Apsio SDKs write every signal they record to a store on the device, already masked: text a
[mask](/concepts/replay) hides never reaches the disk. Your app's [remote
configuration](/remote-configuration) then decides, for each kind of data, what is sent, when,
and how long the rest stays on the device before it is deleted. A session left out of the
sample can still send the minutes before a crash.

Nothing is written while consent is withdrawn or the user has opted out; an app that needs
consent first withdraws it right after `start`, until the user agrees
([privacy](/concepts/privacy#consent)). The app's start options and the policy's capture settings
still decide what is recorded at all: replay, for example, is recorded only if the app turns it
on. The store only decides what leaves the device.

## The families

The store holds seven families of data, and each has its own mode:

| Family | What it holds |
| - | - |
| Session summary | A session's start, end, outcome, release and device class, behind [release health](/concepts/sessions#release-health) |
| Errors | Crashes, terminations without a crash report, ANRs and hangs, handled errors, and every log record at Error or above |
| Logs | Log records below Error, breadcrumbs, feature flag evaluations and screen views |
| Spans | App start, screen loads and your own spans |
| Network | HTTP request spans |
| Metrics | Metric points, MetricKit reports included |
| Replay | [Replay](/concepts/replay) segments |

## What leaves the device

Each family is in one mode:

* **Always:** sent as it is recorded. A family you have not set is in this mode, and the
  session summary stays in it, so crash-free rates and session counts are exact (within your
  plan's [fair use](/pricing#fair-use)).
* **Sampled:** sent from a share of sessions, decided once per session by a hash of its id, as
  [sampling](/concepts/sampling) describes. This is the session sampling rate, now set per
  family.
* **On a trigger:** kept on the device and sent only when a trigger fires in the session, or on
  a remote send request.
* **Never:** kept on the device until it expires, and sent only on a remote request.

In a sampled errors family, the first error of each issue in a calendar month always passes:
the device keeps a set of one-way fingerprints of the errors it sent this month.

## Triggers

A trigger is a crash or a termination without a crash report, an error (any record at Error
severity or above, handled errors included), an ANR or hang, or a named event your app reports
with `Apsio.trigger(name)`. When one fires, the families whose policy lists it send what the
device holds from the minutes before it: the window, 5 minutes by default and up to 60.

* **Which families.** Families in on-a-trigger mode, and sampled families in a session outside
  the sample, when they list that trigger. An always family is already sent; a never family
  ignores triggers.
* **When.** An error, an ANR or hang, or a named event releases its window at once, and it goes
  with the next upload. A crash is found at the next launch, since the app was not running,
  and the window from the session that crashed is sent then. Records reach the device's disk within
  moments of being written, so a crash loses at most the last moments before it.
* **For the rest of the session.** After a trigger in the running app, the triggered families
  stay released until the session ends.
* **Window edges.** Records are stored in batches of about 10 seconds, so up to about 10
  seconds before the window may be sent too.
* **Replay.** A trigger sends at most the session's [replay limit](/concepts/replay#how-much-of-a-session-is-recorded):
  the last minutes of the window.

React Native behaves as the native SDK under it: a fatal JavaScript error is written at once
and handled like a crash at the next launch, and `Apsio.trigger` exists in JavaScript too.

## How long data stays on the device

* **Retention:** 24 hours by default, set from 1 hour to 7 days. It counts from each record's
  own time, and applies to records already released but not yet sent (on a device that is
  offline, for example).
* **Cap:** 20 MiB per app on each device, of which at most 10 MiB is replay. Your policy can lower
  both.
* **When the store is full,** the oldest records go first: replay over its own share first, then
  the other families, with session summaries and errors last.

Apsio accepts records up to 7 days old, so a late upload is stored, and its retention in Apsio
counts from when Apsio received it.

## When data is deleted from the device

* **After it is sent:** once Apsio confirms the upload. A record Apsio rejects as invalid is
  discarded too.
* **When its retention ends,** sent or not.
* **When the store reaches its cap,** oldest first, as above.
* **At once, when the user opts out or withdraws consent, or the kill switch is on:** the whole
  store, including replay held on the device and records not yet sealed into a batch for upload.
* **On a remote delete** (below), at the device's next policy fetch.

Besides the records, the device keeps three small things: the app user's hash (the value your
app passes to `setUser`, never a raw id) for 7 days after it was last set, so a remote request
for that user can still find the device; this month's error fingerprints; and the ids of remote
requests it already served. An opt-out or withdrawn consent deletes the first two as well; the
served request ids hold no personal data.

## Remote requests

An owner or admin can ask one install, or every install of one app user, to act on what it
holds:

* **Send** uploads what the device holds.
* **Delete** deletes what the device holds and what it has queued unsent.

Both cover only the families the request names, or every family when it names none, including
those set to never and the session summaries.

A device checks for requests each time it fetches the policy, at launch and at the policy's
fetch interval (15 minutes by default) while the app runs, and serves a request once. A request
for an app user reaches only that user's records, and only on a device where the app set that
user's hash in the last 7 days. A request lasts up to 7 days, and a project
has at most 100 open at a time. A request never overrides consent, an opt-out or the kill
switch, and never brings back what they deleted.

Requests travel inside the app's policy as a one-way hash, never as the install or user id, and
Apsio stores only that hash and the id's last 4 characters. The device answers with an
`apsio.remote_request` record that says whether it sent, deleted or had nothing, and how many
records. Every request, and its cancellation, is in the organization's audit log, without the
id it targets.

## Setting it

In the console, **SDK › Signals** sets, for each app, each family's mode, rate, triggers and
window, and the retention and cap on the device, and lists the device requests and their state (open, delivered, expired or
canceled). On a user's or a session's page, **Device data** opens a request: **Send to Apsio**
or **Delete on the device**, for every family or the ones you pick, lasting 1, 3 or 7 days. Members can see these settings; owners and admins change them, and every change
is in the audit log. A change is published to the app's policy at once and reaches each device
at its next fetch.

For scripts, the same settings are in the console API:
`GET` and `PUT /v1/orgs/{orgId}/projects/{projectId}/apps/{appId}/instrumentation`, and
`/v1/orgs/{orgId}/projects/{projectId}/device-requests` to list, open and cancel requests; the
API reference lists their fields. How devices read the policy is in the specification:
[the policy's fields](https://spec.apsio.io/v0.1/remote-configuration/#122-the-document) and
[instrumentation](https://spec.apsio.io/v0.1/instrumentation/).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.