@gscdump/contracts 1.1.0 → 1.3.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.
package/dist/v1/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { accountNextActions, accountStatuses, analyticsNextActions, analyticsStatuses, builderStateSchema, gscComparisonFilterSchema, gscdumpAnalysisBundleResponseSchema, gscdumpAnalysisPresetSchema, gscdumpAnalysisResponseSchema, gscdumpAvailableSiteSchema, gscdumpCanonicalMismatchesResponseSchema, gscdumpDataDetailResponseSchema, gscdumpDataResponseSchema, gscdumpIndexingDiagnosticsResponseSchema, gscdumpIndexingResponseSchema, gscdumpKeywordSparklinesResponseSchema, gscdumpPageTrendResponseSchema, gscdumpQueryTrendResponseSchema, gscdumpSiteRegistrationSchema, gscdumpSitemapChangesResponseSchema, gscdumpSitemapsResponseSchema, gscdumpUserRegistrationSchema, indexingNextActions, indexingStatuses, indexingUrlsResponseSchema, lifecycleErrorCodes, propertyNextActions, propertyStatuses, querySourceModes, registerPartnerSiteSchema, registerPartnerUserSchema, searchTypeSchema, sitemapNextActions, sitemapStatuses, updatePartnerUserTokensSchema } from "../_chunks/schemas.mjs";
1
+ import { accountNextActions, accountStatuses, addPartnerTeamMemberSchema, analyticsNextActions, analyticsStatuses, bindPartnerSiteTeamSchema, bindPartnerTeamCatalogResponseSchema, bindPartnerTeamCatalogSchema, builderStateSchema, createPartnerTeamSchema, gscComparisonFilterSchema, gscdumpAnalysisBundleResponseSchema, gscdumpAnalysisPresetSchema, gscdumpAnalysisResponseSchema, gscdumpAvailableSiteSchema, gscdumpCanonicalMismatchesResponseSchema, gscdumpDataDetailResponseSchema, gscdumpDataResponseSchema, gscdumpDeletePartnerUserResponseSchema, gscdumpIndexPercentResponseSchema, gscdumpIndexingDiagnosticsResponseSchema, gscdumpIndexingResponseSchema, gscdumpKeywordSparklinesResponseSchema, gscdumpPageTrendResponseSchema, gscdumpQueryTrendResponseSchema, gscdumpSiteRegistrationSchema, gscdumpSitemapChangesResponseSchema, gscdumpSitemapMembershipParamsSchema, gscdumpSitemapMembershipResponseSchema, gscdumpSitemapsResponseSchema, gscdumpTeamRoleSchema, gscdumpTopAssociationResponseSchema, gscdumpUserRegistrationSchema, indexingNextActions, indexingStatuses, indexingUrlsResponseSchema, lifecycleErrorCodes, partnerSiteTeamBindingResponseSchema, partnerSitemapActionResponseSchema, partnerSitemapActionSchema, partnerTeamCreatedResponseSchema, partnerTeamDeletedResponseSchema, partnerTeamMemberAddedResponseSchema, partnerTeamMemberRemovedResponseSchema, partnerTeamMemberRoleResponseSchema, partnerTeamMembersResponseSchema, partnerTeamRenamedResponseSchema, propertyNextActions, propertyStatuses, querySourceModes, registerPartnerSiteSchema, registerPartnerUserSchema, renamePartnerTeamSchema, searchTypeSchema, siteIntIdCrosswalkResponseSchema, sitemapNextActions, sitemapStatuses, teamCatalogRefSchema, updatePartnerUserTokensSchema } from "../_chunks/schemas.mjs";
2
2
  import { z } from "zod";
3
3
  const HTTP_V1_SURFACES = [
4
4
  "partner",
@@ -1050,6 +1050,232 @@ function createGscdumpV1Protocol() {
1050
1050
  reason: z.enum(["domain_mismatch", "rate_limited"])
1051
1051
  }))
1052
1052
  }), partnerResponseMeta);
1053
+ const dateRangeQuery = z.strictObject({
1054
+ startDate: calendarDate,
1055
+ endDate: calendarDate
1056
+ });
1057
+ const siteDataMeta = z.object({
1058
+ siteUrl: z.string().nullable(),
1059
+ syncStatus: z.string().nullable()
1060
+ });
1061
+ const contentVelocityResponse = defineSuccessResponse(defineResponseObject({
1062
+ weekly: z.array(z.strictObject({
1063
+ week: z.string(),
1064
+ newKeywords: z.number().int().nonnegative(),
1065
+ totalKeywords: z.number().int().nonnegative()
1066
+ })),
1067
+ summary: z.strictObject({
1068
+ totalNewKeywords: z.number().int().nonnegative(),
1069
+ avgPerWeek: z.number().nonnegative(),
1070
+ trend: z.enum([
1071
+ "stable",
1072
+ "accelerating",
1073
+ "decelerating"
1074
+ ])
1075
+ }),
1076
+ meta: siteDataMeta
1077
+ }), partnerResponseMeta);
1078
+ const ctrOutlier = z.strictObject({
1079
+ query: z.string(),
1080
+ clicks: z.number(),
1081
+ impressions: z.number(),
1082
+ ctr: z.number(),
1083
+ position: z.number(),
1084
+ expectedCtr: z.number(),
1085
+ ctrDiff: z.number()
1086
+ });
1087
+ const ctrCurveResponse = defineSuccessResponse(defineResponseObject({
1088
+ curve: z.array(z.strictObject({
1089
+ bucket: z.string(),
1090
+ avgCtr: z.number(),
1091
+ medianPosition: z.number(),
1092
+ keywordCount: z.number(),
1093
+ totalClicks: z.number(),
1094
+ totalImpressions: z.number()
1095
+ })),
1096
+ overperforming: z.array(ctrOutlier),
1097
+ underperforming: z.array(ctrOutlier),
1098
+ meta: siteDataMeta
1099
+ }), partnerResponseMeta);
1100
+ const darkTrafficResponse = defineSuccessResponse(defineResponseObject({
1101
+ summary: z.strictObject({
1102
+ totalClicks: z.number(),
1103
+ attributedClicks: z.number(),
1104
+ darkClicks: z.number(),
1105
+ darkPercent: z.number(),
1106
+ totalImpressions: z.number(),
1107
+ attributedImpressions: z.number()
1108
+ }),
1109
+ pages: z.array(z.strictObject({
1110
+ url: z.string(),
1111
+ totalClicks: z.number(),
1112
+ attributedClicks: z.number(),
1113
+ darkClicks: z.number(),
1114
+ darkPercent: z.number(),
1115
+ keywordCount: z.number()
1116
+ })),
1117
+ meta: siteDataMeta
1118
+ }), partnerResponseMeta);
1119
+ const deviceGapMetrics = z.strictObject({
1120
+ clicks: z.number(),
1121
+ impressions: z.number(),
1122
+ ctr: z.number(),
1123
+ position: z.number()
1124
+ });
1125
+ const deviceGapResponse = defineSuccessResponse(defineResponseObject({
1126
+ daily: z.array(z.strictObject({
1127
+ date: z.string(),
1128
+ desktop: deviceGapMetrics,
1129
+ mobile: deviceGapMetrics,
1130
+ gaps: z.strictObject({
1131
+ ctrGap: z.number(),
1132
+ positionGap: z.number()
1133
+ })
1134
+ })),
1135
+ summary: z.strictObject({
1136
+ avgCtrGap: z.number(),
1137
+ avgPositionGap: z.number(),
1138
+ ctrGapTrend: z.enum([
1139
+ "stable",
1140
+ "improving",
1141
+ "worsening"
1142
+ ]),
1143
+ positionGapTrend: z.enum([
1144
+ "stable",
1145
+ "improving",
1146
+ "worsening"
1147
+ ])
1148
+ }).nullable(),
1149
+ meta: siteDataMeta
1150
+ }), partnerResponseMeta);
1151
+ const keywordBreadthPage = z.strictObject({
1152
+ url: z.string(),
1153
+ keywordCount: z.number(),
1154
+ clicks: z.number(),
1155
+ impressions: z.number()
1156
+ });
1157
+ const keywordBreadthResponse = defineSuccessResponse(defineResponseObject({
1158
+ distribution: z.array(z.strictObject({
1159
+ bucket: z.string(),
1160
+ pageCount: z.number()
1161
+ })),
1162
+ fragilePages: z.array(keywordBreadthPage),
1163
+ authorityPages: z.array(keywordBreadthPage),
1164
+ summary: z.strictObject({
1165
+ totalPages: z.number(),
1166
+ avgKeywordsPerPage: z.number(),
1167
+ fragileCount: z.number(),
1168
+ authorityCount: z.number()
1169
+ }),
1170
+ meta: siteDataMeta
1171
+ }), partnerResponseMeta);
1172
+ const positionDistributionResponse = defineSuccessResponse(defineResponseObject({
1173
+ distribution: z.array(z.strictObject({
1174
+ date: z.string(),
1175
+ pos_1_3: z.number(),
1176
+ pos_4_10: z.number(),
1177
+ pos_11_20: z.number(),
1178
+ pos_20_plus: z.number(),
1179
+ total: z.number()
1180
+ })),
1181
+ meta: siteDataMeta
1182
+ }), partnerResponseMeta);
1183
+ const topAssociationQuery = z.strictObject({
1184
+ type: z.enum(["topPage", "topKeyword"]),
1185
+ identifier: z.string().min(1),
1186
+ startDate: calendarDate,
1187
+ endDate: calendarDate
1188
+ });
1189
+ const topAssociationResponse = defineSuccessResponse(defineResponseObject(gscdumpTopAssociationResponseSchema.shape), partnerResponseMeta);
1190
+ const indexPercentQuery = z.strictObject({
1191
+ invisibleLimit: z.coerce.number().int().min(1).max(500).optional(),
1192
+ invisibleOffset: z.coerce.number().int().min(0).max(1e6).optional(),
1193
+ orphanLimit: z.coerce.number().int().min(1).max(500).optional()
1194
+ });
1195
+ const indexPercentResponse = defineSuccessResponse(defineResponseObject(gscdumpIndexPercentResponseSchema.shape), partnerResponseMeta);
1196
+ const contentVelocityQuery = z.strictObject({ days: z.coerce.number().int().min(1).max(365).optional() });
1197
+ const sitemapActionRequest = partnerSitemapActionSchema;
1198
+ const sitemapActionResponse = defineSuccessResponse({
1199
+ producer: partnerSitemapActionResponseSchema,
1200
+ client: partnerSitemapActionResponseSchema
1201
+ }, partnerResponseMeta);
1202
+ const sitemapMembershipRequest = gscdumpSitemapMembershipParamsSchema.strict();
1203
+ const sitemapMembershipResponse = defineSuccessResponse(defineResponseObject(gscdumpSitemapMembershipResponseSchema.shape), partnerResponseMeta);
1204
+ const createTeamRequest = createPartnerTeamSchema.strict();
1205
+ const teamCreatedResponse = defineSuccessResponse(defineResponseObject(partnerTeamCreatedResponseSchema.shape), partnerResponseMeta);
1206
+ const renameTeamRequest = renamePartnerTeamSchema.strict();
1207
+ const teamRenamedResponse = defineSuccessResponse(defineResponseObject(partnerTeamRenamedResponseSchema.shape), partnerResponseMeta);
1208
+ const teamDeletedResponse = defineSuccessResponse(defineResponseObject(partnerTeamDeletedResponseSchema.shape), partnerResponseMeta);
1209
+ const teamMembersResponse = defineSuccessResponse(defineResponseObject(partnerTeamMembersResponseSchema.shape), partnerResponseMeta);
1210
+ const addTeamMemberRequest = addPartnerTeamMemberSchema.strict();
1211
+ const teamMemberAddedResponse = defineSuccessResponse(defineResponseObject(partnerTeamMemberAddedResponseSchema.shape), partnerResponseMeta);
1212
+ const updateTeamMemberRoleRequest = z.strictObject({ role: gscdumpTeamRoleSchema });
1213
+ const teamMemberRoleResponse = defineSuccessResponse(defineResponseObject(partnerTeamMemberRoleResponseSchema.shape), partnerResponseMeta);
1214
+ const teamMemberRemovedResponse = defineSuccessResponse(defineResponseObject(partnerTeamMemberRemovedResponseSchema.shape), partnerResponseMeta);
1215
+ const bindSiteTeamRequest = bindPartnerSiteTeamSchema.strict();
1216
+ const siteTeamBindingResponse = defineSuccessResponse(defineResponseObject(partnerSiteTeamBindingResponseSchema.shape), partnerResponseMeta);
1217
+ const teamCatalogResponse = defineSuccessResponse(defineResponseObject(teamCatalogRefSchema.shape), partnerResponseMeta);
1218
+ const bindTeamCatalogRequest = bindPartnerTeamCatalogSchema.strict();
1219
+ const teamCatalogBindResponse = defineSuccessResponse(defineResponseObject(bindPartnerTeamCatalogResponseSchema.shape), partnerResponseMeta);
1220
+ const siteIntIdCrosswalkResponse = defineSuccessResponse(defineResponseObject(siteIntIdCrosswalkResponseSchema.shape), partnerResponseMeta);
1221
+ const deletePartnerUserResponse = defineSuccessResponse(defineResponseObject(gscdumpDeletePartnerUserResponseSchema.shape), partnerResponseMeta);
1222
+ const verificationMethodSchema = z.enum([
1223
+ "META",
1224
+ "FILE",
1225
+ "DNS_TXT",
1226
+ "DNS_CNAME",
1227
+ "ANALYTICS",
1228
+ "TAG_MANAGER"
1229
+ ]);
1230
+ const verificationTokenRequest = z.strictObject({
1231
+ siteUrl: z.string().min(1),
1232
+ method: verificationMethodSchema.optional()
1233
+ });
1234
+ const verificationSiteShape = z.object({
1235
+ type: z.string(),
1236
+ identifier: z.string()
1237
+ }).loose();
1238
+ const verificationTokenResponse = defineSuccessResponse(defineResponseObject({
1239
+ siteUrl: z.string(),
1240
+ site: verificationSiteShape,
1241
+ method: z.string(),
1242
+ token: z.string(),
1243
+ metaContent: z.string().nullable(),
1244
+ dnsRecord: z.object({
1245
+ type: z.enum(["TXT", "CNAME"]),
1246
+ host: z.string(),
1247
+ value: z.string()
1248
+ }).nullable()
1249
+ }), partnerResponseMeta);
1250
+ const addAndVerifySiteRequest = verificationTokenRequest;
1251
+ const addAndVerifySiteResponse = defineSuccessResponse(defineResponseObject({
1252
+ siteUrl: z.string(),
1253
+ site: verificationSiteShape,
1254
+ method: z.string(),
1255
+ verified: z.literal(true),
1256
+ owners: z.array(z.string()).optional()
1257
+ }), partnerResponseMeta);
1258
+ const crossSourceRequest = z.strictObject({
1259
+ queryKey: z.enum([
1260
+ "crawl-error-losing-impressions",
1261
+ "declining-clicks-poor-lcp",
1262
+ "striking-distance-slow-pages",
1263
+ "top-impressions-low-performance"
1264
+ ]),
1265
+ rangeDays: z.number().int().positive().max(180).optional(),
1266
+ limit: z.number().int().positive().max(500).optional()
1267
+ });
1268
+ const crossSourceResponse = defineSuccessResponse(defineResponseObject({
1269
+ reason: z.string().optional(),
1270
+ sources: z.array(z.string()),
1271
+ rows: z.array(z.record(z.string(), z.json()))
1272
+ }), partnerResponseMeta);
1273
+ const enrichKeywordsRequest = z.strictObject({ keywords: z.array(z.string().min(1)).min(1).max(500) });
1274
+ const enrichKeywordsResponse = defineSuccessResponse(defineResponseObject({ metrics: z.record(z.string(), z.object({
1275
+ difficulty: z.number().nullable(),
1276
+ searchVolume: z.number().nullable(),
1277
+ cpc: z.number().nullable()
1278
+ }).loose()) }), partnerResponseMeta);
1053
1279
  const responseStreamHead = defineResponseObject({
1054
1280
  streamId: realtimeSchemas.streamId,
1055
1281
  sequence: realtimeSchemas.sequence
@@ -2447,6 +2673,1782 @@ function createGscdumpV1Protocol() {
2447
2673
  }
2448
2674
  }
2449
2675
  }
2676
+ }),
2677
+ getContentVelocity: defineHttpOperation({
2678
+ id: "partner.sites.content.velocity.get",
2679
+ method: "GET",
2680
+ path: "/sites/{siteId}/content-velocity",
2681
+ visibility: "public",
2682
+ semantics: {
2683
+ kind: "query",
2684
+ sideEffects: "none",
2685
+ idempotent: true,
2686
+ retry: "idempotent",
2687
+ readConsistency: "primary"
2688
+ },
2689
+ auth: {
2690
+ credentials: ["user_key", "partner_key"],
2691
+ scopes: ["analytics:read"],
2692
+ ownership: [{
2693
+ credential: "user_key",
2694
+ rule: "authorized_site"
2695
+ }, {
2696
+ credential: "partner_key",
2697
+ rule: "authorized_site"
2698
+ }]
2699
+ },
2700
+ request: {
2701
+ params: z.strictObject({ siteId: realtimeSchemas.publicSiteId }),
2702
+ query: contentVelocityQuery,
2703
+ headers: requestHeaders,
2704
+ body: null
2705
+ },
2706
+ responses: { 200: contentVelocityResponse },
2707
+ errors: partnerSiteErrors,
2708
+ errorResponse: errorEnvelopeSchemas(partnerSiteErrors, realtimeSchemas.publicRequestId),
2709
+ resources: {
2710
+ reads: [{
2711
+ type: "site.analytics",
2712
+ idFrom: "params.siteId"
2713
+ }],
2714
+ changes: []
2715
+ },
2716
+ lifecycle: { introduced: "1.2.0" },
2717
+ docs: {
2718
+ summary: "Get content velocity",
2719
+ description: "Returns the weekly new-keyword series over the requested window with a summary trend.",
2720
+ tags: ["Analytics"],
2721
+ examples: {
2722
+ request: {
2723
+ params: { siteId: "s_01" },
2724
+ query: { days: 90 }
2725
+ },
2726
+ response: {
2727
+ data: {
2728
+ weekly: [],
2729
+ summary: {
2730
+ totalNewKeywords: 0,
2731
+ avgPerWeek: 0,
2732
+ trend: "stable"
2733
+ },
2734
+ meta: {
2735
+ siteUrl: "sc-domain:example.com",
2736
+ syncStatus: "synced"
2737
+ }
2738
+ },
2739
+ meta: {
2740
+ requestId: "req_01",
2741
+ surface: "partner",
2742
+ version: "1.0"
2743
+ }
2744
+ }
2745
+ }
2746
+ }
2747
+ }),
2748
+ getCtrCurve: defineHttpOperation({
2749
+ id: "partner.sites.ctr.curve.get",
2750
+ method: "GET",
2751
+ path: "/sites/{siteId}/ctr-curve",
2752
+ visibility: "public",
2753
+ semantics: {
2754
+ kind: "query",
2755
+ sideEffects: "none",
2756
+ idempotent: true,
2757
+ retry: "idempotent",
2758
+ readConsistency: "primary"
2759
+ },
2760
+ auth: {
2761
+ credentials: ["user_key", "partner_key"],
2762
+ scopes: ["analytics:read"],
2763
+ ownership: [{
2764
+ credential: "user_key",
2765
+ rule: "authorized_site"
2766
+ }, {
2767
+ credential: "partner_key",
2768
+ rule: "authorized_site"
2769
+ }]
2770
+ },
2771
+ request: {
2772
+ params: z.strictObject({ siteId: realtimeSchemas.publicSiteId }),
2773
+ query: dateRangeQuery,
2774
+ headers: requestHeaders,
2775
+ body: null
2776
+ },
2777
+ responses: { 200: ctrCurveResponse },
2778
+ errors: partnerSiteErrors,
2779
+ errorResponse: errorEnvelopeSchemas(partnerSiteErrors, realtimeSchemas.publicRequestId),
2780
+ resources: {
2781
+ reads: [{
2782
+ type: "site.analytics",
2783
+ idFrom: "params.siteId"
2784
+ }],
2785
+ changes: []
2786
+ },
2787
+ lifecycle: { introduced: "1.2.0" },
2788
+ docs: {
2789
+ summary: "Get CTR curve and outliers",
2790
+ description: "Returns the position-bucketed CTR curve plus over/under-performing query outliers for the window.",
2791
+ tags: ["Analytics"],
2792
+ examples: {
2793
+ request: {
2794
+ params: { siteId: "s_01" },
2795
+ query: {
2796
+ startDate: "2026-06-01",
2797
+ endDate: "2026-06-28"
2798
+ }
2799
+ },
2800
+ response: {
2801
+ data: {
2802
+ curve: [],
2803
+ overperforming: [],
2804
+ underperforming: [],
2805
+ meta: {
2806
+ siteUrl: "sc-domain:example.com",
2807
+ syncStatus: "synced"
2808
+ }
2809
+ },
2810
+ meta: {
2811
+ requestId: "req_01",
2812
+ surface: "partner",
2813
+ version: "1.0"
2814
+ }
2815
+ }
2816
+ }
2817
+ }
2818
+ }),
2819
+ getDarkTraffic: defineHttpOperation({
2820
+ id: "partner.sites.dark.traffic.get",
2821
+ method: "GET",
2822
+ path: "/sites/{siteId}/dark-traffic",
2823
+ visibility: "public",
2824
+ semantics: {
2825
+ kind: "query",
2826
+ sideEffects: "none",
2827
+ idempotent: true,
2828
+ retry: "idempotent",
2829
+ readConsistency: "primary"
2830
+ },
2831
+ auth: {
2832
+ credentials: ["user_key", "partner_key"],
2833
+ scopes: ["analytics:read"],
2834
+ ownership: [{
2835
+ credential: "user_key",
2836
+ rule: "authorized_site"
2837
+ }, {
2838
+ credential: "partner_key",
2839
+ rule: "authorized_site"
2840
+ }]
2841
+ },
2842
+ request: {
2843
+ params: z.strictObject({ siteId: realtimeSchemas.publicSiteId }),
2844
+ query: dateRangeQuery,
2845
+ headers: requestHeaders,
2846
+ body: null
2847
+ },
2848
+ responses: { 200: darkTrafficResponse },
2849
+ errors: partnerSiteErrors,
2850
+ errorResponse: errorEnvelopeSchemas(partnerSiteErrors, realtimeSchemas.publicRequestId),
2851
+ resources: {
2852
+ reads: [{
2853
+ type: "site.analytics",
2854
+ idFrom: "params.siteId"
2855
+ }],
2856
+ changes: []
2857
+ },
2858
+ lifecycle: { introduced: "1.2.0" },
2859
+ docs: {
2860
+ summary: "Get dark traffic breakdown",
2861
+ description: "Returns clicks not attributable to any tracked keyword, in total and per page, for the window.",
2862
+ tags: ["Analytics"],
2863
+ examples: {
2864
+ request: {
2865
+ params: { siteId: "s_01" },
2866
+ query: {
2867
+ startDate: "2026-06-01",
2868
+ endDate: "2026-06-28"
2869
+ }
2870
+ },
2871
+ response: {
2872
+ data: {
2873
+ summary: {
2874
+ totalClicks: 0,
2875
+ attributedClicks: 0,
2876
+ darkClicks: 0,
2877
+ darkPercent: 0,
2878
+ totalImpressions: 0,
2879
+ attributedImpressions: 0
2880
+ },
2881
+ pages: [],
2882
+ meta: {
2883
+ siteUrl: "sc-domain:example.com",
2884
+ syncStatus: "synced"
2885
+ }
2886
+ },
2887
+ meta: {
2888
+ requestId: "req_01",
2889
+ surface: "partner",
2890
+ version: "1.0"
2891
+ }
2892
+ }
2893
+ }
2894
+ }
2895
+ }),
2896
+ getDeviceGap: defineHttpOperation({
2897
+ id: "partner.sites.device.gap.get",
2898
+ method: "GET",
2899
+ path: "/sites/{siteId}/device-gap",
2900
+ visibility: "public",
2901
+ semantics: {
2902
+ kind: "query",
2903
+ sideEffects: "none",
2904
+ idempotent: true,
2905
+ retry: "idempotent",
2906
+ readConsistency: "primary"
2907
+ },
2908
+ auth: {
2909
+ credentials: ["user_key", "partner_key"],
2910
+ scopes: ["analytics:read"],
2911
+ ownership: [{
2912
+ credential: "user_key",
2913
+ rule: "authorized_site"
2914
+ }, {
2915
+ credential: "partner_key",
2916
+ rule: "authorized_site"
2917
+ }]
2918
+ },
2919
+ request: {
2920
+ params: z.strictObject({ siteId: realtimeSchemas.publicSiteId }),
2921
+ query: dateRangeQuery,
2922
+ headers: requestHeaders,
2923
+ body: null
2924
+ },
2925
+ responses: { 200: deviceGapResponse },
2926
+ errors: partnerSiteErrors,
2927
+ errorResponse: errorEnvelopeSchemas(partnerSiteErrors, realtimeSchemas.publicRequestId),
2928
+ resources: {
2929
+ reads: [{
2930
+ type: "site.analytics",
2931
+ idFrom: "params.siteId"
2932
+ }],
2933
+ changes: []
2934
+ },
2935
+ lifecycle: { introduced: "1.2.0" },
2936
+ docs: {
2937
+ summary: "Get desktop/mobile gap",
2938
+ description: "Returns per-day desktop vs mobile CTR/position metrics with gap trends for the window.",
2939
+ tags: ["Analytics"],
2940
+ examples: {
2941
+ request: {
2942
+ params: { siteId: "s_01" },
2943
+ query: {
2944
+ startDate: "2026-06-01",
2945
+ endDate: "2026-06-28"
2946
+ }
2947
+ },
2948
+ response: {
2949
+ data: {
2950
+ daily: [],
2951
+ summary: null,
2952
+ meta: {
2953
+ siteUrl: "sc-domain:example.com",
2954
+ syncStatus: "synced"
2955
+ }
2956
+ },
2957
+ meta: {
2958
+ requestId: "req_01",
2959
+ surface: "partner",
2960
+ version: "1.0"
2961
+ }
2962
+ }
2963
+ }
2964
+ }
2965
+ }),
2966
+ getKeywordBreadth: defineHttpOperation({
2967
+ id: "partner.sites.keyword.breadth.get",
2968
+ method: "GET",
2969
+ path: "/sites/{siteId}/keyword-breadth",
2970
+ visibility: "public",
2971
+ semantics: {
2972
+ kind: "query",
2973
+ sideEffects: "none",
2974
+ idempotent: true,
2975
+ retry: "idempotent",
2976
+ readConsistency: "primary"
2977
+ },
2978
+ auth: {
2979
+ credentials: ["user_key", "partner_key"],
2980
+ scopes: ["analytics:read"],
2981
+ ownership: [{
2982
+ credential: "user_key",
2983
+ rule: "authorized_site"
2984
+ }, {
2985
+ credential: "partner_key",
2986
+ rule: "authorized_site"
2987
+ }]
2988
+ },
2989
+ request: {
2990
+ params: z.strictObject({ siteId: realtimeSchemas.publicSiteId }),
2991
+ query: dateRangeQuery,
2992
+ headers: requestHeaders,
2993
+ body: null
2994
+ },
2995
+ responses: { 200: keywordBreadthResponse },
2996
+ errors: partnerSiteErrors,
2997
+ errorResponse: errorEnvelopeSchemas(partnerSiteErrors, realtimeSchemas.publicRequestId),
2998
+ resources: {
2999
+ reads: [{
3000
+ type: "site.analytics",
3001
+ idFrom: "params.siteId"
3002
+ }],
3003
+ changes: []
3004
+ },
3005
+ lifecycle: { introduced: "1.2.0" },
3006
+ docs: {
3007
+ summary: "Get keyword breadth",
3008
+ description: "Returns the pages-per-keyword-count distribution with fragile and authority page lists for the window.",
3009
+ tags: ["Analytics"],
3010
+ examples: {
3011
+ request: {
3012
+ params: { siteId: "s_01" },
3013
+ query: {
3014
+ startDate: "2026-06-01",
3015
+ endDate: "2026-06-28"
3016
+ }
3017
+ },
3018
+ response: {
3019
+ data: {
3020
+ distribution: [],
3021
+ fragilePages: [],
3022
+ authorityPages: [],
3023
+ summary: {
3024
+ totalPages: 0,
3025
+ avgKeywordsPerPage: 0,
3026
+ fragileCount: 0,
3027
+ authorityCount: 0
3028
+ },
3029
+ meta: {
3030
+ siteUrl: "sc-domain:example.com",
3031
+ syncStatus: "synced"
3032
+ }
3033
+ },
3034
+ meta: {
3035
+ requestId: "req_01",
3036
+ surface: "partner",
3037
+ version: "1.0"
3038
+ }
3039
+ }
3040
+ }
3041
+ }
3042
+ }),
3043
+ getPositionDistribution: defineHttpOperation({
3044
+ id: "partner.sites.position.distribution.get",
3045
+ method: "GET",
3046
+ path: "/sites/{siteId}/position-distribution",
3047
+ visibility: "public",
3048
+ semantics: {
3049
+ kind: "query",
3050
+ sideEffects: "none",
3051
+ idempotent: true,
3052
+ retry: "idempotent",
3053
+ readConsistency: "primary"
3054
+ },
3055
+ auth: {
3056
+ credentials: ["user_key", "partner_key"],
3057
+ scopes: ["analytics:read"],
3058
+ ownership: [{
3059
+ credential: "user_key",
3060
+ rule: "authorized_site"
3061
+ }, {
3062
+ credential: "partner_key",
3063
+ rule: "authorized_site"
3064
+ }]
3065
+ },
3066
+ request: {
3067
+ params: z.strictObject({ siteId: realtimeSchemas.publicSiteId }),
3068
+ query: dateRangeQuery,
3069
+ headers: requestHeaders,
3070
+ body: null
3071
+ },
3072
+ responses: { 200: positionDistributionResponse },
3073
+ errors: partnerSiteErrors,
3074
+ errorResponse: errorEnvelopeSchemas(partnerSiteErrors, realtimeSchemas.publicRequestId),
3075
+ resources: {
3076
+ reads: [{
3077
+ type: "site.analytics",
3078
+ idFrom: "params.siteId"
3079
+ }],
3080
+ changes: []
3081
+ },
3082
+ lifecycle: { introduced: "1.2.0" },
3083
+ docs: {
3084
+ summary: "Get position distribution",
3085
+ description: "Returns the per-day keyword counts bucketed by average position for the window.",
3086
+ tags: ["Analytics"],
3087
+ examples: {
3088
+ request: {
3089
+ params: { siteId: "s_01" },
3090
+ query: {
3091
+ startDate: "2026-06-01",
3092
+ endDate: "2026-06-28"
3093
+ }
3094
+ },
3095
+ response: {
3096
+ data: {
3097
+ distribution: [],
3098
+ meta: {
3099
+ siteUrl: "sc-domain:example.com",
3100
+ syncStatus: "synced"
3101
+ }
3102
+ },
3103
+ meta: {
3104
+ requestId: "req_01",
3105
+ surface: "partner",
3106
+ version: "1.0"
3107
+ }
3108
+ }
3109
+ }
3110
+ }
3111
+ }),
3112
+ getTopAssociation: defineHttpOperation({
3113
+ id: "partner.sites.top.association.get",
3114
+ method: "GET",
3115
+ path: "/sites/{siteId}/top-association",
3116
+ visibility: "public",
3117
+ semantics: {
3118
+ kind: "query",
3119
+ sideEffects: "none",
3120
+ idempotent: true,
3121
+ retry: "idempotent",
3122
+ readConsistency: "primary"
3123
+ },
3124
+ auth: {
3125
+ credentials: ["user_key", "partner_key"],
3126
+ scopes: ["analytics:read"],
3127
+ ownership: [{
3128
+ credential: "user_key",
3129
+ rule: "authorized_site"
3130
+ }, {
3131
+ credential: "partner_key",
3132
+ rule: "authorized_site"
3133
+ }]
3134
+ },
3135
+ request: {
3136
+ params: z.strictObject({ siteId: realtimeSchemas.publicSiteId }),
3137
+ query: topAssociationQuery,
3138
+ headers: requestHeaders,
3139
+ body: null
3140
+ },
3141
+ responses: { 200: topAssociationResponse },
3142
+ errors: partnerSiteErrors,
3143
+ errorResponse: errorEnvelopeSchemas(partnerSiteErrors, realtimeSchemas.publicRequestId),
3144
+ resources: {
3145
+ reads: [{
3146
+ type: "site.analytics",
3147
+ idFrom: "params.siteId"
3148
+ }],
3149
+ changes: []
3150
+ },
3151
+ lifecycle: { introduced: "1.2.0" },
3152
+ docs: {
3153
+ summary: "Get top association",
3154
+ description: "Returns the single best-performing page for a keyword, or keyword for a page, over the window.",
3155
+ tags: ["Analytics"],
3156
+ examples: {
3157
+ request: {
3158
+ params: { siteId: "s_01" },
3159
+ query: {
3160
+ type: "topKeyword",
3161
+ identifier: "/docs",
3162
+ startDate: "2026-06-01",
3163
+ endDate: "2026-06-28"
3164
+ }
3165
+ },
3166
+ response: {
3167
+ data: { value: null },
3168
+ meta: {
3169
+ requestId: "req_01",
3170
+ surface: "partner",
3171
+ version: "1.0"
3172
+ }
3173
+ }
3174
+ }
3175
+ }
3176
+ }),
3177
+ getIndexPercent: defineHttpOperation({
3178
+ id: "partner.sites.index.percent.get",
3179
+ method: "GET",
3180
+ path: "/sites/{siteId}/index-percent",
3181
+ visibility: "public",
3182
+ semantics: {
3183
+ kind: "query",
3184
+ sideEffects: "none",
3185
+ idempotent: true,
3186
+ retry: "idempotent",
3187
+ readConsistency: "primary"
3188
+ },
3189
+ auth: {
3190
+ credentials: ["user_key", "partner_key"],
3191
+ scopes: ["indexing:read"],
3192
+ ownership: [{
3193
+ credential: "user_key",
3194
+ rule: "authorized_site"
3195
+ }, {
3196
+ credential: "partner_key",
3197
+ rule: "authorized_site"
3198
+ }]
3199
+ },
3200
+ request: {
3201
+ params: z.strictObject({ siteId: realtimeSchemas.publicSiteId }),
3202
+ query: indexPercentQuery,
3203
+ headers: requestHeaders,
3204
+ body: null
3205
+ },
3206
+ responses: { 200: indexPercentResponse },
3207
+ errors: partnerSiteErrors,
3208
+ errorResponse: errorEnvelopeSchemas(partnerSiteErrors, realtimeSchemas.publicRequestId),
3209
+ resources: {
3210
+ reads: [{
3211
+ type: "site.indexing",
3212
+ idFrom: "params.siteId"
3213
+ }, {
3214
+ type: "site.sitemaps",
3215
+ idFrom: "params.siteId"
3216
+ }],
3217
+ changes: []
3218
+ },
3219
+ lifecycle: { introduced: "1.2.0" },
3220
+ docs: {
3221
+ summary: "Get sitemap index-percent",
3222
+ description: "Returns the sitemap visibility trend, invisible URLs, orphan pages, and per-sitemap counts.",
3223
+ tags: ["Indexing"],
3224
+ examples: {
3225
+ request: {
3226
+ params: { siteId: "s_01" },
3227
+ query: { invisibleLimit: 100 }
3228
+ },
3229
+ response: {
3230
+ data: {
3231
+ trend: [],
3232
+ invisibleUrls: [],
3233
+ invisibleCount: 0,
3234
+ orphanPages: [],
3235
+ orphanCount: 0,
3236
+ sitemaps: [],
3237
+ summary: {
3238
+ currentPercent: 0,
3239
+ totalSitemapUrls: 0,
3240
+ visibleUrls: 0,
3241
+ change7d: null,
3242
+ change28d: null,
3243
+ dataDate: "2026-06-28"
3244
+ },
3245
+ meta: {
3246
+ siteUrl: "sc-domain:example.com",
3247
+ syncStatus: "synced",
3248
+ newestDateSynced: null
3249
+ }
3250
+ },
3251
+ meta: {
3252
+ requestId: "req_01",
3253
+ surface: "partner",
3254
+ version: "1.0"
3255
+ }
3256
+ }
3257
+ }
3258
+ }
3259
+ }),
3260
+ createSitemapAction: defineHttpOperation({
3261
+ id: "partner.sites.sitemaps.action.create",
3262
+ method: "POST",
3263
+ path: "/sites/{siteId}/sitemaps/actions",
3264
+ visibility: "public",
3265
+ semantics: {
3266
+ kind: "mutation",
3267
+ sideEffects: "state",
3268
+ idempotent: false,
3269
+ retry: "never",
3270
+ readConsistency: null
3271
+ },
3272
+ auth: {
3273
+ credentials: ["user_key", "partner_key"],
3274
+ scopes: ["sitemaps:write"],
3275
+ ownership: [{
3276
+ credential: "user_key",
3277
+ rule: "authorized_site"
3278
+ }, {
3279
+ credential: "partner_key",
3280
+ rule: "authorized_site"
3281
+ }]
3282
+ },
3283
+ request: {
3284
+ params: z.strictObject({ siteId: realtimeSchemas.publicSiteId }),
3285
+ query: null,
3286
+ headers: requestHeaders,
3287
+ body: sitemapActionRequest
3288
+ },
3289
+ responses: { 200: sitemapActionResponse },
3290
+ errors: partnerSiteErrors,
3291
+ errorResponse: errorEnvelopeSchemas(partnerSiteErrors, realtimeSchemas.publicRequestId),
3292
+ resources: {
3293
+ reads: [{
3294
+ type: "site.sitemaps",
3295
+ idFrom: "params.siteId"
3296
+ }],
3297
+ changes: [{
3298
+ type: "site.sitemaps",
3299
+ idFrom: "params.siteId"
3300
+ }]
3301
+ },
3302
+ lifecycle: { introduced: "1.3.0" },
3303
+ docs: {
3304
+ summary: "Run a sitemap action",
3305
+ description: "Submits, deletes, refreshes, or auto-discovers a site's sitemap; the response shape depends on the requested action.",
3306
+ tags: ["Sitemaps"],
3307
+ examples: {
3308
+ request: {
3309
+ params: { siteId: "s_01" },
3310
+ body: { action: "refresh" }
3311
+ },
3312
+ response: {
3313
+ data: {
3314
+ success: true,
3315
+ action: "refreshed",
3316
+ sitemapCount: 0,
3317
+ changed: false
3318
+ },
3319
+ meta: {
3320
+ requestId: "req_01",
3321
+ surface: "partner",
3322
+ version: "1.0"
3323
+ }
3324
+ }
3325
+ }
3326
+ }
3327
+ }),
3328
+ querySitemapMembership: defineHttpOperation({
3329
+ id: "partner.sites.sitemaps.membership.query",
3330
+ method: "POST",
3331
+ path: "/sites/{siteId}/sitemaps/membership",
3332
+ visibility: "public",
3333
+ semantics: {
3334
+ kind: "query",
3335
+ sideEffects: "none",
3336
+ idempotent: true,
3337
+ retry: "idempotent",
3338
+ readConsistency: "primary"
3339
+ },
3340
+ auth: {
3341
+ credentials: ["user_key", "partner_key"],
3342
+ scopes: ["sitemaps:read"],
3343
+ ownership: [{
3344
+ credential: "user_key",
3345
+ rule: "authorized_site"
3346
+ }, {
3347
+ credential: "partner_key",
3348
+ rule: "authorized_site"
3349
+ }]
3350
+ },
3351
+ request: {
3352
+ params: z.strictObject({ siteId: realtimeSchemas.publicSiteId }),
3353
+ query: null,
3354
+ headers: requestHeaders,
3355
+ body: sitemapMembershipRequest
3356
+ },
3357
+ responses: { 200: sitemapMembershipResponse },
3358
+ errors: partnerSiteErrors,
3359
+ errorResponse: errorEnvelopeSchemas(partnerSiteErrors, realtimeSchemas.publicRequestId),
3360
+ resources: {
3361
+ reads: [{
3362
+ type: "site.sitemaps",
3363
+ idFrom: "params.siteId"
3364
+ }],
3365
+ changes: []
3366
+ },
3367
+ lifecycle: { introduced: "1.3.0" },
3368
+ docs: {
3369
+ summary: "Query sitemap membership",
3370
+ description: "Checks whether each requested URL is present in the site's known sitemaps, subject to a freshness cap.",
3371
+ tags: ["Sitemaps"],
3372
+ examples: {
3373
+ request: {
3374
+ params: { siteId: "s_01" },
3375
+ body: {
3376
+ urls: ["https://example.com/"],
3377
+ maxAgeDays: 30
3378
+ }
3379
+ },
3380
+ response: {
3381
+ data: {
3382
+ urls: [],
3383
+ meta: {
3384
+ available: true,
3385
+ reason: null,
3386
+ requested: 1,
3387
+ checked: 1,
3388
+ matched: 0,
3389
+ newestFetchedAt: null
3390
+ }
3391
+ },
3392
+ meta: {
3393
+ requestId: "req_01",
3394
+ surface: "partner",
3395
+ version: "1.0"
3396
+ }
3397
+ }
3398
+ }
3399
+ }
3400
+ }),
3401
+ createTeam: defineHttpOperation({
3402
+ id: "partner.teams.create",
3403
+ method: "POST",
3404
+ path: "/teams",
3405
+ visibility: "public",
3406
+ semantics: {
3407
+ kind: "mutation",
3408
+ sideEffects: "state",
3409
+ idempotent: false,
3410
+ retry: "never",
3411
+ readConsistency: null
3412
+ },
3413
+ auth: {
3414
+ credentials: ["partner_key"],
3415
+ scopes: ["teams:write"],
3416
+ ownership: [{
3417
+ credential: "partner_key",
3418
+ rule: "partner_tenant"
3419
+ }]
3420
+ },
3421
+ request: {
3422
+ params: null,
3423
+ query: null,
3424
+ headers: requestHeaders,
3425
+ body: createTeamRequest
3426
+ },
3427
+ responses: { 200: teamCreatedResponse },
3428
+ errors: partnerUserErrors,
3429
+ errorResponse: errorEnvelopeSchemas(partnerUserErrors, realtimeSchemas.publicRequestId),
3430
+ resources: {
3431
+ reads: [],
3432
+ changes: [{
3433
+ type: "partner.team",
3434
+ idFrom: "principal.id"
3435
+ }]
3436
+ },
3437
+ lifecycle: { introduced: "1.3.0" },
3438
+ docs: {
3439
+ summary: "Create a team",
3440
+ description: "Creates a team owned by a user linked to the authenticated partner tenant.",
3441
+ tags: ["Teams"],
3442
+ examples: {
3443
+ request: { body: {
3444
+ ownerUserId: "u_01",
3445
+ name: "Acme Team"
3446
+ } },
3447
+ response: {
3448
+ data: { team: {
3449
+ id: "t_01",
3450
+ ownerId: 1,
3451
+ name: "Acme Team",
3452
+ personalTeam: false,
3453
+ createdAt: 0,
3454
+ updatedAt: 0
3455
+ } },
3456
+ meta: {
3457
+ requestId: "req_01",
3458
+ surface: "partner",
3459
+ version: "1.0"
3460
+ }
3461
+ }
3462
+ }
3463
+ }
3464
+ }),
3465
+ renameTeam: defineHttpOperation({
3466
+ id: "partner.teams.rename",
3467
+ method: "PATCH",
3468
+ path: "/teams/{teamId}",
3469
+ visibility: "public",
3470
+ semantics: {
3471
+ kind: "mutation",
3472
+ sideEffects: "state",
3473
+ idempotent: true,
3474
+ retry: "idempotent",
3475
+ readConsistency: null
3476
+ },
3477
+ auth: {
3478
+ credentials: ["partner_key"],
3479
+ scopes: ["teams:write"],
3480
+ ownership: [{
3481
+ credential: "partner_key",
3482
+ rule: "partner_tenant"
3483
+ }]
3484
+ },
3485
+ request: {
3486
+ params: z.strictObject({ teamId: realtimeSchemas.publicTeamId }),
3487
+ query: null,
3488
+ headers: requestHeaders,
3489
+ body: renameTeamRequest
3490
+ },
3491
+ responses: { 200: teamRenamedResponse },
3492
+ errors: partnerUserErrors,
3493
+ errorResponse: errorEnvelopeSchemas(partnerUserErrors, realtimeSchemas.publicRequestId),
3494
+ resources: {
3495
+ reads: [{
3496
+ type: "partner.team",
3497
+ idFrom: "params.teamId"
3498
+ }],
3499
+ changes: [{
3500
+ type: "partner.team",
3501
+ idFrom: "params.teamId"
3502
+ }]
3503
+ },
3504
+ lifecycle: { introduced: "1.3.0" },
3505
+ docs: {
3506
+ summary: "Rename a team",
3507
+ description: "Renames a team owned by the authenticated partner tenant.",
3508
+ tags: ["Teams"],
3509
+ examples: {
3510
+ request: {
3511
+ params: { teamId: "t_01" },
3512
+ body: { name: "New name" }
3513
+ },
3514
+ response: {
3515
+ data: {
3516
+ ok: true,
3517
+ name: "New name"
3518
+ },
3519
+ meta: {
3520
+ requestId: "req_01",
3521
+ surface: "partner",
3522
+ version: "1.0"
3523
+ }
3524
+ }
3525
+ }
3526
+ }
3527
+ }),
3528
+ deleteTeam: defineHttpOperation({
3529
+ id: "partner.teams.delete",
3530
+ method: "DELETE",
3531
+ path: "/teams/{teamId}",
3532
+ visibility: "public",
3533
+ semantics: {
3534
+ kind: "mutation",
3535
+ sideEffects: "state",
3536
+ idempotent: true,
3537
+ retry: "idempotent",
3538
+ readConsistency: null
3539
+ },
3540
+ auth: {
3541
+ credentials: ["partner_key"],
3542
+ scopes: ["teams:write"],
3543
+ ownership: [{
3544
+ credential: "partner_key",
3545
+ rule: "partner_tenant"
3546
+ }]
3547
+ },
3548
+ request: {
3549
+ params: z.strictObject({ teamId: realtimeSchemas.publicTeamId }),
3550
+ query: null,
3551
+ headers: requestHeaders,
3552
+ body: null
3553
+ },
3554
+ responses: { 200: teamDeletedResponse },
3555
+ errors: partnerUserErrors,
3556
+ errorResponse: errorEnvelopeSchemas(partnerUserErrors, realtimeSchemas.publicRequestId),
3557
+ resources: {
3558
+ reads: [{
3559
+ type: "partner.team",
3560
+ idFrom: "params.teamId"
3561
+ }],
3562
+ changes: [{
3563
+ type: "partner.team",
3564
+ idFrom: "params.teamId"
3565
+ }]
3566
+ },
3567
+ lifecycle: { introduced: "1.3.0" },
3568
+ docs: {
3569
+ summary: "Delete a team",
3570
+ description: "Deletes a team owned by the authenticated partner tenant.",
3571
+ tags: ["Teams"],
3572
+ examples: {
3573
+ request: { params: { teamId: "t_01" } },
3574
+ response: {
3575
+ data: { ok: true },
3576
+ meta: {
3577
+ requestId: "req_01",
3578
+ surface: "partner",
3579
+ version: "1.0"
3580
+ }
3581
+ }
3582
+ }
3583
+ }
3584
+ }),
3585
+ listTeamMembers: defineHttpOperation({
3586
+ id: "partner.teams.members.list",
3587
+ method: "GET",
3588
+ path: "/teams/{teamId}/members",
3589
+ visibility: "public",
3590
+ semantics: {
3591
+ kind: "query",
3592
+ sideEffects: "none",
3593
+ idempotent: true,
3594
+ retry: "idempotent",
3595
+ readConsistency: "primary"
3596
+ },
3597
+ auth: {
3598
+ credentials: ["partner_key"],
3599
+ scopes: ["teams:read"],
3600
+ ownership: [{
3601
+ credential: "partner_key",
3602
+ rule: "partner_tenant"
3603
+ }]
3604
+ },
3605
+ request: {
3606
+ params: z.strictObject({ teamId: realtimeSchemas.publicTeamId }),
3607
+ query: null,
3608
+ headers: requestHeaders,
3609
+ body: null
3610
+ },
3611
+ responses: { 200: teamMembersResponse },
3612
+ errors: partnerUserErrors,
3613
+ errorResponse: errorEnvelopeSchemas(partnerUserErrors, realtimeSchemas.publicRequestId),
3614
+ resources: {
3615
+ reads: [{
3616
+ type: "partner.team",
3617
+ idFrom: "params.teamId"
3618
+ }],
3619
+ changes: []
3620
+ },
3621
+ lifecycle: { introduced: "1.3.0" },
3622
+ docs: {
3623
+ summary: "List team members",
3624
+ description: "Lists the members of a team owned by the authenticated partner tenant.",
3625
+ tags: ["Teams"],
3626
+ examples: {
3627
+ request: { params: { teamId: "t_01" } },
3628
+ response: {
3629
+ data: { members: [] },
3630
+ meta: {
3631
+ requestId: "req_01",
3632
+ surface: "partner",
3633
+ version: "1.0"
3634
+ }
3635
+ }
3636
+ }
3637
+ }
3638
+ }),
3639
+ addTeamMember: defineHttpOperation({
3640
+ id: "partner.teams.members.add",
3641
+ method: "POST",
3642
+ path: "/teams/{teamId}/members",
3643
+ visibility: "public",
3644
+ semantics: {
3645
+ kind: "mutation",
3646
+ sideEffects: "state",
3647
+ idempotent: true,
3648
+ retry: "idempotent",
3649
+ readConsistency: null
3650
+ },
3651
+ auth: {
3652
+ credentials: ["partner_key"],
3653
+ scopes: ["teams:write"],
3654
+ ownership: [{
3655
+ credential: "partner_key",
3656
+ rule: "partner_tenant"
3657
+ }]
3658
+ },
3659
+ request: {
3660
+ params: z.strictObject({ teamId: realtimeSchemas.publicTeamId }),
3661
+ query: null,
3662
+ headers: requestHeaders,
3663
+ body: addTeamMemberRequest
3664
+ },
3665
+ responses: { 200: teamMemberAddedResponse },
3666
+ errors: partnerUserErrors,
3667
+ errorResponse: errorEnvelopeSchemas(partnerUserErrors, realtimeSchemas.publicRequestId),
3668
+ resources: {
3669
+ reads: [{
3670
+ type: "partner.team",
3671
+ idFrom: "params.teamId"
3672
+ }],
3673
+ changes: [{
3674
+ type: "partner.team",
3675
+ idFrom: "params.teamId"
3676
+ }]
3677
+ },
3678
+ lifecycle: { introduced: "1.3.0" },
3679
+ docs: {
3680
+ summary: "Add a team member",
3681
+ description: "Adds a user to a team owned by the authenticated partner tenant, or reports it was already a member.",
3682
+ tags: ["Teams"],
3683
+ examples: {
3684
+ request: {
3685
+ params: { teamId: "t_01" },
3686
+ body: {
3687
+ userId: "u_02",
3688
+ role: "editor"
3689
+ }
3690
+ },
3691
+ response: {
3692
+ data: {
3693
+ ok: true,
3694
+ role: "editor"
3695
+ },
3696
+ meta: {
3697
+ requestId: "req_01",
3698
+ surface: "partner",
3699
+ version: "1.0"
3700
+ }
3701
+ }
3702
+ }
3703
+ }
3704
+ }),
3705
+ updateTeamMemberRole: defineHttpOperation({
3706
+ id: "partner.teams.members.role.update",
3707
+ method: "PATCH",
3708
+ path: "/teams/{teamId}/members/{userId}",
3709
+ visibility: "public",
3710
+ semantics: {
3711
+ kind: "mutation",
3712
+ sideEffects: "state",
3713
+ idempotent: true,
3714
+ retry: "idempotent",
3715
+ readConsistency: null
3716
+ },
3717
+ auth: {
3718
+ credentials: ["partner_key"],
3719
+ scopes: ["teams:write"],
3720
+ ownership: [{
3721
+ credential: "partner_key",
3722
+ rule: "partner_tenant"
3723
+ }]
3724
+ },
3725
+ request: {
3726
+ params: z.strictObject({
3727
+ teamId: realtimeSchemas.publicTeamId,
3728
+ userId: realtimeSchemas.publicUserId
3729
+ }),
3730
+ query: null,
3731
+ headers: requestHeaders,
3732
+ body: updateTeamMemberRoleRequest
3733
+ },
3734
+ responses: { 200: teamMemberRoleResponse },
3735
+ errors: partnerUserErrors,
3736
+ errorResponse: errorEnvelopeSchemas(partnerUserErrors, realtimeSchemas.publicRequestId),
3737
+ resources: {
3738
+ reads: [{
3739
+ type: "partner.team",
3740
+ idFrom: "params.teamId"
3741
+ }, {
3742
+ type: "partner.user",
3743
+ idFrom: "params.userId"
3744
+ }],
3745
+ changes: [{
3746
+ type: "partner.team",
3747
+ idFrom: "params.teamId"
3748
+ }, {
3749
+ type: "partner.user",
3750
+ idFrom: "params.userId"
3751
+ }]
3752
+ },
3753
+ lifecycle: { introduced: "1.3.0" },
3754
+ docs: {
3755
+ summary: "Update a team member role",
3756
+ description: "Updates the role of an existing member of a team owned by the authenticated partner tenant.",
3757
+ tags: ["Teams"],
3758
+ examples: {
3759
+ request: {
3760
+ params: {
3761
+ teamId: "t_01",
3762
+ userId: "u_02"
3763
+ },
3764
+ body: { role: "admin" }
3765
+ },
3766
+ response: {
3767
+ data: {
3768
+ ok: true,
3769
+ role: "admin"
3770
+ },
3771
+ meta: {
3772
+ requestId: "req_01",
3773
+ surface: "partner",
3774
+ version: "1.0"
3775
+ }
3776
+ }
3777
+ }
3778
+ }
3779
+ }),
3780
+ removeTeamMember: defineHttpOperation({
3781
+ id: "partner.teams.members.remove",
3782
+ method: "DELETE",
3783
+ path: "/teams/{teamId}/members/{userId}",
3784
+ visibility: "public",
3785
+ semantics: {
3786
+ kind: "mutation",
3787
+ sideEffects: "state",
3788
+ idempotent: true,
3789
+ retry: "idempotent",
3790
+ readConsistency: null
3791
+ },
3792
+ auth: {
3793
+ credentials: ["partner_key"],
3794
+ scopes: ["teams:write"],
3795
+ ownership: [{
3796
+ credential: "partner_key",
3797
+ rule: "partner_tenant"
3798
+ }]
3799
+ },
3800
+ request: {
3801
+ params: z.strictObject({
3802
+ teamId: realtimeSchemas.publicTeamId,
3803
+ userId: realtimeSchemas.publicUserId
3804
+ }),
3805
+ query: null,
3806
+ headers: requestHeaders,
3807
+ body: null
3808
+ },
3809
+ responses: { 200: teamMemberRemovedResponse },
3810
+ errors: partnerUserErrors,
3811
+ errorResponse: errorEnvelopeSchemas(partnerUserErrors, realtimeSchemas.publicRequestId),
3812
+ resources: {
3813
+ reads: [{
3814
+ type: "partner.team",
3815
+ idFrom: "params.teamId"
3816
+ }, {
3817
+ type: "partner.user",
3818
+ idFrom: "params.userId"
3819
+ }],
3820
+ changes: [{
3821
+ type: "partner.team",
3822
+ idFrom: "params.teamId"
3823
+ }, {
3824
+ type: "partner.user",
3825
+ idFrom: "params.userId"
3826
+ }]
3827
+ },
3828
+ lifecycle: { introduced: "1.3.0" },
3829
+ docs: {
3830
+ summary: "Remove a team member",
3831
+ description: "Removes a member from a team owned by the authenticated partner tenant.",
3832
+ tags: ["Teams"],
3833
+ examples: {
3834
+ request: { params: {
3835
+ teamId: "t_01",
3836
+ userId: "u_02"
3837
+ } },
3838
+ response: {
3839
+ data: { ok: true },
3840
+ meta: {
3841
+ requestId: "req_01",
3842
+ surface: "partner",
3843
+ version: "1.0"
3844
+ }
3845
+ }
3846
+ }
3847
+ }
3848
+ }),
3849
+ updateSiteTeam: defineHttpOperation({
3850
+ id: "partner.sites.team.update",
3851
+ method: "PATCH",
3852
+ path: "/sites/{siteId}/team",
3853
+ visibility: "public",
3854
+ semantics: {
3855
+ kind: "mutation",
3856
+ sideEffects: "state",
3857
+ idempotent: true,
3858
+ retry: "idempotent",
3859
+ readConsistency: null
3860
+ },
3861
+ auth: {
3862
+ credentials: ["partner_key"],
3863
+ scopes: ["sites:write"],
3864
+ ownership: [{
3865
+ credential: "partner_key",
3866
+ rule: "authorized_site"
3867
+ }]
3868
+ },
3869
+ request: {
3870
+ params: z.strictObject({ siteId: realtimeSchemas.publicSiteId }),
3871
+ query: null,
3872
+ headers: requestHeaders,
3873
+ body: bindSiteTeamRequest
3874
+ },
3875
+ responses: { 200: siteTeamBindingResponse },
3876
+ errors: partnerSiteErrors,
3877
+ errorResponse: errorEnvelopeSchemas(partnerSiteErrors, realtimeSchemas.publicRequestId),
3878
+ resources: {
3879
+ reads: [{
3880
+ type: "site.registration",
3881
+ idFrom: "params.siteId"
3882
+ }],
3883
+ changes: [{
3884
+ type: "site.registration",
3885
+ idFrom: "params.siteId"
3886
+ }]
3887
+ },
3888
+ lifecycle: { introduced: "1.3.0" },
3889
+ docs: {
3890
+ summary: "Bind a site to a team",
3891
+ description: "Binds or unbinds (teamId: null) a partner-owned site to a partner-owned team.",
3892
+ tags: ["Teams"],
3893
+ examples: {
3894
+ request: {
3895
+ params: { siteId: "s_01" },
3896
+ body: { teamId: "t_01" }
3897
+ },
3898
+ response: {
3899
+ data: {
3900
+ ok: true,
3901
+ teamId: "t_01"
3902
+ },
3903
+ meta: {
3904
+ requestId: "req_01",
3905
+ surface: "partner",
3906
+ version: "1.0"
3907
+ }
3908
+ }
3909
+ }
3910
+ }
3911
+ }),
3912
+ getTeamCatalog: defineHttpOperation({
3913
+ id: "partner.teams.catalog.get",
3914
+ method: "GET",
3915
+ path: "/teams/{teamId}/catalog",
3916
+ visibility: "public",
3917
+ semantics: {
3918
+ kind: "query",
3919
+ sideEffects: "none",
3920
+ idempotent: true,
3921
+ retry: "idempotent",
3922
+ readConsistency: "primary"
3923
+ },
3924
+ auth: {
3925
+ credentials: ["partner_key"],
3926
+ scopes: ["teams:read"],
3927
+ ownership: [{
3928
+ credential: "partner_key",
3929
+ rule: "partner_tenant"
3930
+ }]
3931
+ },
3932
+ request: {
3933
+ params: z.strictObject({ teamId: realtimeSchemas.publicTeamId }),
3934
+ query: null,
3935
+ headers: requestHeaders,
3936
+ body: null
3937
+ },
3938
+ responses: { 200: teamCatalogResponse },
3939
+ errors: partnerUserErrors,
3940
+ errorResponse: errorEnvelopeSchemas(partnerUserErrors, realtimeSchemas.publicRequestId),
3941
+ resources: {
3942
+ reads: [{
3943
+ type: "partner.team",
3944
+ idFrom: "params.teamId"
3945
+ }],
3946
+ changes: []
3947
+ },
3948
+ lifecycle: { introduced: "1.3.0" },
3949
+ docs: {
3950
+ summary: "Get a team catalog",
3951
+ description: "Returns the Iceberg catalog reference bound to a partner-owned team, if any.",
3952
+ tags: ["Teams"],
3953
+ examples: {
3954
+ request: { params: { teamId: "t_01" } },
3955
+ response: {
3956
+ data: {
3957
+ teamId: "t_01",
3958
+ catalogUri: null,
3959
+ warehouse: null,
3960
+ bucket: null,
3961
+ namespace: null,
3962
+ provisioningState: null,
3963
+ keyEncoding: null,
3964
+ catalogTablesReady: false,
3965
+ readsEnabled: false
3966
+ },
3967
+ meta: {
3968
+ requestId: "req_01",
3969
+ surface: "partner",
3970
+ version: "1.0"
3971
+ }
3972
+ }
3973
+ }
3974
+ }
3975
+ }),
3976
+ bindTeamCatalog: defineHttpOperation({
3977
+ id: "partner.teams.catalog.bind",
3978
+ method: "POST",
3979
+ path: "/teams/{teamId}/catalog",
3980
+ visibility: "public",
3981
+ semantics: {
3982
+ kind: "mutation",
3983
+ sideEffects: "state",
3984
+ idempotent: true,
3985
+ retry: "idempotent",
3986
+ readConsistency: null
3987
+ },
3988
+ auth: {
3989
+ credentials: ["partner_key"],
3990
+ scopes: ["teams:write"],
3991
+ ownership: [{
3992
+ credential: "partner_key",
3993
+ rule: "partner_tenant"
3994
+ }]
3995
+ },
3996
+ request: {
3997
+ params: z.strictObject({ teamId: realtimeSchemas.publicTeamId }),
3998
+ query: null,
3999
+ headers: requestHeaders,
4000
+ body: bindTeamCatalogRequest
4001
+ },
4002
+ responses: { 200: teamCatalogBindResponse },
4003
+ errors: partnerUserErrors,
4004
+ errorResponse: errorEnvelopeSchemas(partnerUserErrors, realtimeSchemas.publicRequestId),
4005
+ resources: {
4006
+ reads: [{
4007
+ type: "partner.team",
4008
+ idFrom: "params.teamId"
4009
+ }],
4010
+ changes: [{
4011
+ type: "partner.team",
4012
+ idFrom: "params.teamId"
4013
+ }]
4014
+ },
4015
+ lifecycle: { introduced: "1.3.0" },
4016
+ docs: {
4017
+ summary: "Bind a team catalog",
4018
+ description: "Binds an Iceberg catalog (URI, warehouse, and optional bucket/namespace) to a partner-owned team.",
4019
+ tags: ["Teams"],
4020
+ examples: {
4021
+ request: {
4022
+ params: { teamId: "t_01" },
4023
+ body: {
4024
+ catalogUri: "gs://bucket/catalog",
4025
+ warehouse: "primary",
4026
+ namespace: "ns1",
4027
+ bucket: "bucket1"
4028
+ }
4029
+ },
4030
+ response: {
4031
+ data: {
4032
+ teamId: "t_01",
4033
+ status: "ready",
4034
+ catalogUri: "gs://bucket/catalog",
4035
+ warehouse: "primary",
4036
+ bucket: "bucket1",
4037
+ namespace: "ns1"
4038
+ },
4039
+ meta: {
4040
+ requestId: "req_01",
4041
+ surface: "partner",
4042
+ version: "1.0"
4043
+ }
4044
+ }
4045
+ }
4046
+ }
4047
+ }),
4048
+ getSiteIntIdCrosswalk: defineHttpOperation({
4049
+ id: "partner.users.sites.crosswalk.get",
4050
+ method: "GET",
4051
+ path: "/users/{userId}/sites/crosswalk",
4052
+ visibility: "public",
4053
+ semantics: {
4054
+ kind: "query",
4055
+ sideEffects: "none",
4056
+ idempotent: true,
4057
+ retry: "idempotent",
4058
+ readConsistency: "primary"
4059
+ },
4060
+ auth: {
4061
+ credentials: ["user_key", "partner_key"],
4062
+ scopes: ["sites:read"],
4063
+ ownership: [{
4064
+ credential: "user_key",
4065
+ rule: "self"
4066
+ }, {
4067
+ credential: "partner_key",
4068
+ rule: "linked_user"
4069
+ }]
4070
+ },
4071
+ request: {
4072
+ params: z.strictObject({ userId: realtimeSchemas.publicUserId }),
4073
+ query: null,
4074
+ headers: requestHeaders,
4075
+ body: null
4076
+ },
4077
+ responses: { 200: siteIntIdCrosswalkResponse },
4078
+ errors: partnerUserErrors,
4079
+ errorResponse: errorEnvelopeSchemas(partnerUserErrors, realtimeSchemas.publicRequestId),
4080
+ resources: {
4081
+ reads: [{
4082
+ type: "partner.user",
4083
+ idFrom: "params.userId"
4084
+ }, {
4085
+ type: "user.sites",
4086
+ idFrom: "params.userId"
4087
+ }],
4088
+ changes: []
4089
+ },
4090
+ lifecycle: { introduced: "1.3.0" },
4091
+ docs: {
4092
+ summary: "Get the site int-id crosswalk",
4093
+ description: "Maps a user's public site IDs to their internal integer IDs, for partners that need the legacy numeric identifier.",
4094
+ tags: ["Sites"],
4095
+ examples: {
4096
+ request: { params: { userId: "u_01" } },
4097
+ response: {
4098
+ data: {
4099
+ crosswalk: {},
4100
+ sites: []
4101
+ },
4102
+ meta: {
4103
+ requestId: "req_01",
4104
+ surface: "partner",
4105
+ version: "1.0"
4106
+ }
4107
+ }
4108
+ }
4109
+ }
4110
+ }),
4111
+ deleteUser: defineHttpOperation({
4112
+ id: "partner.users.delete",
4113
+ method: "DELETE",
4114
+ path: "/users/{userId}",
4115
+ visibility: "public",
4116
+ semantics: {
4117
+ kind: "mutation",
4118
+ sideEffects: "state",
4119
+ idempotent: true,
4120
+ retry: "idempotent",
4121
+ readConsistency: null
4122
+ },
4123
+ auth: {
4124
+ credentials: ["partner_key"],
4125
+ scopes: ["users:write"],
4126
+ ownership: [{
4127
+ credential: "partner_key",
4128
+ rule: "linked_user"
4129
+ }]
4130
+ },
4131
+ request: {
4132
+ params: z.strictObject({ userId: realtimeSchemas.publicUserId }),
4133
+ query: null,
4134
+ headers: requestHeaders,
4135
+ body: null
4136
+ },
4137
+ responses: { 200: deletePartnerUserResponse },
4138
+ errors: partnerUserErrors,
4139
+ errorResponse: errorEnvelopeSchemas(partnerUserErrors, realtimeSchemas.publicRequestId),
4140
+ resources: {
4141
+ reads: [{
4142
+ type: "partner.user",
4143
+ idFrom: "params.userId"
4144
+ }],
4145
+ changes: [{
4146
+ type: "partner.user",
4147
+ idFrom: "params.userId"
4148
+ }, {
4149
+ type: "user.sites",
4150
+ idFrom: "params.userId"
4151
+ }]
4152
+ },
4153
+ lifecycle: { introduced: "1.3.0" },
4154
+ docs: {
4155
+ summary: "Delete a partner user",
4156
+ description: "Queues cascade deletion of a partner-linked user and their sites.",
4157
+ tags: ["Users"],
4158
+ examples: {
4159
+ request: { params: { userId: "u_01" } },
4160
+ response: {
4161
+ data: {
4162
+ ok: true,
4163
+ queued: true,
4164
+ userId: 1,
4165
+ publicId: "u_01"
4166
+ },
4167
+ meta: {
4168
+ requestId: "req_01",
4169
+ surface: "partner",
4170
+ version: "1.0"
4171
+ }
4172
+ }
4173
+ }
4174
+ }
4175
+ }),
4176
+ createVerificationToken: defineHttpOperation({
4177
+ id: "partner.users.verification.token.create",
4178
+ method: "POST",
4179
+ path: "/users/{userId}/verification-token",
4180
+ visibility: "public",
4181
+ semantics: {
4182
+ kind: "query",
4183
+ sideEffects: "none",
4184
+ idempotent: true,
4185
+ retry: "idempotent",
4186
+ readConsistency: "primary"
4187
+ },
4188
+ auth: {
4189
+ credentials: ["user_key", "partner_key"],
4190
+ scopes: ["sites:write"],
4191
+ ownership: [{
4192
+ credential: "user_key",
4193
+ rule: "self"
4194
+ }, {
4195
+ credential: "partner_key",
4196
+ rule: "linked_user"
4197
+ }]
4198
+ },
4199
+ request: {
4200
+ params: z.strictObject({ userId: realtimeSchemas.publicUserId }),
4201
+ query: null,
4202
+ headers: requestHeaders,
4203
+ body: verificationTokenRequest
4204
+ },
4205
+ responses: { 200: verificationTokenResponse },
4206
+ errors: partnerUserErrors,
4207
+ errorResponse: errorEnvelopeSchemas(partnerUserErrors, realtimeSchemas.publicRequestId),
4208
+ resources: {
4209
+ reads: [{
4210
+ type: "partner.user",
4211
+ idFrom: "params.userId"
4212
+ }, {
4213
+ type: "user.sites",
4214
+ idFrom: "params.userId"
4215
+ }],
4216
+ changes: []
4217
+ },
4218
+ lifecycle: { introduced: "1.3.0" },
4219
+ docs: {
4220
+ summary: "Create a site verification token",
4221
+ description: "Mints a Search Console site-ownership verification token (meta tag or DNS record) for a not-yet-registered site.",
4222
+ tags: ["Sites"],
4223
+ examples: {
4224
+ request: {
4225
+ params: { userId: "u_01" },
4226
+ body: {
4227
+ siteUrl: "https://example.com",
4228
+ method: "DNS_TXT"
4229
+ }
4230
+ },
4231
+ response: {
4232
+ data: {
4233
+ siteUrl: "https://example.com",
4234
+ site: {
4235
+ type: "INET_DOMAIN",
4236
+ identifier: "example.com"
4237
+ },
4238
+ method: "DNS_TXT",
4239
+ token: "abc123",
4240
+ metaContent: null,
4241
+ dnsRecord: {
4242
+ type: "TXT",
4243
+ host: "_gsc.example.com",
4244
+ value: "abc123"
4245
+ }
4246
+ },
4247
+ meta: {
4248
+ requestId: "req_01",
4249
+ surface: "partner",
4250
+ version: "1.0"
4251
+ }
4252
+ }
4253
+ }
4254
+ }
4255
+ }),
4256
+ addAndVerifySite: defineHttpOperation({
4257
+ id: "partner.users.sites.verify.create",
4258
+ method: "POST",
4259
+ path: "/users/{userId}/sites/verify",
4260
+ visibility: "public",
4261
+ semantics: {
4262
+ kind: "mutation",
4263
+ sideEffects: "state",
4264
+ idempotent: true,
4265
+ retry: "idempotent",
4266
+ readConsistency: null
4267
+ },
4268
+ auth: {
4269
+ credentials: ["user_key", "partner_key"],
4270
+ scopes: ["sites:write"],
4271
+ ownership: [{
4272
+ credential: "user_key",
4273
+ rule: "self"
4274
+ }, {
4275
+ credential: "partner_key",
4276
+ rule: "linked_user"
4277
+ }]
4278
+ },
4279
+ request: {
4280
+ params: z.strictObject({ userId: realtimeSchemas.publicUserId }),
4281
+ query: null,
4282
+ headers: requestHeaders,
4283
+ body: addAndVerifySiteRequest
4284
+ },
4285
+ responses: { 200: addAndVerifySiteResponse },
4286
+ errors: partnerUserErrors,
4287
+ errorResponse: errorEnvelopeSchemas(partnerUserErrors, realtimeSchemas.publicRequestId),
4288
+ resources: {
4289
+ reads: [{
4290
+ type: "partner.user",
4291
+ idFrom: "params.userId"
4292
+ }],
4293
+ changes: [{
4294
+ type: "user.sites",
4295
+ idFrom: "params.userId"
4296
+ }]
4297
+ },
4298
+ lifecycle: { introduced: "1.3.0" },
4299
+ docs: {
4300
+ summary: "Add and verify a site",
4301
+ description: "Verifies Search Console ownership of a site for a user via the given method and records the verified ownership.",
4302
+ tags: ["Sites"],
4303
+ examples: {
4304
+ request: {
4305
+ params: { userId: "u_01" },
4306
+ body: {
4307
+ siteUrl: "https://example.com",
4308
+ method: "DNS_TXT"
4309
+ }
4310
+ },
4311
+ response: {
4312
+ data: {
4313
+ siteUrl: "https://example.com",
4314
+ site: {
4315
+ type: "INET_DOMAIN",
4316
+ identifier: "example.com"
4317
+ },
4318
+ method: "DNS_TXT",
4319
+ verified: true,
4320
+ owners: ["owner@example.com"]
4321
+ },
4322
+ meta: {
4323
+ requestId: "req_01",
4324
+ surface: "partner",
4325
+ version: "1.0"
4326
+ }
4327
+ }
4328
+ }
4329
+ }
4330
+ }),
4331
+ queryCrossSource: defineHttpOperation({
4332
+ id: "partner.sites.cross.source.query",
4333
+ method: "POST",
4334
+ path: "/sites/{siteId}/cross-source",
4335
+ visibility: "public",
4336
+ semantics: {
4337
+ kind: "query",
4338
+ sideEffects: "none",
4339
+ idempotent: true,
4340
+ retry: "idempotent",
4341
+ readConsistency: "primary"
4342
+ },
4343
+ auth: {
4344
+ credentials: ["partner_key"],
4345
+ scopes: ["analytics:read"],
4346
+ ownership: [{
4347
+ credential: "partner_key",
4348
+ rule: "authorized_site"
4349
+ }]
4350
+ },
4351
+ request: {
4352
+ params: z.strictObject({ siteId: realtimeSchemas.publicSiteId }),
4353
+ query: null,
4354
+ headers: requestHeaders,
4355
+ body: crossSourceRequest
4356
+ },
4357
+ responses: { 200: crossSourceResponse },
4358
+ errors: partnerSiteErrors,
4359
+ errorResponse: errorEnvelopeSchemas(partnerSiteErrors, realtimeSchemas.publicRequestId),
4360
+ resources: {
4361
+ reads: [{
4362
+ type: "site.analytics",
4363
+ idFrom: "params.siteId"
4364
+ }],
4365
+ changes: []
4366
+ },
4367
+ lifecycle: { introduced: "1.3.0" },
4368
+ docs: {
4369
+ summary: "Query cross-source analysis",
4370
+ description: "Runs one predefined cross-source query joining crawl, CWV, and GSC signals for a site.",
4371
+ tags: ["Analytics"],
4372
+ examples: {
4373
+ request: {
4374
+ params: { siteId: "s_01" },
4375
+ body: {
4376
+ queryKey: "crawl-error-losing-impressions",
4377
+ rangeDays: 28,
4378
+ limit: 50
4379
+ }
4380
+ },
4381
+ response: {
4382
+ data: {
4383
+ sources: ["crawl", "gsc"],
4384
+ rows: []
4385
+ },
4386
+ meta: {
4387
+ requestId: "req_01",
4388
+ surface: "partner",
4389
+ version: "1.0"
4390
+ }
4391
+ }
4392
+ }
4393
+ }
4394
+ }),
4395
+ enrichKeywords: defineHttpOperation({
4396
+ id: "partner.keywords.enrich.query",
4397
+ method: "POST",
4398
+ path: "/keywords/enrich",
4399
+ visibility: "public",
4400
+ semantics: {
4401
+ kind: "query",
4402
+ sideEffects: "none",
4403
+ idempotent: true,
4404
+ retry: "idempotent",
4405
+ readConsistency: "primary"
4406
+ },
4407
+ auth: {
4408
+ credentials: ["user_key", "partner_key"],
4409
+ scopes: ["analytics:read"],
4410
+ ownership: [{
4411
+ credential: "user_key",
4412
+ rule: "self"
4413
+ }, {
4414
+ credential: "partner_key",
4415
+ rule: "partner_tenant"
4416
+ }]
4417
+ },
4418
+ request: {
4419
+ params: null,
4420
+ query: null,
4421
+ headers: requestHeaders,
4422
+ body: enrichKeywordsRequest
4423
+ },
4424
+ responses: { 200: enrichKeywordsResponse },
4425
+ errors: partnerUserErrors,
4426
+ errorResponse: errorEnvelopeSchemas(partnerUserErrors, realtimeSchemas.publicRequestId),
4427
+ resources: {
4428
+ reads: [],
4429
+ changes: []
4430
+ },
4431
+ lifecycle: { introduced: "1.3.0" },
4432
+ docs: {
4433
+ summary: "Enrich keywords with metrics",
4434
+ description: "Returns difficulty, search volume, and CPC estimates for up to 500 keywords.",
4435
+ tags: ["Analytics"],
4436
+ examples: {
4437
+ request: { body: { keywords: ["nuxt seo"] } },
4438
+ response: {
4439
+ data: { metrics: { "nuxt seo": {
4440
+ difficulty: 40,
4441
+ searchVolume: 100,
4442
+ cpc: 1.2
4443
+ } } },
4444
+ meta: {
4445
+ requestId: "req_01",
4446
+ surface: "partner",
4447
+ version: "1.0"
4448
+ }
4449
+ }
4450
+ }
4451
+ }
2450
4452
  })
2451
4453
  }
2452
4454
  });