@goable-io/sdk 0.2.0 → 0.4.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.
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * GENERATED FILE — do not edit by hand.
3
- * Source: apps/api/src/openapi/spec.ts (buildOpenApiSpec).
4
- * Regenerate: pnpm --filter @goable-io/sdk gen
3
+ * Source: openapi.json (the committed Goable API contract).
4
+ * Regenerate: pnpm gen
5
5
  */
6
6
 
7
7
  export interface paths {
@@ -119,6 +119,11 @@ export interface paths {
119
119
  /** @description L2a probabilistic ensemble (Pro+) */
120
120
  ensemble?: boolean;
121
121
  members?: number;
122
+ /**
123
+ * @description Skill-conditioned scoring (Pro+).
124
+ * @enum {string}
125
+ */
126
+ rider_skill_level?: "beginner" | "intermediate" | "expert";
122
127
  };
123
128
  };
124
129
  };
@@ -132,7 +137,7 @@ export interface paths {
132
137
  "application/json": components["schemas"]["ScoreResponse"];
133
138
  };
134
139
  };
135
- /** @description Plan upgrade required (ensemble on Free/Starter) */
140
+ /** @description Plan upgrade required (ensemble or rider_skill_level on Free/Starter) */
136
141
  402: {
137
142
  headers: {
138
143
  [name: string]: unknown;
@@ -297,7 +302,10 @@ export interface paths {
297
302
  };
298
303
  get?: never;
299
304
  put?: never;
300
- /** Historical climatology scoring (Pro+) */
305
+ /**
306
+ * Historical climatology scoring (Pro+)
307
+ * @description Percentiles + exceedance + verdict frequency over ERA5 reanalysis. Each entry carries a historical-mode ConfidenceDetail block (see components.schemas.ConfidenceDetailHistorical).
308
+ */
301
309
  post: {
302
310
  parameters: {
303
311
  query?: never;
@@ -328,7 +336,7 @@ export interface paths {
328
336
  };
329
337
  };
330
338
  responses: {
331
- /** @description Percentiles + exceedance + verdict frequency */
339
+ /** @description Percentiles + exceedance + verdict frequency (per-entry confidenceDetail: historical) */
332
340
  200: {
333
341
  headers: {
334
342
  [name: string]: unknown;
@@ -800,7 +808,10 @@ export interface paths {
800
808
  };
801
809
  get?: never;
802
810
  put?: never;
803
- /** Climate-decadal activity viability projection (Scale) */
811
+ /**
812
+ * Climate-decadal activity viability projection (Scale)
813
+ * @description Per-decade projection distributions. Each entry carries a climate-mode ConfidenceDetail block (see components.schemas.ConfidenceDetailClimate).
814
+ */
804
815
  post: {
805
816
  parameters: {
806
817
  query?: never;
@@ -824,7 +835,7 @@ export interface paths {
824
835
  };
825
836
  };
826
837
  responses: {
827
- /** @description Per-decade projection distributions */
838
+ /** @description Per-decade projection distributions (per-entry confidenceDetail: climate) */
828
839
  200: {
829
840
  headers: {
830
841
  [name: string]: unknown;
@@ -870,7 +881,10 @@ export interface paths {
870
881
  };
871
882
  get?: never;
872
883
  put?: never;
873
- /** Parametric underwriting quote (Scale) */
884
+ /**
885
+ * Parametric underwriting quote (Scale)
886
+ * @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.
887
+ */
874
888
  post: {
875
889
  parameters: {
876
890
  query?: never;
@@ -882,43 +896,64 @@ export interface paths {
882
896
  content: {
883
897
  "application/json": {
884
898
  spot?: {
885
- location: components["schemas"]["GeoPoint"];
899
+ point: components["schemas"]["GeoPoint"];
886
900
  activity: string;
887
901
  spotId?: string;
902
+ payout: {
903
+ amount: number;
904
+ /** @enum {string} */
905
+ currency: "EUR" | "USD" | "GBP" | "CHF";
906
+ };
888
907
  };
889
908
  portfolio?: {
890
- location: components["schemas"]["GeoPoint"];
909
+ point: components["schemas"]["GeoPoint"];
891
910
  activity: string;
892
911
  spotId?: string;
912
+ payout: {
913
+ amount: number;
914
+ /** @enum {string} */
915
+ currency: "EUR" | "USD" | "GBP" | "CHF";
916
+ };
893
917
  }[];
894
918
  coverageWindow: {
895
919
  monthFrom: number;
896
920
  dayFrom: number;
897
921
  monthTo: number;
898
922
  dayTo: number;
899
- } & {
900
- [key: string]: unknown;
901
923
  };
902
- payout?: {
903
- amount: number;
924
+ trigger: {
904
925
  /** @enum {string} */
905
- currency: "EUR" | "USD" | "GBP" | "CHF";
926
+ kind: "scoreBelow";
927
+ scoreBelow: number;
928
+ consecutiveHours?: number;
929
+ cooldownHours?: number;
930
+ maxPayoutsPerYear?: number;
931
+ } | {
932
+ /** @enum {string} */
933
+ kind: "verdictAtOrBelow";
934
+ verdict: components["schemas"]["Verdict"];
935
+ consecutiveHours?: number;
936
+ cooldownHours?: number;
937
+ maxPayoutsPerYear?: number;
906
938
  };
907
- } & {
908
- [key: string]: unknown;
939
+ historicalYearsRange?: {
940
+ from: number;
941
+ to: number;
942
+ };
943
+ loadingFactor?: number;
944
+ calibrationConfidenceMin?: number;
945
+ forceIssue?: boolean;
909
946
  };
910
947
  };
911
948
  };
912
949
  responses: {
913
- /** @description Premium + bindable policy terms */
950
+ /** @description Premium + policy echo (with per-spot tier + tierSource) + bindable quote id when policy store is wired */
914
951
  200: {
915
952
  headers: {
916
953
  [name: string]: unknown;
917
954
  };
918
955
  content: {
919
- "application/json": {
920
- [key: string]: unknown;
921
- };
956
+ "application/json": components["schemas"]["UnderwritingQuoteResponse"];
922
957
  };
923
958
  };
924
959
  /** @description Requires Scale plan */
@@ -930,7 +965,16 @@ export interface paths {
930
965
  "application/json": components["schemas"]["Error"];
931
966
  };
932
967
  };
933
- /** @description Validation error */
968
+ /** @description No profile for activity */
969
+ 404: {
970
+ headers: {
971
+ [name: string]: unknown;
972
+ };
973
+ content: {
974
+ "application/json": components["schemas"]["Error"];
975
+ };
976
+ };
977
+ /** @description Validation error / low confidence / invalid trigger */
934
978
  422: {
935
979
  headers: {
936
980
  [name: string]: unknown;
@@ -939,6 +983,15 @@ export interface paths {
939
983
  "application/json": components["schemas"]["Error"];
940
984
  };
941
985
  };
986
+ /** @description HistoricalProvider not configured */
987
+ 503: {
988
+ headers: {
989
+ [name: string]: unknown;
990
+ };
991
+ content: {
992
+ "application/json": components["schemas"]["Error"];
993
+ };
994
+ };
942
995
  };
943
996
  };
944
997
  delete?: never;
@@ -947,48 +1000,35 @@ export interface paths {
947
1000
  patch?: never;
948
1001
  trace?: never;
949
1002
  };
950
- "/v1/underwriting/bind": {
1003
+ "/v1/underwriting/quote/{id}": {
951
1004
  parameters: {
952
1005
  query?: never;
953
1006
  header?: never;
954
1007
  path?: never;
955
1008
  cookie?: never;
956
1009
  };
957
- get?: never;
958
- put?: never;
959
1010
  /**
960
- * Bind a parametric policy (Scale)
961
- * @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.
1011
+ * Fetch a bindable quote by id (Scale)
1012
+ * @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).
962
1013
  */
963
- post: {
1014
+ get: {
964
1015
  parameters: {
965
1016
  query?: never;
966
1017
  header?: never;
967
- path?: never;
968
- cookie?: never;
969
- };
970
- requestBody: {
971
- content: {
972
- "application/json": {
973
- quoteId: string;
974
- premiumConfirmation?: {
975
- [key: string]: unknown;
976
- };
977
- } & {
978
- [key: string]: unknown;
979
- };
1018
+ path: {
1019
+ id: string;
980
1020
  };
1021
+ cookie?: never;
981
1022
  };
1023
+ requestBody?: never;
982
1024
  responses: {
983
- /** @description Bound policy id + cohort hash + settlement schedule */
1025
+ /** @description Quote record (may include boundPolicyId when already bound) */
984
1026
  200: {
985
1027
  headers: {
986
1028
  [name: string]: unknown;
987
1029
  };
988
1030
  content: {
989
- "application/json": {
990
- [key: string]: unknown;
991
- };
1031
+ "application/json": components["schemas"]["UnderwritingQuoteResponse"];
992
1032
  };
993
1033
  };
994
1034
  /** @description Requires Scale plan */
@@ -1000,7 +1040,7 @@ export interface paths {
1000
1040
  "application/json": components["schemas"]["Error"];
1001
1041
  };
1002
1042
  };
1003
- /** @description Quote expired or not found */
1043
+ /** @description Quote not found for this tenant */
1004
1044
  404: {
1005
1045
  headers: {
1006
1046
  [name: string]: unknown;
@@ -1009,8 +1049,8 @@ export interface paths {
1009
1049
  "application/json": components["schemas"]["Error"];
1010
1050
  };
1011
1051
  };
1012
- /** @description Validation error */
1013
- 422: {
1052
+ /** @description Quote persistence not configured */
1053
+ 503: {
1014
1054
  headers: {
1015
1055
  [name: string]: unknown;
1016
1056
  };
@@ -1020,13 +1060,15 @@ export interface paths {
1020
1060
  };
1021
1061
  };
1022
1062
  };
1063
+ put?: never;
1064
+ post?: never;
1023
1065
  delete?: never;
1024
1066
  options?: never;
1025
1067
  head?: never;
1026
1068
  patch?: never;
1027
1069
  trace?: never;
1028
1070
  };
1029
- "/v1/underwriting/evaluate": {
1071
+ "/v1/underwriting/policy/bind": {
1030
1072
  parameters: {
1031
1073
  query?: never;
1032
1074
  header?: never;
@@ -1036,8 +1078,8 @@ export interface paths {
1036
1078
  get?: never;
1037
1079
  put?: never;
1038
1080
  /**
1039
- * Read-only payout projection for a bound policy (Scale)
1040
- * @description Returns the current accrued shortfall + projected payout for a bound policy. Does NOT settle settlement runs automatically on a cron at policy expiry.
1081
+ * Bind a parametric policy (Scale)
1082
+ * @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.
1041
1083
  */
1042
1084
  post: {
1043
1085
  parameters: {
@@ -1049,19 +1091,36 @@ export interface paths {
1049
1091
  requestBody: {
1050
1092
  content: {
1051
1093
  "application/json": {
1052
- policyId: string;
1094
+ /** Format: uuid */
1095
+ quoteId: string;
1096
+ coverageYear: number;
1097
+ /** @enum {string} */
1098
+ premiumCollection: "external" | "stripe" | "invoice_due";
1099
+ premiumPaid?: number;
1053
1100
  };
1054
1101
  };
1055
1102
  };
1056
1103
  responses: {
1057
- /** @description Accrued shortfall + projected payout per currency */
1058
- 200: {
1104
+ /** @description Policy bound */
1105
+ 201: {
1059
1106
  headers: {
1060
1107
  [name: string]: unknown;
1061
1108
  };
1062
1109
  content: {
1063
1110
  "application/json": {
1064
- [key: string]: unknown;
1111
+ policy: components["schemas"]["SerialisedPolicy"];
1112
+ /** Format: uuid */
1113
+ quoteId: string;
1114
+ /** @description Soft warnings for cells with an open watch-level L9 drift event. Present only when non-empty. */
1115
+ driftAdvisories?: {
1116
+ spotIndex: number;
1117
+ activity: string;
1118
+ subSpotSlug: string;
1119
+ /** @enum {string} */
1120
+ severity: "watch";
1121
+ /** Format: date-time */
1122
+ since: string;
1123
+ }[];
1065
1124
  };
1066
1125
  };
1067
1126
  };
@@ -1074,7 +1133,7 @@ export interface paths {
1074
1133
  "application/json": components["schemas"]["Error"];
1075
1134
  };
1076
1135
  };
1077
- /** @description Policy not found */
1136
+ /** @description Quote not found for this tenant */
1078
1137
  404: {
1079
1138
  headers: {
1080
1139
  [name: string]: unknown;
@@ -1083,7 +1142,16 @@ export interface paths {
1083
1142
  "application/json": components["schemas"]["Error"];
1084
1143
  };
1085
1144
  };
1086
- /** @description Validation error */
1145
+ /** @description Quote already bound to another policy */
1146
+ 409: {
1147
+ headers: {
1148
+ [name: string]: unknown;
1149
+ };
1150
+ content: {
1151
+ "application/json": components["schemas"]["Error"];
1152
+ };
1153
+ };
1154
+ /** @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) */
1087
1155
  422: {
1088
1156
  headers: {
1089
1157
  [name: string]: unknown;
@@ -1092,6 +1160,15 @@ export interface paths {
1092
1160
  "application/json": components["schemas"]["Error"];
1093
1161
  };
1094
1162
  };
1163
+ /** @description Policy persistence not configured */
1164
+ 503: {
1165
+ headers: {
1166
+ [name: string]: unknown;
1167
+ };
1168
+ content: {
1169
+ "application/json": components["schemas"]["Error"];
1170
+ };
1171
+ };
1095
1172
  };
1096
1173
  };
1097
1174
  delete?: never;
@@ -1100,54 +1177,44 @@ export interface paths {
1100
1177
  patch?: never;
1101
1178
  trace?: never;
1102
1179
  };
1103
- "/v1/score/{sessionId}/outcome": {
1180
+ "/v1/underwriting/policy": {
1104
1181
  parameters: {
1105
1182
  query?: never;
1106
1183
  header?: never;
1107
1184
  path?: never;
1108
1185
  cookie?: never;
1109
1186
  };
1110
- get?: never;
1111
- put?: never;
1112
1187
  /**
1113
- * Report observed outcome for a scored session
1114
- * @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).
1188
+ * List bound policies for the caller's tenant (Scale)
1189
+ * @description Paginated list of every bound policy owned by the calling tenant. Ordered by boundAt DESC.
1115
1190
  */
1116
- post: {
1191
+ get: {
1117
1192
  parameters: {
1118
- query?: never;
1119
- header?: never;
1120
- path: {
1121
- /** @description Session UUID returned in the /v1/score response metadata. */
1122
- sessionId: string;
1193
+ query?: {
1194
+ status?: "bound" | "triggered" | "settled" | "expired";
1195
+ coverageYear?: number;
1196
+ limit?: number;
1197
+ cursor?: string;
1123
1198
  };
1199
+ header?: never;
1200
+ path?: never;
1124
1201
  cookie?: never;
1125
1202
  };
1126
- requestBody: {
1127
- content: {
1128
- "application/json": {
1129
- /** @enum {string} */
1130
- outcome_type: "ran" | "cancelled" | "rescheduled" | "no_show" | "note";
1131
- detail?: {
1132
- [key: string]: unknown;
1133
- };
1134
- };
1135
- };
1136
- };
1203
+ requestBody?: never;
1137
1204
  responses: {
1138
- /** @description Accepted (queued for the next calibration batch) */
1139
- 202: {
1205
+ /** @description Bound policies for this tenant */
1206
+ 200: {
1140
1207
  headers: {
1141
1208
  [name: string]: unknown;
1142
1209
  };
1143
1210
  content: {
1144
1211
  "application/json": {
1145
- [key: string]: unknown;
1212
+ policies: components["schemas"]["SerialisedPolicy"][];
1146
1213
  };
1147
1214
  };
1148
1215
  };
1149
- /** @description Missing scope: outcomes:write */
1150
- 403: {
1216
+ /** @description Requires Scale plan */
1217
+ 402: {
1151
1218
  headers: {
1152
1219
  [name: string]: unknown;
1153
1220
  };
@@ -1155,8 +1222,8 @@ export interface paths {
1155
1222
  "application/json": components["schemas"]["Error"];
1156
1223
  };
1157
1224
  };
1158
- /** @description Session not found */
1159
- 404: {
1225
+ /** @description Validation error */
1226
+ 422: {
1160
1227
  headers: {
1161
1228
  [name: string]: unknown;
1162
1229
  };
@@ -1164,8 +1231,8 @@ export interface paths {
1164
1231
  "application/json": components["schemas"]["Error"];
1165
1232
  };
1166
1233
  };
1167
- /** @description Validation error */
1168
- 422: {
1234
+ /** @description Policy persistence not configured */
1235
+ 503: {
1169
1236
  headers: {
1170
1237
  [name: string]: unknown;
1171
1238
  };
@@ -1175,56 +1242,58 @@ export interface paths {
1175
1242
  };
1176
1243
  };
1177
1244
  };
1245
+ put?: never;
1246
+ post?: never;
1178
1247
  delete?: never;
1179
1248
  options?: never;
1180
1249
  head?: never;
1181
1250
  patch?: never;
1182
1251
  trace?: never;
1183
1252
  };
1184
- "/v1/score/difficulty": {
1253
+ "/v1/underwriting/policy/{policyId}": {
1185
1254
  parameters: {
1186
1255
  query?: never;
1187
1256
  header?: never;
1188
1257
  path?: never;
1189
1258
  cookie?: never;
1190
1259
  };
1191
- get?: never;
1192
- put?: never;
1193
1260
  /**
1194
- * Skill-conditioned scoring
1195
- * @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").
1261
+ * Fetch a single policy by id (Scale)
1262
+ * @description Read-only lookup. Cross-tenant policies return 404.
1196
1263
  */
1197
- post: {
1264
+ get: {
1198
1265
  parameters: {
1199
1266
  query?: never;
1200
1267
  header?: never;
1201
- path?: never;
1202
- cookie?: never;
1203
- };
1204
- requestBody: {
1205
- content: {
1206
- "application/json": {
1207
- activity: string;
1208
- location: components["schemas"]["GeoPoint"];
1209
- window?: components["schemas"]["TimeWindow"];
1210
- /** @description Skill points (0-1) to score. Defaults to 5 quantile points. */
1211
- riderSkillLevels?: number[];
1212
- };
1268
+ path: {
1269
+ policyId: string;
1213
1270
  };
1271
+ cookie?: never;
1214
1272
  };
1273
+ requestBody?: never;
1215
1274
  responses: {
1216
- /** @description Score per skill level + difficulty band */
1275
+ /** @description Policy record + payout events */
1217
1276
  200: {
1218
1277
  headers: {
1219
1278
  [name: string]: unknown;
1220
1279
  };
1221
1280
  content: {
1222
1281
  "application/json": {
1223
- [key: string]: unknown;
1282
+ policy: components["schemas"]["SerialisedPolicy"];
1283
+ events: components["schemas"]["SerialisedPayoutEvent"][];
1224
1284
  };
1225
1285
  };
1226
1286
  };
1227
- /** @description No profile for activity */
1287
+ /** @description Requires Scale plan */
1288
+ 402: {
1289
+ headers: {
1290
+ [name: string]: unknown;
1291
+ };
1292
+ content: {
1293
+ "application/json": components["schemas"]["Error"];
1294
+ };
1295
+ };
1296
+ /** @description Policy not found */
1228
1297
  404: {
1229
1298
  headers: {
1230
1299
  [name: string]: unknown;
@@ -1233,8 +1302,8 @@ export interface paths {
1233
1302
  "application/json": components["schemas"]["Error"];
1234
1303
  };
1235
1304
  };
1236
- /** @description Validation error */
1237
- 422: {
1305
+ /** @description Policy persistence not configured */
1306
+ 503: {
1238
1307
  headers: {
1239
1308
  [name: string]: unknown;
1240
1309
  };
@@ -1244,13 +1313,15 @@ export interface paths {
1244
1313
  };
1245
1314
  };
1246
1315
  };
1316
+ put?: never;
1317
+ post?: never;
1247
1318
  delete?: never;
1248
1319
  options?: never;
1249
1320
  head?: never;
1250
1321
  patch?: never;
1251
1322
  trace?: never;
1252
1323
  };
1253
- "/v1/recommend-spot": {
1324
+ "/v1/underwriting/policy/{policyId}/evaluate": {
1254
1325
  parameters: {
1255
1326
  query?: never;
1256
1327
  header?: never;
@@ -1260,78 +1331,36 @@ export interface paths {
1260
1331
  get?: never;
1261
1332
  put?: never;
1262
1333
  /**
1263
- * Spot recommender inverse query (L10)
1264
- * @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'.
1334
+ * Re-evaluate a bound policy against historical replay (Scale)
1335
+ * @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.
1265
1336
  */
1266
1337
  post: {
1267
1338
  parameters: {
1268
1339
  query?: never;
1269
1340
  header?: never;
1270
- path?: never;
1271
- cookie?: never;
1272
- };
1273
- requestBody: {
1274
- content: {
1275
- "application/json": {
1276
- activity: string;
1277
- regionCenter: components["schemas"]["GeoPoint"];
1278
- radiusKm: number;
1279
- topK: number;
1280
- window?: components["schemas"]["TimeWindow"];
1281
- /** @description Optional booking-platform-side pseudonym. When present, Pro+ tenants get personalization via the L6 cold-start blend (cap 0.5 weight). */
1282
- userPseudonym?: string;
1283
- };
1341
+ path: {
1342
+ policyId: string;
1284
1343
  };
1344
+ cookie?: never;
1285
1345
  };
1346
+ requestBody?: never;
1286
1347
  responses: {
1287
- /** @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). */
1348
+ /** @description Policy + payout events + counts of newly inserted / skipped */
1288
1349
  200: {
1289
1350
  headers: {
1290
1351
  [name: string]: unknown;
1291
1352
  };
1292
1353
  content: {
1293
1354
  "application/json": {
1294
- results?: {
1295
- spotSlug?: string;
1296
- name?: string;
1297
- location?: components["schemas"]["GeoPoint"];
1298
- distanceKm?: number;
1299
- score?: number;
1300
- effectiveScore?: number;
1301
- verdict?: string;
1302
- personalScore?: number | null;
1303
- personalWeight?: number;
1304
- rank?: number;
1305
- }[];
1306
- allGated?: boolean;
1307
- totalCandidates?: number;
1308
- rankedCandidates?: number;
1309
- effectiveRadiusKm?: number;
1310
- effectiveTopK?: number;
1311
- personalizationApplied?: boolean;
1312
- latencyMs?: number;
1313
- coverage?: {
1314
- /** @enum {string} */
1315
- status: "no_subspots_in_radius";
1316
- nearestSubSpot: {
1317
- slug: string;
1318
- name: string;
1319
- distanceKm: number;
1320
- };
1321
- /** @enum {string} */
1322
- suggestedAction: "expand_radius";
1323
- suggestedRadiusKm: number;
1324
- } | {
1325
- /** @enum {string} */
1326
- status: "no_subspots_for_activity";
1327
- /** @enum {string} */
1328
- suggestedAction: "request_coverage";
1329
- };
1355
+ policy: components["schemas"]["SerialisedPolicy"];
1356
+ events: components["schemas"]["SerialisedPayoutEvent"][];
1357
+ inserted: number;
1358
+ skipped: number;
1330
1359
  };
1331
1360
  };
1332
1361
  };
1333
- /** @description Missing/invalid bearer token */
1334
- 401: {
1362
+ /** @description Requires Scale plan */
1363
+ 402: {
1335
1364
  headers: {
1336
1365
  [name: string]: unknown;
1337
1366
  };
@@ -1339,8 +1368,8 @@ export interface paths {
1339
1368
  "application/json": components["schemas"]["Error"];
1340
1369
  };
1341
1370
  };
1342
- /** @description Plan limit exceeded (radius or topK above plan cap) */
1343
- 402: {
1371
+ /** @description Policy not found */
1372
+ 404: {
1344
1373
  headers: {
1345
1374
  [name: string]: unknown;
1346
1375
  };
@@ -1348,7 +1377,7 @@ export interface paths {
1348
1377
  "application/json": components["schemas"]["Error"];
1349
1378
  };
1350
1379
  };
1351
- /** @description Validation error or unknown activity */
1380
+ /** @description Policy in a terminal state (settled / expired) */
1352
1381
  422: {
1353
1382
  headers: {
1354
1383
  [name: string]: unknown;
@@ -1357,7 +1386,7 @@ export interface paths {
1357
1386
  "application/json": components["schemas"]["Error"];
1358
1387
  };
1359
1388
  };
1360
- /** @description Spatial resolver not wired */
1389
+ /** @description Policy persistence or HistoricalProvider not configured */
1361
1390
  503: {
1362
1391
  headers: {
1363
1392
  [name: string]: unknown;
@@ -1374,7 +1403,7 @@ export interface paths {
1374
1403
  patch?: never;
1375
1404
  trace?: never;
1376
1405
  };
1377
- "/v1/intelligence/edge-case": {
1406
+ "/v1/underwriting/policy/{policyId}/settle": {
1378
1407
  parameters: {
1379
1408
  query?: never;
1380
1409
  header?: never;
@@ -1384,43 +1413,40 @@ export interface paths {
1384
1413
  get?: never;
1385
1414
  put?: never;
1386
1415
  /**
1387
- * LLM analysis of a borderline / surprising score (L2c, Pro+)
1388
- * @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.
1416
+ * Settle a bound policy (platform-ops only)
1417
+ * @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.
1389
1418
  */
1390
1419
  post: {
1391
1420
  parameters: {
1392
1421
  query?: never;
1393
1422
  header?: never;
1394
- path?: never;
1423
+ path: {
1424
+ policyId: string;
1425
+ };
1395
1426
  cookie?: never;
1396
1427
  };
1397
1428
  requestBody: {
1398
1429
  content: {
1399
1430
  "application/json": {
1400
- location: components["schemas"]["GeoPoint"];
1401
- scoreResult?: components["schemas"]["ScoreResponse"];
1402
- /** Format: uuid */
1403
- session_id?: string;
1404
- /** @enum {string} */
1405
- locale?: "en" | "it";
1406
- /** @enum {string} */
1407
- model?: "claude-haiku-4-5-20251001" | "claude-sonnet-4-6";
1431
+ settlementReference: string;
1432
+ /** Format: date-time */
1433
+ settledAt?: string;
1408
1434
  };
1409
1435
  };
1410
1436
  };
1411
1437
  responses: {
1412
- /** @description Narrative + limiting_class */
1438
+ /** @description Settled policy */
1413
1439
  200: {
1414
1440
  headers: {
1415
1441
  [name: string]: unknown;
1416
1442
  };
1417
1443
  content: {
1418
1444
  "application/json": {
1419
- [key: string]: unknown;
1445
+ policy: components["schemas"]["SerialisedPolicy"];
1420
1446
  };
1421
1447
  };
1422
1448
  };
1423
- /** @description Requires Pro+ plan */
1449
+ /** @description Requires Scale plan */
1424
1450
  402: {
1425
1451
  headers: {
1426
1452
  [name: string]: unknown;
@@ -1429,6 +1455,114 @@ export interface paths {
1429
1455
  "application/json": components["schemas"]["Error"];
1430
1456
  };
1431
1457
  };
1458
+ /** @description Missing scope: platform_admin */
1459
+ 403: {
1460
+ headers: {
1461
+ [name: string]: unknown;
1462
+ };
1463
+ content: {
1464
+ "application/json": components["schemas"]["Error"];
1465
+ };
1466
+ };
1467
+ /** @description Policy not found */
1468
+ 404: {
1469
+ headers: {
1470
+ [name: string]: unknown;
1471
+ };
1472
+ content: {
1473
+ "application/json": components["schemas"]["Error"];
1474
+ };
1475
+ };
1476
+ /** @description Validation error or policy not in a settleable state */
1477
+ 422: {
1478
+ headers: {
1479
+ [name: string]: unknown;
1480
+ };
1481
+ content: {
1482
+ "application/json": components["schemas"]["Error"];
1483
+ };
1484
+ };
1485
+ /** @description Policy persistence not configured */
1486
+ 503: {
1487
+ headers: {
1488
+ [name: string]: unknown;
1489
+ };
1490
+ content: {
1491
+ "application/json": components["schemas"]["Error"];
1492
+ };
1493
+ };
1494
+ };
1495
+ };
1496
+ delete?: never;
1497
+ options?: never;
1498
+ head?: never;
1499
+ patch?: never;
1500
+ trace?: never;
1501
+ };
1502
+ "/v1/score/{sessionId}/outcome": {
1503
+ parameters: {
1504
+ query?: never;
1505
+ header?: never;
1506
+ path?: never;
1507
+ cookie?: never;
1508
+ };
1509
+ get?: never;
1510
+ put?: never;
1511
+ /**
1512
+ * Report observed outcome for a scored session
1513
+ * @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).
1514
+ */
1515
+ post: {
1516
+ parameters: {
1517
+ query?: never;
1518
+ header?: never;
1519
+ path: {
1520
+ /** @description Session UUID returned in the /v1/score response metadata. */
1521
+ sessionId: string;
1522
+ };
1523
+ cookie?: never;
1524
+ };
1525
+ requestBody: {
1526
+ content: {
1527
+ "application/json": {
1528
+ /** @enum {string} */
1529
+ outcome_type: "ran" | "cancelled" | "rescheduled" | "no_show" | "note";
1530
+ detail?: {
1531
+ [key: string]: unknown;
1532
+ };
1533
+ };
1534
+ };
1535
+ };
1536
+ responses: {
1537
+ /** @description Accepted (queued for the next calibration batch) */
1538
+ 202: {
1539
+ headers: {
1540
+ [name: string]: unknown;
1541
+ };
1542
+ content: {
1543
+ "application/json": {
1544
+ [key: string]: unknown;
1545
+ };
1546
+ };
1547
+ };
1548
+ /** @description Missing scope: outcomes:write */
1549
+ 403: {
1550
+ headers: {
1551
+ [name: string]: unknown;
1552
+ };
1553
+ content: {
1554
+ "application/json": components["schemas"]["Error"];
1555
+ };
1556
+ };
1557
+ /** @description Session not found */
1558
+ 404: {
1559
+ headers: {
1560
+ [name: string]: unknown;
1561
+ };
1562
+ content: {
1563
+ "application/json": components["schemas"]["Error"];
1564
+ };
1565
+ };
1432
1566
  /** @description Validation error */
1433
1567
  422: {
1434
1568
  headers: {
@@ -1446,7 +1580,7 @@ export interface paths {
1446
1580
  patch?: never;
1447
1581
  trace?: never;
1448
1582
  };
1449
- "/v1/projections/portfolio": {
1583
+ "/v1/score/difficulty": {
1450
1584
  parameters: {
1451
1585
  query?: never;
1452
1586
  header?: never;
@@ -1455,7 +1589,10 @@ export interface paths {
1455
1589
  };
1456
1590
  get?: never;
1457
1591
  put?: never;
1458
- /** Climate projections across a portfolio of spots (Scale) */
1592
+ /**
1593
+ * L15 intrinsic-difficulty atlas lookup (Pro+)
1594
+ * @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).
1595
+ */
1459
1596
  post: {
1460
1597
  parameters: {
1461
1598
  query?: never;
@@ -1466,31 +1603,39 @@ export interface paths {
1466
1603
  requestBody: {
1467
1604
  content: {
1468
1605
  "application/json": {
1469
- spots: {
1470
- location: components["schemas"]["GeoPoint"];
1471
- activity: string;
1472
- spotId?: string;
1473
- }[];
1474
- scenarios: ("SSP1-2.6" | "SSP2-4.5" | "SSP3-7.0" | "SSP5-8.5")[];
1475
- horizonDecades?: string[];
1476
- /** @enum {string} */
1477
- baselineDecade?: "2020s" | "2030s";
1606
+ activity: string;
1607
+ location: components["schemas"]["GeoPoint"];
1478
1608
  };
1479
1609
  };
1480
1610
  };
1481
1611
  responses: {
1482
- /** @description Per-spot per-decade projection distributions */
1612
+ /** @description Per-dimension intrinsic difficulty curves for the resolved sub-spot */
1483
1613
  200: {
1484
1614
  headers: {
1485
1615
  [name: string]: unknown;
1486
1616
  };
1487
1617
  content: {
1488
1618
  "application/json": {
1489
- [key: string]: unknown;
1619
+ resolved: {
1620
+ /** @enum {string} */
1621
+ level: "sub-spot" | "cluster" | "region" | "base";
1622
+ slug: string;
1623
+ sub_spot_slug?: string;
1624
+ distance_to_sub_spot_m?: number;
1625
+ };
1626
+ dimensions: {
1627
+ dimension: string;
1628
+ grid: number[];
1629
+ difficulty: number[];
1630
+ discrimination_a: number;
1631
+ cohort_hash: string;
1632
+ /** Format: date-time */
1633
+ computed_at: string;
1634
+ }[];
1490
1635
  };
1491
1636
  };
1492
1637
  };
1493
- /** @description Requires Scale plan */
1638
+ /** @description Requires Pro+ plan */
1494
1639
  402: {
1495
1640
  headers: {
1496
1641
  [name: string]: unknown;
@@ -1499,6 +1644,15 @@ export interface paths {
1499
1644
  "application/json": components["schemas"]["Error"];
1500
1645
  };
1501
1646
  };
1647
+ /** @description No profile for activity, or no atlas row for the resolved sub-spot */
1648
+ 404: {
1649
+ headers: {
1650
+ [name: string]: unknown;
1651
+ };
1652
+ content: {
1653
+ "application/json": components["schemas"]["Error"];
1654
+ };
1655
+ };
1502
1656
  /** @description Validation error */
1503
1657
  422: {
1504
1658
  headers: {
@@ -1508,6 +1662,15 @@ export interface paths {
1508
1662
  "application/json": components["schemas"]["Error"];
1509
1663
  };
1510
1664
  };
1665
+ /** @description Difficulty atlas reader not wired */
1666
+ 503: {
1667
+ headers: {
1668
+ [name: string]: unknown;
1669
+ };
1670
+ content: {
1671
+ "application/json": components["schemas"]["Error"];
1672
+ };
1673
+ };
1511
1674
  };
1512
1675
  };
1513
1676
  delete?: never;
@@ -1516,7 +1679,7 @@ export interface paths {
1516
1679
  patch?: never;
1517
1680
  trace?: never;
1518
1681
  };
1519
- "/v1/projections/adaptation-report": {
1682
+ "/v1/recommend-spot": {
1520
1683
  parameters: {
1521
1684
  query?: never;
1522
1685
  header?: never;
@@ -1526,8 +1689,8 @@ export interface paths {
1526
1689
  get?: never;
1527
1690
  put?: never;
1528
1691
  /**
1529
- * Climate adaptation report for an operator (Scale)
1530
- * @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.
1692
+ * Spot recommender inverse query (L10)
1693
+ * @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'.
1531
1694
  */
1532
1695
  post: {
1533
1696
  parameters: {
@@ -1539,35 +1702,659 @@ export interface paths {
1539
1702
  requestBody: {
1540
1703
  content: {
1541
1704
  "application/json": {
1542
- spots: ({
1543
- location: components["schemas"]["GeoPoint"];
1544
- activity: string;
1545
- spotId?: string;
1546
- subSpotSlug?: string;
1547
- } & {
1548
- [key: string]: unknown;
1549
- })[];
1550
- scenarios: ("SSP1-2.6" | "SSP2-4.5" | "SSP3-7.0" | "SSP5-8.5")[];
1551
- horizonDecades?: string[];
1552
- } & {
1553
- [key: string]: unknown;
1705
+ activity: string;
1706
+ regionCenter: components["schemas"]["GeoPoint"];
1707
+ radiusKm: number;
1708
+ topK: number;
1709
+ window?: components["schemas"]["TimeWindow"];
1710
+ /** @description Optional booking-platform-side pseudonym. When present, Pro+ tenants get personalization via the L6 cold-start blend (cap 0.5 weight). */
1711
+ userPseudonym?: string;
1712
+ };
1713
+ };
1714
+ };
1715
+ responses: {
1716
+ /** @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). */
1717
+ 200: {
1718
+ headers: {
1719
+ [name: string]: unknown;
1720
+ };
1721
+ content: {
1722
+ "application/json": {
1723
+ results?: {
1724
+ spotSlug?: string;
1725
+ name?: string;
1726
+ location?: components["schemas"]["GeoPoint"];
1727
+ distanceKm?: number;
1728
+ score?: number;
1729
+ effectiveScore?: number;
1730
+ verdict?: string;
1731
+ personalScore?: number | null;
1732
+ personalWeight?: number;
1733
+ rank?: number;
1734
+ }[];
1735
+ allGated?: boolean;
1736
+ totalCandidates?: number;
1737
+ rankedCandidates?: number;
1738
+ effectiveRadiusKm?: number;
1739
+ effectiveTopK?: number;
1740
+ personalizationApplied?: boolean;
1741
+ latencyMs?: number;
1742
+ coverage?: {
1743
+ /** @enum {string} */
1744
+ status: "no_subspots_in_radius";
1745
+ nearestSubSpot: {
1746
+ slug: string;
1747
+ name: string;
1748
+ distanceKm: number;
1749
+ };
1750
+ /** @enum {string} */
1751
+ suggestedAction: "expand_radius";
1752
+ suggestedRadiusKm: number;
1753
+ } | {
1754
+ /** @enum {string} */
1755
+ status: "no_subspots_for_activity";
1756
+ /** @enum {string} */
1757
+ suggestedAction: "request_coverage";
1758
+ };
1759
+ };
1760
+ };
1761
+ };
1762
+ /** @description Missing/invalid bearer token */
1763
+ 401: {
1764
+ headers: {
1765
+ [name: string]: unknown;
1766
+ };
1767
+ content: {
1768
+ "application/json": components["schemas"]["Error"];
1769
+ };
1770
+ };
1771
+ /** @description Plan limit exceeded (radius or topK above plan cap) */
1772
+ 402: {
1773
+ headers: {
1774
+ [name: string]: unknown;
1775
+ };
1776
+ content: {
1777
+ "application/json": components["schemas"]["Error"];
1554
1778
  };
1555
1779
  };
1780
+ /** @description Validation error or unknown activity */
1781
+ 422: {
1782
+ headers: {
1783
+ [name: string]: unknown;
1784
+ };
1785
+ content: {
1786
+ "application/json": components["schemas"]["Error"];
1787
+ };
1788
+ };
1789
+ /** @description Spatial resolver not wired */
1790
+ 503: {
1791
+ headers: {
1792
+ [name: string]: unknown;
1793
+ };
1794
+ content: {
1795
+ "application/json": components["schemas"]["Error"];
1796
+ };
1797
+ };
1798
+ };
1799
+ };
1800
+ delete?: never;
1801
+ options?: never;
1802
+ head?: never;
1803
+ patch?: never;
1804
+ trace?: never;
1805
+ };
1806
+ "/v1/intelligence/edge-case": {
1807
+ parameters: {
1808
+ query?: never;
1809
+ header?: never;
1810
+ path?: never;
1811
+ cookie?: never;
1812
+ };
1813
+ get?: never;
1814
+ put?: never;
1815
+ /**
1816
+ * LLM analysis of a borderline / surprising score (L2c, Pro+)
1817
+ * @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.
1818
+ */
1819
+ post: {
1820
+ parameters: {
1821
+ query?: never;
1822
+ header?: never;
1823
+ path?: never;
1824
+ cookie?: never;
1825
+ };
1826
+ requestBody: {
1827
+ content: {
1828
+ "application/json": {
1829
+ location: components["schemas"]["GeoPoint"];
1830
+ scoreResult?: components["schemas"]["ScoreResponse"];
1831
+ /** Format: uuid */
1832
+ session_id?: string;
1833
+ /** @enum {string} */
1834
+ locale?: "en" | "it";
1835
+ /** @enum {string} */
1836
+ model?: "claude-haiku-4-5-20251001" | "claude-sonnet-4-6";
1837
+ };
1838
+ };
1839
+ };
1840
+ responses: {
1841
+ /** @description Narrative + limiting_class */
1842
+ 200: {
1843
+ headers: {
1844
+ [name: string]: unknown;
1845
+ };
1846
+ content: {
1847
+ "application/json": {
1848
+ [key: string]: unknown;
1849
+ };
1850
+ };
1851
+ };
1852
+ /** @description Requires Pro+ plan */
1853
+ 402: {
1854
+ headers: {
1855
+ [name: string]: unknown;
1856
+ };
1857
+ content: {
1858
+ "application/json": components["schemas"]["Error"];
1859
+ };
1860
+ };
1861
+ /** @description Validation error */
1862
+ 422: {
1863
+ headers: {
1864
+ [name: string]: unknown;
1865
+ };
1866
+ content: {
1867
+ "application/json": components["schemas"]["Error"];
1868
+ };
1869
+ };
1870
+ };
1871
+ };
1872
+ delete?: never;
1873
+ options?: never;
1874
+ head?: never;
1875
+ patch?: never;
1876
+ trace?: never;
1877
+ };
1878
+ "/v1/projections/portfolio": {
1879
+ parameters: {
1880
+ query?: never;
1881
+ header?: never;
1882
+ path?: never;
1883
+ cookie?: never;
1884
+ };
1885
+ get?: never;
1886
+ put?: never;
1887
+ /** Climate projections across a portfolio of spots (Scale) */
1888
+ post: {
1889
+ parameters: {
1890
+ query?: never;
1891
+ header?: never;
1892
+ path?: never;
1893
+ cookie?: never;
1894
+ };
1895
+ requestBody: {
1896
+ content: {
1897
+ "application/json": {
1898
+ spots: {
1899
+ location: components["schemas"]["GeoPoint"];
1900
+ activity: string;
1901
+ spotId?: string;
1902
+ }[];
1903
+ scenarios: ("SSP1-2.6" | "SSP2-4.5" | "SSP3-7.0" | "SSP5-8.5")[];
1904
+ horizonDecades?: string[];
1905
+ /** @enum {string} */
1906
+ baselineDecade?: "2020s" | "2030s";
1907
+ };
1908
+ };
1909
+ };
1910
+ responses: {
1911
+ /** @description Per-spot per-decade projection distributions */
1912
+ 200: {
1913
+ headers: {
1914
+ [name: string]: unknown;
1915
+ };
1916
+ content: {
1917
+ "application/json": {
1918
+ [key: string]: unknown;
1919
+ };
1920
+ };
1921
+ };
1922
+ /** @description Requires Scale plan */
1923
+ 402: {
1924
+ headers: {
1925
+ [name: string]: unknown;
1926
+ };
1927
+ content: {
1928
+ "application/json": components["schemas"]["Error"];
1929
+ };
1930
+ };
1931
+ /** @description Validation error */
1932
+ 422: {
1933
+ headers: {
1934
+ [name: string]: unknown;
1935
+ };
1936
+ content: {
1937
+ "application/json": components["schemas"]["Error"];
1938
+ };
1939
+ };
1940
+ };
1941
+ };
1942
+ delete?: never;
1943
+ options?: never;
1944
+ head?: never;
1945
+ patch?: never;
1946
+ trace?: never;
1947
+ };
1948
+ "/v1/projections/adaptation-report": {
1949
+ parameters: {
1950
+ query?: never;
1951
+ header?: never;
1952
+ path?: never;
1953
+ cookie?: never;
1954
+ };
1955
+ get?: never;
1956
+ put?: never;
1957
+ /**
1958
+ * Climate adaptation report for an operator (Scale)
1959
+ * @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.
1960
+ */
1961
+ post: {
1962
+ parameters: {
1963
+ query?: never;
1964
+ header?: never;
1965
+ path?: never;
1966
+ cookie?: never;
1967
+ };
1968
+ requestBody: {
1969
+ content: {
1970
+ "application/json": {
1971
+ spots: ({
1972
+ location: components["schemas"]["GeoPoint"];
1973
+ activity: string;
1974
+ spotId?: string;
1975
+ subSpotSlug?: string;
1976
+ } & {
1977
+ [key: string]: unknown;
1978
+ })[];
1979
+ scenarios: ("SSP1-2.6" | "SSP2-4.5" | "SSP3-7.0" | "SSP5-8.5")[];
1980
+ horizonDecades?: string[];
1981
+ } & {
1982
+ [key: string]: unknown;
1983
+ };
1984
+ };
1985
+ };
1986
+ responses: {
1987
+ /** @description Adaptation report + binding-dimension summary */
1988
+ 200: {
1989
+ headers: {
1990
+ [name: string]: unknown;
1991
+ };
1992
+ content: {
1993
+ "application/json": {
1994
+ [key: string]: unknown;
1995
+ };
1996
+ };
1997
+ };
1998
+ /** @description Requires Scale plan */
1999
+ 402: {
2000
+ headers: {
2001
+ [name: string]: unknown;
2002
+ };
2003
+ content: {
2004
+ "application/json": components["schemas"]["Error"];
2005
+ };
2006
+ };
2007
+ /** @description Validation error */
2008
+ 422: {
2009
+ headers: {
2010
+ [name: string]: unknown;
2011
+ };
2012
+ content: {
2013
+ "application/json": components["schemas"]["Error"];
2014
+ };
2015
+ };
2016
+ };
2017
+ };
2018
+ delete?: never;
2019
+ options?: never;
2020
+ head?: never;
2021
+ patch?: never;
2022
+ trace?: never;
2023
+ };
2024
+ "/v1/observations": {
2025
+ parameters: {
2026
+ query?: never;
2027
+ header?: never;
2028
+ path?: never;
2029
+ cookie?: never;
2030
+ };
2031
+ get?: never;
2032
+ put?: never;
2033
+ /**
2034
+ * Submit station observations for data assimilation (Pro+)
2035
+ * @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.
2036
+ */
2037
+ post: {
2038
+ parameters: {
2039
+ query?: never;
2040
+ header?: never;
2041
+ path?: never;
2042
+ cookie?: never;
2043
+ };
2044
+ requestBody: {
2045
+ content: {
2046
+ "application/json": {
2047
+ /** Format: uuid */
2048
+ stationId: string;
2049
+ observations: {
2050
+ /** Format: date-time */
2051
+ observedAt: string;
2052
+ /** @enum {string} */
2053
+ variable: "wind_speed_kn" | "wind_dir_deg" | "wave_height_m" | "temp_c" | "sea_surface_temp_c" | "pressure_hpa" | "precip_mm";
2054
+ value: number;
2055
+ /** @enum {string} */
2056
+ qualityFlag?: "verified" | "unflagged" | "flagged_low_quality";
2057
+ }[];
2058
+ };
2059
+ };
2060
+ };
2061
+ responses: {
2062
+ /** @description Accepted */
2063
+ 202: {
2064
+ headers: {
2065
+ [name: string]: unknown;
2066
+ };
2067
+ content: {
2068
+ "application/json": {
2069
+ [key: string]: unknown;
2070
+ };
2071
+ };
2072
+ };
2073
+ /** @description Requires Pro+ plan */
2074
+ 402: {
2075
+ headers: {
2076
+ [name: string]: unknown;
2077
+ };
2078
+ content: {
2079
+ "application/json": components["schemas"]["Error"];
2080
+ };
2081
+ };
2082
+ /** @description Station not found */
2083
+ 404: {
2084
+ headers: {
2085
+ [name: string]: unknown;
2086
+ };
2087
+ content: {
2088
+ "application/json": components["schemas"]["Error"];
2089
+ };
2090
+ };
2091
+ /** @description Station inactive */
2092
+ 409: {
2093
+ headers: {
2094
+ [name: string]: unknown;
2095
+ };
2096
+ content: {
2097
+ "application/json": components["schemas"]["Error"];
2098
+ };
2099
+ };
2100
+ /** @description Validation error / observation in future / variable mismatch */
2101
+ 422: {
2102
+ headers: {
2103
+ [name: string]: unknown;
2104
+ };
2105
+ content: {
2106
+ "application/json": components["schemas"]["Error"];
2107
+ };
2108
+ };
2109
+ /** @description Observation store not wired */
2110
+ 503: {
2111
+ headers: {
2112
+ [name: string]: unknown;
2113
+ };
2114
+ content: {
2115
+ "application/json": components["schemas"]["Error"];
2116
+ };
2117
+ };
2118
+ };
2119
+ };
2120
+ delete?: never;
2121
+ options?: never;
2122
+ head?: never;
2123
+ patch?: never;
2124
+ trace?: never;
2125
+ };
2126
+ "/v1/observations/stations": {
2127
+ parameters: {
2128
+ query?: never;
2129
+ header?: never;
2130
+ path?: never;
2131
+ cookie?: never;
2132
+ };
2133
+ /** List tenant stations */
2134
+ get: {
2135
+ parameters: {
2136
+ query?: never;
2137
+ header?: never;
2138
+ path?: never;
2139
+ cookie?: never;
2140
+ };
2141
+ requestBody?: never;
2142
+ responses: {
2143
+ /** @description Array of stations */
2144
+ 200: {
2145
+ headers: {
2146
+ [name: string]: unknown;
2147
+ };
2148
+ content: {
2149
+ "application/json": {
2150
+ [key: string]: unknown;
2151
+ };
2152
+ };
2153
+ };
2154
+ /** @description Station registry not wired */
2155
+ 503: {
2156
+ headers: {
2157
+ [name: string]: unknown;
2158
+ };
2159
+ content: {
2160
+ "application/json": components["schemas"]["Error"];
2161
+ };
2162
+ };
2163
+ };
2164
+ };
2165
+ put?: never;
2166
+ /** Register a tenant observation station */
2167
+ post: {
2168
+ parameters: {
2169
+ query?: never;
2170
+ header?: never;
2171
+ path?: never;
2172
+ cookie?: never;
2173
+ };
2174
+ requestBody: {
2175
+ content: {
2176
+ "application/json": {
2177
+ name: string;
2178
+ point: components["schemas"]["GeoPoint"];
2179
+ altitudeM?: number;
2180
+ variables: ("wind_speed_kn" | "wind_dir_deg" | "wave_height_m" | "temp_c" | "sea_surface_temp_c" | "pressure_hpa" | "precip_mm")[];
2181
+ /** @enum {string} */
2182
+ stationClass?: "verified" | "unflagged" | "flagged_low_quality";
2183
+ updateCadenceMinutes?: number;
2184
+ notes?: string;
2185
+ };
2186
+ };
2187
+ };
2188
+ responses: {
2189
+ /** @description Station created */
2190
+ 201: {
2191
+ headers: {
2192
+ [name: string]: unknown;
2193
+ };
2194
+ content: {
2195
+ "application/json": {
2196
+ [key: string]: unknown;
2197
+ };
2198
+ };
2199
+ };
2200
+ /** @description Validation error */
2201
+ 422: {
2202
+ headers: {
2203
+ [name: string]: unknown;
2204
+ };
2205
+ content: {
2206
+ "application/json": components["schemas"]["Error"];
2207
+ };
2208
+ };
2209
+ /** @description Station registry not wired */
2210
+ 503: {
2211
+ headers: {
2212
+ [name: string]: unknown;
2213
+ };
2214
+ content: {
2215
+ "application/json": components["schemas"]["Error"];
2216
+ };
2217
+ };
2218
+ };
2219
+ };
2220
+ delete?: never;
2221
+ options?: never;
2222
+ head?: never;
2223
+ patch?: never;
2224
+ trace?: never;
2225
+ };
2226
+ "/v1/observations/stations/{stationId}": {
2227
+ parameters: {
2228
+ query?: never;
2229
+ header?: never;
2230
+ path?: never;
2231
+ cookie?: never;
2232
+ };
2233
+ get?: never;
2234
+ put?: never;
2235
+ post?: never;
2236
+ delete?: never;
2237
+ options?: never;
2238
+ head?: never;
2239
+ /** Update a station */
2240
+ patch: {
2241
+ parameters: {
2242
+ query?: never;
2243
+ header?: never;
2244
+ path: {
2245
+ stationId: string;
2246
+ };
2247
+ cookie?: never;
2248
+ };
2249
+ requestBody: {
2250
+ content: {
2251
+ "application/json": {
2252
+ name?: string;
2253
+ point?: components["schemas"]["GeoPoint"];
2254
+ altitudeM?: number;
2255
+ variables?: ("wind_speed_kn" | "wind_dir_deg" | "wave_height_m" | "temp_c" | "sea_surface_temp_c" | "pressure_hpa" | "precip_mm")[];
2256
+ /** @enum {string} */
2257
+ stationClass?: "verified" | "unflagged" | "flagged_low_quality";
2258
+ updateCadenceMinutes?: number;
2259
+ active?: boolean;
2260
+ notes?: string;
2261
+ };
2262
+ };
2263
+ };
2264
+ responses: {
2265
+ /** @description Updated station */
2266
+ 200: {
2267
+ headers: {
2268
+ [name: string]: unknown;
2269
+ };
2270
+ content: {
2271
+ "application/json": {
2272
+ [key: string]: unknown;
2273
+ };
2274
+ };
2275
+ };
2276
+ /** @description Station not found */
2277
+ 404: {
2278
+ headers: {
2279
+ [name: string]: unknown;
2280
+ };
2281
+ content: {
2282
+ "application/json": components["schemas"]["Error"];
2283
+ };
2284
+ };
2285
+ /** @description Validation error */
2286
+ 422: {
2287
+ headers: {
2288
+ [name: string]: unknown;
2289
+ };
2290
+ content: {
2291
+ "application/json": components["schemas"]["Error"];
2292
+ };
2293
+ };
2294
+ /** @description Station registry not wired */
2295
+ 503: {
2296
+ headers: {
2297
+ [name: string]: unknown;
2298
+ };
2299
+ content: {
2300
+ "application/json": components["schemas"]["Error"];
2301
+ };
2302
+ };
2303
+ };
2304
+ };
2305
+ trace?: never;
2306
+ };
2307
+ "/v1/observations/stations/{stationId}/recent": {
2308
+ parameters: {
2309
+ query?: never;
2310
+ header?: never;
2311
+ path?: never;
2312
+ cookie?: never;
2313
+ };
2314
+ /**
2315
+ * Recent observations for a tenant station
2316
+ * @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.
2317
+ */
2318
+ get: {
2319
+ parameters: {
2320
+ query?: {
2321
+ limit?: number;
2322
+ };
2323
+ header?: never;
2324
+ path: {
2325
+ stationId: string;
2326
+ };
2327
+ cookie?: never;
1556
2328
  };
2329
+ requestBody?: never;
1557
2330
  responses: {
1558
- /** @description Adaptation report + binding-dimension summary */
2331
+ /** @description Most-recent observations for this station (descending by observedAt) */
1559
2332
  200: {
1560
2333
  headers: {
1561
2334
  [name: string]: unknown;
1562
2335
  };
1563
2336
  content: {
1564
2337
  "application/json": {
1565
- [key: string]: unknown;
2338
+ observations: {
2339
+ /** Format: uuid */
2340
+ id: string;
2341
+ /** Format: uuid */
2342
+ stationId: string;
2343
+ /** Format: date-time */
2344
+ observedAt: string;
2345
+ /** @enum {string} */
2346
+ variable: "wind_speed_kn" | "wind_dir_deg" | "wave_height_m" | "temp_c" | "sea_surface_temp_c" | "pressure_hpa" | "precip_mm";
2347
+ value: number;
2348
+ /** @enum {string|null} */
2349
+ qualityFlag?: "verified" | "unflagged" | "flagged_low_quality" | null;
2350
+ /** Format: date-time */
2351
+ ingestedAt: string;
2352
+ }[];
1566
2353
  };
1567
2354
  };
1568
2355
  };
1569
- /** @description Requires Scale plan */
1570
- 402: {
2356
+ /** @description Station not found */
2357
+ 404: {
1571
2358
  headers: {
1572
2359
  [name: string]: unknown;
1573
2360
  };
@@ -1575,8 +2362,8 @@ export interface paths {
1575
2362
  "application/json": components["schemas"]["Error"];
1576
2363
  };
1577
2364
  };
1578
- /** @description Validation error */
1579
- 422: {
2365
+ /** @description Observation store not wired */
2366
+ 503: {
1580
2367
  headers: {
1581
2368
  [name: string]: unknown;
1582
2369
  };
@@ -1586,13 +2373,15 @@ export interface paths {
1586
2373
  };
1587
2374
  };
1588
2375
  };
2376
+ put?: never;
2377
+ post?: never;
1589
2378
  delete?: never;
1590
2379
  options?: never;
1591
2380
  head?: never;
1592
2381
  patch?: never;
1593
2382
  trace?: never;
1594
2383
  };
1595
- "/v1/observations": {
2384
+ "/v1/public/signup": {
1596
2385
  parameters: {
1597
2386
  query?: never;
1598
2387
  header?: never;
@@ -1602,8 +2391,8 @@ export interface paths {
1602
2391
  get?: never;
1603
2392
  put?: never;
1604
2393
  /**
1605
- * Submit station observation for data assimilation (Pro+)
1606
- * @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.
2394
+ * Self-service tenant signup (no auth)
2395
+ * @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.
1607
2396
  */
1608
2397
  post: {
1609
2398
  parameters: {
@@ -1615,21 +2404,20 @@ export interface paths {
1615
2404
  requestBody: {
1616
2405
  content: {
1617
2406
  "application/json": {
1618
- /** Format: uuid */
1619
- stationId: string;
1620
- /** Format: date-time */
1621
- observedAt: string;
1622
- values: {
1623
- [key: string]: number;
1624
- };
1625
- qualityFlags?: {
1626
- [key: string]: unknown;
1627
- };
2407
+ displayName: string;
2408
+ /** Format: email */
2409
+ contactEmail: string;
2410
+ /**
2411
+ * @description Must be true — records ToS / Privacy / DPA acceptance.
2412
+ * @enum {boolean}
2413
+ */
2414
+ acceptTerms: true;
2415
+ turnstileToken?: string;
1628
2416
  };
1629
2417
  };
1630
2418
  };
1631
2419
  responses: {
1632
- /** @description Accepted */
2420
+ /** @description Accepted — magic-link sent if signup valid */
1633
2421
  202: {
1634
2422
  headers: {
1635
2423
  [name: string]: unknown;
@@ -1640,17 +2428,8 @@ export interface paths {
1640
2428
  };
1641
2429
  };
1642
2430
  };
1643
- /** @description Requires Pro+ plan */
1644
- 402: {
1645
- headers: {
1646
- [name: string]: unknown;
1647
- };
1648
- content: {
1649
- "application/json": components["schemas"]["Error"];
1650
- };
1651
- };
1652
- /** @description Station not found */
1653
- 404: {
2431
+ /** @description Validation error */
2432
+ 422: {
1654
2433
  headers: {
1655
2434
  [name: string]: unknown;
1656
2435
  };
@@ -1658,8 +2437,8 @@ export interface paths {
1658
2437
  "application/json": components["schemas"]["Error"];
1659
2438
  };
1660
2439
  };
1661
- /** @description Validation error */
1662
- 422: {
2440
+ /** @description IP rate limit exceeded */
2441
+ 429: {
1663
2442
  headers: {
1664
2443
  [name: string]: unknown;
1665
2444
  };
@@ -1675,73 +2454,92 @@ export interface paths {
1675
2454
  patch?: never;
1676
2455
  trace?: never;
1677
2456
  };
1678
- "/v1/observations/stations": {
2457
+ "/v1/public/sustainability-index": {
1679
2458
  parameters: {
1680
2459
  query?: never;
1681
2460
  header?: never;
1682
2461
  path?: never;
1683
2462
  cookie?: never;
1684
2463
  };
1685
- /** List tenant stations */
2464
+ /**
2465
+ * Public Goable Sustainability Index (no auth, JSON-LD)
2466
+ * @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.
2467
+ */
1686
2468
  get: {
1687
2469
  parameters: {
1688
- query?: never;
2470
+ query: {
2471
+ /** @description Inclusive start of the reporting period. */
2472
+ from: string;
2473
+ /** @description Exclusive end of the reporting period. */
2474
+ to: string;
2475
+ /** @description Zone grid cell edge length in km (default: reader-configured). */
2476
+ zoneKm?: number;
2477
+ /** @description k-anonymity threshold: zones with fewer than k sessions are suppressed. */
2478
+ k?: number;
2479
+ };
1689
2480
  header?: never;
1690
2481
  path?: never;
1691
2482
  cookie?: never;
1692
2483
  };
1693
2484
  requestBody?: never;
1694
2485
  responses: {
1695
- /** @description Array of stations */
2486
+ /** @description Sustainability Index document (JSON-LD) */
1696
2487
  200: {
1697
2488
  headers: {
1698
2489
  [name: string]: unknown;
1699
2490
  };
1700
2491
  content: {
1701
- "application/json": {
1702
- [key: string]: unknown;
1703
- };
1704
- };
1705
- };
1706
- };
1707
- };
1708
- put?: never;
1709
- /** Register a tenant observation station (Pro+) */
1710
- post: {
1711
- parameters: {
1712
- query?: never;
1713
- header?: never;
1714
- path?: never;
1715
- cookie?: never;
1716
- };
1717
- requestBody: {
1718
- content: {
1719
- "application/json": {
1720
- name: string;
1721
- location: components["schemas"]["GeoPoint"];
1722
- elevationM?: number;
1723
- metadata?: {
1724
- [key: string]: unknown;
1725
- };
1726
- } & {
1727
- [key: string]: unknown;
1728
- };
1729
- };
1730
- };
1731
- responses: {
1732
- /** @description Station created */
1733
- 201: {
1734
- headers: {
1735
- [name: string]: unknown;
1736
- };
1737
- content: {
1738
- "application/json": {
2492
+ "application/ld+json": {
2493
+ /** @example https://schema.org */
2494
+ "@context": string;
2495
+ /** @enum {string} */
2496
+ "@type": "GoableSustainabilityIndex";
2497
+ /** Format: date-time */
2498
+ generatedAt: string;
2499
+ period: {
2500
+ /** Format: date-time */
2501
+ from: string;
2502
+ /** Format: date-time */
2503
+ to: string;
2504
+ };
2505
+ methodology: {
2506
+ indexFormula: string;
2507
+ weights: {
2508
+ carbonNeutralShare: number;
2509
+ electrificationShare: number;
2510
+ };
2511
+ suppression: string;
2512
+ notes: string;
2513
+ };
2514
+ overall: {
2515
+ index: number;
2516
+ totalSessions: number;
2517
+ carbonNeutralSessions: number;
2518
+ carbonPositiveSessions: number;
2519
+ carbonNeutralShare: number;
2520
+ zonesReleased: number;
2521
+ zonesSuppressed: number;
2522
+ };
2523
+ zones: {
2524
+ zoneKey: string;
2525
+ label: string | null;
2526
+ centroid: components["schemas"]["GeoPoint"] | null;
2527
+ totalSessions: number;
2528
+ carbonNeutralShare: number;
2529
+ electrificationShare: number | null;
2530
+ seasonalConcentration: number | null;
2531
+ zoneIndex: number;
2532
+ }[];
2533
+ /** @enum {string} */
2534
+ license: "CC BY 4.0";
2535
+ attribution: string;
2536
+ } & {
1739
2537
  [key: string]: unknown;
1740
2538
  };
1741
2539
  };
1742
2540
  };
1743
- /** @description Requires Pro+ plan */
1744
- 402: {
2541
+ /** @description Validation error */
2542
+ 422: {
1745
2543
  headers: {
1746
2544
  [name: string]: unknown;
1747
2545
  };
@@ -1749,8 +2547,8 @@ export interface paths {
1749
2547
  "application/json": components["schemas"]["Error"];
1750
2548
  };
1751
2549
  };
1752
- /** @description Validation error */
1753
- 422: {
2550
+ /** @description Sustainability index reader not wired */
2551
+ 503: {
1754
2552
  headers: {
1755
2553
  [name: string]: unknown;
1756
2554
  };
@@ -1760,63 +2558,48 @@ export interface paths {
1760
2558
  };
1761
2559
  };
1762
2560
  };
2561
+ put?: never;
2562
+ post?: never;
1763
2563
  delete?: never;
1764
2564
  options?: never;
1765
2565
  head?: never;
1766
2566
  patch?: never;
1767
2567
  trace?: never;
1768
2568
  };
1769
- "/v1/observations/stations/{stationId}": {
2569
+ "/v1/research/verification/export": {
1770
2570
  parameters: {
1771
2571
  query?: never;
1772
2572
  header?: never;
1773
2573
  path?: never;
1774
2574
  cookie?: never;
1775
2575
  };
1776
- get?: never;
1777
- put?: never;
1778
- post?: never;
1779
- delete?: never;
1780
- options?: never;
1781
- head?: never;
1782
- /** Update a station (Pro+) */
1783
- patch: {
2576
+ /**
2577
+ * Public Stream F forecast verification export (no auth, JSONL)
2578
+ * @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.
2579
+ */
2580
+ get: {
1784
2581
  parameters: {
1785
- query?: never;
1786
- header?: never;
1787
- path: {
1788
- stationId: string;
2582
+ query?: {
2583
+ from?: string;
2584
+ to?: string;
1789
2585
  };
2586
+ header?: never;
2587
+ path?: never;
1790
2588
  cookie?: never;
1791
2589
  };
1792
- requestBody: {
1793
- content: {
1794
- "application/json": {
1795
- name?: string;
1796
- location?: components["schemas"]["GeoPoint"];
1797
- elevationM?: number;
1798
- metadata?: {
1799
- [key: string]: unknown;
1800
- };
1801
- } & {
1802
- [key: string]: unknown;
1803
- };
1804
- };
1805
- };
2590
+ requestBody?: never;
1806
2591
  responses: {
1807
- /** @description Updated station */
2592
+ /** @description Streamed JSONL — one cell per line + trailing meta line */
1808
2593
  200: {
1809
2594
  headers: {
1810
2595
  [name: string]: unknown;
1811
2596
  };
1812
2597
  content: {
1813
- "application/json": {
1814
- [key: string]: unknown;
1815
- };
2598
+ "application/x-ndjson": string;
1816
2599
  };
1817
2600
  };
1818
- /** @description Requires Pro+ plan */
1819
- 402: {
2601
+ /** @description Validation error */
2602
+ 422: {
1820
2603
  headers: {
1821
2604
  [name: string]: unknown;
1822
2605
  };
@@ -1824,8 +2607,8 @@ export interface paths {
1824
2607
  "application/json": components["schemas"]["Error"];
1825
2608
  };
1826
2609
  };
1827
- /** @description Station not found */
1828
- 404: {
2610
+ /** @description Verification reader not wired */
2611
+ 503: {
1829
2612
  headers: {
1830
2613
  [name: string]: unknown;
1831
2614
  };
@@ -1835,66 +2618,45 @@ export interface paths {
1835
2618
  };
1836
2619
  };
1837
2620
  };
2621
+ put?: never;
2622
+ post?: never;
2623
+ delete?: never;
2624
+ options?: never;
2625
+ head?: never;
2626
+ patch?: never;
1838
2627
  trace?: never;
1839
2628
  };
1840
- "/v1/public/signup": {
2629
+ "/v1/research/difficulty-atlas/export.jsonl": {
1841
2630
  parameters: {
1842
2631
  query?: never;
1843
2632
  header?: never;
1844
2633
  path?: never;
1845
2634
  cookie?: never;
1846
2635
  };
1847
- get?: never;
1848
- put?: never;
1849
2636
  /**
1850
- * Self-service tenant signup (no auth)
1851
- * @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.
2637
+ * Public L15 Difficulty Atlas export (no auth, JSONL)
2638
+ * @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.
1852
2639
  */
1853
- post: {
2640
+ get: {
1854
2641
  parameters: {
1855
2642
  query?: never;
1856
2643
  header?: never;
1857
2644
  path?: never;
1858
2645
  cookie?: never;
1859
2646
  };
1860
- requestBody: {
1861
- content: {
1862
- "application/json": {
1863
- displayName: string;
1864
- /** Format: email */
1865
- contactEmail: string;
1866
- /**
1867
- * @description Must be true — records ToS / Privacy / DPA acceptance.
1868
- * @enum {boolean}
1869
- */
1870
- acceptTerms: true;
1871
- turnstileToken?: string;
1872
- };
1873
- };
1874
- };
2647
+ requestBody?: never;
1875
2648
  responses: {
1876
- /** @description Acceptedmagic-link sent if signup valid */
1877
- 202: {
1878
- headers: {
1879
- [name: string]: unknown;
1880
- };
1881
- content: {
1882
- "application/json": {
1883
- [key: string]: unknown;
1884
- };
1885
- };
1886
- };
1887
- /** @description Validation error */
1888
- 422: {
2649
+ /** @description Streamed JSONL one atlas row per line + trailing meta line */
2650
+ 200: {
1889
2651
  headers: {
1890
2652
  [name: string]: unknown;
1891
2653
  };
1892
2654
  content: {
1893
- "application/json": components["schemas"]["Error"];
2655
+ "application/x-ndjson": string;
1894
2656
  };
1895
2657
  };
1896
- /** @description IP rate limit exceeded */
1897
- 429: {
2658
+ /** @description Difficulty atlas reader not wired */
2659
+ 503: {
1898
2660
  headers: {
1899
2661
  [name: string]: unknown;
1900
2662
  };
@@ -1904,6 +2666,8 @@ export interface paths {
1904
2666
  };
1905
2667
  };
1906
2668
  };
2669
+ put?: never;
2670
+ post?: never;
1907
2671
  delete?: never;
1908
2672
  options?: never;
1909
2673
  head?: never;
@@ -2049,12 +2813,8 @@ export interface components {
2049
2813
  distribution?: {
2050
2814
  [key: string]: unknown;
2051
2815
  };
2052
- confidenceDetail?: {
2053
- [key: string]: unknown;
2054
- };
2055
- calibration_provenance?: {
2056
- [key: string]: unknown;
2057
- };
2816
+ confidenceDetail?: components["schemas"]["ConfidenceDetailForecast"];
2817
+ calibration_provenance?: components["schemas"]["CalibrationProvenance"] | null;
2058
2818
  assimilation?: {
2059
2819
  [key: string]: unknown;
2060
2820
  };
@@ -2084,6 +2844,297 @@ export interface components {
2084
2844
  } & {
2085
2845
  [key: string]: unknown;
2086
2846
  };
2847
+ CalibrationProvenance: {
2848
+ /** @enum {string} */
2849
+ level?: "sub-spot" | "cluster" | "region" | "base";
2850
+ slug?: string;
2851
+ scoring_profile_slug?: string;
2852
+ sub_spot_slug?: string;
2853
+ distance_to_sub_spot_m?: number;
2854
+ /** @enum {integer} */
2855
+ tier?: 1 | 2 | 3;
2856
+ /** @enum {string} */
2857
+ tier_source?: "catalog" | "classifier";
2858
+ };
2859
+ CalibrationProvenanceSummary: {
2860
+ /** @enum {string} */
2861
+ level: "base" | "region" | "cluster" | "sub-spot";
2862
+ n_local: number | null;
2863
+ shrinkage_weight_from_parent: number | null;
2864
+ };
2865
+ DriftFlag: {
2866
+ /** @enum {string} */
2867
+ severity: "watch" | "warning" | "critical";
2868
+ /** Format: date-time */
2869
+ since_timestamp: string;
2870
+ };
2871
+ ConfidenceDetail: components["schemas"]["ConfidenceDetailForecast"] | components["schemas"]["ConfidenceDetailHistorical"] | components["schemas"]["ConfidenceDetailClimate"];
2872
+ ConfidenceDetailForecast: {
2873
+ /**
2874
+ * @description discriminator enum property added by openapi-typescript
2875
+ * @enum {string}
2876
+ */
2877
+ mode: "forecast";
2878
+ forecast_skill: number;
2879
+ provider_agreement: number;
2880
+ profile_maturity: number;
2881
+ hierarchical_calibration: number;
2882
+ calibration_provenance?: components["schemas"]["CalibrationProvenanceSummary"];
2883
+ skill_calibration?: {
2884
+ applied: boolean;
2885
+ n_train: number;
2886
+ cohort_hash: string;
2887
+ scalar: number;
2888
+ };
2889
+ drift_flag?: components["schemas"]["DriftFlag"];
2890
+ } & {
2891
+ [key: string]: unknown;
2892
+ };
2893
+ ConfidenceDetailHistorical: {
2894
+ /**
2895
+ * @description discriminator enum property added by openapi-typescript
2896
+ * @enum {string}
2897
+ */
2898
+ mode: "historical";
2899
+ sample_size_confidence: number;
2900
+ base_climatology_quality: number;
2901
+ calibration_confidence: number;
2902
+ hierarchical_calibration: number;
2903
+ calibration_provenance?: components["schemas"]["CalibrationProvenanceSummary"];
2904
+ drift_flag?: components["schemas"]["DriftFlag"];
2905
+ } & {
2906
+ [key: string]: unknown;
2907
+ };
2908
+ ConfidenceDetailClimate: {
2909
+ /**
2910
+ * @description discriminator enum property added by openapi-typescript
2911
+ * @enum {string}
2912
+ */
2913
+ mode: "climate";
2914
+ model_spread: number;
2915
+ bias_correction_residual: number;
2916
+ scenario_uncertainty: number;
2917
+ horizon_uncertainty: number;
2918
+ hierarchical_calibration: number;
2919
+ calibration_provenance?: components["schemas"]["CalibrationProvenanceSummary"];
2920
+ drift_flag?: components["schemas"]["DriftFlag"];
2921
+ } & {
2922
+ [key: string]: unknown;
2923
+ };
2924
+ SerialisedPolicy: {
2925
+ /** Format: uuid */
2926
+ id: string;
2927
+ /** Format: uuid */
2928
+ tenantId: string;
2929
+ /** Format: uuid */
2930
+ quoteId: string;
2931
+ /** @enum {string} */
2932
+ status: "bound" | "triggered" | "settled" | "expired";
2933
+ /** @description Null for mixed-currency portfolios; the breakdown lives on the underlying quote. */
2934
+ payoutAmount?: number | null;
2935
+ payoutCurrency?: ("EUR" | "USD" | "GBP" | "CHF") | null;
2936
+ premiumPaid?: number | null;
2937
+ /** @enum {string} */
2938
+ premiumCollection: "external" | "stripe" | "invoice_due";
2939
+ coverageWindow: {
2940
+ monthFrom: number;
2941
+ dayFrom: number;
2942
+ monthTo: number;
2943
+ dayTo: number;
2944
+ };
2945
+ coverageYear: number;
2946
+ /** @description Frozen at bind time — same shape as the quote's normalised trigger. */
2947
+ trigger: {
2948
+ [key: string]: unknown;
2949
+ };
2950
+ /** @description Frozen snapshot of the per-spot profile + payout terms — decouples the policy from later catalog changes. */
2951
+ policyTerms: {
2952
+ [key: string]: unknown;
2953
+ };
2954
+ /** Format: date-time */
2955
+ boundAt: string;
2956
+ /** Format: date-time */
2957
+ triggeredAt: string | null;
2958
+ /** Format: date-time */
2959
+ settledAt: string | null;
2960
+ /** Format: date-time */
2961
+ expiredAt: string | null;
2962
+ settlementReference: string | null;
2963
+ };
2964
+ SerialisedPayoutEvent: {
2965
+ /** Format: uuid */
2966
+ id: string;
2967
+ /** Format: uuid */
2968
+ policyId: string;
2969
+ spotIndex: number;
2970
+ /** Format: date-time */
2971
+ eventStartAt: string;
2972
+ /** Format: date-time */
2973
+ eventEndAt: string;
2974
+ hoursFired: number;
2975
+ payoutAmount: number;
2976
+ /** @enum {string} */
2977
+ payoutCurrency: "EUR" | "USD" | "GBP" | "CHF";
2978
+ /** Format: date-time */
2979
+ detectedAt: string;
2980
+ /** @enum {string} */
2981
+ evaluationSource: "manual" | "scheduled";
2982
+ };
2983
+ UnderwritingQuoteResponse: {
2984
+ policy: {
2985
+ coverageWindow: {
2986
+ monthFrom: number;
2987
+ dayFrom: number;
2988
+ monthTo: number;
2989
+ dayTo: number;
2990
+ };
2991
+ /** @description Normalised trigger echoed back with defaulted consecutiveHours/cooldownHours. */
2992
+ trigger: {
2993
+ [key: string]: unknown;
2994
+ };
2995
+ historicalYearsRange: {
2996
+ from: number;
2997
+ to: number;
2998
+ };
2999
+ spot?: {
3000
+ resolvedProfileSlug: string;
3001
+ point: components["schemas"]["GeoPoint"];
3002
+ spotId?: string;
3003
+ payout: {
3004
+ amount: number;
3005
+ /** @enum {string} */
3006
+ currency: "EUR" | "USD" | "GBP" | "CHF";
3007
+ };
3008
+ /**
3009
+ * @description Sub-spot tier (1/2/3) applied to the tier risk multiplier; null when no sub-spot resolved.
3010
+ * @enum {integer|null}
3011
+ */
3012
+ tier?: 1 | 2 | 3 | null;
3013
+ /**
3014
+ * @description Provenance of the tier: 'catalog' from profile YAML, 'classifier' from L11 confidence=1.0 override, null when no tier applied.
3015
+ * @enum {string|null}
3016
+ */
3017
+ tierSource?: "catalog" | "classifier" | null;
3018
+ };
3019
+ portfolio?: {
3020
+ resolvedProfileSlug: string;
3021
+ point: components["schemas"]["GeoPoint"];
3022
+ spotId?: string;
3023
+ payout: {
3024
+ amount: number;
3025
+ /** @enum {string} */
3026
+ currency: "EUR" | "USD" | "GBP" | "CHF";
3027
+ };
3028
+ /**
3029
+ * @description Sub-spot tier (1/2/3) applied to the tier risk multiplier; null when no sub-spot resolved.
3030
+ * @enum {integer|null}
3031
+ */
3032
+ tier?: 1 | 2 | 3 | null;
3033
+ /**
3034
+ * @description Provenance of the tier: 'catalog' from profile YAML, 'classifier' from L11 confidence=1.0 override, null when no tier applied.
3035
+ * @enum {string|null}
3036
+ */
3037
+ tierSource?: "catalog" | "classifier" | null;
3038
+ }[];
3039
+ } & {
3040
+ [key: string]: unknown;
3041
+ };
3042
+ expectedPayouts: {
3043
+ byCurrency: {
3044
+ EUR?: {
3045
+ mean: number;
3046
+ p10: number;
3047
+ p50: number;
3048
+ p90: number;
3049
+ perYear: number[];
3050
+ varianceStability: number;
3051
+ };
3052
+ USD?: {
3053
+ mean: number;
3054
+ p10: number;
3055
+ p50: number;
3056
+ p90: number;
3057
+ perYear: number[];
3058
+ varianceStability: number;
3059
+ };
3060
+ GBP?: {
3061
+ mean: number;
3062
+ p10: number;
3063
+ p50: number;
3064
+ p90: number;
3065
+ perYear: number[];
3066
+ varianceStability: number;
3067
+ };
3068
+ CHF?: {
3069
+ mean: number;
3070
+ p10: number;
3071
+ p50: number;
3072
+ p90: number;
3073
+ perYear: number[];
3074
+ varianceStability: number;
3075
+ };
3076
+ };
3077
+ };
3078
+ expectedPremium: {
3079
+ byCurrency: {
3080
+ EUR?: {
3081
+ fair: number;
3082
+ loaded: number;
3083
+ /** @description Weighted-average tier risk multiplier applied across spots in this currency (1.0 when no sub-spot). */
3084
+ tierMultiplierWeightedAvg: number;
3085
+ };
3086
+ USD?: {
3087
+ fair: number;
3088
+ loaded: number;
3089
+ /** @description Weighted-average tier risk multiplier applied across spots in this currency (1.0 when no sub-spot). */
3090
+ tierMultiplierWeightedAvg: number;
3091
+ };
3092
+ GBP?: {
3093
+ fair: number;
3094
+ loaded: number;
3095
+ /** @description Weighted-average tier risk multiplier applied across spots in this currency (1.0 when no sub-spot). */
3096
+ tierMultiplierWeightedAvg: number;
3097
+ };
3098
+ CHF?: {
3099
+ fair: number;
3100
+ loaded: number;
3101
+ /** @description Weighted-average tier risk multiplier applied across spots in this currency (1.0 when no sub-spot). */
3102
+ tierMultiplierWeightedAvg: number;
3103
+ };
3104
+ };
3105
+ loadingFactor: number;
3106
+ };
3107
+ modelConfidence: number;
3108
+ /** @enum {string} */
3109
+ advisoryLevel: "high_confidence" | "moderate_confidence" | "low_confidence";
3110
+ issuable: boolean;
3111
+ notes: string[];
3112
+ /** @description HistoricalScoreDistribution — object for single spot, array for portfolio. */
3113
+ underlying: {
3114
+ [key: string]: unknown;
3115
+ } | {
3116
+ [key: string]: unknown;
3117
+ }[];
3118
+ /**
3119
+ * Format: uuid
3120
+ * @description Present when the policy store is wired — pass to /v1/underwriting/policy/bind to bind this quote.
3121
+ */
3122
+ quoteId?: string;
3123
+ /**
3124
+ * Format: date-time
3125
+ * @description ISO timestamp after which the quote can no longer be bound.
3126
+ */
3127
+ expiresAt?: string;
3128
+ /**
3129
+ * Format: uuid
3130
+ * @description Present on GET after the quote has been bound — the resulting policy id.
3131
+ */
3132
+ boundPolicyId?: string;
3133
+ } & {
3134
+ [key: string]: unknown;
3135
+ };
3136
+ /** @enum {string} */
3137
+ WebhookEvent: "outcome.created" | "drift.fired" | "drift.resolved" | "calibration.completed" | "billing.subscription_updated" | "recommendation.completed" | "underwriting.policy.bound" | "underwriting.policy.triggered" | "underwriting.policy.settled";
2087
3138
  };
2088
3139
  responses: never;
2089
3140
  parameters: never;