@encorekit/web-sdk 2.0.4 → 2.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +112 -40
- package/dist/cjs/index.cjs +1 -1
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/umd/encore.min.js +1 -1
- package/dist/umd/encore.min.js.map +1 -1
- package/embed/README.md +82 -367
- package/examples/ios-webview/EncoreURLBuilder.swift +179 -61
- package/examples/ios-webview/README.md +75 -354
- package/examples/ios-webview/SimpleEncoreView.swift +49 -36
- package/examples/ios-webview/SimpleExample.swift +30 -86
- package/package.json +2 -7
- package/types/api/APIClient.d.ts +3 -1
- package/types/api/APIClient.d.ts.map +1 -1
- package/types/api/errorReportLimits.d.ts +50 -0
- package/types/api/errorReportLimits.d.ts.map +1 -0
- package/types/api/generated/types.d.ts +54 -7
- package/types/api/generated/types.d.ts.map +1 -1
- package/types/core/Encore.d.ts +25 -2
- package/types/core/Encore.d.ts.map +1 -1
- package/types/core/PlacementBuilder.d.ts +14 -2
- package/types/core/PlacementBuilder.d.ts.map +1 -1
- package/types/core/TransactionStore.d.ts +57 -13
- package/types/core/TransactionStore.d.ts.map +1 -1
- package/types/core/VerificationPoller.d.ts.map +1 -1
- package/types/core/claimBehavior.d.ts +28 -0
- package/types/core/claimBehavior.d.ts.map +1 -0
- package/types/core/errorContext.d.ts +18 -0
- package/types/core/errorContext.d.ts.map +1 -0
- package/types/core/errorType.d.ts +59 -0
- package/types/core/errorType.d.ts.map +1 -0
- package/types/core/hostDisplay.d.ts +50 -0
- package/types/core/hostDisplay.d.ts.map +1 -0
- package/types/core/version.d.ts +1 -1
- package/types/types.d.ts +146 -1
- package/types/types.d.ts.map +1 -1
- package/types/ui/OfferModal.d.ts +20 -0
- package/types/ui/OfferModal.d.ts.map +1 -1
- package/types/ui/PrimerScreen.d.ts +18 -11
- package/types/ui/PrimerScreen.d.ts.map +1 -1
- package/types/ui/RedemptionScreen.d.ts +15 -19
- package/types/ui/RedemptionScreen.d.ts.map +1 -1
- package/types/ui/RewardActions.d.ts +26 -0
- package/types/ui/RewardActions.d.ts.map +1 -1
- package/types/ui/brandComponents.d.ts.map +1 -1
- package/types/ui/styles.d.ts.map +1 -1
- package/types/utils/logger.d.ts +11 -1
- package/types/utils/logger.d.ts.map +1 -1
- package/embed/index.html +0 -57
- package/embed/styles.css +0 -154
- package/examples/ios-webview/EncoreWebViewBridge.swift +0 -419
- package/examples/ios-webview/ExampleViewController.swift +0 -233
|
@@ -1,114 +1,59 @@
|
|
|
1
1
|
//
|
|
2
2
|
// SimpleExample.swift
|
|
3
|
-
// Encore
|
|
3
|
+
// Encore hosted offer page, UIKit usage
|
|
4
4
|
//
|
|
5
|
-
// The simplest possible integration
|
|
5
|
+
// The simplest possible integration: build a URL, open it in a WKWebView.
|
|
6
|
+
//
|
|
7
|
+
// Requires `EncoreURLBuilder.swift`. The SwiftUI equivalent lives in
|
|
8
|
+
// `SimpleEncoreView.swift`.
|
|
6
9
|
//
|
|
7
10
|
|
|
8
11
|
import UIKit
|
|
9
12
|
import WebKit
|
|
10
13
|
|
|
11
|
-
class SimpleEncoreViewController: UIViewController {
|
|
12
|
-
|
|
14
|
+
final class SimpleEncoreViewController: UIViewController {
|
|
15
|
+
|
|
13
16
|
private var webView: WKWebView!
|
|
14
|
-
|
|
17
|
+
|
|
15
18
|
override func viewDidLoad() {
|
|
16
19
|
super.viewDidLoad()
|
|
17
|
-
|
|
18
|
-
// 1. Create
|
|
20
|
+
|
|
21
|
+
// 1. Create the webview.
|
|
19
22
|
webView = WKWebView(frame: view.bounds)
|
|
20
23
|
webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
|
21
|
-
webView.backgroundColor = .clear
|
|
22
|
-
webView.isOpaque = false
|
|
23
24
|
view.addSubview(webView)
|
|
24
|
-
|
|
25
|
-
// 2. Build Encore URL
|
|
26
|
-
let url = EncoreURL.build(
|
|
27
|
-
apiKey: "your_api_key_here",
|
|
28
|
-
userId: "user_123",
|
|
29
|
-
environment: "production",
|
|
30
|
-
attributes: [
|
|
31
|
-
"email": "user@example.com",
|
|
32
|
-
"subscriptionTier": "free"
|
|
33
|
-
]
|
|
34
|
-
)!
|
|
35
|
-
|
|
36
|
-
// 3. Load it!
|
|
37
|
-
webView.load(URLRequest(url: url))
|
|
38
|
-
|
|
39
|
-
// That's it! The embed will auto-initialize and show offers.
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// MARK: - SwiftUI Version
|
|
44
|
-
|
|
45
|
-
import SwiftUI
|
|
46
25
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
apiKey: "
|
|
26
|
+
// 2. Build the URL. Only coarse targeting values belong here: the page
|
|
27
|
+
// refuses email, phone, names and precise location, and says so.
|
|
28
|
+
guard let url = EncoreURL.build(
|
|
29
|
+
apiKey: "pk_live_your_key_here",
|
|
51
30
|
userId: "user_123",
|
|
52
31
|
attributes: [
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
]
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
32
|
+
"subscriptionTier": "free",
|
|
33
|
+
"countryCode": "US",
|
|
34
|
+
],
|
|
35
|
+
custom: ["plan": "gold"]
|
|
36
|
+
) else { return }
|
|
59
37
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
let environment: String
|
|
64
|
-
let attributes: [String: String]?
|
|
65
|
-
|
|
66
|
-
init(
|
|
67
|
-
apiKey: String,
|
|
68
|
-
userId: String? = nil,
|
|
69
|
-
environment: String = "production",
|
|
70
|
-
attributes: [String: String]? = nil
|
|
71
|
-
) {
|
|
72
|
-
self.apiKey = apiKey
|
|
73
|
-
self.userId = userId
|
|
74
|
-
self.environment = environment
|
|
75
|
-
self.attributes = attributes
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
func makeUIView(context: Context) -> WKWebView {
|
|
79
|
-
let webView = WKWebView()
|
|
80
|
-
webView.backgroundColor = .clear
|
|
81
|
-
webView.isOpaque = false
|
|
82
|
-
|
|
83
|
-
if let url = EncoreURL.build(
|
|
84
|
-
apiKey: apiKey,
|
|
85
|
-
userId: userId,
|
|
86
|
-
environment: environment,
|
|
87
|
-
attributes: attributes
|
|
88
|
-
) {
|
|
89
|
-
webView.load(URLRequest(url: url))
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return webView
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
func updateUIView(_ webView: WKWebView, context: Context) {
|
|
96
|
-
// No updates needed
|
|
38
|
+
// 3. Load it. Nothing else is required: no message handler to register,
|
|
39
|
+
// no bridge to install, no delegate to implement.
|
|
40
|
+
webView.load(URLRequest(url: url))
|
|
97
41
|
}
|
|
98
42
|
}
|
|
99
43
|
|
|
100
|
-
// MARK: -
|
|
44
|
+
// MARK: - Presentation patterns
|
|
101
45
|
|
|
102
46
|
extension SimpleEncoreViewController {
|
|
103
|
-
|
|
104
|
-
///
|
|
47
|
+
|
|
48
|
+
/// Present from a cancellation flow, where the churn carousel fits better
|
|
49
|
+
/// than the page's default reward sheet.
|
|
105
50
|
static func showInCancellationFlow(from viewController: UIViewController) {
|
|
106
51
|
let encoreVC = SimpleEncoreViewController()
|
|
107
52
|
encoreVC.modalPresentationStyle = .fullScreen
|
|
108
53
|
viewController.present(encoreVC, animated: true)
|
|
109
54
|
}
|
|
110
|
-
|
|
111
|
-
///
|
|
55
|
+
|
|
56
|
+
/// Present as a sheet.
|
|
112
57
|
static func showAsSheet(from viewController: UIViewController) {
|
|
113
58
|
let encoreVC = SimpleEncoreViewController()
|
|
114
59
|
if let sheet = encoreVC.sheetPresentationController {
|
|
@@ -117,8 +62,8 @@ extension SimpleEncoreViewController {
|
|
|
117
62
|
}
|
|
118
63
|
viewController.present(encoreVC, animated: true)
|
|
119
64
|
}
|
|
120
|
-
|
|
121
|
-
///
|
|
65
|
+
|
|
66
|
+
/// Embed in an existing view.
|
|
122
67
|
static func embedInView(_ containerView: UIView, parentVC: UIViewController) {
|
|
123
68
|
let encoreVC = SimpleEncoreViewController()
|
|
124
69
|
parentVC.addChild(encoreVC)
|
|
@@ -128,4 +73,3 @@ extension SimpleEncoreViewController {
|
|
|
128
73
|
encoreVC.didMove(toParent: parentVC)
|
|
129
74
|
}
|
|
130
75
|
}
|
|
131
|
-
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@encorekit/web-sdk",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Encore SDK for web applications - display targeted offers and manage user entitlements",
|
|
6
6
|
"main": "./dist/cjs/index.cjs",
|
|
@@ -24,19 +24,15 @@
|
|
|
24
24
|
"files": [
|
|
25
25
|
"dist",
|
|
26
26
|
"types",
|
|
27
|
-
"embed/dist",
|
|
28
|
-
"embed/index.html",
|
|
29
|
-
"embed/styles.css",
|
|
30
27
|
"embed/README.md",
|
|
31
28
|
"examples",
|
|
32
29
|
"README.md",
|
|
33
30
|
"LICENSE"
|
|
34
31
|
],
|
|
35
32
|
"scripts": {
|
|
36
|
-
"build": "npm run build:types && npm run build:bundle
|
|
33
|
+
"build": "npm run build:types && npm run build:bundle",
|
|
37
34
|
"build:types": "tsc -p tsconfig.types.json --emitDeclarationOnly",
|
|
38
35
|
"build:bundle": "rollup -c",
|
|
39
|
-
"build:embed": "rollup -c --config-embed",
|
|
40
36
|
"build:watch": "rollup -c -w",
|
|
41
37
|
"test": "vitest run",
|
|
42
38
|
"test:watch": "vitest",
|
|
@@ -50,7 +46,6 @@
|
|
|
50
46
|
"demo:backend": "ENV_FILE=.env.local node demo/backend.js",
|
|
51
47
|
"demo:dev": "concurrently -n watch,backend,mock,frontend -c blue,magenta,yellow,green \"npm run build:watch\" \"npm run demo:backend\" \"npm run demo:mock\" \"npm run demo:frontend\"",
|
|
52
48
|
"dev:example": "bash scripts/dev-example.sh",
|
|
53
|
-
"embed:serve": "http-server -p 3001 -o embed/index.html",
|
|
54
49
|
"clean:examples": "bash scripts/clean-examples.sh",
|
|
55
50
|
"prepublishOnly": "npm test && npm run build && npm run clean:examples",
|
|
56
51
|
"pack:local": "npm run build && npm run clean:examples && npm pack",
|
package/types/api/APIClient.d.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import type { UserAttributes, Entitlement, VerificationStatus, UseCase } from '../types';
|
|
7
7
|
import type { StartTransactionResponse, InitUserResponse, IdentifyUserResponse, RemoteConfigResponse, OfferModel, BackendVerificationStatus } from './models';
|
|
8
|
+
import type { ErrorContext } from '../core/errorContext';
|
|
8
9
|
interface RequestOptions {
|
|
9
10
|
method: 'GET' | 'POST' | 'PATCH';
|
|
10
11
|
headers?: Record<string, string>;
|
|
@@ -119,7 +120,8 @@ export declare class APIClient {
|
|
|
119
120
|
reportError(params: {
|
|
120
121
|
errorType: string;
|
|
121
122
|
message: string;
|
|
122
|
-
context
|
|
123
|
+
/** Typed so the backend's `[errorType, context]` fingerprint groups with iOS and Android. */
|
|
124
|
+
context: ErrorContext;
|
|
123
125
|
severity?: 'fatal' | 'error' | 'warning' | 'info';
|
|
124
126
|
stackTrace?: string;
|
|
125
127
|
userId?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"APIClient.d.ts","sourceRoot":"","sources":["../../src/api/APIClient.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAe,cAAc,EAAE,WAAW,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAEtG,OAAO,KAAK,EAEV,wBAAwB,EAIxB,gBAAgB,EAChB,oBAAoB,EACpB,oBAAoB,EACpB,UAAU,EACV,yBAAyB,EAC1B,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"APIClient.d.ts","sourceRoot":"","sources":["../../src/api/APIClient.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Field limits for `POST /errors`, copied from the endpoint's own Zod schema
|
|
3
|
+
* (`backend/packages/core/src/infrastructure/errors/schema.ts`).
|
|
4
|
+
*
|
|
5
|
+
* These matter because Zod rejects the WHOLE report when one field is over
|
|
6
|
+
* length. The 400 is not a truncated report, it is no report. And the failure
|
|
7
|
+
* is worst exactly where the report is most valuable: a deep stack in a
|
|
8
|
+
* minified production bundle is both the most likely to exceed 10000
|
|
9
|
+
* characters and the hardest to reproduce locally.
|
|
10
|
+
*
|
|
11
|
+
* Keep these in step with the schema. A limit that drifts too high loses
|
|
12
|
+
* reports again; one that drifts too low only costs detail.
|
|
13
|
+
*/
|
|
14
|
+
export declare const ERROR_REPORT_LIMITS: {
|
|
15
|
+
readonly errorType: 100;
|
|
16
|
+
readonly message: 2000;
|
|
17
|
+
readonly context: 200;
|
|
18
|
+
readonly stackTrace: 10000;
|
|
19
|
+
readonly underlyingError: 2000;
|
|
20
|
+
readonly appBundleId: 200;
|
|
21
|
+
readonly userId: 200;
|
|
22
|
+
readonly metadataValue: 500;
|
|
23
|
+
};
|
|
24
|
+
/** Stand-ins for the `.min(1)` fields when the value we have is empty. */
|
|
25
|
+
export declare const ERROR_REPORT_FALLBACKS: {
|
|
26
|
+
readonly message: "Unknown error";
|
|
27
|
+
readonly appBundleId: "unknown";
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Clips a field to its limit and says so.
|
|
31
|
+
*
|
|
32
|
+
* Keeps the START of the value. For a stack trace that is the frames nearest
|
|
33
|
+
* the throw, which is the diagnostic half; the tail is framework noise. The
|
|
34
|
+
* marker is what stops a reader in Sentry or Cloud Logging reading a clipped
|
|
35
|
+
* trace as a shallow one.
|
|
36
|
+
*/
|
|
37
|
+
export declare function bounded(value: string, limit: number): string;
|
|
38
|
+
/**
|
|
39
|
+
* Bounds a field that the schema also requires to be non-empty.
|
|
40
|
+
*
|
|
41
|
+
* `message` and `appBundleId` carry `.min(1)` as well as a `.max()`, and both
|
|
42
|
+
* can legitimately arrive empty: `new Error()` has `message === ''`, and
|
|
43
|
+
* `window.location.hostname` is `''` on a `file://` page or a sandboxed iframe.
|
|
44
|
+
* An empty value fails validation exactly as an over-long one does, and loses
|
|
45
|
+
* the whole report the same way.
|
|
46
|
+
*/
|
|
47
|
+
export declare function boundedNonEmpty(value: string, limit: number, fallback: string): string;
|
|
48
|
+
/** Bounds every value of a metadata record, dropping nothing. */
|
|
49
|
+
export declare function boundedMetadata(metadata: Record<string, string>): Record<string, string>;
|
|
50
|
+
//# sourceMappingURL=errorReportLimits.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errorReportLimits.d.ts","sourceRoot":"","sources":["../../src/api/errorReportLimits.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;CAStB,CAAC;AAIX,0EAA0E;AAC1E,eAAO,MAAM,sBAAsB;;;CAGzB,CAAC;AAEX;;;;;;;GAOG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAG5D;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEtF;AAED,iEAAiE;AACjE,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAOxB"}
|
|
@@ -82,6 +82,8 @@ export interface paths {
|
|
|
82
82
|
priority: number | null;
|
|
83
83
|
/** @description When true, this campaign is auto-granted to every newly created app. Independent of priority (which controls ranking order only). */
|
|
84
84
|
defaultCatalog: boolean;
|
|
85
|
+
/** @description When true, this is a demo offer: a fictional brand served only to pk_test_* keys so an integration can be exercised end to end without contacting a real advertiser. Surface it as such rather than presenting it as real inventory. Absent or false means ordinary inventory. */
|
|
86
|
+
isDemo?: boolean;
|
|
85
87
|
/**
|
|
86
88
|
* @description Deprecated; always 'whitelist'. Retained because iOS/Android SDK decoders require the field. Access is governed solely by campaign_allowed_apps + enabled.
|
|
87
89
|
* @enum {string}
|
|
@@ -376,6 +378,8 @@ export interface paths {
|
|
|
376
378
|
priority: number | null;
|
|
377
379
|
/** @description When true, this campaign is auto-granted to every newly created app. Independent of priority (which controls ranking order only). */
|
|
378
380
|
defaultCatalog: boolean;
|
|
381
|
+
/** @description When true, this is a demo offer: a fictional brand served only to pk_test_* keys so an integration can be exercised end to end without contacting a real advertiser. Surface it as such rather than presenting it as real inventory. Absent or false means ordinary inventory. */
|
|
382
|
+
isDemo?: boolean;
|
|
379
383
|
/**
|
|
380
384
|
* @description Deprecated; always 'whitelist'. Retained because iOS/Android SDK decoders require the field. Access is governed solely by campaign_allowed_apps + enabled.
|
|
381
385
|
* @enum {string}
|
|
@@ -574,7 +578,7 @@ export interface paths {
|
|
|
574
578
|
};
|
|
575
579
|
get?: never;
|
|
576
580
|
put?: never;
|
|
577
|
-
/** @description Rebrokering catalog: the full set of offers your app may serve to a given end user, to render on your own surface. Two hard, server-enforced filters always apply: entitlement (your app's opt-outs + whitelist) and geo (offers not redeemable in the end user's country are dropped
|
|
581
|
+
/** @description Rebrokering catalog: the full set of offers your app may serve to a given end user, to render on your own surface. Two hard, server-enforced filters always apply: entitlement (your app's opt-outs + whitelist) and geo (offers not redeemable in the end user's country are dropped: non-redeemable offers are never returned). Serve path requires `userId` (a stable end-user identifier) and geo (`countryCode`, or the end user's IP forwarded via X-Forwarded-For); missing either is a 400. Each returned creative carries a tokenized `clickUrl`: route the end user there on tap and it records an attributed transaction (bound to your app, the offer, the creative, and the userId) then 302s to the advertiser. Filter to your curated offer IDs and order CLIENT-SIDE, falling back to other returned offers so your surface is never empty (the server does not narrow to your picks). Set `includeAllRegions: true` for config-time discovery of your full addressable inventory (skips geo, needs no userId, returns no clickUrls; each offer carries targetCountries); not for display. Authentication: publishable key (X-API-Key: pk_live_* / pk_test_*). */
|
|
578
582
|
post: {
|
|
579
583
|
parameters: {
|
|
580
584
|
query?: never;
|
|
@@ -591,6 +595,13 @@ export interface paths {
|
|
|
591
595
|
clientId?: string;
|
|
592
596
|
/** @description ISO 3166-1 alpha-2 country code of the END USER the offers will be shown to (e.g. "US", "GB"). Provides the geography signal for geo-filtering. Resolution: this value → client IP (X-Forwarded-For aware) → (rejected if neither resolves). Send it when calling server-to-server without forwarding the end user's IP; omit it when the end user (or a proxy forwarding their IP) is the caller. Ignored when includeAllRegions is true. */
|
|
593
597
|
countryCode?: string;
|
|
598
|
+
/**
|
|
599
|
+
* @description Optional device family of the END USER the offers will be shown to (enum; any other value is rejected with 400). Analytics dimension only (platform on the serve event); never 'api'.
|
|
600
|
+
* @enum {string}
|
|
601
|
+
*/
|
|
602
|
+
platform?: "android" | "ios" | "web";
|
|
603
|
+
/** @description Optional publisher-defined label for the surface this offer is shown on (e.g. 'cancel_flow', 'home_feed', 'post_purchase'). Stamped onto the impression AND onto the click it produces, so publisher analytics can break the funnel out by placement. Normalised server-side: a blank value is treated as absent, anything longer than 64 characters is TRUNCATED rather than rejected, and the stored label NEVER carries leading or trailing whitespace — including after truncation, so ' my label ' and 'my label' are always one placement and can never show up as two rows. Case IS significant: 'Cancel_Flow' and 'cancel_flow' are different placements. Omit it and this traffic groups under '(unlabeled)' — the same bucket as an SDK integration that supplies no placement. */
|
|
604
|
+
placementId?: string;
|
|
594
605
|
/** @description CONFIG-TIME ONLY. When true, skips geo-filtering and returns the full addressable inventory for your app (entitlement still applies), with each offer's targetCountries — use this to build your curation. Do NOT use for offers displayed to an end user; those must come from a geo-scoped request (countryCode or forwarded IP). When true, no geography signal is required and countryCode is ignored. */
|
|
595
606
|
includeAllRegions?: boolean;
|
|
596
607
|
/** @description Optional page size (1..100). OMIT to return ALL eligible offers (the default) — catalogs are small enough that you normally take the whole set and curate client-side. Provide only if you want to page (with offset). */
|
|
@@ -629,6 +640,8 @@ export interface paths {
|
|
|
629
640
|
/** @description New/discounted price for the offer */
|
|
630
641
|
newPrice: string | null;
|
|
631
642
|
targetCountries: string[];
|
|
643
|
+
/** @description When true, this is a demo offer: a fictional brand served only to pk_test_* keys so an integration can be exercised end to end without contacting a real advertiser. Surface it as such rather than presenting it as real inventory. Absent or false means ordinary inventory. */
|
|
644
|
+
isDemo?: boolean;
|
|
632
645
|
organization?: {
|
|
633
646
|
/** Format: uuid */
|
|
634
647
|
id: string;
|
|
@@ -724,7 +737,7 @@ export interface paths {
|
|
|
724
737
|
};
|
|
725
738
|
get?: never;
|
|
726
739
|
put?: never;
|
|
727
|
-
/** @description Select one eligible offer for a third-party messaging surface (WhatsApp, Messenger, Instagram) and return the raw offer fields
|
|
740
|
+
/** @description DEPRECATED (2026-08-24): kept serving for existing integrators; new integrations use /offers/feed. Select one eligible offer for a third-party messaging surface (WhatsApp, Messenger, Instagram) and return the raw offer fields, the branded short clickUrl plus context (title, description, imageUrl, advertiserName, perk), so the integrator composes their own message. Calling this endpoint is NOT itself the impression: it emits sdk_offer_requested and returns impressionUid; confirm display via POST /offers/impressions/{impressionUid}/delivered once the message is delivered. Authentication: publishable key (X-API-Key: pk_live_* / pk_test_*). Retries are NOT deduplicated server-side (no retry-stable client key on the request), so each call produces a distinct short link and analytics row. */
|
|
728
741
|
post: {
|
|
729
742
|
parameters: {
|
|
730
743
|
query?: never;
|
|
@@ -751,6 +764,8 @@ export interface paths {
|
|
|
751
764
|
*/
|
|
752
765
|
platform?: "android" | "ios" | "web";
|
|
753
766
|
};
|
|
767
|
+
/** @description Optional publisher-defined label for the surface this offer is shown on (e.g. 'cancel_flow', 'home_feed', 'post_purchase'). Stamped onto the impression AND onto the click it produces, so publisher analytics can break the funnel out by placement. Normalised server-side: a blank value is treated as absent, anything longer than 64 characters is TRUNCATED rather than rejected, and the stored label NEVER carries leading or trailing whitespace — including after truncation, so ' my label ' and 'my label' are always one placement and can never show up as two rows. Case IS significant: 'Cancel_Flow' and 'cancel_flow' are different placements. Omit it and this traffic groups under '(unlabeled)' — the same bucket as an SDK integration that supplies no placement. */
|
|
768
|
+
placementId?: string;
|
|
754
769
|
};
|
|
755
770
|
};
|
|
756
771
|
};
|
|
@@ -764,6 +779,11 @@ export interface paths {
|
|
|
764
779
|
"application/json": {
|
|
765
780
|
/** @enum {boolean} */
|
|
766
781
|
success: true;
|
|
782
|
+
/**
|
|
783
|
+
* Format: uuid
|
|
784
|
+
* @description Handle for the served offer; POST /offers/impressions/{impressionUid}/delivered when the message is confirmed delivered to the user (that call is the impression). Absent when no offer was served.
|
|
785
|
+
*/
|
|
786
|
+
impressionUid?: string;
|
|
767
787
|
/** @description Selected offer details — the branded short clickUrl plus context fields — for the integrator to compose and send their own message, or null when no eligible offer was found. No message is composed server-side. */
|
|
768
788
|
offer: {
|
|
769
789
|
/**
|
|
@@ -782,6 +802,8 @@ export interface paths {
|
|
|
782
802
|
imageUrl: string;
|
|
783
803
|
/** @description Display name of the advertiser (organization) behind the offer. */
|
|
784
804
|
advertiserName: string;
|
|
805
|
+
/** @description When true, this is a demo offer: a fictional brand served only to pk_test_* keys so an integration can be exercised end to end without contacting a real advertiser. Surface it as such rather than presenting it as real inventory. Absent or false means ordinary inventory. */
|
|
806
|
+
isDemo?: boolean;
|
|
785
807
|
/** @description Campaign value proposition (e.g. "3 months of Apple Music free"), or null when the campaign has none. Surfaced explicitly so integrators can render the deal in the message they compose. */
|
|
786
808
|
perk?: string | null;
|
|
787
809
|
/**
|
|
@@ -846,7 +868,7 @@ export interface paths {
|
|
|
846
868
|
};
|
|
847
869
|
get?: never;
|
|
848
870
|
put?: never;
|
|
849
|
-
/** @description Return the bandit-ranked eligible offers for a user (up to `limit`, default 10) so a native / custom-UI app can render them and cycle through them client-side. Each offer carries its own impressionUid plus the branded short clickUrl and context (title, description, imageUrl, additionalImages, advertiserName, advertiserLogo, perk). Calling this endpoint is NOT itself the impression
|
|
871
|
+
/** @description Return the bandit-ranked eligible offers for a user (up to `limit`, default 10) so a native / custom-UI app can render them and cycle through them client-side. Each offer carries its own impressionUid plus the branded short clickUrl and context (title, description, imageUrl, additionalImages, advertiserName, advertiserLogo, perk). Calling this endpoint is NOT itself the impression: the server emits one sdk_offer_requested per offer as the requests baseline; confirm an actual impression by calling POST /offers/impressions/:impression_uid/delivered when an offer is shown. Returns offers:[] when no eligible offer was found. Authentication: publishable key (X-API-Key: pk_live_* / pk_test_*). */
|
|
850
872
|
post: {
|
|
851
873
|
parameters: {
|
|
852
874
|
query?: never;
|
|
@@ -878,6 +900,8 @@ export interface paths {
|
|
|
878
900
|
* @default 10
|
|
879
901
|
*/
|
|
880
902
|
limit?: number;
|
|
903
|
+
/** @description Optional publisher-defined label for the surface this offer is shown on (e.g. 'cancel_flow', 'home_feed', 'post_purchase'). Stamped onto the impression AND onto the click it produces, so publisher analytics can break the funnel out by placement. Normalised server-side: a blank value is treated as absent, anything longer than 64 characters is TRUNCATED rather than rejected, and the stored label NEVER carries leading or trailing whitespace — including after truncation, so ' my label ' and 'my label' are always one placement and can never show up as two rows. Case IS significant: 'Cancel_Flow' and 'cancel_flow' are different placements. Omit it and this traffic groups under '(unlabeled)' — the same bucket as an SDK integration that supplies no placement. */
|
|
904
|
+
placementId?: string;
|
|
881
905
|
};
|
|
882
906
|
};
|
|
883
907
|
};
|
|
@@ -900,6 +924,11 @@ export interface paths {
|
|
|
900
924
|
* @description Branded short click URL (https://link.encorekit.com/<slug>) that 302s to the advertiser with attribution. Open it in-app to register the click; also usable as a share-to-friend link.
|
|
901
925
|
*/
|
|
902
926
|
clickUrl: string;
|
|
927
|
+
/**
|
|
928
|
+
* Format: uuid
|
|
929
|
+
* @description Stable identifier for the offer (campaign). Use THIS to match a completion back to the offer you displayed: the `offer_completed` webhook carries the same value as its `campaignId`. Unlike `title` (which is per-creative and localized) and `campaignName` (an editable admin label), this never changes for a given offer.
|
|
930
|
+
*/
|
|
931
|
+
campaignId: string;
|
|
903
932
|
/** @description Creative headline/title for the offer. */
|
|
904
933
|
title: string;
|
|
905
934
|
/** @description Longer creative description, or null when the creative has none. */
|
|
@@ -914,6 +943,8 @@ export interface paths {
|
|
|
914
943
|
* @default []
|
|
915
944
|
*/
|
|
916
945
|
additionalImages: string[];
|
|
946
|
+
/** @description When true, this is a demo offer: a fictional brand served only to pk_test_* keys so an integration can be exercised end to end without contacting a real advertiser. Surface it as such rather than presenting it as real inventory. Absent or false means ordinary inventory. */
|
|
947
|
+
isDemo?: boolean;
|
|
917
948
|
/** @description Display name of the advertiser (organization) behind the offer. */
|
|
918
949
|
advertiserName: string;
|
|
919
950
|
/**
|
|
@@ -985,7 +1016,7 @@ export interface paths {
|
|
|
985
1016
|
};
|
|
986
1017
|
get?: never;
|
|
987
1018
|
put?: never;
|
|
988
|
-
/** @description
|
|
1019
|
+
/** @description Confirms an impression: call once per offer as it is displayed, for /offers/feed, /offers/catalog and /offers/message alike; the impression id comes from the serve response or the click url. Emits sdk_offer_presented keyed on the impression id; analytics stitches it to the serve in the warehouse. Authentication: publishable key (X-API-Key: pk_live_* / pk_test_*). */
|
|
989
1020
|
post: {
|
|
990
1021
|
parameters: {
|
|
991
1022
|
query?: never;
|
|
@@ -999,9 +1030,9 @@ export interface paths {
|
|
|
999
1030
|
requestBody: {
|
|
1000
1031
|
content: {
|
|
1001
1032
|
"application/json": {
|
|
1002
|
-
/** @description Unix seconds — Meta webhook `statuses[].timestamp` where status='delivered'. */
|
|
1033
|
+
/** @description Unix seconds — Meta webhook `statuses[].timestamp` where status='delivered'. Milliseconds are also accepted (any value above 1e11 is read as milliseconds), and a value that is not plausibly a real delivery is replaced by the receipt time on the analytics event; the value you send is always preserved verbatim in the event's properties. */
|
|
1003
1034
|
deliveredTimestamp: number;
|
|
1004
|
-
/** @description Unix seconds — Meta webhook `statuses[].timestamp` where status='read'. Optional. */
|
|
1035
|
+
/** @description Unix seconds — Meta webhook `statuses[].timestamp` where status='read'. Optional. Milliseconds are also accepted, exactly as for `deliveredTimestamp`. Unlike `deliveredTimestamp` there is no receipt-time substitution: a value that is not plausibly a real read is simply reported as null on the analytics event, since nothing depends on it. */
|
|
1005
1036
|
readTimestamp?: number;
|
|
1006
1037
|
};
|
|
1007
1038
|
};
|
|
@@ -2053,6 +2084,18 @@ export interface paths {
|
|
|
2053
2084
|
};
|
|
2054
2085
|
};
|
|
2055
2086
|
/** @description Default Response */
|
|
2087
|
+
429: {
|
|
2088
|
+
headers: {
|
|
2089
|
+
[name: string]: unknown;
|
|
2090
|
+
};
|
|
2091
|
+
content: {
|
|
2092
|
+
"application/json": {
|
|
2093
|
+
error: string;
|
|
2094
|
+
details?: unknown;
|
|
2095
|
+
};
|
|
2096
|
+
};
|
|
2097
|
+
};
|
|
2098
|
+
/** @description Default Response */
|
|
2056
2099
|
500: {
|
|
2057
2100
|
headers: {
|
|
2058
2101
|
[name: string]: unknown;
|
|
@@ -2880,7 +2923,7 @@ export interface paths {
|
|
|
2880
2923
|
};
|
|
2881
2924
|
get?: never;
|
|
2882
2925
|
put?: never;
|
|
2883
|
-
/** @description Simulate a conversion postback for the
|
|
2926
|
+
/** @description Simulate a conversion postback for the Halcyon Stream campaign. Unauthenticated; hard-scoped to the demo campaign at the service layer. */
|
|
2884
2927
|
post: {
|
|
2885
2928
|
parameters: {
|
|
2886
2929
|
query?: never;
|
|
@@ -4468,6 +4511,8 @@ export interface components {
|
|
|
4468
4511
|
priority: number | null;
|
|
4469
4512
|
/** @description When true, this campaign is auto-granted to every newly created app. Independent of priority (which controls ranking order only). */
|
|
4470
4513
|
defaultCatalog: boolean;
|
|
4514
|
+
/** @description When true, this is a demo offer: a fictional brand served only to pk_test_* keys so an integration can be exercised end to end without contacting a real advertiser. Surface it as such rather than presenting it as real inventory. Absent or false means ordinary inventory. */
|
|
4515
|
+
isDemo?: boolean;
|
|
4471
4516
|
/**
|
|
4472
4517
|
* @description Deprecated; always 'whitelist'. Retained because iOS/Android SDK decoders require the field. Access is governed solely by campaign_allowed_apps + enabled.
|
|
4473
4518
|
* @enum {string}
|
|
@@ -4654,6 +4699,8 @@ export interface components {
|
|
|
4654
4699
|
priority: number | null;
|
|
4655
4700
|
/** @description When true, this campaign is auto-granted to every newly created app. Independent of priority (which controls ranking order only). */
|
|
4656
4701
|
defaultCatalog: boolean;
|
|
4702
|
+
/** @description When true, this is a demo offer: a fictional brand served only to pk_test_* keys so an integration can be exercised end to end without contacting a real advertiser. Surface it as such rather than presenting it as real inventory. Absent or false means ordinary inventory. */
|
|
4703
|
+
isDemo?: boolean;
|
|
4657
4704
|
/**
|
|
4658
4705
|
* @description Deprecated; always 'whitelist'. Retained because iOS/Android SDK decoders require the field. Access is governed solely by campaign_allowed_apps + enabled.
|
|
4659
4706
|
* @enum {string}
|