@goable-io/sdk 0.3.0 → 0.5.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,6 +118,11 @@ export interface paths {
118
118
  /** @description L2a probabilistic ensemble (Pro+) */
119
119
  ensemble?: boolean;
120
120
  members?: number;
121
+ /**
122
+ * @description Skill-conditioned scoring (Pro+).
123
+ * @enum {string}
124
+ */
125
+ rider_skill_level?: "beginner" | "intermediate" | "expert";
121
126
  };
122
127
  };
123
128
  };
@@ -125,13 +130,19 @@ export interface paths {
125
130
  /** @description Score 0-100 + verdict + confidence + eco + calibration_provenance */
126
131
  200: {
127
132
  headers: {
133
+ /** @description Daily safety cap for this endpoint + plan (not the monthly billing quota). Omitted on unlimited (Scale) plans. */
134
+ "X-RateLimit-Limit"?: number;
135
+ /** @description Requests remaining in the current UTC-midnight daily window. Omitted on unlimited (Scale) plans. */
136
+ "X-RateLimit-Remaining"?: number;
137
+ /** @description Unix timestamp (seconds) at which the daily window resets. Omitted on unlimited (Scale) plans. */
138
+ "X-RateLimit-Reset"?: number;
128
139
  [name: string]: unknown;
129
140
  };
130
141
  content: {
131
142
  "application/json": components["schemas"]["ScoreResponse"];
132
143
  };
133
144
  };
134
- /** @description Plan upgrade required (ensemble on Free/Starter) */
145
+ /** @description Plan upgrade required (ensemble or rider_skill_level on Free/Starter) */
135
146
  402: {
136
147
  headers: {
137
148
  [name: string]: unknown;
@@ -158,6 +169,17 @@ export interface paths {
158
169
  "application/json": components["schemas"]["Error"];
159
170
  };
160
171
  };
172
+ /** @description Daily rate limit (safety cap) exceeded for this endpoint + plan. */
173
+ 429: {
174
+ headers: {
175
+ /** @description Seconds until the daily rate-limit window resets. Present only on this 429 response. */
176
+ "Retry-After"?: number;
177
+ [name: string]: unknown;
178
+ };
179
+ content: {
180
+ "application/json": components["schemas"]["Error"];
181
+ };
182
+ };
161
183
  };
162
184
  };
163
185
  delete?: never;
@@ -296,7 +318,10 @@ export interface paths {
296
318
  };
297
319
  get?: never;
298
320
  put?: never;
299
- /** Historical climatology scoring (Pro+) */
321
+ /**
322
+ * Historical climatology scoring (Pro+)
323
+ * @description Percentiles + exceedance + verdict frequency over ERA5 reanalysis. Each entry carries a historical-mode ConfidenceDetail block (see components.schemas.ConfidenceDetailHistorical).
324
+ */
300
325
  post: {
301
326
  parameters: {
302
327
  query?: never;
@@ -327,7 +352,7 @@ export interface paths {
327
352
  };
328
353
  };
329
354
  responses: {
330
- /** @description Percentiles + exceedance + verdict frequency */
355
+ /** @description Percentiles + exceedance + verdict frequency (per-entry confidenceDetail: historical) */
331
356
  200: {
332
357
  headers: {
333
358
  [name: string]: unknown;
@@ -478,7 +503,7 @@ export interface paths {
478
503
  binding_constraint?: boolean;
479
504
  best_window_24h?: boolean;
480
505
  best_nearby_spot_km?: number;
481
- /** @description LLM explanation (Pro+) */
506
+ /** @description LLM explanation. Available on any plan (Anthropic BYOK) — resolves the tenant's own Anthropic key set via PUT /v1/tenant/llm-key; no key configured degrades to a deterministic template (natural_language.degraded_mode=true) rather than an error. */
482
507
  natural_language?: boolean;
483
508
  };
484
509
  };
@@ -496,15 +521,6 @@ export interface paths {
496
521
  };
497
522
  };
498
523
  };
499
- /** @description natural_language=true requires Pro+ */
500
- 402: {
501
- headers: {
502
- [name: string]: unknown;
503
- };
504
- content: {
505
- "application/json": components["schemas"]["Error"];
506
- };
507
- };
508
524
  /** @description No profile for activity */
509
525
  404: {
510
526
  headers: {
@@ -540,7 +556,7 @@ export interface paths {
540
556
  };
541
557
  get?: never;
542
558
  put?: never;
543
- /** Personalized go/no-go decision (Pro+) */
559
+ /** Personalized go/no-go decision (any plan — Anthropic BYOK) */
544
560
  post: {
545
561
  parameters: {
546
562
  query?: never;
@@ -570,7 +586,7 @@ export interface paths {
570
586
  };
571
587
  };
572
588
  responses: {
573
- /** @description score + verdict + decision block + degraded_mode + advisory_notice */
589
+ /** @description score + verdict + decision block + degraded_mode + advisory_notice. Available on any plan; the LLM reasoning narrative requires the tenant's own Anthropic key (PUT /v1/tenant/llm-key) — without one, degraded_mode=true and a deterministic template is returned instead of an error. */
574
590
  200: {
575
591
  headers: {
576
592
  [name: string]: unknown;
@@ -581,15 +597,6 @@ export interface paths {
581
597
  };
582
598
  };
583
599
  };
584
- /** @description Requires Pro+ plan */
585
- 402: {
586
- headers: {
587
- [name: string]: unknown;
588
- };
589
- content: {
590
- "application/json": components["schemas"]["Error"];
591
- };
592
- };
593
600
  /** @description No profile for activity */
594
601
  404: {
595
602
  headers: {
@@ -661,7 +668,10 @@ export interface paths {
661
668
  };
662
669
  get?: never;
663
670
  put?: never;
664
- /** LLM narrative explanation of a score (L2c, Pro+) */
671
+ /**
672
+ * LLM narrative explanation of a score (L2c, any plan — Anthropic BYOK)
673
+ * @description Requires the tenant to have its own Anthropic key set via PUT /v1/tenant/llm-key — no longer plan-gated. No key configured → 503 INTELLIGENCE_UNAVAILABLE.
674
+ */
665
675
  post: {
666
676
  parameters: {
667
677
  query?: never;
@@ -695,8 +705,8 @@ export interface paths {
695
705
  };
696
706
  };
697
707
  };
698
- /** @description Requires Pro+ plan */
699
- 402: {
708
+ /** @description Validation error */
709
+ 422: {
700
710
  headers: {
701
711
  [name: string]: unknown;
702
712
  };
@@ -704,8 +714,8 @@ export interface paths {
704
714
  "application/json": components["schemas"]["Error"];
705
715
  };
706
716
  };
707
- /** @description Validation error */
708
- 422: {
717
+ /** @description No Anthropic key configured for this tenant */
718
+ 503: {
709
719
  headers: {
710
720
  [name: string]: unknown;
711
721
  };
@@ -730,7 +740,10 @@ export interface paths {
730
740
  };
731
741
  get?: never;
732
742
  put?: never;
733
- /** LLM multi-slot briefing (L2c, Pro+) */
743
+ /**
744
+ * LLM multi-slot briefing (L2c, any plan — Anthropic BYOK)
745
+ * @description Requires the tenant to have its own Anthropic key set via PUT /v1/tenant/llm-key — no longer plan-gated. No key configured → 503 INTELLIGENCE_UNAVAILABLE.
746
+ */
734
747
  post: {
735
748
  parameters: {
736
749
  query?: never;
@@ -764,8 +777,8 @@ export interface paths {
764
777
  };
765
778
  };
766
779
  };
767
- /** @description Requires Pro+ plan */
768
- 402: {
780
+ /** @description Validation error */
781
+ 422: {
769
782
  headers: {
770
783
  [name: string]: unknown;
771
784
  };
@@ -773,8 +786,8 @@ export interface paths {
773
786
  "application/json": components["schemas"]["Error"];
774
787
  };
775
788
  };
776
- /** @description Validation error */
777
- 422: {
789
+ /** @description No Anthropic key configured for this tenant */
790
+ 503: {
778
791
  headers: {
779
792
  [name: string]: unknown;
780
793
  };
@@ -790,17 +803,58 @@ export interface paths {
790
803
  patch?: never;
791
804
  trace?: never;
792
805
  };
793
- "/v1/projections": {
806
+ "/v1/tenant/llm-key": {
794
807
  parameters: {
795
808
  query?: never;
796
809
  header?: never;
797
810
  path?: never;
798
811
  cookie?: never;
799
812
  };
800
- get?: never;
801
- put?: never;
802
- /** Climate-decadal activity viability projection (Scale) */
803
- post: {
813
+ /**
814
+ * Get the tenant's Anthropic key status (Anthropic BYOK)
815
+ * @description Never returns the key — only whether one is set, its last 4 characters, and timestamps.
816
+ */
817
+ get: {
818
+ parameters: {
819
+ query?: never;
820
+ header?: never;
821
+ path?: never;
822
+ cookie?: never;
823
+ };
824
+ requestBody?: never;
825
+ responses: {
826
+ /** @description Masked key status */
827
+ 200: {
828
+ headers: {
829
+ [name: string]: unknown;
830
+ };
831
+ content: {
832
+ "application/json": {
833
+ set: boolean;
834
+ last4?: string;
835
+ /** Format: date-time */
836
+ setAt?: string;
837
+ /** Format: date-time */
838
+ lastValidatedAt?: string | null;
839
+ };
840
+ };
841
+ };
842
+ /** @description BYOK storage not configured on this deployment */
843
+ 503: {
844
+ headers: {
845
+ [name: string]: unknown;
846
+ };
847
+ content: {
848
+ "application/json": components["schemas"]["Error"];
849
+ };
850
+ };
851
+ };
852
+ };
853
+ /**
854
+ * Set/rotate the tenant's Anthropic API key (Anthropic BYOK)
855
+ * @description Validates the key with one cheap Anthropic API call before storing. Encrypts at rest (AES-256-GCM); the plaintext key is NEVER echoed back by this or any other endpoint. Available to any authenticated plan. Rate-limited (this call itself makes one real Anthropic API request).
856
+ */
857
+ put: {
804
858
  parameters: {
805
859
  query?: never;
806
860
  header?: never;
@@ -810,32 +864,30 @@ export interface paths {
810
864
  requestBody: {
811
865
  content: {
812
866
  "application/json": {
813
- spot: {
814
- location: components["schemas"]["GeoPoint"];
815
- activity: string;
816
- spotId?: string;
817
- };
818
- scenarios: ("SSP1-2.6" | "SSP2-4.5" | "SSP3-7.0" | "SSP5-8.5")[];
819
- horizonDecades?: string[];
820
- /** @enum {string} */
821
- baselineDecade?: "2020s" | "2030s";
867
+ /** @description The tenant's own Anthropic API key (sk-ant-...). */
868
+ apiKey: string;
822
869
  };
823
870
  };
824
871
  };
825
872
  responses: {
826
- /** @description Per-decade projection distributions */
827
- 200: {
873
+ /** @description Key validated, encrypted, and stored. */
874
+ 204: {
875
+ headers: {
876
+ [name: string]: unknown;
877
+ };
878
+ content?: never;
879
+ };
880
+ /** @description Validation error, or Anthropic rejected the key (LLM_KEY_INVALID) */
881
+ 422: {
828
882
  headers: {
829
883
  [name: string]: unknown;
830
884
  };
831
885
  content: {
832
- "application/json": {
833
- [key: string]: unknown;
834
- };
886
+ "application/json": components["schemas"]["Error"];
835
887
  };
836
888
  };
837
- /** @description Requires Scale plan */
838
- 402: {
889
+ /** @description Rate limited */
890
+ 429: {
839
891
  headers: {
840
892
  [name: string]: unknown;
841
893
  };
@@ -843,8 +895,40 @@ export interface paths {
843
895
  "application/json": components["schemas"]["Error"];
844
896
  };
845
897
  };
846
- /** @description Validation error */
847
- 422: {
898
+ /** @description BYOK storage or BYOK_MASTER_KEY not configured on this deployment */
899
+ 503: {
900
+ headers: {
901
+ [name: string]: unknown;
902
+ };
903
+ content: {
904
+ "application/json": components["schemas"]["Error"];
905
+ };
906
+ };
907
+ };
908
+ };
909
+ post?: never;
910
+ /**
911
+ * Remove the tenant's Anthropic key (Anthropic BYOK)
912
+ * @description Hard-deletes the stored ciphertext. LLM routes revert to the no-key degraded/503 path for this tenant.
913
+ */
914
+ delete: {
915
+ parameters: {
916
+ query?: never;
917
+ header?: never;
918
+ path?: never;
919
+ cookie?: never;
920
+ };
921
+ requestBody?: never;
922
+ responses: {
923
+ /** @description Key removed. */
924
+ 204: {
925
+ headers: {
926
+ [name: string]: unknown;
927
+ };
928
+ content?: never;
929
+ };
930
+ /** @description BYOK storage not configured on this deployment */
931
+ 503: {
848
932
  headers: {
849
933
  [name: string]: unknown;
850
934
  };
@@ -854,13 +938,12 @@ export interface paths {
854
938
  };
855
939
  };
856
940
  };
857
- delete?: never;
858
941
  options?: never;
859
942
  head?: never;
860
943
  patch?: never;
861
944
  trace?: never;
862
945
  };
863
- "/v1/underwriting/quote": {
946
+ "/v1/projections": {
864
947
  parameters: {
865
948
  query?: never;
866
949
  header?: never;
@@ -869,7 +952,10 @@ export interface paths {
869
952
  };
870
953
  get?: never;
871
954
  put?: never;
872
- /** Parametric underwriting quote (Scale) */
955
+ /**
956
+ * Climate-decadal activity viability projection (Scale)
957
+ * @description Per-decade projection distributions. Each entry carries a climate-mode ConfidenceDetail block (see components.schemas.ConfidenceDetailClimate).
958
+ */
873
959
  post: {
874
960
  parameters: {
875
961
  query?: never;
@@ -880,36 +966,20 @@ export interface paths {
880
966
  requestBody: {
881
967
  content: {
882
968
  "application/json": {
883
- spot?: {
884
- location: components["schemas"]["GeoPoint"];
885
- activity: string;
886
- spotId?: string;
887
- };
888
- portfolio?: {
969
+ spot: {
889
970
  location: components["schemas"]["GeoPoint"];
890
971
  activity: string;
891
972
  spotId?: string;
892
- }[];
893
- coverageWindow: {
894
- monthFrom: number;
895
- dayFrom: number;
896
- monthTo: number;
897
- dayTo: number;
898
- } & {
899
- [key: string]: unknown;
900
- };
901
- payout?: {
902
- amount: number;
903
- /** @enum {string} */
904
- currency: "EUR" | "USD" | "GBP" | "CHF";
905
973
  };
906
- } & {
907
- [key: string]: unknown;
974
+ scenarios: ("SSP1-2.6" | "SSP2-4.5" | "SSP3-7.0" | "SSP5-8.5")[];
975
+ horizonDecades?: string[];
976
+ /** @enum {string} */
977
+ baselineDecade?: "2020s" | "2030s";
908
978
  };
909
979
  };
910
980
  };
911
981
  responses: {
912
- /** @description Premium + bindable policy terms */
982
+ /** @description Per-decade projection distributions (per-entry confidenceDetail: climate) */
913
983
  200: {
914
984
  headers: {
915
985
  [name: string]: unknown;
@@ -946,7 +1016,7 @@ export interface paths {
946
1016
  patch?: never;
947
1017
  trace?: never;
948
1018
  };
949
- "/v1/underwriting/bind": {
1019
+ "/v1/underwriting/quote": {
950
1020
  parameters: {
951
1021
  query?: never;
952
1022
  header?: never;
@@ -956,8 +1026,8 @@ export interface paths {
956
1026
  get?: never;
957
1027
  put?: never;
958
1028
  /**
959
- * Bind a parametric policy (Scale)
960
- * @description Convert a recent /v1/underwriting/quote into a bound policy. Submit the quoteId (≤24h old); returns a policyId + immutable cohortHash anchoring the bound weather sample set.
1029
+ * Parametric underwriting quote (Scale)
1030
+ * @description Multi-currency `expectedPremium.byCurrency` — a mixed-currency portfolio returns per-currency stats with no FX conversion. `policy.spot.tier` / `policy.portfolio[i].tier` echo the resolved sub-spot tier (1/2/3) or null when no sub-spot covers the point; `tierSource` marks whether it came from the catalog YAML or L11's data-driven classifier.
961
1031
  */
962
1032
  post: {
963
1033
  parameters: {
@@ -969,25 +1039,65 @@ export interface paths {
969
1039
  requestBody: {
970
1040
  content: {
971
1041
  "application/json": {
972
- quoteId: string;
973
- premiumConfirmation?: {
974
- [key: string]: unknown;
1042
+ spot?: {
1043
+ point: components["schemas"]["GeoPoint"];
1044
+ activity: string;
1045
+ spotId?: string;
1046
+ payout: {
1047
+ amount: number;
1048
+ /** @enum {string} */
1049
+ currency: "EUR" | "USD" | "GBP" | "CHF";
1050
+ };
975
1051
  };
976
- } & {
977
- [key: string]: unknown;
1052
+ portfolio?: {
1053
+ point: components["schemas"]["GeoPoint"];
1054
+ activity: string;
1055
+ spotId?: string;
1056
+ payout: {
1057
+ amount: number;
1058
+ /** @enum {string} */
1059
+ currency: "EUR" | "USD" | "GBP" | "CHF";
1060
+ };
1061
+ }[];
1062
+ coverageWindow: {
1063
+ monthFrom: number;
1064
+ dayFrom: number;
1065
+ monthTo: number;
1066
+ dayTo: number;
1067
+ };
1068
+ trigger: {
1069
+ /** @enum {string} */
1070
+ kind: "scoreBelow";
1071
+ scoreBelow: number;
1072
+ consecutiveHours?: number;
1073
+ cooldownHours?: number;
1074
+ maxPayoutsPerYear?: number;
1075
+ } | {
1076
+ /** @enum {string} */
1077
+ kind: "verdictAtOrBelow";
1078
+ verdict: components["schemas"]["Verdict"];
1079
+ consecutiveHours?: number;
1080
+ cooldownHours?: number;
1081
+ maxPayoutsPerYear?: number;
1082
+ };
1083
+ historicalYearsRange?: {
1084
+ from: number;
1085
+ to: number;
1086
+ };
1087
+ loadingFactor?: number;
1088
+ calibrationConfidenceMin?: number;
1089
+ forceIssue?: boolean;
978
1090
  };
979
1091
  };
980
1092
  };
981
1093
  responses: {
982
- /** @description Bound policy id + cohort hash + settlement schedule */
1094
+ /** @description Premium + policy echo (with per-spot tier + tierSource) + bindable quote id when policy store is wired */
983
1095
  200: {
984
1096
  headers: {
985
1097
  [name: string]: unknown;
986
1098
  };
987
1099
  content: {
988
- "application/json": {
989
- [key: string]: unknown;
990
- };
1100
+ "application/json": components["schemas"]["UnderwritingQuoteResponse"];
991
1101
  };
992
1102
  };
993
1103
  /** @description Requires Scale plan */
@@ -999,7 +1109,7 @@ export interface paths {
999
1109
  "application/json": components["schemas"]["Error"];
1000
1110
  };
1001
1111
  };
1002
- /** @description Quote expired or not found */
1112
+ /** @description No profile for activity */
1003
1113
  404: {
1004
1114
  headers: {
1005
1115
  [name: string]: unknown;
@@ -1008,7 +1118,7 @@ export interface paths {
1008
1118
  "application/json": components["schemas"]["Error"];
1009
1119
  };
1010
1120
  };
1011
- /** @description Validation error */
1121
+ /** @description Validation error / low confidence / invalid trigger */
1012
1122
  422: {
1013
1123
  headers: {
1014
1124
  [name: string]: unknown;
@@ -1017,6 +1127,15 @@ export interface paths {
1017
1127
  "application/json": components["schemas"]["Error"];
1018
1128
  };
1019
1129
  };
1130
+ /** @description HistoricalProvider not configured */
1131
+ 503: {
1132
+ headers: {
1133
+ [name: string]: unknown;
1134
+ };
1135
+ content: {
1136
+ "application/json": components["schemas"]["Error"];
1137
+ };
1138
+ };
1020
1139
  };
1021
1140
  };
1022
1141
  delete?: never;
@@ -1025,43 +1144,35 @@ export interface paths {
1025
1144
  patch?: never;
1026
1145
  trace?: never;
1027
1146
  };
1028
- "/v1/underwriting/evaluate": {
1147
+ "/v1/underwriting/quote/{id}": {
1029
1148
  parameters: {
1030
1149
  query?: never;
1031
1150
  header?: never;
1032
1151
  path?: never;
1033
1152
  cookie?: never;
1034
1153
  };
1035
- get?: never;
1036
- put?: never;
1037
1154
  /**
1038
- * Read-only payout projection for a bound policy (Scale)
1039
- * @description Returns the current accrued shortfall + projected payout for a bound policy. Does NOT settle settlement runs automatically on a cron at policy expiry.
1155
+ * Fetch a bindable quote by id (Scale)
1156
+ * @description Tenant-scoped read of a quote created via POST /v1/underwriting/quote. Returns the same body shape as the create response (with the optional `boundPolicyId` populated once the quote has been bound).
1040
1157
  */
1041
- post: {
1158
+ get: {
1042
1159
  parameters: {
1043
1160
  query?: never;
1044
1161
  header?: never;
1045
- path?: never;
1046
- cookie?: never;
1047
- };
1048
- requestBody: {
1049
- content: {
1050
- "application/json": {
1051
- policyId: string;
1052
- };
1162
+ path: {
1163
+ id: string;
1053
1164
  };
1165
+ cookie?: never;
1054
1166
  };
1167
+ requestBody?: never;
1055
1168
  responses: {
1056
- /** @description Accrued shortfall + projected payout per currency */
1169
+ /** @description Quote record (may include boundPolicyId when already bound) */
1057
1170
  200: {
1058
1171
  headers: {
1059
1172
  [name: string]: unknown;
1060
1173
  };
1061
1174
  content: {
1062
- "application/json": {
1063
- [key: string]: unknown;
1064
- };
1175
+ "application/json": components["schemas"]["UnderwritingQuoteResponse"];
1065
1176
  };
1066
1177
  };
1067
1178
  /** @description Requires Scale plan */
@@ -1073,7 +1184,7 @@ export interface paths {
1073
1184
  "application/json": components["schemas"]["Error"];
1074
1185
  };
1075
1186
  };
1076
- /** @description Policy not found */
1187
+ /** @description Quote not found for this tenant */
1077
1188
  404: {
1078
1189
  headers: {
1079
1190
  [name: string]: unknown;
@@ -1082,8 +1193,8 @@ export interface paths {
1082
1193
  "application/json": components["schemas"]["Error"];
1083
1194
  };
1084
1195
  };
1085
- /** @description Validation error */
1086
- 422: {
1196
+ /** @description Quote persistence not configured */
1197
+ 503: {
1087
1198
  headers: {
1088
1199
  [name: string]: unknown;
1089
1200
  };
@@ -1093,13 +1204,15 @@ export interface paths {
1093
1204
  };
1094
1205
  };
1095
1206
  };
1207
+ put?: never;
1208
+ post?: never;
1096
1209
  delete?: never;
1097
1210
  options?: never;
1098
1211
  head?: never;
1099
1212
  patch?: never;
1100
1213
  trace?: never;
1101
1214
  };
1102
- "/v1/score/{sessionId}/outcome": {
1215
+ "/v1/underwriting/policy/bind": {
1103
1216
  parameters: {
1104
1217
  query?: never;
1105
1218
  header?: never;
@@ -1109,44 +1222,57 @@ export interface paths {
1109
1222
  get?: never;
1110
1223
  put?: never;
1111
1224
  /**
1112
- * Report observed outcome for a scored session
1113
- * @description Close the calibration loop. Submit the actual outcome (ran/cancelled/no_show/rescheduled/note) of a /v1/score session. The calibration pipeline + forecast verification + drift monitor consume these. Requires the `outcomes:write` scope (live keys carry it by default; test keys don't).
1225
+ * Bind a parametric policy (Scale)
1226
+ * @description Convert a bindable quote (≤24h old) into a bound policy for a specific coverage year. Returns the serialised policy + the quoteId + optional `driftAdvisories` — a soft warning surfaced when the resolved cell has an open watch-level L9 drift event. A warning/critical drift event at bind time REFUSES the bind with 422 DRIFT_ACTIVE (see the 422 response). Fires the `underwriting.policy.bound` webhook on success. Supports the optional `Idempotency-Key` header (see parameter description) so a client-side retry after a network timeout can't double-bind.
1114
1227
  */
1115
1228
  post: {
1116
1229
  parameters: {
1117
1230
  query?: never;
1118
- header?: never;
1119
- path: {
1120
- /** @description Session UUID returned in the /v1/score response metadata. */
1121
- sessionId: string;
1231
+ header?: {
1232
+ /** @description Optional client-generated key (unique per logical request, scoped to your tenant). A retry with the SAME key and the SAME request body replays the original response verbatim without re-executing the request. A retry with the same key and a DIFFERENT body, or one that arrives while the original is still in flight, returns 409 IDEMPOTENCY_KEY_CONFLICT. Claims expire after 24h. */
1233
+ "Idempotency-Key"?: string;
1122
1234
  };
1235
+ path?: never;
1123
1236
  cookie?: never;
1124
1237
  };
1125
1238
  requestBody: {
1126
1239
  content: {
1127
1240
  "application/json": {
1241
+ /** Format: uuid */
1242
+ quoteId: string;
1243
+ coverageYear: number;
1128
1244
  /** @enum {string} */
1129
- outcome_type: "ran" | "cancelled" | "rescheduled" | "no_show" | "note";
1130
- detail?: {
1131
- [key: string]: unknown;
1132
- };
1245
+ premiumCollection: "external" | "stripe" | "invoice_due";
1246
+ premiumPaid?: number;
1133
1247
  };
1134
1248
  };
1135
1249
  };
1136
1250
  responses: {
1137
- /** @description Accepted (queued for the next calibration batch) */
1138
- 202: {
1251
+ /** @description Policy bound */
1252
+ 201: {
1139
1253
  headers: {
1140
1254
  [name: string]: unknown;
1141
1255
  };
1142
1256
  content: {
1143
1257
  "application/json": {
1144
- [key: string]: unknown;
1258
+ policy: components["schemas"]["SerialisedPolicy"];
1259
+ /** Format: uuid */
1260
+ quoteId: string;
1261
+ /** @description Soft warnings for cells with an open watch-level L9 drift event. Present only when non-empty. */
1262
+ driftAdvisories?: {
1263
+ spotIndex: number;
1264
+ activity: string;
1265
+ subSpotSlug: string;
1266
+ /** @enum {string} */
1267
+ severity: "watch";
1268
+ /** Format: date-time */
1269
+ since: string;
1270
+ }[];
1145
1271
  };
1146
1272
  };
1147
1273
  };
1148
- /** @description Missing scope: outcomes:write */
1149
- 403: {
1274
+ /** @description Requires Scale plan */
1275
+ 402: {
1150
1276
  headers: {
1151
1277
  [name: string]: unknown;
1152
1278
  };
@@ -1154,7 +1280,7 @@ export interface paths {
1154
1280
  "application/json": components["schemas"]["Error"];
1155
1281
  };
1156
1282
  };
1157
- /** @description Session not found */
1283
+ /** @description Quote not found for this tenant */
1158
1284
  404: {
1159
1285
  headers: {
1160
1286
  [name: string]: unknown;
@@ -1163,7 +1289,16 @@ export interface paths {
1163
1289
  "application/json": components["schemas"]["Error"];
1164
1290
  };
1165
1291
  };
1166
- /** @description Validation error */
1292
+ /** @description Quote already bound to another policy (QUOTE_ALREADY_BOUND), or IDEMPOTENCY_KEY_CONFLICT — a request with this Idempotency-Key is still in flight, or was already used with a different request body */
1293
+ 409: {
1294
+ headers: {
1295
+ [name: string]: unknown;
1296
+ };
1297
+ content: {
1298
+ "application/json": components["schemas"]["Error"];
1299
+ };
1300
+ };
1301
+ /** @description Validation error / quote expired / quote not issuable / catalog drift / DRIFT_ACTIVE (open warning or critical drift event on resolved cell — `detail.openDriftEvents` lists each blocking cell) */
1167
1302
  422: {
1168
1303
  headers: {
1169
1304
  [name: string]: unknown;
@@ -1172,6 +1307,15 @@ export interface paths {
1172
1307
  "application/json": components["schemas"]["Error"];
1173
1308
  };
1174
1309
  };
1310
+ /** @description Policy persistence not configured */
1311
+ 503: {
1312
+ headers: {
1313
+ [name: string]: unknown;
1314
+ };
1315
+ content: {
1316
+ "application/json": components["schemas"]["Error"];
1317
+ };
1318
+ };
1175
1319
  };
1176
1320
  };
1177
1321
  delete?: never;
@@ -1180,51 +1324,44 @@ export interface paths {
1180
1324
  patch?: never;
1181
1325
  trace?: never;
1182
1326
  };
1183
- "/v1/score/difficulty": {
1327
+ "/v1/underwriting/policy": {
1184
1328
  parameters: {
1185
1329
  query?: never;
1186
1330
  header?: never;
1187
1331
  path?: never;
1188
1332
  cookie?: never;
1189
1333
  };
1190
- get?: never;
1191
- put?: never;
1192
1334
  /**
1193
- * Skill-conditioned scoring
1194
- * @description Same scoring engine, but conditioned on a rider/operator skill level. Returns the score curve as a function of skill so a booking flow can branch ("good for beginners" vs "experts only").
1335
+ * List bound policies for the caller's tenant (Scale)
1336
+ * @description Paginated list of every bound policy owned by the calling tenant. Ordered by boundAt DESC.
1195
1337
  */
1196
- post: {
1338
+ get: {
1197
1339
  parameters: {
1198
- query?: never;
1340
+ query?: {
1341
+ status?: "bound" | "triggered" | "settled" | "expired";
1342
+ coverageYear?: number;
1343
+ limit?: number;
1344
+ cursor?: string;
1345
+ };
1199
1346
  header?: never;
1200
1347
  path?: never;
1201
1348
  cookie?: never;
1202
1349
  };
1203
- requestBody: {
1204
- content: {
1205
- "application/json": {
1206
- activity: string;
1207
- location: components["schemas"]["GeoPoint"];
1208
- window?: components["schemas"]["TimeWindow"];
1209
- /** @description Skill points (0-1) to score. Defaults to 5 quantile points. */
1210
- riderSkillLevels?: number[];
1211
- };
1212
- };
1213
- };
1350
+ requestBody?: never;
1214
1351
  responses: {
1215
- /** @description Score per skill level + difficulty band */
1352
+ /** @description Bound policies for this tenant */
1216
1353
  200: {
1217
1354
  headers: {
1218
1355
  [name: string]: unknown;
1219
1356
  };
1220
1357
  content: {
1221
1358
  "application/json": {
1222
- [key: string]: unknown;
1359
+ policies: components["schemas"]["SerialisedPolicy"][];
1223
1360
  };
1224
1361
  };
1225
1362
  };
1226
- /** @description No profile for activity */
1227
- 404: {
1363
+ /** @description Requires Scale plan */
1364
+ 402: {
1228
1365
  headers: {
1229
1366
  [name: string]: unknown;
1230
1367
  };
@@ -1241,104 +1378,60 @@ export interface paths {
1241
1378
  "application/json": components["schemas"]["Error"];
1242
1379
  };
1243
1380
  };
1381
+ /** @description Policy persistence not configured */
1382
+ 503: {
1383
+ headers: {
1384
+ [name: string]: unknown;
1385
+ };
1386
+ content: {
1387
+ "application/json": components["schemas"]["Error"];
1388
+ };
1389
+ };
1244
1390
  };
1245
1391
  };
1392
+ put?: never;
1393
+ post?: never;
1246
1394
  delete?: never;
1247
1395
  options?: never;
1248
1396
  head?: never;
1249
1397
  patch?: never;
1250
1398
  trace?: never;
1251
1399
  };
1252
- "/v1/recommend-spot": {
1400
+ "/v1/underwriting/policy/{policyId}": {
1253
1401
  parameters: {
1254
1402
  query?: never;
1255
1403
  header?: never;
1256
1404
  path?: never;
1257
1405
  cookie?: never;
1258
1406
  };
1259
- get?: never;
1260
- put?: never;
1261
1407
  /**
1262
- * Spot recommender inverse query (L10)
1263
- * @description Given (activity, region center, radius, window) → top-K ranked sub-spots in the catalog. Composition of L1-L3 scoring + L4.6 confidence + L6 personal blend (Pro+, when pseudonym supplied). Plan caps: radius 25/50/200/1000 km, topK 5/10/20/50 across Free / Starter / Pro / Scale. Personalization Pro+ only. Hard-gated candidates (lightning ≥ 0.85, AQI hazardous) are dropped from results; `allGated=true` distinguishes 'all in-radius spots were unsafe' from 'no spots in radius at all'.
1408
+ * Fetch a single policy by id (Scale)
1409
+ * @description Read-only lookup. Cross-tenant policies return 404.
1264
1410
  */
1265
- post: {
1411
+ get: {
1266
1412
  parameters: {
1267
1413
  query?: never;
1268
1414
  header?: never;
1269
- path?: never;
1270
- cookie?: never;
1271
- };
1272
- requestBody: {
1273
- content: {
1274
- "application/json": {
1275
- activity: string;
1276
- regionCenter: components["schemas"]["GeoPoint"];
1277
- radiusKm: number;
1278
- topK: number;
1279
- window?: components["schemas"]["TimeWindow"];
1280
- /** @description Optional booking-platform-side pseudonym. When present, Pro+ tenants get personalization via the L6 cold-start blend (cap 0.5 weight). */
1281
- userPseudonym?: string;
1282
- };
1415
+ path: {
1416
+ policyId: string;
1283
1417
  };
1418
+ cookie?: never;
1284
1419
  };
1420
+ requestBody?: never;
1285
1421
  responses: {
1286
- /** @description Ranked top-K sub-spots + observability metadata. The optional `coverage` field appears only on empty results from a catalog gap (not from hard-gating). */
1422
+ /** @description Policy record + payout events */
1287
1423
  200: {
1288
1424
  headers: {
1289
1425
  [name: string]: unknown;
1290
1426
  };
1291
1427
  content: {
1292
1428
  "application/json": {
1293
- results?: {
1294
- spotSlug?: string;
1295
- name?: string;
1296
- location?: components["schemas"]["GeoPoint"];
1297
- distanceKm?: number;
1298
- score?: number;
1299
- effectiveScore?: number;
1300
- verdict?: string;
1301
- personalScore?: number | null;
1302
- personalWeight?: number;
1303
- rank?: number;
1304
- }[];
1305
- allGated?: boolean;
1306
- totalCandidates?: number;
1307
- rankedCandidates?: number;
1308
- effectiveRadiusKm?: number;
1309
- effectiveTopK?: number;
1310
- personalizationApplied?: boolean;
1311
- latencyMs?: number;
1312
- coverage?: {
1313
- /** @enum {string} */
1314
- status: "no_subspots_in_radius";
1315
- nearestSubSpot: {
1316
- slug: string;
1317
- name: string;
1318
- distanceKm: number;
1319
- };
1320
- /** @enum {string} */
1321
- suggestedAction: "expand_radius";
1322
- suggestedRadiusKm: number;
1323
- } | {
1324
- /** @enum {string} */
1325
- status: "no_subspots_for_activity";
1326
- /** @enum {string} */
1327
- suggestedAction: "request_coverage";
1328
- };
1429
+ policy: components["schemas"]["SerialisedPolicy"];
1430
+ events: components["schemas"]["SerialisedPayoutEvent"][];
1329
1431
  };
1330
1432
  };
1331
1433
  };
1332
- /** @description Missing/invalid bearer token */
1333
- 401: {
1334
- headers: {
1335
- [name: string]: unknown;
1336
- };
1337
- content: {
1338
- "application/json": components["schemas"]["Error"];
1339
- };
1340
- };
1341
- /** @description Plan limit exceeded (radius or topK above plan cap) */
1434
+ /** @description Requires Scale plan */
1342
1435
  402: {
1343
1436
  headers: {
1344
1437
  [name: string]: unknown;
@@ -1347,8 +1440,8 @@ export interface paths {
1347
1440
  "application/json": components["schemas"]["Error"];
1348
1441
  };
1349
1442
  };
1350
- /** @description Validation error or unknown activity */
1351
- 422: {
1443
+ /** @description Policy not found */
1444
+ 404: {
1352
1445
  headers: {
1353
1446
  [name: string]: unknown;
1354
1447
  };
@@ -1356,7 +1449,7 @@ export interface paths {
1356
1449
  "application/json": components["schemas"]["Error"];
1357
1450
  };
1358
1451
  };
1359
- /** @description Spatial resolver not wired */
1452
+ /** @description Policy persistence not configured */
1360
1453
  503: {
1361
1454
  headers: {
1362
1455
  [name: string]: unknown;
@@ -1367,13 +1460,15 @@ export interface paths {
1367
1460
  };
1368
1461
  };
1369
1462
  };
1463
+ put?: never;
1464
+ post?: never;
1370
1465
  delete?: never;
1371
1466
  options?: never;
1372
1467
  head?: never;
1373
1468
  patch?: never;
1374
1469
  trace?: never;
1375
1470
  };
1376
- "/v1/intelligence/edge-case": {
1471
+ "/v1/underwriting/policy/{policyId}/evaluate": {
1377
1472
  parameters: {
1378
1473
  query?: never;
1379
1474
  header?: never;
@@ -1383,43 +1478,35 @@ export interface paths {
1383
1478
  get?: never;
1384
1479
  put?: never;
1385
1480
  /**
1386
- * LLM analysis of a borderline / surprising score (L2c, Pro+)
1387
- * @description Asks the LLM to inspect a score that's near a verdict boundary or contradicts operator intuition. Returns a narrative + a structured `limiting_class` taxonomy entry.
1481
+ * Re-evaluate a bound policy against historical replay (Scale)
1482
+ * @description Runs the trigger walk against the historical archive for the policy's coverage year and inserts any newly detected payout events. No request body. Fires the `underwriting.policy.triggered` webhook the first time new events are inserted.
1388
1483
  */
1389
1484
  post: {
1390
1485
  parameters: {
1391
1486
  query?: never;
1392
1487
  header?: never;
1393
- path?: never;
1394
- cookie?: never;
1395
- };
1396
- requestBody: {
1397
- content: {
1398
- "application/json": {
1399
- location: components["schemas"]["GeoPoint"];
1400
- scoreResult?: components["schemas"]["ScoreResponse"];
1401
- /** Format: uuid */
1402
- session_id?: string;
1403
- /** @enum {string} */
1404
- locale?: "en" | "it";
1405
- /** @enum {string} */
1406
- model?: "claude-haiku-4-5-20251001" | "claude-sonnet-4-6";
1407
- };
1488
+ path: {
1489
+ policyId: string;
1408
1490
  };
1491
+ cookie?: never;
1409
1492
  };
1493
+ requestBody?: never;
1410
1494
  responses: {
1411
- /** @description Narrative + limiting_class */
1495
+ /** @description Policy + payout events + counts of newly inserted / skipped */
1412
1496
  200: {
1413
1497
  headers: {
1414
1498
  [name: string]: unknown;
1415
1499
  };
1416
1500
  content: {
1417
1501
  "application/json": {
1418
- [key: string]: unknown;
1502
+ policy: components["schemas"]["SerialisedPolicy"];
1503
+ events: components["schemas"]["SerialisedPayoutEvent"][];
1504
+ inserted: number;
1505
+ skipped: number;
1419
1506
  };
1420
1507
  };
1421
1508
  };
1422
- /** @description Requires Pro+ plan */
1509
+ /** @description Requires Scale plan */
1423
1510
  402: {
1424
1511
  headers: {
1425
1512
  [name: string]: unknown;
@@ -1428,7 +1515,16 @@ export interface paths {
1428
1515
  "application/json": components["schemas"]["Error"];
1429
1516
  };
1430
1517
  };
1431
- /** @description Validation error */
1518
+ /** @description Policy not found */
1519
+ 404: {
1520
+ headers: {
1521
+ [name: string]: unknown;
1522
+ };
1523
+ content: {
1524
+ "application/json": components["schemas"]["Error"];
1525
+ };
1526
+ };
1527
+ /** @description Policy in a terminal state (settled / expired) */
1432
1528
  422: {
1433
1529
  headers: {
1434
1530
  [name: string]: unknown;
@@ -1437,6 +1533,15 @@ export interface paths {
1437
1533
  "application/json": components["schemas"]["Error"];
1438
1534
  };
1439
1535
  };
1536
+ /** @description Policy persistence or HistoricalProvider not configured */
1537
+ 503: {
1538
+ headers: {
1539
+ [name: string]: unknown;
1540
+ };
1541
+ content: {
1542
+ "application/json": components["schemas"]["Error"];
1543
+ };
1544
+ };
1440
1545
  };
1441
1546
  };
1442
1547
  delete?: never;
@@ -1445,7 +1550,7 @@ export interface paths {
1445
1550
  patch?: never;
1446
1551
  trace?: never;
1447
1552
  };
1448
- "/v1/projections/portfolio": {
1553
+ "/v1/underwriting/policy/{policyId}/settle": {
1449
1554
  parameters: {
1450
1555
  query?: never;
1451
1556
  header?: never;
@@ -1454,38 +1559,37 @@ export interface paths {
1454
1559
  };
1455
1560
  get?: never;
1456
1561
  put?: never;
1457
- /** Climate projections across a portfolio of spots (Scale) */
1562
+ /**
1563
+ * Settle a bound policy (platform-ops only)
1564
+ * @description PLATFORM-OPS ONLY — requires the `platform_admin` scope (cross-tenant Goable/underwriter operation, not a policyholder self-service action). Records the settlement wire reference + freezes the payout, and fires the `underwriting.policy.settled` webhook. Normally invoked by the daily settlement cron; use manually only for out-of-band settlement.
1565
+ */
1458
1566
  post: {
1459
1567
  parameters: {
1460
1568
  query?: never;
1461
1569
  header?: never;
1462
- path?: never;
1570
+ path: {
1571
+ policyId: string;
1572
+ };
1463
1573
  cookie?: never;
1464
1574
  };
1465
1575
  requestBody: {
1466
1576
  content: {
1467
1577
  "application/json": {
1468
- spots: {
1469
- location: components["schemas"]["GeoPoint"];
1470
- activity: string;
1471
- spotId?: string;
1472
- }[];
1473
- scenarios: ("SSP1-2.6" | "SSP2-4.5" | "SSP3-7.0" | "SSP5-8.5")[];
1474
- horizonDecades?: string[];
1475
- /** @enum {string} */
1476
- baselineDecade?: "2020s" | "2030s";
1578
+ settlementReference: string;
1579
+ /** Format: date-time */
1580
+ settledAt?: string;
1477
1581
  };
1478
1582
  };
1479
1583
  };
1480
1584
  responses: {
1481
- /** @description Per-spot per-decade projection distributions */
1585
+ /** @description Settled policy */
1482
1586
  200: {
1483
1587
  headers: {
1484
1588
  [name: string]: unknown;
1485
1589
  };
1486
1590
  content: {
1487
1591
  "application/json": {
1488
- [key: string]: unknown;
1592
+ policy: components["schemas"]["SerialisedPolicy"];
1489
1593
  };
1490
1594
  };
1491
1595
  };
@@ -1498,7 +1602,25 @@ export interface paths {
1498
1602
  "application/json": components["schemas"]["Error"];
1499
1603
  };
1500
1604
  };
1501
- /** @description Validation error */
1605
+ /** @description Missing scope: platform_admin */
1606
+ 403: {
1607
+ headers: {
1608
+ [name: string]: unknown;
1609
+ };
1610
+ content: {
1611
+ "application/json": components["schemas"]["Error"];
1612
+ };
1613
+ };
1614
+ /** @description Policy not found */
1615
+ 404: {
1616
+ headers: {
1617
+ [name: string]: unknown;
1618
+ };
1619
+ content: {
1620
+ "application/json": components["schemas"]["Error"];
1621
+ };
1622
+ };
1623
+ /** @description Validation error or policy not in a settleable state */
1502
1624
  422: {
1503
1625
  headers: {
1504
1626
  [name: string]: unknown;
@@ -1507,6 +1629,15 @@ export interface paths {
1507
1629
  "application/json": components["schemas"]["Error"];
1508
1630
  };
1509
1631
  };
1632
+ /** @description Policy persistence not configured */
1633
+ 503: {
1634
+ headers: {
1635
+ [name: string]: unknown;
1636
+ };
1637
+ content: {
1638
+ "application/json": components["schemas"]["Error"];
1639
+ };
1640
+ };
1510
1641
  };
1511
1642
  };
1512
1643
  delete?: never;
@@ -1515,7 +1646,7 @@ export interface paths {
1515
1646
  patch?: never;
1516
1647
  trace?: never;
1517
1648
  };
1518
- "/v1/projections/adaptation-report": {
1649
+ "/v1/score/{sessionId}/outcome": {
1519
1650
  parameters: {
1520
1651
  query?: never;
1521
1652
  header?: never;
@@ -1525,37 +1656,36 @@ export interface paths {
1525
1656
  get?: never;
1526
1657
  put?: never;
1527
1658
  /**
1528
- * Climate adaptation report for an operator (Scale)
1529
- * @description Combines projections across a portfolio + qualitative summary of which dimensions are likely to bind (wind shifts, water-temp shifts, etc.). Designed as input for a tourism-board adaptation plan.
1659
+ * Report observed outcome for a scored session
1660
+ * @description Close the calibration loop. Submit the actual outcome (ran/cancelled/no_show/rescheduled/note) of a /v1/score session. The calibration pipeline + forecast verification + drift monitor consume these. Requires the `outcomes:write` scope (live keys carry it by default; test keys don't). Supports the optional `Idempotency-Key` header (see parameter description) so a client-side retry can't record the same outcome twice.
1530
1661
  */
1531
1662
  post: {
1532
1663
  parameters: {
1533
1664
  query?: never;
1534
- header?: never;
1535
- path?: never;
1665
+ header?: {
1666
+ /** @description Optional client-generated key (unique per logical request, scoped to your tenant). A retry with the SAME key and the SAME request body replays the original response verbatim without re-executing the request. A retry with the same key and a DIFFERENT body, or one that arrives while the original is still in flight, returns 409 IDEMPOTENCY_KEY_CONFLICT. Claims expire after 24h. */
1667
+ "Idempotency-Key"?: string;
1668
+ };
1669
+ path: {
1670
+ /** @description Session UUID returned in the /v1/score response metadata. */
1671
+ sessionId: string;
1672
+ };
1536
1673
  cookie?: never;
1537
1674
  };
1538
1675
  requestBody: {
1539
1676
  content: {
1540
1677
  "application/json": {
1541
- spots: ({
1542
- location: components["schemas"]["GeoPoint"];
1543
- activity: string;
1544
- spotId?: string;
1545
- subSpotSlug?: string;
1546
- } & {
1678
+ /** @enum {string} */
1679
+ outcome_type: "ran" | "cancelled" | "rescheduled" | "no_show" | "note";
1680
+ detail?: {
1547
1681
  [key: string]: unknown;
1548
- })[];
1549
- scenarios: ("SSP1-2.6" | "SSP2-4.5" | "SSP3-7.0" | "SSP5-8.5")[];
1550
- horizonDecades?: string[];
1551
- } & {
1552
- [key: string]: unknown;
1682
+ };
1553
1683
  };
1554
1684
  };
1555
1685
  };
1556
1686
  responses: {
1557
- /** @description Adaptation report + binding-dimension summary */
1558
- 200: {
1687
+ /** @description Accepted (queued for the next calibration batch) */
1688
+ 202: {
1559
1689
  headers: {
1560
1690
  [name: string]: unknown;
1561
1691
  };
@@ -1565,8 +1695,8 @@ export interface paths {
1565
1695
  };
1566
1696
  };
1567
1697
  };
1568
- /** @description Requires Scale plan */
1569
- 402: {
1698
+ /** @description Missing scope: outcomes:write */
1699
+ 403: {
1570
1700
  headers: {
1571
1701
  [name: string]: unknown;
1572
1702
  };
@@ -1574,8 +1704,8 @@ export interface paths {
1574
1704
  "application/json": components["schemas"]["Error"];
1575
1705
  };
1576
1706
  };
1577
- /** @description Validation error */
1578
- 422: {
1707
+ /** @description Session not found */
1708
+ 404: {
1579
1709
  headers: {
1580
1710
  [name: string]: unknown;
1581
1711
  };
@@ -1583,15 +1713,33 @@ export interface paths {
1583
1713
  "application/json": components["schemas"]["Error"];
1584
1714
  };
1585
1715
  };
1586
- };
1587
- };
1588
- delete?: never;
1589
- options?: never;
1716
+ /** @description IDEMPOTENCY_KEY_CONFLICT — a request with this Idempotency-Key is still in flight, or was already used with a different request body */
1717
+ 409: {
1718
+ headers: {
1719
+ [name: string]: unknown;
1720
+ };
1721
+ content: {
1722
+ "application/json": components["schemas"]["Error"];
1723
+ };
1724
+ };
1725
+ /** @description Validation error */
1726
+ 422: {
1727
+ headers: {
1728
+ [name: string]: unknown;
1729
+ };
1730
+ content: {
1731
+ "application/json": components["schemas"]["Error"];
1732
+ };
1733
+ };
1734
+ };
1735
+ };
1736
+ delete?: never;
1737
+ options?: never;
1590
1738
  head?: never;
1591
1739
  patch?: never;
1592
1740
  trace?: never;
1593
1741
  };
1594
- "/v1/observations": {
1742
+ "/v1/score/difficulty": {
1595
1743
  parameters: {
1596
1744
  query?: never;
1597
1745
  header?: never;
@@ -1601,8 +1749,8 @@ export interface paths {
1601
1749
  get?: never;
1602
1750
  put?: never;
1603
1751
  /**
1604
- * Submit station observation for data assimilation (Pro+)
1605
- * @description Push tenant-station observations (wind / wave / temperature / etc.) into the 0-6h assimilation window. The optimal-interpolation blending pulls them into forecast samples for nearby spots, improving short-horizon skill.
1752
+ * L15 intrinsic-difficulty atlas lookup (Pro+)
1753
+ * @description Returns the per-dimension intrinsic difficulty curve δ(x) for the sub-spot the (activity, location) resolves to. Pure atlas read — no scoring, no weather fetch. 404 NO_DIFFICULTY_ATLAS when the sub-spot resolves but no atlas row exists yet (dormant cell communicated honestly rather than synthesised).
1606
1754
  */
1607
1755
  post: {
1608
1756
  parameters: {
@@ -1614,28 +1762,35 @@ export interface paths {
1614
1762
  requestBody: {
1615
1763
  content: {
1616
1764
  "application/json": {
1617
- /** Format: uuid */
1618
- stationId: string;
1619
- /** Format: date-time */
1620
- observedAt: string;
1621
- values: {
1622
- [key: string]: number;
1623
- };
1624
- qualityFlags?: {
1625
- [key: string]: unknown;
1626
- };
1765
+ activity: string;
1766
+ location: components["schemas"]["GeoPoint"];
1627
1767
  };
1628
1768
  };
1629
1769
  };
1630
1770
  responses: {
1631
- /** @description Accepted */
1632
- 202: {
1771
+ /** @description Per-dimension intrinsic difficulty curves for the resolved sub-spot */
1772
+ 200: {
1633
1773
  headers: {
1634
1774
  [name: string]: unknown;
1635
1775
  };
1636
1776
  content: {
1637
1777
  "application/json": {
1638
- [key: string]: unknown;
1778
+ resolved: {
1779
+ /** @enum {string} */
1780
+ level: "sub-spot" | "cluster" | "region" | "base";
1781
+ slug: string;
1782
+ sub_spot_slug?: string;
1783
+ distance_to_sub_spot_m?: number;
1784
+ };
1785
+ dimensions: {
1786
+ dimension: string;
1787
+ grid: number[];
1788
+ difficulty: number[];
1789
+ discrimination_a: number;
1790
+ cohort_hash: string;
1791
+ /** Format: date-time */
1792
+ computed_at: string;
1793
+ }[];
1639
1794
  };
1640
1795
  };
1641
1796
  };
@@ -1648,7 +1803,7 @@ export interface paths {
1648
1803
  "application/json": components["schemas"]["Error"];
1649
1804
  };
1650
1805
  };
1651
- /** @description Station not found */
1806
+ /** @description No profile for activity, or no atlas row for the resolved sub-spot */
1652
1807
  404: {
1653
1808
  headers: {
1654
1809
  [name: string]: unknown;
@@ -1666,6 +1821,15 @@ export interface paths {
1666
1821
  "application/json": components["schemas"]["Error"];
1667
1822
  };
1668
1823
  };
1824
+ /** @description Difficulty atlas reader not wired */
1825
+ 503: {
1826
+ headers: {
1827
+ [name: string]: unknown;
1828
+ };
1829
+ content: {
1830
+ "application/json": components["schemas"]["Error"];
1831
+ };
1832
+ };
1669
1833
  };
1670
1834
  };
1671
1835
  delete?: never;
@@ -1674,38 +1838,19 @@ export interface paths {
1674
1838
  patch?: never;
1675
1839
  trace?: never;
1676
1840
  };
1677
- "/v1/observations/stations": {
1841
+ "/v1/recommend-spot": {
1678
1842
  parameters: {
1679
1843
  query?: never;
1680
1844
  header?: never;
1681
1845
  path?: never;
1682
1846
  cookie?: never;
1683
1847
  };
1684
- /** List tenant stations */
1685
- get: {
1686
- parameters: {
1687
- query?: never;
1688
- header?: never;
1689
- path?: never;
1690
- cookie?: never;
1691
- };
1692
- requestBody?: never;
1693
- responses: {
1694
- /** @description Array of stations */
1695
- 200: {
1696
- headers: {
1697
- [name: string]: unknown;
1698
- };
1699
- content: {
1700
- "application/json": {
1701
- [key: string]: unknown;
1702
- };
1703
- };
1704
- };
1705
- };
1706
- };
1848
+ get?: never;
1707
1849
  put?: never;
1708
- /** Register a tenant observation station (Pro+) */
1850
+ /**
1851
+ * Spot recommender — inverse query (L10)
1852
+ * @description Given (activity, region center, radius, window) → top-K ranked sub-spots in the catalog. Composition of L1-L3 scoring + L4.6 confidence + L6 personal blend (Pro+, when pseudonym supplied). Plan caps: radius 25/50/200/1000 km, topK 5/10/20/50 across Free / Starter / Pro / Scale. Personalization Pro+ only. Hard-gated candidates (lightning ≥ 0.85, AQI hazardous) are dropped from results; `allGated=true` distinguishes 'all in-radius spots were unsafe' from 'no spots in radius at all'.
1853
+ */
1709
1854
  post: {
1710
1855
  parameters: {
1711
1856
  query?: never;
@@ -1716,30 +1861,79 @@ export interface paths {
1716
1861
  requestBody: {
1717
1862
  content: {
1718
1863
  "application/json": {
1719
- name: string;
1720
- location: components["schemas"]["GeoPoint"];
1721
- elevationM?: number;
1722
- metadata?: {
1723
- [key: string]: unknown;
1724
- };
1725
- } & {
1726
- [key: string]: unknown;
1864
+ activity: string;
1865
+ regionCenter: components["schemas"]["GeoPoint"];
1866
+ radiusKm: number;
1867
+ topK: number;
1868
+ window?: components["schemas"]["TimeWindow"];
1869
+ /** @description Optional booking-platform-side pseudonym. When present, Pro+ tenants get personalization via the L6 cold-start blend (cap 0.5 weight). */
1870
+ userPseudonym?: string;
1727
1871
  };
1728
1872
  };
1729
1873
  };
1730
1874
  responses: {
1731
- /** @description Station created */
1732
- 201: {
1875
+ /** @description Ranked top-K sub-spots + observability metadata. The optional `coverage` field appears only on empty results from a catalog gap (not from hard-gating). */
1876
+ 200: {
1733
1877
  headers: {
1878
+ /** @description Daily safety cap for this endpoint + plan (not the monthly billing quota). Omitted on unlimited (Scale) plans. */
1879
+ "X-RateLimit-Limit"?: number;
1880
+ /** @description Requests remaining in the current UTC-midnight daily window. Omitted on unlimited (Scale) plans. */
1881
+ "X-RateLimit-Remaining"?: number;
1882
+ /** @description Unix timestamp (seconds) at which the daily window resets. Omitted on unlimited (Scale) plans. */
1883
+ "X-RateLimit-Reset"?: number;
1734
1884
  [name: string]: unknown;
1735
1885
  };
1736
1886
  content: {
1737
1887
  "application/json": {
1738
- [key: string]: unknown;
1888
+ results?: {
1889
+ spotSlug?: string;
1890
+ name?: string;
1891
+ location?: components["schemas"]["GeoPoint"];
1892
+ distanceKm?: number;
1893
+ score?: number;
1894
+ effectiveScore?: number;
1895
+ verdict?: string;
1896
+ personalScore?: number | null;
1897
+ personalWeight?: number;
1898
+ rank?: number;
1899
+ }[];
1900
+ allGated?: boolean;
1901
+ totalCandidates?: number;
1902
+ rankedCandidates?: number;
1903
+ effectiveRadiusKm?: number;
1904
+ effectiveTopK?: number;
1905
+ personalizationApplied?: boolean;
1906
+ latencyMs?: number;
1907
+ coverage?: {
1908
+ /** @enum {string} */
1909
+ status: "no_subspots_in_radius";
1910
+ nearestSubSpot: {
1911
+ slug: string;
1912
+ name: string;
1913
+ distanceKm: number;
1914
+ };
1915
+ /** @enum {string} */
1916
+ suggestedAction: "expand_radius";
1917
+ suggestedRadiusKm: number;
1918
+ } | {
1919
+ /** @enum {string} */
1920
+ status: "no_subspots_for_activity";
1921
+ /** @enum {string} */
1922
+ suggestedAction: "request_coverage";
1923
+ };
1739
1924
  };
1740
1925
  };
1741
1926
  };
1742
- /** @description Requires Pro+ plan */
1927
+ /** @description Missing/invalid bearer token */
1928
+ 401: {
1929
+ headers: {
1930
+ [name: string]: unknown;
1931
+ };
1932
+ content: {
1933
+ "application/json": components["schemas"]["Error"];
1934
+ };
1935
+ };
1936
+ /** @description Plan limit exceeded (radius or topK above plan cap) */
1743
1937
  402: {
1744
1938
  headers: {
1745
1939
  [name: string]: unknown;
@@ -1748,7 +1942,7 @@ export interface paths {
1748
1942
  "application/json": components["schemas"]["Error"];
1749
1943
  };
1750
1944
  };
1751
- /** @description Validation error */
1945
+ /** @description Validation error or unknown activity */
1752
1946
  422: {
1753
1947
  headers: {
1754
1948
  [name: string]: unknown;
@@ -1757,6 +1951,26 @@ export interface paths {
1757
1951
  "application/json": components["schemas"]["Error"];
1758
1952
  };
1759
1953
  };
1954
+ /** @description Daily rate limit (safety cap) exceeded for this endpoint + plan. */
1955
+ 429: {
1956
+ headers: {
1957
+ /** @description Seconds until the daily rate-limit window resets. Present only on this 429 response. */
1958
+ "Retry-After"?: number;
1959
+ [name: string]: unknown;
1960
+ };
1961
+ content: {
1962
+ "application/json": components["schemas"]["Error"];
1963
+ };
1964
+ };
1965
+ /** @description Spatial resolver not wired */
1966
+ 503: {
1967
+ headers: {
1968
+ [name: string]: unknown;
1969
+ };
1970
+ content: {
1971
+ "application/json": components["schemas"]["Error"];
1972
+ };
1973
+ };
1760
1974
  };
1761
1975
  };
1762
1976
  delete?: never;
@@ -1765,7 +1979,7 @@ export interface paths {
1765
1979
  patch?: never;
1766
1980
  trace?: never;
1767
1981
  };
1768
- "/v1/observations/stations/{stationId}": {
1982
+ "/v1/intelligence/edge-case": {
1769
1983
  parameters: {
1770
1984
  query?: never;
1771
1985
  header?: never;
@@ -1774,36 +1988,33 @@ export interface paths {
1774
1988
  };
1775
1989
  get?: never;
1776
1990
  put?: never;
1777
- post?: never;
1778
- delete?: never;
1779
- options?: never;
1780
- head?: never;
1781
- /** Update a station (Pro+) */
1782
- patch: {
1991
+ /**
1992
+ * LLM analysis of a borderline / surprising score (L2c, any plan — Anthropic BYOK)
1993
+ * @description Asks the LLM to inspect a score that's near a verdict boundary or contradicts operator intuition. Returns a narrative + a structured `limiting_class` taxonomy entry. Requires the tenant's own Anthropic key (PUT /v1/tenant/llm-key) — no longer plan-gated.
1994
+ */
1995
+ post: {
1783
1996
  parameters: {
1784
1997
  query?: never;
1785
1998
  header?: never;
1786
- path: {
1787
- stationId: string;
1788
- };
1999
+ path?: never;
1789
2000
  cookie?: never;
1790
2001
  };
1791
2002
  requestBody: {
1792
2003
  content: {
1793
2004
  "application/json": {
1794
- name?: string;
1795
- location?: components["schemas"]["GeoPoint"];
1796
- elevationM?: number;
1797
- metadata?: {
1798
- [key: string]: unknown;
1799
- };
1800
- } & {
1801
- [key: string]: unknown;
2005
+ location: components["schemas"]["GeoPoint"];
2006
+ scoreResult?: components["schemas"]["ScoreResponse"];
2007
+ /** Format: uuid */
2008
+ session_id?: string;
2009
+ /** @enum {string} */
2010
+ locale?: "en" | "it";
2011
+ /** @enum {string} */
2012
+ model?: "claude-haiku-4-5-20251001" | "claude-sonnet-4-6";
1802
2013
  };
1803
2014
  };
1804
2015
  };
1805
2016
  responses: {
1806
- /** @description Updated station */
2017
+ /** @description Narrative + limiting_class */
1807
2018
  200: {
1808
2019
  headers: {
1809
2020
  [name: string]: unknown;
@@ -1814,8 +2025,8 @@ export interface paths {
1814
2025
  };
1815
2026
  };
1816
2027
  };
1817
- /** @description Requires Pro+ plan */
1818
- 402: {
2028
+ /** @description Validation error */
2029
+ 422: {
1819
2030
  headers: {
1820
2031
  [name: string]: unknown;
1821
2032
  };
@@ -1823,8 +2034,8 @@ export interface paths {
1823
2034
  "application/json": components["schemas"]["Error"];
1824
2035
  };
1825
2036
  };
1826
- /** @description Station not found */
1827
- 404: {
2037
+ /** @description No Anthropic key configured for this tenant */
2038
+ 503: {
1828
2039
  headers: {
1829
2040
  [name: string]: unknown;
1830
2041
  };
@@ -1834,9 +2045,13 @@ export interface paths {
1834
2045
  };
1835
2046
  };
1836
2047
  };
2048
+ delete?: never;
2049
+ options?: never;
2050
+ head?: never;
2051
+ patch?: never;
1837
2052
  trace?: never;
1838
2053
  };
1839
- "/v1/public/signup": {
2054
+ "/v1/projections/portfolio": {
1840
2055
  parameters: {
1841
2056
  query?: never;
1842
2057
  header?: never;
@@ -1845,10 +2060,7 @@ export interface paths {
1845
2060
  };
1846
2061
  get?: never;
1847
2062
  put?: never;
1848
- /**
1849
- * Self-service tenant signup (no auth)
1850
- * @description Public endpoint, IP-rate-limited (3 attempts / 24h / IP). Creates a tenant + sends a magic-link to the contact email. Always returns 202 on success — never reveals whether an email is already registered. Optional Cloudflare Turnstile token strengthens the gate.
1851
- */
2063
+ /** Climate projections across a portfolio of spots (Scale) */
1852
2064
  post: {
1853
2065
  parameters: {
1854
2066
  query?: never;
@@ -1859,21 +2071,21 @@ export interface paths {
1859
2071
  requestBody: {
1860
2072
  content: {
1861
2073
  "application/json": {
1862
- displayName: string;
1863
- /** Format: email */
1864
- contactEmail: string;
1865
- /**
1866
- * @description Must be true — records ToS / Privacy / DPA acceptance.
1867
- * @enum {boolean}
1868
- */
1869
- acceptTerms: true;
1870
- turnstileToken?: string;
2074
+ spots: {
2075
+ location: components["schemas"]["GeoPoint"];
2076
+ activity: string;
2077
+ spotId?: string;
2078
+ }[];
2079
+ scenarios: ("SSP1-2.6" | "SSP2-4.5" | "SSP3-7.0" | "SSP5-8.5")[];
2080
+ horizonDecades?: string[];
2081
+ /** @enum {string} */
2082
+ baselineDecade?: "2020s" | "2030s";
1871
2083
  };
1872
2084
  };
1873
2085
  };
1874
2086
  responses: {
1875
- /** @description Accepted — magic-link sent if signup valid */
1876
- 202: {
2087
+ /** @description Per-spot per-decade projection distributions */
2088
+ 200: {
1877
2089
  headers: {
1878
2090
  [name: string]: unknown;
1879
2091
  };
@@ -1883,8 +2095,8 @@ export interface paths {
1883
2095
  };
1884
2096
  };
1885
2097
  };
1886
- /** @description Validation error */
1887
- 422: {
2098
+ /** @description Requires Scale plan */
2099
+ 402: {
1888
2100
  headers: {
1889
2101
  [name: string]: unknown;
1890
2102
  };
@@ -1892,8 +2104,8 @@ export interface paths {
1892
2104
  "application/json": components["schemas"]["Error"];
1893
2105
  };
1894
2106
  };
1895
- /** @description IP rate limit exceeded */
1896
- 429: {
2107
+ /** @description Validation error */
2108
+ 422: {
1897
2109
  headers: {
1898
2110
  [name: string]: unknown;
1899
2111
  };
@@ -1909,32 +2121,761 @@ export interface paths {
1909
2121
  patch?: never;
1910
2122
  trace?: never;
1911
2123
  };
1912
- "/v1/public/catalog-stats": {
2124
+ "/v1/projections/adaptation-report": {
1913
2125
  parameters: {
1914
2126
  query?: never;
1915
2127
  header?: never;
1916
2128
  path?: never;
1917
2129
  cookie?: never;
1918
2130
  };
2131
+ get?: never;
2132
+ put?: never;
1919
2133
  /**
1920
- * Open catalogue coverage stats (no auth)
1921
- * @description Live coverage numbers for the open activity catalogue (CC BY 4.0). Returns totals across activities · sub-spots · clusters · regions · countries plus a per-activity breakdown with country codes + status (`seeded` ≥10 sub-spots / `partial` 1-9 / `empty` 0). Edge-cached for 5 minutes — new sub-spots ship via PR + release cycle, not at runtime. Same JSON the /catalog landing renders; expose this surface for marketing / partner sites that want to embed live numbers without scraping HTML.
2134
+ * Climate adaptation report for an operator (Scale)
2135
+ * @description Combines projections across a portfolio + qualitative summary of which dimensions are likely to bind (wind shifts, water-temp shifts, etc.). Designed as input for a tourism-board adaptation plan.
1922
2136
  */
1923
- get: {
2137
+ post: {
1924
2138
  parameters: {
1925
2139
  query?: never;
1926
2140
  header?: never;
1927
2141
  path?: never;
1928
2142
  cookie?: never;
1929
2143
  };
1930
- requestBody?: never;
1931
- responses: {
1932
- /** @description Live coverage stats from the @goable-io/profiles-catalog package */
1933
- 200: {
1934
- headers: {
1935
- [name: string]: unknown;
1936
- };
1937
- content: {
2144
+ requestBody: {
2145
+ content: {
2146
+ "application/json": {
2147
+ spots: ({
2148
+ location: components["schemas"]["GeoPoint"];
2149
+ activity: string;
2150
+ spotId?: string;
2151
+ subSpotSlug?: string;
2152
+ } & {
2153
+ [key: string]: unknown;
2154
+ })[];
2155
+ scenarios: ("SSP1-2.6" | "SSP2-4.5" | "SSP3-7.0" | "SSP5-8.5")[];
2156
+ horizonDecades?: string[];
2157
+ } & {
2158
+ [key: string]: unknown;
2159
+ };
2160
+ };
2161
+ };
2162
+ responses: {
2163
+ /** @description Adaptation report + binding-dimension summary */
2164
+ 200: {
2165
+ headers: {
2166
+ [name: string]: unknown;
2167
+ };
2168
+ content: {
2169
+ "application/json": {
2170
+ [key: string]: unknown;
2171
+ };
2172
+ };
2173
+ };
2174
+ /** @description Requires Scale plan */
2175
+ 402: {
2176
+ headers: {
2177
+ [name: string]: unknown;
2178
+ };
2179
+ content: {
2180
+ "application/json": components["schemas"]["Error"];
2181
+ };
2182
+ };
2183
+ /** @description Validation error */
2184
+ 422: {
2185
+ headers: {
2186
+ [name: string]: unknown;
2187
+ };
2188
+ content: {
2189
+ "application/json": components["schemas"]["Error"];
2190
+ };
2191
+ };
2192
+ };
2193
+ };
2194
+ delete?: never;
2195
+ options?: never;
2196
+ head?: never;
2197
+ patch?: never;
2198
+ trace?: never;
2199
+ };
2200
+ "/v1/observations": {
2201
+ parameters: {
2202
+ query?: never;
2203
+ header?: never;
2204
+ path?: never;
2205
+ cookie?: never;
2206
+ };
2207
+ get?: never;
2208
+ put?: never;
2209
+ /**
2210
+ * Submit station observations for data assimilation (Pro+)
2211
+ * @description Push tenant-station observations (wind / wave / temperature / etc.) into the 0-6h assimilation window. The optimal-interpolation blending pulls them into forecast samples for nearby spots, improving short-horizon skill.
2212
+ */
2213
+ post: {
2214
+ parameters: {
2215
+ query?: never;
2216
+ header?: never;
2217
+ path?: never;
2218
+ cookie?: never;
2219
+ };
2220
+ requestBody: {
2221
+ content: {
2222
+ "application/json": {
2223
+ /** Format: uuid */
2224
+ stationId: string;
2225
+ observations: {
2226
+ /** Format: date-time */
2227
+ observedAt: string;
2228
+ /** @enum {string} */
2229
+ variable: "wind_speed_kn" | "wind_dir_deg" | "wave_height_m" | "temp_c" | "sea_surface_temp_c" | "pressure_hpa" | "precip_mm";
2230
+ value: number;
2231
+ /** @enum {string} */
2232
+ qualityFlag?: "verified" | "unflagged" | "flagged_low_quality";
2233
+ }[];
2234
+ };
2235
+ };
2236
+ };
2237
+ responses: {
2238
+ /** @description Accepted */
2239
+ 202: {
2240
+ headers: {
2241
+ [name: string]: unknown;
2242
+ };
2243
+ content: {
2244
+ "application/json": {
2245
+ [key: string]: unknown;
2246
+ };
2247
+ };
2248
+ };
2249
+ /** @description Requires Pro+ plan */
2250
+ 402: {
2251
+ headers: {
2252
+ [name: string]: unknown;
2253
+ };
2254
+ content: {
2255
+ "application/json": components["schemas"]["Error"];
2256
+ };
2257
+ };
2258
+ /** @description Station not found */
2259
+ 404: {
2260
+ headers: {
2261
+ [name: string]: unknown;
2262
+ };
2263
+ content: {
2264
+ "application/json": components["schemas"]["Error"];
2265
+ };
2266
+ };
2267
+ /** @description Station inactive */
2268
+ 409: {
2269
+ headers: {
2270
+ [name: string]: unknown;
2271
+ };
2272
+ content: {
2273
+ "application/json": components["schemas"]["Error"];
2274
+ };
2275
+ };
2276
+ /** @description Validation error / observation in future / variable mismatch */
2277
+ 422: {
2278
+ headers: {
2279
+ [name: string]: unknown;
2280
+ };
2281
+ content: {
2282
+ "application/json": components["schemas"]["Error"];
2283
+ };
2284
+ };
2285
+ /** @description Observation store not wired */
2286
+ 503: {
2287
+ headers: {
2288
+ [name: string]: unknown;
2289
+ };
2290
+ content: {
2291
+ "application/json": components["schemas"]["Error"];
2292
+ };
2293
+ };
2294
+ };
2295
+ };
2296
+ delete?: never;
2297
+ options?: never;
2298
+ head?: never;
2299
+ patch?: never;
2300
+ trace?: never;
2301
+ };
2302
+ "/v1/observations/stations": {
2303
+ parameters: {
2304
+ query?: never;
2305
+ header?: never;
2306
+ path?: never;
2307
+ cookie?: never;
2308
+ };
2309
+ /** List tenant stations */
2310
+ get: {
2311
+ parameters: {
2312
+ query?: never;
2313
+ header?: never;
2314
+ path?: never;
2315
+ cookie?: never;
2316
+ };
2317
+ requestBody?: never;
2318
+ responses: {
2319
+ /** @description Array of stations */
2320
+ 200: {
2321
+ headers: {
2322
+ [name: string]: unknown;
2323
+ };
2324
+ content: {
2325
+ "application/json": {
2326
+ [key: string]: unknown;
2327
+ };
2328
+ };
2329
+ };
2330
+ /** @description Station registry not wired */
2331
+ 503: {
2332
+ headers: {
2333
+ [name: string]: unknown;
2334
+ };
2335
+ content: {
2336
+ "application/json": components["schemas"]["Error"];
2337
+ };
2338
+ };
2339
+ };
2340
+ };
2341
+ put?: never;
2342
+ /** Register a tenant observation station */
2343
+ post: {
2344
+ parameters: {
2345
+ query?: never;
2346
+ header?: never;
2347
+ path?: never;
2348
+ cookie?: never;
2349
+ };
2350
+ requestBody: {
2351
+ content: {
2352
+ "application/json": {
2353
+ name: string;
2354
+ point: components["schemas"]["GeoPoint"];
2355
+ altitudeM?: number;
2356
+ variables: ("wind_speed_kn" | "wind_dir_deg" | "wave_height_m" | "temp_c" | "sea_surface_temp_c" | "pressure_hpa" | "precip_mm")[];
2357
+ /** @enum {string} */
2358
+ stationClass?: "verified" | "unflagged" | "flagged_low_quality";
2359
+ updateCadenceMinutes?: number;
2360
+ notes?: string;
2361
+ };
2362
+ };
2363
+ };
2364
+ responses: {
2365
+ /** @description Station created */
2366
+ 201: {
2367
+ headers: {
2368
+ [name: string]: unknown;
2369
+ };
2370
+ content: {
2371
+ "application/json": {
2372
+ [key: string]: unknown;
2373
+ };
2374
+ };
2375
+ };
2376
+ /** @description Validation error */
2377
+ 422: {
2378
+ headers: {
2379
+ [name: string]: unknown;
2380
+ };
2381
+ content: {
2382
+ "application/json": components["schemas"]["Error"];
2383
+ };
2384
+ };
2385
+ /** @description Station registry not wired */
2386
+ 503: {
2387
+ headers: {
2388
+ [name: string]: unknown;
2389
+ };
2390
+ content: {
2391
+ "application/json": components["schemas"]["Error"];
2392
+ };
2393
+ };
2394
+ };
2395
+ };
2396
+ delete?: never;
2397
+ options?: never;
2398
+ head?: never;
2399
+ patch?: never;
2400
+ trace?: never;
2401
+ };
2402
+ "/v1/observations/stations/{stationId}": {
2403
+ parameters: {
2404
+ query?: never;
2405
+ header?: never;
2406
+ path?: never;
2407
+ cookie?: never;
2408
+ };
2409
+ get?: never;
2410
+ put?: never;
2411
+ post?: never;
2412
+ delete?: never;
2413
+ options?: never;
2414
+ head?: never;
2415
+ /** Update a station */
2416
+ patch: {
2417
+ parameters: {
2418
+ query?: never;
2419
+ header?: never;
2420
+ path: {
2421
+ stationId: string;
2422
+ };
2423
+ cookie?: never;
2424
+ };
2425
+ requestBody: {
2426
+ content: {
2427
+ "application/json": {
2428
+ name?: string;
2429
+ point?: components["schemas"]["GeoPoint"];
2430
+ altitudeM?: number;
2431
+ variables?: ("wind_speed_kn" | "wind_dir_deg" | "wave_height_m" | "temp_c" | "sea_surface_temp_c" | "pressure_hpa" | "precip_mm")[];
2432
+ /** @enum {string} */
2433
+ stationClass?: "verified" | "unflagged" | "flagged_low_quality";
2434
+ updateCadenceMinutes?: number;
2435
+ active?: boolean;
2436
+ notes?: string;
2437
+ };
2438
+ };
2439
+ };
2440
+ responses: {
2441
+ /** @description Updated station */
2442
+ 200: {
2443
+ headers: {
2444
+ [name: string]: unknown;
2445
+ };
2446
+ content: {
2447
+ "application/json": {
2448
+ [key: string]: unknown;
2449
+ };
2450
+ };
2451
+ };
2452
+ /** @description Station not found */
2453
+ 404: {
2454
+ headers: {
2455
+ [name: string]: unknown;
2456
+ };
2457
+ content: {
2458
+ "application/json": components["schemas"]["Error"];
2459
+ };
2460
+ };
2461
+ /** @description Validation error */
2462
+ 422: {
2463
+ headers: {
2464
+ [name: string]: unknown;
2465
+ };
2466
+ content: {
2467
+ "application/json": components["schemas"]["Error"];
2468
+ };
2469
+ };
2470
+ /** @description Station registry not wired */
2471
+ 503: {
2472
+ headers: {
2473
+ [name: string]: unknown;
2474
+ };
2475
+ content: {
2476
+ "application/json": components["schemas"]["Error"];
2477
+ };
2478
+ };
2479
+ };
2480
+ };
2481
+ trace?: never;
2482
+ };
2483
+ "/v1/observations/stations/{stationId}/recent": {
2484
+ parameters: {
2485
+ query?: never;
2486
+ header?: never;
2487
+ path?: never;
2488
+ cookie?: never;
2489
+ };
2490
+ /**
2491
+ * Recent observations for a tenant station
2492
+ * @description Returns up to `limit` most-recent observations submitted for the station (tenant-scoped). Useful for the tenant's own dashboard / debugging feed. Requires the `score:read` scope.
2493
+ */
2494
+ get: {
2495
+ parameters: {
2496
+ query?: {
2497
+ limit?: number;
2498
+ };
2499
+ header?: never;
2500
+ path: {
2501
+ stationId: string;
2502
+ };
2503
+ cookie?: never;
2504
+ };
2505
+ requestBody?: never;
2506
+ responses: {
2507
+ /** @description Most-recent observations for this station (descending by observedAt) */
2508
+ 200: {
2509
+ headers: {
2510
+ [name: string]: unknown;
2511
+ };
2512
+ content: {
2513
+ "application/json": {
2514
+ observations: {
2515
+ /** Format: uuid */
2516
+ id: string;
2517
+ /** Format: uuid */
2518
+ stationId: string;
2519
+ /** Format: date-time */
2520
+ observedAt: string;
2521
+ /** @enum {string} */
2522
+ variable: "wind_speed_kn" | "wind_dir_deg" | "wave_height_m" | "temp_c" | "sea_surface_temp_c" | "pressure_hpa" | "precip_mm";
2523
+ value: number;
2524
+ /** @enum {string|null} */
2525
+ qualityFlag?: "verified" | "unflagged" | "flagged_low_quality" | null;
2526
+ /** Format: date-time */
2527
+ ingestedAt: string;
2528
+ }[];
2529
+ };
2530
+ };
2531
+ };
2532
+ /** @description Station not found */
2533
+ 404: {
2534
+ headers: {
2535
+ [name: string]: unknown;
2536
+ };
2537
+ content: {
2538
+ "application/json": components["schemas"]["Error"];
2539
+ };
2540
+ };
2541
+ /** @description Observation store not wired */
2542
+ 503: {
2543
+ headers: {
2544
+ [name: string]: unknown;
2545
+ };
2546
+ content: {
2547
+ "application/json": components["schemas"]["Error"];
2548
+ };
2549
+ };
2550
+ };
2551
+ };
2552
+ put?: never;
2553
+ post?: never;
2554
+ delete?: never;
2555
+ options?: never;
2556
+ head?: never;
2557
+ patch?: never;
2558
+ trace?: never;
2559
+ };
2560
+ "/v1/public/signup": {
2561
+ parameters: {
2562
+ query?: never;
2563
+ header?: never;
2564
+ path?: never;
2565
+ cookie?: never;
2566
+ };
2567
+ get?: never;
2568
+ put?: never;
2569
+ /**
2570
+ * Self-service tenant signup (no auth)
2571
+ * @description Public endpoint, IP-rate-limited (3 attempts / 24h / IP). Creates a tenant + sends a magic-link to the contact email. Always returns 202 on success — never reveals whether an email is already registered. Optional Cloudflare Turnstile token strengthens the gate.
2572
+ */
2573
+ post: {
2574
+ parameters: {
2575
+ query?: never;
2576
+ header?: never;
2577
+ path?: never;
2578
+ cookie?: never;
2579
+ };
2580
+ requestBody: {
2581
+ content: {
2582
+ "application/json": {
2583
+ displayName: string;
2584
+ /** Format: email */
2585
+ contactEmail: string;
2586
+ /**
2587
+ * @description Must be true — records ToS / Privacy / DPA acceptance.
2588
+ * @enum {boolean}
2589
+ */
2590
+ acceptTerms: true;
2591
+ turnstileToken?: string;
2592
+ };
2593
+ };
2594
+ };
2595
+ responses: {
2596
+ /** @description Accepted — magic-link sent if signup valid */
2597
+ 202: {
2598
+ headers: {
2599
+ [name: string]: unknown;
2600
+ };
2601
+ content: {
2602
+ "application/json": {
2603
+ [key: string]: unknown;
2604
+ };
2605
+ };
2606
+ };
2607
+ /** @description Validation error */
2608
+ 422: {
2609
+ headers: {
2610
+ [name: string]: unknown;
2611
+ };
2612
+ content: {
2613
+ "application/json": components["schemas"]["Error"];
2614
+ };
2615
+ };
2616
+ /** @description IP rate limit exceeded */
2617
+ 429: {
2618
+ headers: {
2619
+ [name: string]: unknown;
2620
+ };
2621
+ content: {
2622
+ "application/json": components["schemas"]["Error"];
2623
+ };
2624
+ };
2625
+ };
2626
+ };
2627
+ delete?: never;
2628
+ options?: never;
2629
+ head?: never;
2630
+ patch?: never;
2631
+ trace?: never;
2632
+ };
2633
+ "/v1/public/sustainability-index": {
2634
+ parameters: {
2635
+ query?: never;
2636
+ header?: never;
2637
+ path?: never;
2638
+ cookie?: never;
2639
+ };
2640
+ /**
2641
+ * Public Goable Sustainability Index (no auth, JSON-LD)
2642
+ * @description Public JSON-LD artefact of the Goable Sustainability Index (CC BY 4.0). Session-weighted 0-100 index across zones plus per-zone breakdowns. Governed by k-anonymity (default k≥10) + a 90-day publication lag in the underlying reader — no additional privacy work required for the public surface. Content-Type: application/ld+json. Edge-cached for 5 minutes.
2643
+ */
2644
+ get: {
2645
+ parameters: {
2646
+ query: {
2647
+ /** @description Inclusive start of the reporting period. */
2648
+ from: string;
2649
+ /** @description Exclusive end of the reporting period. */
2650
+ to: string;
2651
+ /** @description Zone grid cell edge length in km (default: reader-configured). */
2652
+ zoneKm?: number;
2653
+ /** @description k-anonymity threshold: zones with fewer than k sessions are suppressed. */
2654
+ k?: number;
2655
+ };
2656
+ header?: never;
2657
+ path?: never;
2658
+ cookie?: never;
2659
+ };
2660
+ requestBody?: never;
2661
+ responses: {
2662
+ /** @description Sustainability Index document (JSON-LD) */
2663
+ 200: {
2664
+ headers: {
2665
+ [name: string]: unknown;
2666
+ };
2667
+ content: {
2668
+ "application/ld+json": {
2669
+ /** @example https://schema.org */
2670
+ "@context": string;
2671
+ /** @enum {string} */
2672
+ "@type": "GoableSustainabilityIndex";
2673
+ /** Format: date-time */
2674
+ generatedAt: string;
2675
+ period: {
2676
+ /** Format: date-time */
2677
+ from: string;
2678
+ /** Format: date-time */
2679
+ to: string;
2680
+ };
2681
+ methodology: {
2682
+ indexFormula: string;
2683
+ weights: {
2684
+ carbonNeutralShare: number;
2685
+ electrificationShare: number;
2686
+ };
2687
+ suppression: string;
2688
+ notes: string;
2689
+ };
2690
+ overall: {
2691
+ index: number;
2692
+ totalSessions: number;
2693
+ carbonNeutralSessions: number;
2694
+ carbonPositiveSessions: number;
2695
+ carbonNeutralShare: number;
2696
+ zonesReleased: number;
2697
+ zonesSuppressed: number;
2698
+ };
2699
+ zones: {
2700
+ zoneKey: string;
2701
+ label: string | null;
2702
+ centroid: components["schemas"]["GeoPoint"] | null;
2703
+ totalSessions: number;
2704
+ carbonNeutralShare: number;
2705
+ electrificationShare: number | null;
2706
+ seasonalConcentration: number | null;
2707
+ zoneIndex: number;
2708
+ }[];
2709
+ /** @enum {string} */
2710
+ license: "CC BY 4.0";
2711
+ attribution: string;
2712
+ } & {
2713
+ [key: string]: unknown;
2714
+ };
2715
+ };
2716
+ };
2717
+ /** @description Validation error */
2718
+ 422: {
2719
+ headers: {
2720
+ [name: string]: unknown;
2721
+ };
2722
+ content: {
2723
+ "application/json": components["schemas"]["Error"];
2724
+ };
2725
+ };
2726
+ /** @description Sustainability index reader not wired */
2727
+ 503: {
2728
+ headers: {
2729
+ [name: string]: unknown;
2730
+ };
2731
+ content: {
2732
+ "application/json": components["schemas"]["Error"];
2733
+ };
2734
+ };
2735
+ };
2736
+ };
2737
+ put?: never;
2738
+ post?: never;
2739
+ delete?: never;
2740
+ options?: never;
2741
+ head?: never;
2742
+ patch?: never;
2743
+ trace?: never;
2744
+ };
2745
+ "/v1/research/verification/export": {
2746
+ parameters: {
2747
+ query?: never;
2748
+ header?: never;
2749
+ path?: never;
2750
+ cookie?: never;
2751
+ };
2752
+ /**
2753
+ * Public Stream F forecast verification export (no auth, JSONL)
2754
+ * @description Streamed newline-delimited JSON (`application/x-ndjson`) — one anonymised (activity × 1km² grid × weekly bucket) skill cell per line, followed by a trailing metadata line. CC BY 4.0. Governance: k≥10 contributor floor + 90-day publication lag + `research_eligible=true` filter enforced in the SQL reader.
2755
+ */
2756
+ get: {
2757
+ parameters: {
2758
+ query?: {
2759
+ from?: string;
2760
+ to?: string;
2761
+ };
2762
+ header?: never;
2763
+ path?: never;
2764
+ cookie?: never;
2765
+ };
2766
+ requestBody?: never;
2767
+ responses: {
2768
+ /** @description Streamed JSONL — one cell per line + trailing meta line */
2769
+ 200: {
2770
+ headers: {
2771
+ [name: string]: unknown;
2772
+ };
2773
+ content: {
2774
+ "application/x-ndjson": string;
2775
+ };
2776
+ };
2777
+ /** @description Validation error */
2778
+ 422: {
2779
+ headers: {
2780
+ [name: string]: unknown;
2781
+ };
2782
+ content: {
2783
+ "application/json": components["schemas"]["Error"];
2784
+ };
2785
+ };
2786
+ /** @description Verification reader not wired */
2787
+ 503: {
2788
+ headers: {
2789
+ [name: string]: unknown;
2790
+ };
2791
+ content: {
2792
+ "application/json": components["schemas"]["Error"];
2793
+ };
2794
+ };
2795
+ };
2796
+ };
2797
+ put?: never;
2798
+ post?: never;
2799
+ delete?: never;
2800
+ options?: never;
2801
+ head?: never;
2802
+ patch?: never;
2803
+ trace?: never;
2804
+ };
2805
+ "/v1/research/difficulty-atlas/export.jsonl": {
2806
+ parameters: {
2807
+ query?: never;
2808
+ header?: never;
2809
+ path?: never;
2810
+ cookie?: never;
2811
+ };
2812
+ /**
2813
+ * Public L15 Difficulty Atlas export (no auth, JSONL)
2814
+ * @description Streamed newline-delimited JSON (`application/x-ndjson`) — one anonymised (activity × sub-spot × dimension) difficulty row per line, followed by a trailing metadata line. CC BY 4.0. First openly-available intrinsic-difficulty measurement for outdoor activities. Governance mirrors Stream F: k≥10 contributors + 90-day publication lag.
2815
+ */
2816
+ get: {
2817
+ parameters: {
2818
+ query?: never;
2819
+ header?: never;
2820
+ path?: never;
2821
+ cookie?: never;
2822
+ };
2823
+ requestBody?: never;
2824
+ responses: {
2825
+ /** @description Streamed JSONL — one atlas row per line + trailing meta line */
2826
+ 200: {
2827
+ headers: {
2828
+ [name: string]: unknown;
2829
+ };
2830
+ content: {
2831
+ "application/x-ndjson": string;
2832
+ };
2833
+ };
2834
+ /** @description Difficulty atlas reader not wired */
2835
+ 503: {
2836
+ headers: {
2837
+ [name: string]: unknown;
2838
+ };
2839
+ content: {
2840
+ "application/json": components["schemas"]["Error"];
2841
+ };
2842
+ };
2843
+ };
2844
+ };
2845
+ put?: never;
2846
+ post?: never;
2847
+ delete?: never;
2848
+ options?: never;
2849
+ head?: never;
2850
+ patch?: never;
2851
+ trace?: never;
2852
+ };
2853
+ "/v1/public/catalog-stats": {
2854
+ parameters: {
2855
+ query?: never;
2856
+ header?: never;
2857
+ path?: never;
2858
+ cookie?: never;
2859
+ };
2860
+ /**
2861
+ * Open catalogue coverage stats (no auth)
2862
+ * @description Live coverage numbers for the open activity catalogue (CC BY 4.0). Returns totals across activities · sub-spots · clusters · regions · countries plus a per-activity breakdown with country codes + status (`seeded` ≥10 sub-spots / `partial` 1-9 / `empty` 0). Edge-cached for 5 minutes — new sub-spots ship via PR + release cycle, not at runtime. Same JSON the /catalog landing renders; expose this surface for marketing / partner sites that want to embed live numbers without scraping HTML.
2863
+ */
2864
+ get: {
2865
+ parameters: {
2866
+ query?: never;
2867
+ header?: never;
2868
+ path?: never;
2869
+ cookie?: never;
2870
+ };
2871
+ requestBody?: never;
2872
+ responses: {
2873
+ /** @description Live coverage stats from the @goable-io/profiles-catalog package */
2874
+ 200: {
2875
+ headers: {
2876
+ [name: string]: unknown;
2877
+ };
2878
+ content: {
1938
2879
  "application/json": {
1939
2880
  /**
1940
2881
  * Format: date-time
@@ -1990,6 +2931,239 @@ export interface paths {
1990
2931
  patch?: never;
1991
2932
  trace?: never;
1992
2933
  };
2934
+ "/v1/outcomes": {
2935
+ parameters: {
2936
+ query?: never;
2937
+ header?: never;
2938
+ path?: never;
2939
+ cookie?: never;
2940
+ };
2941
+ get?: never;
2942
+ put?: never;
2943
+ /**
2944
+ * Report a standalone activity outcome
2945
+ * @description Submit an observed outcome for an activity session not tied to a specific /v1/score call — the operator-reported behavioural signal behind the calibration + research datasets. For an outcome linked to a specific scored session, use POST /v1/score/{sessionId}/outcome instead. Requires the `outcomes:write` scope (live keys carry it by default; test keys don't).
2946
+ */
2947
+ post: {
2948
+ parameters: {
2949
+ query?: never;
2950
+ header?: never;
2951
+ path?: never;
2952
+ cookie?: never;
2953
+ };
2954
+ requestBody: {
2955
+ content: {
2956
+ "application/json": {
2957
+ /** Format: date-time */
2958
+ occurred_at: string;
2959
+ activity_slug: string;
2960
+ /** @enum {string} */
2961
+ outcome_type: "ran" | "cancelled" | "rescheduled" | "no_show" | "note";
2962
+ /** @description Optional catalogue sub-spot slug. */
2963
+ spot_id?: string;
2964
+ /** @description Optional link back to a scoring_audit_log row. */
2965
+ audit_log_id?: string;
2966
+ detail?: {
2967
+ [key: string]: unknown;
2968
+ };
2969
+ /**
2970
+ * @description Stream G equipment-transition signal.
2971
+ * @enum {string}
2972
+ */
2973
+ equipment_type?: "electric" | "combustion" | "manual";
2974
+ };
2975
+ };
2976
+ };
2977
+ responses: {
2978
+ /** @description Accepted (queued for the next calibration batch) */
2979
+ 202: {
2980
+ headers: {
2981
+ [name: string]: unknown;
2982
+ };
2983
+ content: {
2984
+ "application/json": {
2985
+ [key: string]: unknown;
2986
+ };
2987
+ };
2988
+ };
2989
+ /** @description Missing scope: outcomes:write */
2990
+ 403: {
2991
+ headers: {
2992
+ [name: string]: unknown;
2993
+ };
2994
+ content: {
2995
+ "application/json": components["schemas"]["Error"];
2996
+ };
2997
+ };
2998
+ /** @description Validation error */
2999
+ 422: {
3000
+ headers: {
3001
+ [name: string]: unknown;
3002
+ };
3003
+ content: {
3004
+ "application/json": components["schemas"]["Error"];
3005
+ };
3006
+ };
3007
+ };
3008
+ };
3009
+ delete?: never;
3010
+ options?: never;
3011
+ head?: never;
3012
+ patch?: never;
3013
+ trace?: never;
3014
+ };
3015
+ "/v1/audit/export": {
3016
+ parameters: {
3017
+ query?: never;
3018
+ header?: never;
3019
+ path?: never;
3020
+ cookie?: never;
3021
+ };
3022
+ /**
3023
+ * Export the caller's own score + outcome audit history
3024
+ * @description Tenant-scoped export of your own scoring_audit_log activity (one row per /v1/score call, with any linked outcome inline), date-range filtered. The tenant is always the calling API key's tenant — there is no way to request another tenant's data. Outcomes reported via the free-standing POST /v1/outcomes (i.e. not linked to a specific score via audit_log_id) are not included. Offset-paginated; `X-Total-Count` on every response carries the total row count for the window.
3025
+ */
3026
+ get: {
3027
+ parameters: {
3028
+ query: {
3029
+ from: string;
3030
+ to: string;
3031
+ format?: "csv" | "json";
3032
+ limit?: number;
3033
+ offset?: number;
3034
+ };
3035
+ header?: never;
3036
+ path?: never;
3037
+ cookie?: never;
3038
+ };
3039
+ requestBody?: never;
3040
+ responses: {
3041
+ /** @description CSV (format=csv) or JSON (format=json, default) export of the window. */
3042
+ 200: {
3043
+ headers: {
3044
+ [name: string]: unknown;
3045
+ };
3046
+ content: {
3047
+ "application/json": {
3048
+ rows: {
3049
+ [key: string]: unknown;
3050
+ }[];
3051
+ meta: {
3052
+ total: number;
3053
+ limit: number;
3054
+ offset: number;
3055
+ window: {
3056
+ from?: string;
3057
+ to?: string;
3058
+ };
3059
+ };
3060
+ };
3061
+ "text/csv": string;
3062
+ };
3063
+ };
3064
+ /** @description Validation error, `to` before `from`, or WINDOW_TOO_LARGE (range exceeds 366 days) */
3065
+ 422: {
3066
+ headers: {
3067
+ [name: string]: unknown;
3068
+ };
3069
+ content: {
3070
+ "application/json": components["schemas"]["Error"];
3071
+ };
3072
+ };
3073
+ /** @description Audit export is not configured */
3074
+ 503: {
3075
+ headers: {
3076
+ [name: string]: unknown;
3077
+ };
3078
+ content: {
3079
+ "application/json": components["schemas"]["Error"];
3080
+ };
3081
+ };
3082
+ };
3083
+ };
3084
+ put?: never;
3085
+ post?: never;
3086
+ delete?: never;
3087
+ options?: never;
3088
+ head?: never;
3089
+ patch?: never;
3090
+ trace?: never;
3091
+ };
3092
+ "/v1/legal/{kind}/current": {
3093
+ parameters: {
3094
+ query?: never;
3095
+ header?: never;
3096
+ path?: never;
3097
+ cookie?: never;
3098
+ };
3099
+ /**
3100
+ * Fetch the current published legal document (no auth)
3101
+ * @description Public read of the currently-published version of a legal document (Terms of Service, Privacy Policy, DPA, SLA, Acceptable Use, Cookie Policy). Returns the full body + content hash + published timestamp. Used by the signup flow + marketing site to render always-current legal copy without hardcoding it.
3102
+ */
3103
+ get: {
3104
+ parameters: {
3105
+ query?: never;
3106
+ header?: never;
3107
+ path: {
3108
+ /** @description Legal document kind. */
3109
+ kind: "terms_of_service" | "privacy_policy" | "data_processing_agreement" | "sla" | "acceptable_use" | "cookie_policy";
3110
+ };
3111
+ cookie?: never;
3112
+ };
3113
+ requestBody?: never;
3114
+ responses: {
3115
+ /** @description The current published document of the requested kind */
3116
+ 200: {
3117
+ headers: {
3118
+ [name: string]: unknown;
3119
+ };
3120
+ content: {
3121
+ "application/json": {
3122
+ document: {
3123
+ /** Format: uuid */
3124
+ id: string;
3125
+ kind: string;
3126
+ version: string;
3127
+ title: string;
3128
+ body: string;
3129
+ contentHash: string;
3130
+ status: string;
3131
+ /** Format: date-time */
3132
+ createdAt: string;
3133
+ /** Format: date-time */
3134
+ publishedAt: string | null;
3135
+ };
3136
+ };
3137
+ };
3138
+ };
3139
+ /** @description Unknown document kind, or no published document */
3140
+ 404: {
3141
+ headers: {
3142
+ [name: string]: unknown;
3143
+ };
3144
+ content: {
3145
+ "application/json": components["schemas"]["Error"];
3146
+ };
3147
+ };
3148
+ /** @description Legal document store not wired */
3149
+ 503: {
3150
+ headers: {
3151
+ [name: string]: unknown;
3152
+ };
3153
+ content: {
3154
+ "application/json": components["schemas"]["Error"];
3155
+ };
3156
+ };
3157
+ };
3158
+ };
3159
+ put?: never;
3160
+ post?: never;
3161
+ delete?: never;
3162
+ options?: never;
3163
+ head?: never;
3164
+ patch?: never;
3165
+ trace?: never;
3166
+ };
1993
3167
  }
1994
3168
  export type webhooks = Record<string, never>;
1995
3169
  export interface components {
@@ -2024,10 +3198,12 @@ export interface components {
2024
3198
  verdict: components["schemas"]["Verdict"];
2025
3199
  confidence: number;
2026
3200
  breakdown: ({
2027
- dimension?: string;
2028
- score?: number;
3201
+ name?: string;
3202
+ value?: number;
3203
+ suitability?: number;
2029
3204
  weight?: number;
2030
3205
  contribution?: number;
3206
+ hasData?: boolean;
2031
3207
  } & {
2032
3208
  [key: string]: unknown;
2033
3209
  })[];
@@ -2035,9 +3211,10 @@ export interface components {
2035
3211
  [key: string]: unknown;
2036
3212
  };
2037
3213
  alerts: ({
3214
+ /** @enum {string} */
3215
+ level?: "info" | "warning" | "critical";
2038
3216
  code?: string;
2039
- severity?: string;
2040
- message?: string;
3217
+ description?: string;
2041
3218
  } & {
2042
3219
  [key: string]: unknown;
2043
3220
  })[];
@@ -2048,12 +3225,8 @@ export interface components {
2048
3225
  distribution?: {
2049
3226
  [key: string]: unknown;
2050
3227
  };
2051
- confidenceDetail?: {
2052
- [key: string]: unknown;
2053
- };
2054
- calibration_provenance?: {
2055
- [key: string]: unknown;
2056
- };
3228
+ confidenceDetail?: components["schemas"]["ConfidenceDetailForecast"];
3229
+ calibration_provenance?: components["schemas"]["CalibrationProvenance"] | null;
2057
3230
  assimilation?: {
2058
3231
  [key: string]: unknown;
2059
3232
  };
@@ -2083,6 +3256,297 @@ export interface components {
2083
3256
  } & {
2084
3257
  [key: string]: unknown;
2085
3258
  };
3259
+ CalibrationProvenance: {
3260
+ /** @enum {string} */
3261
+ level?: "sub-spot" | "cluster" | "region" | "base";
3262
+ slug?: string;
3263
+ scoring_profile_slug?: string;
3264
+ sub_spot_slug?: string;
3265
+ distance_to_sub_spot_m?: number;
3266
+ /** @enum {integer} */
3267
+ tier?: 1 | 2 | 3;
3268
+ /** @enum {string} */
3269
+ tier_source?: "catalog" | "classifier";
3270
+ };
3271
+ CalibrationProvenanceSummary: {
3272
+ /** @enum {string} */
3273
+ level: "base" | "region" | "cluster" | "sub-spot";
3274
+ n_local: number | null;
3275
+ shrinkage_weight_from_parent: number | null;
3276
+ };
3277
+ DriftFlag: {
3278
+ /** @enum {string} */
3279
+ severity: "watch" | "warning" | "critical";
3280
+ /** Format: date-time */
3281
+ since_timestamp: string;
3282
+ };
3283
+ ConfidenceDetail: components["schemas"]["ConfidenceDetailForecast"] | components["schemas"]["ConfidenceDetailHistorical"] | components["schemas"]["ConfidenceDetailClimate"];
3284
+ ConfidenceDetailForecast: {
3285
+ /**
3286
+ * @description discriminator enum property added by openapi-typescript
3287
+ * @enum {string}
3288
+ */
3289
+ mode: "forecast";
3290
+ forecast_skill: number;
3291
+ provider_agreement: number;
3292
+ profile_maturity: number;
3293
+ hierarchical_calibration: number;
3294
+ calibration_provenance?: components["schemas"]["CalibrationProvenanceSummary"];
3295
+ skill_calibration?: {
3296
+ applied: boolean;
3297
+ n_train: number;
3298
+ cohort_hash: string;
3299
+ scalar: number;
3300
+ };
3301
+ drift_flag?: components["schemas"]["DriftFlag"];
3302
+ } & {
3303
+ [key: string]: unknown;
3304
+ };
3305
+ ConfidenceDetailHistorical: {
3306
+ /**
3307
+ * @description discriminator enum property added by openapi-typescript
3308
+ * @enum {string}
3309
+ */
3310
+ mode: "historical";
3311
+ sample_size_confidence: number;
3312
+ base_climatology_quality: number;
3313
+ calibration_confidence: number;
3314
+ hierarchical_calibration: number;
3315
+ calibration_provenance?: components["schemas"]["CalibrationProvenanceSummary"];
3316
+ drift_flag?: components["schemas"]["DriftFlag"];
3317
+ } & {
3318
+ [key: string]: unknown;
3319
+ };
3320
+ ConfidenceDetailClimate: {
3321
+ /**
3322
+ * @description discriminator enum property added by openapi-typescript
3323
+ * @enum {string}
3324
+ */
3325
+ mode: "climate";
3326
+ model_spread: number;
3327
+ bias_correction_residual: number;
3328
+ scenario_uncertainty: number;
3329
+ horizon_uncertainty: number;
3330
+ hierarchical_calibration: number;
3331
+ calibration_provenance?: components["schemas"]["CalibrationProvenanceSummary"];
3332
+ drift_flag?: components["schemas"]["DriftFlag"];
3333
+ } & {
3334
+ [key: string]: unknown;
3335
+ };
3336
+ SerialisedPolicy: {
3337
+ /** Format: uuid */
3338
+ id: string;
3339
+ /** Format: uuid */
3340
+ tenantId: string;
3341
+ /** Format: uuid */
3342
+ quoteId: string;
3343
+ /** @enum {string} */
3344
+ status: "bound" | "triggered" | "settled" | "expired";
3345
+ /** @description Null for mixed-currency portfolios; the breakdown lives on the underlying quote. */
3346
+ payoutAmount?: number | null;
3347
+ payoutCurrency?: ("EUR" | "USD" | "GBP" | "CHF") | null;
3348
+ premiumPaid?: number | null;
3349
+ /** @enum {string} */
3350
+ premiumCollection: "external" | "stripe" | "invoice_due";
3351
+ coverageWindow: {
3352
+ monthFrom: number;
3353
+ dayFrom: number;
3354
+ monthTo: number;
3355
+ dayTo: number;
3356
+ };
3357
+ coverageYear: number;
3358
+ /** @description Frozen at bind time — same shape as the quote's normalised trigger. */
3359
+ trigger: {
3360
+ [key: string]: unknown;
3361
+ };
3362
+ /** @description Frozen snapshot of the per-spot profile + payout terms — decouples the policy from later catalog changes. */
3363
+ policyTerms: {
3364
+ [key: string]: unknown;
3365
+ };
3366
+ /** Format: date-time */
3367
+ boundAt: string;
3368
+ /** Format: date-time */
3369
+ triggeredAt: string | null;
3370
+ /** Format: date-time */
3371
+ settledAt: string | null;
3372
+ /** Format: date-time */
3373
+ expiredAt: string | null;
3374
+ settlementReference: string | null;
3375
+ };
3376
+ SerialisedPayoutEvent: {
3377
+ /** Format: uuid */
3378
+ id: string;
3379
+ /** Format: uuid */
3380
+ policyId: string;
3381
+ spotIndex: number;
3382
+ /** Format: date-time */
3383
+ eventStartAt: string;
3384
+ /** Format: date-time */
3385
+ eventEndAt: string;
3386
+ hoursFired: number;
3387
+ payoutAmount: number;
3388
+ /** @enum {string} */
3389
+ payoutCurrency: "EUR" | "USD" | "GBP" | "CHF";
3390
+ /** Format: date-time */
3391
+ detectedAt: string;
3392
+ /** @enum {string} */
3393
+ evaluationSource: "manual" | "scheduled";
3394
+ };
3395
+ UnderwritingQuoteResponse: {
3396
+ policy: {
3397
+ coverageWindow: {
3398
+ monthFrom: number;
3399
+ dayFrom: number;
3400
+ monthTo: number;
3401
+ dayTo: number;
3402
+ };
3403
+ /** @description Normalised trigger echoed back with defaulted consecutiveHours/cooldownHours. */
3404
+ trigger: {
3405
+ [key: string]: unknown;
3406
+ };
3407
+ historicalYearsRange: {
3408
+ from: number;
3409
+ to: number;
3410
+ };
3411
+ spot?: {
3412
+ resolvedProfileSlug: string;
3413
+ point: components["schemas"]["GeoPoint"];
3414
+ spotId?: string;
3415
+ payout: {
3416
+ amount: number;
3417
+ /** @enum {string} */
3418
+ currency: "EUR" | "USD" | "GBP" | "CHF";
3419
+ };
3420
+ /**
3421
+ * @description Sub-spot tier (1/2/3) applied to the tier risk multiplier; null when no sub-spot resolved.
3422
+ * @enum {integer|null}
3423
+ */
3424
+ tier?: 1 | 2 | 3 | null;
3425
+ /**
3426
+ * @description Provenance of the tier: 'catalog' from profile YAML, 'classifier' from L11 confidence=1.0 override, null when no tier applied.
3427
+ * @enum {string|null}
3428
+ */
3429
+ tierSource?: "catalog" | "classifier" | null;
3430
+ };
3431
+ portfolio?: {
3432
+ resolvedProfileSlug: string;
3433
+ point: components["schemas"]["GeoPoint"];
3434
+ spotId?: string;
3435
+ payout: {
3436
+ amount: number;
3437
+ /** @enum {string} */
3438
+ currency: "EUR" | "USD" | "GBP" | "CHF";
3439
+ };
3440
+ /**
3441
+ * @description Sub-spot tier (1/2/3) applied to the tier risk multiplier; null when no sub-spot resolved.
3442
+ * @enum {integer|null}
3443
+ */
3444
+ tier?: 1 | 2 | 3 | null;
3445
+ /**
3446
+ * @description Provenance of the tier: 'catalog' from profile YAML, 'classifier' from L11 confidence=1.0 override, null when no tier applied.
3447
+ * @enum {string|null}
3448
+ */
3449
+ tierSource?: "catalog" | "classifier" | null;
3450
+ }[];
3451
+ } & {
3452
+ [key: string]: unknown;
3453
+ };
3454
+ expectedPayouts: {
3455
+ byCurrency: {
3456
+ EUR?: {
3457
+ mean: number;
3458
+ p10: number;
3459
+ p50: number;
3460
+ p90: number;
3461
+ perYear: number[];
3462
+ varianceStability: number;
3463
+ };
3464
+ USD?: {
3465
+ mean: number;
3466
+ p10: number;
3467
+ p50: number;
3468
+ p90: number;
3469
+ perYear: number[];
3470
+ varianceStability: number;
3471
+ };
3472
+ GBP?: {
3473
+ mean: number;
3474
+ p10: number;
3475
+ p50: number;
3476
+ p90: number;
3477
+ perYear: number[];
3478
+ varianceStability: number;
3479
+ };
3480
+ CHF?: {
3481
+ mean: number;
3482
+ p10: number;
3483
+ p50: number;
3484
+ p90: number;
3485
+ perYear: number[];
3486
+ varianceStability: number;
3487
+ };
3488
+ };
3489
+ };
3490
+ expectedPremium: {
3491
+ byCurrency: {
3492
+ EUR?: {
3493
+ fair: number;
3494
+ loaded: number;
3495
+ /** @description Weighted-average tier risk multiplier applied across spots in this currency (1.0 when no sub-spot). */
3496
+ tierMultiplierWeightedAvg: number;
3497
+ };
3498
+ USD?: {
3499
+ fair: number;
3500
+ loaded: number;
3501
+ /** @description Weighted-average tier risk multiplier applied across spots in this currency (1.0 when no sub-spot). */
3502
+ tierMultiplierWeightedAvg: number;
3503
+ };
3504
+ GBP?: {
3505
+ fair: number;
3506
+ loaded: number;
3507
+ /** @description Weighted-average tier risk multiplier applied across spots in this currency (1.0 when no sub-spot). */
3508
+ tierMultiplierWeightedAvg: number;
3509
+ };
3510
+ CHF?: {
3511
+ fair: number;
3512
+ loaded: number;
3513
+ /** @description Weighted-average tier risk multiplier applied across spots in this currency (1.0 when no sub-spot). */
3514
+ tierMultiplierWeightedAvg: number;
3515
+ };
3516
+ };
3517
+ loadingFactor: number;
3518
+ };
3519
+ modelConfidence: number;
3520
+ /** @enum {string} */
3521
+ advisoryLevel: "high_confidence" | "moderate_confidence" | "low_confidence";
3522
+ issuable: boolean;
3523
+ notes: string[];
3524
+ /** @description HistoricalScoreDistribution — object for single spot, array for portfolio. */
3525
+ underlying: {
3526
+ [key: string]: unknown;
3527
+ } | {
3528
+ [key: string]: unknown;
3529
+ }[];
3530
+ /**
3531
+ * Format: uuid
3532
+ * @description Present when the policy store is wired — pass to /v1/underwriting/policy/bind to bind this quote.
3533
+ */
3534
+ quoteId?: string;
3535
+ /**
3536
+ * Format: date-time
3537
+ * @description ISO timestamp after which the quote can no longer be bound.
3538
+ */
3539
+ expiresAt?: string;
3540
+ /**
3541
+ * Format: uuid
3542
+ * @description Present on GET after the quote has been bound — the resulting policy id.
3543
+ */
3544
+ boundPolicyId?: string;
3545
+ } & {
3546
+ [key: string]: unknown;
3547
+ };
3548
+ /** @enum {string} */
3549
+ WebhookEvent: "outcome.created" | "drift.fired" | "drift.resolved" | "calibration.completed" | "billing.subscription_updated" | "recommendation.completed" | "underwriting.policy.bound" | "underwriting.policy.triggered" | "underwriting.policy.settled";
2086
3550
  };
2087
3551
  responses: never;
2088
3552
  parameters: never;