React Native SDK API
Functions, options, parameters, promises, and native behavior for the Anectico React Native SDK.
On this page
@anectico/react-native is a Promise-based JavaScript bridge over the native iOS and Android SDKs. The
native layer owns identity, sessions, persistence, network delivery, and native crash handlers; the
JavaScript layer adds Hermes and JSC error parsing and global JavaScript error capture.
Use separate project-bound native_ingest and mobile_capture keys. React Native replay is not supported.
Intake credentials
Set apiKey to a project-bound native_ingest key (ingest:write) and
captureApiKey to a different mobile_capture key (analytics:write). Replace the
example placeholders with your issued keys. Never bundle server, read or management credentials.
Mobile capture declares a public-client origin; it does not establish trusted server authority.
Without captureApiKey, product events, identify and group and automatic lifecycle events are no-ops
before identity changes or queue admission; OTLP remains available and configuration logs a warning.
Malformed or shared capture keys reject configuration. Missing credentials do not erase queued
product events: the flush result reports pending product positions. Consent withdrawal still purges
the queue and remains in force after a key is added. Explicit logout or reset retains its behavior.
See native intake keys for creation and verification.
Configure and shut down
import * as Anectico from '@anectico/react-native';
await Anectico.configure({
apiKey: 'an_...',
captureApiKey: "an_<mobile-capture-key>",
environment: 'production',
release: '1.4.0',
dist: '42',
});
| Function | Parameters | Returns | Behavior |
|---|---|---|---|
configure(options) |
AnecticoOptions |
Promise<void> |
Configures native SDK state and installs JS error or rejection handlers unless disabled. Rejects invalid native options; native configuration is idempotent. |
flush() |
— | Promise<TelemetryFlushResult> |
Flushes the native diagnostic-event queue and resolves the per-signal delivery outcomes. See Knowing whether your events arrived. |
shutdown() |
— | Promise<TelemetryFlushResult> |
Removes JS and native hooks, flushes, and releases background resources. Resolves the final flush's outcome, like flush(). |
configure(options, { resumeAnalyticsCollection: true }) |
Explicit new consent after awaited shutdown | Promise<void> |
Purges all old analytics queue data before reopening collection; failures reject configuration. |
disableAnalytics() |
— | Promise<boolean> |
Persist analytics withdrawal and purge queued data; await the result before relying on the native gate. |
stats() |
— | Promise<AnalyticsStats> |
Delivery counters — recorded, delivered, dropped, queued. |
bridgeStats() |
— | { recorded, dropped } |
Narrower: product events passed through the bridge, and how many of them the bridge itself discarded. |
onDeliveryError(handler) |
handler or null |
void |
Registers a handler called with { status, dropped, reason } once per permanently discarded batch. |
installGlobalErrorCapture() |
— | void |
Idempotently installs JS global error and unhandled-rejection capture. Normally called by configure. |
Configuration rejects when the native module is unavailable. Other bridge calls resolve safe defaults rather than crashing the application. Rebuild the native application after installing the package; Metro reload cannot add a native module.
When the native module is missing, every product event is discarded on the spot — there is nothing to
deliver it to. That is counted and reported rather than silently resolving: bridgeStats().dropped
rises, any handler registered through onDeliveryError is called with reason: "native_module_unlinked" and status: 0, and a console.warn names the count. In that state
flush() and shutdown() report unknown signal outcomes and stats() reports what the bridge saw — nothing was
delivered, so nothing is reported as accepted. A handler that throws is swallowed and never reaches
your capture() call.
Withdrawing analytics consent
Await Anectico.disableAnalytics() when analytics consent is withdrawn. The native SDK closes analytics admission,
including capture, identify, group, reset, and lifecycle events, and discards pending events without
flushing them. Errors, crashes, agent traces, and OTLP logs have separate controls and continue.
An already-admitted request can still reach the server: validated receipt outcomes remain credited;
uncertain retries and unsent remainder are discarded with collection_disabled and status 0.
This does not erase previously accepted data. recorded = delivered + dropped + queued remains true.
A true result confirms both the persistent withdrawal marker and deletion of the analytics spool.
On false, this configured client stays disabled, but persistence and purge needs retry; do not assume
restart safety until a retry succeeds. Await the promise before relying on the native admission fence. A missing native module returns false.
Ordinary configuration preserves the marker across restart. Only after obtaining new consent,
await Anectico.shutdown(), then call await Anectico.configure(options, { resumeAnalyticsCollection: true }).
Explicit renewal purges all old analytics queue data even when the marker is absent; a purge failure
fails configuration. Repeated configuration of an already configured SDK does not renew consent.
Wait for shutdown completion before reconfiguring; configuration during retirement fails.
Knowing whether your events arrived
Anectico.flush and Anectico.shutdown return a per-signal TelemetryFlushResult through the
native completion or bridge future. Read productEvents, logs and traces: each has accepted,
rejected, pending, unknown, sampled, warnings, status and nullable unavailableReason.
Logs are null on iOS, which has no public log producer. Traces include errors, messages, crashes
and agent-run spans. Pending is the queue and spool snapshot; the other counts describe outcomes since
the previous completed public flush or shutdown. An empty accepted queue with count zero proves
no delivery. Read every count: pending work can coexist with a refused sibling.
Local queue eviction and readable expired or excess error payloads count as rejected records. Pending counts readable records rather than spool files. Corrupt payloads and failed directory listing report unknown with an unavailable reason; zero pending then does not prove an empty queue. A durable pre-send claim excludes each batch from restart replay. Interrupted claims are quarantined as unknown; only a retryable receipt can restore retryable work. Failed claim persistence prevents the send, and failed terminal cleanup cannot replay a partially accepted or unknown batch.
A complete OTLP response preserves rejected counts and warnings, even when HTTP 200 refused every record. Partial batches are retired without replaying admitted siblings. An unreadable success receipt is reported as unknown. Accepted counts describe intake acceptance, not unique stored rows or query visibility. An unavailable native bridge reports unknown with a reason.
Error and message capture return ErrorCaptureResult with nullable errorId, outcome and
reason. Outcomes are sampled, queued, rejected and unknown. Queued means local admission;
a sampled or rejected attempt can still have an ID, and that ID proves no emitted occurrence.
Use the trace result and then read the stored evidence before concluding delivery.
A complete version-one receipt classifies each original position before the SDK updates its queue or counters:
| Receipt outcome | Your events | flush |
Queue and callback |
|---|---|---|---|
queued |
removed from the pending queue | succeeds if all positions were queued | increments delivered |
rejected |
refused positions are discarded | failure | increments dropped; fires a drop callback |
unknown, malformed, truncated, oversized, or absent receipt |
uncertain positions remain pending | failure | retains original message IDs; no drop callback |
Typed permanent request failures also discard the attempted batch. Network failures, rate limits, and dependency failures retain it. Repeated uncertainty backs off from one second to 60 seconds; a fully settled receipt resets that delay. Server retry hints are clamped to that range.
stats() reports recorded, delivered, dropped, and queued; recorded equals
the other three added together. delivered counts validated queue acknowledgements,
not unique stored events.
onDeliveryError(handler) reports the number of discarded events once per affected chunk. A batch
receipt can refuse events with HTTP 200: invalid_event, quota_exceeded, or
mixed_rejection. Typed request failures use bad_request, unauthorized, forbidden,
not_found, payload_too_large, unprocessable, or rejected. Local queue_overflow
and not_serializable drops carry status 0. Unknown outcomes remain queued without a callback unless explicitly discarded on withdrawal with collection_disabled. The handler also receives native_module_unlinked drops when the native module is missing. A throwing handler is swallowed.
Capturing stays non-blocking: capture, identify, group, and screen queue the event and return
immediately, so the delivery outcome reaches you through the three signals above and never through
their own return value.
Anectico.onDeliveryError((error) => {
console.warn(`lost ${error.dropped} events: ${error.reason} (HTTP ${error.status})`);
});
const delivery = await Anectico.flush();
if (delivery.productEvents.status !== "accepted") {
const stats = await Anectico.stats();
// stats.queued > 0 means a retry is pending; otherwise the events are gone.
}
AnecticoOptions
Only apiKey is required. Native defaults match the iOS and Android contract.
Invalid endpoints, timers, capacities, frame limits, and non-finite or out-of-range sampling values
reject the configuration promise instead of being clamped.
| Option | Type and default | Purpose |
|---|---|---|
apiKey |
string; required |
native_ingest key for OTLP. |
captureApiKey |
optional | Separate mobile_capture key for product events, identify and group. |
endpoint |
string; https://api.anectico.com |
Native API base URL. |
environment |
string; development |
Deployment environment. |
release, dist |
optional strings | Release and artifact identifiers. |
serviceName, serviceVersion |
optional strings | Native resource identity; defaults from the app. |
flushAt |
number; 20 |
Event-count flush threshold. |
flushIntervalMs |
number; 5000 |
Foreground-only native periodic flush; zero disables it. |
maxStackTraceFrames |
number; 50 |
Native and JS frame cap. |
breadcrumbCapacity |
number; 100 |
Native ring-buffer capacity. |
maxQueueSize |
number; 10000 |
Native event queue cap. |
errorSampleRate |
number; 1 |
Non-fatal error sampling from 0–1. |
enableCrashReporting |
boolean; true |
Install native crash handlers. |
debug |
boolean; false |
Native diagnostic logging. |
enableJsErrorCapture |
boolean; true |
JavaScript-only global error or rejection handlers. |
Identity, accounts, and events
| Function | Parameters | Returns | Behavior |
|---|---|---|---|
identify(distinctId, set?) |
stable ID; person properties | Promise<void> |
Links anonymous and known identity and queues identify. |
group(type, key, set?) |
account type and key; properties | Promise<void> |
Records membership and $groupidentify. |
capture(event, properties?) |
event; properties | Promise<void> |
Queues a diagnostic event in native storage, natively stamped with the reserved $release/$app_version properties (see Reserved event properties) when configure() set release/serviceVersion. |
screen(name, properties?) |
screen; properties | Promise<void> |
Queues $screen with $screen_name. |
reset() |
— | Promise<void> |
Logout: rotates native identity or session and clears groups. |
getDistinctId() |
— | Promise<string> |
Current identity, or "" without native configuration. |
getSessionId() |
— | Promise<string> |
Current session without extending its idle window, or "". |
identify and group update local identity only after their control event passes native queue
admission. An event that cannot be serialized or whose UTF-8 request envelope exceeds 1 MiB is
dropped with status 0 and reason not_serializable or payload_too_large. The previous person ID
and account memberships remain in use for subsequent telemetry. Valid controls are queued before
events use the new identity; the call does not wait for network delivery.
Errors, messages, users, and breadcrumbs
| Function | Parameters | Returns | Behavior |
|---|---|---|---|
captureError(error, options?) |
unknown; CaptureOptions |
Promise<ErrorCaptureResult> |
Normalizes the value, parses Hermes and JSC frames, attaches the SDK-owned rn.captureError mechanism, and returns the native capture outcome and attempt ID. |
captureMessage(message, level?, options?) |
text; ErrorLevel; options |
Promise<ErrorCaptureResult> |
Captures a message; error/fatal receive error status. |
addBreadcrumb(crumb) |
Breadcrumb |
Promise<void> |
Adds native trail context for the next error. |
setUser(user) |
AnecticoUser |
Promise<void> |
Sets native global error user context. |
clearUser() |
— | Promise<void> |
Clears that error user context. |
CaptureOptions currently transmits tags and level. Per-capture extra, user, and fingerprint
are not supported by this bridge; use setUser for user context. ErrorLevel is debug, info,
warning, error, or fatal. A Breadcrumb contains category, message, optional level, and
optional data. AnecticoUser contains id, email, username, ipAddress, and segment.
The native transport adds SDK-owned app and build, OS, and privacy-safe hardware-model context. It does not collect stable device identifiers or user-assigned device names.
Global handlers preserve and chain previous React Native handlers. On iOS and Android, every
JavaScript error has SDK-owned level, handled state, and mechanism: fatal errors are fatal /
unhandled, other errors are error / handled, and the mechanism is rn.captureError for explicit
captures, rn.globalHandler for uncaught errors, or rn.unhandledRejection for rejected promises.
Application tags cannot override those classification fields. A fatal JavaScript error bypasses
client sampling. For fatal global errors only, the bridge durably captures JavaScript frames through
a blocking native method before chaining React Native's previous handler. A five-second, one-shot
native handoff suppresses only RN's matching wrapper report; it does not suppress genuine native
crashes. Missing, throwing, or unsuccessful synchronous capture falls back to the asynchronous path
with native fallback reporting left active.
Agent runs
startAgentRun(options: AgentRunOptions): AgentRun is synchronous. It copies the options and
records a start timestamp; the native bridge attaches current identity when the run ends.
import * as Anectico from '@anectico/react-native';
const run = Anectico.startAgentRun({
agent: 'checkout-helper',
agentId: 'checkout',
agentVersion: '1.4.0',
conversationId: 'chat-42',
});
// Perform the agent's work, then declare its actual outcome.
const accepted = await run.end('completed');
// On failure instead: await run.end('failed', 'tool_failed');
AgentRunOptions.agent is required. agentId, agentVersion, and conversationId are optional strings mapping to
gen_ai.agent.id, gen_ai.agent.version, and gen_ai.conversation.id. The agent name maps to
gen_ai.agent.name. Configure and identify the customer before starting a run. A terminal record
is an invoke_agent span sent with ingest:write; it does not require an agent-read or agent-write
management scope. It is not automatic instrumentation of nested model and tool calls.
end(status?: AgentRunTerminalStatus, terminalReason?: string): Promise<boolean> defaults to
completed and accepts completed, failed, timed_out, cancelled, and max_steps.
Use a stable, non-sensitive reason code, not an exception message, prompt, or customer value.
Reasons are trimmed, lowercased, and must match ^[a-z][a-z0-9_]{0,63}$. Omitted reasons default to
timeout for a timeout, user_cancelled for cancellation, and the wire status for other outcomes.
These are source-declared outcomes; Anectico does not infer success from the absence of an error.
An invalid status or reason rejects the promise and leaves the handle open. A native false
result or rejection also leaves it open for retry. Concurrent valid end calls share the in-flight
promise; after one is accepted, later valid calls resolve true without emitting again. A true
result is native acceptance, not confirmed network delivery. Finish the run before resetting or
switching customer identity.
Backend propagation
propagationHeaders(): Promise<Record<string,string>> creates a fresh W3C traceparent and current
identity baggage. Add the returned headers only to a trusted, instrumented application backend.
They are correlation metadata, not proof of authentication.
Exported stack types
StackFrame is the bridge wire shape: filename, function, lineno, colno, and in_app. Stack
normalization and parsing is internal to captureError; applications should not construct raw bridge
errors.
Native capture receipt migration
Both bundled native sources now validate complete version-one indexed capture acknowledgements.
Mixed receipts retire queued and refused positions while retaining uncertain original events in
their durable spools. Malformed or incomplete responses remain uncertain. Ledger refusals can carry
HTTP 200 with invalid_event, quota_exceeded, or mixed_rejection; those codes cross this bridge.
Without a hint, native retry delay doubles from one second to a 60-second cap until a receipt
fully settles. Retry-After is clamped to 1–60 seconds. Published-package, device and backend qualification remains open; this is not a completed release.
Application capability configuration
Android and iOS delegate flags and headless surveys to their native clients. The iOS adapter preserves the same typed result schema, including exact versions, explicit presence and zero ratings. Its shared Swift core tests pass; an iOS app build, simulator and device run remain unverified. These methods need a rebuilt classic NativeModule, including in an Expo development build. Published-package qualification remains separate from host tests.
| Option | Type | Meaning |
|---|---|---|
applicationEndpoint |
optional string | Explicit application API base URL. |
projectID |
optional string | Canonical project UUID; required with application options. |
flagDeliveryApiKey |
optional string | Separate CLIENT_DELIVERY credential with flags:evaluate. |
surveyDeliveryApiKey |
optional string | Separate CLIENT_DELIVERY credential with surveys:respond. |
surveyScreenPaths |
optional string map | Declared screen names to slash-prefixed templates. |
bootstrap |
optional FlagBootstrap |
Original request and decision JSON, bound to the current context and original expiry. |
Supply endpoint and project with application options. Capability omission creates no network reader.
Intake keys cannot substitute for delivery keys. configureApplication(optionsOrNull) returns an
ApplicationResult and replaces or retires application capabilities independently of telemetry.
FlagBootstrap has projectID, requestJson, decisionJson and optional decimal-string
evaluationGeneration. Native validation refuses wrong project, identity, context or expiry.
Typed flags
| Method | Result | Behavior |
|---|---|---|
flags.setEvaluationBinding({ distinctId, personProperties?, groups? }) |
EvaluationBindingResult |
Replaces the current explicit application subject and context. |
flags.clearEvaluationBinding() |
EvaluationBindingResult |
Clears the explicit binding; implicit identity still requires consent and configured capture. |
flags.evaluationBinding() |
EvaluationBindingResult |
Current native snapshot, nullable binding and exact decimal-string generation. |
flags.reload({ keys }) |
FlagReloadResult |
Explicit bounded remote evaluation for 1–100 unique keys. |
flags.installBootstrap(bootstrap) |
FlagReloadResult |
Imports original request and decision documents without extending freshness. |
flags.value(key, { testCaseId? }?) |
FlagReadResult |
Serves the native decision; exposure admission requires product consent and a matching telemetry subject. |
flags.payload(key) |
FlagReadResult |
Returns the same presence-aware decision with its payload and native exposure behavior. |
flags.status(key) |
FlagReadResult |
Inspects cached state without serving an exposure. |
flags.boolean(key, explicitFallback) |
BooleanFlagResult |
Returns value, underlying read and separate application fallback value, used marker and reason. |
All methods return promises. Successful reloads contain document: projectID, exact
evaluatedAt, maxAgeSeconds: 30, expiresAt, decisionContextId, selected-key coverage
and keyed decisions. Each decision contains key, state, valuePresent, valueType, optional
value, payloadPresent, optional payload, exact definitionVersion, isFallback and
inputContext. States are evaluated, evaluation_error and unavailable_flag.
inputContext preserves basis, optional requiredInputsPresent and optional declared inputBinding.
valuePresent: true, valueType: 'boolean', value: false is an evaluated false. Absence uses
valuePresent: false, valueType: 'absent'; it is never a default false. JSON null requires
payloadPresent: true, payload: null. Version timestamps retain six fractional digits, with no
conversion through a millisecond date. Unavailable or unversioned values acquire no invented version.
The decision context is not an exposure receipt or definition version.
The application fallback is separate from a server's isFallback. Native expiry, unknown and
failure statuses remain in read. While analytics is disabled or capture is unconfigured, no
implicit subject is usable: declare the current binding or receive identity_unavailable.
Explicit binding does not mutate telemetry identity, group membership or session. Native clients
own generations, cache lifetime, one active flag request, a maximum of ten groups, bounded
properties, a 16-KiB targeting envelope and a 1-MiB response cap.
Headless surveys
| Method | Result | Behavior |
|---|---|---|
surveys.refresh(screen) |
SurveyRefreshResult |
Gets native offers for a declared screen. |
surveys.eligible(screen) |
SurveyRefreshResult |
Uses the same native refresh and five-second floor. |
surveys.shown(handle, deliveryId?) |
SurveyDeliveryResult |
Records an offer after your UI displays it. |
surveys.submit(handle, answers, deliveryId?) |
SurveyDeliveryResult |
Validates answers natively and preserves the immutable original retry. |
surveys.dismiss(handle, deliveryId?) |
SurveyDeliveryResult |
Records dismissal for that native offer. |
A successful refresh has offers, at most three entries of { handle, survey }. The opaque handle
refers to the native offered object. A survey ID and revision cannot manufacture eligibility.
survey contains id, a positive decimal-string revision retaining all signed 64-bit digits, and
1–10 questions. Stable question IDs, prompt, required marker, options and scale are preserved.
Types are single_choice, multiple_choice, rating and open_text; choice questions have 2–12
options. Rating scale is within 0–10. An answer has questionId and an optional integer rating,
optionIds or text. Missing rating differs from zero. Fractional, nonfinite, wrong-type and
out-of-range ratings are refused; native validation checks required fields and duplicate or unknown
questions and options. Text is limited to 2,000 characters.
A successful delivery has receipt: { at, replayed } only after native validation of a complete
receipt. at remains an exact timestamp string. Native retries retain original identity, source
session, answers and ID; withdrawal or retired identity or configuration closes the offer and clears
pending answers. Surveys require product consent. Your application owns rendering, and the SDK
emits no duplicate survey events. See the screen example.
Capability outcomes and evidence
ApplicationResult distinguishes accepted, unknown, expired, invalid_request,
invalid_response, identity_unavailable, transport_error, refused, cancelled,
unconfigured, closed, busy, throttled and capability_unavailable. Errors preserve optional
failure with literal code, native status, httpStatus, retryable and retryAfterMs.
Inspect retry advice before repeating an uncertain operation. Cancellation settles once; a late
reply cannot change its result. A valid empty offer list is accepted, while failure has no usable
list or receipt.
An unlinked module reports native_module_unlinked. An unavailable capability reports
native_capability_unavailable, a channel exception reports native_bridge_failed, and malformed
native documents report invalid_native_response. These capability outcomes are separate from
the existing product-event delivery callback and per-signal flush results.
A flag decision does not prove exposure. A receipt does not prove query visibility or canonical person joining. Answer rows are not response totals. Read stored evidence under the same stable ID and project before claiming shared web and mobile coverage. Host tests and native channel tests are separate from actual linked runtime and later storage verification.
Delivery credentials and qualification
Follow mobile setup and limits for the separate
application HTTPS host and project-bound delivery keys. Keep ingestion endpoint, apiKey
(native_ingest, only ingest:write) and captureApiKey (mobile_capture, only
analytics:write) separate. Never embed management, MCP, flags:read or server APPLICATION
experiment-delivery authority. Private endpoints require the approved private network path;
do not publish an API to bypass that boundary.
Source availability and host tests do not establish a linked operating-system, installed-package or stored person and session pass. Each operating system needs its own runtime proof with original application IDs and independent stored readback. The public native per-exposure original-ID and outcome handoff remains unavailable; store-discovered IDs and aggregate flush counts cannot replace it. A flush or survey receipt is not shared-person proof. A separate Android API 36 x86_64 emulator run with React Native 0.86, Fabric and Hermes verifies actual native HTTPS flag and survey delivery and the shared person timeline with original sessions. Other configurations and physical devices require their own verification. Native replay remains outside this capability.
Native replay controls
The optional nativeReplay configuration is default off. It requires enabled, an explicit HTTPS
endpoint, projectID, a separate uploadApiKey and optional static declaredLabels (at most 32).
The project-bound replay-upload key carries only replay:write. Current native capture and store
permission plus independent local replay consent are required. Missing authority fails closed.
| Method | Native result or action |
|---|---|
replay.bindOwner(owner) |
Explicit current application owner; never analytics or flag identity. |
replay.grantConsent() |
Independent replay grant; does not supply owner or remote permission. |
replay.withdrawConsent(), replay.stop() |
Stops and purges native unsent replay; resume requires another grant. |
replay.start() |
Current native state with optional finite context; never grants consent. |
replay.status() |
Actual disabled, recording, paused or refused state and available format and policy context. |
replay.refreshPolicy() |
Asks the native controller for current policy; absence and expiry remains refused. |
replay.capture(rootHost, staticScreen, context) |
One bounded source snapshot through resolved host refs. |
replay.tap(host, context), replay.scroll(host, context) |
Targets the latest admitted native frame; no raw event and offset payload. |
replay.inputChanged(host, context), replay.inputFocusLost(host, context) |
One masked change per editing episode, then ends that episode; no values or lengths. |
replay.flush() |
Native acceptance with retention unknown, idle, retry, refusal or cancellation. |
Withdrawal and stop also return localPurgeComplete, the actual local purge result. It is never
server erasure proof. A source or reference failure returns unsupported. An absent or older native module returns unavailable,
never successful no-op recording. A native result identifies native_structural, version 1, with
settings revision when available. The optional context contains only token, opaque generation and
remaining lifetime (at most 5,000 ms). No authority can be constructed from telemetry identity or a
hook registration. Control messages are closed and at most 8,192 bytes; no JS scene is accepted.
Replay component source matrix
ReplayProvider supplies the current context. ReplayMask and ReplayBlock create View boundaries;
mask and block ancestry dominates all action labels. ReplaySafeAction uses a static configured label
key and observes its own touch without forwarding its payload. useReplayNode(component, options)
returns the ref callback for a concrete host, with optional static nodeKey, treatment and labelKey.
Use a stable callback, preserve the application's ref, and disable View collapsing. Unmount or
replacement unregisters its unique lease. Never pass numeric tags or composite refs.
| Declaration | Native mapping candidates | Source content and qualification |
|---|---|---|
| View | Android ReactViewGroup; iOS classic RCTView | Geometry; static action wrapper can be a button box. |
| Text | ReactTextView; RCTTextView | Masked geometry, no text or accessibility fallback. |
| TextInput | ReactEditText; classic singleline and multiline input wrappers | Masked geometry and coalesced changed placeholder, no value, length or internal child extraction. |
| Image | ReactImageView; RCTImageView | Masked empty box, no asset, URL or bytes. |
| ScrollView | ReactScrollView and horizontal equivalent; RCTScrollView | Masked box and offset; unregistered internal content remains an omission. |
| Unsupported or unmatched host | Custom classes, embedded and drawing regions, collapsed or unresolved refs, transformed hosts, iOS Fabric | Placeholder without children, with an unknown-count gap; an unavailable root produces a gap. |
These are exact-class source implementations, not linked runtime support claims. Linux host and portable native tests do not qualify Android and iOS recording, either architecture, release performance, stored steps and player or deletion. Every actual host frame currently includes an unsupported gap for that qualification boundary. There is no complete-scene or universal-component claim. Static labels also require current remote eligibility and unchanged source redaction. They remain bounded untrusted declarations, with no guarantee about arbitrary-secret provenance. Consent, policy and generation changes invalidate old contexts before extraction and again at admission. A receipt can mean discard and is not retention, playback or erasure proof.
Unchanged structural frames
replay.capture can return outcome: "unchanged" when the latest admitted frame is still exact.
replay.captureAutomatically waits for current policy and admission capacity. When observed roots
have not rendered, it can confirm the previous frame before reading host geometry. Manual capture
always refreshes source facts. On iOS, initialized native Animated support keeps fresh source reads
because those animations can bypass the ordinary render and scroll signals.
It creates no event, chunk or upload and spends no frame or event budget. Keep the current context and action targets; an unchanged capture preserves the latest admitted frame. Permission changes, screen or viewport changes, gaps and pauses require a fresh complete frame.