From efeda6ac4deb9c8bbd98874172378f5088598b17 Mon Sep 17 00:00:00 2001 From: Natalie Date: Tue, 14 Apr 2026 11:03:41 +1200 Subject: [PATCH 1/5] feat(audience): add core types (AudienceConfig, ConsentLevel, AudienceError, Constants) Adds public types needed before any implementation work begins: - AudienceConfig: PublishableKey, Consent, DistributionPlatform, Debug, FlushIntervalSeconds, FlushSize, OnError. No Environment field. - ConsentLevel: None / Anonymous / Full - AudienceError + AudienceErrorCode - Constants: key-prefix URL routing, API paths, flush defaults, DistributionPlatforms string constants - ImmutableAudience: wire in Init(AudienceConfig) now that the type exists Linear: SDK-119 --- .../Audience/Runtime/AudienceConfig.cs | 16 +++++++ .../Audience/Runtime/AudienceConfig.cs.meta | 11 +++++ .../Audience/Runtime/AudienceError.cs | 27 ++++++++++++ .../Audience/Runtime/AudienceError.cs.meta | 11 +++++ src/Packages/Audience/Runtime/ConsentLevel.cs | 13 ++++++ .../Audience/Runtime/ConsentLevel.cs.meta | 11 +++++ src/Packages/Audience/Runtime/Core.meta | 8 ++++ .../Audience/Runtime/Core/Constants.cs | 43 +++++++++++++++++++ .../Audience/Runtime/Core/Constants.cs.meta | 11 +++++ .../Audience/Runtime/ImmutableAudience.cs | 12 ++++-- .../Runtime/ImmutableAudience.cs.meta | 2 +- src/Packages/Audience/package.json | 7 ++- 12 files changed, 167 insertions(+), 5 deletions(-) create mode 100644 src/Packages/Audience/Runtime/AudienceConfig.cs create mode 100644 src/Packages/Audience/Runtime/AudienceConfig.cs.meta create mode 100644 src/Packages/Audience/Runtime/AudienceError.cs create mode 100644 src/Packages/Audience/Runtime/AudienceError.cs.meta create mode 100644 src/Packages/Audience/Runtime/ConsentLevel.cs create mode 100644 src/Packages/Audience/Runtime/ConsentLevel.cs.meta create mode 100644 src/Packages/Audience/Runtime/Core.meta create mode 100644 src/Packages/Audience/Runtime/Core/Constants.cs create mode 100644 src/Packages/Audience/Runtime/Core/Constants.cs.meta diff --git a/src/Packages/Audience/Runtime/AudienceConfig.cs b/src/Packages/Audience/Runtime/AudienceConfig.cs new file mode 100644 index 000000000..b350243d8 --- /dev/null +++ b/src/Packages/Audience/Runtime/AudienceConfig.cs @@ -0,0 +1,16 @@ +using System; + +namespace Immutable.Audience +{ + /// Configuration passed to . + public class AudienceConfig + { + public string PublishableKey { get; set; } + public ConsentLevel Consent { get; set; } = ConsentLevel.None; + public string DistributionPlatform { get; set; } + public bool Debug { get; set; } = false; + public int FlushIntervalSeconds { get; set; } = Constants.DefaultFlushIntervalSeconds; + public int FlushSize { get; set; } = Constants.DefaultFlushSize; + public Action OnError { get; set; } + } +} diff --git a/src/Packages/Audience/Runtime/AudienceConfig.cs.meta b/src/Packages/Audience/Runtime/AudienceConfig.cs.meta new file mode 100644 index 000000000..ab5d113be --- /dev/null +++ b/src/Packages/Audience/Runtime/AudienceConfig.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0815c3d8a1164cd8811176be7d640aff +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/Packages/Audience/Runtime/AudienceError.cs b/src/Packages/Audience/Runtime/AudienceError.cs new file mode 100644 index 000000000..d95ba8bc2 --- /dev/null +++ b/src/Packages/Audience/Runtime/AudienceError.cs @@ -0,0 +1,27 @@ +using System; + +namespace Immutable.Audience +{ + public enum AudienceErrorCode + { + FlushFailed, + ValidationRejected, + ConsentSyncFailed, + NetworkError + } + + public class AudienceError : Exception + { + public AudienceErrorCode Code { get; } + public int Status { get; } + public string Endpoint { get; } + + public AudienceError(AudienceErrorCode code, string message, int status = 0, string endpoint = null) + : base(message) + { + Code = code; + Status = status; + Endpoint = endpoint; + } + } +} diff --git a/src/Packages/Audience/Runtime/AudienceError.cs.meta b/src/Packages/Audience/Runtime/AudienceError.cs.meta new file mode 100644 index 000000000..b1b7d9574 --- /dev/null +++ b/src/Packages/Audience/Runtime/AudienceError.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 93fbfe0c15974366b7a16693ceedb4df +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/Packages/Audience/Runtime/ConsentLevel.cs b/src/Packages/Audience/Runtime/ConsentLevel.cs new file mode 100644 index 000000000..66e518c05 --- /dev/null +++ b/src/Packages/Audience/Runtime/ConsentLevel.cs @@ -0,0 +1,13 @@ +namespace Immutable.Audience +{ + /// Controls what the Audience SDK tracks. + public enum ConsentLevel + { + /// SDK inert. No events queued or sent. No IDs persisted to disk. + None, + /// Track events with anonymousId only. Identify/Alias discarded with warning. + Anonymous, + /// All events. Identify/Alias send. userId attached to track events. + Full + } +} diff --git a/src/Packages/Audience/Runtime/ConsentLevel.cs.meta b/src/Packages/Audience/Runtime/ConsentLevel.cs.meta new file mode 100644 index 000000000..4f6dee409 --- /dev/null +++ b/src/Packages/Audience/Runtime/ConsentLevel.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 03ccd5190cb24864a2cde2c94f68b7f3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/Packages/Audience/Runtime/Core.meta b/src/Packages/Audience/Runtime/Core.meta new file mode 100644 index 000000000..e353cf31f --- /dev/null +++ b/src/Packages/Audience/Runtime/Core.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: fecac2b913974672addc6aa5d735c058 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/Packages/Audience/Runtime/Core/Constants.cs b/src/Packages/Audience/Runtime/Core/Constants.cs new file mode 100644 index 000000000..020834e24 --- /dev/null +++ b/src/Packages/Audience/Runtime/Core/Constants.cs @@ -0,0 +1,43 @@ +namespace Immutable.Audience +{ + internal static class Constants + { + // Base URL derived from publishable key prefix -- no environment param exposed to studios. + // Per the Product Environments RFC, Audience APIs will consolidate to a single endpoint. + // When that ships, only this file changes -- no studio-facing interface change required. + internal const string TestKeyPrefix = "pk_imapik-test-"; + internal const string SandboxBaseUrl = "https://api.sandbox.immutable.com"; + internal const string ProductionBaseUrl = "https://api.immutable.com"; + + internal const string MessagesPath = "/v1/audience/messages"; + internal const string ConsentPath = "/v1/audience/tracking-consent"; + internal const string DataPath = "/v1/audience/data"; + + internal const int DefaultFlushIntervalSeconds = 5; + internal const int DefaultFlushSize = 20; + internal const int MaxBatchSize = 100; + internal const int StaleEventDays = 30; + + internal const string LibraryName = "com.immutable.audience"; + internal const string Surface = "unity"; + internal const string ConsentSource = "UnitySDK"; + + internal static string BaseUrl(string publishableKey) => + publishableKey != null && publishableKey.StartsWith(TestKeyPrefix) + ? SandboxBaseUrl + : ProductionBaseUrl; + } + + /// + /// String constants for common game distribution platforms. + /// Any string is accepted -- studios are not limited to these values. + /// + public static class DistributionPlatforms + { + public const string Steam = "steam"; + public const string Epic = "epic"; + public const string GOG = "gog"; + public const string Itch = "itch"; + public const string Standalone = "standalone"; + } +} diff --git a/src/Packages/Audience/Runtime/Core/Constants.cs.meta b/src/Packages/Audience/Runtime/Core/Constants.cs.meta new file mode 100644 index 000000000..d222e9aa5 --- /dev/null +++ b/src/Packages/Audience/Runtime/Core/Constants.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ecbefc60c65a4c0f961601a2a47b5c8f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/Packages/Audience/Runtime/ImmutableAudience.cs b/src/Packages/Audience/Runtime/ImmutableAudience.cs index 677c80370..6c882b7b7 100644 --- a/src/Packages/Audience/Runtime/ImmutableAudience.cs +++ b/src/Packages/Audience/Runtime/ImmutableAudience.cs @@ -2,11 +2,17 @@ namespace Immutable.Audience { /// /// Entry point for the Immutable Audience SDK. - /// Call Init once on startup, then use the static methods from any thread. + /// Call once on startup, then use the static methods from any thread. /// public static class ImmutableAudience { - // Core types (AudienceConfig, ConsentLevel, AudienceError) and Init - // are added in the next PR once those types exist. See SDK-119. + // Scaffold only -- implementation follows in subsequent sub-issues (see SDK-99). + + /// Initialise the SDK. Call once, typically in your game's entry scene. + public static void Init(AudienceConfig config) + { + throw new System.NotImplementedException( + "Immutable Audience SDK: implementation pending. See SDK-99."); + } } } diff --git a/src/Packages/Audience/Runtime/ImmutableAudience.cs.meta b/src/Packages/Audience/Runtime/ImmutableAudience.cs.meta index 6e249f726..bf98375d9 100644 --- a/src/Packages/Audience/Runtime/ImmutableAudience.cs.meta +++ b/src/Packages/Audience/Runtime/ImmutableAudience.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 9e8335985aa845dbb0edd341b6b4ecd4 +guid: 6c42c74555864965bb48ea6a2bd9c915 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/src/Packages/Audience/package.json b/src/Packages/Audience/package.json index e6b8e96fb..88013ad02 100644 --- a/src/Packages/Audience/package.json +++ b/src/Packages/Audience/package.json @@ -5,5 +5,10 @@ "displayName": "Immutable Audience", "author": {"name": "Immutable", "url": "https://immutable.com"}, "keywords": ["unity", "immutable", "audience", "analytics"], - "unity": "2021.3" + "unity": "2021.3", + "samples": [{ + "displayName": "Quick Start", + "description": "Demo scene and script demonstrating SDK initialisation and event tracking.", + "path": "Samples~/QuickStart" + }] } From be29067f084acfa8389e32ce7fab48a381179a8d Mon Sep 17 00:00:00 2001 From: Natalie Bunduwongse Date: Wed, 15 Apr 2026 11:04:02 +1200 Subject: [PATCH 2/5] refactor(audience): clean up public API for C#/Unity idioms - Remove RFC comment and rename TestKeyPrefix -> SandboxKeyPrefix in Constants - Drop Status and Endpoint from AudienceError (leaked internal HTTP details) - Move OnError from AudienceConfig callback property to a static event on ImmutableAudience, following the same pattern as Passport.OnAuthEvent Co-Authored-By: Claude Sonnet 4.6 --- src/Packages/Audience/Runtime/AudienceConfig.cs | 3 --- src/Packages/Audience/Runtime/AudienceError.cs | 6 +----- src/Packages/Audience/Runtime/Core/Constants.cs | 8 +++----- src/Packages/Audience/Runtime/ImmutableAudience.cs | 8 ++++++++ 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/Packages/Audience/Runtime/AudienceConfig.cs b/src/Packages/Audience/Runtime/AudienceConfig.cs index b350243d8..5c43ffb9a 100644 --- a/src/Packages/Audience/Runtime/AudienceConfig.cs +++ b/src/Packages/Audience/Runtime/AudienceConfig.cs @@ -1,5 +1,3 @@ -using System; - namespace Immutable.Audience { /// Configuration passed to . @@ -11,6 +9,5 @@ public class AudienceConfig public bool Debug { get; set; } = false; public int FlushIntervalSeconds { get; set; } = Constants.DefaultFlushIntervalSeconds; public int FlushSize { get; set; } = Constants.DefaultFlushSize; - public Action OnError { get; set; } } } diff --git a/src/Packages/Audience/Runtime/AudienceError.cs b/src/Packages/Audience/Runtime/AudienceError.cs index d95ba8bc2..3e57ada0b 100644 --- a/src/Packages/Audience/Runtime/AudienceError.cs +++ b/src/Packages/Audience/Runtime/AudienceError.cs @@ -13,15 +13,11 @@ public enum AudienceErrorCode public class AudienceError : Exception { public AudienceErrorCode Code { get; } - public int Status { get; } - public string Endpoint { get; } - public AudienceError(AudienceErrorCode code, string message, int status = 0, string endpoint = null) + public AudienceError(AudienceErrorCode code, string message) : base(message) { Code = code; - Status = status; - Endpoint = endpoint; } } } diff --git a/src/Packages/Audience/Runtime/Core/Constants.cs b/src/Packages/Audience/Runtime/Core/Constants.cs index 020834e24..8f63b9861 100644 --- a/src/Packages/Audience/Runtime/Core/Constants.cs +++ b/src/Packages/Audience/Runtime/Core/Constants.cs @@ -2,10 +2,8 @@ namespace Immutable.Audience { internal static class Constants { - // Base URL derived from publishable key prefix -- no environment param exposed to studios. - // Per the Product Environments RFC, Audience APIs will consolidate to a single endpoint. - // When that ships, only this file changes -- no studio-facing interface change required. - internal const string TestKeyPrefix = "pk_imapik-test-"; + // Base URL is derived from the publishable key prefix; no environment param is exposed. + internal const string SandboxKeyPrefix = "pk_imapik-test-"; internal const string SandboxBaseUrl = "https://api.sandbox.immutable.com"; internal const string ProductionBaseUrl = "https://api.immutable.com"; @@ -23,7 +21,7 @@ internal static class Constants internal const string ConsentSource = "UnitySDK"; internal static string BaseUrl(string publishableKey) => - publishableKey != null && publishableKey.StartsWith(TestKeyPrefix) + publishableKey != null && publishableKey.StartsWith(SandboxKeyPrefix) ? SandboxBaseUrl : ProductionBaseUrl; } diff --git a/src/Packages/Audience/Runtime/ImmutableAudience.cs b/src/Packages/Audience/Runtime/ImmutableAudience.cs index 6c882b7b7..a74e08455 100644 --- a/src/Packages/Audience/Runtime/ImmutableAudience.cs +++ b/src/Packages/Audience/Runtime/ImmutableAudience.cs @@ -1,3 +1,5 @@ +using System; + namespace Immutable.Audience { /// @@ -8,6 +10,12 @@ public static class ImmutableAudience { // Scaffold only -- implementation follows in subsequent sub-issues (see SDK-99). + /// + /// Raised when the SDK encounters an error (e.g. a failed flush or consent sync). + /// Subscribe before calling . + /// + public static event Action? OnError; + /// Initialise the SDK. Call once, typically in your game's entry scene. public static void Init(AudienceConfig config) { From 81713d9306595db21711ce81cf28ae282052a8cc Mon Sep 17 00:00:00 2001 From: Natalie Bunduwongse Date: Wed, 15 Apr 2026 11:22:49 +1200 Subject: [PATCH 3/5] refactor(audience): revert SandboxKeyPrefix to TestKeyPrefix; drop OnError scaffold TestKeyPrefix matches the prod/test mental model consumers have. OnError belongs in the implementation PR (SDK-99), not the scaffold. Co-Authored-By: Claude Sonnet 4.6 --- src/Packages/Audience/Runtime/Core/Constants.cs | 5 ++--- src/Packages/Audience/Runtime/ImmutableAudience.cs | 8 -------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/src/Packages/Audience/Runtime/Core/Constants.cs b/src/Packages/Audience/Runtime/Core/Constants.cs index 8f63b9861..6573730d8 100644 --- a/src/Packages/Audience/Runtime/Core/Constants.cs +++ b/src/Packages/Audience/Runtime/Core/Constants.cs @@ -2,8 +2,7 @@ namespace Immutable.Audience { internal static class Constants { - // Base URL is derived from the publishable key prefix; no environment param is exposed. - internal const string SandboxKeyPrefix = "pk_imapik-test-"; + internal const string TestKeyPrefix = "pk_imapik-test-"; internal const string SandboxBaseUrl = "https://api.sandbox.immutable.com"; internal const string ProductionBaseUrl = "https://api.immutable.com"; @@ -21,7 +20,7 @@ internal static class Constants internal const string ConsentSource = "UnitySDK"; internal static string BaseUrl(string publishableKey) => - publishableKey != null && publishableKey.StartsWith(SandboxKeyPrefix) + publishableKey != null && publishableKey.StartsWith(TestKeyPrefix) ? SandboxBaseUrl : ProductionBaseUrl; } diff --git a/src/Packages/Audience/Runtime/ImmutableAudience.cs b/src/Packages/Audience/Runtime/ImmutableAudience.cs index a74e08455..6c882b7b7 100644 --- a/src/Packages/Audience/Runtime/ImmutableAudience.cs +++ b/src/Packages/Audience/Runtime/ImmutableAudience.cs @@ -1,5 +1,3 @@ -using System; - namespace Immutable.Audience { /// @@ -10,12 +8,6 @@ public static class ImmutableAudience { // Scaffold only -- implementation follows in subsequent sub-issues (see SDK-99). - /// - /// Raised when the SDK encounters an error (e.g. a failed flush or consent sync). - /// Subscribe before calling . - /// - public static event Action? OnError; - /// Initialise the SDK. Call once, typically in your game's entry scene. public static void Init(AudienceConfig config) { From 9529bb95159f39e294a10e49e6e430c856bf4c39 Mon Sep 17 00:00:00 2001 From: Natalie Bunduwongse Date: Wed, 15 Apr 2026 11:25:20 +1200 Subject: [PATCH 4/5] chore(audience): remove samples entry from package.json No sample app yet; add back when examples/audience is created. Co-Authored-By: Claude Sonnet 4.6 --- src/Packages/Audience/package.json | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/Packages/Audience/package.json b/src/Packages/Audience/package.json index 88013ad02..e6b8e96fb 100644 --- a/src/Packages/Audience/package.json +++ b/src/Packages/Audience/package.json @@ -5,10 +5,5 @@ "displayName": "Immutable Audience", "author": {"name": "Immutable", "url": "https://immutable.com"}, "keywords": ["unity", "immutable", "audience", "analytics"], - "unity": "2021.3", - "samples": [{ - "displayName": "Quick Start", - "description": "Demo scene and script demonstrating SDK initialisation and event tracking.", - "path": "Samples~/QuickStart" - }] + "unity": "2021.3" } From 6068c6f82a5d2535c2d8b43526face407da7d0f0 Mon Sep 17 00:00:00 2001 From: Natalie Bunduwongse Date: Wed, 15 Apr 2026 11:29:28 +1200 Subject: [PATCH 5/5] docs(audience): add xmldoc on DistributionPlatform pointing to DistributionPlatforms Co-Authored-By: Claude Sonnet 4.6 --- src/Packages/Audience/Runtime/AudienceConfig.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Packages/Audience/Runtime/AudienceConfig.cs b/src/Packages/Audience/Runtime/AudienceConfig.cs index 5c43ffb9a..759645562 100644 --- a/src/Packages/Audience/Runtime/AudienceConfig.cs +++ b/src/Packages/Audience/Runtime/AudienceConfig.cs @@ -5,6 +5,10 @@ public class AudienceConfig { public string PublishableKey { get; set; } public ConsentLevel Consent { get; set; } = ConsentLevel.None; + /// + /// Distribution platform the game is running on. + /// Use for common values, or pass any custom string. + /// public string DistributionPlatform { get; set; } public bool Debug { get; set; } = false; public int FlushIntervalSeconds { get; set; } = Constants.DefaultFlushIntervalSeconds;