Android SDK API
Functions, options, parameters, return values, and propagation helpers for the Anectico Kotlin SDK.
On this page
This is the public Kotlin API for the native Anectico Android SDK (minSdk 24). init reports
failure rather than absorbing it: IllegalArgumentException for invalid configuration, and
AnecticoInitException (with the original cause attached) if anything else stops the SDK from being
set up. Telemetry calls made before a successful init stay no-ops that return "", but they are
never silent — see Calling before init. Delivery failures after
initialization are still never thrown into your app. Network I/O runs on background executors;
durable queue writes complete before a capture call returns.
Use a project key with ingest:write and analytics:write. The native SDK does not record replay.
Initialize and stop
Anectico.init(
applicationContext,
AnecticoOptions(
apiKey = BuildConfig.ANECTICO_API_KEY,
environment = "production",
),
)
| API | Parameters | Returns | Behavior |
|---|---|---|---|
Anectico.init(context, options) |
Android Context; AnecticoOptions |
Unit |
Validates options, then configures identity, sessions, durable event/log queues, error transport, crash/lifecycle handlers, and timers. Repeated calls are ignored. Invalid options throw IllegalArgumentException; any other setup failure throws AnecticoInitException with the cause. A failed init leaves the SDK uninitialized and a later init may retry. |
Anectico.flush(completion?) |
callback taking Boolean |
Unit |
Starts asynchronous event and log flushes, then hands the completion whether every diagnostic-event batch it attempted was accepted. See Knowing whether your events arrived. |
Anectico.shutdown(completion?) |
callback taking Boolean |
Unit |
Stops workers/hooks, flushes, releases state, and permits a later init after completion fires. The completion carries the final flush's outcome, like flush. |
Anectico.disableAnalytics() |
— | Boolean |
Persist analytics withdrawal and purge queued data; false requires retry before restart safety can be assumed. |
Anectico.init(context, options, resumeAnalyticsCollection = true) |
Explicit new consent after shutdown completion | Unit |
Purges old queue data before reopening collection; failure throws AnecticoInitException. |
Anectico.stats() |
— | AnalyticsStats? |
Delivery counters — recorded, delivered, dropped, queued — or null before init. |
Anectico.onDeliveryError(handler?) |
handler taking DeliveryError, or null |
Unit |
Registers a handler called once per permanently discarded batch with status, dropped, and reason. Safe to call before init. |
Withdrawing analytics consent
Call 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/purge needs retry; do not assume
restart safety until a retry succeeds. The call waits for local disk work, but does not wait for an active network request. An uninitialized SDK returns false.
Ordinary configuration preserves the marker across restart. Only after obtaining new consent,
wait for the Anectico.shutdown completion, then call Anectico.init(context, 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
flush answers one question: was everything the SDK had queued accepted? It reports success only
when every batch it attempted was accepted, and finding an empty queue is a success — nothing was
left unaccepted. Errors and crash reports travel their own pipeline and are not covered by this
answer.
A failure has two shapes, and Anectico.stats() separates them:
| What happened | Your events | flush |
stats().queued |
drop callback |
|---|---|---|---|---|
Permanently rejected — a bad request, or a key that is revoked, wrong, or missing the analytics:write scope |
discarded, never retried | failure | excludes refused positions | fires once |
| Temporarily unavailable — no network, rate limited, or a server error | still queued for the next flush | failure | includes them | silent |
Anectico.stats() reports recorded, delivered, dropped, and queued, and recorded always equals the
other three added together. delivered counts positions acknowledged queued in a complete version-one
capture ledger, not unique stored rows or immediate query visibility. queued includes in-flight events.
Mixed receipts retire queued and refused positions while preserving uncertain original IDs, timestamps,
identities and payloads ahead of unsent events. The durable spool retains uncertain events for restart
recovery. Malformed, oversized (over 64 KiB), incomplete or contradictory receipts at any HTTP status
retain the affected chunk. HTTP 200 alone cannot claim delivery; an untyped HTTP 4xx cannot authorize
a drop. Without a server hint, consecutive uncertain attempts back off from one second, doubling up to
60 seconds and resetting after a fully settled receipt. Retry-After hints are clamped to 1–60 seconds.
The HTTP transport uses a 10-second connection timeout and a 15-second socket read timeout.
The complete capture body must arrive within a 15-second body budget; an outstanding socket read
can delay cancellation by up to one read timeout. This is not a total connection/header/write deadline.
OTLP error, log and crash delivery keeps its separate transport policy.
Session and identity changes remain effective in memory when local persistence fails. Logging out
rotates both identities even if storage cannot be cleared; a backward clock change starts a new
session. Property serialization stops at depth 32 and emits [MaxDepth] for deeper values. HTTP
redirects are rejected so event credentials are sent only to the configured endpoint.
Anectico.onDeliveryError fires once per discarded batch with the response HTTP status (including 200 for ledger refusals), how many events were
lost, and a stable lower-case reason. Ledger refusals use invalid_event, quota_exceeded, or
mixed_rejection for multiple refusal causes in one chunk. Typed request refusals use bad_request,
unauthorized, forbidden, or payload_too_large. Two causes never reach the network and report
status 0: queue_overflow (the queue filled up and the oldest events were evicted) and
not_serializable (an event's properties could not be encoded). A temporary failure alone never fires it; explicit withdrawal discards uncertain retries with collection_disabled. A handler that throws is swallowed and never reaches your capture or flush call.
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 ->
Log.w("app", "lost ${error.dropped} events: ${error.reason} (HTTP ${error.status})")
}
Anectico.flush { accepted ->
if (!accepted) {
val stats = Anectico.stats()
// stats?.queued > 0 means a retry is pending; otherwise the events are gone.
}
}
Calling before init
A telemetry call made while the SDK is not initialized records nothing and sends nothing. It reports itself rather than passing for a success:
- Debug builds —
AnecticoOptions.debug, or an application manifest marked debuggable — throwAnecticoNotInitializedExceptionat the call site, so the mistake surfaces where it was made. - Release builds keep the documented return (
""for the id-returning methods, no effect for the rest) and log the first such call at ERROR naming the method; later ones log at WARN, so a call in a loop cannot bury the first.
getDistinctId() and getSessionId() are exempt and simply answer null, which is already an
unambiguous result you can branch on; propagationHeaders() is built on that answer and returns
traceparent alone. shutdown(completion?) is exempt too — it is cleanup, safe to call whether or
not init ever succeeded, and its completion always runs.
AnecticoOptions
Only apiKey is required. Invalid endpoints, timers, capacities, frame limits, and non-finite or
out-of-range sampling values are rejected rather than clamped.
| Option | Type/default | Purpose |
|---|---|---|
apiKey |
String; required |
Key sent to error/event endpoints. |
endpoint |
String; https://api.anectico.com |
Base URL for /v1/traces, /v1/logs, and /api/v1/capture. |
environment |
String; development |
Deployment environment resource value. |
release |
String?; real service version |
Regression and symbol-artifact identifier. |
dist |
String; empty |
Build/artifact discriminator. |
serviceName |
String?; application ID |
service.name. |
serviceVersion |
String; app version name then 0.0.0 |
service.version. |
flushAt |
Int; 20 |
Independent event/log count flush threshold. |
flushIntervalMs |
Long; 5000 |
Foreground-only periodic event/log flush; zero disables it. |
maxStackTraceFrames |
Int; 50 |
Maximum frames per error. |
breadcrumbCapacity |
Int; 100 |
Ring-buffer capacity. |
maxQueueSize |
Int; 10000 |
Per-queue cap: up to this many durable events and independently this many durable logs. |
errorSampleRate |
Double; 1 |
Non-fatal error sampling from 0–1. |
enableCrashReporting |
Boolean; true |
Install the uncaught-exception handler. |
debug |
Boolean; false |
Emit SDK diagnostics through Logcat. |
Identity, accounts, and events
| Method | Parameters | Returns | Behavior |
|---|---|---|---|
Anectico.identify(distinctId, set?) |
stable ID; person properties | Unit |
Links anonymous and known identity and queues identify. |
Anectico.group(type, key, set?) |
account type/key; properties | Unit |
Records membership and $groupidentify. |
Anectico.capture(event, properties?) |
event; properties | Unit |
Queues a diagnostic event with current identity/session/groups. |
Anectico.screen(name, properties?) |
screen; properties | Unit |
Queues $screen and $screen_name. |
Anectico.reset() |
— | Unit |
Logout: rotates anonymous identity/session and clears groups, global error-user context, and breadcrumbs. |
Anectico.getDistinctId() |
— | String? |
Current known/anonymous ID, or null before initialization. |
Anectico.getSessionId() |
— | String? |
Durable current session ID without extending its idle window. |
Logs
| Method | Parameters | Returns | Behavior |
|---|---|---|---|
Anectico.captureLog(body, severity, attributes?) |
nonblank body; severity default info; attributes |
String |
Atomically queues an OTLP log and returns its stable 32-hex anectico.log.id, or "" before init/for invalid or oversized input/if durable persistence fails. |
Severity accepts trace, debug, info, warn/warning, error, and fatal. Caller attribute
values are sanitized and stringified. Each occurrence snapshots its original time, identity,
non-activity session, groups, release/dist, app/build, device, service, and environment. Retry after
offline use or process recreation retains that snapshot and preserves oldest-first order.
The log spool is independently bounded to maxQueueSize records and 10 MiB, dropping oldest; one
record is capped at 1 MiB. It flushes at flushAt, periodically, on background, flush, shutdown,
and next initialization. Network/429/5xx responses retry; 2xx acknowledges; other 4xx drops.
Android is currently the only native mobile SDK with this public log API; iOS and RN/Flutter bridges
do not yet expose it.
The SDK-owned $app_opened lifecycle event carries the resolved release, app version/build,
platform, OS, and privacy-safe device family. One event is emitted per real foreground transition.
Backgrounding emits one $app_backgrounded, stops recurring flushes before one explicit event/log
flush, and the next foreground recreates exactly one timer without rotating the session by itself.
These properties cannot be overridden and contain no
installation/device identifier. Anectico uses the marker to count canonical-person release adoption and
the adopted-person error-free rate.
The reserved $release/$app_version string properties are stamped on every captured event
(not only $app_opened), sourced from the same release/serviceVersion AnecticoOptions
resolved. Neither is invented: an app that never sets release and has no versionName to fall
back to sends events with neither property. See
Reserved event properties.
Errors, messages, users, and breadcrumbs
| Method | Parameters | Returns | Behavior |
|---|---|---|---|
Anectico.captureError(throwable, options?) |
Throwable; optional CaptureOptions |
String |
Captures a caught, handled error with mechanism java.caught_exception and returns its ID, or "" before init. |
Anectico.captureMessage(message, level, options?) |
message; level default info; options |
String |
Captures a message and returns its ID. |
Anectico.addBreadcrumb(category, message, level, data?) |
fields; level default info |
Unit |
Adds context for a later error. |
Anectico.setUser(user) |
AnecticoUser |
Unit |
Sets global error user context. |
Anectico.clearUser() |
— | Unit |
Clears global error user context. |
Anectico.captureRawError(...) |
type, message, frame JSON, fatal, mechanism, tags | String |
Bridge API for React Native/Flutter; native apps should use captureError. |
CaptureOptions fields are tags, extra, optional user, optional level, and optional
fingerprint. AnecticoUser fields are id, email, username, ipAddress, and segment.
Breadcrumb levels are debug, info, warning, and error.
Fatal crashes and failed manual error sends are spooled for the next launch. Upload the matching
R8/ProGuard mapping.txt for every release/dist. The crash handler marks uncaught throwables as
unhandled fatal errors with mechanism java.uncaught_exception. Native level, handling, and
mechanism values are SDK-owned; caller tags cannot override them.
Each occurrence automatically includes release/distribution, Anectico SDK/platform, Android model and OS version, and app version/build. These fields are SDK-owned so Issue Story receives trustworthy device context. Anectico records the shared hardware model, not a physical-device identifier: Android ID, serial number, installation ID, and the user-assigned device name are never collected.
Agent runs
Anectico.startAgentRun(agent: String, options: AgentRunOptions = AgentRunOptions()): AgentRun
starts a local run handle and snapshots its start time, identity, and session.
import com.anectico.sdk.Anectico
import com.anectico.sdk.AgentRunOptions
import com.anectico.sdk.AgentRunTerminalStatus
val run = Anectico.startAgentRun(
"checkout-helper",
AgentRunOptions(agentId = "checkout", agentVersion = "1.4.0", conversationId = "chat-42"),
)
// Perform the agent's work, then declare its actual outcome.
val accepted = run.end(AgentRunTerminalStatus.COMPLETED)
// On failure instead: run.end(AgentRunTerminalStatus.FAILED, terminalReason = "tool_failed")
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/tool calls.
end(status: AgentRunTerminalStatus = COMPLETED, terminalReason: String = defaultReason(status)): Boolean
accepts COMPLETED, FAILED, TIMED_OUT, CANCELLED, or MAX_STEPS; their wire values are
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 reason or rejected local scheduling attempt returns false and leaves the handle
open for retry. The first accepted outcome wins; later valid calls return true without a second
span. Acceptance is local scheduling, not confirmed network delivery.
recordAgentRun is the native bridge entry point for React Native and Flutter. Kotlin application
code should use the typed startAgentRun handle.
Backend propagation
| API | Parameters | Returns | Behavior |
|---|---|---|---|
Anectico.propagationHeaders() |
— | Map<String,String> |
Fresh traceparent plus identity baggage when available. |
AnecticoInterceptor() |
OkHttp interceptor | Interceptor |
Applies trace/identity headers to every request made by that OkHttp client. |
AnecticoPropagation.apply(connection) |
HttpURLConnection |
Unit |
Applies headers before connect(). |
AnecticoPropagation.headers(distinctId) |
optional ID | header map | Pure header builder. |
newTraceparent() |
— | String |
Creates a sampled W3C traceparent. |
baggageWithDistinctId(id, existing?) |
ID; baggage | String |
Replaces the Anectico member while preserving others. |
percentEncode(value) |
string | String |
Encodes a baggage value. |
Attach AnecticoInterceptor only to clients whose destinations you trust. Public-device baggage is
correlation context, not authenticated proof of the user.