InAppify

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

import 'package:inappify_sdk/inappify_sdk.dart';

Future<void> updateMobileSegment() async {
  final written = await InappifySdk.setAttributesResult({
    'segment': 'returning', 'removed': null, 'also_removed': '',
  });
  if (!written.isSuccess) return;
  final refreshed = await InappifySdk.syncAttributesAndOfferingsIfNeeded();
  if (!refreshed.isSuccess) return;
  refreshed.data?.getCurrentOfferingForPlacement('upgrade');
}

Kotlin

import com.inappify.sdk.*

suspend fun updateMobileSegment(client: InappifyV2Client) {
    val written = client.setAttributes(mapOf(
        "segment" to "returning", "removed" to null, "also_removed" to "",
    ))
    if (written is InappifyResult.Failure) return
    when (val result = client.syncAttributesAndOfferingsIfNeeded()) {
        is InappifyResult.Success -> {
            val selected = result.data.getCurrentOfferingForPlacement("upgrade")
        }
        is InappifyResult.Failure -> { /* Retain/retry according to the error. */ }
    }
}

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

import 'package:inappify_sdk/inappify_sdk.dart';

Future<void> reservedExamples() async {
  await InappifySdk.setEmail('person@example.com');
  await InappifySdk.setDisplayName('Alex');
  await InappifySdk.setPhoneNumber('+989120000000');
  await InappifySdk.setCampaign('spring');
  await InappifySdk.setKeyword('upgrade');
  final allowed = await InappifySdk.canSetReservedAttributeResult(r'$email');
  if (allowed.isSuccess && allowed.data == true) {
    await InappifySdk.setReservedAttributeResult(
      attribute: InappifyReservedAttribute.email, value: '',
    );
  }
}

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.

KeyNote
$email, $displayName, $phoneNumberPrefer the dedicated setter
$apnsTokens, $fcmTokens, $idfa, $idfvCheck platform capability, permission and write allowlist
$ipAccepted by Kotlin/Go V2; current iOS validator rejects it and Web does not expose it as reserved
$campaign, $keywordAttribution, 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