Customer attributes and offering sync
Use setAttributes maps, null/empty deletion, reserved attributes and bounded syncAttributesAndOfferingsIfNeeded.
Attributes feed targeting; they neither authenticate accounts nor grant access. Match dashboard keys and values.
Set, delete and obtain a fresh offer
Flutter Android and iOS
Kotlin
setAttributes takes a Map<String, String?>. Null/empty deletes; whitespace is a value. The SDK owns durable queuing, retry and quarantine; the new public API has no separate queue/flush/sync/delete controls. Successful storage is not a guarantee that every pending value already reached the server. Network failure must not clear the queue or replace the customer.
Key and value limits
Mobile custom keys match [A-Za-z][A-Za-z0-9_-]{0,39}; values allow up to 500 code points. Uploads use batches of up to 50, with at most 1000 queued keys. Definitive 422 validation failures follow the existing quarantine rules. Go V2 writes are write-only; don't expect the setter's list or CustomerInfo to return all attributes.
Web custom/reserved writes require Web App allowlists. JavaScript sends changes to the server immediately and has no native-style offline queue. Do not assume mobile validation limits apply to Web.
Reserved attributes
Kotlin equivalents include client.setEmail("person@example.com"), setDisplayName("Alex"), and setPhoneNumber("+989120000000"), returning Result. Other one-string helpers are setApnsToken, setFcmToken, setIdfa, setIdfv, setIp, setCampaign, setKeyword. Empty values delete. Use the exact reserved key, such as $email, for capability queries.
| Key | Note |
|---|---|
$email, $displayName, $phoneNumber | Prefer the dedicated setter |
$apnsTokens, $fcmTokens, $idfa, $idfv | Check platform capability, permission and write allowlist |
$ip | Accepted by Kotlin/Go V2; current iOS validator rejects it and Web does not expose it as reserved |
$campaign, $keyword | Attribution, not account authentication |
Combined refresh quota
Kotlin and Flutter Android/iOS admit five calls per rolling 60 seconds. Admitted failures count; identity changes do not reset client quota. Extra calls make no network request and return current-session cache or RATE_LIMITED_NO_CACHE with retry timing. An empty queue still permits a fresh fetch within quota.
Direct JS uses await client.setAttributes({segment: 'returning', removed: null}), then await client.syncAttributesAndOfferingsIfNeeded(). Web sync refreshes the customer snapshot, then offerings. Flutter Web 0.2.1 does not implement that operation.
Do not manually rewrite cache, country or mobile version. The new facade has no setTargetingContext. Offerings · Platform matrix