luchy 0.2.0 → 1.1.0

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.
@@ -118,7 +118,7 @@ export interface paths {
118
118
  patch?: never;
119
119
  trace?: never;
120
120
  };
121
- "/query": {
121
+ "/ingest/identify": {
122
122
  parameters: {
123
123
  query?: never;
124
124
  header?: never;
@@ -127,6 +127,7 @@ export interface paths {
127
127
  };
128
128
  get?: never;
129
129
  put?: never;
130
+ /** @description Stores person profile traits. Requires the project's secret key (`lsk_…`) as the bearer token; the public ingest key is rejected. */
130
131
  post: {
131
132
  parameters: {
132
133
  query?: never;
@@ -136,17 +137,17 @@ export interface paths {
136
137
  };
137
138
  requestBody?: {
138
139
  content: {
139
- "application/json": components["schemas"]["QueryRequest"];
140
+ "application/json": components["schemas"]["IdentifyInput"];
140
141
  };
141
142
  };
142
143
  responses: {
143
- /** @description Query executed successfully */
144
+ /** @description Profile stored */
144
145
  200: {
145
146
  headers: {
146
147
  [name: string]: unknown;
147
148
  };
148
149
  content: {
149
- "application/json": components["schemas"]["QueryResponse"];
150
+ "application/json": components["schemas"]["IdentifyResponse"];
150
151
  };
151
152
  };
152
153
  /** @description Invalid request body or validation error */
@@ -158,6 +159,15 @@ export interface paths {
158
159
  "application/json": components["schemas"]["ValidationErrorResponse"];
159
160
  };
160
161
  };
162
+ /** @description Missing or invalid secret key */
163
+ 401: {
164
+ headers: {
165
+ [name: string]: unknown;
166
+ };
167
+ content: {
168
+ "application/json": components["schemas"]["ErrorResponse"];
169
+ };
170
+ };
161
171
  /** @description Internal server error */
162
172
  500: {
163
173
  headers: {
@@ -210,6 +220,214 @@ export interface paths {
210
220
  patch?: never;
211
221
  trace?: never;
212
222
  };
223
+ "/analytics.query": {
224
+ parameters: {
225
+ query?: never;
226
+ header?: never;
227
+ path?: never;
228
+ cookie?: never;
229
+ };
230
+ get?: never;
231
+ put?: never;
232
+ /**
233
+ * Metrics for the project over a time range: totals, an optional breakdown, time series and a comparison with another period.
234
+ * @description Every requested metric is computed. visitors = distinct coalesce(user, visitor_hash); users = distinct identified users; avg_duration in seconds; bounce_rate in %.
235
+ *
236
+ * Add `granularity` for a zero-filled time series and `compare: "previous_period"` to also get the period before with deltas; the result renders as a chart.
237
+ *
238
+ * Example — who used the app this week vs last: `{ metrics: ["users", "events"], granularity: "day", compare: "previous_period" }`.
239
+ *
240
+ * Anything this cannot express goes through `analytics.sql`.
241
+ *
242
+ * Requires the `analytics:read` permission.
243
+ */
244
+ post: operations["analytics.query"];
245
+ delete?: never;
246
+ options?: never;
247
+ head?: never;
248
+ patch?: never;
249
+ trace?: never;
250
+ };
251
+ "/analytics.sql": {
252
+ parameters: {
253
+ query?: never;
254
+ header?: never;
255
+ path?: never;
256
+ cookie?: never;
257
+ };
258
+ get?: never;
259
+ put?: never;
260
+ /**
261
+ * Run one read-only SELECT or WITH statement over the analytics database, for anything `analytics.query` cannot express.
262
+ * @description Not scoped to a project: filter on `project_id = ?` and pass the current project's id (MCP: it's in the server instructions) in `params`. Rows are capped by `limit`.
263
+ *
264
+ * Luchy analytics database (SQLite / Cloudflare D1). Read-only: one SELECT or WITH statement.
265
+ * Timestamps are ISO-8601 UTC strings ('2025-01-31T12:00:00.000Z'); compare them as strings or use datetime()/strftime().
266
+ * Not scoped to a project: filter on project_id.
267
+ *
268
+ * projects — one tracked site/app
269
+ * id TEXT PK, name TEXT, created_at TEXT
270
+ *
271
+ * sessions — one visit by one (user, actor) or one anonymous visitor; 30 min idle timeout, 24 h cap
272
+ * id TEXT PK, project_id TEXT -> projects.id
273
+ * visitor_hash TEXT anonymous browser id (sha256 of a daily salt, project, ip, user agent); rotates daily
274
+ * user TEXT app-provided user id (signed identity), NULL when anonymous
275
+ * actor TEXT the real person when impersonating (user = who is viewed as), else NULL
276
+ * started_at TEXT, last_seen_at TEXT duration = (julianday(last_seen_at) - julianday(started_at)) * 86400 seconds
277
+ * browser TEXT, os TEXT, device TEXT ('desktop' | 'mobile' | 'tablet' | ...), country TEXT (ISO 3166-1 alpha-2), language TEXT
278
+ * entry_page TEXT pathname of the first event
279
+ *
280
+ * events — everything that happened, pageviews included
281
+ * id TEXT PK, project_id TEXT -> projects.id, session_id TEXT -> sessions.id
282
+ * type TEXT 'pageview' for pageviews; otherwise sender-defined ('server', 'click', ...) or NULL
283
+ * name TEXT 'pageview' for pageviews; event name otherwise ('__pageleave' marks a page exit)
284
+ * pathname TEXT, referrer TEXT
285
+ * tenant TEXT the consumer's client/workspace from the signed identity
286
+ * payload TEXT JSON object of props: json_extract(payload, '$.role')
287
+ * created_at TEXT
288
+ *
289
+ * people — person profiles sent via identify; one row per (project, user)
290
+ * project_id TEXT -> projects.id, user TEXT (same id as sessions.user)
291
+ * traits TEXT JSON object (name, email, ...): json_extract(traits, '$.email')
292
+ * created_at TEXT, updated_at TEXT
293
+ *
294
+ * Conventions
295
+ * visitors = COUNT(DISTINCT coalesce(s.user, s.visitor_hash)); users = COUNT(DISTINCT s.user)
296
+ * Join: FROM events e JOIN sessions s ON s.id = e.session_id
297
+ * Profiles: LEFT JOIN people p ON p.project_id = s.project_id AND p.user = s.user
298
+ * Alias duplicate column names: rows are objects keyed by column name.
299
+ *
300
+ * Requires the `analytics:read` permission.
301
+ */
302
+ post: operations["analytics.sql"];
303
+ delete?: never;
304
+ options?: never;
305
+ head?: never;
306
+ patch?: never;
307
+ trace?: never;
308
+ };
309
+ "/analytics.people": {
310
+ parameters: {
311
+ query?: never;
312
+ header?: never;
313
+ path?: never;
314
+ cookie?: never;
315
+ };
316
+ get?: never;
317
+ put?: never;
318
+ /**
319
+ * Identified people active in the period or the one before, with sessions and actions this period vs the previous one. Quiet people (active before, not now) come last.
320
+ * @description Requires the `analytics:read` permission.
321
+ */
322
+ post: operations["analytics.people"];
323
+ delete?: never;
324
+ options?: never;
325
+ head?: never;
326
+ patch?: never;
327
+ trace?: never;
328
+ };
329
+ "/analytics.customers": {
330
+ parameters: {
331
+ query?: never;
332
+ header?: never;
333
+ path?: never;
334
+ cookie?: never;
335
+ };
336
+ get?: never;
337
+ put?: never;
338
+ /**
339
+ * Customers (the `tenant` of events) active in the period or the one before: people and actions this period vs the previous one.
340
+ * @description Requires the `analytics:read` permission.
341
+ */
342
+ post: operations["analytics.customers"];
343
+ delete?: never;
344
+ options?: never;
345
+ head?: never;
346
+ patch?: never;
347
+ trace?: never;
348
+ };
349
+ "/analytics.person": {
350
+ parameters: {
351
+ query?: never;
352
+ header?: never;
353
+ path?: never;
354
+ cookie?: never;
355
+ };
356
+ get?: never;
357
+ put?: never;
358
+ /**
359
+ * One person, all time: profile traits, customers, who acted as them, and their latest sessions with what they did.
360
+ * @description Requires the `analytics:read` permission.
361
+ */
362
+ post: operations["analytics.person"];
363
+ delete?: never;
364
+ options?: never;
365
+ head?: never;
366
+ patch?: never;
367
+ trace?: never;
368
+ };
369
+ "/analytics.live": {
370
+ parameters: {
371
+ query?: never;
372
+ header?: never;
373
+ path?: never;
374
+ cookie?: never;
375
+ };
376
+ get?: never;
377
+ put?: never;
378
+ /**
379
+ * Who is on the site right now: open sessions with their current page, top pages, countries and referrers.
380
+ * @description Requires the `analytics:read` permission.
381
+ */
382
+ post: operations["analytics.live"];
383
+ delete?: never;
384
+ options?: never;
385
+ head?: never;
386
+ patch?: never;
387
+ trace?: never;
388
+ };
389
+ "/admin.projects": {
390
+ parameters: {
391
+ query?: never;
392
+ header?: never;
393
+ path?: never;
394
+ cookie?: never;
395
+ };
396
+ get?: never;
397
+ put?: never;
398
+ /**
399
+ * List every project (tracked site or app) with its id. Use the id as ?project=<id> on /mcp or /api to switch project.
400
+ * @description Requires the `admin` permission.
401
+ */
402
+ post: operations["admin.projects"];
403
+ delete?: never;
404
+ options?: never;
405
+ head?: never;
406
+ patch?: never;
407
+ trace?: never;
408
+ };
409
+ "/admin.keys": {
410
+ parameters: {
411
+ query?: never;
412
+ header?: never;
413
+ path?: never;
414
+ cookie?: never;
415
+ };
416
+ get?: never;
417
+ put?: never;
418
+ /**
419
+ * The current project's ingest keys (public, for the browser script) and its secret key (signs identities, authenticates identify).
420
+ * @description Query keys (`wak_…`) live in the willy.im IdP and are managed from the dashboard; they are not listed here.
421
+ *
422
+ * Requires the `admin` permission.
423
+ */
424
+ post: operations["admin.keys"];
425
+ delete?: never;
426
+ options?: never;
427
+ head?: never;
428
+ patch?: never;
429
+ trace?: never;
430
+ };
213
431
  }
214
432
  export type webhooks = Record<string, never>;
215
433
  export interface components {
@@ -275,6 +493,15 @@ export interface components {
275
493
  * @example https://google.com
276
494
  */
277
495
  referrer?: string;
496
+ /**
497
+ * @description Pageview properties
498
+ * @example {
499
+ * "plan": "pro"
500
+ * }
501
+ */
502
+ payload?: {
503
+ [key: string]: string | number | boolean;
504
+ };
278
505
  /**
279
506
  * @description The user agent string
280
507
  * @example Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36
@@ -286,10 +513,20 @@ export interface components {
286
513
  */
287
514
  language?: string;
288
515
  /**
289
- * @description The user country code
516
+ * @description The user country code. Wins over the country Cloudflare infers from the request.
290
517
  * @example US
291
518
  */
292
519
  country?: string;
520
+ /**
521
+ * @description Signed identity token (`base64url(json).base64url(hmac)`). An invalid token is ignored and the event is stored anonymously.
522
+ * @example eyJ1IjoidV8xIn0.3q2-7w
523
+ */
524
+ identity?: string;
525
+ /**
526
+ * @description Client IP, for server-side senders. Only used to derive the daily visitor hash; never stored. Defaults to the connecting IP.
527
+ * @example 203.0.113.7
528
+ */
529
+ ip?: string;
293
530
  };
294
531
  /** @description Event tracking data */
295
532
  EventInput: {
@@ -339,98 +576,55 @@ export interface components {
339
576
  */
340
577
  language?: string;
341
578
  /**
342
- * @description The user country code
579
+ * @description The user country code. Wins over the country Cloudflare infers from the request.
343
580
  * @example US
344
581
  */
345
582
  country?: string;
583
+ /**
584
+ * @description Signed identity token (`base64url(json).base64url(hmac)`). An invalid token is ignored and the event is stored anonymously.
585
+ * @example eyJ1IjoidV8xIn0.3q2-7w
586
+ */
587
+ identity?: string;
588
+ /**
589
+ * @description Client IP, for server-side senders. Only used to derive the daily visitor hash; never stored. Defaults to the connecting IP.
590
+ * @example 203.0.113.7
591
+ */
592
+ ip?: string;
346
593
  };
347
- /**
348
- * Analytics Query Request
349
- * @description Query parameters for analytics data
350
- */
351
- QueryRequest: {
352
- /** @description Time range for the query */
353
- date_range: string | {
354
- /**
355
- * @description Start date (ISO 8601)
356
- * @example 2025-01-01
357
- */
358
- from: string;
359
- /**
360
- * @description End date (ISO 8601)
361
- * @example 2025-01-31
362
- */
363
- to: string;
364
- };
365
- /** @description Timezone (e.g., America/Santo_Domingo) */
366
- tz?: string;
367
- /**
368
- * @description Metrics to calculate
369
- * @example [
370
- * "pageviews",
371
- * "visitors"
372
- * ]
373
- */
374
- metrics: ("pageviews" | "visitors" | "sessions" | "visit:bounce_rate" | "visit:avg_duration" | "events")[];
375
- /**
376
- * @description Dimensions to group by
377
- * @example [
378
- * "event:page"
379
- * ]
380
- */
381
- dimensions?: ("event:page" | "visit:country_name" | "visit:source" | "visit:referrer" | "visit:device" | "visit:browser" | "event:name")[];
382
- /**
383
- * @description Filters to apply
384
- * @example []
385
- */
386
- filters?: (("equals" | "not_equals" | "contains" | "starts_with" | "in" | "not_in" | "between" | "is_not") | string | (string | number | string[] | number[] | string[]))[][] | (("equals" | "not_equals" | "contains" | "starts_with" | "in" | "not_in" | "between" | "is_not") | string | (string | number | string[] | number[] | string[]))[][];
387
- /**
388
- * @description Ordering criteria
389
- * @example []
390
- */
391
- order_by?: (string | ("asc" | "desc"))[][] | (string | ("asc" | "desc"))[][];
392
- /** @description Pagination options */
393
- pagination?: {
394
- /**
395
- * @default 100
396
- * @example 50
397
- */
398
- limit: number;
399
- /**
400
- * @default 0
401
- * @example 0
402
- */
403
- offset: number;
404
- };
405
- /** @description Additional data to include in response */
406
- include?: {
407
- /** @default false */
408
- time_labels: boolean;
409
- /** @default false */
410
- total_rows: boolean;
411
- /**
412
- * @default daily
413
- * @enum {string}
414
- */
415
- granularity: "hourly" | "daily" | "weekly" | "monthly" | "yearly";
416
- };
594
+ /** @description Profile stored */
595
+ IdentifyResponse: {
596
+ /** @example true */
597
+ success: boolean;
417
598
  };
418
- /**
419
- * Analytics Query Response
420
- * @description Analytics query results
421
- */
422
- QueryResponse: {
423
- /** @description Query results */
424
- results: {
425
- dimensions: string[];
426
- metrics: number[];
427
- time_series?: number[];
428
- }[];
429
- /** @description Metadata about the query */
430
- meta: {
431
- time_labels?: string[];
432
- total_rows?: number;
433
- query: components["schemas"]["QueryRequest"];
599
+ /** @description Error response */
600
+ ErrorResponse: {
601
+ /**
602
+ * @description Error message
603
+ * @example Invalid request body
604
+ */
605
+ error: string;
606
+ /**
607
+ * @description Detailed error information
608
+ * @example Invalid JSON format
609
+ */
610
+ details: string;
611
+ };
612
+ /** @description Person profile traits */
613
+ IdentifyInput: {
614
+ /**
615
+ * @description The stable user id, the same one identity tokens carry as `user`.
616
+ * @example usr_7Hq2mXk9
617
+ */
618
+ user: string;
619
+ /**
620
+ * @description Merged into the stored profile. `null` deletes a trait. The dashboard labels a person by `name`, else `email`, else the id.
621
+ * @example {
622
+ * "name": "María Pérez",
623
+ * "email": "maria@example.com"
624
+ * }
625
+ */
626
+ traits: {
627
+ [key: string]: string | number | boolean | null;
434
628
  };
435
629
  };
436
630
  /** @description Health check response */
@@ -446,12 +640,586 @@ export interface components {
446
640
  */
447
641
  timestamp: string;
448
642
  };
643
+ Error: {
644
+ error: string;
645
+ };
646
+ InvalidInput: {
647
+ error: string;
648
+ fields: {
649
+ [key: string]: string[];
650
+ };
651
+ };
652
+ };
653
+ responses: {
654
+ /** @description The input doesn't match the method's schema. `fields` maps each field to its errors. */
655
+ InvalidInput: {
656
+ headers: {
657
+ [name: string]: unknown;
658
+ };
659
+ content: {
660
+ "application/json": components["schemas"]["InvalidInput"];
661
+ };
662
+ };
663
+ /** @description No credentials, or invalid or expired ones. */
664
+ Unauthorized: {
665
+ headers: {
666
+ [name: string]: unknown;
667
+ };
668
+ content: {
669
+ "application/json": components["schemas"]["Error"];
670
+ };
671
+ };
672
+ /** @description The caller lacks the permission. */
673
+ Forbidden: {
674
+ headers: {
675
+ [name: string]: unknown;
676
+ };
677
+ content: {
678
+ "application/json": components["schemas"]["Error"];
679
+ };
680
+ };
681
+ /** @description No such record. */
682
+ NotFound: {
683
+ headers: {
684
+ [name: string]: unknown;
685
+ };
686
+ content: {
687
+ "application/json": components["schemas"]["Error"];
688
+ };
689
+ };
690
+ /** @description The record's state doesn't allow this. */
691
+ Conflict: {
692
+ headers: {
693
+ [name: string]: unknown;
694
+ };
695
+ content: {
696
+ "application/json": components["schemas"]["Error"];
697
+ };
698
+ };
449
699
  };
450
- responses: never;
451
700
  parameters: never;
452
701
  requestBodies: never;
453
702
  headers: never;
454
703
  pathItems: never;
455
704
  }
456
705
  export type $defs = Record<string, never>;
457
- export type operations = Record<string, never>;
706
+ export interface operations {
707
+ "analytics.query": {
708
+ parameters: {
709
+ query?: never;
710
+ header?: never;
711
+ path?: never;
712
+ cookie?: never;
713
+ };
714
+ requestBody: {
715
+ content: {
716
+ "application/json": {
717
+ /** @description Inclusive start (ISO 8601). Default: 7 days before `to`. */
718
+ from?: string;
719
+ /** @description Exclusive end (ISO 8601). Default: now. */
720
+ to?: string;
721
+ /** @description IANA timezone for buckets, hour and weekday. Default UTC. */
722
+ tz?: string;
723
+ /** @description e.g. ["pageviews", "visitors"] */
724
+ metrics: ("events" | "pageviews" | "sessions" | "visitors" | "users" | "avg_duration" | "bounce_rate")[];
725
+ /** @description A fixed field or `props.<key>` (a key of the event payload). `hour` and `weekday` (0 = Sunday) are in `tz`. */
726
+ breakdown?: ("page" | "event" | "type" | "source" | "referrer" | "country" | "device" | "browser" | "os" | "tenant" | "user" | "actor" | "hour" | "weekday") | string;
727
+ /** @description [field, op, value]; `in` takes a list. e.g. [["props.role", "eq", "admin"]] */
728
+ filters?: [
729
+ ("page" | "event" | "type" | "source" | "referrer" | "country" | "device" | "browser" | "os" | "tenant" | "user" | "actor" | "hour" | "weekday") | string,
730
+ "eq" | "neq" | "in",
731
+ (string | number | boolean | null) | (string | number | boolean | null)[]
732
+ ][];
733
+ /**
734
+ * @description Adds a zero-filled time series
735
+ * @enum {string}
736
+ */
737
+ granularity?: "hour" | "day" | "week" | "month";
738
+ /** @description Also runs the query on another range and returns deltas */
739
+ compare?: "previous_period" | {
740
+ from: string;
741
+ to: string;
742
+ };
743
+ /** @description Max breakdown rows (default 100) */
744
+ limit?: number;
745
+ };
746
+ };
747
+ };
748
+ responses: {
749
+ /** @description OK */
750
+ 200: {
751
+ headers: {
752
+ [name: string]: unknown;
753
+ };
754
+ content: {
755
+ "application/json": {
756
+ /** @description The totals in one line, for reading aloud */
757
+ summary: string;
758
+ current: {
759
+ from: string;
760
+ to: string;
761
+ totals: {
762
+ events?: number;
763
+ pageviews?: number;
764
+ sessions?: number;
765
+ visitors?: number;
766
+ users?: number;
767
+ avg_duration?: number;
768
+ bounce_rate?: number;
769
+ };
770
+ rows?: {
771
+ events?: number;
772
+ pageviews?: number;
773
+ sessions?: number;
774
+ visitors?: number;
775
+ users?: number;
776
+ avg_duration?: number;
777
+ bounce_rate?: number;
778
+ key: (string | number) | null;
779
+ }[];
780
+ series?: {
781
+ events?: number;
782
+ pageviews?: number;
783
+ sessions?: number;
784
+ visitors?: number;
785
+ users?: number;
786
+ avg_duration?: number;
787
+ bounce_rate?: number;
788
+ bucket: string;
789
+ }[];
790
+ };
791
+ previous?: {
792
+ from: string;
793
+ to: string;
794
+ totals: {
795
+ events?: number;
796
+ pageviews?: number;
797
+ sessions?: number;
798
+ visitors?: number;
799
+ users?: number;
800
+ avg_duration?: number;
801
+ bounce_rate?: number;
802
+ };
803
+ rows?: {
804
+ events?: number;
805
+ pageviews?: number;
806
+ sessions?: number;
807
+ visitors?: number;
808
+ users?: number;
809
+ avg_duration?: number;
810
+ bounce_rate?: number;
811
+ key: (string | number) | null;
812
+ }[];
813
+ series?: {
814
+ events?: number;
815
+ pageviews?: number;
816
+ sessions?: number;
817
+ visitors?: number;
818
+ users?: number;
819
+ avg_duration?: number;
820
+ bounce_rate?: number;
821
+ bucket: string;
822
+ }[];
823
+ };
824
+ delta?: {
825
+ events?: {
826
+ abs: number;
827
+ /** @description null when the previous value is 0 */
828
+ pct: number | null;
829
+ };
830
+ pageviews?: {
831
+ abs: number;
832
+ /** @description null when the previous value is 0 */
833
+ pct: number | null;
834
+ };
835
+ sessions?: {
836
+ abs: number;
837
+ /** @description null when the previous value is 0 */
838
+ pct: number | null;
839
+ };
840
+ visitors?: {
841
+ abs: number;
842
+ /** @description null when the previous value is 0 */
843
+ pct: number | null;
844
+ };
845
+ users?: {
846
+ abs: number;
847
+ /** @description null when the previous value is 0 */
848
+ pct: number | null;
849
+ };
850
+ avg_duration?: {
851
+ abs: number;
852
+ /** @description null when the previous value is 0 */
853
+ pct: number | null;
854
+ };
855
+ bounce_rate?: {
856
+ abs: number;
857
+ /** @description null when the previous value is 0 */
858
+ pct: number | null;
859
+ };
860
+ };
861
+ };
862
+ };
863
+ };
864
+ 400: components["responses"]["InvalidInput"];
865
+ 401: components["responses"]["Unauthorized"];
866
+ 403: components["responses"]["Forbidden"];
867
+ 404: components["responses"]["NotFound"];
868
+ 409: components["responses"]["Conflict"];
869
+ };
870
+ };
871
+ "analytics.sql": {
872
+ parameters: {
873
+ query?: never;
874
+ header?: never;
875
+ path?: never;
876
+ cookie?: never;
877
+ };
878
+ requestBody: {
879
+ content: {
880
+ "application/json": {
881
+ /** @description e.g. SELECT tenant, COUNT(*) AS actions FROM events WHERE project_id = ? GROUP BY tenant */
882
+ query: string;
883
+ /** @description Bound to `?` placeholders in order */
884
+ params?: (string | number | boolean | null)[];
885
+ /** @description Max rows (default 1000) */
886
+ limit?: number;
887
+ };
888
+ };
889
+ };
890
+ responses: {
891
+ /** @description OK */
892
+ 200: {
893
+ headers: {
894
+ [name: string]: unknown;
895
+ };
896
+ content: {
897
+ "application/json": {
898
+ columns: string[];
899
+ rows: {
900
+ [key: string]: unknown;
901
+ }[];
902
+ truncated: boolean;
903
+ };
904
+ };
905
+ };
906
+ 400: components["responses"]["InvalidInput"];
907
+ 401: components["responses"]["Unauthorized"];
908
+ 403: components["responses"]["Forbidden"];
909
+ 404: components["responses"]["NotFound"];
910
+ 409: components["responses"]["Conflict"];
911
+ };
912
+ };
913
+ "analytics.people": {
914
+ parameters: {
915
+ query?: never;
916
+ header?: never;
917
+ path?: never;
918
+ cookie?: never;
919
+ };
920
+ requestBody?: {
921
+ content: {
922
+ "application/json": {
923
+ /** @description Inclusive start (ISO 8601). Default: 7 days before `to`. */
924
+ from?: string;
925
+ /** @description Exclusive end (ISO 8601). Default: now. */
926
+ to?: string;
927
+ /** @description IANA timezone for buckets, hour and weekday. Default UTC. */
928
+ tz?: string;
929
+ /** @description Only people acting for this customer */
930
+ tenant?: string;
931
+ limit?: number;
932
+ };
933
+ };
934
+ };
935
+ responses: {
936
+ /** @description OK */
937
+ 200: {
938
+ headers: {
939
+ [name: string]: unknown;
940
+ };
941
+ content: {
942
+ "application/json": {
943
+ from: string;
944
+ to: string;
945
+ people: {
946
+ user: string;
947
+ name: string;
948
+ email: string | null;
949
+ tenant: string | null;
950
+ lastSeen: string;
951
+ sessions: number;
952
+ actions: number;
953
+ pageviews: number;
954
+ helpedSessions: number;
955
+ previousActions: number;
956
+ quiet: boolean;
957
+ }[];
958
+ };
959
+ };
960
+ };
961
+ 400: components["responses"]["InvalidInput"];
962
+ 401: components["responses"]["Unauthorized"];
963
+ 403: components["responses"]["Forbidden"];
964
+ 404: components["responses"]["NotFound"];
965
+ 409: components["responses"]["Conflict"];
966
+ };
967
+ };
968
+ "analytics.customers": {
969
+ parameters: {
970
+ query?: never;
971
+ header?: never;
972
+ path?: never;
973
+ cookie?: never;
974
+ };
975
+ requestBody?: {
976
+ content: {
977
+ "application/json": {
978
+ /** @description Inclusive start (ISO 8601). Default: 7 days before `to`. */
979
+ from?: string;
980
+ /** @description Exclusive end (ISO 8601). Default: now. */
981
+ to?: string;
982
+ /** @description IANA timezone for buckets, hour and weekday. Default UTC. */
983
+ tz?: string;
984
+ limit?: number;
985
+ };
986
+ };
987
+ };
988
+ responses: {
989
+ /** @description OK */
990
+ 200: {
991
+ headers: {
992
+ [name: string]: unknown;
993
+ };
994
+ content: {
995
+ "application/json": {
996
+ from: string;
997
+ to: string;
998
+ customers: {
999
+ tenant: string;
1000
+ people: number;
1001
+ previousPeople: number;
1002
+ actions: number;
1003
+ previousActions: number;
1004
+ helpedSessions: number;
1005
+ lastSeen: string;
1006
+ quiet: boolean;
1007
+ }[];
1008
+ };
1009
+ };
1010
+ };
1011
+ 400: components["responses"]["InvalidInput"];
1012
+ 401: components["responses"]["Unauthorized"];
1013
+ 403: components["responses"]["Forbidden"];
1014
+ 404: components["responses"]["NotFound"];
1015
+ 409: components["responses"]["Conflict"];
1016
+ };
1017
+ };
1018
+ "analytics.person": {
1019
+ parameters: {
1020
+ query?: never;
1021
+ header?: never;
1022
+ path?: never;
1023
+ cookie?: never;
1024
+ };
1025
+ requestBody: {
1026
+ content: {
1027
+ "application/json": {
1028
+ /** @description The app-provided user id */
1029
+ user: string;
1030
+ /** @description Latest sessions to include (default 30) */
1031
+ sessions?: number;
1032
+ };
1033
+ };
1034
+ };
1035
+ responses: {
1036
+ /** @description OK */
1037
+ 200: {
1038
+ headers: {
1039
+ [name: string]: unknown;
1040
+ };
1041
+ content: {
1042
+ "application/json": {
1043
+ user: string;
1044
+ name: string;
1045
+ email: string | null;
1046
+ traits: {
1047
+ [key: string]: string | number | boolean;
1048
+ };
1049
+ firstSeen: string;
1050
+ lastSeen: string;
1051
+ sessions: number;
1052
+ actions: number;
1053
+ tenants: {
1054
+ tenant: string;
1055
+ actions: number;
1056
+ }[];
1057
+ actors: {
1058
+ id: string;
1059
+ name: string;
1060
+ email: string | null;
1061
+ }[];
1062
+ timeline: {
1063
+ id: string;
1064
+ startedAt: string;
1065
+ lastSeenAt: string | null;
1066
+ actor: {
1067
+ id: string;
1068
+ name: string;
1069
+ email: string | null;
1070
+ } | null;
1071
+ browser: string | null;
1072
+ os: string | null;
1073
+ device: string | null;
1074
+ country: string | null;
1075
+ events: {
1076
+ id: string;
1077
+ type: string | null;
1078
+ name: string;
1079
+ pathname: string;
1080
+ tenant: string | null;
1081
+ props: {
1082
+ [key: string]: unknown;
1083
+ };
1084
+ createdAt: string;
1085
+ }[];
1086
+ }[];
1087
+ };
1088
+ };
1089
+ };
1090
+ 400: components["responses"]["InvalidInput"];
1091
+ 401: components["responses"]["Unauthorized"];
1092
+ 403: components["responses"]["Forbidden"];
1093
+ 404: components["responses"]["NotFound"];
1094
+ 409: components["responses"]["Conflict"];
1095
+ };
1096
+ };
1097
+ "analytics.live": {
1098
+ parameters: {
1099
+ query?: never;
1100
+ header?: never;
1101
+ path?: never;
1102
+ cookie?: never;
1103
+ };
1104
+ requestBody?: {
1105
+ content: {
1106
+ "application/json": {
1107
+ /** @description How recent a pageview counts as live (default 5) */
1108
+ windowMinutes?: number;
1109
+ };
1110
+ };
1111
+ };
1112
+ responses: {
1113
+ /** @description OK */
1114
+ 200: {
1115
+ headers: {
1116
+ [name: string]: unknown;
1117
+ };
1118
+ content: {
1119
+ "application/json": {
1120
+ /** @description People on the site right now */
1121
+ count: number;
1122
+ sessions: {
1123
+ id: string;
1124
+ user: {
1125
+ id: string;
1126
+ name: string;
1127
+ email: string | null;
1128
+ } | null;
1129
+ actor: {
1130
+ id: string;
1131
+ name: string;
1132
+ email: string | null;
1133
+ } | null;
1134
+ country: string | null;
1135
+ browser: string | null;
1136
+ os: string | null;
1137
+ device: string | null;
1138
+ currentPage: string;
1139
+ referrer: string | null;
1140
+ startedAt: string;
1141
+ }[];
1142
+ pages: {
1143
+ count: number;
1144
+ pathname: string;
1145
+ }[];
1146
+ countries: {
1147
+ count: number;
1148
+ country: string;
1149
+ }[];
1150
+ referrers: {
1151
+ count: number;
1152
+ referrer: string;
1153
+ }[];
1154
+ };
1155
+ };
1156
+ };
1157
+ 400: components["responses"]["InvalidInput"];
1158
+ 401: components["responses"]["Unauthorized"];
1159
+ 403: components["responses"]["Forbidden"];
1160
+ 404: components["responses"]["NotFound"];
1161
+ 409: components["responses"]["Conflict"];
1162
+ };
1163
+ };
1164
+ "admin.projects": {
1165
+ parameters: {
1166
+ query?: never;
1167
+ header?: never;
1168
+ path?: never;
1169
+ cookie?: never;
1170
+ };
1171
+ requestBody?: never;
1172
+ responses: {
1173
+ /** @description OK */
1174
+ 200: {
1175
+ headers: {
1176
+ [name: string]: unknown;
1177
+ };
1178
+ content: {
1179
+ "application/json": {
1180
+ projects: {
1181
+ id: string;
1182
+ name: string;
1183
+ createdAt: string;
1184
+ }[];
1185
+ };
1186
+ };
1187
+ };
1188
+ 401: components["responses"]["Unauthorized"];
1189
+ 403: components["responses"]["Forbidden"];
1190
+ 404: components["responses"]["NotFound"];
1191
+ 409: components["responses"]["Conflict"];
1192
+ };
1193
+ };
1194
+ "admin.keys": {
1195
+ parameters: {
1196
+ query?: never;
1197
+ header?: never;
1198
+ path?: never;
1199
+ cookie?: never;
1200
+ };
1201
+ requestBody?: never;
1202
+ responses: {
1203
+ /** @description OK */
1204
+ 200: {
1205
+ headers: {
1206
+ [name: string]: unknown;
1207
+ };
1208
+ content: {
1209
+ "application/json": {
1210
+ ingestKeys: {
1211
+ id: string;
1212
+ name: string | null;
1213
+ createdAt: string;
1214
+ }[];
1215
+ secretKey: string | null;
1216
+ };
1217
+ };
1218
+ };
1219
+ 401: components["responses"]["Unauthorized"];
1220
+ 403: components["responses"]["Forbidden"];
1221
+ 404: components["responses"]["NotFound"];
1222
+ 409: components["responses"]["Conflict"];
1223
+ };
1224
+ };
1225
+ }