@trillboards/api-client 0.2.0 → 0.4.1
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 +72 -10
- package/dist/index.d.mts +430 -45
- package/dist/index.d.ts +430 -45
- package/dist/index.js +261 -28
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +258 -29
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -45,6 +45,43 @@ interface ListResponse<T> {
|
|
|
45
45
|
has_more: boolean;
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
|
+
type ContentFitMode = 'fit' | 'fill';
|
|
49
|
+
interface ContentFitSettings {
|
|
50
|
+
/** Direct and programmatic advertisements. */
|
|
51
|
+
advertisements?: ContentFitMode;
|
|
52
|
+
/** Default/background streams. */
|
|
53
|
+
default_streams?: ContentFitMode;
|
|
54
|
+
/** Self-promotion and QR-code house ads. */
|
|
55
|
+
self_promo?: ContentFitMode;
|
|
56
|
+
}
|
|
57
|
+
interface PartnerDisplaySettingsInput {
|
|
58
|
+
content_fit?: ContentFitSettings;
|
|
59
|
+
[key: string]: unknown;
|
|
60
|
+
}
|
|
61
|
+
interface PartnerDisplaySettingsResponse {
|
|
62
|
+
object: 'display_settings' | string;
|
|
63
|
+
screen_id: string;
|
|
64
|
+
content_fit: Required<ContentFitSettings>;
|
|
65
|
+
[key: string]: unknown;
|
|
66
|
+
}
|
|
67
|
+
interface PartnerDeviceRegistrationInput {
|
|
68
|
+
/** Stable partner-owned device identifier. */
|
|
69
|
+
device_id?: string;
|
|
70
|
+
/** Legacy SDK alias retained for backwards compatibility. */
|
|
71
|
+
device_name?: string;
|
|
72
|
+
device_type?: string;
|
|
73
|
+
name?: string;
|
|
74
|
+
display?: Record<string, unknown>;
|
|
75
|
+
display_settings?: PartnerDisplaySettingsInput;
|
|
76
|
+
location?: Record<string, unknown>;
|
|
77
|
+
venue?: Record<string, unknown>;
|
|
78
|
+
placement?: Record<string, unknown>;
|
|
79
|
+
[key: string]: unknown;
|
|
80
|
+
}
|
|
81
|
+
interface PartnerDeviceBatchRegistrationInput {
|
|
82
|
+
devices: PartnerDeviceRegistrationInput[];
|
|
83
|
+
defaults?: Omit<PartnerDeviceRegistrationInput, 'device_id' | 'device_name'>;
|
|
84
|
+
}
|
|
48
85
|
interface Screen {
|
|
49
86
|
screen_id: string;
|
|
50
87
|
name: string;
|
|
@@ -160,6 +197,128 @@ interface BillingInfo {
|
|
|
160
197
|
usage_this_month: Record<string, number>;
|
|
161
198
|
stripe_connected: boolean;
|
|
162
199
|
}
|
|
200
|
+
/**
|
|
201
|
+
* Comparison operators accepted for a Route threshold condition — the same set
|
|
202
|
+
* the API validates against (services/fein/clipTriggerSchema VALID_OPERATORS).
|
|
203
|
+
*/
|
|
204
|
+
type RouteConditionOp = 'gt' | 'gte' | 'lt' | 'lte' | 'eq' | 'neq';
|
|
205
|
+
/** A threshold condition: fires when `field` crosses `value` via `op`. */
|
|
206
|
+
interface RouteCondition {
|
|
207
|
+
field: string;
|
|
208
|
+
op?: RouteConditionOp;
|
|
209
|
+
value?: number | string | boolean;
|
|
210
|
+
}
|
|
211
|
+
/** Channels a Route action can fire on. */
|
|
212
|
+
type RouteChannel = 'webhook' | 'whatsapp' | 'sms' | 'slack' | 'email' | 'clip';
|
|
213
|
+
/** One channel action fired when a Route matches. */
|
|
214
|
+
interface RouteAction {
|
|
215
|
+
channel: RouteChannel;
|
|
216
|
+
webhookId?: string;
|
|
217
|
+
recipientE164?: string;
|
|
218
|
+
recipientEmail?: string;
|
|
219
|
+
slackChannelId?: string;
|
|
220
|
+
templateName?: string;
|
|
221
|
+
attachClip?: boolean;
|
|
222
|
+
}
|
|
223
|
+
/** Scheduled-digest config (mode = 'digest'). */
|
|
224
|
+
interface RouteDigest {
|
|
225
|
+
metric: 'count' | 'avg' | 'max';
|
|
226
|
+
windowMinutes: number;
|
|
227
|
+
intervalMinutes: number;
|
|
228
|
+
nextRunAt?: string;
|
|
229
|
+
}
|
|
230
|
+
/** A Sense Anything Route — a (condition -> channel actions) alert/automation rule. */
|
|
231
|
+
interface SensingRoute {
|
|
232
|
+
routeId: string;
|
|
233
|
+
ownerKind: 'earner_user' | 'partner';
|
|
234
|
+
ownerId: string;
|
|
235
|
+
partnerId?: string | null;
|
|
236
|
+
name: string;
|
|
237
|
+
enabled: boolean;
|
|
238
|
+
profileName?: string | null;
|
|
239
|
+
mode: 'threshold' | 'digest';
|
|
240
|
+
condition: RouteCondition | null;
|
|
241
|
+
actions: RouteAction[];
|
|
242
|
+
screenMongoIds: string[];
|
|
243
|
+
sustainWindowSeconds?: number;
|
|
244
|
+
sustainMinSamples?: number;
|
|
245
|
+
debounceSeconds?: number;
|
|
246
|
+
activeTz?: string | null;
|
|
247
|
+
activeStartMinute?: number | null;
|
|
248
|
+
activeEndMinute?: number | null;
|
|
249
|
+
activeDaysMask?: number;
|
|
250
|
+
digest: RouteDigest | null;
|
|
251
|
+
createdAt: string;
|
|
252
|
+
updatedAt: string;
|
|
253
|
+
}
|
|
254
|
+
/** Request body to create a Route. */
|
|
255
|
+
interface CreateRouteParams {
|
|
256
|
+
name: string;
|
|
257
|
+
screenMongoIds: string[];
|
|
258
|
+
actions: RouteAction[];
|
|
259
|
+
mode?: 'threshold' | 'digest';
|
|
260
|
+
enabled?: boolean;
|
|
261
|
+
profileName?: string;
|
|
262
|
+
condition?: RouteCondition;
|
|
263
|
+
digest?: {
|
|
264
|
+
metric: 'count' | 'avg' | 'max';
|
|
265
|
+
field?: string;
|
|
266
|
+
windowMinutes: number;
|
|
267
|
+
intervalMinutes: number;
|
|
268
|
+
};
|
|
269
|
+
sustainWindowSeconds?: number;
|
|
270
|
+
sustainMinSamples?: number;
|
|
271
|
+
debounceSeconds?: number;
|
|
272
|
+
activeTz?: string;
|
|
273
|
+
activeStartMinute?: number;
|
|
274
|
+
activeEndMinute?: number;
|
|
275
|
+
activeDaysMask?: number;
|
|
276
|
+
}
|
|
277
|
+
/** Request body to update a Route (partial, non-clobbering). */
|
|
278
|
+
type UpdateRouteParams = Partial<CreateRouteParams>;
|
|
279
|
+
/**
|
|
280
|
+
* The webhook events a Route fire can deliver. The event type is in the
|
|
281
|
+
* `X-Trillboards-Event` request header: a custom sensed field delivers
|
|
282
|
+
* `sensing.threshold_crossed`, a known semantic delivers the matching
|
|
283
|
+
* `audience.*` / `venue.*` event, and a scheduled digest delivers
|
|
284
|
+
* `venue.traffic_summary`.
|
|
285
|
+
*/
|
|
286
|
+
type RouteWebhookEvent = 'sensing.threshold_crossed' | 'audience.spike' | 'audience.venue_busy' | 'audience.purchase_intent' | 'audience.demographics_update' | 'venue.traffic_summary';
|
|
287
|
+
/**
|
|
288
|
+
* The aggregate summary delivered as the `observed` value of a digest Route fire
|
|
289
|
+
* (`venue.traffic_summary`). The digest runner builds it over the Route's window.
|
|
290
|
+
*/
|
|
291
|
+
interface RouteDigestSummary {
|
|
292
|
+
metric: 'count' | 'avg' | 'max';
|
|
293
|
+
field: string | null;
|
|
294
|
+
/** The headline value for `metric`. null for an avg/max window with no samples. */
|
|
295
|
+
value: number | null;
|
|
296
|
+
count: number;
|
|
297
|
+
avg: number | null;
|
|
298
|
+
max: number | null;
|
|
299
|
+
window_minutes: number;
|
|
300
|
+
window_end: string;
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* The FLAT webhook request body delivered when a Route fires. Unlike the
|
|
304
|
+
* device/programmatic events there is NO `{event,timestamp,data}` wrapper — the
|
|
305
|
+
* event type rides the `X-Trillboards-Event` header. Verify the
|
|
306
|
+
* `X-Trillboards-Signature` header over the raw body before trusting it.
|
|
307
|
+
*
|
|
308
|
+
* `observed` is a scalar for threshold Routes; for a digest Route
|
|
309
|
+
* (`venue.traffic_summary`) it is a {@link RouteDigestSummary} object.
|
|
310
|
+
*/
|
|
311
|
+
interface SensingThresholdCrossedEvent {
|
|
312
|
+
route_id: string;
|
|
313
|
+
route_name: string;
|
|
314
|
+
owner_kind: 'earner_user' | 'partner';
|
|
315
|
+
screen_id: string;
|
|
316
|
+
field: string | null;
|
|
317
|
+
op: RouteConditionOp | null;
|
|
318
|
+
threshold: number | string | boolean | null;
|
|
319
|
+
observed: number | string | boolean | RouteDigestSummary | null;
|
|
320
|
+
fired_at: string;
|
|
321
|
+
}
|
|
163
322
|
|
|
164
323
|
/**
|
|
165
324
|
* Internal HTTP client for the Trillboards API.
|
|
@@ -173,6 +332,32 @@ interface HttpClientConfig {
|
|
|
173
332
|
baseUrl: string;
|
|
174
333
|
version?: string;
|
|
175
334
|
}
|
|
335
|
+
interface RetryOptions {
|
|
336
|
+
/** Maximum number of retry attempts after the initial call. Defaults to 6. */
|
|
337
|
+
retries?: number;
|
|
338
|
+
/** Base delay in ms for exponential backoff. Defaults to 1000. */
|
|
339
|
+
baseMs?: number;
|
|
340
|
+
/**
|
|
341
|
+
* Optional callback invoked when an attempt fails with a retryable error.
|
|
342
|
+
* `attempt` is 1-indexed (1 = first failure). Useful for observability —
|
|
343
|
+
* the CTV SDK uses this to emit `upload_error` events without coupling
|
|
344
|
+
* the retry loop to event emission.
|
|
345
|
+
*/
|
|
346
|
+
onAttemptError?: (attempt: number, error: unknown) => void;
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Wrap an async operation in retry-with-exponential-backoff.
|
|
350
|
+
*
|
|
351
|
+
* - Retries up to `retries` times on retryable errors (429, 5xx, TypeError network).
|
|
352
|
+
* - Honors `Retry-After` header on 429 responses (parsed into TrillboardsError.retryAfter).
|
|
353
|
+
* - Throws the last error after the budget is exhausted.
|
|
354
|
+
* - Permanent 4xx (auth, validation, not-found) throw immediately on first attempt.
|
|
355
|
+
*
|
|
356
|
+
* This is the SINGLE retry primitive in the SDK — every POST/PUT/DELETE is
|
|
357
|
+
* wrapped automatically by HttpClient. GET is left to the caller because
|
|
358
|
+
* idempotency assumptions vary by route.
|
|
359
|
+
*/
|
|
360
|
+
declare function retryWithBackoff<T>(operation: () => Promise<T>, opts?: RetryOptions): Promise<T>;
|
|
176
361
|
declare class HttpClient {
|
|
177
362
|
private readonly config;
|
|
178
363
|
constructor(config: HttpClientConfig);
|
|
@@ -180,10 +365,33 @@ declare class HttpClient {
|
|
|
180
365
|
get<T>(path: string, opts?: {
|
|
181
366
|
params?: Record<string, unknown>;
|
|
182
367
|
}): Promise<T>;
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
368
|
+
/**
|
|
369
|
+
* POST / PUT / PATCH / DELETE auto-retry on 429 + 5xx + network TypeError
|
|
370
|
+
* via `retryWithBackoff()`. Callers can opt into observability by passing
|
|
371
|
+
* `retry.onAttemptError` to receive a callback per transient failure.
|
|
372
|
+
*
|
|
373
|
+
* The retry behavior is identical across all four methods because all of
|
|
374
|
+
* them are state-changing operations subject to the same transient-failure
|
|
375
|
+
* classes (rate limit, upstream-down, network-flap). The wire idempotency
|
|
376
|
+
* of POST vs DELETE is enforced server-side via Idempotency-Key headers,
|
|
377
|
+
* which the caller can attach via `opts.headers`.
|
|
378
|
+
*/
|
|
379
|
+
post<T>(path: string, body?: unknown, opts?: {
|
|
380
|
+
retry?: RetryOptions;
|
|
381
|
+
headers?: Record<string, string>;
|
|
382
|
+
}): Promise<T>;
|
|
383
|
+
put<T>(path: string, body?: unknown, opts?: {
|
|
384
|
+
retry?: RetryOptions;
|
|
385
|
+
headers?: Record<string, string>;
|
|
386
|
+
}): Promise<T>;
|
|
387
|
+
patch<T>(path: string, body?: unknown, opts?: {
|
|
388
|
+
retry?: RetryOptions;
|
|
389
|
+
headers?: Record<string, string>;
|
|
390
|
+
}): Promise<T>;
|
|
391
|
+
delete<T>(path: string, opts?: {
|
|
392
|
+
retry?: RetryOptions;
|
|
393
|
+
headers?: Record<string, string>;
|
|
394
|
+
}): Promise<T>;
|
|
187
395
|
private buildUrl;
|
|
188
396
|
}
|
|
189
397
|
|
|
@@ -207,18 +415,18 @@ declare class ScreensClient {
|
|
|
207
415
|
data: Screen;
|
|
208
416
|
}>;
|
|
209
417
|
/** Register a new device/screen. */
|
|
210
|
-
create(body: {
|
|
211
|
-
device_name: string;
|
|
212
|
-
device_type?: string;
|
|
213
|
-
venue_type?: string;
|
|
214
|
-
country?: string;
|
|
215
|
-
city?: string;
|
|
216
|
-
timezone?: string;
|
|
217
|
-
resolution?: string;
|
|
218
|
-
}): Promise<{
|
|
418
|
+
create(body: PartnerDeviceRegistrationInput): Promise<{
|
|
219
419
|
success: boolean;
|
|
220
420
|
data: Screen;
|
|
221
421
|
}>;
|
|
422
|
+
/** Get the resolved display settings for one screen. */
|
|
423
|
+
getDisplaySettings(screenId: string): Promise<PartnerDisplaySettingsResponse>;
|
|
424
|
+
/** Partially update display settings; omitted content-fit keys are preserved. */
|
|
425
|
+
patchDisplaySettings(screenId: string, settings: PartnerDisplaySettingsInput): Promise<PartnerDisplaySettingsResponse>;
|
|
426
|
+
/** Update display settings through the PUT endpoint. */
|
|
427
|
+
updateDisplaySettings(screenId: string, settings: PartnerDisplaySettingsInput): Promise<PartnerDisplaySettingsResponse>;
|
|
428
|
+
/** Apply a partial display-settings update to up to 100 screens. */
|
|
429
|
+
batchUpdateDisplaySettings(screenIds: string[], settings: PartnerDisplaySettingsInput): Promise<Record<string, unknown>>;
|
|
222
430
|
/** Delete a screen/device. */
|
|
223
431
|
delete(screenId: string): Promise<{
|
|
224
432
|
success: boolean;
|
|
@@ -245,20 +453,32 @@ declare class DevicesClient {
|
|
|
245
453
|
data: Device;
|
|
246
454
|
}>;
|
|
247
455
|
/** Register a new device. */
|
|
248
|
-
create(body: {
|
|
249
|
-
device_name: string;
|
|
250
|
-
device_type?: string;
|
|
251
|
-
platform?: string;
|
|
252
|
-
}): Promise<{
|
|
456
|
+
create(body: PartnerDeviceRegistrationInput): Promise<{
|
|
253
457
|
success: boolean;
|
|
254
458
|
data: Device;
|
|
255
459
|
}>;
|
|
460
|
+
/** Register or re-register up to 100 devices with optional fleet defaults. */
|
|
461
|
+
createBatch(body: PartnerDeviceBatchRegistrationInput): Promise<{
|
|
462
|
+
success: boolean;
|
|
463
|
+
summary: Record<string, number>;
|
|
464
|
+
devices: Array<Record<string, unknown>>;
|
|
465
|
+
errors: Array<Record<string, unknown>>;
|
|
466
|
+
}>;
|
|
256
467
|
/** Delete a device. */
|
|
257
468
|
delete(deviceId: string): Promise<{
|
|
258
469
|
success: boolean;
|
|
259
470
|
}>;
|
|
260
|
-
/**
|
|
261
|
-
|
|
471
|
+
/**
|
|
472
|
+
* Send heartbeat for a device.
|
|
473
|
+
*
|
|
474
|
+
* Auto-retries on 429 + 5xx + network TypeError via the shared
|
|
475
|
+
* retryWithBackoff() helper (see http.ts). Pass `opts.retry.onAttemptError`
|
|
476
|
+
* to observe transient failures (e.g. emit an `upload_error` event from
|
|
477
|
+
* the CTV SDK without coupling the retry loop to its event emitter).
|
|
478
|
+
*/
|
|
479
|
+
heartbeat(deviceId: string, body?: Record<string, unknown>, opts?: {
|
|
480
|
+
retry?: RetryOptions;
|
|
481
|
+
}): Promise<{
|
|
262
482
|
success: boolean;
|
|
263
483
|
}>;
|
|
264
484
|
}
|
|
@@ -515,6 +735,57 @@ declare class BillingClient {
|
|
|
515
735
|
}>;
|
|
516
736
|
}
|
|
517
737
|
|
|
738
|
+
/**
|
|
739
|
+
* RoutesClient — Sense Anything "Routes": alerting + automation on the sensing
|
|
740
|
+
* stream. A Route fires channel actions (webhook / managed WhatsApp / SMS /
|
|
741
|
+
* Slack / email) when a sensed field crosses a threshold on a set of your
|
|
742
|
+
* screens, or on a scheduled digest.
|
|
743
|
+
*
|
|
744
|
+
* Base path: /v1/partner/sensing/routes
|
|
745
|
+
*/
|
|
746
|
+
|
|
747
|
+
declare class RoutesClient {
|
|
748
|
+
private readonly http;
|
|
749
|
+
constructor(http: HttpClient);
|
|
750
|
+
/** Create a Route. Every screen in `screenMongoIds` must be one you own. */
|
|
751
|
+
create(body: CreateRouteParams): Promise<{
|
|
752
|
+
success: boolean;
|
|
753
|
+
data: {
|
|
754
|
+
route: SensingRoute;
|
|
755
|
+
};
|
|
756
|
+
}>;
|
|
757
|
+
/** List your Routes. Pass `includeDisabled: false` to return only enabled Routes. */
|
|
758
|
+
list(params?: {
|
|
759
|
+
includeDisabled?: boolean;
|
|
760
|
+
}): Promise<{
|
|
761
|
+
success: boolean;
|
|
762
|
+
data: {
|
|
763
|
+
routes: SensingRoute[];
|
|
764
|
+
};
|
|
765
|
+
}>;
|
|
766
|
+
/** Get one Route by id. */
|
|
767
|
+
get(routeId: string): Promise<{
|
|
768
|
+
success: boolean;
|
|
769
|
+
data: {
|
|
770
|
+
route: SensingRoute;
|
|
771
|
+
};
|
|
772
|
+
}>;
|
|
773
|
+
/** Update a Route (partial, non-clobbering — only the fields you send change). */
|
|
774
|
+
update(routeId: string, body: UpdateRouteParams): Promise<{
|
|
775
|
+
success: boolean;
|
|
776
|
+
data: {
|
|
777
|
+
route: SensingRoute;
|
|
778
|
+
};
|
|
779
|
+
}>;
|
|
780
|
+
/** Soft-delete a Route. */
|
|
781
|
+
delete(routeId: string): Promise<{
|
|
782
|
+
success: boolean;
|
|
783
|
+
data: {
|
|
784
|
+
routeId: string;
|
|
785
|
+
};
|
|
786
|
+
}>;
|
|
787
|
+
}
|
|
788
|
+
|
|
518
789
|
/**
|
|
519
790
|
* TrillboardsClient — main entry point for the Trillboards Partner API SDK.
|
|
520
791
|
*
|
|
@@ -537,6 +808,7 @@ declare class TrillboardsClient {
|
|
|
537
808
|
readonly data: DataClient;
|
|
538
809
|
readonly errors: ErrorsClient;
|
|
539
810
|
readonly billing: BillingClient;
|
|
811
|
+
readonly routes: RoutesClient;
|
|
540
812
|
/** The underlying HTTP client (exposed for advanced use cases). */
|
|
541
813
|
readonly _http: HttpClient;
|
|
542
814
|
constructor(options: ClientOptions);
|
|
@@ -600,46 +872,133 @@ declare class TrillboardsError extends Error {
|
|
|
600
872
|
}
|
|
601
873
|
|
|
602
874
|
/**
|
|
603
|
-
* Webhook signature verification for Trillboards
|
|
875
|
+
* Webhook signature verification for Trillboards webhooks.
|
|
876
|
+
*
|
|
877
|
+
* Header format produced by the server (matches Stripe-style compound shape):
|
|
878
|
+
*
|
|
879
|
+
* X-Trillboards-Signature: t=<unix_seconds>,v0=<hex>,v1=<hex>
|
|
880
|
+
* X-Trillboards-Timestamp: <unix_seconds> // redundant; same value as t=
|
|
881
|
+
* X-Trillboards-Event: <event_name>
|
|
882
|
+
* X-Trillboards-Webhook-Id: <subscription_id>
|
|
883
|
+
*
|
|
884
|
+
* Where v1 = HMAC-SHA256(secret, `${timestamp}.${payload}`)
|
|
885
|
+
* v0 = HMAC-SHA256(secret, payload) ← legacy body-only, emitted for back-compat
|
|
604
886
|
*
|
|
605
|
-
*
|
|
606
|
-
*
|
|
887
|
+
* `constructEvent` prefers v1 (replay-protected); falls back to v0 when v1 is
|
|
888
|
+
* absent (server in pre-migration state) or when only the legacy
|
|
889
|
+
* `sha256=<hex>` form is present.
|
|
890
|
+
*
|
|
891
|
+
* Replay protection: a configurable tolerance window (default 300s, Stripe-
|
|
892
|
+
* aligned) rejects payloads whose timestamp is too far from the verifier's
|
|
893
|
+
* clock. Adjust via the `tolerance` option.
|
|
894
|
+
*
|
|
895
|
+
* Migration note (since 0.3.0):
|
|
896
|
+
* The 0.2.0-shape `constructEvent(payload, signatureString, secret)` is kept
|
|
897
|
+
* as `constructEventLegacy` for one minor version. It does NOT verify the
|
|
898
|
+
* timestamp (the 0.2.0 verifier had no access to one). Migrate to the new
|
|
899
|
+
* call shape `constructEvent(payload, req.headers, secret)` to gain replay
|
|
900
|
+
* protection and compound-header support.
|
|
607
901
|
*/
|
|
608
902
|
|
|
903
|
+
interface ConstructEventOptions {
|
|
904
|
+
/** Maximum allowed age (seconds) of a signed payload. Default 300 (Stripe-aligned). */
|
|
905
|
+
tolerance?: number;
|
|
906
|
+
/** Override the verifier's current time (seconds). Tests only. */
|
|
907
|
+
nowSec?: number;
|
|
908
|
+
}
|
|
909
|
+
/**
|
|
910
|
+
* Headers carried by a Trillboards webhook delivery. Accepts either the
|
|
911
|
+
* canonical Node `IncomingHttpHeaders` shape (lowercase keys) or any
|
|
912
|
+
* Record<string, string|string[]> with case-insensitive lookup.
|
|
913
|
+
*/
|
|
914
|
+
type WebhookHeaders = Record<string, string | string[] | undefined> | {
|
|
915
|
+
[key: string]: string | string[] | undefined;
|
|
916
|
+
};
|
|
917
|
+
/**
|
|
918
|
+
* Parsed signature-header components.
|
|
919
|
+
*/
|
|
920
|
+
interface ParsedSignatureHeader {
|
|
921
|
+
/** Unix seconds from `t=` if present. */
|
|
922
|
+
timestamp?: number;
|
|
923
|
+
/** Algorithm → hex-digest map. Standard keys: `v0`, `v1`. */
|
|
924
|
+
versions: Map<string, string>;
|
|
925
|
+
/** Set when the raw input matched the legacy `sha256=<hex>` shape. */
|
|
926
|
+
legacy?: string;
|
|
927
|
+
}
|
|
928
|
+
/**
|
|
929
|
+
* Parse a Trillboards `X-Trillboards-Signature` header into its component
|
|
930
|
+
* algorithm-keyed digests.
|
|
931
|
+
*
|
|
932
|
+
* Accepts:
|
|
933
|
+
* "t=<ts>,v1=<hex>" → canonical
|
|
934
|
+
* "t=<ts>,v0=<hex>,v1=<hex>" → canonical with legacy companion
|
|
935
|
+
* "sha256=<hex>" → pre-migration emitter (single value)
|
|
936
|
+
*
|
|
937
|
+
* @public
|
|
938
|
+
*/
|
|
939
|
+
declare function parseSignatureHeader(rawSig: string): ParsedSignatureHeader;
|
|
609
940
|
/**
|
|
610
941
|
* Verify a webhook payload signature and parse the event.
|
|
611
942
|
*
|
|
612
|
-
* @param payload
|
|
613
|
-
*
|
|
614
|
-
* @param
|
|
615
|
-
*
|
|
616
|
-
*
|
|
943
|
+
* @param payload — raw request body (string or Buffer). Must be EXACTLY the
|
|
944
|
+
* bytes the server signed — do NOT re-stringify after JSON.parse.
|
|
945
|
+
* @param headers — IncomingHttpHeaders or any Record<string, string|string[]>
|
|
946
|
+
* produced by your HTTP framework. Case-insensitive lookup.
|
|
947
|
+
* For Express, pass `req.headers`. For Next.js, `req.headers`.
|
|
948
|
+
* Backwards-compat: a raw signature string (the 0.2.0 shape)
|
|
949
|
+
* is also accepted and parsed as if it were the value of
|
|
950
|
+
* `X-Trillboards-Signature`; in that case the verifier looks
|
|
951
|
+
* for the timestamp in the compound header itself, OR throws.
|
|
952
|
+
* @param secret — webhook subscription secret (whsec_...).
|
|
953
|
+
* @param opts.tolerance — replay-window in seconds. Default 300.
|
|
954
|
+
* @returns parsed WebhookEvent if signature valid AND timestamp within tolerance.
|
|
955
|
+
* @throws TrillboardsError(authentication_error / webhook_signature_invalid)
|
|
617
956
|
*
|
|
618
957
|
* @example
|
|
619
|
-
* ```typescript
|
|
620
958
|
* import { Webhooks } from '@trillboards/api-client';
|
|
621
959
|
*
|
|
622
|
-
* app.post('/webhooks', (req, res) => {
|
|
623
|
-
*
|
|
624
|
-
*
|
|
625
|
-
*
|
|
626
|
-
*
|
|
627
|
-
*
|
|
628
|
-
*
|
|
629
|
-
*
|
|
960
|
+
* app.post('/webhooks', express.raw({ type: 'application/json' }), (req, res) => {
|
|
961
|
+
* try {
|
|
962
|
+
* const event = Webhooks.constructEvent(
|
|
963
|
+
* req.body, // raw Buffer or string
|
|
964
|
+
* req.headers, // IncomingHttpHeaders
|
|
965
|
+
* process.env.WEBHOOK_SECRET!, // your subscription secret
|
|
966
|
+
* );
|
|
967
|
+
* console.log('Received', event.type);
|
|
968
|
+
* res.sendStatus(200);
|
|
969
|
+
* } catch (err) {
|
|
970
|
+
* // TrillboardsError with code "webhook_signature_invalid"
|
|
971
|
+
* res.sendStatus(401);
|
|
972
|
+
* }
|
|
630
973
|
* });
|
|
631
|
-
* ```
|
|
632
974
|
*/
|
|
633
|
-
declare function constructEvent(payload: string | Buffer,
|
|
975
|
+
declare function constructEvent(payload: string | Buffer, headers: WebhookHeaders | string, secret: string, opts?: ConstructEventOptions): WebhookEvent;
|
|
976
|
+
/**
|
|
977
|
+
* @deprecated since 0.3.0 — kept for one minor version for partners stuck on
|
|
978
|
+
* the 0.2.0 call shape. Use `constructEvent(payload, req.headers, secret)`
|
|
979
|
+
* instead — it gains replay protection and compound-header support.
|
|
980
|
+
*
|
|
981
|
+
* Reproduces the 0.2.0 verifier exactly:
|
|
982
|
+
* HMAC-SHA256(secret, payload) compared (constant-time) against the
|
|
983
|
+
* supplied raw-hex signature string.
|
|
984
|
+
*
|
|
985
|
+
* Does NOT verify any timestamp. The 0.2.0 SDK never had access to one and
|
|
986
|
+
* silently accepted any age of payload — that is a vulnerability this
|
|
987
|
+
* legacy shim preserves only for migration compatibility.
|
|
988
|
+
*/
|
|
989
|
+
declare function constructEventLegacy(payload: string | Buffer, signature: string, secret: string): WebhookEvent;
|
|
990
|
+
/** Namespaced export — matches the Stripe-style API. */
|
|
634
991
|
declare const Webhooks: {
|
|
635
992
|
constructEvent: typeof constructEvent;
|
|
993
|
+
constructEventLegacy: typeof constructEventLegacy;
|
|
994
|
+
parseSignatureHeader: typeof parseSignatureHeader;
|
|
636
995
|
};
|
|
637
996
|
|
|
638
997
|
/**
|
|
639
998
|
* SDK version constant. Sent in User-Agent and X-Trillboards-Client headers.
|
|
640
999
|
* Keep in sync with package.json version.
|
|
641
1000
|
*/
|
|
642
|
-
declare const VERSION = "0.1
|
|
1001
|
+
declare const VERSION = "0.4.1";
|
|
643
1002
|
|
|
644
1003
|
type BleScanResultData = {
|
|
645
1004
|
appleContinuitySubtype?: number | null;
|
|
@@ -861,6 +1220,32 @@ type PartnerHeartbeatRequest = {
|
|
|
861
1220
|
last_vast_error_code?: number;
|
|
862
1221
|
last_vast_request_at?: string;
|
|
863
1222
|
last_vast_response_at?: string;
|
|
1223
|
+
/** Current GPS fix for a moving screen. Processed only for screens whose `mobility_type` is `mobile` or `semi_mobile` (declare it at registration or via PATCH /device/{deviceId}), OR for any screen still missing coordinates (a one-time self-heal that also kicks off venue classification). Static screens with valid coordinates ignore it. Updates live geo-targeting, geofencing and analytics. Fully optional — omit it on stationary screens. */
|
|
1224
|
+
location?: {
|
|
1225
|
+
/** Horizontal accuracy radius in meters. Fixes above ~500m are clamped. */
|
|
1226
|
+
accuracy_m?: number;
|
|
1227
|
+
/** Up to 200 fixes recorded while the device was offline, replayed as a historical track. Timestamps MUST be in ascending order — a non-chronological batch is rejected with HTTP 400. Only the top-level `location` updates the live serving position. */
|
|
1228
|
+
buffered_locations?: Array<{
|
|
1229
|
+
accuracy_m?: number;
|
|
1230
|
+
heading?: number;
|
|
1231
|
+
lat: number;
|
|
1232
|
+
lng: number;
|
|
1233
|
+
speed_mps?: number;
|
|
1234
|
+
timestamp?: number;
|
|
1235
|
+
}>;
|
|
1236
|
+
/** Bearing in degrees (0–360, 0 = true north). Omit if unavailable. */
|
|
1237
|
+
heading?: number;
|
|
1238
|
+
/** Latitude in decimal degrees (WGS 84). */
|
|
1239
|
+
lat?: number;
|
|
1240
|
+
/** Longitude in decimal degrees (WGS 84). */
|
|
1241
|
+
lng?: number;
|
|
1242
|
+
/** `gps` — a live on-device fix. `gps_buffered` — a fix recorded while offline and replayed on reconnect. */
|
|
1243
|
+
source?: "gps" | "gps_buffered";
|
|
1244
|
+
/** Ground speed in meters per second. Omit if unavailable. */
|
|
1245
|
+
speed_mps?: number;
|
|
1246
|
+
/** Unix epoch milliseconds of the on-device fix. Send the device clock value; the server tolerates up to 10 minutes of skew. A position jump implying > 320 km/h between consecutive fixes is rejected. */
|
|
1247
|
+
timestamp?: number;
|
|
1248
|
+
};
|
|
864
1249
|
/** Alternate namespace for telemetry keys (legacy support). */
|
|
865
1250
|
metadata?: Record<string, unknown>;
|
|
866
1251
|
muted?: boolean;
|
|
@@ -1018,12 +1403,12 @@ interface heartbeatOperation {
|
|
|
1018
1403
|
response: PartnerHeartbeatResponse;
|
|
1019
1404
|
}
|
|
1020
1405
|
declare const generatedTypes: {
|
|
1021
|
-
readonly schemaCount:
|
|
1022
|
-
readonly operationCount:
|
|
1023
|
-
readonly schemas: readonly ["Ad", "AdSettings", "AudienceAnalyticsResponse", "AudienceExportResponse", "AudienceMetricsResponse", "AudienceSegment", "BatchResult", "BleScanResultData", "ChangelogEntry", "ChangelogResponse", "ContentPreferencesInput", "ContentPreferencesResponse", "ContentPreset", "DashboardResponse", "DemographicsResponse", "Device", "DeviceCapabilityPayload", "DeviceSummary", "DisplaySettingsInput", "DisplaySettingsResponse", "EarningsBreakdownResponse", "EarningsResponse", "Error", "HeaderBiddingSettings", "HttpProbeResult", "MdnsNetworkDevice", "Pagination", "Partner", "PartnerAdDeliveryProfile", "PartnerDefaultStream", "PartnerDefaultStreamCreate", "PartnerDefaultStreamUpdate", "PartnerDeviceHeartbeatBody", "PartnerHeartbeatCommand", "PartnerHeartbeatData", "PartnerHeartbeatMeasurementMetadata", "PartnerHeartbeatQueryData", "PartnerHeartbeatQueryResponse", "PartnerHeartbeatRequest", "PartnerHeartbeatResponse", "PartnerHeartbeatTelemetry", "PartnerLBarContent", "PartnerLBarDisplayPreferences", "PartnerLBarSettings", "PartnerSafeHeartbeatTelemetry", "PartnerSelfPromoAd", "PartnerSelfPromoCreate", "PartnerSelfPromoPlacement", "PartnerStats", "PayoutResponse", "PlaygroundResult", "PlaygroundValidationError", "ProgrammaticSettingsInput", "ProgrammaticSettingsResponse", "RequestMetadataResponse", "RolePermissions", "SDKConfig", "ScreenAnalyticsResponse", "SsdpDeviceInfo", "TeamInviteResponse", "TeamMember", "Transaction", "Webhook", "WebhookDelivery", "WebhookEvent", "WifiEnvironmentSnapshot", "WifiScanResult"];
|
|
1024
|
-
readonly operations: readonly ["listPartnerSelfPromoAds", "createPartnerSelfPromoAd", "listPartnerSelfPromoPlacements", "schedulePartnerSelfPromo", "createPartnerSelfPromoUpload", "
|
|
1406
|
+
readonly schemaCount: 99;
|
|
1407
|
+
readonly operationCount: 210;
|
|
1408
|
+
readonly schemas: readonly ["Ad", "AdSettings", "AudienceAnalyticsResponse", "AudienceAttentionPrediction", "AudienceExportResponse", "AudienceLoyaltyFeaturesRow", "AudienceMetricsResponse", "AudienceSegment", "AudienceSocialAttention", "BatchResult", "BleScanResultData", "ChangelogEntry", "ChangelogResponse", "ContentFitSettings", "ContentPreferencesInput", "ContentPreferencesResponse", "ContentPreset", "CreatePartnerSensingRouteRequest", "CreativeClassification", "DashboardResponse", "DataExportSummaryResponse", "DemographicsResponse", "Device", "DeviceCapabilityPayload", "DeviceSummary", "DisplaySettingsInput", "DisplaySettingsResponse", "EarningsBreakdownResponse", "EarningsResponse", "Error", "FleetCommand", "FleetCommandEvent", "HeaderBiddingSettings", "HttpProbeResult", "MdnsNetworkDevice", "Pagination", "Partner", "PartnerAdDeliveryProfile", "PartnerDefaultStream", "PartnerDefaultStreamCreate", "PartnerDefaultStreamUpdate", "PartnerDeviceHeartbeatBody", "PartnerHeartbeatCommand", "PartnerHeartbeatData", "PartnerHeartbeatMeasurementMetadata", "PartnerHeartbeatQueryData", "PartnerHeartbeatQueryResponse", "PartnerHeartbeatRequest", "PartnerHeartbeatResponse", "PartnerHeartbeatTelemetry", "PartnerLBarContent", "PartnerLBarDisplayPreferences", "PartnerLBarSettings", "PartnerMonetizationRequirements", "PartnerNetworkConfig", "PartnerNetworkConfigUpdate", "PartnerPlay", "PartnerRegistrationDisplaySettings", "PartnerRequirement", "PartnerSafeHeartbeatTelemetry", "PartnerSelfPromoAd", "PartnerSelfPromoCreate", "PartnerSelfPromoPlacement", "PartnerStats", "PayoutResponse", "PlaygroundResult", "PlaygroundValidationError", "ProgrammaticSettingsInput", "ProgrammaticSettingsResponse", "RequestMetadataResponse", "ResolvedContentPolicy", "ResolvedSensingConfig", "RolePermissions", "SDKConfig", "ScreenAnalyticsResponse", "ScreenGroup", "ScreenGroupContentPolicy", "ScreenGroupContentPolicyInput", "ScreenGroupEvent", "ScreenGroupSensingConfig", "ScreenGroupSensingConfigInput", "SensingPerformanceMode", "SensingRoute", "SensingRouteAction", "SensingRouteCondition", "SensingSettingsResponse", "SensingThresholdCrossedEvent", "SsdpDeviceInfo", "TeamInviteResponse", "TeamMember", "Transaction", "UpdatePartnerSensingRouteRequest", "VastWaterfall", "VastWaterfallSource", "Webhook", "WebhookDelivery", "WebhookEvent", "WifiEnvironmentSnapshot", "WifiScanResult"];
|
|
1409
|
+
readonly operations: readonly ["listPartnerSelfPromoAds", "createPartnerSelfPromoAd", "listPartnerSelfPromoPlacements", "schedulePartnerSelfPromo", "createPartnerSelfPromoUpload", "disableSelfPromoAd", "registerAgent", "getAnalytics", "getAudienceContinuity", "getAudienceProfile", "getBleSummary", "getContentPerformance", "searchCreativeCatalog", "getCreativeScenes", "getCrossScreenFlow", "getPartnerAnalyticsDashboard", "getDeviceSuiteSignature", "exportPartnerAnalytics", "exportPartnerAdsByDay", "getFleetHourly", "getHaloFunnel", "getLoyaltyClock", "getNetworkSummary", "getNetworkTrend", "getProximitySummary", "getRevenueAttentionCorrelation", "getSceneFeed", "getSceneIntelligence", "rotateApiKey", "getAudienceAnalytics", "exportAudienceData", "getAudienceHeatmap", "getLiveAudience", "getLookalikeScreens", "predictScreenAudience", "getResolvedHouseholdReach", "listAudienceSegments", "createAudienceSegment", "deleteAudienceSegment", "getAudienceSegment", "updateAudienceSegment", "matchAudienceSegment", "estimateSegmentReach", "generateSegmentVastTag", "getAudienceStats", "cancelBillingSubscription", "purchaseCredits", "createCreditCheckout", "listBillingPlans", "setupBilling", "createBillingSubscription", "getBillingSubscription", "getBillingUsage", "getUsageSummary", "stripeWebhookCallback", "getConnectDashboard", "getConnectEarningsChart", "getConnectScreenHealth", "listContentPresets", "getContentPreset", "submitPartnerCreative", "getPartnerCreative", "getDashboard", "exportProximityGraphEdges", "getDataExportSummary", "exportDataset", "getGeohashAudiencePanel", "getOutcomeAttribution", "registerDevice", "deleteDevice", "getDevice", "updateDevice", "getAdsForDevice", "ackDeviceCommand", "getDeviceHeartbeat", "heartbeat", "recordProgrammaticEvent", "listDevices", "registerDeviceV2", "registerDevicesBatch", "batchUpdateDeviceOrientation", "reconcileDevices", "partnerDocsBrowser", "getEarnings", "getEarningsBreakdown", "requestPayout", "getTransactions", "getActiveSSEConnections", "subscribeToEvents", "getFleetCommand", "getFleetAnalytics", "pushFleetCommand", "getFleetOverview", "updateFleetSettings", "recordImpressionGet", "recordImpressionPost", "recordImpressionsBatch", "getPartnerInfo", "listIntentCategories", "getIntentCategory", "getIntentPricing", "getMeasurementMethodology", "getNetworkConfig", "patchNetworkConfig", "updateNetworkConfig", "getNetworkTrends", "partnerPing", "partnerPingAuth", "recordPartnerPlay", "recordPartnerPlaysBatch", "getPricing", "recordProgrammaticEventLegacyAlias", "quickStart", "registerPartner", "generateSandboxEvent", "resetSandbox", "createSandboxScreen", "getSandboxStatus", "listScreenGroups", "createScreenGroup", "deleteScreenGroup", "getScreenGroup", "updateScreenGroup", "getScreenGroupAnalytics", "createScreenGroupCommand", "getScreenGroupContentPolicy", "updateScreenGroupContentPolicy", "removeScreenGroupMembers", "listScreenGroupMembers", "addScreenGroupMembers", "getScreenGroupSensingConfig", "updateScreenGroupSensingConfig", "getScreenAudienceSegments", "batchUpdateContentPreferences", "batchUpdateDisplaySettings", "batchUpdateProgrammaticSettings", "resolvePartnerScreen", "getScreenAnalytics", "getPartnerScreenAtmosphere", "getPartnerScreenIntelligence", "getPartnerScreenTasteProfile", "getPartnerScreenTraffic", "getPartnerScreenVAS", "applyContentPreset", "getAudienceMetrics", "getContentPreferences", "patchContentPreferences", "updateContentPreferences", "listPartnerDefaultStreams", "createPartnerDefaultStream", "createPartnerDefaultStreamImages", "reorderPartnerDefaultStreams", "createPartnerDefaultStreamUpload", "deletePartnerDefaultStream", "updatePartnerDefaultStream", "getScreenDemographics", "getDisplaySettings", "patchDisplaySettings", "updateDisplaySettings", "getPartnerLBarSettings", "updatePartnerLBarSettings", "updatePartnerLBarLiveUpdates", "getProgrammaticSettings", "patchProgrammaticSettings", "updateProgrammaticSettings", "getResolvedContentPolicy", "getResolvedSensingConfig", "getScreenSensingChips", "interpretScreenSensingIntent", "deployScreenSensingProfile", "getScreenSensingSettings", "updateScreenSensingSettings", "listPartnerSensingRoutes", "createPartnerSensingRoute", "deletePartnerSensingRoute", "getPartnerSensingRoute", "updatePartnerSensingRoute", "createStripeConnectAccount", "getStripeLoginLink", "getStripeOnboardingLink", "requestStripePayout", "getStripePayoutHistory", "getStripeAccountStatus", "handleStripeWebhook", "listTeamMembers", "acceptInvitation", "inviteTeamMember", "getInvitationByToken", "resendInvitation", "removeTeamMember", "changeTeamMemberRole", "getTrackingPublicKey", "recordTrackingBatch", "getUsageSummary__get__usage", "getUsageDashboard", "getRateLimitStatus", "queryChangelog", "playgroundDryRun", "getRequestMetadata", "getVastConfig", "getVenuesNearby", "getVenueAtmosphere", "getVenueTraffic", "getVenueIntelligence", "listWebhooks", "createWebhook", "deleteWebhook", "updateWebhook", "getWebhookDeliveries", "testWebhook"];
|
|
1025
1410
|
};
|
|
1026
1411
|
type GeneratedSchemaName = (typeof generatedTypes.schemas)[number];
|
|
1027
1412
|
type GeneratedOperationName = (typeof generatedTypes.operations)[number];
|
|
1028
1413
|
|
|
1029
|
-
export { AnalyticsClient, type AnalyticsData, type ApiErrorType, AudienceClient, type AudienceData, BillingClient, type BillingInfo, type BleScanResultData, type ClientOptions, DataClient, type Device, type DeviceCapabilityPayload, DevicesClient, EarningsClient, type EarningsData, type ErrorDetail, type ErrorEntry, type ErrorResponseBody, ErrorsClient, FleetClient, type FleetCommand, type GeneratedOperationName, type GeneratedSchemaName, type HttpProbeResult, type Impression, ImpressionsClient, type ListResponse, type MdnsNetworkDevice, type PartnerAdDeliveryProfile, type PartnerDeviceHeartbeatBody, type PartnerHeartbeatMeasurementMetadata, type PartnerHeartbeatQueryData, type PartnerHeartbeatQueryResponse, type PartnerHeartbeatRequest, type PartnerHeartbeatResponse, type PartnerSafeHeartbeatTelemetry, type RequestOptions, type Screen, ScreensClient, type SsdpDeviceInfo, TrillboardsClient, TrillboardsError, type TrillboardsErrorOptions, VERSION, type Webhook, type WebhookEvent, Webhooks, WebhooksClient, type WifiEnvironmentSnapshot, type WifiScanResult, constructEvent, generatedTypes, type getDeviceHeartbeatOperation, type heartbeatOperation };
|
|
1414
|
+
export { AnalyticsClient, type AnalyticsData, type ApiErrorType, AudienceClient, type AudienceData, BillingClient, type BillingInfo, type BleScanResultData, type ClientOptions, type ConstructEventOptions, type ContentFitMode, type ContentFitSettings, type CreateRouteParams, DataClient, type Device, type DeviceCapabilityPayload, DevicesClient, EarningsClient, type EarningsData, type ErrorDetail, type ErrorEntry, type ErrorResponseBody, ErrorsClient, FleetClient, type FleetCommand, type GeneratedOperationName, type GeneratedSchemaName, type HttpProbeResult, type Impression, ImpressionsClient, type ListResponse, type MdnsNetworkDevice, type ParsedSignatureHeader, type PartnerAdDeliveryProfile, type PartnerDeviceBatchRegistrationInput, type PartnerDeviceHeartbeatBody, type PartnerDeviceRegistrationInput, type PartnerDisplaySettingsInput, type PartnerDisplaySettingsResponse, type PartnerHeartbeatMeasurementMetadata, type PartnerHeartbeatQueryData, type PartnerHeartbeatQueryResponse, type PartnerHeartbeatRequest, type PartnerHeartbeatResponse, type PartnerSafeHeartbeatTelemetry, type RequestOptions, type RetryOptions, type RouteAction, type RouteChannel, type RouteCondition, type RouteConditionOp, type RouteDigest, type RouteDigestSummary, type RouteWebhookEvent, RoutesClient, type Screen, ScreensClient, type SensingRoute, type SensingThresholdCrossedEvent, type SsdpDeviceInfo, TrillboardsClient, TrillboardsError, type TrillboardsErrorOptions, type UpdateRouteParams, VERSION, type Webhook, type WebhookEvent, type WebhookHeaders, Webhooks, WebhooksClient, type WifiEnvironmentSnapshot, type WifiScanResult, constructEvent, constructEventLegacy, generatedTypes, type getDeviceHeartbeatOperation, type heartbeatOperation, parseSignatureHeader, retryWithBackoff };
|