SDK platform
Start here
Build a reason to stay. Turn the value your app already delivers into a personal, brand-matched story. PieceJobYour app + SDKSupplies approved values and local access facts; checks eligibility and renders the story. It does not take payments.Recappel BuilderMobile workspace for connecting apps, editing stories, testing, publishing and results.Recappel serviceStores configuration, receives approved aggregates/events and delivers compatible story versions when the SDK requests them. Use your existing app Recappel supports React Native and Expo on iOS and Android. Keep your authentication, payment provider and navigation. The SDK sends only what you explicitly report—not your database or every action.Choose evidence of delivered value: saved highlights, completed sessions or finished projects. Do not claim books read or time saved unless your app measures it. Customers see your brand; developers manage it in Recappel. Start integrating Follow the five steps → or use your coding agent →. Both paths use the same SDK and checks.Install @recappel/react-native@latest, test in Sandbox, then prepare your customer release. Check Release status → for version compatibility and availability.
Connect in five steps Follow the Builder’s five setup steps. Verify each result before continuing. Before you start A React Native / Expo project you can edit and build. Installing the SDK requires an updated native app on your device.Add the app in Builder. Copy its public Sandbox key and service URL; keep its Production configuration separate.Use the app’s existing customer ID, permission decision and explicit Free/Trial/Paid facts. Your agent can locate these in code; it should ask only for missing decisions.Either a development or release build can connect to Sandbox. The configured key and environment—not the build type—choose where data goes. 1. Install SDK Add the SDK and compatible persistent storage.Confirm the dependency is installed and your app builds. Installation is checked locally, not by the server.Open this step → 2. Connect Add the app’s public key and service URL, then wire your customer identity, permission and access facts. Start with Sandbox.A received request identifies the app, environment, SDK version and contact time. Mock transport cannot prove contact. No story yet is not a connection failure.Open this step → 3. Pass billing and access Map trusted paid/trial facts into setLifecycle, or supply explicit free audience facts.Check source freshness, local eligibility and the selected Sandbox device’s diagnostics. Detailed lifecycle stays on-device. Diagnostics are not independent payment verification.Open this step → 4. Send useful values Report an approved snapshot or completed value moment in Sandbox.Builder shows the received key, method, safe example and last-received time. A saved definition or resolved flush is not receipt. Confirm meaning separately.Open this step → 5. Show and measure Add the invitation, verify playback in Sandbox, then prepare the customer build and publish from Builder.Check invitation visibility, opening, completion and the separate outcome individually. Invited is not a view. Client outcomes are unverified; previews do not count toward Results.Open this step → After setup Confirm value meanings, review the outline and edit the generated draft.Test the exact version and edge cases in Sandbox.Prepare and ship your host app with its Production key, production environment and matching access facts. No story needs to be live yet.Review audience, timing, version and capacity checks, then Publish in Builder. No additional publish switch is needed in your code.Follow the production checklist →. Keep the comparison group and allow the outcome window to finish.
Connect using your agent Give your coding agent one setup task, review the changes, then run your app. Open your host-app project in your coding agent. Copy task 1 below; it checks compatibility and installs the SDK. Review its result, then copy the next task. These handoffs do not upload your repository to Recappel.For Connect, provide the app’s public Sandbox key and service URL from Builder. The agent finds your existing identity, permission and access sources in code and asks only when something important is missing. Never paste provider secrets, receipts or customer content. Setup tasks Review the result Changed files and the exact command to run or build the updated app.What worked locally and what Builder actually received.Any missing input or check before the next step.The final task prepares the Production configuration. You control store submission and publication in Builder; the agent does not purchase or launch anything for you. Pause host-owned polling, retries and value work on temporary background/offline transitions; cancel late work without retiring otherwise valid authority. Do not call configure({ enabled: false }), replace SDK identity or unmount the presenter solely for AppState/connectivity. Gate already handles foreground/background checks and may flush queued work; cinematic motion pauses without replacing the player. Keep the original authority deadline active, and check identity, permission and expiry before and after resume awaits. Real logout, permission withdrawal, account change and expiry still retire immediately. Do not refresh cached timestamps or reset show caps. Private-media providers must still abort/clear background requests; that is not global session retirement. Open a story and cancel an OS share/review sheet: return to the same session, story card and show counts, without another present() or duplicate exposure. Test expiry, permission withdrawal and account change while suspended: old work must not resume. Check offline recovery, failed/late renewals and unchanged cached observations without extending authority or starting duplicate work.
Connect the SDK
1. Install SDK Install Recappel and configure storage to keep values across restarts. Run this in your host-app project. The SDK contains compiled JavaScript, types and assets under a proprietary license. The latest tag selects the release recommended for new installations. Keep the resolved version in your lockfile and your app’s bundle/package ID unchanged. Existing apps do not update automatically; review compatibility, run this command again to upgrade, then apply any required native build steps and test. No uninstall or reconnection is needed for a compatible update. Keep the app key, billing/access adapter, value keys and persistent storage. Check release notes for any migration. SDK releases declaring RecappelPosterShare also contain autolinked Android/iOS native source. Use the existing CocoaPods/native build flow when first adding that module; Metro reload and Expo Go cannot add it. An SDK-only update from beta.12 to beta.13 uses the same native code and does not require a new native rebuild. Verify the installed package, actual binary and updated bundle. No separate sharing package is required. Verify with npm ls @recappel/react-native --depth=0 and node -p "require('@recappel/react-native/package.json').version"; the installed version must match the lockfile. A lockfile-only edit is not installation. If they disagree, finish the targeted install and verify again before building. Restart the host bundler after updating; an old APK still contains its old bundle. Do not delete the lockfile or app data to repair a package mismatch. Persistent storage AsyncStorage is an optional peer. Install a compatible version or provide a StorageAdapter. Without working persistence, fetch transport uses memory and loses values when the process ends. Mock transport uses memory by default. Build and verify Check React / React Native, Skia, Reanimated and Safe Area Context against the package peer requirements. Keep compatible existing versions. For Expo, use expo install for missing compatible native dependencies. Match Reanimated/Worklets and Babel configuration to the installed version. On bare iOS follow your CocoaPods flow on macOS. Create a new development or release build using your project’s normal build command, then install it. Expo Go and an older APK do not contain the newly installed native libraries. Confirm public imports resolve, run checks and launch the app; then select Mark as installed in Builder. A release build can connect to Sandbox. Use a development build when you want Metro and debugging tools. See Expo libraries ↗ and development builds ↗. Connect verifies the first server contact. 2. Connect your app Connect using your agent Release status Previous: Connect using your agent Next: 2. Connect your app
2. Connect your app Use the app’s public key and a stable customer ID to connect the right data to the right project. Create an app record Builder → Apps → Add app. Enter the name, platform and exact bundle/application ID; the icon is optional.Use this app’s Sandbox connection for testing. Its Production key and delivery remain separate.Copy the public project key and service base URL from this app’s setup instructions.A bundle ID is not a credential. Each value reuses this connection; it does not need another key. Never put a Supabase service-role key, AI key or billing secret in the host app. Configure at the session boundary Add the helper below to your integration module and call connectRecappel(...) after your app resolves the customer and access facts. Supply these six inputs from your existing app; merely pasting the helper does not run it.InputSourcepublicProjectKeyThis app’s public key from Builder. Use Sandbox first.apiBaseUrlService base URL copied from Builder. Required by this SDK; do not use the npm URL or invent a server.sdkEnvironment'sandbox' for testing; 'production' with the matching Production key for customers.opaqueCustomerIdYour stable internal customer ID, not email. No hardcoded tester UID is needed.enabledForThisCustomerYour existing permission/enrollment decision. Keep genuine opt-outs; this is not a publication switch.accessFactsExplicit free/trial/paid AudienceContext and the time actually observed. Unknown access is not Free.The same initialization runs in development and release builds. Do not gate it behind __DEV__ or an extra test flag. There is no publish/enable code switch: you publish stories from Builder after setup. Configure before lifecycle/values, not on every render; handle customer and permission changes as described in Customer identity & sessions. Verify contact Open the updated app and sign in if required. Reach the screen where initialization runs.Open this app’s Connect screen in Builder and refresh. Check a new last-seen time and SDK version.If the installed release APK predates the code change, rebuild and install it before checking again.Onboarding alone does not connect when the app has not resolved a customer identity. Keep existing permission and access checks; do not invent a guest ID to force Connected.story: null is expected before a story is available. No published story or reported value is needed for contact. With the right configuration installed, the SDK fetches published stories later.Builder reads received requests; pressing Refresh cannot start the SDK inside your app.Fetch transport contacts the service; mock stays local. A resolved configure or flush does not prove receipt.If waiting continues, check identity, permission, access freshness, key, environment, base URL and network. The SDK appends its own route to the supplied base URL.Keep the connection currentIf your access adapter uses a validity deadline, refresh from its real source before the freshness deadline expires while the app is active. Keep original observation times; do not make cached facts fresh. Use one in-flight check, finite timeouts and bounded retry backoff. Stop stale reporting but retain a safe recovery path. Pause host-owned polling, retries and value work on temporary background/offline transitions; cancel late work without retiring otherwise valid authority. Do not call configure({ enabled: false }), replace SDK identity or unmount the presenter solely for AppState/connectivity. Gate already handles foreground/background checks and may flush queued work; cinematic motion pauses without replacing the player. Keep the original authority deadline active, and check identity, permission and expiry before and after resume awaits. Real logout, permission withdrawal, account change and expiry still retire immediately. Do not refresh cached timestamps or reset show caps. Private-media providers must still abort/clear background requests; that is not global session retirement. Open a story and cancel an OS share/review sheet: return to the same session, story card and show counts, without another present() or duplicate exposure. Test expiry, permission withdrawal and account change while suspended: old work must not resume. Check offline recovery, failed/late renewals and unchanged cached observations without extending authority or starting duplicate work. There is no universal five-minute SDK timeout.Test staying in the foreground beyond two refresh intervals, changing a value after expiry, failed lookups, recovery, account switching and opt-out. Local success, accepted value receipts, saved definitions and story playback are separate checks. After a successful authoritative same-account access renewal, use refreshAccess only when the installed public API declarations support it. Do not call configure on every successful access refresh or retire the permitted session merely because a routine authority lookup starts. Keep the original freshness deadline/expiry and its hard-expiry handler active during both the authority lookup and SDK await; recheck expiry, identity, permission and cancellation afterward. While checking, cached facts must not receive a new timestamp. Required maxAgeMs is a positive safe integer bounded by configured SDK/story freshness; use the authority's documented shorter freshness policy, not an invented timer. Paid/Trial needs equivalent active, unexpired, nonsuppressed lifecycle facts with strictly newer audience and lifecycle observations; Free has no lifecycle. refreshAccess is only for strictly newer, still-fresh observations of equivalent audience, profile and lifecycle facts in the exact already enabled/enrolled/unblocked project/environment/customer context. Only observedAt may change; both old and new observations must remain fresh. A true result preserves the session, caps and current player; it does not automatically bootstrap or prove a server receipt. A false result requires the normal configure recovery path after resolving current permitted facts; never replay captured old-account configuration. Matched invalid, changed or expired facts disable admission/presentation; a mismatched account cannot mutate the new account. Real identity, permission, plan or lifecycle changes still use deliberate reconfiguration. Older packages need an approved upgrade for this API; never cast, call private methods or assume newer docs upgraded the installed binary.
Customer identity & sessions Use a stable opaque customer ID to keep each person’s values and story assignment separate. IdentityUseBuilder accountDeveloper access to apps and the Recappel plan.App / project keyPublic identifier for an app and SDK environment—not a password.Opaque customer IDHost-owned ID tying values, assignment and outcomes to one customer.Use the same ID across that customer’s devices. Never use email, name, phone, advertising ID or provider customer/transaction ID. Opaque IDs are pseudonymous, not legally anonymous. Changing customers Wait until the customer is resolved. On logout disable enrollment/reporting and hide the presenter. Guard late host callbacks, then configure the next resolved customer with that customer’s access facts. Storage and queues are isolated by exact project key, environment and opaque customer.Use your existing session flowThere is no public login(), logout() or reset() API. Reconfigure at deliberate session/access changes and test pending requests, visible stories and account switching. Never use deleteEndUser() for sign-out. Guests and multiple devices Guest merging is not implemented. Define a stable guest ID and explicit merge policy before enabling it; do not create a new ID each launch.Device-local increments are not account-wide totals. For synced data, use an authoritative host snapshot. Verify cross-device deduplication and merging before claiming exact totals.
3. Pass billing and access Keep your payment provider. Pass trusted access facts without granting Recappel billing-account access. Map the backend, store API, payment SDK or entitlement cache your app already trusts. No customer-side RevenueCat installation is required; Recappel uses it only for its own Builder billing.Pass access, real dates, renewal state, environment and known grace/retry/refund flags.Never pass secrets, payment details, receipts, raw provider objects or customer contacts.Refresh at sign-in/startup and source changes. Cached facts keep their original observation time. Recurring-subscription example Add this host-owned adapter, then call passSubscriptionFacts(facts) from the app’s resolved access callback and after actual source changes. Supply your existing source’s facts—not example dates. Use it only for a conventional recurring subscription whose period end supports the displayed wording.Match SDK sandbox to lifecycle SANDBOX, or production to PRODUCTION. Never relabel store test purchases as live. Free access uses explicit audience facts instead of this adapter. Purchase-model support ModelUse only known factsRecurringLegacy LifecycleDTO or provider-neutral LifecycleDTOv2. Cancellation is not necessarily lost access.PrepaidActual access end and automatic/manual renewal; do not invent a next charge.Postpaid / invoiceLifecycleDTOv2 separates collection mode and known period, charge and due dates.One-time / lifetimeExplicit one-time access with an honest no-recurring-charge ending.Free / Trial / PaidHost-supplied AudienceContext. Trial must be explicit; unknown access must not become Free.LifecycleDTOv2 is exported alongside the legacy DTO. Use its exact types. Free stories use non-billing moments and do not need fabricated purchase dates. Verify access facts setLifecycle validates and stores detailed facts locally. Open the updated app: Builder’s Sandbox setup check automatically receives a bounded access-readiness status from supported SDK versions. It does not receive your billing account, raw customer records or payment receipts. Bootstrap contact is not independent payment verification.Inspect eligibility, then test the actual transparency/ending on-device. Stale or mismatched access and grace/retry/refund must not become a positive renewal claim.Keep the connection currentIf your access adapter uses a validity deadline, refresh from its real source before the freshness deadline expires while the app is active. Keep original observation times; do not make cached facts fresh. Use one in-flight check, finite timeouts and bounded retry backoff. Stop stale reporting but retain a safe recovery path. Pause host-owned polling, retries and value work on temporary background/offline transitions; cancel late work without retiring otherwise valid authority. Do not call configure({ enabled: false }), replace SDK identity or unmount the presenter solely for AppState/connectivity. Gate already handles foreground/background checks and may flush queued work; cinematic motion pauses without replacing the player. Keep the original authority deadline active, and check identity, permission and expiry before and after resume awaits. Real logout, permission withdrawal, account change and expiry still retire immediately. Do not refresh cached timestamps or reset show caps. Private-media providers must still abort/clear background requests; that is not global session retirement. Open a story and cancel an OS share/review sheet: return to the same session, story card and show counts, without another present() or duplicate exposure. Test expiry, permission withdrawal and account change while suspended: old work must not resume. Check offline recovery, failed/late renewals and unchanged cached observations without extending authority or starting duplicate work. There is no universal five-minute SDK timeout.Test staying in the foreground beyond two refresh intervals, changing a value after expiry, failed lookups, recovery, account switching and opt-out. Local success, accepted value receipts, saved definitions and story playback are separate checks. After a successful authoritative same-account access renewal, use refreshAccess only when the installed public API declarations support it. Do not call configure on every successful access refresh or retire the permitted session merely because a routine authority lookup starts. Keep the original freshness deadline/expiry and its hard-expiry handler active during both the authority lookup and SDK await; recheck expiry, identity, permission and cancellation afterward. While checking, cached facts must not receive a new timestamp. Required maxAgeMs is a positive safe integer bounded by configured SDK/story freshness; use the authority's documented shorter freshness policy, not an invented timer. Paid/Trial needs equivalent active, unexpired, nonsuppressed lifecycle facts with strictly newer audience and lifecycle observations; Free has no lifecycle. refreshAccess is only for strictly newer, still-fresh observations of equivalent audience, profile and lifecycle facts in the exact already enabled/enrolled/unblocked project/environment/customer context. Only observedAt may change; both old and new observations must remain fresh. A true result preserves the session, caps and current player; it does not automatically bootstrap or prove a server receipt. A false result requires the normal configure recovery path after resolving current permitted facts; never replay captured old-account configuration. Matched invalid, changed or expired facts disable admission/presentation; a mismatched account cannot mutate the new account. Real identity, permission, plan or lifecycle changes still use deliberate reconfiguration. Older packages need an approved upgrade for this API; never cast, call private methods or assume newer docs upgraded the installed binary.
4. Send useful values Report delivered value with an accurate meaning, unit, time window and reporting method. Discover the useful catalog first; choose each story later. Review all eight families, not two sample totals. These five narrative jobs are not five compulsory screens.Story jobPurpose and evidenceRecognitionMake the customer curious about their own useful activity. The product benefit and a truthful hook leading to its strongest measurable proof. The opening is an attractor, not an arbitrary statistic or an unearned identity claim. Its following hero supplies the number.Concrete proofShow the most meaningful result, then distinct supporting detail. Completed useful work, accumulated value or a clearly labelled current collection; breadth, depth and valid derived facts. A tap is not completion. A current library count is not books read. Supporting cards may contain several different facts.Progress and patternsShow activity over a known period or a genuine change. Actual ordered time buckets, comparable periods, distinct active days and verified participation. A previous SDK bucket alone is not a comparable period. More activity is not necessarily improvement.Breadth and breakdownShow composition, range or a clearly defined leader. Distinct resources, complete exclusive category counts, and a safe leading category with its supporting count. Overlapping tags are not pie-chart parts. Most represented is not automatically favourite. Never send customer-written labels.Milestones and recapShow an earned milestone and a concise keepsake. Real streak/history or achievements; reuse the strongest proof and a few complementary facts in the recap. No invented threshold, rank or separate data collection just for sharing. Actual access clarity comes from the host.Choose the hero by direct benefit, reliable source and understandable scope, not the largest number. A supporting card can use several facts. Recap reuses evidence; access clarity comes from the host.Discovery familyLook forEvidenceValue deliveredUseful work completed, saved, created, revisited or resolved; amounts, duration or distance only when measured.Successful source actions or authoritative totals; separate current holdings, period activity and lifetime history.Breadth and depthDistinct resources used, useful collection size, variety, favourites and meaningful reuse.Deduplicate the right entity; an item added is not an item read, used or completed.Averages and ratesAverages per resource or active day, completion rates, proportions and intensity.Record numerator, denominator, formula, same population/window/unit, exclusions and rounding. A zero denominator means unavailable, not zero; do not average averages.Category mixCounts by approved categories or sources; distinct category breadth and bounded distributions.Define the denominator and unknown/other bucket. A percentage composition needs a complete non-overlapping partition; multi-tag counts may overlap and must not be presented as one.Leaders and preferencesMost-used category, most-revisited type or an explicitly chosen favourite, plus its count.Specify ranking measure, population, window and ties. A measured leader is not a declared preference. Use an approved nonpersonal enum; a customer tag, title or person is not safe just because it is short.Time and changeReal weekly/monthly buckets, matched-period comparisons and distinct active days.Define boundaries, timezone, order, completeness and resets. Imports may preserve old timestamps; surviving records are not full activity history. Compare completed or matched elapsed periods only.Habits and milestonesCurrent and longest streaks, earned badges, personal bests and verified checkpoints.Reuse the app’s qualifying-action and day rules; distinguish current versus historical best and truncated history. No made-up badge threshold or leaderboard.Brand and artworkLicensed app-owned logo, illustrations, category icons, badges, background tokens and registered fonts.Catalog local source, rights, public-use approval, subject, crop and intended role. Do not upload customer photos, book covers or private content as generic decor. No asset means a plain-layout fallback.Aim for 8–20 distinct useful numeric candidates when the app supports them. For a rich-story shortlist, seek at least 6 meaningful facts across 3 numeric families, including a core result and supporting facts. These are catalog review targets, not hard discovery minimums. The catalog supports up to 32 numeric definitions plus 8 structured groups with separate limits. Ordinary derived scalars still consume numeric slots; a registered ratio, distribution or series consumes one structured slot. Preserve every existing key, including all 31 scalar keys in an existing 31-value catalog. Never remove working values to make room for a group. Check the installed declarations and compatible Builder/service path; older builds retain their declared limits. Review excess candidates locally before connecting more. New generated stories require 3–5 distinct approved logical values, not exactly five. A scalar or a reviewed structured group is one logical value: each group counts as one, not one per category or period. If fewer than 3 suitable values are available, do not generate a new story; keep useful values connected and report what is missing. Never fabricate, duplicate or pad values to meet this selection minimum. Existing saved stories and legacy compact SDK/runtime stories remain compatible with fewer values, including one numeric proof, subject to their existing validation and eligibility rules. This selection rule is not a discovery quota or a minimum number of displayed cards. Artwork and label-only metrics do not count as numeric coverage.PriorityMeaningP1 — CoreDirect evidence of the app’s main customer benefit. Inspect and connect these first.P2 — SupportingAdds distinct context: participation, depth, progress, breadth or an earned milestone.P3 — OptionalA useful secondary view or safe personalisation; omit when it adds no meaning.Priority is not confidence, permission or final card order. Keep source-confirmed, inferred and unresolved meanings separate. Return all families as ready/wired, pending, unavailable or excluded with reasons. Do not fabricate values to meet a target. Report after completion Configure the resolved customer and audience; pass paid/trial lifecycle before period values. Explicit free access uses its non-billing cycle. Increment after a confirmed save—not render, screen open or a pending request. Deduplicate retries/imports in the host. Choose one method per key MethodStoresUseincrement / addCycle sumConfirmed additive events; repeated calls add again.setLatest snapshot; can decreaseHost-owned current, all-time, period or rolling total. Repeated calls replace.setMaxHighest observed in this cycleNot a current streak or an automatic all-time record.setLabelReviewed enumNo titles, notes, names or arbitrary text.STRUCTURED VALUES When the installed public declarations expose StructuredValueDefinition, StructuredValueSnapshot, configure.structuredValueDefinitions and setStructuredValue(key, snapshot), add up to 8 structured definitions to the existing initializer, separately from its 32 numeric valueDefinitions. Do not invent an API or assume an older installed build gained it. Report a missing capability and use the approved package-update flow when needed. Preserve existing keys and semantics; do not register a group or generated binding that collides with any scalar key. Structured keys and fixed category keys match ^[a-z][a-z0-9_]{0,31}$. - ratio: declare public numeratorLabel, denominatorLabel and precision (0–3, default 1). Report numerator and denominator from the same authoritative population/window; the SDK divides and rounds, without automatic percentage scaling. A zero denominator is unavailable, never a fabricated zero or average of averages. - distribution: declare 2–24 fixed public categories {key,label}, basisLabel, exclusive:true and complete:true. Report every category count plus total; counts must sum to total. Use only permission-reviewed app-defined taxonomy, never private/custom tags, titles or dynamic customer labels. The runtime shows the top three categories plus Other for the complete remainder, preserving the denominator. Equal leaders are ties with no unique leader claim; a unique positive leader is most represented, not automatically favourite. Unknowns need an explicit truthful category in the fixed taxonomy, not silent omission. Overlapping tags are not this distribution. - series: scope must be rolling. Declare interval day, week or month and periods 2–4. Supply exactly that many ordered contiguous completed calendar windows {startDay,endDayExclusive,value}, an IANA timeZone and genuine coverage. Weeks start Monday; months start on the first. The last window ends at the current interval boundary in that timezone; no partial current period or stale set of windows. Missing history is null or unavailable, not zero; any missing window makes the group unavailable for presentation. A decline is not automatically improvement. Every snapshot includes status and genuine UTC observedAt. maxAgeHours is an integer 1–168, default 24. Do not refresh observedAt on stale data. Reporting {status:'unavailable',observedAt} clears the entire local group's prior displayable values; expiration also makes it unavailable. This is not an arbitrary scalar reset API. Readings are finite, nonnegative and at most one trillion; count distributions require integer counts. Only definitions, availability status and revisions are sent for the structured facts; their numbers stay on the device in account-scoped storage. The request still uses the existing app/environment/customer/device scope and access guards; ordinary scalar aggregate telemetry is unchanged. Never send numeric examples, source paths or private labels in definitions. Recheck the exact active account, permission and access after every source await. Cancel retired work and invalidate an unavailable source only for its still-current permitted session. Never clear a new customer's group from an old callback. Verify metadata received, available/unavailable receipt and rendered story separately; unavailable is connected with no usable reading, not missing SDK wiring. Group shape stays registered; owner wording edits and Rewrite must preserve its meaning and formula. Do not add fake labels, history or counters to fill story slots. Define, then verify For each candidate, record its key, customer benefit, source, unit, exact window/timezone, behavior, decrease/reset rules, direction, reporting method, possible story uses, priority, evidence confidence and sharing safety. Keep source paths and implementation notes in a local catalog.Enter the approved meaning in Builder’s existing fields. Put advisory priority and story use in its description: “Priority: Core. Current saved highlights; deletions reduce the count. Useful as proof.” This is an existing description field, not a new priority control or SDK parameter. Do not add unsupported fields to a ContextManifest; the importer does not forward the whole local catalog to authoring.Builder access is not needed to discover or instrument values. Reuse local keys when correct, wire clear useful candidates within existing permissions, and ask only about material gaps. A scalar receipt later proves the exact key and method arrived—not its meaning. A structured receipt reports availability, never the customer numbers. Check environment and last-received time, then confirm the meaning before story use.Keys: 1–64 characters; lowercase letter first, then lowercase letters, digits or underscores.Numbers: finite, with domain-valid signs. Zero is valid data, not an achievement.Labels: 1–40 letters/digits/spaces/hyphens/underscores. Syntax is not a privacy filter; use an enum allowlist. Current automated authoring uses numeric definitions, not label-only proof.No code upload, automatic field crawling or new API key per value.Derived facts need a formula, numerator, denominator, matching population/window, exclusions and rounding. A zero denominator is unavailable, not zero. Skipping a scalar report may retain its older value; require a safe stale-display guard. Supported registered groups instead provide unavailable clearing and freshness expiry. Test synthetic inputs, ties, imports, decreases and failed reads.Builder generation selects reviewed scalar meanings and bounded registered groups. Dynamic customer labels and arbitrary second-metric captions remain unsupported. Registered distributions group a complete approved taxonomy as top three plus Other; registered series use two to four completed periods. Other scalar stack/chart paths keep their existing item limits; overlapping categories never become a complete partition. Asset files must be reviewed and uploaded separately; the model sees descriptions and approved IDs, not customer photos or an automatically extracted app.Add a stable valueDefinitions array to the existing configure call for every wired numeric value. Names, meanings and counting rules reach Builder automatically through the SDK. Open Values → Received from your app to review them; existing definitions are kept. No file import is needed. The copyable task includes the supported fields and an example.A received definition is not a received number. Confirm matching keys, counting rules and fresh receipts separately. Waiting for value means only the definition has arrived. Never send source paths, customer data or example numbers in definitions. Relationship approvals and artwork remain separate. Cycles and snapshots All five calls use SDK cycle buckets; the SDK retains current and previous. The separate structured snapshot API uses its declared source scope and completed calendar windows. Legacy subscription periods, lifecycle-v2 anchors and explicit free access select their supported cycles. A bucket is not automatically a calendar or rolling business window.Keep all-time/rolling calculations in the host. Re-send authoritative snapshots after customer, source or cycle changes. Document the metric’s timezone and reset rule; earlier reports are not automatically reattributed.Snapshot-only storiesminTotalEvents sums only increment/add values. Do not require positive additive totals for snapshot-only stories or add fake increments to pass the gate.
5. Show and measure Offer an invitation and measure story interaction separately from the business outcome. Mount the invitation Add the component below and render <RecapInvitation /> in one stable host-screen location, under your existing SafeAreaProvider. It displays the banner and opens the native story on tap. Do not mount a second presenter beside it.Wire this before publishing. With no available story—or for ineligible and holdout customers—it renders nothing. Do not reserve a fixed empty gap. Report the business outcome Use the experiment’s exact outcome key. Report confirmed success for both groups, even without a story opening—not from banner taps, review requests, restores or payment-screen visits. Outcome attribution A destination tap, review request or share-sheet opening is not proof of a renewal, posted review or completed share.recordOutcome() uses the current cycle and available experiment assignment; it cannot name a historical renewal period. Renewals may occur after rollover or without another session. Correct attribution and server verification are required before causal renewal claims. Test the full path Select and pair a valid Sandbox version; refresh access/values and check eligibility.Verify visible invitation, open, finish and early dismissal separately.Test each destination on its intended OS.Trigger the genuine outcome independently; check key and attribution.Repeat with no data, stale facts, wrong environment, holdout and billing trouble.Keep opened, completed and outcome received separate. Invited telemetry is preparation, not proof of an on-screen view. Shared previews are excluded from Results.Next, prepare the Production build and publish →. Reuse the same initializer and presenter; no extra code switch is needed to turn publication on. Compatible SDKs recheck a waiting invitation when newly supplied values become available, with coalesced checks rather than a polling loop. Keep one stable Gate; do not reconfigure the SDK, remount the presenter or reset an open player to force delivery. Eligibility and session checks still apply. Confirm this behavior in the installed SDK.
Build your story
Add a new value Use an arriving value first. Add host-app code only when the needed fact is missing. Use an arriving value Open app → Values, or a card’s Data → Add value.Select a received key. Review its meaning, unit, scope, safe example and timestamp.Correct any mistakes, confirm and return to the same draft card.No reconnection or new code is needed for an already-correct value. The Builder sees reported aggregates, not every variable in your app. Connect a missing value Describe the fact; review its suggested meaning, window, reporting method and stable key.Get its SDK instructions or use agent task 4. Reuse this app’s connection.Add the host reporting call and run the real action in Sandbox. Keep Waiting for first value resumable.After receipt, confirm key, method, example and time; choose Use in story to return to the card.During initial integration, collect the broader useful catalog using the five story-value criteria →; do not limit instrumentation to the current card. Keep priority and evidence separate. Builder access is needed to verify receipt, not to inspect source and wire clearly supported values.Example numbers can preview a draft while waiting. They cannot prove receipt or pass Production checks. Change or remove a value Renaming the display label does not change the key.Changing meaning/window may need a new key and instrumentation; never relabel old history.Show dependent cards before removal. Published versions remain fixed.Archiving is not erasure and does not remove the host SDK call.New calls require an app release. Test older versions that do not send the value.
Create & edit a story Review recommended values and an outline, then edit the story with a live preview. Story → Create story. Review useful candidates across your approved value catalog.Confirm preselected meanings and any material uncertainty.Choose 3–5 distinct approved logical values for this story; each scalar or registered group counts as one; other connected values remain available for another draft or revision.Review the outline: card purpose, value and time window.Generate the draft, then edit one live card with focused controls and the card strip nearby.New generated stories require 3–5 distinct approved logical values, not exactly five. A scalar or a reviewed structured group is one logical value: each group counts as one, not one per category or period. If fewer than 3 suitable values are available, do not generate a new story; keep useful values connected and report what is missing. Never fabricate, duplicate or pad values to meet this selection minimum. Existing saved stories and legacy compact SDK/runtime stories remain compatible with fewer values, including one numeric proof, subject to their existing validation and eligibility rules. This selection rule is not a discovery quota or a minimum number of displayed cards.The five narrative jobs guide selection; they do not force five cards. Priority suggests relevance, not permission or an automatic final order. Actual customer data and the story’s renderer/eligibility rules still decide what can appear. Editor controls ControlChangesDataApproved value and meaning. Real customer numbers stay bound to data.TextWording around the value, never invented totals or dates.LayoutArrangement of the same fact—not a different metric.Colour & artworkApproved tokens/assets; test contrast and image failure.Reorder / removeValue-card order, with recognition/transparency restrictions and Undo.Preview, Save and Publish are separate. Edits create a draft without changing the live story. Suggestions and approval Create Story uses approved app context, value meanings and reviewed public artwork to choose copy, compatible layouts, images, a subtle texture, palette and ready fonts. Review the resulting draft before publication.No model runs in the runtime SDK. Source code, customer rows, IDs, billing records and private pictures do not go to the model. The optional artwork tool scans locally and uploads only approved files. Arbitrary fonts still need supported host registration. Values Rewrite remains text-only.
Cards & layouts Choose supported native cards and layouts. Stories contain validated data, not executable UI. Recognition → value → next action (optional) → transparency → rating (optional) → share (optional).Standard definitions contain 4–9 cards. A validated compact cinematic story can use three: recognition, one numeric proof and clarity. Recognition comes first; the ending must match paid/trial/free access. Customers can exit at any time. The ten card types TypeUseAvailable layoutsrecognitionWarm framing of the customer’s valuestandard, illustrated, openingsingle_metricOne real value and a sentencenumber_first, statement_first, poster, orbit, milestonecomparisonSame measure in comparable periodscolumns, statementbreakdown2–4 meaningful componentsbars, bubblestimelineExplicit labelled items, not an inferred event historydots, bars, stackrecoveryAn evidenced comeback, not guessed activitydefaultnext_actionA real destination in your appdefaulttransparencyActual billing/access facts or a free-access endingdefaultratingOptional native review/store destinationdefaultshareAn approved summary without raw contenttext, trading_card, recapBuilder previews use the SDK renderer. The service checks declared SDK capabilities before delivering newer layouts or compact stories to a device. Content rules Use suppress_card for zero achievements. Never celebrate zero or missing values.Compare the same measure with comparable periods and coverage; two numbers alone do not establish comparability.Breakdowns require non-overlapping components. One cycle total cannot supply a daily timeline.Copy/art variants use the first matching rule, at most six per supported card; they cannot change numbers or access facts.No remote code, HTML, WebViews, arbitrary components or executable links. Missing artwork must not hide meaning.
Brand, assets & motion Use your brand’s colours and approved artwork without sacrificing readability or reliability. Token / assetBehavioraccentRequired hex colour for accents and controls.secondaryOptional secondary hex colour.logoUrl / assetsValidated HTTPS images; up to eight declared assets referenced by card asset IDs.motionstandard or reduced; honour system reduced-motion preferences.Tokens belong to a story version. Test contrast on-device; automatic colour selection is not an accessibility guarantee. Connect artwork once Open App Settings → Brand & artwork → Connect artwork. Copy the agent instructions or run the supplied recappel-visuals command in your app Git project with Node 24+.Review local thumbnails, public-use rights and prepared colour/font/media settings. Nothing uploads before approval.Paste the temporary code directly in the local page and select Approve & connect. Saved assets appear in Builder automatically; no manual import.Create Story chooses suitable approved artwork and ready fonts. Preview actual playback, image failure and reduced motion.Up to sixteen PNG/JPEG/WebP images, one MiB and 4096 pixels each; up to eight used assets per story. SVG/code graphics need a separately approved supported export. Fonts stay bundled in the host; unsupported families use the documented fallback. Changed files need new review. Personal pictures stay local Optional storyMedia providers return permitted current-account HTTPS avatars or galleries at playback. Only a binding description is saved in Builder. Pictures/URLs never enter Recappel telemetry, story JSON or AI. Builder previews use synthetic placeholders.Check permission and session before and after reads, respect cancellation and omit missing images. Avatar limit one; gallery up to eight. Sharing is excluded unless explicitly permitted. Authorized image-host requests and native image-loader caches still follow your host privacy policy. Copy-paste artwork handoff Connect this app's visual identity and optional personal images to its existing Recappel integration. Preserve values, billing/access, customer identity, permissions, storage, native customizations and unrelated work. Read the installed @recappel/react-native README and public declarations; verify the installed version matches its lockfile. This task requires the recappel-visuals command and configure.storyMedia when personal pictures are used. If missing, request a targeted SDK update through this app's package manager; never invent APIs or edit node_modules.DISCOVER LOCALLY Inspect the app's theme tokens, font registration, asset references, resource folders and model/selector types. Search by references and file type, not a required directory name. Respect Git ignores, root boundaries, permissions and secret exclusions. Do not inspect customer databases, print private image URLs or upload source code. Find public app logos, meaningful illustrations, category/activity icons, earned-badge art and subtle repeatable background textures. Record unsupported SVG, code-drawn, animated, remote or oversized artwork separately; only rights-cleared local PNG/JPEG/WebP files up to one MiB and 4096 pixels are accepted. A local raster export can be prepared when the app's existing tools and rights allow it; do not silently rasterize private content or add dependencies. Do not add fake assets to fill slots.Use the actual theme source for primary, secondary, background and surface colours. Fonts must be licensed, bundled and registered in configure.fontAssets under their exact registered family names; a family string alone does not load a font. Verify the local registration and font licence before confirming hostRegistered/licenseConfirmed. Custom families are supported: use up to 80 letters, numbers, spaces, underscores, dots or hyphens, with no surrounding whitespace, URLs, paths or executable content. The reviewed per-app catalog accepts up to twelve font entries with unique lower_snake_case IDs; system is reserved for the native fallback. Automatic authoring selects app-approved font IDs and resolves their exact family names, never arbitrary model-supplied families. The SDK loads at most eight safe local configure.fontAssets entries, prioritizing the story's registered heading and body faces before remaining entries. Older SDKs may require those faces among the first eight entries; verify the installed version. Font files stay in the host. Builder can show a preview fallback for a custom family it has not bundled; verify actual host playback before claiming font fidelity.PERSONAL IMAGES STAY LOCAL Discover existing permission-safe avatar/gallery sources from code and types, not actual customers. Wire optional configure.storyMedia providers into the existing configuration. Each provider receives { limit, signal } and returns a bounded list of { uri, alt?, shareAllowed? }. Use HTTPS URLs without credentials, at most eight images (one for avatar), and only the currently active customer's permitted source. Capture/check the exact host session and permission before and after async reads; abort/retire on logout, account switch, opt-out and backgrounding. Respect signal. Do not widen permissions, scrape contacts, use names in alt text, fall back to another account or return stale images. Return undefined/[] on missing permission, failed lookup or missing media. Sharing is excluded by default; shareAllowed:true requires separate existing authority to export that image. Do not log or persist returned URLs. The SDK does not upload them; native image-loader caches and authorized image-host requests still follow the host's privacy policy. No new consent is implied by installing this feature.PREPARE REVIEWABLE SETTINGS Create/update recappel.visuals.json locally using only verified app-wide settings: optional primary/secondary/background/surface six-digit hex colours, fonts: [{id,family,licenseConfirmed:true,hostRegistered:true}], and media: [{key,label,description,kind,metricKeys,limit,hostRegistered:true,permissionChecked:true}]. Omit unknown colours; use empty fonts/media arrays when none are ready. Media kind is avatar or gallery, keys are stable lower_snake_case, avatar limit is one and gallery limit is one to eight. Descriptions explain the source's meaning without personal examples, URLs or source paths. Only reference existing connected numeric metric keys with a genuine relationship; an avatar can accompany recognition without a metric. At most eight bindings. Never mark hostRegistered/permissionChecked true before implementing and testing them. This is an agent-prepared local declaration, not a file the customer must import into Builder.REVIEW ONCE, THEN SYNC Run the exact command from Recappel → App Settings → Brand & artwork → Connect artwork in this app's Git project: npx --no-install recappel-visuals . --endpoint <the supplied visual-sync endpoint>. Node 24+ and Git are required; the command ships in the installed SDK but is not part of the mobile runtime. Open its loopback review URL privately. It scans bounded tracked/unignored files; review any limit warning rather than assuming full coverage. The developer selects up to sixteen suitable public images, confirms rights/public use and reviews the colours, fonts and media descriptions. Use logo only for app identity, badge only as truthful decoration, and illustration for other appropriate decor. Never upload customer pictures, copyrighted covers without rights, source/config files or secrets as artwork.The developer obtains a fifteen-minute app-scoped code from Connect artwork and enters it directly in the local review page, never in chat, source or environment files. Approve & connect uploads only reviewed normalized image bytes and approved metadata. Builder refreshes automatically; no manual JSON import/export. Changed files/settings need new review. Existing conflicting settings are not silently overwritten: review those in Builder. Retry an uncertain upload without changing the files; it is idempotent. Public SDK keys cannot upload artwork. Removing an image from the current catalog does not erase it from already saved story versions.VERIFY AND STOP Test provider cancellation, timeout, permission revoke, account switching, backgrounding, expired/broken image and default share exclusion; typecheck and run relevant host tests. Keep source recipes locally. Report discovered/approved/connected/unsupported separately, plus exact missing rights or source gaps. Confirm saved artwork/metadata in Builder independently of local success. Create Story sees only approved public artwork and allowed brand choices; it selects compatible per-card art, optional subtle texture, palette and ready fonts while preserving reviewed values. Personal media resolves later in the host, never in AI or shared story storage. Builder previews use synthetic placeholders. Verify genuine host playback separately; do not publish a story, alter a store build or claim production readiness from tool success. Stop after visual connection and local runtime wiring. Assets and motion Upload licensed artwork through Builder’s scoped asset flow or use approved HTTPS assets. Do not send customer photos as decorative art.Keep text and values usable when images fail; test offline and slow connections.The renderer owns transitions, easing, interaction and reduced-motion behavior.Richer animation code needs a tested SDK/host update. Remote story data cannot supply executable animation code. If the app has an explicit owner-approved monochrome dotted paint, the optional pattern setting is {kind:'dots',color,opacity,dotWidth,dotHeight,tileWidth,tileHeight,alignment:'center',offsetX,offsetY}. Every field is required: color is six-digit hex; opacity is 0–1; dotWidth/dotHeight are greater than zero and at most 64; tileWidth/tileHeight are 4–512 logical pixels; each dot dimension must fit its tile; offsets are -512–512 logical pixels. Tile sizes mean pitch, not the empty gap. Preserve the actual ellipse dimensions, colour, opacity, centered alignment and source offset, including any source scale converted once into logical units. Never recolour an asset, infer pattern paint from an arbitrary palette, invent geometry, or upload customer input. This fixed native primitive is public brand metadata, not executable SVG/code or a raster upload. Review all fields before approval. The compiler preserves the approved paint rather than asking AI to create or modify it. Cinematic playback requires pattern-paint-v1; verify the installed declarations and use the approved package-update flow when missing. No pattern means no new paint. Legacy patternUrl textures keep their existing subdued rendering; when both exist, the explicit paint takes precedence. Runtime contrast safety may reduce opacity for readability; verify real-device appearance separately. Explicit dotted paint Optional brand.pattern preserves an owner-approved native dot paint independently of image assets or model palette choices. Its complete descriptor is {kind:'dots', color, opacity, dotWidth, dotHeight, tileWidth, tileHeight, alignment:'center', offsetX, offsetY}. Geometry uses logical pixels; tile dimensions are pitch, not gap. Preserve source ellipse proportions and convert any source scale once. Review the exact settings through recappel.visuals.json; the compiler does not let AI invent or recolour this paint. See the complete bounds and handoff. This cinematic capability requires pattern-paint-v1; older clients cannot receive these versions. A missing descriptor retains existing behavior. Legacy patternUrl textures remain supported and subdued; explicit paint takes precedence if both are present. Runtime contrast safeguards may reduce opacity, so separately verify real-device playback and export appearance. No executable pattern code, private content or customer-derived colour is accepted. Optional cinematic brand.ambientColors contains exactly three #RRGGBB strings for the built-in ambient glow layers. Supply verified theme colours in the reviewed recappel.visuals.json settings, for example "ambientColors": ["#24010F", "#E8C199", "#29101A"]. These are illustrative colours, not a required palette. No alpha, CSS gradients or animation code is accepted. Omitting the field retains the existing accent/secondary/accent fallback. Colour → Custom Palette exposes the three gradient colour rows when the story uses an opening/recap glow or has explicit ambient colours. Older stories show their current preview fallback colours without changing the saved story. The rows use the same staged confirmation as other colours. The renderer preserves its built-in motion geometry; card changes and colour edits do not restart the continuous ambient phase within the open presenter. Foreground/paused state and system or story reduced-motion settings still gate animation. A new presenter can start a new phase. Check contrast and native playback separately.
When the story appears Offer the story when the customer opens your app during a meaningful window—not at an exact background alarm. Choose a known event App → Audience controls who can send SDK activity. Story → Audience & timing controls who can see this story. Choose Free, Trial and Paid in any combination, optional plan keys, minimum values, timing and frequency.EventRequirementNext chargeExplicit automatic-charge date from lifecycle-v2.Access endsKnown access end; do not imply a charge.Invoice dueActual due date, distinct from period/access end.Post-purchase checkpointReal purchase/access anchor, not a fake renewal.Monthly / App milestoneNon-billing timing; required when Free is included. Current timing behavior Selections in one list are alternatives; different restrictions must all match. Free OR Paid, on Pro OR Premium, with highlights ≥ 3 AND books ≥ 1. Missing values do not satisfy a minimum, even zero.Monthly/manual audience stories use UTC calendar-month buckets. App milestones require the host’s present({ manualMilestone: true }) call. The SDK requests stories while the app runs; publishing does not push open a closed app or guarantee delivery at midnight. Suppressed states Wrong environment or missing/stale access facts. Paid/trial also require active access without grace/retry/refund; explicit Free is a separate path.Insufficient data, missing required previous period, frequency cap or holdout.Unknown required date: omit the unsupported moment; never infer it from a plan name.Cancelled renewal is not necessarily lost access. Use the real entitlement state.Keep tested data thresholds, comparison-group percentage and observation windows. Do not bypass them to force a test story.
Invitation & placement Place the invitation in your app. Builder appearance controls must not rearrange your host screen. Mount one Gate or Host in a natural place such as the library or account screen. Avoid checkout interruptions, destructive actions and empty space when hidden. The host owns placement. Builder controls supported banner copy, colours, artwork and styling; the SDK renders the compatible definition. Gate props include renderBanner, onOpen, onDismiss and placement. Do not treat the placement label as a measured exposure. Published beta.15 behavior Shared-preview admission returns true before playback. Gate queues the pinned preview in its existing invitation banner, not an automatic modal. Hidden/background placements wait in memory; a banner tap rechecks current local facts and expiry. Host-only retains direct preview opening. Do not redeem again or call present() to open a pending Gate preview. Custom invitation Mount Host and show your native invitation only when canShow() returns true. On tap, call present(); it checks eligibility again before opening. Call canShow() for comparison customers too: it records eligible holdout exposure. Recheck relevant host-state changes and catch failures. Verify on the device Test appearance, dismissal, long copy, accessibility and smaller screens. Invited means prepared, not viewed. Do not turn that event into a banner-impression count. Gate responds to supported state changes and rechecks when tapped. Test access changes, account switching and foreground refresh. Offline limit Server pause/revocation reaches the device on refresh. It cannot instantly erase bytes from an offline phone. 5. Show and measure When the story appears Results & measurement Previous: When the story appears Next: Actions, rating & sharing
Actions, rating & sharing Connect each story action to a real destination, and measure taps separately from completed outcomes. Destinations and access management Use tested deep links. Pageling’s “Revisit your highlights” should open its existing library, not promise an unbuilt feature. Keep transparency before social asks. Use the real management destination appropriate to the access model and provider. Free/one-time access must not imply a recurring subscription. Test both platforms; a saved link is not proof that the destination works. Supply an action only when the current app session can open its real destination: for example, View plans for available purchase options or Manage subscription for an existing subscription. A compatible presenter closes the story modal before handing off once to that destination. Verify the destination actually opens; a tap alone does not prove a purchase or subscription change. Published beta.15 adds local managementAction.returnToStory:true with onOpen: () => Promise<void>. Settle only after the actual host destination closes, including cancellation—not after navigation or foregrounding. The SDK hides its modal first, then resumes the same card only for the current foreground session. Omit the option for existing dismiss-before-management behavior. Do not guess closure with a timer; account/access/permission retirement still wins. Verify installed declarations and actual platform behavior. Native hooks Native review remains an optional host hook. SDK releases declaring RecappelPosterShare include an autolinked Android/iOS PNG module: no separate sharing package or custom capture callback is required. Run the normal native rebuild when first adding the module; Metro reload and Expo Go cannot add it. An SDK-only update from beta.12 to beta.13 uses the same native code and does not require a new native rebuild. Existing capture/share hooks remain optional overrides. Verify the installed SDK, actual binary and updated bundle before testing. Android uses bounded SDK-native capture at the clean poster’s physical-pixel dimensions, including supported TextureView artwork on API 24+; it does not depend on the host’s optional Skia screenshot hook. iOS retains the existing Skia capture path. Unsupported or oversized captures report unavailable, not a partial or silently reduced image. Android holds one capture through cancellation and cleanup. Its ten-second deadline is checked around platform work; an already-running GPU read cannot be interrupted. No storage permission or arbitrary host-file access is added. A sharing failure can instead mean that an installed module’s activity/window or capture surface is temporarily unready. Check capability before rebuilding; never add a second sharing package. The published beta.16 correction distinguishes temporary readiness from a missing module and retains the story for a deliberate retry; it is not in published beta.15. Never automatically open another sheet after an uncertain result. What a tap proves Rating acted means tapped—not a displayed OS prompt or posted review. Suppress a card without a valid review destination. For the cinematic poster, known cancellation is not completion. A successful OS handoff is not a published post or delivery to another person; destinations depend on installed apps. A missing native module or older binary without the new Android capture reports unavailable; no text-only fallback or second sheet after an uncertain result. SDK temporary PNGs are bounded to 4096px per edge and 8MiB encoded. Android capture is limited to 4MP and 48MiB of accounted raster buffers; the iOS image limit remains 16MP. At most eight files are retained. Handed-off files become eligible for cleanup after one hour, on a later capture or OS cache cleanup—not an exact deletion timer. Share only explicitly approved figures, artwork and wording. Existing externalSharing:false recaps remain private; local-only leaders are excluded from exported recaps. The published beta.15 cinematic preview exception below remains subject to existing private-recap and session guards. The image is not uploaded to Recappel; the chosen recipient app receives it. Values review and Edit value provide an explicit Allow in share cards choice for supported numeric and public structured facts. Save the approval before creating a new story. Every fact actually referenced by its recap must be approved; one missing approval keeps the new recap private. This does not change existing private stories or permit local-only winning tags to be exported. SDK session retirement cancels queued native sharing before OS handoff. Once handoff starts, the recipient’s access cannot be recalled by closing the story. Custom sharing overrides retain responsibility for their own session safeguards. Published beta.15 preview exception Explicit cinematic preview image sharing respects externalSharing:false, native capture limits and current-session guards, with no acted/outcome/Results credit. Existing private recaps stay private. Preview media is synthetic; sender values and customer pictures are not transferred. Rating, ordinary external navigation and classic text sharing remain suppressed. Publication is not native/iOS acceptance. Cards & layouts 5. Show and measure Data handling & deletion Previous: Invitation & placement Next: Sandbox & test devices
Test & publish
Sandbox & test devices Test one story version without reaching Production customers. Payment-store testing is separate. Pair a test device Open this app’s Sandbox in Builder and create a pairing code. In the configured host app, pass the code through a deliberate tester action to await Recappel.pairSandbox(code). Codes are one-use and expire after 10 minutes. Pairing identifies the configured opaque Sandbox test customer, not a device or billing account. Paired does not necessarily mean eligible. Select an immutable saved version and review its test conditions in Builder. Run Test compiles that version if needed, enables its Sandbox delivery, then opens its receipt-based test status. Enablement does not force playback. Shared preview is different A shared preview link/QR opens its selected saved story with the recipient’s own local values. It does not bypass host sign-in, earn Sandbox test completion or count toward Results. Registering a native scheme is not enough: the host must also forward launch and incoming URLs to its ready preview controller. Choose a saved version, link duration and maximum link uses, then Create Link & QR. The default is Sandbox, one day and ten uses; Production must be selected explicitly. The success screen shows the selectable URL, QR code, expiry/use limit, and deliberate Copy Link / Share Link actions. Copy it before leaving: Builder does not show the bearer URL again in link history. History lets the owner inspect use counts and revoke an active link. The expandable Preview setup area is one-time host setup, not a per-version task. A valid saved prefix reuses the owner’s earlier confirmation. A new or changed prefix requires confirming that the scheme is registered and launch and incoming URL forwarding is implemented; a scheme alone is not enough. This is not a claim of successful redemption. Create the link, then verify the installed app’s cold and warm paths. Builder reuses the exact version’s compiled outcome, or one unambiguous outcome already configured across the app’s non-preview compiled versions. It asks for the real event key only when that meaning is missing or ambiguous; entering a key does not wire an event in the host. Never substitute preview_only or an invented event to complete setup. Register a real custom native scheme in the host, then the matching yourapp://recappel-preview prefix in Builder. Package/bundle IDs alone cannot open an app. Scheme changes may require a native rebuild; Metro cannot register a scheme. No customer installation or native acceptance is claimed here. Registering the scheme only launches the host. Its existing router must also forward both the cold-start URL (Linking.getInitialURL()) and warm incoming URL events (Linking.addEventListener(‘url’, …)) to the same preview controller. Do not add a second competing listener or let the ordinary router consume the preview URL without forwarding it. Verify both paths on an installed build. The call to Recappel.openSharedPreview(url) is only the final step of an already-ready session, not a complete startup listener or production bridge. The host-specific controller must satisfy the following lifecycle contract: Route only its exact registered scheme and preview route. Before calling the SDK, finish account restoration, obtain required permission/consent, configure the intended project/environment/customer, supply current local access and values, and mount the ready Gate/Host presenter in the foreground. Use one consistent authoritative identity source for startup and later updates. Model unresolved startup separately from confirmed sign-out; a synchronous snapshot and an initial UI prop must not become competing authorities. Never let a stale placeholder null cancel a valid pending startup request or replace an already-resolved identity. Do not redeem while account restoration is unresolved. An authoritative signed-out result still retires the request; ask the user to reopen deliberately after sign-in. If a cold or warm URL arrives before readiness, hold only a bounded, short-lived pending request in memory, with a fixed queue size/expiry policy. Resume it when the same controller session becomes ready, not on a timer that blindly calls the SDK. Do not persist the URL in navigation state or storage. Coalesce duplicate initial-URL and incoming-event deliveries for the same attempt. Keep bounded in-memory deduplication and mark the attempt claimed before the SDK call; do not call it from every render/readiness notification. Retire pending work and ignore late initial-URL/readiness/redemption results on sign-out, account/project/environment change, consent withdrawal or controller disposal. Never replay a prior startup URL into a replacement account or remounted controller. If ownership cannot be proved, discard it and ask the user to open the link again deliberately. Treat ordinary backgrounding or temporary presenter unreadiness as a pause, not an account change. Do not drain while unavailable or reconfigure with enabled: false merely to model that pause; revoking SDK authority can retire an existing story. Recheck the same authority before resuming. Once openSharedPreview has been invoked, do not automatically requeue/retry on false, timeout, network uncertainty or a late lifecycle change. A server redemption may already have consumed a use. Show a non-sensitive result; another attempt must be a deliberate user action and may consume another use. Replace yourapp with the registered scheme. Links require exactly a 64-lowercase-hex token and UUID appId, no path/fragment/credentials/duplicate/extra fields or dangerous scheme, maximum 512 characters. Never log/persist complete links or tokens, put them in analytics/crash breadcrumbs, or include them in diagnostic errors. openSharedPreview validates the pinned story/app and uses only this recipient’s local facts. Its request contains only project key, environment and token—not customer ID, values, lifecycle or device ID. It returns false if rejected/unsafe. Default allowance: ten redemptions; duration: one day/week/month or no expiry (Forever, still bounded by the use limit and revocation). Redemption consumes allowance even if local gates later prevent display. Published beta.15 behavior: true means admitted to the presenter, not opened or viewed. Gate stores the pinned preview invitation in memory and shows it in its normal banner placement; opening requires a deliberate banner tap and fresh local access/value/expiry checks. When renderBanner is false or the app is backgrounded, it waits until that placement is available. Host-only integration retains direct preview opening because it has no invitation UI. Do not call present() or redeem again to open an already-admitted Gate preview. Test admission, banner visibility, tapping and playback separately. Published beta.16 preview request feedback: Gate shows Loading, Ready to open, or an error after a valid SDK attempt begins. Loading uses a gentle indeterminate pulse, not a percentage or download estimate; Reduce Motion keeps it static, and background/hidden placement stops the motion. Ready means a validated presentation resolved against recipient facts is available, not that every native font or image is loaded. An explicit Open tap rechecks current facts and expiry; no automatic Gate playback or Results credit is added. Host-only retains its existing direct-opening contract. The host still owns pre-attempt startup, account restoration, consent and readiness feedback. Only retryable errors offer a deliberate retry. An uncertain attempt may have consumed an opening; another attempt may consume another. Never automatically retry or requeue redemption. A retry input is retained for at most five minutes per attempt in private memory, never observable state, UI props, storage or diagnostics; replacement, dismissal, retirement, disposal, success and terminal failure clear it. Session changes and late results cannot revive an old request. Missing module errors require checking the installed SDK/native binary and the normal first-install or native-change rebuild—not a separate sharing package. An existing activity or capture surface can instead be temporarily unready; inspect capability before rebuilding, keep the story for a deliberate retry and never open a second sheet after an uncertain result. Verify actual media/font readiness, native feedback, PNG output and cold/warm links separately; these source changes are not publication or device acceptance. Shared previews bypass schedule/date, frequency cap and holdout only. Inactive access, billing trouble/overdue/refund, stale facts, environment/profile and zero/minimum-data gates remain. No exposure/outcome/show/assignment, runtime-cache change or paid-seat activation is emitted. Rating, ordinary external navigation and classic text sharing remain suppressed. In published beta.15, explicit cinematic image sharing respects existing externalSharing:false, current-session and native capture guards, with no acted credit. An explicitly configured local managementAction.returnToStory:true may perform its guarded host round trip; its promise must settle only when the destination closes. These exceptions are not remote permissions or automatic actions. Host onOpen is not a preview-open receipt; local banner dismissal can notify onDismiss. Neither is Results credit. Server revocation cannot instantly recall bytes on an offline device or an already-started OS/host action. Tester identity Developers sign in to Builder; testers sign in to the host app if required. Builder email OTP does not match someone to the host database. Pair the device/session with its opaque host customer ID. Keep test controls away from ordinary customers. Configure payment-store testers separately; test purchases cannot unlock paid Production coverage. Test the selected version Open the installed Sandbox host with the paired account. Confirm the received immutable version, refresh access/values and inspect eligibility under the actual data/access/timing rules. Tap the invitation and finish the story. Verify visible invitation, opening, completion and dismissal separately. Builder advances from available to opened to completed only from the matching server receipts. Test destinations and the independent outcome. Return to Builder or use Refresh Status to read the exact-version opened/completed receipts. Loading, failed reads and enablement alone do not establish completion. Record build, version, scenario and whether each check was manual, local or server-observed. Example-number previews are not device tests. Include little/zero data, no prior period, billing trouble, wrong environment, expired pairing and offline cases. Waiting alone does not guarantee background delivery. With version-scoped Sandbox delivery, a newly selected saved version needs its own invited, opened and completed evidence. An older version’s completed receipt is not proof that the new version arrived. Match the immutable version ID as well as the app and environment; a per-story Version 1 label is not an identifier. Production frequency limits remain unchanged. Already-enabled or completed versions offer View Test Status without enabling again. Re-enabling does not reset the show allowance. Save another immutable version for a new test, or use shared preview for read-only replay. Connect in five steps Versions & Production Troubleshooting Previous: Actions, rating & sharing Next: Versions & Production
Versions & Production Publish a tested version explicitly. Saving, testing or paying never publishes it automatically. Prepare the customer build Keep the working Sandbox configuration for testing. In your customer-release profile, use this app’s Production key and service URL from Builder, with environment: production.For paid/trial access, supply genuine PRODUCTION lifecycle facts from the matching source; never relabel a test purchase. Free customers use explicit audience facts.Use the same initializer and mounted invitation in the release build. Verify session changes, permission, persistence and native playback before shipping it.Ship the host-app build through your normal release process. It can connect before any story is published.There is no publish/enable code switch. Once that configuration is installed, publication is controlled in Builder. Changing a key or adding SDK/native capabilities requires a host-app update; compatible story edits do not.Confirmed value meanings/sources and valid card/brand schema.Truthful transparency, destinations, timing, audience, data and group rules.This exact version tested; unresolved blockers visible.Verified Production capacity assigned. Test purchases do not qualify. Publish the tested version In Builder, open Story → Review publication. Resolve any listed blockers.If this app has no coverage, choose an available plan and wait for verified capacity.Publish the tested version unchanged. Saving a draft or paying does not publish it.Check the live version and publication time, then monitor received activity separately.One publication personalises the eligible audience with each customer’s values. It is not a per-customer charge or guaranteed impression; access, timing, data and holdout still apply. Edit, pause or roll back ActionEffectEditCreate a draft; leave live unchanged.ReplacePublish a tested immutable version; preserve history.PauseStop new invitations as devices receive the updated state.RollbackSelect a compatible previous version and pass publication checks again.Delivery is not instantBootstrap refresh is due after 15 minutes, with a maximum playable cache age of one hour. A network failure cannot extend it indefinitely. Live means published, not received on every device; an offline phone cannot be remotely cleared instantly.
Understand & manage
Results & measurement See story activity separately from evidence that offering it changed the chosen outcome. Story activity MeasureDefinitionInvitations preparedEligible invitations prepared; not proof they were visible on-screen.OpenedUnique customers who opened within the selected scope.CompletedUnique completions; completion rate = completed ÷ opened.Action tapsNext-action, review and share taps—not downstream success.Filter by date, environment and version. Show counts with rates and matching deduplication scopes. No activity means No activity yet—not invented uplift. Delivery diagnostics and active-audience usage are separate from story performance. Outcome comparison Compare all appropriately assigned story and comparison customers, not just openers. Assignment is deterministic and sticky per experiment.Outcome count / eligible assigned count for each group.Percentage-point difference, uncertainty and observation window.Collecting, Inconclusive or Data incomplete when evidence is insufficient.Define eligibility, assignment, attribution, sample-size approach and outcome source before launch; a random split alone is not enough. Measurement limits Client-recorded outcomes are unverified. The separate authenticated server-outcome path needs an actual trusted host-backend event and correct experiment/cycle attribution. Server-authenticated does not mean independently payment-verified. Renewals may occur without another app session.Current events cannot prove per-card dwell/drop-off, rescued revenue, posted reviews or completed shares. Do not relabel a prepared invitation as a view. Subscriber trends alone do not prove story impact.
Settings, plan & billing One monthly Recappel audience allowance is pooled across up to ten live apps; each app keeps separate story controls. Drafts and Sandbox remain free. Test purchases cannot grant paid Production coverage. Approved monthly USD list prices are $29 for 10,000 active customers, $99 for 100,000, $299 for 250,000 and $499 for 500,000. These reference prices are not native store offers. One account subscription pools the audience allowance across up to ten covered live apps. Ten apps is an included limit, not a per-app price multiplier. A paused covered app still reserves its place; iOS and Android editions of one product count as one app. New shopping is monthly only. Existing app-slot and annual subscriptions remain manageable; they are not automatically migrated, cancelled or replaced. No larger tier, AI quota or unlimited plan is approved. Active customers this month counts each accepted Production app + opaque customer pair once per UTC calendar month after a value sync or story-availability activity. Repeat activity and additional devices do not count again; the same customer in two apps counts twice. Sandbox, shared previews and invalid requests do not consume paid audience. The month resets at 00:00 UTC on its first day, separately from store renewal. Changing tiers, pausing, changing filters or deleting an app never resets or refunds accumulated usage. Where to manage it Location Controls App → Settings Connection, public keys/environment, profile, privacy and this app’s coverage. Account → Plan & billing Audience allowance and usage, included live-app capacity, covered apps, renewal/status, change plan, manage and restore. Story → Review publication Version checks and Publish. Payment appears only without verified coverage. What each action changes Action Effect Pause story Stops invitations after refresh. Place and bill unchanged. Remove coverage Used seats retain a 30-day cross-app reassignment cooldown. The same app may resume; deactivation/deletion cannot rotate one used seat between apps. Reduce plan A same-store audience reduction takes effect at renewal. Upgrades require verified store confirmation. Use the confirmed effective date, never guessed proration. Cancel renewal Stops future renewal; verified access lasts to its confirmed end. A configured audience allowance uses a finite 5% buffer, then stops new admissions in arrival order. Already-admitted customers keep ordinary safety checks. Tier changes preserve accumulated usage, and an expired or exhausted allowance never becomes unlimited access. No automatic overage charge occurs. Legacy-to-audience and cross-store changes remain management-only until a reviewed store transition is available; do not create a second subscription to migrate. Recappel billing is separate Builder uses its own store catalog and RevenueCat. Adding an app creates no new store listing or subscription product. That app can use any provider; its customers do not subscribe to Recappel. This website has no checkout, store links or restore service. Audience checkout remains unavailable until the exact localized native offer and enabled server catalog are verified for the authenticated owner, app/store identity, monthly cadence, ten-app capacity and audience allowance. Approved list prices do not grant access. Legal URLs, payment lifecycle and sustained service capacity require separate release validation. Versions & Production Data handling & deletion 3. Pass billing and access Previous: Results & measurement Next: Data handling & deletion
Data handling & deletion Send only what the story and measurement need. Keep personal content and billing secrets out. Request Data App enrollment Public key/environment before personal requests; coarse audience kind and optional plan key accompany permitted activity. Bootstrap Public key, environment, opaque customer ID, SDK/capabilities, schema version and cache tag. Aggregate sync App/customer/device scope, cycle, metric keys/methods, numbers or safe labels and revisions. Story interactions / diagnostics Bounded experiment/version/group/cycle, supported interaction or diagnostic code and time; diagnostics are optional. Outcome Outcome key, cycle, experiment/group and source; client reports are unverified. End-user deletion App/environment and opaque customer ID. Detailed billing facts remain host-side; cycle identifiers and approved aggregates may still leave the device. Story/outcome events have timestamps and artwork may load from HTTPS hosts. Do not describe the SDK as entirely on-device. Structured values: public definitions, availability status and revisions can reach the service within the existing app/environment/customer/device scope. Structured numbers stay on the device in account-scoped storage; ordinary scalar aggregate telemetry is unchanged. No customer-written categories, numbers, examples, source paths or history rows belong in definitions. Account, permission, access and late-callback guards still apply; this feature is not permission for new collection. Value definitions: public app key/environment, stable metric keys, display names, descriptions, numeric counting rules and supported semantics. These contain no customer identity, source code, individual values or owner approvals. Review reported meanings in Builder before using them in a story. Excluded data Names, email, phone, advertising IDs and provider customer IDs. Passages, notes, messages, photos and arbitrary labels. Payment credentials, receipts, transaction objects and provider secrets. Full source repositories sent to the authoring service. Valid label syntax is not a privacy filter: ordinary uploaded labels require approved enums. Permission-reviewed local-only structured leaders use their dedicated API, not setLabel or public definitions. Authoring uses reviewed app context/value definitions; future cohort summaries need privacy thresholds and review. The optional local artwork tool uploads only images and bounded theme/font/binding metadata that the developer approves for public use. Source files, paths, ignored files and unapproved candidates stay local. Approved public artwork can be sent to the model during story creation; Values Rewrite does not receive images. Optional personal-image callbacks load from the host-approved HTTPS service during playback. Those URLs/photos are not sent to Recappel telemetry, saved stories or AI. Preview uses synthetic media. Account/permission checks remain the host’s responsibility; late callbacks are discarded, but already-started network requests and native image caches cannot always be recalled. Sharing requires explicit shareAllowed permission. Optional local-only leader snapshots keep the actual winning label and count in the host’s account-scoped device storage. Recappel receives static definitions and availability, not those names or counts. A leader may appear as a captioned supporting stack tile on compatible versions, never as a lead or exported recap fact. Preview uses Example; this is not permission for new collection. Optional valueRelationships reports source-verified static equal/subset metadata with exact definition revisions, not customer readings, examples or source code. It does not change ordinary aggregate telemetry or grant sharing approval. Approved poster sharing writes a bounded temporary PNG and hands it to the OS sharing interface only after a deliberate tap. The SDK does not upload that image to Recappel or post automatically. Existing externalSharing:false recaps remain private; local-only leaders are excluded. Queued native sharing is cancelled on SDK session retirement before handoff, but a recipient’s access cannot be recalled after handoff begins. Handed-off files become eligible for cleanup after one hour on a later capture or OS cache cleanup, not an exact deletion timer. Published beta.16 getLocalBreakdown is local-only. Published beta.18 also resolves the current recipient’s exact cached partition during genuine shared-link playback, pinned to the current owner, session, observation and consent. Labels never come from the link, server or another owner, and never enter persistence, telemetry, AI or poster/share-safe exports. Synthetic mock previews still exclude the getter. Canonical aggregates, saved stories, leaders and recap facts are unchanged. managementAction.appearance also remains local configuration; bundled host CTA artwork is not uploaded to the public artwork catalog. Neither feature grants collection or export permission. Verify the published and installed version before relying on beta.18 behavior. Delete a customer Call from an authenticated, confirmed deletion flow—not sign-out. It targets the current opaque customer, wipes local state and leaves a tombstone that stops tracking. Server deletion and policies A resolved deleteEndUser() is not proof of server erasure, cross-device cleanup or legal compliance. The request is best-effort. Production needs authorized confirmation/retry, retention rules and handling of queued traffic from other devices. Before collecting data, publish approved privacy/terms, retention, processor/security and support details. Customer identity & sessions 4. Send useful values React Native API reference Previous: Settings, plan & billing Next: React Native API reference
Reference
React Native API reference Use the installed declarations for exact fields and supported types. configure(config): void Field Contract projectKey Public app/environment key copied from Builder, not a private credential. environment sandbox or production. userId Stable, host-owned opaque string. baseUrl Service URL provided by Builder; required for fetch. transport fetch by default; mock has no network evidence. enabled Host enrollment/permission decision; false stops ordinary activity, not explicit privacy deletion. audience Explicit free/trial/paid, genuine observedAt and optional approved profileKey. locale / lifecycleMaxAgeHours Supported locale and freshness policy; default access freshness 72 hours. storageAdapter Optional getItem / setItem / removeItem adapter. review / shareCapture / onShareImage Native review is an optional host hook. In SDK releases declaring RecappelPosterShare, capture/share callbacks are optional overrides of the bundled PNG path; see Actions for first-install native build steps. An SDK-only update from beta.12 to beta.13 uses the same native code and does not require a new native rebuild. Android uses bounded SDK-native poster capture, separate from optional host screenshot hooks; supported TextureView artwork requires API 24+. iOS retains the Skia capture path. Older binaries report the new Android capture unavailable until rebuilt. No additional public capture API is required. Returns void, not a ready promise. Later calls queue behind initialization; calls before configure can fail. Reconfigure deliberately when session/access/consent changes, not on every render. refreshAccess(input): Promise<boolean> accepts exported AccessRefreshInput : exact projectKey , environment , userId , audience: AudienceContext , optional lifecycle: LifecycleInput (required for Paid/Trial, absent for Free), and required maxAgeMs . After a successful authoritative same-account access renewal, use refreshAccess only when the installed public API declarations support it. Do not call configure on every successful access refresh or retire the permitted session merely because a routine authority lookup starts. Keep the original freshness deadline/expiry and its hard-expiry handler active during both the authority lookup and SDK await; recheck expiry, identity, permission and cancellation afterward. While checking, cached facts must not receive a new timestamp. Required maxAgeMs is a positive safe integer bounded by configured SDK/story freshness; use the authority’s documented shorter freshness policy, not an invented timer. Paid/Trial needs equivalent active, unexpired, nonsuppressed lifecycle facts with strictly newer audience and lifecycle observations; Free has no lifecycle. refreshAccess is only for strictly newer, still-fresh observations of equivalent audience, profile and lifecycle facts in the exact already enabled/enrolled/unblocked project/environment/customer context. Only observedAt may change; both old and new observations must remain fresh. A true result preserves the session, caps and current player; it does not automatically bootstrap or prove a server receipt. A false result requires the normal configure recovery path after resolving current permitted facts; never replay captured old-account configuration. Matched invalid, changed or expired facts disable admission/presentation; a mismatched account cannot mutate the new account. Real identity, permission, plan or lifecycle changes still use deliberate reconfiguration. Older packages need an approved upgrade for this API; never cast, call private methods or assume newer docs upgraded the installed binary. Both audience and applicable lifecycle observations must be nonfuture UTC and strictly newer. Age equal to maxAgeMs is expired; its maximum is the smaller of configured lifecycleMaxAgeHours (default72) and the active story freshness window, in milliseconds. Backward clock movement during the check is rejected. The SDK never rewrites timestamps or renews remote admission TTL; refresh() separately revalidates delivery. valueDefinitions?: readonly ValueDefinition[] adds names, short meanings and numeric counting rules to the existing configuration. The SDK sends them automatically, with bounded retries. Keep one stable array of at most 32 unique keys; no second initializer or file import. Required fields: key, name, description and aggregation (sum/max/latest). Optional revision starts at 1; increase it when a meaning changes. Optional scope, behavior, unit, meaning and direction use the exported enums; leave unknown facts unknown. Never include customer data, examples, source paths or approval fields. structuredValueDefinitions?: readonly StructuredValueDefinition[] adds up to 8 separate groups alongside 32 numeric definitions. Each reviewed group counts as one of the 3–5 logical values selected for an initial AI-generated draft. With compatible Builder/service support, manually edited stories may use up to 12 reviewed logical values, within existing 9-card, per-card metric, runtime and privacy checks. Check installed declarations and preserve existing scalar keys; older builds retain their own limits. Public kind is ratio, distribution, series or local-only leader; group/category keys are lowercase snake_case up to 32 characters. Required common semantics are scope, unit, meaning and direction. Revision defaults to one; maxAgeHours is 1–168, default 24. valueRelationships?: ValueRelationshipManifest is an optional {revision,relations} manifest with at most 32 source-verified equal/subset assertions. Each from/to reference names kind, key and exact definition revision; distribution_category also names categoryKey. Numeric kinds are scalar, ratio_numerator, ratio_denominator, distribution_total, distribution_category, series_total and leader_value. Both sides require the same actual scope and explicit unit, unitKey, populationKey and windowKey. Subset means from is no greater than to. Never infer relationships from names or examples. Increase revision on changes; a newer empty list revokes, omission preserves older-client compatibility. Check installed SDK and compatible service/Builder support. fontAssets?: FontAssets uses the exported bundled-font registry type. The updated type accepts number | readonly number[] per unchanged registered family: an existing module ID or one to four intrinsic faces, with eight local faces loaded in total. No font download or host registration is performed for you. The array form requires a newer SDK than public beta.11. Verify the installed FontAssets declaration before using arrays; use single IDs with beta.11. storyMedia?: Readonly<Record<string, StoryMediaProvider>> supplies optional personal-image callbacks. Each callback receives { limit, signal } and returns images with HTTPS uri, optional alt and optional shareAllowed (off by default). Check current account and permission before and after asynchronous reads. Binding keys are app-level metadata; customer images and URLs are not. See visual stories for the local review tool and complete universal handoff. → Published beta.15 addition: managementAction?: { label?: string } & ({ returnToStory: true; onOpen: () => Promise<void> } | { returnToStory?: false; onOpen: () => void | Promise<void> }) . It is local host configuration, never remote story code. With true, settle only when the real destination closes, including cancellation—not after navigation dispatch or foregrounding. The retained card resumes only for the same current foreground session. Omission/false preserves existing dismissal. Check installed declarations and native behavior. Published beta.16 additions: getLocalBreakdown?: (request: LocalBreakdownRequest) => LocalBreakdownSnapshot | undefined . Request is {key, revision, observedAt}; revision is the semantic definition revision, observedAt the exact committed canonical observation. Return a synchronous already-cached {observedAt, total, items:[{label:string|null,value:number}]} for the same owner/generation and consent, population, unit and category precedence. Equal totals alone do not prove provenance: never redate a newer read to an older deduplicated timestamp. Build and retire the cache in the existing host pipeline; return undefined for unavailable exact cache/permission, never query, fetch or reconfigure in the getter. At most 32 complete rows including zeros, exact total/sum match, finite nonnegative values at most 1e12, safe integers for count units. Plain data only; safe Unicode labels at most 80 code points / 320 bytes, unique after trimming/NFC. null means genuinely missing: Not assigned, spoken No category assigned; real Other and Unclassified stay distinct. More than eight positive rows use the top seven plus N remaining categories and the exact remainder sum; generated-label collisions reject the supplement. One positive structured category is valid; legacy unstructured rules are unchanged. Invalid/missing local data keeps the canonical chart. In published beta.18, ordinary presentations and genuine shared-link playback may resolve only the current recipient’s exact local snapshot, pinned to the current owner, session and observation. Synthetic mock previews exclude the getter. No labels come from a link, server or another owner. No persistence, export, AI or telemetry; canonical aggregates, conditions, leaders, saved stories and recap facts are unchanged. Verify the published and installed SDK before relying on beta.18 behavior. Published beta.16: managementAction.appearance?: ManagementActionAppearance keeps the genuine label and host callback. Optional artwork is a numeric registered bundled raster module, for example static require of the host’s approved offline PNG export; no arbitrary style, remote URI, SVG string, component or render callback. Opaque #RRGGBB backgroundColor/textColor/borderColor; borderWidth 0–8, borderRadius 0–999, minHeight 48–160, paddingHorizontal/paddingVertical 0–48, fontSize 12–32, fontWeight 100–900 in steps of 100, hideIcon?: boolean. No font loading or purchase execution. Pass only the real purchase-entry artwork and matching accessible label, never an invented offer/price. One SDK-owned Pressable uses contain/intrinsic aspect, at most 480 points wide / 160 high and minimum 48-point press target; registered image dimensions must be positive and at most 8192 with aspect 1–8. Missing/invalid assets, image errors and large text use the scalable label fallback. Valid artwork has no duplicate text/icons. Published beta.18 also omits the unpaid Free row and no-payment copy: use one genuine custom host purchase control, or one functional View plans fallback only when the host supplies its real callback. Never invent a plan, trial, price or active subscription. Paid/trial/access and billing-trouble clarity remains intact. With returnToStory:true, onOpen settles only on actual destination closure, including cancel—not navigation dispatch or a timer. The paused scene clock, measured Clarity layout and scroll survive Modal child disposal; current owner/access retirement wins. Test artwork, contrast, large text and same-card return on device. These local corrections add no native dependencies; bundled sharing still needs the normal first-install/native-change rebuild, not a separate package. setLifecycle(dto): void Input Meaning LifecycleDTO Backward-compatible recurring facts: entitlementActive, purchaseDate, expirationDate, willRenew, periodType, store, environment and observedAt. LifecycleDTOv2 lifecycleVersion: 2; explicit accessModel, paymentCadence, collectionTiming, renewalMode, phase, channel, profileKey, lifecycle/period identifiers and known dates/amounts. Known dates Period end, access end, next charge and payment due have different meanings; leave unknown facts null as the contract permits. Environment / freshness SANDBOX / PRODUCTION and genuine source-observation time. Trouble flags Pass known grace, retry, overdue and refund states; never omit them to force eligibility. Free access Supply explicit free AudienceContext; never fabricate a subscription. The exported LifecycleInput union accepts both DTOs. Detailed facts remain local; inspect installed types and step 3 before mapping. Metric methods: void Call Effect increment(key, by = 1) Add to cycle sum. add(key, amount) Add amount to cycle sum. set(key, value) Replace snapshot; decreases allowed. setMax(key, value) Retain cycle maximum. setLabel(key, label) Replace approved enum label. Keep one method and meaning per key. Updates queue after configuration and sync as snapshots; repeated business events are not automatically deduplicated. setStructuredValue(key, snapshot): void takes the exported StructuredValueSnapshot. Every reading has status and genuine UTC observedAt. Ratio reads numerator/denominator and precision 0–3; zero denominator is unavailable. Distribution declares 2–24 fixed public exclusive/complete categories and reads all counts plus their matching total; top three plus Other preserves coverage and ties have no unique leader. Series declares rolling scope and 2–4 completed contiguous day/week/month windows, with timeZone, startDay, endDayExclusive and value. Weeks start Monday; the final window ends at the current period boundary. Missing windows make the group unavailable. Explicit unavailable clears prior local group values; maxAgeHours expiry prevents stale display. A supported leader declares static valueLabel/labelName, ties:’unavailable’ and labelPrivacy:’local_only’; its snapshot carries one unique winning plain-text label plus a positive integer count. Ties, zero or invalid labels are unavailable. Labels are limited to 80 code points and 320 UTF-8 bytes; actual names/counts stay local and cannot bind into exported recaps. Never invent labels/history or refresh timestamps on stale data. Presentation and diagnostics Call Returns / behavior canShow() Promise<boolean>; checks treatment eligibility and records holdout_eligible when appropriate. checkEligibility() Promise<EligibilityResult>; diagnostic, without holdout exposure. present(options?) Rechecks eligibility; requires Gate/Host. manualMilestone must be an explicit host action. flush() Attempts delivery; not proof of server receipt. refresh() Requests refreshed enrollment/bootstrap state; check its result and diagnostics. pairSandbox(code) Redeems Builder’s short-lived one-use Sandbox pairing code. openSharedPreview(url) Admits a valid link only after the registered host router forwards it into the intended ready session. Handle cold and warm URLs once, after account restoration, permission, current access/values and a ready Gate/Host. Keep pending work/deduplication bounded and in memory; retire it on authority change. Never log/store bearer links or automatically retry after a possibly consumed redemption. A true result means admitted, not visibly opened. Shared preview never earns Sandbox test completion or Results credit; published beta.15 action exceptions remain policy guarded. getDiagnostics() Recent bounded diagnostics; redact before sharing. deleteEndUser() Best-effort server request plus local wipe/tombstone. Catch async failures. Missing stories or transport errors must not block the host app. Published beta.15 shared-preview contract: openSharedPreview(url) returns admission, not proof of visible playback. Gate queues the pinned invitation in its existing banner; a deliberate tap rechecks local facts and expiry. Hidden/background banners wait in memory. Host-only keeps direct opening. Do not call present() or redeem again for a pending Gate invitation. Preview actions do not earn Results credit. Published beta.16 preview request feedback: Gate shows Loading, Ready to open, or an error after a valid SDK attempt begins. Loading uses a gentle indeterminate pulse, not a percentage or download estimate; Reduce Motion keeps it static, and background/hidden placement stops motion. Ready means a validated presentation resolved against recipient facts is available, not that every native font or image is loaded. An explicit Open tap rechecks current facts and expiry; no automatic Gate playback or Results credit is added. Host-only retains its existing direct-opening contract. The host still owns pre-attempt startup, account restoration, consent and readiness feedback. Only retryable errors offer a deliberate retry. An uncertain attempt may have consumed an opening; another attempt may consume another. Never automatically retry or requeue redemption. A retry input is retained for at most five minutes per attempt in private memory, never observable state, UI props, storage or diagnostics; replacement, dismissal, retirement, disposal, success and terminal failure clear it. Session changes and late results cannot revive an old request. Verify installed declarations, actual media/font readiness, native feedback and cold/warm links separately; these source changes are not publication or device acceptance. recordOutcome(outcomeKey): void Queues an unverified client event for the current cycle and available experiment assignment. Idempotency is per customer/outcome/cycle—not a count of every purchase. Report confirmed success once, not a tap followed by confirmation. This client call cannot select a historical cycle or upload transaction payloads. Use the separately authenticated server-outcome integration for trusted backend events and explicit attribution; never put its secret in a mobile app. Components and exports Export Purpose ProductWorthItGate Built-in invitation and presenter; may be locally aliased RecappelGate. ProductWorthItHost Presenter only; may be locally aliased RecappelHost. StoryPlayer / public preview API Actual shared renderer; previews are not a delivery or Results shortcut. CARD_LAYOUTS Supported layouts. demoStory Local test fixture, not a Production version. Types ProductWorthItConfig, LifecycleInput, AudienceContext, story/brand, storage and public preview/wire types. Installed declarations are authoritative. Mount one presenter under SafeAreaProvider. Do not import private lib paths. Gate props include placement (not a measured exposure), onOpen, onDismiss and renderBanner. In SDKs supporting late-value invitation recovery, valid value updates request a bounded recheck without replacing an active player. This does not bypass eligibility, force a network refresh on every render or poll for absent data. BrandTokens.ambientColors?: [string, string, string] is cinematic-only and accepts exactly three #RRGGBB strings. It supplies colours for the built-in glow layers, not CSS gradients, geometry or executable motion. Omission retains the accent/secondary/accent fallback. Approved app-wide values can be reviewed through recappel.visuals.json ; this is not a customer reporting API. Card changes and colour edits preserve the continuous ambient phase within an open presenter. Animation runs only in the foreground when unpaused and respects system/story reduced-motion settings. A new presenter can start a new phase. Use the existing editor controls for staged font, artwork, opening-copy and colour edits; Confirm applies and Close cancels. Explicit pattern paint BrandTokens.pattern?: DotPatternPaint is an optional cinematic-only app-wide paint, not a customer reporting method. All fields are required: {kind:'dots', color:'#RRGGBB', opacity, dotWidth, dotHeight, tileWidth, tileHeight, alignment:'center', offsetX, offsetY} . Opacity is 0–1, dot dimensions >0–64, tile dimensions 4–512, offsets -512–512; each dot dimension fits its tile. All numbers must be finite and geometry is logical pixels. Unknown fields are rejected. It requires pattern-paint-v1 , preserves approved source geometry and takes precedence over legacy patternUrl . See visual setup . Service transport Host developers use public SDK methods, not hand-built requests to managed-service endpoints. POST route Purpose /sdk/v1/audience:config Get app enrollment settings before personal requests. /sdk/v1/bootstrap Get a compatible validated story or null. /sdk/v1/aggregates:sync Send a cycle snapshot. /sdk/v1/exposures Send supported interactions / holdout eligibility. /sdk/v1/outcomes Send unverified client outcomes. /sdk/v1/end-user:delete Request deletion. Legacy x-pwi-key, x-pwi-env and x-pwi-sdk headers remain; additive capabilities and coarse audience headers protect newer features. Use SDK methods for pairing/preview/diagnostics. No client key grants Builder publication or server-outcome privileges. /sdk/v1/definitions:sync carries only app-level numeric definitions. The SDK handles this route; do not build a second transport. Definitions never grant story approval or fabricate a numeric receipt. /sdk/v1/structured:sync sends public group definitions and availability/revision receipts in the existing app/environment/customer/device scope, never the structured numbers, source windows or local-only winning labels. Use the public SDK, not a second transport. Builder unavailable status proves wiring without claiming usable data; metadata receipt, available data and rendered playback remain separate. /sdk/v1/relationships:sync reports the optional static manifest and normalized scalar/structured definitions automatically through the existing authenticated SDK connection. Its body contains no customer identifiers, readings, examples or source paths. No manual import or second host transport is needed. Builder validates exact definition revisions and uses source-verified relationships for coherent synthetic examples; stale references and conflicting fixed examples remain review gaps. 2. Connect your app 4. Send useful values Troubleshooting Previous: Data handling & deletion Next: Troubleshooting
Troubleshooting Check the exact failure reason. Keep safety rules intact while diagnosing missing stories. Run after configure. Catch setup errors so the app stays usable; review and redact diagnostics before sharing. Eligibility reasons Reason / condition Check no_story No compatible selected/published/cached story. Contact can succeed without one. no_lifecycle Paid/trial need valid lifecycle; Free needs explicit fresh free audience and a compatible story. environment_mismatch sandbox/production must match SANDBOX/PRODUCTION lifecycle. lifecycle_stale Refresh the source, not just its timestamp. entitlement_inactive Do not falsify paid access. Explicit Free is a separate supported flow. suppressed_billing_state Grace/retry/refund/overdue; resolve at the source. outside_window Verify real anchors, offsets and clock. insufficient_data Check keys, cycle and thresholds. Snapshots do not supply additive-event totals. already_shown / holdout Expected frequency/experiment behavior; do not bypass in Production. Enrollment / audience / allowance Check enabled, explicit facts, app enrollment, story rules and any configured allowance. deleted Deletion tombstone; never restart tracking silently. Not configured / init failure Check inputs/order. Async calls may reject; preserve the host app. Setup is still waiting Install: build and install the updated host app. The server cannot observe npm installation or an unopened APK. Contact: open the app and sign in if required. Initialization waits for a resolved customer, permission and reliable access facts; onboarding alone may not reach it. Check a new last-seen time in Builder. Build: development and release builds both work. Remove accidental __DEV__ or extra test-flag guards around initialization, not genuine permission checks. Rebuild if the installed APK predates your change. Configuration: match the public key, service base URL and environment. Builder Refresh reads receipts; it cannot make the host app run. Access: refresh the host source and inspect local/paired diagnostics; no raw remote lifecycle receipt is required. Value: run the action and check exact key/method and last-received time. Story: select a Sandbox version, pair, refresh and check opening/completion separately from outcomes. Poster sharing unavailable: check the resolved SDK version, bundled RecappelPosterShare native module and rebuilt installed binary. A Metro refresh cannot add native code. Preserve private recap policy and every referenced fact’s approval; do not enable private content or add a text fallback to make the button succeed. Known cancellation is not completion, and an uncertain result must not open a second sheet. Test the actual OS handoff separately from a successful JavaScript bundle. Supported TextureView capture requires Android API 24+ and the bounded SDK-native path; do not relax a host screenshot safeguard or add another capture package as a workaround. Unsupported compositions and oversize images fail safely. Image sharing is bundled; no separate sharing or capture package is needed. An error can also mean that an existing activity or capture surface is not ready, rather than a missing module; inspect capability before rebuilding. The published beta.16 readiness-error correction distinguishes those states and keeps the story open for a deliberate retry; it is not in published beta.15. Leader or relationship unavailable: check installed SDK and compatible service/Builder support, exact current source definitions, matching scope/unit/population/window and a fresh permitted reading. Ties and zero make a leader unavailable. A failed metadata receipt or conflicting fixed preview example is a review gap, not a reason to invent values or upload private winning labels. Offline and cached data Validated stories, aggregates and queued requests can persist. A first offline install has no remote story cache; memory storage cannot survive restart. A resolved flush does not prove delivery. Bootstrap refresh is due after 15 minutes; playable cached data has a maximum age of one hour. Cold-start enrollment failure does not assume permission. Use isolated Sandbox fixtures, not changed Production assignments or invented access facts. Connect in five steps Sandbox & test devices Release status Previous: React Native API reference Next: Release status
Release status undefined Current release Install @recappel/react-native@latest for the recommended public release. Check npm for the resolved version; verify your installed package and lockfile agree. Compiled runtime, declarations/assets and proprietary license are included. SDK values, lifecycle-v2, Free/Trial/Paid enrollment, native renderer/preview, banner styling, pairing, shared links and bounded diagnostics. Current releases also report Sandbox setup status; older installed builds may not report access/value/presenter readiness. Connected development Builder/backend: accounts, app keys, received values, constrained authoring, immutable versions, Results and coverage controls. Reviewed automated checks and package installation evidence; not a guarantee on every native host or at production scale. Earlier September 27 publication checkpoint: beta.17 was public latest. Its exact immutable 212,786-byte, 363-file archive was checked against an anonymous registry installation. It includes top-edge bubble entrances, refined recap typography, optional category-list bindings and narrow Android consumer rules that preserve native sharing under release minification. Rebuild the native app when upgrading from a release without those rules; no separate sharing or capture package is needed. Package and installed-consumer checks do not establish all-screen native/iOS, performance, real billing or Production acceptance. Use the verified installed declarations. Published September 27: beta.18 is public latest. Its corrections include the following: unpaid upsells omit the separate Free row and no-payment copy, preserve one genuine custom host purchase control, or show one functional fallback only when the host supplies a real purchase capability. Paid, trial, billing-trouble and access disclosures remain truthful. Genuine shared-link playback can resolve the current recipient’s exact local category names, never labels from the link, server or another owner. These names are not uploaded, persisted, used in AI/telemetry or exported in posters. Narrow recap posters scale their interiors proportionally with readable text and tighter divider spacing. The exact 213,046-byte, 363-file package passed installed-consumer checks and an anonymous public-registry download plus fresh @latest installation at 01:54 UTC on September 27; every file matched the qualified archive. Native files and runtime dependencies are unchanged from beta.17. Installed-host visuals and native acceptance remain separate. Remaining acceptance Area Still required Real host Install the public package into your actual app; test both native platforms, sessions, offline, fonts, accessibility and sharing. Production / scale Operational release approval and representative aggregate load testing. No 500K/10M capacity guarantee. Store billing Audience prices are approved, but checkout remains unavailable until exact native products, localized offers and the server catalog are verified. Real purchase/renewal/restore, legal and sustained-capacity acceptance remain separate. Visual stories Review discovered public artwork locally; connect approved theme and compatible registered fonts. Verify the generated design and any optional host-owned personal-image providers on real devices. No unrestricted repository upload or automatic font installation. Measurement Real trusted backend outcomes and complete observation windows; no promised retention lift or per-card dwell analytics. Other platforms Standalone Swift, Kotlin and Flutter SDKs are not published. Public-launch checklist Use the exact published SDK and compatible native peers. Connect the host app to its Sandbox key and service endpoint. Verify received values, selected version, audience/timing, session changes, presentation and outcomes end-to-end. Complete store, privacy, billing and operational acceptance separately. Ship the matching Production configuration, then publish the tested story from Builder. Saving, paying or installing never launches it automatically. npm distribution and backend hosting are separate. A public SDK does not mean Builder is already available in the app stores or that checkout is enabled. Historical September 25 beta.13 checkpoint: @recappel/react-native 0.2.0-beta.13 was public latest. The 198,072-byte, 335-file archive and a fresh anonymous latest installation match the tested package. It removes the outer poster matte and duplicate backdrop while retaining the opaque inner artwork, tilt and shadow, with native sources unchanged from beta.12. A Samsung Android host produced an actual 1080 × 1957 PNG with transparent outer margins; the chooser, cancellation back to the same story and retry were verified. This does not establish iOS sharing, successful recipient posting, full visual completion, Production readiness or website publication. Historical beta.12 checkpoint: its verified 198,101-byte, 335-file release established native image export and chooser return. Beta.13 supersedes its rectangular poster-background blending issue; the beta.12 archive remains unchanged. The normal Android free-account subscription-management handoff was also checked without making a purchase or invoking Restore. Package and device evidence do not replace acceptance in your own host. Further reading Use the published package README and installed declarations for exact signatures. Older v0.1 source notes are historical, not the current installation guide. npm package ↗ , Expo libraries ↗ and Expo development builds ↗ . 1. Install SDK Connect in five steps Settings, plan & billing Previous: Troubleshooting
SDK platform
Start here
Build a reason to stay. Turn the value your app already delivers into a personal, brand-matched story. PieceJobYour app + SDKSupplies approved values and local access facts; checks eligibility and renders the story. It does not take payments.Recappel BuilderMobile workspace for connecting apps, editing stories, testing, publishing and results.Recappel serviceStores configuration, receives approved aggregates/events and delivers compatible story versions when the SDK requests them. Use your existing app Recappel supports React Native and Expo on iOS and Android. Keep your authentication, payment provider and navigation. The SDK sends only what you explicitly report—not your database or every action.Choose evidence of delivered value: saved highlights, completed sessions or finished projects. Do not claim books read or time saved unless your app measures it. Customers see your brand; developers manage it in Recappel. Start integrating Follow the five steps → or use your coding agent →. Both paths use the same SDK and checks.Install @recappel/react-native@latest, test in Sandbox, then prepare your customer release. Check Release status → for version compatibility and availability.
Connect in five steps Follow the Builder’s five setup steps. Verify each result before continuing. Before you start A React Native / Expo project you can edit and build. Installing the SDK requires an updated native app on your device.Add the app in Builder. Copy its public Sandbox key and service URL; keep its Production configuration separate.Use the app’s existing customer ID, permission decision and explicit Free/Trial/Paid facts. Your agent can locate these in code; it should ask only for missing decisions.Either a development or release build can connect to Sandbox. The configured key and environment—not the build type—choose where data goes. 1. Install SDK Add the SDK and compatible persistent storage.Confirm the dependency is installed and your app builds. Installation is checked locally, not by the server.Open this step → 2. Connect Add the app’s public key and service URL, then wire your customer identity, permission and access facts. Start with Sandbox.A received request identifies the app, environment, SDK version and contact time. Mock transport cannot prove contact. No story yet is not a connection failure.Open this step → 3. Pass billing and access Map trusted paid/trial facts into setLifecycle, or supply explicit free audience facts.Check source freshness, local eligibility and the selected Sandbox device’s diagnostics. Detailed lifecycle stays on-device. Diagnostics are not independent payment verification.Open this step → 4. Send useful values Report an approved snapshot or completed value moment in Sandbox.Builder shows the received key, method, safe example and last-received time. A saved definition or resolved flush is not receipt. Confirm meaning separately.Open this step → 5. Show and measure Add the invitation, verify playback in Sandbox, then prepare the customer build and publish from Builder.Check invitation visibility, opening, completion and the separate outcome individually. Invited is not a view. Client outcomes are unverified; previews do not count toward Results.Open this step → After setup Confirm value meanings, review the outline and edit the generated draft.Test the exact version and edge cases in Sandbox.Prepare and ship your host app with its Production key, production environment and matching access facts. No story needs to be live yet.Review audience, timing, version and capacity checks, then Publish in Builder. No additional publish switch is needed in your code.Follow the production checklist →. Keep the comparison group and allow the outcome window to finish.
Connect using your agent Give your coding agent one setup task, review the changes, then run your app. Open your host-app project in your coding agent. Copy task 1 below; it checks compatibility and installs the SDK. Review its result, then copy the next task. These handoffs do not upload your repository to Recappel.For Connect, provide the app’s public Sandbox key and service URL from Builder. The agent finds your existing identity, permission and access sources in code and asks only when something important is missing. Never paste provider secrets, receipts or customer content. Setup tasks Review the result Changed files and the exact command to run or build the updated app.What worked locally and what Builder actually received.Any missing input or check before the next step.The final task prepares the Production configuration. You control store submission and publication in Builder; the agent does not purchase or launch anything for you. Pause host-owned polling, retries and value work on temporary background/offline transitions; cancel late work without retiring otherwise valid authority. Do not call configure({ enabled: false }), replace SDK identity or unmount the presenter solely for AppState/connectivity. Gate already handles foreground/background checks and may flush queued work; cinematic motion pauses without replacing the player. Keep the original authority deadline active, and check identity, permission and expiry before and after resume awaits. Real logout, permission withdrawal, account change and expiry still retire immediately. Do not refresh cached timestamps or reset show caps. Private-media providers must still abort/clear background requests; that is not global session retirement. Open a story and cancel an OS share/review sheet: return to the same session, story card and show counts, without another present() or duplicate exposure. Test expiry, permission withdrawal and account change while suspended: old work must not resume. Check offline recovery, failed/late renewals and unchanged cached observations without extending authority or starting duplicate work.
Connect the SDK
1. Install SDK Install Recappel and configure storage to keep values across restarts. Run this in your host-app project. The SDK contains compiled JavaScript, types and assets under a proprietary license. The latest tag selects the release recommended for new installations. Keep the resolved version in your lockfile and your app’s bundle/package ID unchanged. Existing apps do not update automatically; review compatibility, run this command again to upgrade, then apply any required native build steps and test. No uninstall or reconnection is needed for a compatible update. Keep the app key, billing/access adapter, value keys and persistent storage. Check release notes for any migration. SDK releases declaring RecappelPosterShare also contain autolinked Android/iOS native source. Use the existing CocoaPods/native build flow when first adding that module; Metro reload and Expo Go cannot add it. An SDK-only update from beta.12 to beta.13 uses the same native code and does not require a new native rebuild. Verify the installed package, actual binary and updated bundle. No separate sharing package is required. Verify with npm ls @recappel/react-native --depth=0 and node -p "require('@recappel/react-native/package.json').version"; the installed version must match the lockfile. A lockfile-only edit is not installation. If they disagree, finish the targeted install and verify again before building. Restart the host bundler after updating; an old APK still contains its old bundle. Do not delete the lockfile or app data to repair a package mismatch. Persistent storage AsyncStorage is an optional peer. Install a compatible version or provide a StorageAdapter. Without working persistence, fetch transport uses memory and loses values when the process ends. Mock transport uses memory by default. Build and verify Check React / React Native, Skia, Reanimated and Safe Area Context against the package peer requirements. Keep compatible existing versions. For Expo, use expo install for missing compatible native dependencies. Match Reanimated/Worklets and Babel configuration to the installed version. On bare iOS follow your CocoaPods flow on macOS. Create a new development or release build using your project’s normal build command, then install it. Expo Go and an older APK do not contain the newly installed native libraries. Confirm public imports resolve, run checks and launch the app; then select Mark as installed in Builder. A release build can connect to Sandbox. Use a development build when you want Metro and debugging tools. See Expo libraries ↗ and development builds ↗. Connect verifies the first server contact. 2. Connect your app Connect using your agent Release status Previous: Connect using your agent Next: 2. Connect your app
2. Connect your app Use the app’s public key and a stable customer ID to connect the right data to the right project. Create an app record Builder → Apps → Add app. Enter the name, platform and exact bundle/application ID; the icon is optional.Use this app’s Sandbox connection for testing. Its Production key and delivery remain separate.Copy the public project key and service base URL from this app’s setup instructions.A bundle ID is not a credential. Each value reuses this connection; it does not need another key. Never put a Supabase service-role key, AI key or billing secret in the host app. Configure at the session boundary Add the helper below to your integration module and call connectRecappel(...) after your app resolves the customer and access facts. Supply these six inputs from your existing app; merely pasting the helper does not run it.InputSourcepublicProjectKeyThis app’s public key from Builder. Use Sandbox first.apiBaseUrlService base URL copied from Builder. Required by this SDK; do not use the npm URL or invent a server.sdkEnvironment'sandbox' for testing; 'production' with the matching Production key for customers.opaqueCustomerIdYour stable internal customer ID, not email. No hardcoded tester UID is needed.enabledForThisCustomerYour existing permission/enrollment decision. Keep genuine opt-outs; this is not a publication switch.accessFactsExplicit free/trial/paid AudienceContext and the time actually observed. Unknown access is not Free.The same initialization runs in development and release builds. Do not gate it behind __DEV__ or an extra test flag. There is no publish/enable code switch: you publish stories from Builder after setup. Configure before lifecycle/values, not on every render; handle customer and permission changes as described in Customer identity & sessions. Verify contact Open the updated app and sign in if required. Reach the screen where initialization runs.Open this app’s Connect screen in Builder and refresh. Check a new last-seen time and SDK version.If the installed release APK predates the code change, rebuild and install it before checking again.Onboarding alone does not connect when the app has not resolved a customer identity. Keep existing permission and access checks; do not invent a guest ID to force Connected.story: null is expected before a story is available. No published story or reported value is needed for contact. With the right configuration installed, the SDK fetches published stories later.Builder reads received requests; pressing Refresh cannot start the SDK inside your app.Fetch transport contacts the service; mock stays local. A resolved configure or flush does not prove receipt.If waiting continues, check identity, permission, access freshness, key, environment, base URL and network. The SDK appends its own route to the supplied base URL.Keep the connection currentIf your access adapter uses a validity deadline, refresh from its real source before the freshness deadline expires while the app is active. Keep original observation times; do not make cached facts fresh. Use one in-flight check, finite timeouts and bounded retry backoff. Stop stale reporting but retain a safe recovery path. Pause host-owned polling, retries and value work on temporary background/offline transitions; cancel late work without retiring otherwise valid authority. Do not call configure({ enabled: false }), replace SDK identity or unmount the presenter solely for AppState/connectivity. Gate already handles foreground/background checks and may flush queued work; cinematic motion pauses without replacing the player. Keep the original authority deadline active, and check identity, permission and expiry before and after resume awaits. Real logout, permission withdrawal, account change and expiry still retire immediately. Do not refresh cached timestamps or reset show caps. Private-media providers must still abort/clear background requests; that is not global session retirement. Open a story and cancel an OS share/review sheet: return to the same session, story card and show counts, without another present() or duplicate exposure. Test expiry, permission withdrawal and account change while suspended: old work must not resume. Check offline recovery, failed/late renewals and unchanged cached observations without extending authority or starting duplicate work. There is no universal five-minute SDK timeout.Test staying in the foreground beyond two refresh intervals, changing a value after expiry, failed lookups, recovery, account switching and opt-out. Local success, accepted value receipts, saved definitions and story playback are separate checks. After a successful authoritative same-account access renewal, use refreshAccess only when the installed public API declarations support it. Do not call configure on every successful access refresh or retire the permitted session merely because a routine authority lookup starts. Keep the original freshness deadline/expiry and its hard-expiry handler active during both the authority lookup and SDK await; recheck expiry, identity, permission and cancellation afterward. While checking, cached facts must not receive a new timestamp. Required maxAgeMs is a positive safe integer bounded by configured SDK/story freshness; use the authority's documented shorter freshness policy, not an invented timer. Paid/Trial needs equivalent active, unexpired, nonsuppressed lifecycle facts with strictly newer audience and lifecycle observations; Free has no lifecycle. refreshAccess is only for strictly newer, still-fresh observations of equivalent audience, profile and lifecycle facts in the exact already enabled/enrolled/unblocked project/environment/customer context. Only observedAt may change; both old and new observations must remain fresh. A true result preserves the session, caps and current player; it does not automatically bootstrap or prove a server receipt. A false result requires the normal configure recovery path after resolving current permitted facts; never replay captured old-account configuration. Matched invalid, changed or expired facts disable admission/presentation; a mismatched account cannot mutate the new account. Real identity, permission, plan or lifecycle changes still use deliberate reconfiguration. Older packages need an approved upgrade for this API; never cast, call private methods or assume newer docs upgraded the installed binary.
Customer identity & sessions Use a stable opaque customer ID to keep each person’s values and story assignment separate. IdentityUseBuilder accountDeveloper access to apps and the Recappel plan.App / project keyPublic identifier for an app and SDK environment—not a password.Opaque customer IDHost-owned ID tying values, assignment and outcomes to one customer.Use the same ID across that customer’s devices. Never use email, name, phone, advertising ID or provider customer/transaction ID. Opaque IDs are pseudonymous, not legally anonymous. Changing customers Wait until the customer is resolved. On logout disable enrollment/reporting and hide the presenter. Guard late host callbacks, then configure the next resolved customer with that customer’s access facts. Storage and queues are isolated by exact project key, environment and opaque customer.Use your existing session flowThere is no public login(), logout() or reset() API. Reconfigure at deliberate session/access changes and test pending requests, visible stories and account switching. Never use deleteEndUser() for sign-out. Guests and multiple devices Guest merging is not implemented. Define a stable guest ID and explicit merge policy before enabling it; do not create a new ID each launch.Device-local increments are not account-wide totals. For synced data, use an authoritative host snapshot. Verify cross-device deduplication and merging before claiming exact totals.
3. Pass billing and access Keep your payment provider. Pass trusted access facts without granting Recappel billing-account access. Map the backend, store API, payment SDK or entitlement cache your app already trusts. No customer-side RevenueCat installation is required; Recappel uses it only for its own Builder billing.Pass access, real dates, renewal state, environment and known grace/retry/refund flags.Never pass secrets, payment details, receipts, raw provider objects or customer contacts.Refresh at sign-in/startup and source changes. Cached facts keep their original observation time. Recurring-subscription example Add this host-owned adapter, then call passSubscriptionFacts(facts) from the app’s resolved access callback and after actual source changes. Supply your existing source’s facts—not example dates. Use it only for a conventional recurring subscription whose period end supports the displayed wording.Match SDK sandbox to lifecycle SANDBOX, or production to PRODUCTION. Never relabel store test purchases as live. Free access uses explicit audience facts instead of this adapter. Purchase-model support ModelUse only known factsRecurringLegacy LifecycleDTO or provider-neutral LifecycleDTOv2. Cancellation is not necessarily lost access.PrepaidActual access end and automatic/manual renewal; do not invent a next charge.Postpaid / invoiceLifecycleDTOv2 separates collection mode and known period, charge and due dates.One-time / lifetimeExplicit one-time access with an honest no-recurring-charge ending.Free / Trial / PaidHost-supplied AudienceContext. Trial must be explicit; unknown access must not become Free.LifecycleDTOv2 is exported alongside the legacy DTO. Use its exact types. Free stories use non-billing moments and do not need fabricated purchase dates. Verify access facts setLifecycle validates and stores detailed facts locally. Open the updated app: Builder’s Sandbox setup check automatically receives a bounded access-readiness status from supported SDK versions. It does not receive your billing account, raw customer records or payment receipts. Bootstrap contact is not independent payment verification.Inspect eligibility, then test the actual transparency/ending on-device. Stale or mismatched access and grace/retry/refund must not become a positive renewal claim.Keep the connection currentIf your access adapter uses a validity deadline, refresh from its real source before the freshness deadline expires while the app is active. Keep original observation times; do not make cached facts fresh. Use one in-flight check, finite timeouts and bounded retry backoff. Stop stale reporting but retain a safe recovery path. Pause host-owned polling, retries and value work on temporary background/offline transitions; cancel late work without retiring otherwise valid authority. Do not call configure({ enabled: false }), replace SDK identity or unmount the presenter solely for AppState/connectivity. Gate already handles foreground/background checks and may flush queued work; cinematic motion pauses without replacing the player. Keep the original authority deadline active, and check identity, permission and expiry before and after resume awaits. Real logout, permission withdrawal, account change and expiry still retire immediately. Do not refresh cached timestamps or reset show caps. Private-media providers must still abort/clear background requests; that is not global session retirement. Open a story and cancel an OS share/review sheet: return to the same session, story card and show counts, without another present() or duplicate exposure. Test expiry, permission withdrawal and account change while suspended: old work must not resume. Check offline recovery, failed/late renewals and unchanged cached observations without extending authority or starting duplicate work. There is no universal five-minute SDK timeout.Test staying in the foreground beyond two refresh intervals, changing a value after expiry, failed lookups, recovery, account switching and opt-out. Local success, accepted value receipts, saved definitions and story playback are separate checks. After a successful authoritative same-account access renewal, use refreshAccess only when the installed public API declarations support it. Do not call configure on every successful access refresh or retire the permitted session merely because a routine authority lookup starts. Keep the original freshness deadline/expiry and its hard-expiry handler active during both the authority lookup and SDK await; recheck expiry, identity, permission and cancellation afterward. While checking, cached facts must not receive a new timestamp. Required maxAgeMs is a positive safe integer bounded by configured SDK/story freshness; use the authority's documented shorter freshness policy, not an invented timer. Paid/Trial needs equivalent active, unexpired, nonsuppressed lifecycle facts with strictly newer audience and lifecycle observations; Free has no lifecycle. refreshAccess is only for strictly newer, still-fresh observations of equivalent audience, profile and lifecycle facts in the exact already enabled/enrolled/unblocked project/environment/customer context. Only observedAt may change; both old and new observations must remain fresh. A true result preserves the session, caps and current player; it does not automatically bootstrap or prove a server receipt. A false result requires the normal configure recovery path after resolving current permitted facts; never replay captured old-account configuration. Matched invalid, changed or expired facts disable admission/presentation; a mismatched account cannot mutate the new account. Real identity, permission, plan or lifecycle changes still use deliberate reconfiguration. Older packages need an approved upgrade for this API; never cast, call private methods or assume newer docs upgraded the installed binary.
4. Send useful values Report delivered value with an accurate meaning, unit, time window and reporting method. Discover the useful catalog first; choose each story later. Review all eight families, not two sample totals. These five narrative jobs are not five compulsory screens.Story jobPurpose and evidenceRecognitionMake the customer curious about their own useful activity. The product benefit and a truthful hook leading to its strongest measurable proof. The opening is an attractor, not an arbitrary statistic or an unearned identity claim. Its following hero supplies the number.Concrete proofShow the most meaningful result, then distinct supporting detail. Completed useful work, accumulated value or a clearly labelled current collection; breadth, depth and valid derived facts. A tap is not completion. A current library count is not books read. Supporting cards may contain several different facts.Progress and patternsShow activity over a known period or a genuine change. Actual ordered time buckets, comparable periods, distinct active days and verified participation. A previous SDK bucket alone is not a comparable period. More activity is not necessarily improvement.Breadth and breakdownShow composition, range or a clearly defined leader. Distinct resources, complete exclusive category counts, and a safe leading category with its supporting count. Overlapping tags are not pie-chart parts. Most represented is not automatically favourite. Never send customer-written labels.Milestones and recapShow an earned milestone and a concise keepsake. Real streak/history or achievements; reuse the strongest proof and a few complementary facts in the recap. No invented threshold, rank or separate data collection just for sharing. Actual access clarity comes from the host.Choose the hero by direct benefit, reliable source and understandable scope, not the largest number. A supporting card can use several facts. Recap reuses evidence; access clarity comes from the host.Discovery familyLook forEvidenceValue deliveredUseful work completed, saved, created, revisited or resolved; amounts, duration or distance only when measured.Successful source actions or authoritative totals; separate current holdings, period activity and lifetime history.Breadth and depthDistinct resources used, useful collection size, variety, favourites and meaningful reuse.Deduplicate the right entity; an item added is not an item read, used or completed.Averages and ratesAverages per resource or active day, completion rates, proportions and intensity.Record numerator, denominator, formula, same population/window/unit, exclusions and rounding. A zero denominator means unavailable, not zero; do not average averages.Category mixCounts by approved categories or sources; distinct category breadth and bounded distributions.Define the denominator and unknown/other bucket. A percentage composition needs a complete non-overlapping partition; multi-tag counts may overlap and must not be presented as one.Leaders and preferencesMost-used category, most-revisited type or an explicitly chosen favourite, plus its count.Specify ranking measure, population, window and ties. A measured leader is not a declared preference. Use an approved nonpersonal enum; a customer tag, title or person is not safe just because it is short.Time and changeReal weekly/monthly buckets, matched-period comparisons and distinct active days.Define boundaries, timezone, order, completeness and resets. Imports may preserve old timestamps; surviving records are not full activity history. Compare completed or matched elapsed periods only.Habits and milestonesCurrent and longest streaks, earned badges, personal bests and verified checkpoints.Reuse the app’s qualifying-action and day rules; distinguish current versus historical best and truncated history. No made-up badge threshold or leaderboard.Brand and artworkLicensed app-owned logo, illustrations, category icons, badges, background tokens and registered fonts.Catalog local source, rights, public-use approval, subject, crop and intended role. Do not upload customer photos, book covers or private content as generic decor. No asset means a plain-layout fallback.Aim for 8–20 distinct useful numeric candidates when the app supports them. For a rich-story shortlist, seek at least 6 meaningful facts across 3 numeric families, including a core result and supporting facts. These are catalog review targets, not hard discovery minimums. The catalog supports up to 32 numeric definitions plus 8 structured groups with separate limits. Ordinary derived scalars still consume numeric slots; a registered ratio, distribution or series consumes one structured slot. Preserve every existing key, including all 31 scalar keys in an existing 31-value catalog. Never remove working values to make room for a group. Check the installed declarations and compatible Builder/service path; older builds retain their declared limits. Review excess candidates locally before connecting more. New generated stories require 3–5 distinct approved logical values, not exactly five. A scalar or a reviewed structured group is one logical value: each group counts as one, not one per category or period. If fewer than 3 suitable values are available, do not generate a new story; keep useful values connected and report what is missing. Never fabricate, duplicate or pad values to meet this selection minimum. Existing saved stories and legacy compact SDK/runtime stories remain compatible with fewer values, including one numeric proof, subject to their existing validation and eligibility rules. This selection rule is not a discovery quota or a minimum number of displayed cards. Artwork and label-only metrics do not count as numeric coverage.PriorityMeaningP1 — CoreDirect evidence of the app’s main customer benefit. Inspect and connect these first.P2 — SupportingAdds distinct context: participation, depth, progress, breadth or an earned milestone.P3 — OptionalA useful secondary view or safe personalisation; omit when it adds no meaning.Priority is not confidence, permission or final card order. Keep source-confirmed, inferred and unresolved meanings separate. Return all families as ready/wired, pending, unavailable or excluded with reasons. Do not fabricate values to meet a target. Report after completion Configure the resolved customer and audience; pass paid/trial lifecycle before period values. Explicit free access uses its non-billing cycle. Increment after a confirmed save—not render, screen open or a pending request. Deduplicate retries/imports in the host. Choose one method per key MethodStoresUseincrement / addCycle sumConfirmed additive events; repeated calls add again.setLatest snapshot; can decreaseHost-owned current, all-time, period or rolling total. Repeated calls replace.setMaxHighest observed in this cycleNot a current streak or an automatic all-time record.setLabelReviewed enumNo titles, notes, names or arbitrary text.STRUCTURED VALUES When the installed public declarations expose StructuredValueDefinition, StructuredValueSnapshot, configure.structuredValueDefinitions and setStructuredValue(key, snapshot), add up to 8 structured definitions to the existing initializer, separately from its 32 numeric valueDefinitions. Do not invent an API or assume an older installed build gained it. Report a missing capability and use the approved package-update flow when needed. Preserve existing keys and semantics; do not register a group or generated binding that collides with any scalar key. Structured keys and fixed category keys match ^[a-z][a-z0-9_]{0,31}$. - ratio: declare public numeratorLabel, denominatorLabel and precision (0–3, default 1). Report numerator and denominator from the same authoritative population/window; the SDK divides and rounds, without automatic percentage scaling. A zero denominator is unavailable, never a fabricated zero or average of averages. - distribution: declare 2–24 fixed public categories {key,label}, basisLabel, exclusive:true and complete:true. Report every category count plus total; counts must sum to total. Use only permission-reviewed app-defined taxonomy, never private/custom tags, titles or dynamic customer labels. The runtime shows the top three categories plus Other for the complete remainder, preserving the denominator. Equal leaders are ties with no unique leader claim; a unique positive leader is most represented, not automatically favourite. Unknowns need an explicit truthful category in the fixed taxonomy, not silent omission. Overlapping tags are not this distribution. - series: scope must be rolling. Declare interval day, week or month and periods 2–4. Supply exactly that many ordered contiguous completed calendar windows {startDay,endDayExclusive,value}, an IANA timeZone and genuine coverage. Weeks start Monday; months start on the first. The last window ends at the current interval boundary in that timezone; no partial current period or stale set of windows. Missing history is null or unavailable, not zero; any missing window makes the group unavailable for presentation. A decline is not automatically improvement. Every snapshot includes status and genuine UTC observedAt. maxAgeHours is an integer 1–168, default 24. Do not refresh observedAt on stale data. Reporting {status:'unavailable',observedAt} clears the entire local group's prior displayable values; expiration also makes it unavailable. This is not an arbitrary scalar reset API. Readings are finite, nonnegative and at most one trillion; count distributions require integer counts. Only definitions, availability status and revisions are sent for the structured facts; their numbers stay on the device in account-scoped storage. The request still uses the existing app/environment/customer/device scope and access guards; ordinary scalar aggregate telemetry is unchanged. Never send numeric examples, source paths or private labels in definitions. Recheck the exact active account, permission and access after every source await. Cancel retired work and invalidate an unavailable source only for its still-current permitted session. Never clear a new customer's group from an old callback. Verify metadata received, available/unavailable receipt and rendered story separately; unavailable is connected with no usable reading, not missing SDK wiring. Group shape stays registered; owner wording edits and Rewrite must preserve its meaning and formula. Do not add fake labels, history or counters to fill story slots. Define, then verify For each candidate, record its key, customer benefit, source, unit, exact window/timezone, behavior, decrease/reset rules, direction, reporting method, possible story uses, priority, evidence confidence and sharing safety. Keep source paths and implementation notes in a local catalog.Enter the approved meaning in Builder’s existing fields. Put advisory priority and story use in its description: “Priority: Core. Current saved highlights; deletions reduce the count. Useful as proof.” This is an existing description field, not a new priority control or SDK parameter. Do not add unsupported fields to a ContextManifest; the importer does not forward the whole local catalog to authoring.Builder access is not needed to discover or instrument values. Reuse local keys when correct, wire clear useful candidates within existing permissions, and ask only about material gaps. A scalar receipt later proves the exact key and method arrived—not its meaning. A structured receipt reports availability, never the customer numbers. Check environment and last-received time, then confirm the meaning before story use.Keys: 1–64 characters; lowercase letter first, then lowercase letters, digits or underscores.Numbers: finite, with domain-valid signs. Zero is valid data, not an achievement.Labels: 1–40 letters/digits/spaces/hyphens/underscores. Syntax is not a privacy filter; use an enum allowlist. Current automated authoring uses numeric definitions, not label-only proof.No code upload, automatic field crawling or new API key per value.Derived facts need a formula, numerator, denominator, matching population/window, exclusions and rounding. A zero denominator is unavailable, not zero. Skipping a scalar report may retain its older value; require a safe stale-display guard. Supported registered groups instead provide unavailable clearing and freshness expiry. Test synthetic inputs, ties, imports, decreases and failed reads.Builder generation selects reviewed scalar meanings and bounded registered groups. Dynamic customer labels and arbitrary second-metric captions remain unsupported. Registered distributions group a complete approved taxonomy as top three plus Other; registered series use two to four completed periods. Other scalar stack/chart paths keep their existing item limits; overlapping categories never become a complete partition. Asset files must be reviewed and uploaded separately; the model sees descriptions and approved IDs, not customer photos or an automatically extracted app.Add a stable valueDefinitions array to the existing configure call for every wired numeric value. Names, meanings and counting rules reach Builder automatically through the SDK. Open Values → Received from your app to review them; existing definitions are kept. No file import is needed. The copyable task includes the supported fields and an example.A received definition is not a received number. Confirm matching keys, counting rules and fresh receipts separately. Waiting for value means only the definition has arrived. Never send source paths, customer data or example numbers in definitions. Relationship approvals and artwork remain separate. Cycles and snapshots All five calls use SDK cycle buckets; the SDK retains current and previous. The separate structured snapshot API uses its declared source scope and completed calendar windows. Legacy subscription periods, lifecycle-v2 anchors and explicit free access select their supported cycles. A bucket is not automatically a calendar or rolling business window.Keep all-time/rolling calculations in the host. Re-send authoritative snapshots after customer, source or cycle changes. Document the metric’s timezone and reset rule; earlier reports are not automatically reattributed.Snapshot-only storiesminTotalEvents sums only increment/add values. Do not require positive additive totals for snapshot-only stories or add fake increments to pass the gate.
5. Show and measure Offer an invitation and measure story interaction separately from the business outcome. Mount the invitation Add the component below and render <RecapInvitation /> in one stable host-screen location, under your existing SafeAreaProvider. It displays the banner and opens the native story on tap. Do not mount a second presenter beside it.Wire this before publishing. With no available story—or for ineligible and holdout customers—it renders nothing. Do not reserve a fixed empty gap. Report the business outcome Use the experiment’s exact outcome key. Report confirmed success for both groups, even without a story opening—not from banner taps, review requests, restores or payment-screen visits. Outcome attribution A destination tap, review request or share-sheet opening is not proof of a renewal, posted review or completed share.recordOutcome() uses the current cycle and available experiment assignment; it cannot name a historical renewal period. Renewals may occur after rollover or without another session. Correct attribution and server verification are required before causal renewal claims. Test the full path Select and pair a valid Sandbox version; refresh access/values and check eligibility.Verify visible invitation, open, finish and early dismissal separately.Test each destination on its intended OS.Trigger the genuine outcome independently; check key and attribution.Repeat with no data, stale facts, wrong environment, holdout and billing trouble.Keep opened, completed and outcome received separate. Invited telemetry is preparation, not proof of an on-screen view. Shared previews are excluded from Results.Next, prepare the Production build and publish →. Reuse the same initializer and presenter; no extra code switch is needed to turn publication on. Compatible SDKs recheck a waiting invitation when newly supplied values become available, with coalesced checks rather than a polling loop. Keep one stable Gate; do not reconfigure the SDK, remount the presenter or reset an open player to force delivery. Eligibility and session checks still apply. Confirm this behavior in the installed SDK.
Build your story
Add a new value Use an arriving value first. Add host-app code only when the needed fact is missing. Use an arriving value Open app → Values, or a card’s Data → Add value.Select a received key. Review its meaning, unit, scope, safe example and timestamp.Correct any mistakes, confirm and return to the same draft card.No reconnection or new code is needed for an already-correct value. The Builder sees reported aggregates, not every variable in your app. Connect a missing value Describe the fact; review its suggested meaning, window, reporting method and stable key.Get its SDK instructions or use agent task 4. Reuse this app’s connection.Add the host reporting call and run the real action in Sandbox. Keep Waiting for first value resumable.After receipt, confirm key, method, example and time; choose Use in story to return to the card.During initial integration, collect the broader useful catalog using the five story-value criteria →; do not limit instrumentation to the current card. Keep priority and evidence separate. Builder access is needed to verify receipt, not to inspect source and wire clearly supported values.Example numbers can preview a draft while waiting. They cannot prove receipt or pass Production checks. Change or remove a value Renaming the display label does not change the key.Changing meaning/window may need a new key and instrumentation; never relabel old history.Show dependent cards before removal. Published versions remain fixed.Archiving is not erasure and does not remove the host SDK call.New calls require an app release. Test older versions that do not send the value.
Create & edit a story Review recommended values and an outline, then edit the story with a live preview. Story → Create story. Review useful candidates across your approved value catalog.Confirm preselected meanings and any material uncertainty.Choose 3–5 distinct approved logical values for this story; each scalar or registered group counts as one; other connected values remain available for another draft or revision.Review the outline: card purpose, value and time window.Generate the draft, then edit one live card with focused controls and the card strip nearby.New generated stories require 3–5 distinct approved logical values, not exactly five. A scalar or a reviewed structured group is one logical value: each group counts as one, not one per category or period. If fewer than 3 suitable values are available, do not generate a new story; keep useful values connected and report what is missing. Never fabricate, duplicate or pad values to meet this selection minimum. Existing saved stories and legacy compact SDK/runtime stories remain compatible with fewer values, including one numeric proof, subject to their existing validation and eligibility rules. This selection rule is not a discovery quota or a minimum number of displayed cards.The five narrative jobs guide selection; they do not force five cards. Priority suggests relevance, not permission or an automatic final order. Actual customer data and the story’s renderer/eligibility rules still decide what can appear. Editor controls ControlChangesDataApproved value and meaning. Real customer numbers stay bound to data.TextWording around the value, never invented totals or dates.LayoutArrangement of the same fact—not a different metric.Colour & artworkApproved tokens/assets; test contrast and image failure.Reorder / removeValue-card order, with recognition/transparency restrictions and Undo.Preview, Save and Publish are separate. Edits create a draft without changing the live story. Suggestions and approval Create Story uses approved app context, value meanings and reviewed public artwork to choose copy, compatible layouts, images, a subtle texture, palette and ready fonts. Review the resulting draft before publication.No model runs in the runtime SDK. Source code, customer rows, IDs, billing records and private pictures do not go to the model. The optional artwork tool scans locally and uploads only approved files. Arbitrary fonts still need supported host registration. Values Rewrite remains text-only.
Cards & layouts Choose supported native cards and layouts. Stories contain validated data, not executable UI. Recognition → value → next action (optional) → transparency → rating (optional) → share (optional).Standard definitions contain 4–9 cards. A validated compact cinematic story can use three: recognition, one numeric proof and clarity. Recognition comes first; the ending must match paid/trial/free access. Customers can exit at any time. The ten card types TypeUseAvailable layoutsrecognitionWarm framing of the customer’s valuestandard, illustrated, openingsingle_metricOne real value and a sentencenumber_first, statement_first, poster, orbit, milestonecomparisonSame measure in comparable periodscolumns, statementbreakdown2–4 meaningful componentsbars, bubblestimelineExplicit labelled items, not an inferred event historydots, bars, stackrecoveryAn evidenced comeback, not guessed activitydefaultnext_actionA real destination in your appdefaulttransparencyActual billing/access facts or a free-access endingdefaultratingOptional native review/store destinationdefaultshareAn approved summary without raw contenttext, trading_card, recapBuilder previews use the SDK renderer. The service checks declared SDK capabilities before delivering newer layouts or compact stories to a device. Content rules Use suppress_card for zero achievements. Never celebrate zero or missing values.Compare the same measure with comparable periods and coverage; two numbers alone do not establish comparability.Breakdowns require non-overlapping components. One cycle total cannot supply a daily timeline.Copy/art variants use the first matching rule, at most six per supported card; they cannot change numbers or access facts.No remote code, HTML, WebViews, arbitrary components or executable links. Missing artwork must not hide meaning.
Brand, assets & motion Use your brand’s colours and approved artwork without sacrificing readability or reliability. Token / assetBehavioraccentRequired hex colour for accents and controls.secondaryOptional secondary hex colour.logoUrl / assetsValidated HTTPS images; up to eight declared assets referenced by card asset IDs.motionstandard or reduced; honour system reduced-motion preferences.Tokens belong to a story version. Test contrast on-device; automatic colour selection is not an accessibility guarantee. Connect artwork once Open App Settings → Brand & artwork → Connect artwork. Copy the agent instructions or run the supplied recappel-visuals command in your app Git project with Node 24+.Review local thumbnails, public-use rights and prepared colour/font/media settings. Nothing uploads before approval.Paste the temporary code directly in the local page and select Approve & connect. Saved assets appear in Builder automatically; no manual import.Create Story chooses suitable approved artwork and ready fonts. Preview actual playback, image failure and reduced motion.Up to sixteen PNG/JPEG/WebP images, one MiB and 4096 pixels each; up to eight used assets per story. SVG/code graphics need a separately approved supported export. Fonts stay bundled in the host; unsupported families use the documented fallback. Changed files need new review. Personal pictures stay local Optional storyMedia providers return permitted current-account HTTPS avatars or galleries at playback. Only a binding description is saved in Builder. Pictures/URLs never enter Recappel telemetry, story JSON or AI. Builder previews use synthetic placeholders.Check permission and session before and after reads, respect cancellation and omit missing images. Avatar limit one; gallery up to eight. Sharing is excluded unless explicitly permitted. Authorized image-host requests and native image-loader caches still follow your host privacy policy. Copy-paste artwork handoff Connect this app's visual identity and optional personal images to its existing Recappel integration. Preserve values, billing/access, customer identity, permissions, storage, native customizations and unrelated work. Read the installed @recappel/react-native README and public declarations; verify the installed version matches its lockfile. This task requires the recappel-visuals command and configure.storyMedia when personal pictures are used. If missing, request a targeted SDK update through this app's package manager; never invent APIs or edit node_modules.DISCOVER LOCALLY Inspect the app's theme tokens, font registration, asset references, resource folders and model/selector types. Search by references and file type, not a required directory name. Respect Git ignores, root boundaries, permissions and secret exclusions. Do not inspect customer databases, print private image URLs or upload source code. Find public app logos, meaningful illustrations, category/activity icons, earned-badge art and subtle repeatable background textures. Record unsupported SVG, code-drawn, animated, remote or oversized artwork separately; only rights-cleared local PNG/JPEG/WebP files up to one MiB and 4096 pixels are accepted. A local raster export can be prepared when the app's existing tools and rights allow it; do not silently rasterize private content or add dependencies. Do not add fake assets to fill slots.Use the actual theme source for primary, secondary, background and surface colours. Fonts must be licensed, bundled and registered in configure.fontAssets under their exact registered family names; a family string alone does not load a font. Verify the local registration and font licence before confirming hostRegistered/licenseConfirmed. Custom families are supported: use up to 80 letters, numbers, spaces, underscores, dots or hyphens, with no surrounding whitespace, URLs, paths or executable content. The reviewed per-app catalog accepts up to twelve font entries with unique lower_snake_case IDs; system is reserved for the native fallback. Automatic authoring selects app-approved font IDs and resolves their exact family names, never arbitrary model-supplied families. The SDK loads at most eight safe local configure.fontAssets entries, prioritizing the story's registered heading and body faces before remaining entries. Older SDKs may require those faces among the first eight entries; verify the installed version. Font files stay in the host. Builder can show a preview fallback for a custom family it has not bundled; verify actual host playback before claiming font fidelity.PERSONAL IMAGES STAY LOCAL Discover existing permission-safe avatar/gallery sources from code and types, not actual customers. Wire optional configure.storyMedia providers into the existing configuration. Each provider receives { limit, signal } and returns a bounded list of { uri, alt?, shareAllowed? }. Use HTTPS URLs without credentials, at most eight images (one for avatar), and only the currently active customer's permitted source. Capture/check the exact host session and permission before and after async reads; abort/retire on logout, account switch, opt-out and backgrounding. Respect signal. Do not widen permissions, scrape contacts, use names in alt text, fall back to another account or return stale images. Return undefined/[] on missing permission, failed lookup or missing media. Sharing is excluded by default; shareAllowed:true requires separate existing authority to export that image. Do not log or persist returned URLs. The SDK does not upload them; native image-loader caches and authorized image-host requests still follow the host's privacy policy. No new consent is implied by installing this feature.PREPARE REVIEWABLE SETTINGS Create/update recappel.visuals.json locally using only verified app-wide settings: optional primary/secondary/background/surface six-digit hex colours, fonts: [{id,family,licenseConfirmed:true,hostRegistered:true}], and media: [{key,label,description,kind,metricKeys,limit,hostRegistered:true,permissionChecked:true}]. Omit unknown colours; use empty fonts/media arrays when none are ready. Media kind is avatar or gallery, keys are stable lower_snake_case, avatar limit is one and gallery limit is one to eight. Descriptions explain the source's meaning without personal examples, URLs or source paths. Only reference existing connected numeric metric keys with a genuine relationship; an avatar can accompany recognition without a metric. At most eight bindings. Never mark hostRegistered/permissionChecked true before implementing and testing them. This is an agent-prepared local declaration, not a file the customer must import into Builder.REVIEW ONCE, THEN SYNC Run the exact command from Recappel → App Settings → Brand & artwork → Connect artwork in this app's Git project: npx --no-install recappel-visuals . --endpoint <the supplied visual-sync endpoint>. Node 24+ and Git are required; the command ships in the installed SDK but is not part of the mobile runtime. Open its loopback review URL privately. It scans bounded tracked/unignored files; review any limit warning rather than assuming full coverage. The developer selects up to sixteen suitable public images, confirms rights/public use and reviews the colours, fonts and media descriptions. Use logo only for app identity, badge only as truthful decoration, and illustration for other appropriate decor. Never upload customer pictures, copyrighted covers without rights, source/config files or secrets as artwork.The developer obtains a fifteen-minute app-scoped code from Connect artwork and enters it directly in the local review page, never in chat, source or environment files. Approve & connect uploads only reviewed normalized image bytes and approved metadata. Builder refreshes automatically; no manual JSON import/export. Changed files/settings need new review. Existing conflicting settings are not silently overwritten: review those in Builder. Retry an uncertain upload without changing the files; it is idempotent. Public SDK keys cannot upload artwork. Removing an image from the current catalog does not erase it from already saved story versions.VERIFY AND STOP Test provider cancellation, timeout, permission revoke, account switching, backgrounding, expired/broken image and default share exclusion; typecheck and run relevant host tests. Keep source recipes locally. Report discovered/approved/connected/unsupported separately, plus exact missing rights or source gaps. Confirm saved artwork/metadata in Builder independently of local success. Create Story sees only approved public artwork and allowed brand choices; it selects compatible per-card art, optional subtle texture, palette and ready fonts while preserving reviewed values. Personal media resolves later in the host, never in AI or shared story storage. Builder previews use synthetic placeholders. Verify genuine host playback separately; do not publish a story, alter a store build or claim production readiness from tool success. Stop after visual connection and local runtime wiring. Assets and motion Upload licensed artwork through Builder’s scoped asset flow or use approved HTTPS assets. Do not send customer photos as decorative art.Keep text and values usable when images fail; test offline and slow connections.The renderer owns transitions, easing, interaction and reduced-motion behavior.Richer animation code needs a tested SDK/host update. Remote story data cannot supply executable animation code. If the app has an explicit owner-approved monochrome dotted paint, the optional pattern setting is {kind:'dots',color,opacity,dotWidth,dotHeight,tileWidth,tileHeight,alignment:'center',offsetX,offsetY}. Every field is required: color is six-digit hex; opacity is 0–1; dotWidth/dotHeight are greater than zero and at most 64; tileWidth/tileHeight are 4–512 logical pixels; each dot dimension must fit its tile; offsets are -512–512 logical pixels. Tile sizes mean pitch, not the empty gap. Preserve the actual ellipse dimensions, colour, opacity, centered alignment and source offset, including any source scale converted once into logical units. Never recolour an asset, infer pattern paint from an arbitrary palette, invent geometry, or upload customer input. This fixed native primitive is public brand metadata, not executable SVG/code or a raster upload. Review all fields before approval. The compiler preserves the approved paint rather than asking AI to create or modify it. Cinematic playback requires pattern-paint-v1; verify the installed declarations and use the approved package-update flow when missing. No pattern means no new paint. Legacy patternUrl textures keep their existing subdued rendering; when both exist, the explicit paint takes precedence. Runtime contrast safety may reduce opacity for readability; verify real-device appearance separately. Explicit dotted paint Optional brand.pattern preserves an owner-approved native dot paint independently of image assets or model palette choices. Its complete descriptor is {kind:'dots', color, opacity, dotWidth, dotHeight, tileWidth, tileHeight, alignment:'center', offsetX, offsetY}. Geometry uses logical pixels; tile dimensions are pitch, not gap. Preserve source ellipse proportions and convert any source scale once. Review the exact settings through recappel.visuals.json; the compiler does not let AI invent or recolour this paint. See the complete bounds and handoff. This cinematic capability requires pattern-paint-v1; older clients cannot receive these versions. A missing descriptor retains existing behavior. Legacy patternUrl textures remain supported and subdued; explicit paint takes precedence if both are present. Runtime contrast safeguards may reduce opacity, so separately verify real-device playback and export appearance. No executable pattern code, private content or customer-derived colour is accepted. Optional cinematic brand.ambientColors contains exactly three #RRGGBB strings for the built-in ambient glow layers. Supply verified theme colours in the reviewed recappel.visuals.json settings, for example "ambientColors": ["#24010F", "#E8C199", "#29101A"]. These are illustrative colours, not a required palette. No alpha, CSS gradients or animation code is accepted. Omitting the field retains the existing accent/secondary/accent fallback. Colour → Custom Palette exposes the three gradient colour rows when the story uses an opening/recap glow or has explicit ambient colours. Older stories show their current preview fallback colours without changing the saved story. The rows use the same staged confirmation as other colours. The renderer preserves its built-in motion geometry; card changes and colour edits do not restart the continuous ambient phase within the open presenter. Foreground/paused state and system or story reduced-motion settings still gate animation. A new presenter can start a new phase. Check contrast and native playback separately.
When the story appears Offer the story when the customer opens your app during a meaningful window—not at an exact background alarm. Choose a known event App → Audience controls who can send SDK activity. Story → Audience & timing controls who can see this story. Choose Free, Trial and Paid in any combination, optional plan keys, minimum values, timing and frequency.EventRequirementNext chargeExplicit automatic-charge date from lifecycle-v2.Access endsKnown access end; do not imply a charge.Invoice dueActual due date, distinct from period/access end.Post-purchase checkpointReal purchase/access anchor, not a fake renewal.Monthly / App milestoneNon-billing timing; required when Free is included. Current timing behavior Selections in one list are alternatives; different restrictions must all match. Free OR Paid, on Pro OR Premium, with highlights ≥ 3 AND books ≥ 1. Missing values do not satisfy a minimum, even zero.Monthly/manual audience stories use UTC calendar-month buckets. App milestones require the host’s present({ manualMilestone: true }) call. The SDK requests stories while the app runs; publishing does not push open a closed app or guarantee delivery at midnight. Suppressed states Wrong environment or missing/stale access facts. Paid/trial also require active access without grace/retry/refund; explicit Free is a separate path.Insufficient data, missing required previous period, frequency cap or holdout.Unknown required date: omit the unsupported moment; never infer it from a plan name.Cancelled renewal is not necessarily lost access. Use the real entitlement state.Keep tested data thresholds, comparison-group percentage and observation windows. Do not bypass them to force a test story.
Invitation & placement Place the invitation in your app. Builder appearance controls must not rearrange your host screen. Mount one Gate or Host in a natural place such as the library or account screen. Avoid checkout interruptions, destructive actions and empty space when hidden. The host owns placement. Builder controls supported banner copy, colours, artwork and styling; the SDK renders the compatible definition. Gate props include renderBanner, onOpen, onDismiss and placement. Do not treat the placement label as a measured exposure. Published beta.15 behavior Shared-preview admission returns true before playback. Gate queues the pinned preview in its existing invitation banner, not an automatic modal. Hidden/background placements wait in memory; a banner tap rechecks current local facts and expiry. Host-only retains direct preview opening. Do not redeem again or call present() to open a pending Gate preview. Custom invitation Mount Host and show your native invitation only when canShow() returns true. On tap, call present(); it checks eligibility again before opening. Call canShow() for comparison customers too: it records eligible holdout exposure. Recheck relevant host-state changes and catch failures. Verify on the device Test appearance, dismissal, long copy, accessibility and smaller screens. Invited means prepared, not viewed. Do not turn that event into a banner-impression count. Gate responds to supported state changes and rechecks when tapped. Test access changes, account switching and foreground refresh. Offline limit Server pause/revocation reaches the device on refresh. It cannot instantly erase bytes from an offline phone. 5. Show and measure When the story appears Results & measurement Previous: When the story appears Next: Actions, rating & sharing
Actions, rating & sharing Connect each story action to a real destination, and measure taps separately from completed outcomes. Destinations and access management Use tested deep links. Pageling’s “Revisit your highlights” should open its existing library, not promise an unbuilt feature. Keep transparency before social asks. Use the real management destination appropriate to the access model and provider. Free/one-time access must not imply a recurring subscription. Test both platforms; a saved link is not proof that the destination works. Supply an action only when the current app session can open its real destination: for example, View plans for available purchase options or Manage subscription for an existing subscription. A compatible presenter closes the story modal before handing off once to that destination. Verify the destination actually opens; a tap alone does not prove a purchase or subscription change. Published beta.15 adds local managementAction.returnToStory:true with onOpen: () => Promise<void>. Settle only after the actual host destination closes, including cancellation—not after navigation or foregrounding. The SDK hides its modal first, then resumes the same card only for the current foreground session. Omit the option for existing dismiss-before-management behavior. Do not guess closure with a timer; account/access/permission retirement still wins. Verify installed declarations and actual platform behavior. Native hooks Native review remains an optional host hook. SDK releases declaring RecappelPosterShare include an autolinked Android/iOS PNG module: no separate sharing package or custom capture callback is required. Run the normal native rebuild when first adding the module; Metro reload and Expo Go cannot add it. An SDK-only update from beta.12 to beta.13 uses the same native code and does not require a new native rebuild. Existing capture/share hooks remain optional overrides. Verify the installed SDK, actual binary and updated bundle before testing. Android uses bounded SDK-native capture at the clean poster’s physical-pixel dimensions, including supported TextureView artwork on API 24+; it does not depend on the host’s optional Skia screenshot hook. iOS retains the existing Skia capture path. Unsupported or oversized captures report unavailable, not a partial or silently reduced image. Android holds one capture through cancellation and cleanup. Its ten-second deadline is checked around platform work; an already-running GPU read cannot be interrupted. No storage permission or arbitrary host-file access is added. A sharing failure can instead mean that an installed module’s activity/window or capture surface is temporarily unready. Check capability before rebuilding; never add a second sharing package. The published beta.16 correction distinguishes temporary readiness from a missing module and retains the story for a deliberate retry; it is not in published beta.15. Never automatically open another sheet after an uncertain result. What a tap proves Rating acted means tapped—not a displayed OS prompt or posted review. Suppress a card without a valid review destination. For the cinematic poster, known cancellation is not completion. A successful OS handoff is not a published post or delivery to another person; destinations depend on installed apps. A missing native module or older binary without the new Android capture reports unavailable; no text-only fallback or second sheet after an uncertain result. SDK temporary PNGs are bounded to 4096px per edge and 8MiB encoded. Android capture is limited to 4MP and 48MiB of accounted raster buffers; the iOS image limit remains 16MP. At most eight files are retained. Handed-off files become eligible for cleanup after one hour, on a later capture or OS cache cleanup—not an exact deletion timer. Share only explicitly approved figures, artwork and wording. Existing externalSharing:false recaps remain private; local-only leaders are excluded from exported recaps. The published beta.15 cinematic preview exception below remains subject to existing private-recap and session guards. The image is not uploaded to Recappel; the chosen recipient app receives it. Values review and Edit value provide an explicit Allow in share cards choice for supported numeric and public structured facts. Save the approval before creating a new story. Every fact actually referenced by its recap must be approved; one missing approval keeps the new recap private. This does not change existing private stories or permit local-only winning tags to be exported. SDK session retirement cancels queued native sharing before OS handoff. Once handoff starts, the recipient’s access cannot be recalled by closing the story. Custom sharing overrides retain responsibility for their own session safeguards. Published beta.15 preview exception Explicit cinematic preview image sharing respects externalSharing:false, native capture limits and current-session guards, with no acted/outcome/Results credit. Existing private recaps stay private. Preview media is synthetic; sender values and customer pictures are not transferred. Rating, ordinary external navigation and classic text sharing remain suppressed. Publication is not native/iOS acceptance. Cards & layouts 5. Show and measure Data handling & deletion Previous: Invitation & placement Next: Sandbox & test devices
Test & publish
Sandbox & test devices Test one story version without reaching Production customers. Payment-store testing is separate. Pair a test device Open this app’s Sandbox in Builder and create a pairing code. In the configured host app, pass the code through a deliberate tester action to await Recappel.pairSandbox(code). Codes are one-use and expire after 10 minutes. Pairing identifies the configured opaque Sandbox test customer, not a device or billing account. Paired does not necessarily mean eligible. Select an immutable saved version and review its test conditions in Builder. Run Test compiles that version if needed, enables its Sandbox delivery, then opens its receipt-based test status. Enablement does not force playback. Shared preview is different A shared preview link/QR opens its selected saved story with the recipient’s own local values. It does not bypass host sign-in, earn Sandbox test completion or count toward Results. Registering a native scheme is not enough: the host must also forward launch and incoming URLs to its ready preview controller. Choose a saved version, link duration and maximum link uses, then Create Link & QR. The default is Sandbox, one day and ten uses; Production must be selected explicitly. The success screen shows the selectable URL, QR code, expiry/use limit, and deliberate Copy Link / Share Link actions. Copy it before leaving: Builder does not show the bearer URL again in link history. History lets the owner inspect use counts and revoke an active link. The expandable Preview setup area is one-time host setup, not a per-version task. A valid saved prefix reuses the owner’s earlier confirmation. A new or changed prefix requires confirming that the scheme is registered and launch and incoming URL forwarding is implemented; a scheme alone is not enough. This is not a claim of successful redemption. Create the link, then verify the installed app’s cold and warm paths. Builder reuses the exact version’s compiled outcome, or one unambiguous outcome already configured across the app’s non-preview compiled versions. It asks for the real event key only when that meaning is missing or ambiguous; entering a key does not wire an event in the host. Never substitute preview_only or an invented event to complete setup. Register a real custom native scheme in the host, then the matching yourapp://recappel-preview prefix in Builder. Package/bundle IDs alone cannot open an app. Scheme changes may require a native rebuild; Metro cannot register a scheme. No customer installation or native acceptance is claimed here. Registering the scheme only launches the host. Its existing router must also forward both the cold-start URL (Linking.getInitialURL()) and warm incoming URL events (Linking.addEventListener(‘url’, …)) to the same preview controller. Do not add a second competing listener or let the ordinary router consume the preview URL without forwarding it. Verify both paths on an installed build. The call to Recappel.openSharedPreview(url) is only the final step of an already-ready session, not a complete startup listener or production bridge. The host-specific controller must satisfy the following lifecycle contract: Route only its exact registered scheme and preview route. Before calling the SDK, finish account restoration, obtain required permission/consent, configure the intended project/environment/customer, supply current local access and values, and mount the ready Gate/Host presenter in the foreground. Use one consistent authoritative identity source for startup and later updates. Model unresolved startup separately from confirmed sign-out; a synchronous snapshot and an initial UI prop must not become competing authorities. Never let a stale placeholder null cancel a valid pending startup request or replace an already-resolved identity. Do not redeem while account restoration is unresolved. An authoritative signed-out result still retires the request; ask the user to reopen deliberately after sign-in. If a cold or warm URL arrives before readiness, hold only a bounded, short-lived pending request in memory, with a fixed queue size/expiry policy. Resume it when the same controller session becomes ready, not on a timer that blindly calls the SDK. Do not persist the URL in navigation state or storage. Coalesce duplicate initial-URL and incoming-event deliveries for the same attempt. Keep bounded in-memory deduplication and mark the attempt claimed before the SDK call; do not call it from every render/readiness notification. Retire pending work and ignore late initial-URL/readiness/redemption results on sign-out, account/project/environment change, consent withdrawal or controller disposal. Never replay a prior startup URL into a replacement account or remounted controller. If ownership cannot be proved, discard it and ask the user to open the link again deliberately. Treat ordinary backgrounding or temporary presenter unreadiness as a pause, not an account change. Do not drain while unavailable or reconfigure with enabled: false merely to model that pause; revoking SDK authority can retire an existing story. Recheck the same authority before resuming. Once openSharedPreview has been invoked, do not automatically requeue/retry on false, timeout, network uncertainty or a late lifecycle change. A server redemption may already have consumed a use. Show a non-sensitive result; another attempt must be a deliberate user action and may consume another use. Replace yourapp with the registered scheme. Links require exactly a 64-lowercase-hex token and UUID appId, no path/fragment/credentials/duplicate/extra fields or dangerous scheme, maximum 512 characters. Never log/persist complete links or tokens, put them in analytics/crash breadcrumbs, or include them in diagnostic errors. openSharedPreview validates the pinned story/app and uses only this recipient’s local facts. Its request contains only project key, environment and token—not customer ID, values, lifecycle or device ID. It returns false if rejected/unsafe. Default allowance: ten redemptions; duration: one day/week/month or no expiry (Forever, still bounded by the use limit and revocation). Redemption consumes allowance even if local gates later prevent display. Published beta.15 behavior: true means admitted to the presenter, not opened or viewed. Gate stores the pinned preview invitation in memory and shows it in its normal banner placement; opening requires a deliberate banner tap and fresh local access/value/expiry checks. When renderBanner is false or the app is backgrounded, it waits until that placement is available. Host-only integration retains direct preview opening because it has no invitation UI. Do not call present() or redeem again to open an already-admitted Gate preview. Test admission, banner visibility, tapping and playback separately. Published beta.16 preview request feedback: Gate shows Loading, Ready to open, or an error after a valid SDK attempt begins. Loading uses a gentle indeterminate pulse, not a percentage or download estimate; Reduce Motion keeps it static, and background/hidden placement stops the motion. Ready means a validated presentation resolved against recipient facts is available, not that every native font or image is loaded. An explicit Open tap rechecks current facts and expiry; no automatic Gate playback or Results credit is added. Host-only retains its existing direct-opening contract. The host still owns pre-attempt startup, account restoration, consent and readiness feedback. Only retryable errors offer a deliberate retry. An uncertain attempt may have consumed an opening; another attempt may consume another. Never automatically retry or requeue redemption. A retry input is retained for at most five minutes per attempt in private memory, never observable state, UI props, storage or diagnostics; replacement, dismissal, retirement, disposal, success and terminal failure clear it. Session changes and late results cannot revive an old request. Missing module errors require checking the installed SDK/native binary and the normal first-install or native-change rebuild—not a separate sharing package. An existing activity or capture surface can instead be temporarily unready; inspect capability before rebuilding, keep the story for a deliberate retry and never open a second sheet after an uncertain result. Verify actual media/font readiness, native feedback, PNG output and cold/warm links separately; these source changes are not publication or device acceptance. Shared previews bypass schedule/date, frequency cap and holdout only. Inactive access, billing trouble/overdue/refund, stale facts, environment/profile and zero/minimum-data gates remain. No exposure/outcome/show/assignment, runtime-cache change or paid-seat activation is emitted. Rating, ordinary external navigation and classic text sharing remain suppressed. In published beta.15, explicit cinematic image sharing respects existing externalSharing:false, current-session and native capture guards, with no acted credit. An explicitly configured local managementAction.returnToStory:true may perform its guarded host round trip; its promise must settle only when the destination closes. These exceptions are not remote permissions or automatic actions. Host onOpen is not a preview-open receipt; local banner dismissal can notify onDismiss. Neither is Results credit. Server revocation cannot instantly recall bytes on an offline device or an already-started OS/host action. Tester identity Developers sign in to Builder; testers sign in to the host app if required. Builder email OTP does not match someone to the host database. Pair the device/session with its opaque host customer ID. Keep test controls away from ordinary customers. Configure payment-store testers separately; test purchases cannot unlock paid Production coverage. Test the selected version Open the installed Sandbox host with the paired account. Confirm the received immutable version, refresh access/values and inspect eligibility under the actual data/access/timing rules. Tap the invitation and finish the story. Verify visible invitation, opening, completion and dismissal separately. Builder advances from available to opened to completed only from the matching server receipts. Test destinations and the independent outcome. Return to Builder or use Refresh Status to read the exact-version opened/completed receipts. Loading, failed reads and enablement alone do not establish completion. Record build, version, scenario and whether each check was manual, local or server-observed. Example-number previews are not device tests. Include little/zero data, no prior period, billing trouble, wrong environment, expired pairing and offline cases. Waiting alone does not guarantee background delivery. With version-scoped Sandbox delivery, a newly selected saved version needs its own invited, opened and completed evidence. An older version’s completed receipt is not proof that the new version arrived. Match the immutable version ID as well as the app and environment; a per-story Version 1 label is not an identifier. Production frequency limits remain unchanged. Already-enabled or completed versions offer View Test Status without enabling again. Re-enabling does not reset the show allowance. Save another immutable version for a new test, or use shared preview for read-only replay. Connect in five steps Versions & Production Troubleshooting Previous: Actions, rating & sharing Next: Versions & Production
Versions & Production Publish a tested version explicitly. Saving, testing or paying never publishes it automatically. Prepare the customer build Keep the working Sandbox configuration for testing. In your customer-release profile, use this app’s Production key and service URL from Builder, with environment: production.For paid/trial access, supply genuine PRODUCTION lifecycle facts from the matching source; never relabel a test purchase. Free customers use explicit audience facts.Use the same initializer and mounted invitation in the release build. Verify session changes, permission, persistence and native playback before shipping it.Ship the host-app build through your normal release process. It can connect before any story is published.There is no publish/enable code switch. Once that configuration is installed, publication is controlled in Builder. Changing a key or adding SDK/native capabilities requires a host-app update; compatible story edits do not.Confirmed value meanings/sources and valid card/brand schema.Truthful transparency, destinations, timing, audience, data and group rules.This exact version tested; unresolved blockers visible.Verified Production capacity assigned. Test purchases do not qualify. Publish the tested version In Builder, open Story → Review publication. Resolve any listed blockers.If this app has no coverage, choose an available plan and wait for verified capacity.Publish the tested version unchanged. Saving a draft or paying does not publish it.Check the live version and publication time, then monitor received activity separately.One publication personalises the eligible audience with each customer’s values. It is not a per-customer charge or guaranteed impression; access, timing, data and holdout still apply. Edit, pause or roll back ActionEffectEditCreate a draft; leave live unchanged.ReplacePublish a tested immutable version; preserve history.PauseStop new invitations as devices receive the updated state.RollbackSelect a compatible previous version and pass publication checks again.Delivery is not instantBootstrap refresh is due after 15 minutes, with a maximum playable cache age of one hour. A network failure cannot extend it indefinitely. Live means published, not received on every device; an offline phone cannot be remotely cleared instantly.
Understand & manage
Results & measurement See story activity separately from evidence that offering it changed the chosen outcome. Story activity MeasureDefinitionInvitations preparedEligible invitations prepared; not proof they were visible on-screen.OpenedUnique customers who opened within the selected scope.CompletedUnique completions; completion rate = completed ÷ opened.Action tapsNext-action, review and share taps—not downstream success.Filter by date, environment and version. Show counts with rates and matching deduplication scopes. No activity means No activity yet—not invented uplift. Delivery diagnostics and active-audience usage are separate from story performance. Outcome comparison Compare all appropriately assigned story and comparison customers, not just openers. Assignment is deterministic and sticky per experiment.Outcome count / eligible assigned count for each group.Percentage-point difference, uncertainty and observation window.Collecting, Inconclusive or Data incomplete when evidence is insufficient.Define eligibility, assignment, attribution, sample-size approach and outcome source before launch; a random split alone is not enough. Measurement limits Client-recorded outcomes are unverified. The separate authenticated server-outcome path needs an actual trusted host-backend event and correct experiment/cycle attribution. Server-authenticated does not mean independently payment-verified. Renewals may occur without another app session.Current events cannot prove per-card dwell/drop-off, rescued revenue, posted reviews or completed shares. Do not relabel a prepared invitation as a view. Subscriber trends alone do not prove story impact.
Settings, plan & billing One monthly Recappel audience allowance is pooled across up to ten live apps; each app keeps separate story controls. Drafts and Sandbox remain free. Test purchases cannot grant paid Production coverage. Approved monthly USD list prices are $29 for 10,000 active customers, $99 for 100,000, $299 for 250,000 and $499 for 500,000. These reference prices are not native store offers. One account subscription pools the audience allowance across up to ten covered live apps. Ten apps is an included limit, not a per-app price multiplier. A paused covered app still reserves its place; iOS and Android editions of one product count as one app. New shopping is monthly only. Existing app-slot and annual subscriptions remain manageable; they are not automatically migrated, cancelled or replaced. No larger tier, AI quota or unlimited plan is approved. Active customers this month counts each accepted Production app + opaque customer pair once per UTC calendar month after a value sync or story-availability activity. Repeat activity and additional devices do not count again; the same customer in two apps counts twice. Sandbox, shared previews and invalid requests do not consume paid audience. The month resets at 00:00 UTC on its first day, separately from store renewal. Changing tiers, pausing, changing filters or deleting an app never resets or refunds accumulated usage. Where to manage it Location Controls App → Settings Connection, public keys/environment, profile, privacy and this app’s coverage. Account → Plan & billing Audience allowance and usage, included live-app capacity, covered apps, renewal/status, change plan, manage and restore. Story → Review publication Version checks and Publish. Payment appears only without verified coverage. What each action changes Action Effect Pause story Stops invitations after refresh. Place and bill unchanged. Remove coverage Used seats retain a 30-day cross-app reassignment cooldown. The same app may resume; deactivation/deletion cannot rotate one used seat between apps. Reduce plan A same-store audience reduction takes effect at renewal. Upgrades require verified store confirmation. Use the confirmed effective date, never guessed proration. Cancel renewal Stops future renewal; verified access lasts to its confirmed end. A configured audience allowance uses a finite 5% buffer, then stops new admissions in arrival order. Already-admitted customers keep ordinary safety checks. Tier changes preserve accumulated usage, and an expired or exhausted allowance never becomes unlimited access. No automatic overage charge occurs. Legacy-to-audience and cross-store changes remain management-only until a reviewed store transition is available; do not create a second subscription to migrate. Recappel billing is separate Builder uses its own store catalog and RevenueCat. Adding an app creates no new store listing or subscription product. That app can use any provider; its customers do not subscribe to Recappel. This website has no checkout, store links or restore service. Audience checkout remains unavailable until the exact localized native offer and enabled server catalog are verified for the authenticated owner, app/store identity, monthly cadence, ten-app capacity and audience allowance. Approved list prices do not grant access. Legal URLs, payment lifecycle and sustained service capacity require separate release validation. Versions & Production Data handling & deletion 3. Pass billing and access Previous: Results & measurement Next: Data handling & deletion
Data handling & deletion Send only what the story and measurement need. Keep personal content and billing secrets out. Request Data App enrollment Public key/environment before personal requests; coarse audience kind and optional plan key accompany permitted activity. Bootstrap Public key, environment, opaque customer ID, SDK/capabilities, schema version and cache tag. Aggregate sync App/customer/device scope, cycle, metric keys/methods, numbers or safe labels and revisions. Story interactions / diagnostics Bounded experiment/version/group/cycle, supported interaction or diagnostic code and time; diagnostics are optional. Outcome Outcome key, cycle, experiment/group and source; client reports are unverified. End-user deletion App/environment and opaque customer ID. Detailed billing facts remain host-side; cycle identifiers and approved aggregates may still leave the device. Story/outcome events have timestamps and artwork may load from HTTPS hosts. Do not describe the SDK as entirely on-device. Structured values: public definitions, availability status and revisions can reach the service within the existing app/environment/customer/device scope. Structured numbers stay on the device in account-scoped storage; ordinary scalar aggregate telemetry is unchanged. No customer-written categories, numbers, examples, source paths or history rows belong in definitions. Account, permission, access and late-callback guards still apply; this feature is not permission for new collection. Value definitions: public app key/environment, stable metric keys, display names, descriptions, numeric counting rules and supported semantics. These contain no customer identity, source code, individual values or owner approvals. Review reported meanings in Builder before using them in a story. Excluded data Names, email, phone, advertising IDs and provider customer IDs. Passages, notes, messages, photos and arbitrary labels. Payment credentials, receipts, transaction objects and provider secrets. Full source repositories sent to the authoring service. Valid label syntax is not a privacy filter: ordinary uploaded labels require approved enums. Permission-reviewed local-only structured leaders use their dedicated API, not setLabel or public definitions. Authoring uses reviewed app context/value definitions; future cohort summaries need privacy thresholds and review. The optional local artwork tool uploads only images and bounded theme/font/binding metadata that the developer approves for public use. Source files, paths, ignored files and unapproved candidates stay local. Approved public artwork can be sent to the model during story creation; Values Rewrite does not receive images. Optional personal-image callbacks load from the host-approved HTTPS service during playback. Those URLs/photos are not sent to Recappel telemetry, saved stories or AI. Preview uses synthetic media. Account/permission checks remain the host’s responsibility; late callbacks are discarded, but already-started network requests and native image caches cannot always be recalled. Sharing requires explicit shareAllowed permission. Optional local-only leader snapshots keep the actual winning label and count in the host’s account-scoped device storage. Recappel receives static definitions and availability, not those names or counts. A leader may appear as a captioned supporting stack tile on compatible versions, never as a lead or exported recap fact. Preview uses Example; this is not permission for new collection. Optional valueRelationships reports source-verified static equal/subset metadata with exact definition revisions, not customer readings, examples or source code. It does not change ordinary aggregate telemetry or grant sharing approval. Approved poster sharing writes a bounded temporary PNG and hands it to the OS sharing interface only after a deliberate tap. The SDK does not upload that image to Recappel or post automatically. Existing externalSharing:false recaps remain private; local-only leaders are excluded. Queued native sharing is cancelled on SDK session retirement before handoff, but a recipient’s access cannot be recalled after handoff begins. Handed-off files become eligible for cleanup after one hour on a later capture or OS cache cleanup, not an exact deletion timer. Published beta.16 getLocalBreakdown is local-only. Published beta.18 also resolves the current recipient’s exact cached partition during genuine shared-link playback, pinned to the current owner, session, observation and consent. Labels never come from the link, server or another owner, and never enter persistence, telemetry, AI or poster/share-safe exports. Synthetic mock previews still exclude the getter. Canonical aggregates, saved stories, leaders and recap facts are unchanged. managementAction.appearance also remains local configuration; bundled host CTA artwork is not uploaded to the public artwork catalog. Neither feature grants collection or export permission. Verify the published and installed version before relying on beta.18 behavior. Delete a customer Call from an authenticated, confirmed deletion flow—not sign-out. It targets the current opaque customer, wipes local state and leaves a tombstone that stops tracking. Server deletion and policies A resolved deleteEndUser() is not proof of server erasure, cross-device cleanup or legal compliance. The request is best-effort. Production needs authorized confirmation/retry, retention rules and handling of queued traffic from other devices. Before collecting data, publish approved privacy/terms, retention, processor/security and support details. Customer identity & sessions 4. Send useful values React Native API reference Previous: Settings, plan & billing Next: React Native API reference
Reference
React Native API reference Use the installed declarations for exact fields and supported types. configure(config): void Field Contract projectKey Public app/environment key copied from Builder, not a private credential. environment sandbox or production. userId Stable, host-owned opaque string. baseUrl Service URL provided by Builder; required for fetch. transport fetch by default; mock has no network evidence. enabled Host enrollment/permission decision; false stops ordinary activity, not explicit privacy deletion. audience Explicit free/trial/paid, genuine observedAt and optional approved profileKey. locale / lifecycleMaxAgeHours Supported locale and freshness policy; default access freshness 72 hours. storageAdapter Optional getItem / setItem / removeItem adapter. review / shareCapture / onShareImage Native review is an optional host hook. In SDK releases declaring RecappelPosterShare, capture/share callbacks are optional overrides of the bundled PNG path; see Actions for first-install native build steps. An SDK-only update from beta.12 to beta.13 uses the same native code and does not require a new native rebuild. Android uses bounded SDK-native poster capture, separate from optional host screenshot hooks; supported TextureView artwork requires API 24+. iOS retains the Skia capture path. Older binaries report the new Android capture unavailable until rebuilt. No additional public capture API is required. Returns void, not a ready promise. Later calls queue behind initialization; calls before configure can fail. Reconfigure deliberately when session/access/consent changes, not on every render. refreshAccess(input): Promise<boolean> accepts exported AccessRefreshInput : exact projectKey , environment , userId , audience: AudienceContext , optional lifecycle: LifecycleInput (required for Paid/Trial, absent for Free), and required maxAgeMs . After a successful authoritative same-account access renewal, use refreshAccess only when the installed public API declarations support it. Do not call configure on every successful access refresh or retire the permitted session merely because a routine authority lookup starts. Keep the original freshness deadline/expiry and its hard-expiry handler active during both the authority lookup and SDK await; recheck expiry, identity, permission and cancellation afterward. While checking, cached facts must not receive a new timestamp. Required maxAgeMs is a positive safe integer bounded by configured SDK/story freshness; use the authority’s documented shorter freshness policy, not an invented timer. Paid/Trial needs equivalent active, unexpired, nonsuppressed lifecycle facts with strictly newer audience and lifecycle observations; Free has no lifecycle. refreshAccess is only for strictly newer, still-fresh observations of equivalent audience, profile and lifecycle facts in the exact already enabled/enrolled/unblocked project/environment/customer context. Only observedAt may change; both old and new observations must remain fresh. A true result preserves the session, caps and current player; it does not automatically bootstrap or prove a server receipt. A false result requires the normal configure recovery path after resolving current permitted facts; never replay captured old-account configuration. Matched invalid, changed or expired facts disable admission/presentation; a mismatched account cannot mutate the new account. Real identity, permission, plan or lifecycle changes still use deliberate reconfiguration. Older packages need an approved upgrade for this API; never cast, call private methods or assume newer docs upgraded the installed binary. Both audience and applicable lifecycle observations must be nonfuture UTC and strictly newer. Age equal to maxAgeMs is expired; its maximum is the smaller of configured lifecycleMaxAgeHours (default72) and the active story freshness window, in milliseconds. Backward clock movement during the check is rejected. The SDK never rewrites timestamps or renews remote admission TTL; refresh() separately revalidates delivery. valueDefinitions?: readonly ValueDefinition[] adds names, short meanings and numeric counting rules to the existing configuration. The SDK sends them automatically, with bounded retries. Keep one stable array of at most 32 unique keys; no second initializer or file import. Required fields: key, name, description and aggregation (sum/max/latest). Optional revision starts at 1; increase it when a meaning changes. Optional scope, behavior, unit, meaning and direction use the exported enums; leave unknown facts unknown. Never include customer data, examples, source paths or approval fields. structuredValueDefinitions?: readonly StructuredValueDefinition[] adds up to 8 separate groups alongside 32 numeric definitions. Each reviewed group counts as one of the 3–5 logical values selected for an initial AI-generated draft. With compatible Builder/service support, manually edited stories may use up to 12 reviewed logical values, within existing 9-card, per-card metric, runtime and privacy checks. Check installed declarations and preserve existing scalar keys; older builds retain their own limits. Public kind is ratio, distribution, series or local-only leader; group/category keys are lowercase snake_case up to 32 characters. Required common semantics are scope, unit, meaning and direction. Revision defaults to one; maxAgeHours is 1–168, default 24. valueRelationships?: ValueRelationshipManifest is an optional {revision,relations} manifest with at most 32 source-verified equal/subset assertions. Each from/to reference names kind, key and exact definition revision; distribution_category also names categoryKey. Numeric kinds are scalar, ratio_numerator, ratio_denominator, distribution_total, distribution_category, series_total and leader_value. Both sides require the same actual scope and explicit unit, unitKey, populationKey and windowKey. Subset means from is no greater than to. Never infer relationships from names or examples. Increase revision on changes; a newer empty list revokes, omission preserves older-client compatibility. Check installed SDK and compatible service/Builder support. fontAssets?: FontAssets uses the exported bundled-font registry type. The updated type accepts number | readonly number[] per unchanged registered family: an existing module ID or one to four intrinsic faces, with eight local faces loaded in total. No font download or host registration is performed for you. The array form requires a newer SDK than public beta.11. Verify the installed FontAssets declaration before using arrays; use single IDs with beta.11. storyMedia?: Readonly<Record<string, StoryMediaProvider>> supplies optional personal-image callbacks. Each callback receives { limit, signal } and returns images with HTTPS uri, optional alt and optional shareAllowed (off by default). Check current account and permission before and after asynchronous reads. Binding keys are app-level metadata; customer images and URLs are not. See visual stories for the local review tool and complete universal handoff. → Published beta.15 addition: managementAction?: { label?: string } & ({ returnToStory: true; onOpen: () => Promise<void> } | { returnToStory?: false; onOpen: () => void | Promise<void> }) . It is local host configuration, never remote story code. With true, settle only when the real destination closes, including cancellation—not after navigation dispatch or foregrounding. The retained card resumes only for the same current foreground session. Omission/false preserves existing dismissal. Check installed declarations and native behavior. Published beta.16 additions: getLocalBreakdown?: (request: LocalBreakdownRequest) => LocalBreakdownSnapshot | undefined . Request is {key, revision, observedAt}; revision is the semantic definition revision, observedAt the exact committed canonical observation. Return a synchronous already-cached {observedAt, total, items:[{label:string|null,value:number}]} for the same owner/generation and consent, population, unit and category precedence. Equal totals alone do not prove provenance: never redate a newer read to an older deduplicated timestamp. Build and retire the cache in the existing host pipeline; return undefined for unavailable exact cache/permission, never query, fetch or reconfigure in the getter. At most 32 complete rows including zeros, exact total/sum match, finite nonnegative values at most 1e12, safe integers for count units. Plain data only; safe Unicode labels at most 80 code points / 320 bytes, unique after trimming/NFC. null means genuinely missing: Not assigned, spoken No category assigned; real Other and Unclassified stay distinct. More than eight positive rows use the top seven plus N remaining categories and the exact remainder sum; generated-label collisions reject the supplement. One positive structured category is valid; legacy unstructured rules are unchanged. Invalid/missing local data keeps the canonical chart. In published beta.18, ordinary presentations and genuine shared-link playback may resolve only the current recipient’s exact local snapshot, pinned to the current owner, session and observation. Synthetic mock previews exclude the getter. No labels come from a link, server or another owner. No persistence, export, AI or telemetry; canonical aggregates, conditions, leaders, saved stories and recap facts are unchanged. Verify the published and installed SDK before relying on beta.18 behavior. Published beta.16: managementAction.appearance?: ManagementActionAppearance keeps the genuine label and host callback. Optional artwork is a numeric registered bundled raster module, for example static require of the host’s approved offline PNG export; no arbitrary style, remote URI, SVG string, component or render callback. Opaque #RRGGBB backgroundColor/textColor/borderColor; borderWidth 0–8, borderRadius 0–999, minHeight 48–160, paddingHorizontal/paddingVertical 0–48, fontSize 12–32, fontWeight 100–900 in steps of 100, hideIcon?: boolean. No font loading or purchase execution. Pass only the real purchase-entry artwork and matching accessible label, never an invented offer/price. One SDK-owned Pressable uses contain/intrinsic aspect, at most 480 points wide / 160 high and minimum 48-point press target; registered image dimensions must be positive and at most 8192 with aspect 1–8. Missing/invalid assets, image errors and large text use the scalable label fallback. Valid artwork has no duplicate text/icons. Published beta.18 also omits the unpaid Free row and no-payment copy: use one genuine custom host purchase control, or one functional View plans fallback only when the host supplies its real callback. Never invent a plan, trial, price or active subscription. Paid/trial/access and billing-trouble clarity remains intact. With returnToStory:true, onOpen settles only on actual destination closure, including cancel—not navigation dispatch or a timer. The paused scene clock, measured Clarity layout and scroll survive Modal child disposal; current owner/access retirement wins. Test artwork, contrast, large text and same-card return on device. These local corrections add no native dependencies; bundled sharing still needs the normal first-install/native-change rebuild, not a separate package. setLifecycle(dto): void Input Meaning LifecycleDTO Backward-compatible recurring facts: entitlementActive, purchaseDate, expirationDate, willRenew, periodType, store, environment and observedAt. LifecycleDTOv2 lifecycleVersion: 2; explicit accessModel, paymentCadence, collectionTiming, renewalMode, phase, channel, profileKey, lifecycle/period identifiers and known dates/amounts. Known dates Period end, access end, next charge and payment due have different meanings; leave unknown facts null as the contract permits. Environment / freshness SANDBOX / PRODUCTION and genuine source-observation time. Trouble flags Pass known grace, retry, overdue and refund states; never omit them to force eligibility. Free access Supply explicit free AudienceContext; never fabricate a subscription. The exported LifecycleInput union accepts both DTOs. Detailed facts remain local; inspect installed types and step 3 before mapping. Metric methods: void Call Effect increment(key, by = 1) Add to cycle sum. add(key, amount) Add amount to cycle sum. set(key, value) Replace snapshot; decreases allowed. setMax(key, value) Retain cycle maximum. setLabel(key, label) Replace approved enum label. Keep one method and meaning per key. Updates queue after configuration and sync as snapshots; repeated business events are not automatically deduplicated. setStructuredValue(key, snapshot): void takes the exported StructuredValueSnapshot. Every reading has status and genuine UTC observedAt. Ratio reads numerator/denominator and precision 0–3; zero denominator is unavailable. Distribution declares 2–24 fixed public exclusive/complete categories and reads all counts plus their matching total; top three plus Other preserves coverage and ties have no unique leader. Series declares rolling scope and 2–4 completed contiguous day/week/month windows, with timeZone, startDay, endDayExclusive and value. Weeks start Monday; the final window ends at the current period boundary. Missing windows make the group unavailable. Explicit unavailable clears prior local group values; maxAgeHours expiry prevents stale display. A supported leader declares static valueLabel/labelName, ties:’unavailable’ and labelPrivacy:’local_only’; its snapshot carries one unique winning plain-text label plus a positive integer count. Ties, zero or invalid labels are unavailable. Labels are limited to 80 code points and 320 UTF-8 bytes; actual names/counts stay local and cannot bind into exported recaps. Never invent labels/history or refresh timestamps on stale data. Presentation and diagnostics Call Returns / behavior canShow() Promise<boolean>; checks treatment eligibility and records holdout_eligible when appropriate. checkEligibility() Promise<EligibilityResult>; diagnostic, without holdout exposure. present(options?) Rechecks eligibility; requires Gate/Host. manualMilestone must be an explicit host action. flush() Attempts delivery; not proof of server receipt. refresh() Requests refreshed enrollment/bootstrap state; check its result and diagnostics. pairSandbox(code) Redeems Builder’s short-lived one-use Sandbox pairing code. openSharedPreview(url) Admits a valid link only after the registered host router forwards it into the intended ready session. Handle cold and warm URLs once, after account restoration, permission, current access/values and a ready Gate/Host. Keep pending work/deduplication bounded and in memory; retire it on authority change. Never log/store bearer links or automatically retry after a possibly consumed redemption. A true result means admitted, not visibly opened. Shared preview never earns Sandbox test completion or Results credit; published beta.15 action exceptions remain policy guarded. getDiagnostics() Recent bounded diagnostics; redact before sharing. deleteEndUser() Best-effort server request plus local wipe/tombstone. Catch async failures. Missing stories or transport errors must not block the host app. Published beta.15 shared-preview contract: openSharedPreview(url) returns admission, not proof of visible playback. Gate queues the pinned invitation in its existing banner; a deliberate tap rechecks local facts and expiry. Hidden/background banners wait in memory. Host-only keeps direct opening. Do not call present() or redeem again for a pending Gate invitation. Preview actions do not earn Results credit. Published beta.16 preview request feedback: Gate shows Loading, Ready to open, or an error after a valid SDK attempt begins. Loading uses a gentle indeterminate pulse, not a percentage or download estimate; Reduce Motion keeps it static, and background/hidden placement stops motion. Ready means a validated presentation resolved against recipient facts is available, not that every native font or image is loaded. An explicit Open tap rechecks current facts and expiry; no automatic Gate playback or Results credit is added. Host-only retains its existing direct-opening contract. The host still owns pre-attempt startup, account restoration, consent and readiness feedback. Only retryable errors offer a deliberate retry. An uncertain attempt may have consumed an opening; another attempt may consume another. Never automatically retry or requeue redemption. A retry input is retained for at most five minutes per attempt in private memory, never observable state, UI props, storage or diagnostics; replacement, dismissal, retirement, disposal, success and terminal failure clear it. Session changes and late results cannot revive an old request. Verify installed declarations, actual media/font readiness, native feedback and cold/warm links separately; these source changes are not publication or device acceptance. recordOutcome(outcomeKey): void Queues an unverified client event for the current cycle and available experiment assignment. Idempotency is per customer/outcome/cycle—not a count of every purchase. Report confirmed success once, not a tap followed by confirmation. This client call cannot select a historical cycle or upload transaction payloads. Use the separately authenticated server-outcome integration for trusted backend events and explicit attribution; never put its secret in a mobile app. Components and exports Export Purpose ProductWorthItGate Built-in invitation and presenter; may be locally aliased RecappelGate. ProductWorthItHost Presenter only; may be locally aliased RecappelHost. StoryPlayer / public preview API Actual shared renderer; previews are not a delivery or Results shortcut. CARD_LAYOUTS Supported layouts. demoStory Local test fixture, not a Production version. Types ProductWorthItConfig, LifecycleInput, AudienceContext, story/brand, storage and public preview/wire types. Installed declarations are authoritative. Mount one presenter under SafeAreaProvider. Do not import private lib paths. Gate props include placement (not a measured exposure), onOpen, onDismiss and renderBanner. In SDKs supporting late-value invitation recovery, valid value updates request a bounded recheck without replacing an active player. This does not bypass eligibility, force a network refresh on every render or poll for absent data. BrandTokens.ambientColors?: [string, string, string] is cinematic-only and accepts exactly three #RRGGBB strings. It supplies colours for the built-in glow layers, not CSS gradients, geometry or executable motion. Omission retains the accent/secondary/accent fallback. Approved app-wide values can be reviewed through recappel.visuals.json ; this is not a customer reporting API. Card changes and colour edits preserve the continuous ambient phase within an open presenter. Animation runs only in the foreground when unpaused and respects system/story reduced-motion settings. A new presenter can start a new phase. Use the existing editor controls for staged font, artwork, opening-copy and colour edits; Confirm applies and Close cancels. Explicit pattern paint BrandTokens.pattern?: DotPatternPaint is an optional cinematic-only app-wide paint, not a customer reporting method. All fields are required: {kind:'dots', color:'#RRGGBB', opacity, dotWidth, dotHeight, tileWidth, tileHeight, alignment:'center', offsetX, offsetY} . Opacity is 0–1, dot dimensions >0–64, tile dimensions 4–512, offsets -512–512; each dot dimension fits its tile. All numbers must be finite and geometry is logical pixels. Unknown fields are rejected. It requires pattern-paint-v1 , preserves approved source geometry and takes precedence over legacy patternUrl . See visual setup . Service transport Host developers use public SDK methods, not hand-built requests to managed-service endpoints. POST route Purpose /sdk/v1/audience:config Get app enrollment settings before personal requests. /sdk/v1/bootstrap Get a compatible validated story or null. /sdk/v1/aggregates:sync Send a cycle snapshot. /sdk/v1/exposures Send supported interactions / holdout eligibility. /sdk/v1/outcomes Send unverified client outcomes. /sdk/v1/end-user:delete Request deletion. Legacy x-pwi-key, x-pwi-env and x-pwi-sdk headers remain; additive capabilities and coarse audience headers protect newer features. Use SDK methods for pairing/preview/diagnostics. No client key grants Builder publication or server-outcome privileges. /sdk/v1/definitions:sync carries only app-level numeric definitions. The SDK handles this route; do not build a second transport. Definitions never grant story approval or fabricate a numeric receipt. /sdk/v1/structured:sync sends public group definitions and availability/revision receipts in the existing app/environment/customer/device scope, never the structured numbers, source windows or local-only winning labels. Use the public SDK, not a second transport. Builder unavailable status proves wiring without claiming usable data; metadata receipt, available data and rendered playback remain separate. /sdk/v1/relationships:sync reports the optional static manifest and normalized scalar/structured definitions automatically through the existing authenticated SDK connection. Its body contains no customer identifiers, readings, examples or source paths. No manual import or second host transport is needed. Builder validates exact definition revisions and uses source-verified relationships for coherent synthetic examples; stale references and conflicting fixed examples remain review gaps. 2. Connect your app 4. Send useful values Troubleshooting Previous: Data handling & deletion Next: Troubleshooting
Troubleshooting Check the exact failure reason. Keep safety rules intact while diagnosing missing stories. Run after configure. Catch setup errors so the app stays usable; review and redact diagnostics before sharing. Eligibility reasons Reason / condition Check no_story No compatible selected/published/cached story. Contact can succeed without one. no_lifecycle Paid/trial need valid lifecycle; Free needs explicit fresh free audience and a compatible story. environment_mismatch sandbox/production must match SANDBOX/PRODUCTION lifecycle. lifecycle_stale Refresh the source, not just its timestamp. entitlement_inactive Do not falsify paid access. Explicit Free is a separate supported flow. suppressed_billing_state Grace/retry/refund/overdue; resolve at the source. outside_window Verify real anchors, offsets and clock. insufficient_data Check keys, cycle and thresholds. Snapshots do not supply additive-event totals. already_shown / holdout Expected frequency/experiment behavior; do not bypass in Production. Enrollment / audience / allowance Check enabled, explicit facts, app enrollment, story rules and any configured allowance. deleted Deletion tombstone; never restart tracking silently. Not configured / init failure Check inputs/order. Async calls may reject; preserve the host app. Setup is still waiting Install: build and install the updated host app. The server cannot observe npm installation or an unopened APK. Contact: open the app and sign in if required. Initialization waits for a resolved customer, permission and reliable access facts; onboarding alone may not reach it. Check a new last-seen time in Builder. Build: development and release builds both work. Remove accidental __DEV__ or extra test-flag guards around initialization, not genuine permission checks. Rebuild if the installed APK predates your change. Configuration: match the public key, service base URL and environment. Builder Refresh reads receipts; it cannot make the host app run. Access: refresh the host source and inspect local/paired diagnostics; no raw remote lifecycle receipt is required. Value: run the action and check exact key/method and last-received time. Story: select a Sandbox version, pair, refresh and check opening/completion separately from outcomes. Poster sharing unavailable: check the resolved SDK version, bundled RecappelPosterShare native module and rebuilt installed binary. A Metro refresh cannot add native code. Preserve private recap policy and every referenced fact’s approval; do not enable private content or add a text fallback to make the button succeed. Known cancellation is not completion, and an uncertain result must not open a second sheet. Test the actual OS handoff separately from a successful JavaScript bundle. Supported TextureView capture requires Android API 24+ and the bounded SDK-native path; do not relax a host screenshot safeguard or add another capture package as a workaround. Unsupported compositions and oversize images fail safely. Image sharing is bundled; no separate sharing or capture package is needed. An error can also mean that an existing activity or capture surface is not ready, rather than a missing module; inspect capability before rebuilding. The published beta.16 readiness-error correction distinguishes those states and keeps the story open for a deliberate retry; it is not in published beta.15. Leader or relationship unavailable: check installed SDK and compatible service/Builder support, exact current source definitions, matching scope/unit/population/window and a fresh permitted reading. Ties and zero make a leader unavailable. A failed metadata receipt or conflicting fixed preview example is a review gap, not a reason to invent values or upload private winning labels. Offline and cached data Validated stories, aggregates and queued requests can persist. A first offline install has no remote story cache; memory storage cannot survive restart. A resolved flush does not prove delivery. Bootstrap refresh is due after 15 minutes; playable cached data has a maximum age of one hour. Cold-start enrollment failure does not assume permission. Use isolated Sandbox fixtures, not changed Production assignments or invented access facts. Connect in five steps Sandbox & test devices Release status Previous: React Native API reference Next: Release status
Release status undefined Current release Install @recappel/react-native@latest for the recommended public release. Check npm for the resolved version; verify your installed package and lockfile agree. Compiled runtime, declarations/assets and proprietary license are included. SDK values, lifecycle-v2, Free/Trial/Paid enrollment, native renderer/preview, banner styling, pairing, shared links and bounded diagnostics. Current releases also report Sandbox setup status; older installed builds may not report access/value/presenter readiness. Connected development Builder/backend: accounts, app keys, received values, constrained authoring, immutable versions, Results and coverage controls. Reviewed automated checks and package installation evidence; not a guarantee on every native host or at production scale. Earlier September 27 publication checkpoint: beta.17 was public latest. Its exact immutable 212,786-byte, 363-file archive was checked against an anonymous registry installation. It includes top-edge bubble entrances, refined recap typography, optional category-list bindings and narrow Android consumer rules that preserve native sharing under release minification. Rebuild the native app when upgrading from a release without those rules; no separate sharing or capture package is needed. Package and installed-consumer checks do not establish all-screen native/iOS, performance, real billing or Production acceptance. Use the verified installed declarations. Published September 27: beta.18 is public latest. Its corrections include the following: unpaid upsells omit the separate Free row and no-payment copy, preserve one genuine custom host purchase control, or show one functional fallback only when the host supplies a real purchase capability. Paid, trial, billing-trouble and access disclosures remain truthful. Genuine shared-link playback can resolve the current recipient’s exact local category names, never labels from the link, server or another owner. These names are not uploaded, persisted, used in AI/telemetry or exported in posters. Narrow recap posters scale their interiors proportionally with readable text and tighter divider spacing. The exact 213,046-byte, 363-file package passed installed-consumer checks and an anonymous public-registry download plus fresh @latest installation at 01:54 UTC on September 27; every file matched the qualified archive. Native files and runtime dependencies are unchanged from beta.17. Installed-host visuals and native acceptance remain separate. Remaining acceptance Area Still required Real host Install the public package into your actual app; test both native platforms, sessions, offline, fonts, accessibility and sharing. Production / scale Operational release approval and representative aggregate load testing. No 500K/10M capacity guarantee. Store billing Audience prices are approved, but checkout remains unavailable until exact native products, localized offers and the server catalog are verified. Real purchase/renewal/restore, legal and sustained-capacity acceptance remain separate. Visual stories Review discovered public artwork locally; connect approved theme and compatible registered fonts. Verify the generated design and any optional host-owned personal-image providers on real devices. No unrestricted repository upload or automatic font installation. Measurement Real trusted backend outcomes and complete observation windows; no promised retention lift or per-card dwell analytics. Other platforms Standalone Swift, Kotlin and Flutter SDKs are not published. Public-launch checklist Use the exact published SDK and compatible native peers. Connect the host app to its Sandbox key and service endpoint. Verify received values, selected version, audience/timing, session changes, presentation and outcomes end-to-end. Complete store, privacy, billing and operational acceptance separately. Ship the matching Production configuration, then publish the tested story from Builder. Saving, paying or installing never launches it automatically. npm distribution and backend hosting are separate. A public SDK does not mean Builder is already available in the app stores or that checkout is enabled. Historical September 25 beta.13 checkpoint: @recappel/react-native 0.2.0-beta.13 was public latest. The 198,072-byte, 335-file archive and a fresh anonymous latest installation match the tested package. It removes the outer poster matte and duplicate backdrop while retaining the opaque inner artwork, tilt and shadow, with native sources unchanged from beta.12. A Samsung Android host produced an actual 1080 × 1957 PNG with transparent outer margins; the chooser, cancellation back to the same story and retry were verified. This does not establish iOS sharing, successful recipient posting, full visual completion, Production readiness or website publication. Historical beta.12 checkpoint: its verified 198,101-byte, 335-file release established native image export and chooser return. Beta.13 supersedes its rectangular poster-background blending issue; the beta.12 archive remains unchanged. The normal Android free-account subscription-management handoff was also checked without making a purchase or invoking Restore. Package and device evidence do not replace acceptance in your own host. Further reading Use the published package README and installed declarations for exact signatures. Older v0.1 source notes are historical, not the current installation guide. npm package ↗ , Expo libraries ↗ and Expo development builds ↗ . 1. Install SDK Connect in five steps Settings, plan & billing Previous: Troubleshooting
Start here
SDK reference
Build a reason to stay.
Turn the value your app already delivers into a personal, brand-matched story.
Piece | Job |
|---|---|
Your app + SDK | Supplies approved values and local access facts; checks eligibility and renders the story. It does not take payments. |
Recappel Builder | Mobile workspace for connecting apps, editing stories, testing, publishing and results. |
Recappel service | Stores configuration, receives approved aggregates/events and delivers compatible story versions when the SDK requests them. |
Use your existing app
Recappel supports React Native and Expo on iOS and Android. Keep your authentication, payment provider and navigation. The SDK sends only what you explicitly report—not your database or every action.
Choose evidence of delivered value: saved highlights, completed sessions or finished projects. Do not claim books read or time saved unless your app measures it. Customers see your brand; developers manage it in Recappel.
Start integrating
Follow the five steps → or use your coding agent →. Both paths use the same SDK and checks.
Install @recappel/react-native@latest, test in Sandbox, then prepare your customer release. Check Release status → for version compatibility and availability.
start-with-your-app|Use your existing app choose-a-path|Start integrating