@gscdump/contracts 1.4.0 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,4282 @@
1
+ import { accountNextActions, accountStatuses, analyticsNextActions, analyticsStatuses, indexingNextActions, indexingStatuses, lifecycleErrorCodes, propertyNextActions, propertyStatuses, querySourceModes, sitemapNextActions, sitemapStatuses } from "../onboarding.mjs";
2
+ import { addPartnerTeamMemberSchema, 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, indexingUrlsResponseSchema, partnerSiteTeamBindingResponseSchema, partnerSitemapActionResponseSchema, partnerSitemapActionSchema, partnerTeamCreatedResponseSchema, partnerTeamDeletedResponseSchema, partnerTeamMemberAddedResponseSchema, partnerTeamMemberRemovedResponseSchema, partnerTeamMemberRoleResponseSchema, partnerTeamMembersResponseSchema, partnerTeamRenamedResponseSchema, registerPartnerSiteSchema, registerPartnerUserSchema, renamePartnerTeamSchema, searchTypeSchema, siteIntIdCrosswalkResponseSchema, teamCatalogRefSchema, updatePartnerUserTokensSchema } from "../schemas.mjs";
3
+ import { HTTP_V1_ERROR_CODES, defineHttpOperation, defineHttpSurface, defineResponseObject, defineSuccessResponse } from "./http.mjs";
4
+ import { GSCDUMP_REALTIME_ACK_POLICY, GSCDUMP_REALTIME_CLOSE_CODES, GSCDUMP_REALTIME_CONNECTION_POLICY, GSCDUMP_REALTIME_LIMITS, GSCDUMP_REALTIME_PING, GSCDUMP_REALTIME_PONG, GSCDUMP_REALTIME_REPLAY_POLICY, GSCDUMP_REALTIME_SUBPROTOCOL, GSCDUMP_REALTIME_TICKET_AUDIENCE, GSCDUMP_REALTIME_TICKET_ISSUER, GSCDUMP_REALTIME_TICKET_POLICY, GSCDUMP_REALTIME_TICKET_PREFIX, REALTIME_V1_EVENT_SEMANTICS, createRealtimeV1Schemas } from "./realtime.mjs";
5
+ import { z } from "zod";
6
+ const GSCDUMP_HTTP_V1_VERSION = "1.0";
7
+ function errorEnvelopeSchemas(codes, publicRequestId) {
8
+ const errorDetails = z.record(z.string(), z.json());
9
+ const producer = z.strictObject({
10
+ code: z.enum(codes),
11
+ message: z.string().min(1),
12
+ requestId: publicRequestId,
13
+ retryable: z.boolean(),
14
+ details: errorDetails
15
+ });
16
+ const client = z.looseObject({
17
+ code: z.enum(codes),
18
+ message: z.string().min(1),
19
+ requestId: publicRequestId,
20
+ retryable: z.boolean(),
21
+ details: errorDetails
22
+ });
23
+ return defineResponseObject({ error: producer }, { error: client });
24
+ }
25
+ function lifecycleResponseSchemas(ids) {
26
+ const progress = defineResponseObject({
27
+ completed: z.number().int().nonnegative(),
28
+ failed: z.number().int().nonnegative(),
29
+ total: z.number().int().nonnegative(),
30
+ percent: z.number().min(0).max(100)
31
+ });
32
+ const latestError = defineResponseObject({
33
+ code: z.enum(lifecycleErrorCodes),
34
+ message: z.string(),
35
+ retryable: z.boolean()
36
+ });
37
+ const account = defineResponseObject({
38
+ status: z.enum(accountStatuses),
39
+ grantedScopes: z.array(z.string()),
40
+ missingScopes: z.array(z.string()),
41
+ nextAction: z.enum(accountNextActions)
42
+ });
43
+ const property = defineResponseObject({
44
+ status: z.enum(propertyStatuses),
45
+ nextAction: z.enum(propertyNextActions)
46
+ });
47
+ const syncedRange = defineResponseObject({
48
+ oldest: z.iso.date().nullable(),
49
+ newest: z.iso.date().nullable()
50
+ });
51
+ const analytics = defineResponseObject({
52
+ status: z.enum(analyticsStatuses),
53
+ progress: progress.producer,
54
+ queryable: z.boolean(),
55
+ sourceMode: z.enum(querySourceModes),
56
+ syncedRange: syncedRange.producer,
57
+ nextAction: z.enum(analyticsNextActions)
58
+ }, {
59
+ status: z.enum(analyticsStatuses),
60
+ progress: progress.client,
61
+ queryable: z.boolean(),
62
+ sourceMode: z.enum(querySourceModes),
63
+ syncedRange: syncedRange.client,
64
+ nextAction: z.enum(analyticsNextActions)
65
+ });
66
+ const sitemaps = defineResponseObject({
67
+ status: z.enum(sitemapStatuses),
68
+ discoveredCount: z.number().int().nonnegative(),
69
+ nextAction: z.enum(sitemapNextActions)
70
+ });
71
+ const indexing = defineResponseObject({
72
+ status: z.enum(indexingStatuses),
73
+ eligible: z.boolean(),
74
+ reason: z.string().nullable(),
75
+ progress: progress.producer,
76
+ nextAction: z.enum(indexingNextActions)
77
+ }, {
78
+ status: z.enum(indexingStatuses),
79
+ eligible: z.boolean(),
80
+ reason: z.string().nullable(),
81
+ progress: progress.client,
82
+ nextAction: z.enum(indexingNextActions)
83
+ });
84
+ const site = defineResponseObject({
85
+ siteId: ids.publicSiteId,
86
+ externalSiteId: z.string().nullable(),
87
+ requestedUrl: z.string().min(1),
88
+ gscPropertyUrl: z.string().nullable(),
89
+ permissionLevel: z.string().nullable(),
90
+ property: property.producer,
91
+ analytics: analytics.producer,
92
+ sitemaps: sitemaps.producer,
93
+ indexing: indexing.producer,
94
+ latestError: latestError.producer.nullable(),
95
+ updatedAt: z.iso.datetime()
96
+ }, {
97
+ siteId: ids.publicSiteId,
98
+ externalSiteId: z.string().nullable(),
99
+ requestedUrl: z.string().min(1),
100
+ gscPropertyUrl: z.string().nullable(),
101
+ permissionLevel: z.string().nullable(),
102
+ property: property.client,
103
+ analytics: analytics.client,
104
+ sitemaps: sitemaps.client,
105
+ indexing: indexing.client,
106
+ latestError: latestError.client.nullable(),
107
+ updatedAt: z.iso.datetime()
108
+ });
109
+ return defineResponseObject({
110
+ userId: ids.publicUserId,
111
+ partnerId: ids.publicPartnerId.nullable(),
112
+ currentTeamId: ids.publicTeamId.nullable(),
113
+ account: account.producer,
114
+ sites: z.array(site.producer)
115
+ }, {
116
+ userId: ids.publicUserId,
117
+ partnerId: ids.publicPartnerId.nullable(),
118
+ currentTeamId: ids.publicTeamId.nullable(),
119
+ account: account.client,
120
+ sites: z.array(site.client)
121
+ });
122
+ }
123
+ function createGscdumpV1Protocol() {
124
+ const realtimeSchemas = createRealtimeV1Schemas();
125
+ const surfaceSchema = z.enum([
126
+ "partner",
127
+ "analytics",
128
+ "realtime"
129
+ ]);
130
+ const responseMeta = defineResponseObject({
131
+ requestId: realtimeSchemas.publicRequestId,
132
+ surface: surfaceSchema,
133
+ version: z.literal("1.0")
134
+ });
135
+ const partnerResponseMeta = defineResponseObject({
136
+ requestId: realtimeSchemas.publicRequestId,
137
+ surface: z.literal("partner"),
138
+ version: z.literal("1.0")
139
+ });
140
+ const realtimeResponseMeta = defineResponseObject({
141
+ requestId: realtimeSchemas.publicRequestId,
142
+ surface: z.literal("realtime"),
143
+ version: z.literal("1.0")
144
+ });
145
+ const requestMetadata = z.strictObject({
146
+ requestId: realtimeSchemas.publicRequestId,
147
+ surface: surfaceSchema,
148
+ version: z.literal("1.0")
149
+ });
150
+ const requestHeaders = z.strictObject({ "x-request-id": realtimeSchemas.publicRequestId.optional() });
151
+ const errorEnvelope = errorEnvelopeSchemas(HTTP_V1_ERROR_CODES, realtimeSchemas.publicRequestId);
152
+ const lifecycleResponse = defineSuccessResponse(lifecycleResponseSchemas(realtimeSchemas), partnerResponseMeta);
153
+ const dimensions = [
154
+ "page",
155
+ "query",
156
+ "queryCanonical",
157
+ "country",
158
+ "device",
159
+ "date",
160
+ "searchAppearance",
161
+ "hour"
162
+ ];
163
+ const metrics = [
164
+ "clicks",
165
+ "impressions",
166
+ "ctr",
167
+ "position"
168
+ ];
169
+ const filterDimension = z.enum([
170
+ ...dimensions,
171
+ ...metrics,
172
+ "searchType"
173
+ ]);
174
+ const normalizedFilterLeaf = z.union([z.strictObject({
175
+ dimension: filterDimension,
176
+ operator: z.enum([
177
+ "equals",
178
+ "notEquals",
179
+ "contains",
180
+ "notContains",
181
+ "includingRegex",
182
+ "excludingRegex",
183
+ "gte",
184
+ "gt",
185
+ "lte",
186
+ "lt",
187
+ "metricGte",
188
+ "metricGt",
189
+ "metricLte",
190
+ "metricLt",
191
+ "topLevel"
192
+ ]),
193
+ expression: z.string()
194
+ }), z.strictObject({
195
+ dimension: filterDimension,
196
+ operator: z.enum(["between", "metricBetween"]),
197
+ expression: z.string(),
198
+ expression2: z.string()
199
+ })]);
200
+ const normalizedFilter = z.lazy(() => z.union([z.strictObject({
201
+ _filters: z.array(normalizedFilterLeaf).min(1),
202
+ _nestedGroups: z.array(normalizedFilter).optional(),
203
+ _groupType: z.enum(["and", "or"]).optional()
204
+ }), z.strictObject({
205
+ _filters: z.array(normalizedFilterLeaf).max(0),
206
+ _nestedGroups: z.array(normalizedFilter).min(1),
207
+ _groupType: z.enum(["and", "or"]).optional()
208
+ })]));
209
+ const analyticsRowsRequest = z.strictObject({
210
+ dimensions: z.array(z.enum(dimensions)).min(1),
211
+ metrics: z.array(z.enum(metrics)).optional(),
212
+ filter: normalizedFilter.optional(),
213
+ prefilter: normalizedFilter.optional(),
214
+ orderBy: z.strictObject({
215
+ column: z.enum([...metrics, "date"]),
216
+ dir: z.enum(["asc", "desc"])
217
+ }).optional(),
218
+ rowLimit: z.number().int().positive().max(25e3).optional(),
219
+ startRow: z.number().int().nonnegative().optional(),
220
+ dataState: z.enum([
221
+ "final",
222
+ "all",
223
+ "hourly_all"
224
+ ]).optional(),
225
+ aggregationType: z.enum([
226
+ "auto",
227
+ "byPage",
228
+ "byProperty",
229
+ "byNewsShowcasePanel"
230
+ ]).optional(),
231
+ searchType: z.enum([
232
+ "web",
233
+ "image",
234
+ "video",
235
+ "news",
236
+ "discover",
237
+ "googleNews"
238
+ ]).optional()
239
+ });
240
+ const analyticsRowData = defineResponseObject({ rows: z.array(z.record(z.string(), z.union([
241
+ z.string(),
242
+ z.number(),
243
+ z.boolean(),
244
+ z.null()
245
+ ]))) });
246
+ const analyticsMeta = defineResponseObject({
247
+ requestId: realtimeSchemas.publicRequestId,
248
+ surface: z.literal("analytics"),
249
+ version: z.literal("1.0"),
250
+ sourceName: z.string().min(1),
251
+ sourceKind: z.enum(["row", "sql"]),
252
+ queryMs: z.number().nonnegative()
253
+ });
254
+ const analyticsRowsResponse = defineSuccessResponse(analyticsRowData, analyticsMeta);
255
+ const analyticsReportState = builderStateSchema.extend({ dimensions: z.array(z.enum(dimensions)).min(1) });
256
+ const analyticsListReportState = analyticsReportState.refine((state) => !state.dimensions.includes("date"), {
257
+ message: "List reports do not accept the date dimension.",
258
+ path: ["dimensions"]
259
+ });
260
+ const analyticsDetailReportState = analyticsReportState.refine((state) => state.dimensions.includes("date"), {
261
+ message: "Detail reports require the date dimension.",
262
+ path: ["dimensions"]
263
+ });
264
+ const analyticsReportRequest = z.strictObject({
265
+ state: analyticsListReportState,
266
+ comparison: analyticsListReportState.optional(),
267
+ filter: gscComparisonFilterSchema.optional()
268
+ });
269
+ const analyticsReportDetailRequest = z.strictObject({
270
+ state: analyticsDetailReportState,
271
+ comparison: analyticsDetailReportState.optional()
272
+ });
273
+ const analyticsReportData = defineResponseObject(gscdumpDataResponseSchema.shape);
274
+ const analyticsReportDetailData = defineResponseObject(gscdumpDataDetailResponseSchema.shape);
275
+ const analyticsReportResponse = defineSuccessResponse(analyticsReportData, analyticsMeta);
276
+ const analyticsReportDetailResponse = defineSuccessResponse(analyticsReportDetailData, analyticsMeta);
277
+ const integerQuery = (number, wirePattern) => z.union([number, z.string().regex(wirePattern)]);
278
+ const numericQuery = z.union([z.number(), z.string().regex(/^-?(?:\d+(?:\.\d+)?|\.\d+)$/)]);
279
+ const indexingSummaryQuery = z.strictObject({ days: integerQuery(z.number().int().min(1).max(90), /^(?:[1-9]|[1-8]\d|90)$/).optional() });
280
+ const indexingUrlsQuery = z.strictObject({
281
+ limit: integerQuery(z.number().int().min(1).max(500), /^(?:[1-9]|[1-9]\d|[1-4]\d{2}|500)$/).optional(),
282
+ offset: integerQuery(z.number().int().min(0), /^(?:0|[1-9]\d*)$/).optional(),
283
+ status: z.enum([
284
+ "indexed",
285
+ "not_indexed",
286
+ "pending"
287
+ ]).optional(),
288
+ issue: z.string().optional(),
289
+ search: z.string().optional(),
290
+ count: z.union([
291
+ z.literal(0),
292
+ z.literal(false),
293
+ z.literal("0"),
294
+ z.literal("false")
295
+ ]).optional()
296
+ });
297
+ const indexingDiagnosticsQuery = z.strictObject({
298
+ sampleIssues: z.union([z.string(), z.array(z.string())]).optional(),
299
+ sampleLimit: integerQuery(z.number().int().min(1).max(25), /^(?:[1-9]|1\d|2[0-5])$/).optional()
300
+ });
301
+ const sitemapChangesQuery = z.strictObject({ days: integerQuery(z.number().int().min(1).max(90), /^(?:[1-9]|[1-8]\d|90)$/).optional() });
302
+ const analysisBaseQueryShape = {
303
+ startDate: z.string().min(1),
304
+ endDate: z.string().min(1),
305
+ prevStartDate: z.string().min(1).optional(),
306
+ prevEndDate: z.string().min(1).optional(),
307
+ brandTerms: z.string().optional(),
308
+ limit: integerQuery(z.number().int().min(1).max(1e3), /^(?:[1-9]|[1-9]\d|[1-9]\d{2}|1000)$/).optional(),
309
+ offset: integerQuery(z.number().int().min(0), /^(?:0|[1-9]\d*)$/).optional(),
310
+ search: z.string().optional(),
311
+ minImpressions: numericQuery.optional(),
312
+ minPosition: numericQuery.optional(),
313
+ maxPosition: numericQuery.optional(),
314
+ maxCtr: numericQuery.optional(),
315
+ searchType: searchTypeSchema.optional()
316
+ };
317
+ const analysisQuery = z.strictObject({
318
+ ...analysisBaseQueryShape,
319
+ preset: gscdumpAnalysisPresetSchema
320
+ }).superRefine((value, ctx) => {
321
+ if ((value.preset === "brand-only" || value.preset === "non-brand") && !value.brandTerms?.trim()) ctx.addIssue({
322
+ code: "custom",
323
+ path: ["brandTerms"],
324
+ message: "brandTerms is required for brand/non-brand presets"
325
+ });
326
+ });
327
+ const analysisBundleQuery = z.strictObject({
328
+ ...analysisBaseQueryShape,
329
+ presets: z.union([z.array(gscdumpAnalysisPresetSchema).min(1).max(8), z.string().min(1)])
330
+ }).superRefine((value, ctx) => {
331
+ const presets = typeof value.presets === "string" ? value.presets.split(",").map((preset) => preset.trim()).filter(Boolean) : value.presets;
332
+ if (presets.length < 1 || presets.length > 8 || presets.some((preset) => !gscdumpAnalysisPresetSchema.safeParse(preset).success)) ctx.addIssue({
333
+ code: "custom",
334
+ path: ["presets"],
335
+ message: "presets must contain 1 to 8 supported presets"
336
+ });
337
+ if (presets.some((preset) => preset === "brand-only" || preset === "non-brand") && !value.brandTerms?.trim()) ctx.addIssue({
338
+ code: "custom",
339
+ path: ["brandTerms"],
340
+ message: "brandTerms is required for brand/non-brand presets"
341
+ });
342
+ if (presets.some((preset) => preset === "movers-rising" || preset === "movers-declining" || preset === "decay") && (!value.prevStartDate || !value.prevEndDate)) ctx.addIssue({
343
+ code: "custom",
344
+ path: ["prevStartDate"],
345
+ message: "Comparison presets require both previous-period dates"
346
+ });
347
+ });
348
+ const availableSitesQuery = z.strictObject({ refresh: z.union([
349
+ z.literal(true),
350
+ z.literal(1),
351
+ z.literal("true"),
352
+ z.literal("1")
353
+ ]).optional() });
354
+ const registerSiteRequest = registerPartnerSiteSchema.omit({
355
+ userId: true,
356
+ teamId: true
357
+ }).strict();
358
+ const indexingSummaryResponse = defineSuccessResponse(defineResponseObject(gscdumpIndexingResponseSchema.shape), partnerResponseMeta);
359
+ const indexingUrlsResponse = defineSuccessResponse(defineResponseObject(indexingUrlsResponseSchema.shape), partnerResponseMeta);
360
+ const indexingDiagnosticsResponse = defineSuccessResponse(defineResponseObject(gscdumpIndexingDiagnosticsResponseSchema.shape), partnerResponseMeta);
361
+ const sitemapsResponse = defineSuccessResponse(defineResponseObject(gscdumpSitemapsResponseSchema.shape), partnerResponseMeta);
362
+ const sitemapChangesResponse = defineSuccessResponse(defineResponseObject({
363
+ ...gscdumpSitemapChangesResponseSchema.shape,
364
+ summary: gscdumpSitemapChangesResponseSchema.shape.summary.unwrap()
365
+ }), partnerResponseMeta);
366
+ const analysisResponse = defineSuccessResponse(defineResponseObject(gscdumpAnalysisResponseSchema.shape), partnerResponseMeta);
367
+ const analysisBundleResponse = defineSuccessResponse(defineResponseObject(gscdumpAnalysisBundleResponseSchema.shape), partnerResponseMeta);
368
+ const availableSitesResponse = defineSuccessResponse(defineResponseObject({ sites: z.array(gscdumpAvailableSiteSchema) }), partnerResponseMeta);
369
+ const siteRegistrationResponse = defineSuccessResponse(defineResponseObject(gscdumpSiteRegistrationSchema.shape), partnerResponseMeta);
370
+ const siteDeletionResponse = defineSuccessResponse(defineResponseObject({
371
+ deleted: z.literal(true),
372
+ siteId: realtimeSchemas.publicSiteId,
373
+ siteUrl: z.string().min(1)
374
+ }), partnerResponseMeta);
375
+ const userRegistrationResponse = defineSuccessResponse(defineResponseObject(gscdumpUserRegistrationSchema.shape), partnerResponseMeta);
376
+ const userTokenUpdateResponse = defineSuccessResponse(defineResponseObject({
377
+ userId: realtimeSchemas.publicUserId,
378
+ updated: z.boolean(),
379
+ sites: z.array(gscdumpAvailableSiteSchema)
380
+ }), partnerResponseMeta);
381
+ const calendarDate = z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "expected YYYY-MM-DD");
382
+ const trendQuery = z.strictObject({
383
+ startDate: calendarDate,
384
+ endDate: calendarDate,
385
+ searchType: searchTypeSchema.optional(),
386
+ prevStartDate: calendarDate.optional(),
387
+ prevEndDate: calendarDate.optional()
388
+ });
389
+ const keywordSparklinesRequest = z.strictObject({
390
+ keywords: z.array(z.string().trim().min(1)).min(1).max(20),
391
+ startDate: calendarDate,
392
+ endDate: calendarDate,
393
+ searchType: searchTypeSchema.optional()
394
+ });
395
+ const indexingInspectRequest = z.strictObject({ urls: z.array(z.string().url()).min(1).max(10) });
396
+ const canonicalMismatchesResponse = defineSuccessResponse(defineResponseObject(gscdumpCanonicalMismatchesResponseSchema.shape), partnerResponseMeta);
397
+ const keywordSparklinesResponse = defineSuccessResponse(defineResponseObject(gscdumpKeywordSparklinesResponseSchema.shape), partnerResponseMeta);
398
+ const queryTrendResponse = defineSuccessResponse(defineResponseObject(gscdumpQueryTrendResponseSchema.shape), partnerResponseMeta);
399
+ const pageTrendResponse = defineSuccessResponse(defineResponseObject(gscdumpPageTrendResponseSchema.shape), partnerResponseMeta);
400
+ const permissionRecoveryResponse = defineSuccessResponse(defineResponseObject({
401
+ success: z.boolean(),
402
+ permissionLevel: z.string().nullable(),
403
+ jobsQueued: z.number().int().nonnegative(),
404
+ message: z.string().min(1)
405
+ }), partnerResponseMeta);
406
+ const inspectionFact = z.string().nullable();
407
+ const inspectionResult = z.object({
408
+ url: z.string(),
409
+ verdict: inspectionFact,
410
+ coverageState: inspectionFact,
411
+ indexingState: inspectionFact,
412
+ robotsTxtState: inspectionFact,
413
+ pageFetchState: inspectionFact,
414
+ lastCrawlTime: inspectionFact,
415
+ crawlingUserAgent: inspectionFact,
416
+ userCanonical: inspectionFact,
417
+ googleCanonical: inspectionFact,
418
+ sitemaps: inspectionFact,
419
+ referringUrls: inspectionFact,
420
+ mobileVerdict: inspectionFact,
421
+ mobileIssues: inspectionFact,
422
+ richResultsVerdict: inspectionFact,
423
+ richResultsItems: inspectionFact,
424
+ ampVerdict: inspectionFact,
425
+ ampUrl: inspectionFact,
426
+ ampIndexingState: inspectionFact,
427
+ ampIndexStatusVerdict: inspectionFact,
428
+ ampRobotsTxtState: inspectionFact,
429
+ ampPageFetchState: inspectionFact,
430
+ ampLastCrawlTime: inspectionFact,
431
+ ampIssues: inspectionFact,
432
+ inspectionResultLink: inspectionFact
433
+ });
434
+ const indexingInspectionResponse = defineSuccessResponse(defineResponseObject({
435
+ siteId: realtimeSchemas.publicSiteId,
436
+ rateLimit: z.strictObject({
437
+ reserved: z.number().int().nonnegative(),
438
+ remaining: z.number().int().nonnegative(),
439
+ limit: z.number().int().positive()
440
+ }),
441
+ results: z.array(inspectionResult),
442
+ errors: z.array(z.strictObject({
443
+ url: z.string(),
444
+ error: z.string()
445
+ })),
446
+ skipped: z.array(z.strictObject({
447
+ url: z.string(),
448
+ reason: z.enum(["domain_mismatch", "rate_limited"])
449
+ }))
450
+ }), partnerResponseMeta);
451
+ const dateRangeQuery = z.strictObject({
452
+ startDate: calendarDate,
453
+ endDate: calendarDate
454
+ });
455
+ const siteDataMeta = z.object({
456
+ siteUrl: z.string().nullable(),
457
+ syncStatus: z.string().nullable()
458
+ });
459
+ const contentVelocityResponse = defineSuccessResponse(defineResponseObject({
460
+ weekly: z.array(z.strictObject({
461
+ week: z.string(),
462
+ newKeywords: z.number().int().nonnegative(),
463
+ totalKeywords: z.number().int().nonnegative()
464
+ })),
465
+ summary: z.strictObject({
466
+ totalNewKeywords: z.number().int().nonnegative(),
467
+ avgPerWeek: z.number().nonnegative(),
468
+ trend: z.enum([
469
+ "stable",
470
+ "accelerating",
471
+ "decelerating"
472
+ ])
473
+ }),
474
+ meta: siteDataMeta
475
+ }), partnerResponseMeta);
476
+ const ctrOutlier = z.strictObject({
477
+ query: z.string(),
478
+ clicks: z.number(),
479
+ impressions: z.number(),
480
+ ctr: z.number(),
481
+ position: z.number(),
482
+ expectedCtr: z.number(),
483
+ ctrDiff: z.number()
484
+ });
485
+ const ctrCurveResponse = defineSuccessResponse(defineResponseObject({
486
+ curve: z.array(z.strictObject({
487
+ bucket: z.string(),
488
+ avgCtr: z.number(),
489
+ medianPosition: z.number(),
490
+ keywordCount: z.number(),
491
+ totalClicks: z.number(),
492
+ totalImpressions: z.number()
493
+ })),
494
+ overperforming: z.array(ctrOutlier),
495
+ underperforming: z.array(ctrOutlier),
496
+ meta: siteDataMeta
497
+ }), partnerResponseMeta);
498
+ const darkTrafficResponse = defineSuccessResponse(defineResponseObject({
499
+ summary: z.strictObject({
500
+ totalClicks: z.number(),
501
+ attributedClicks: z.number(),
502
+ darkClicks: z.number(),
503
+ darkPercent: z.number(),
504
+ totalImpressions: z.number(),
505
+ attributedImpressions: z.number()
506
+ }),
507
+ pages: z.array(z.strictObject({
508
+ url: z.string(),
509
+ totalClicks: z.number(),
510
+ attributedClicks: z.number(),
511
+ darkClicks: z.number(),
512
+ darkPercent: z.number(),
513
+ keywordCount: z.number()
514
+ })),
515
+ meta: siteDataMeta
516
+ }), partnerResponseMeta);
517
+ const deviceGapMetrics = z.strictObject({
518
+ clicks: z.number(),
519
+ impressions: z.number(),
520
+ ctr: z.number(),
521
+ position: z.number()
522
+ });
523
+ const deviceGapResponse = defineSuccessResponse(defineResponseObject({
524
+ daily: z.array(z.strictObject({
525
+ date: z.string(),
526
+ desktop: deviceGapMetrics,
527
+ mobile: deviceGapMetrics,
528
+ gaps: z.strictObject({
529
+ ctrGap: z.number(),
530
+ positionGap: z.number()
531
+ })
532
+ })),
533
+ summary: z.strictObject({
534
+ avgCtrGap: z.number(),
535
+ avgPositionGap: z.number(),
536
+ ctrGapTrend: z.enum([
537
+ "stable",
538
+ "improving",
539
+ "worsening"
540
+ ]),
541
+ positionGapTrend: z.enum([
542
+ "stable",
543
+ "improving",
544
+ "worsening"
545
+ ])
546
+ }).nullable(),
547
+ meta: siteDataMeta
548
+ }), partnerResponseMeta);
549
+ const keywordBreadthPage = z.strictObject({
550
+ url: z.string(),
551
+ keywordCount: z.number(),
552
+ clicks: z.number(),
553
+ impressions: z.number()
554
+ });
555
+ const keywordBreadthResponse = defineSuccessResponse(defineResponseObject({
556
+ distribution: z.array(z.strictObject({
557
+ bucket: z.string(),
558
+ pageCount: z.number()
559
+ })),
560
+ fragilePages: z.array(keywordBreadthPage),
561
+ authorityPages: z.array(keywordBreadthPage),
562
+ summary: z.strictObject({
563
+ totalPages: z.number(),
564
+ avgKeywordsPerPage: z.number(),
565
+ fragileCount: z.number(),
566
+ authorityCount: z.number()
567
+ }),
568
+ meta: siteDataMeta
569
+ }), partnerResponseMeta);
570
+ const positionDistributionResponse = defineSuccessResponse(defineResponseObject({
571
+ distribution: z.array(z.strictObject({
572
+ date: z.string(),
573
+ pos_1_3: z.number(),
574
+ pos_4_10: z.number(),
575
+ pos_11_20: z.number(),
576
+ pos_20_plus: z.number(),
577
+ total: z.number()
578
+ })),
579
+ meta: siteDataMeta
580
+ }), partnerResponseMeta);
581
+ const topAssociationQuery = z.strictObject({
582
+ type: z.enum(["topPage", "topKeyword"]),
583
+ identifier: z.string().min(1),
584
+ startDate: calendarDate,
585
+ endDate: calendarDate
586
+ });
587
+ const topAssociationResponse = defineSuccessResponse(defineResponseObject(gscdumpTopAssociationResponseSchema.shape), partnerResponseMeta);
588
+ const indexPercentQuery = z.strictObject({
589
+ invisibleLimit: z.coerce.number().int().min(1).max(500).optional(),
590
+ invisibleOffset: z.coerce.number().int().min(0).max(1e6).optional(),
591
+ orphanLimit: z.coerce.number().int().min(1).max(500).optional()
592
+ });
593
+ const indexPercentResponse = defineSuccessResponse(defineResponseObject(gscdumpIndexPercentResponseSchema.shape), partnerResponseMeta);
594
+ const contentVelocityQuery = z.strictObject({ days: z.coerce.number().int().min(1).max(365).optional() });
595
+ const sitemapActionRequest = partnerSitemapActionSchema;
596
+ const sitemapActionResponse = defineSuccessResponse({
597
+ producer: partnerSitemapActionResponseSchema,
598
+ client: partnerSitemapActionResponseSchema
599
+ }, partnerResponseMeta);
600
+ const sitemapMembershipRequest = gscdumpSitemapMembershipParamsSchema.strict();
601
+ const sitemapMembershipResponse = defineSuccessResponse(defineResponseObject(gscdumpSitemapMembershipResponseSchema.shape), partnerResponseMeta);
602
+ const createTeamRequest = createPartnerTeamSchema.strict();
603
+ const teamCreatedResponse = defineSuccessResponse(defineResponseObject(partnerTeamCreatedResponseSchema.shape), partnerResponseMeta);
604
+ const renameTeamRequest = renamePartnerTeamSchema.strict();
605
+ const teamRenamedResponse = defineSuccessResponse(defineResponseObject(partnerTeamRenamedResponseSchema.shape), partnerResponseMeta);
606
+ const teamDeletedResponse = defineSuccessResponse(defineResponseObject(partnerTeamDeletedResponseSchema.shape), partnerResponseMeta);
607
+ const teamMembersResponse = defineSuccessResponse(defineResponseObject(partnerTeamMembersResponseSchema.shape), partnerResponseMeta);
608
+ const addTeamMemberRequest = addPartnerTeamMemberSchema.strict();
609
+ const teamMemberAddedResponse = defineSuccessResponse(defineResponseObject(partnerTeamMemberAddedResponseSchema.shape), partnerResponseMeta);
610
+ const updateTeamMemberRoleRequest = z.strictObject({ role: gscdumpTeamRoleSchema });
611
+ const teamMemberRoleResponse = defineSuccessResponse(defineResponseObject(partnerTeamMemberRoleResponseSchema.shape), partnerResponseMeta);
612
+ const teamMemberRemovedResponse = defineSuccessResponse(defineResponseObject(partnerTeamMemberRemovedResponseSchema.shape), partnerResponseMeta);
613
+ const bindSiteTeamRequest = bindPartnerSiteTeamSchema.strict();
614
+ const siteTeamBindingResponse = defineSuccessResponse(defineResponseObject(partnerSiteTeamBindingResponseSchema.shape), partnerResponseMeta);
615
+ const teamCatalogResponse = defineSuccessResponse(defineResponseObject(teamCatalogRefSchema.shape), partnerResponseMeta);
616
+ const bindTeamCatalogRequest = bindPartnerTeamCatalogSchema.strict();
617
+ const teamCatalogBindResponse = defineSuccessResponse(defineResponseObject(bindPartnerTeamCatalogResponseSchema.shape), partnerResponseMeta);
618
+ const siteIntIdCrosswalkResponse = defineSuccessResponse(defineResponseObject(siteIntIdCrosswalkResponseSchema.shape), partnerResponseMeta);
619
+ const deletePartnerUserResponse = defineSuccessResponse(defineResponseObject(gscdumpDeletePartnerUserResponseSchema.shape), partnerResponseMeta);
620
+ const verificationMethodSchema = z.enum([
621
+ "META",
622
+ "FILE",
623
+ "DNS_TXT",
624
+ "DNS_CNAME",
625
+ "ANALYTICS",
626
+ "TAG_MANAGER"
627
+ ]);
628
+ const verificationTokenRequest = z.strictObject({
629
+ siteUrl: z.string().min(1),
630
+ method: verificationMethodSchema.optional()
631
+ });
632
+ const verificationSiteShape = z.object({
633
+ type: z.string(),
634
+ identifier: z.string()
635
+ }).loose();
636
+ const verificationTokenResponse = defineSuccessResponse(defineResponseObject({
637
+ siteUrl: z.string(),
638
+ site: verificationSiteShape,
639
+ method: z.string(),
640
+ token: z.string(),
641
+ metaContent: z.string().nullable(),
642
+ dnsRecord: z.object({
643
+ type: z.enum(["TXT", "CNAME"]),
644
+ host: z.string(),
645
+ value: z.string()
646
+ }).nullable()
647
+ }), partnerResponseMeta);
648
+ const addAndVerifySiteRequest = verificationTokenRequest;
649
+ const addAndVerifySiteResponse = defineSuccessResponse(defineResponseObject({
650
+ siteUrl: z.string(),
651
+ site: verificationSiteShape,
652
+ method: z.string(),
653
+ verified: z.literal(true),
654
+ owners: z.array(z.string()).optional()
655
+ }), partnerResponseMeta);
656
+ const crossSourceRequest = z.strictObject({
657
+ queryKey: z.enum([
658
+ "crawl-error-losing-impressions",
659
+ "declining-clicks-poor-lcp",
660
+ "striking-distance-slow-pages",
661
+ "top-impressions-low-performance"
662
+ ]),
663
+ rangeDays: z.number().int().positive().max(180).optional(),
664
+ limit: z.number().int().positive().max(500).optional()
665
+ });
666
+ const crossSourceResponse = defineSuccessResponse(defineResponseObject({
667
+ reason: z.string().optional(),
668
+ sources: z.array(z.string()),
669
+ rows: z.array(z.record(z.string(), z.json()))
670
+ }), partnerResponseMeta);
671
+ const enrichKeywordsRequest = z.strictObject({ keywords: z.array(z.string().min(1)).min(1).max(500) });
672
+ const enrichKeywordsResponse = defineSuccessResponse(defineResponseObject({ metrics: z.record(z.string(), z.object({
673
+ difficulty: z.number().nullable(),
674
+ searchVolume: z.number().nullable(),
675
+ cpc: z.number().nullable()
676
+ }).loose()) }), partnerResponseMeta);
677
+ const responseStreamHead = defineResponseObject({
678
+ streamId: realtimeSchemas.streamId,
679
+ sequence: realtimeSchemas.sequence
680
+ });
681
+ const headResponse = defineSuccessResponse(defineResponseObject({ head: responseStreamHead.producer }, { head: responseStreamHead.client }), realtimeResponseMeta);
682
+ const ticketDataProducerShape = {
683
+ socketUrl: z.url().refine((value) => value.startsWith("wss://"), "socketUrl must use wss."),
684
+ protocol: z.literal(GSCDUMP_REALTIME_SUBPROTOCOL),
685
+ protocolVersion: z.literal(1),
686
+ ticket: z.string().max(GSCDUMP_REALTIME_TICKET_POLICY.maxBytes).regex(/^gscdump\.ticket\.v1\.[\w-]+\.[\w-]+$/),
687
+ expiresAt: z.iso.datetime(),
688
+ head: responseStreamHead.producer,
689
+ maxConnectionSeconds: z.literal(900)
690
+ };
691
+ const ticketResponse = defineSuccessResponse(defineResponseObject(ticketDataProducerShape, {
692
+ ...ticketDataProducerShape,
693
+ head: responseStreamHead.client
694
+ }), realtimeResponseMeta);
695
+ const partnerUserLifecycleErrors = [
696
+ "invalid_request",
697
+ "unauthorized",
698
+ "forbidden",
699
+ "user_not_found",
700
+ "rate_limited",
701
+ "internal_error",
702
+ "contract_violation"
703
+ ];
704
+ const partnerSiteErrors = [
705
+ "invalid_request",
706
+ "unauthorized",
707
+ "forbidden",
708
+ "site_not_found",
709
+ "rate_limited",
710
+ "internal_error",
711
+ "contract_violation"
712
+ ];
713
+ const partnerUserErrors = [
714
+ "invalid_request",
715
+ "unauthorized",
716
+ "forbidden",
717
+ "user_not_found",
718
+ "rate_limited",
719
+ "internal_error",
720
+ "contract_violation"
721
+ ];
722
+ const analyticsRowsErrors = [
723
+ "invalid_request",
724
+ "unauthorized",
725
+ "forbidden",
726
+ "site_not_found",
727
+ "rate_limited",
728
+ "internal_error",
729
+ "contract_violation"
730
+ ];
731
+ const realtimeErrors = [
732
+ "invalid_request",
733
+ "unauthorized",
734
+ "forbidden",
735
+ "rate_limited",
736
+ "realtime_unavailable",
737
+ "internal_error",
738
+ "contract_violation"
739
+ ];
740
+ const partner = defineHttpSurface({
741
+ name: "partner",
742
+ prefix: "/api/partner/v1",
743
+ version: "1.0",
744
+ operations: {
745
+ getUserLifecycle: defineHttpOperation({
746
+ id: "partner.users.lifecycle.get",
747
+ method: "GET",
748
+ path: "/users/{userId}/lifecycle",
749
+ visibility: "public",
750
+ semantics: {
751
+ kind: "query",
752
+ sideEffects: "none",
753
+ idempotent: true,
754
+ retry: "idempotent",
755
+ readConsistency: "primary"
756
+ },
757
+ auth: {
758
+ credentials: ["user_key", "partner_key"],
759
+ scopes: ["users:read"],
760
+ ownership: [{
761
+ credential: "user_key",
762
+ rule: "self"
763
+ }, {
764
+ credential: "partner_key",
765
+ rule: "linked_user"
766
+ }]
767
+ },
768
+ request: {
769
+ params: z.strictObject({ userId: realtimeSchemas.publicUserId }),
770
+ query: null,
771
+ headers: requestHeaders,
772
+ body: null
773
+ },
774
+ responses: { 200: lifecycleResponse },
775
+ errors: partnerUserLifecycleErrors,
776
+ errorResponse: errorEnvelopeSchemas(partnerUserLifecycleErrors, realtimeSchemas.publicRequestId),
777
+ resources: {
778
+ reads: [{
779
+ type: "partner.user",
780
+ idFrom: "params.userId"
781
+ }, {
782
+ type: "user.sites",
783
+ idFrom: "params.userId"
784
+ }],
785
+ changes: []
786
+ },
787
+ lifecycle: { introduced: "1.0.0" },
788
+ docs: {
789
+ summary: "Get user lifecycle",
790
+ description: "Returns the authoritative lifecycle state for one visible user and their sites.",
791
+ tags: ["Users"],
792
+ examples: {
793
+ request: { params: { userId: "u_01" } },
794
+ response: {
795
+ data: {
796
+ userId: "u_01",
797
+ partnerId: "p_01",
798
+ currentTeamId: null,
799
+ account: {
800
+ status: "ready",
801
+ grantedScopes: ["https://www.googleapis.com/auth/webmasters.readonly"],
802
+ missingScopes: [],
803
+ nextAction: "none"
804
+ },
805
+ sites: []
806
+ },
807
+ meta: {
808
+ requestId: "req_01",
809
+ surface: "partner",
810
+ version: "1.0"
811
+ }
812
+ }
813
+ }
814
+ }
815
+ }),
816
+ listAvailableSites: defineHttpOperation({
817
+ id: "partner.users.sites.available.list",
818
+ method: "GET",
819
+ path: "/users/{userId}/available-sites",
820
+ visibility: "public",
821
+ semantics: {
822
+ kind: "query",
823
+ sideEffects: "none",
824
+ idempotent: true,
825
+ retry: "idempotent",
826
+ readConsistency: "primary"
827
+ },
828
+ auth: {
829
+ credentials: ["user_key", "partner_key"],
830
+ scopes: ["sites:read"],
831
+ ownership: [{
832
+ credential: "user_key",
833
+ rule: "self"
834
+ }, {
835
+ credential: "partner_key",
836
+ rule: "linked_user"
837
+ }]
838
+ },
839
+ request: {
840
+ params: z.strictObject({ userId: realtimeSchemas.publicUserId }),
841
+ query: availableSitesQuery,
842
+ headers: requestHeaders,
843
+ body: null
844
+ },
845
+ responses: { 200: availableSitesResponse },
846
+ errors: partnerUserErrors,
847
+ errorResponse: errorEnvelopeSchemas(partnerUserErrors, realtimeSchemas.publicRequestId),
848
+ resources: {
849
+ reads: [{
850
+ type: "partner.user",
851
+ idFrom: "params.userId"
852
+ }, {
853
+ type: "user.sites",
854
+ idFrom: "params.userId"
855
+ }],
856
+ changes: []
857
+ },
858
+ lifecycle: { introduced: "1.0.0" },
859
+ docs: {
860
+ summary: "List available Search Console properties",
861
+ description: "Lists the visible Google Search Console properties and their registration state.",
862
+ tags: ["Sites"],
863
+ examples: {
864
+ request: {
865
+ params: { userId: "u_01" },
866
+ query: {}
867
+ },
868
+ response: {
869
+ data: { sites: [] },
870
+ meta: {
871
+ requestId: "req_01",
872
+ surface: "partner",
873
+ version: "1.0"
874
+ }
875
+ }
876
+ }
877
+ }
878
+ }),
879
+ createSite: defineHttpOperation({
880
+ id: "partner.users.sites.create",
881
+ method: "POST",
882
+ path: "/users/{userId}/sites",
883
+ visibility: "public",
884
+ semantics: {
885
+ kind: "mutation",
886
+ sideEffects: "state",
887
+ idempotent: true,
888
+ retry: "idempotent",
889
+ readConsistency: null
890
+ },
891
+ auth: {
892
+ credentials: ["user_key", "partner_key"],
893
+ scopes: ["sites:write"],
894
+ ownership: [{
895
+ credential: "user_key",
896
+ rule: "self"
897
+ }, {
898
+ credential: "partner_key",
899
+ rule: "linked_user"
900
+ }]
901
+ },
902
+ request: {
903
+ params: z.strictObject({ userId: realtimeSchemas.publicUserId }),
904
+ query: null,
905
+ headers: requestHeaders,
906
+ body: registerSiteRequest
907
+ },
908
+ responses: { 200: siteRegistrationResponse },
909
+ errors: partnerUserErrors,
910
+ errorResponse: errorEnvelopeSchemas(partnerUserErrors, realtimeSchemas.publicRequestId),
911
+ resources: {
912
+ reads: [{
913
+ type: "partner.user",
914
+ idFrom: "params.userId"
915
+ }],
916
+ changes: [{
917
+ type: "site.registration",
918
+ idFrom: "params.userId"
919
+ }]
920
+ },
921
+ lifecycle: { introduced: "1.0.0" },
922
+ docs: {
923
+ summary: "Register a site",
924
+ description: "Registers one Search Console property for an authorized user.",
925
+ tags: ["Sites"],
926
+ examples: {
927
+ request: {
928
+ params: { userId: "u_01" },
929
+ body: { siteUrl: "sc-domain:example.com" }
930
+ },
931
+ response: {
932
+ data: {
933
+ siteId: "s_01",
934
+ status: "pending"
935
+ },
936
+ meta: {
937
+ requestId: "req_01",
938
+ surface: "partner",
939
+ version: "1.0"
940
+ }
941
+ }
942
+ }
943
+ }
944
+ }),
945
+ createUser: defineHttpOperation({
946
+ id: "partner.users.create",
947
+ method: "POST",
948
+ path: "/users",
949
+ visibility: "public",
950
+ semantics: {
951
+ kind: "mutation",
952
+ sideEffects: "state",
953
+ idempotent: true,
954
+ retry: "idempotent",
955
+ readConsistency: null
956
+ },
957
+ auth: {
958
+ credentials: ["partner_key"],
959
+ scopes: ["users:write"],
960
+ ownership: [{
961
+ credential: "partner_key",
962
+ rule: "partner_tenant"
963
+ }]
964
+ },
965
+ request: {
966
+ params: null,
967
+ query: null,
968
+ headers: requestHeaders,
969
+ body: registerPartnerUserSchema.strict()
970
+ },
971
+ responses: { 200: userRegistrationResponse },
972
+ errors: partnerUserErrors,
973
+ errorResponse: errorEnvelopeSchemas(partnerUserErrors, realtimeSchemas.publicRequestId),
974
+ resources: {
975
+ reads: [],
976
+ changes: [{
977
+ type: "partner.user",
978
+ idFrom: "principal.id"
979
+ }]
980
+ },
981
+ lifecycle: { introduced: "1.0.0" },
982
+ docs: {
983
+ summary: "Register a partner user",
984
+ description: "Creates or updates a user owned by the authenticated partner tenant.",
985
+ tags: ["Users"],
986
+ examples: {
987
+ request: { body: {
988
+ userGoogleId: "google_01",
989
+ userEmail: "owner@example.com",
990
+ accessToken: "token",
991
+ refreshToken: "refresh"
992
+ } },
993
+ response: {
994
+ data: {
995
+ userId: "u_01",
996
+ status: "provisioning"
997
+ },
998
+ meta: {
999
+ requestId: "req_01",
1000
+ surface: "partner",
1001
+ version: "1.0"
1002
+ }
1003
+ }
1004
+ }
1005
+ }
1006
+ }),
1007
+ updateUserTokens: defineHttpOperation({
1008
+ id: "partner.users.tokens.update",
1009
+ method: "PATCH",
1010
+ path: "/users/{userId}/tokens",
1011
+ visibility: "public",
1012
+ semantics: {
1013
+ kind: "mutation",
1014
+ sideEffects: "state",
1015
+ idempotent: true,
1016
+ retry: "idempotent",
1017
+ readConsistency: null
1018
+ },
1019
+ auth: {
1020
+ credentials: ["partner_key"],
1021
+ scopes: ["users:write"],
1022
+ ownership: [{
1023
+ credential: "partner_key",
1024
+ rule: "linked_user"
1025
+ }]
1026
+ },
1027
+ request: {
1028
+ params: z.strictObject({ userId: realtimeSchemas.publicUserId }),
1029
+ query: null,
1030
+ headers: requestHeaders,
1031
+ body: updatePartnerUserTokensSchema.strict()
1032
+ },
1033
+ responses: { 200: userTokenUpdateResponse },
1034
+ errors: partnerUserErrors,
1035
+ errorResponse: errorEnvelopeSchemas(partnerUserErrors, realtimeSchemas.publicRequestId),
1036
+ resources: {
1037
+ reads: [{
1038
+ type: "partner.user",
1039
+ idFrom: "params.userId"
1040
+ }],
1041
+ changes: [{
1042
+ type: "partner.user",
1043
+ idFrom: "params.userId"
1044
+ }]
1045
+ },
1046
+ lifecycle: { introduced: "1.0.0" },
1047
+ docs: {
1048
+ summary: "Update user OAuth tokens",
1049
+ description: "Replaces the Google OAuth grant for a partner-linked user.",
1050
+ tags: ["Users"],
1051
+ examples: {
1052
+ request: {
1053
+ params: { userId: "u_01" },
1054
+ body: {
1055
+ accessToken: "token",
1056
+ refreshToken: "refresh"
1057
+ }
1058
+ },
1059
+ response: {
1060
+ data: {
1061
+ userId: "u_01",
1062
+ updated: true,
1063
+ sites: []
1064
+ },
1065
+ meta: {
1066
+ requestId: "req_01",
1067
+ surface: "partner",
1068
+ version: "1.0"
1069
+ }
1070
+ }
1071
+ }
1072
+ }
1073
+ }),
1074
+ getSiteIndexing: defineHttpOperation({
1075
+ id: "partner.sites.indexing.get",
1076
+ method: "GET",
1077
+ path: "/sites/{siteId}/indexing",
1078
+ visibility: "public",
1079
+ semantics: {
1080
+ kind: "query",
1081
+ sideEffects: "none",
1082
+ idempotent: true,
1083
+ retry: "idempotent",
1084
+ readConsistency: "primary"
1085
+ },
1086
+ auth: {
1087
+ credentials: ["user_key", "partner_key"],
1088
+ scopes: ["indexing:read"],
1089
+ ownership: [{
1090
+ credential: "user_key",
1091
+ rule: "authorized_site"
1092
+ }, {
1093
+ credential: "partner_key",
1094
+ rule: "authorized_site"
1095
+ }]
1096
+ },
1097
+ request: {
1098
+ params: z.strictObject({ siteId: realtimeSchemas.publicSiteId }),
1099
+ query: indexingSummaryQuery,
1100
+ headers: requestHeaders,
1101
+ body: null
1102
+ },
1103
+ responses: { 200: indexingSummaryResponse },
1104
+ errors: partnerSiteErrors,
1105
+ errorResponse: errorEnvelopeSchemas(partnerSiteErrors, realtimeSchemas.publicRequestId),
1106
+ resources: {
1107
+ reads: [{
1108
+ type: "site.indexing",
1109
+ idFrom: "params.siteId"
1110
+ }],
1111
+ changes: []
1112
+ },
1113
+ lifecycle: { introduced: "1.0.0" },
1114
+ docs: {
1115
+ summary: "Get site indexing summary",
1116
+ description: "Returns indexing coverage, trend, and inspection signal totals.",
1117
+ tags: ["Indexing"],
1118
+ examples: {
1119
+ request: {
1120
+ params: { siteId: "s_01" },
1121
+ query: { days: 28 }
1122
+ },
1123
+ response: {
1124
+ data: {
1125
+ trend: [],
1126
+ summary: {
1127
+ totalUrls: 0,
1128
+ indexed: 0,
1129
+ notIndexed: 0,
1130
+ pending: 0,
1131
+ indexedPercent: 0,
1132
+ oldestCheck: null,
1133
+ newestCheck: null,
1134
+ change7d: null,
1135
+ change28d: null,
1136
+ signals: {
1137
+ mobilePass: 0,
1138
+ mobileFail: 0,
1139
+ mobileUnspecified: 0,
1140
+ richResultsPass: 0,
1141
+ richResultsFail: 0,
1142
+ richResultTypes: [],
1143
+ crawlingMobile: 0,
1144
+ crawlingDesktop: 0
1145
+ }
1146
+ },
1147
+ meta: {
1148
+ siteUrl: "sc-domain:example.com",
1149
+ syncStatus: "synced",
1150
+ indexingStatus: "complete",
1151
+ indexingProgress: 100,
1152
+ sitemapTotal: 0,
1153
+ inspectedCount: 0,
1154
+ noSitemapsSubmitted: false,
1155
+ sitemapsPending: false
1156
+ }
1157
+ },
1158
+ meta: {
1159
+ requestId: "req_01",
1160
+ surface: "partner",
1161
+ version: "1.0"
1162
+ }
1163
+ }
1164
+ }
1165
+ }
1166
+ }),
1167
+ listSiteIndexingUrls: defineHttpOperation({
1168
+ id: "partner.sites.indexing.urls.list",
1169
+ method: "GET",
1170
+ path: "/sites/{siteId}/indexing/urls",
1171
+ visibility: "public",
1172
+ semantics: {
1173
+ kind: "query",
1174
+ sideEffects: "none",
1175
+ idempotent: true,
1176
+ retry: "idempotent",
1177
+ readConsistency: "primary"
1178
+ },
1179
+ auth: {
1180
+ credentials: ["user_key", "partner_key"],
1181
+ scopes: ["indexing:read"],
1182
+ ownership: [{
1183
+ credential: "user_key",
1184
+ rule: "authorized_site"
1185
+ }, {
1186
+ credential: "partner_key",
1187
+ rule: "authorized_site"
1188
+ }]
1189
+ },
1190
+ request: {
1191
+ params: z.strictObject({ siteId: realtimeSchemas.publicSiteId }),
1192
+ query: indexingUrlsQuery,
1193
+ headers: requestHeaders,
1194
+ body: null
1195
+ },
1196
+ responses: { 200: indexingUrlsResponse },
1197
+ errors: partnerSiteErrors,
1198
+ errorResponse: errorEnvelopeSchemas(partnerSiteErrors, realtimeSchemas.publicRequestId),
1199
+ resources: {
1200
+ reads: [{
1201
+ type: "site.indexing",
1202
+ idFrom: "params.siteId"
1203
+ }],
1204
+ changes: []
1205
+ },
1206
+ lifecycle: { introduced: "1.0.0" },
1207
+ docs: {
1208
+ summary: "List indexed URLs",
1209
+ description: "Returns the filtered URL-inspection index with pagination metadata.",
1210
+ tags: ["Indexing"],
1211
+ examples: {
1212
+ request: {
1213
+ params: { siteId: "s_01" },
1214
+ query: {
1215
+ limit: 100,
1216
+ offset: 0
1217
+ }
1218
+ },
1219
+ response: {
1220
+ data: {
1221
+ urls: [],
1222
+ pagination: {
1223
+ total: 0,
1224
+ limit: 100,
1225
+ offset: 0,
1226
+ hasMore: false
1227
+ },
1228
+ meta: {
1229
+ siteUrl: "sc-domain:example.com",
1230
+ status: "all",
1231
+ issue: null
1232
+ }
1233
+ },
1234
+ meta: {
1235
+ requestId: "req_01",
1236
+ surface: "partner",
1237
+ version: "1.0"
1238
+ }
1239
+ }
1240
+ }
1241
+ }
1242
+ }),
1243
+ getSiteIndexingDiagnostics: defineHttpOperation({
1244
+ id: "partner.sites.indexing.diagnostics.get",
1245
+ method: "GET",
1246
+ path: "/sites/{siteId}/indexing/diagnostics",
1247
+ visibility: "public",
1248
+ semantics: {
1249
+ kind: "query",
1250
+ sideEffects: "none",
1251
+ idempotent: true,
1252
+ retry: "idempotent",
1253
+ readConsistency: "primary"
1254
+ },
1255
+ auth: {
1256
+ credentials: ["user_key", "partner_key"],
1257
+ scopes: ["indexing:read"],
1258
+ ownership: [{
1259
+ credential: "user_key",
1260
+ rule: "authorized_site"
1261
+ }, {
1262
+ credential: "partner_key",
1263
+ rule: "authorized_site"
1264
+ }]
1265
+ },
1266
+ request: {
1267
+ params: z.strictObject({ siteId: realtimeSchemas.publicSiteId }),
1268
+ query: indexingDiagnosticsQuery,
1269
+ headers: requestHeaders,
1270
+ body: null
1271
+ },
1272
+ responses: { 200: indexingDiagnosticsResponse },
1273
+ errors: partnerSiteErrors,
1274
+ errorResponse: errorEnvelopeSchemas(partnerSiteErrors, realtimeSchemas.publicRequestId),
1275
+ resources: {
1276
+ reads: [{
1277
+ type: "site.indexing",
1278
+ idFrom: "params.siteId"
1279
+ }],
1280
+ changes: []
1281
+ },
1282
+ lifecycle: { introduced: "1.0.0" },
1283
+ docs: {
1284
+ summary: "Get indexing diagnostics",
1285
+ description: "Returns indexing issue groups and optional URL samples.",
1286
+ tags: ["Indexing"],
1287
+ examples: {
1288
+ request: {
1289
+ params: { siteId: "s_01" },
1290
+ query: {}
1291
+ },
1292
+ response: {
1293
+ data: {
1294
+ summary: {
1295
+ totalUrls: 0,
1296
+ indexed: 0,
1297
+ indexedPercent: 0
1298
+ },
1299
+ issues: [],
1300
+ meta: { siteUrl: "sc-domain:example.com" }
1301
+ },
1302
+ meta: {
1303
+ requestId: "req_01",
1304
+ surface: "partner",
1305
+ version: "1.0"
1306
+ }
1307
+ }
1308
+ }
1309
+ }
1310
+ }),
1311
+ getSiteSitemaps: defineHttpOperation({
1312
+ id: "partner.sites.sitemaps.get",
1313
+ method: "GET",
1314
+ path: "/sites/{siteId}/sitemaps",
1315
+ visibility: "public",
1316
+ semantics: {
1317
+ kind: "query",
1318
+ sideEffects: "none",
1319
+ idempotent: true,
1320
+ retry: "idempotent",
1321
+ readConsistency: "primary"
1322
+ },
1323
+ auth: {
1324
+ credentials: ["user_key", "partner_key"],
1325
+ scopes: ["sitemaps:read"],
1326
+ ownership: [{
1327
+ credential: "user_key",
1328
+ rule: "authorized_site"
1329
+ }, {
1330
+ credential: "partner_key",
1331
+ rule: "authorized_site"
1332
+ }]
1333
+ },
1334
+ request: {
1335
+ params: z.strictObject({ siteId: realtimeSchemas.publicSiteId }),
1336
+ query: null,
1337
+ headers: requestHeaders,
1338
+ body: null
1339
+ },
1340
+ responses: { 200: sitemapsResponse },
1341
+ errors: partnerSiteErrors,
1342
+ errorResponse: errorEnvelopeSchemas(partnerSiteErrors, realtimeSchemas.publicRequestId),
1343
+ resources: {
1344
+ reads: [{
1345
+ type: "site.sitemaps",
1346
+ idFrom: "params.siteId"
1347
+ }],
1348
+ changes: []
1349
+ },
1350
+ lifecycle: { introduced: "1.0.0" },
1351
+ docs: {
1352
+ summary: "Get sitemap snapshot",
1353
+ description: "Returns registered sitemaps and their aggregate and per-sitemap history.",
1354
+ tags: ["Sitemaps"],
1355
+ examples: {
1356
+ request: { params: { siteId: "s_01" } },
1357
+ response: {
1358
+ data: {
1359
+ sitemaps: [],
1360
+ history: [],
1361
+ perSitemapHistory: {},
1362
+ meta: {
1363
+ siteUrl: "sc-domain:example.com",
1364
+ gscPropertyUrl: "sc-domain:example.com",
1365
+ syncStatus: "synced"
1366
+ }
1367
+ },
1368
+ meta: {
1369
+ requestId: "req_01",
1370
+ surface: "partner",
1371
+ version: "1.0"
1372
+ }
1373
+ }
1374
+ }
1375
+ }
1376
+ }),
1377
+ getSiteSitemapChanges: defineHttpOperation({
1378
+ id: "partner.sites.sitemaps.changes.get",
1379
+ method: "GET",
1380
+ path: "/sites/{siteId}/sitemaps/changes",
1381
+ visibility: "public",
1382
+ semantics: {
1383
+ kind: "query",
1384
+ sideEffects: "none",
1385
+ idempotent: true,
1386
+ retry: "idempotent",
1387
+ readConsistency: "primary"
1388
+ },
1389
+ auth: {
1390
+ credentials: ["user_key", "partner_key"],
1391
+ scopes: ["sitemaps:read"],
1392
+ ownership: [{
1393
+ credential: "user_key",
1394
+ rule: "authorized_site"
1395
+ }, {
1396
+ credential: "partner_key",
1397
+ rule: "authorized_site"
1398
+ }]
1399
+ },
1400
+ request: {
1401
+ params: z.strictObject({ siteId: realtimeSchemas.publicSiteId }),
1402
+ query: sitemapChangesQuery,
1403
+ headers: requestHeaders,
1404
+ body: null
1405
+ },
1406
+ responses: { 200: sitemapChangesResponse },
1407
+ errors: partnerSiteErrors,
1408
+ errorResponse: errorEnvelopeSchemas(partnerSiteErrors, realtimeSchemas.publicRequestId),
1409
+ resources: {
1410
+ reads: [{
1411
+ type: "site.sitemaps",
1412
+ idFrom: "params.siteId"
1413
+ }],
1414
+ changes: []
1415
+ },
1416
+ lifecycle: { introduced: "1.0.0" },
1417
+ docs: {
1418
+ summary: "Get sitemap URL changes",
1419
+ description: "Returns recently added and removed sitemap URLs for the requested window.",
1420
+ tags: ["Sitemaps"],
1421
+ examples: {
1422
+ request: {
1423
+ params: { siteId: "s_01" },
1424
+ query: { days: 28 }
1425
+ },
1426
+ response: {
1427
+ data: {
1428
+ added: [],
1429
+ removed: [],
1430
+ summary: {
1431
+ totalAdded: 0,
1432
+ totalRemoved: 0,
1433
+ period: { days: 28 }
1434
+ }
1435
+ },
1436
+ meta: {
1437
+ requestId: "req_01",
1438
+ surface: "partner",
1439
+ version: "1.0"
1440
+ }
1441
+ }
1442
+ }
1443
+ }
1444
+ }),
1445
+ getSiteAnalysis: defineHttpOperation({
1446
+ id: "partner.sites.analysis.get",
1447
+ method: "GET",
1448
+ path: "/sites/{siteId}/analysis",
1449
+ visibility: "public",
1450
+ semantics: {
1451
+ kind: "query",
1452
+ sideEffects: "none",
1453
+ idempotent: true,
1454
+ retry: "idempotent",
1455
+ readConsistency: "primary"
1456
+ },
1457
+ auth: {
1458
+ credentials: ["user_key", "partner_key"],
1459
+ scopes: ["analytics:execute"],
1460
+ ownership: [{
1461
+ credential: "user_key",
1462
+ rule: "authorized_site"
1463
+ }, {
1464
+ credential: "partner_key",
1465
+ rule: "authorized_site"
1466
+ }]
1467
+ },
1468
+ request: {
1469
+ params: z.strictObject({ siteId: realtimeSchemas.publicSiteId }),
1470
+ query: analysisQuery,
1471
+ headers: requestHeaders,
1472
+ body: null
1473
+ },
1474
+ responses: { 200: analysisResponse },
1475
+ errors: partnerSiteErrors,
1476
+ errorResponse: errorEnvelopeSchemas(partnerSiteErrors, realtimeSchemas.publicRequestId),
1477
+ resources: {
1478
+ reads: [{
1479
+ type: "site.analytics",
1480
+ idFrom: "params.siteId"
1481
+ }],
1482
+ changes: []
1483
+ },
1484
+ lifecycle: { introduced: "1.0.0" },
1485
+ docs: {
1486
+ summary: "Run an analysis preset",
1487
+ description: "Runs one validated analysis preset for an analytics window.",
1488
+ tags: ["Analysis"],
1489
+ examples: {
1490
+ request: {
1491
+ params: { siteId: "s_01" },
1492
+ query: {
1493
+ preset: "opportunity",
1494
+ startDate: "2026-06-01",
1495
+ endDate: "2026-06-30"
1496
+ }
1497
+ },
1498
+ response: {
1499
+ data: {
1500
+ preset: "opportunity",
1501
+ keywords: [],
1502
+ totalCount: 0,
1503
+ meta: {
1504
+ siteUrl: "sc-domain:example.com",
1505
+ params: {
1506
+ startDate: "2026-06-01",
1507
+ endDate: "2026-06-30"
1508
+ }
1509
+ }
1510
+ },
1511
+ meta: {
1512
+ requestId: "req_01",
1513
+ surface: "partner",
1514
+ version: "1.0"
1515
+ }
1516
+ }
1517
+ }
1518
+ }
1519
+ }),
1520
+ getSiteAnalysisBundle: defineHttpOperation({
1521
+ id: "partner.sites.analysis.bundle.get",
1522
+ method: "GET",
1523
+ path: "/sites/{siteId}/analysis/bundle",
1524
+ visibility: "public",
1525
+ semantics: {
1526
+ kind: "query",
1527
+ sideEffects: "none",
1528
+ idempotent: true,
1529
+ retry: "idempotent",
1530
+ readConsistency: "primary"
1531
+ },
1532
+ auth: {
1533
+ credentials: ["user_key", "partner_key"],
1534
+ scopes: ["analytics:execute"],
1535
+ ownership: [{
1536
+ credential: "user_key",
1537
+ rule: "authorized_site"
1538
+ }, {
1539
+ credential: "partner_key",
1540
+ rule: "authorized_site"
1541
+ }]
1542
+ },
1543
+ request: {
1544
+ params: z.strictObject({ siteId: realtimeSchemas.publicSiteId }),
1545
+ query: analysisBundleQuery,
1546
+ headers: requestHeaders,
1547
+ body: null
1548
+ },
1549
+ responses: { 200: analysisBundleResponse },
1550
+ errors: partnerSiteErrors,
1551
+ errorResponse: errorEnvelopeSchemas(partnerSiteErrors, realtimeSchemas.publicRequestId),
1552
+ resources: {
1553
+ reads: [{
1554
+ type: "site.analytics",
1555
+ idFrom: "params.siteId"
1556
+ }],
1557
+ changes: []
1558
+ },
1559
+ lifecycle: { introduced: "1.0.0" },
1560
+ docs: {
1561
+ summary: "Run an analysis bundle",
1562
+ description: "Runs several validated presets against one shared analytics pool.",
1563
+ tags: ["Analysis"],
1564
+ examples: {
1565
+ request: {
1566
+ params: { siteId: "s_01" },
1567
+ query: {
1568
+ presets: ["opportunity"],
1569
+ startDate: "2026-06-01",
1570
+ endDate: "2026-06-30"
1571
+ }
1572
+ },
1573
+ response: {
1574
+ data: {
1575
+ bundle: {},
1576
+ meta: {
1577
+ siteUrl: "sc-domain:example.com",
1578
+ params: {
1579
+ startDate: "2026-06-01",
1580
+ endDate: "2026-06-30"
1581
+ }
1582
+ }
1583
+ },
1584
+ meta: {
1585
+ requestId: "req_01",
1586
+ surface: "partner",
1587
+ version: "1.0"
1588
+ }
1589
+ }
1590
+ }
1591
+ }
1592
+ }),
1593
+ deleteSite: defineHttpOperation({
1594
+ id: "partner.sites.delete",
1595
+ method: "DELETE",
1596
+ path: "/sites/{siteId}",
1597
+ visibility: "public",
1598
+ semantics: {
1599
+ kind: "mutation",
1600
+ sideEffects: "state",
1601
+ idempotent: true,
1602
+ retry: "idempotent",
1603
+ readConsistency: null
1604
+ },
1605
+ auth: {
1606
+ credentials: ["user_key", "partner_key"],
1607
+ scopes: ["sites:write"],
1608
+ ownership: [{
1609
+ credential: "user_key",
1610
+ rule: "authorized_site"
1611
+ }, {
1612
+ credential: "partner_key",
1613
+ rule: "authorized_site"
1614
+ }]
1615
+ },
1616
+ request: {
1617
+ params: z.strictObject({ siteId: realtimeSchemas.publicSiteId }),
1618
+ query: null,
1619
+ headers: requestHeaders,
1620
+ body: null
1621
+ },
1622
+ responses: { 200: siteDeletionResponse },
1623
+ errors: partnerSiteErrors,
1624
+ errorResponse: errorEnvelopeSchemas(partnerSiteErrors, realtimeSchemas.publicRequestId),
1625
+ resources: {
1626
+ reads: [{
1627
+ type: "site.registration",
1628
+ idFrom: "params.siteId"
1629
+ }],
1630
+ changes: [{
1631
+ type: "site.registration",
1632
+ idFrom: "params.siteId"
1633
+ }, {
1634
+ type: "site.lifecycle",
1635
+ idFrom: "params.siteId"
1636
+ }]
1637
+ },
1638
+ lifecycle: { introduced: "1.0.0" },
1639
+ docs: {
1640
+ summary: "Delete a site registration",
1641
+ description: "Removes the site registration and queued work while retaining the user data store.",
1642
+ tags: ["Sites"],
1643
+ examples: {
1644
+ request: { params: { siteId: "s_01" } },
1645
+ response: {
1646
+ data: {
1647
+ deleted: true,
1648
+ siteId: "s_01",
1649
+ siteUrl: "example.com"
1650
+ },
1651
+ meta: {
1652
+ requestId: "req_01",
1653
+ surface: "partner",
1654
+ version: "1.0"
1655
+ }
1656
+ }
1657
+ }
1658
+ }
1659
+ }),
1660
+ getCanonicalMismatches: defineHttpOperation({
1661
+ id: "partner.sites.canonical.mismatches.get",
1662
+ method: "GET",
1663
+ path: "/sites/{siteId}/canonical-mismatches",
1664
+ visibility: "public",
1665
+ semantics: {
1666
+ kind: "query",
1667
+ sideEffects: "none",
1668
+ idempotent: true,
1669
+ retry: "idempotent",
1670
+ readConsistency: "primary"
1671
+ },
1672
+ auth: {
1673
+ credentials: ["user_key", "partner_key"],
1674
+ scopes: ["indexing:read"],
1675
+ ownership: [{
1676
+ credential: "user_key",
1677
+ rule: "authorized_site"
1678
+ }, {
1679
+ credential: "partner_key",
1680
+ rule: "authorized_site"
1681
+ }]
1682
+ },
1683
+ request: {
1684
+ params: z.strictObject({ siteId: realtimeSchemas.publicSiteId }),
1685
+ query: null,
1686
+ headers: requestHeaders,
1687
+ body: null
1688
+ },
1689
+ responses: { 200: canonicalMismatchesResponse },
1690
+ errors: partnerSiteErrors,
1691
+ errorResponse: errorEnvelopeSchemas(partnerSiteErrors, realtimeSchemas.publicRequestId),
1692
+ resources: {
1693
+ reads: [{
1694
+ type: "site.indexing",
1695
+ idFrom: "params.siteId"
1696
+ }],
1697
+ changes: []
1698
+ },
1699
+ lifecycle: { introduced: "1.1.0" },
1700
+ docs: {
1701
+ summary: "List canonical mismatches",
1702
+ description: "Returns sitemap-scoped URLs whose Google-chosen canonical diverges from the declared canonical, with consolidation targets and trend.",
1703
+ tags: ["Indexing"],
1704
+ examples: {
1705
+ request: { params: { siteId: "s_01" } },
1706
+ response: {
1707
+ data: {
1708
+ mismatches: [],
1709
+ totalCount: 0,
1710
+ consolidationTargets: [],
1711
+ trend: [],
1712
+ meta: {
1713
+ siteUrl: "sc-domain:example.com",
1714
+ syncStatus: "synced"
1715
+ }
1716
+ },
1717
+ meta: {
1718
+ requestId: "req_01",
1719
+ surface: "partner",
1720
+ version: "1.0"
1721
+ }
1722
+ }
1723
+ }
1724
+ }
1725
+ }),
1726
+ inspectSiteUrls: defineHttpOperation({
1727
+ id: "partner.sites.indexing.inspect.create",
1728
+ method: "POST",
1729
+ path: "/sites/{siteId}/indexing/inspect",
1730
+ visibility: "public",
1731
+ semantics: {
1732
+ kind: "mutation",
1733
+ sideEffects: "state",
1734
+ idempotent: false,
1735
+ retry: "never",
1736
+ readConsistency: null
1737
+ },
1738
+ auth: {
1739
+ credentials: ["user_key", "partner_key"],
1740
+ scopes: ["indexing:write"],
1741
+ ownership: [{
1742
+ credential: "user_key",
1743
+ rule: "authorized_site"
1744
+ }, {
1745
+ credential: "partner_key",
1746
+ rule: "authorized_site"
1747
+ }]
1748
+ },
1749
+ request: {
1750
+ params: z.strictObject({ siteId: realtimeSchemas.publicSiteId }),
1751
+ query: null,
1752
+ headers: requestHeaders,
1753
+ body: indexingInspectRequest
1754
+ },
1755
+ responses: { 200: indexingInspectionResponse },
1756
+ errors: partnerSiteErrors,
1757
+ errorResponse: errorEnvelopeSchemas(partnerSiteErrors, realtimeSchemas.publicRequestId),
1758
+ resources: {
1759
+ reads: [{
1760
+ type: "site.indexing",
1761
+ idFrom: "params.siteId"
1762
+ }],
1763
+ changes: [{
1764
+ type: "site.indexing",
1765
+ idFrom: "params.siteId"
1766
+ }]
1767
+ },
1768
+ lifecycle: { introduced: "1.1.0" },
1769
+ docs: {
1770
+ summary: "Inspect URLs on demand",
1771
+ description: "Runs live URL inspections against the daily per-site quota; URLs outside the site domain and over-quota URLs are reported as skipped. Exhausted quota fails with rate_limited.",
1772
+ tags: ["Indexing"],
1773
+ examples: {
1774
+ request: {
1775
+ params: { siteId: "s_01" },
1776
+ body: { urls: ["https://example.com/"] }
1777
+ },
1778
+ response: {
1779
+ data: {
1780
+ siteId: "s_01",
1781
+ rateLimit: {
1782
+ reserved: 1,
1783
+ remaining: 199,
1784
+ limit: 200
1785
+ },
1786
+ results: [],
1787
+ errors: [],
1788
+ skipped: []
1789
+ },
1790
+ meta: {
1791
+ requestId: "req_01",
1792
+ surface: "partner",
1793
+ version: "1.0"
1794
+ }
1795
+ }
1796
+ }
1797
+ }
1798
+ }),
1799
+ recoverSitePermission: defineHttpOperation({
1800
+ id: "partner.sites.permission.recover",
1801
+ method: "POST",
1802
+ path: "/sites/{siteId}/permission/recover",
1803
+ visibility: "public",
1804
+ semantics: {
1805
+ kind: "mutation",
1806
+ sideEffects: "state",
1807
+ idempotent: true,
1808
+ retry: "idempotent",
1809
+ readConsistency: null
1810
+ },
1811
+ auth: {
1812
+ credentials: ["user_key", "partner_key"],
1813
+ scopes: ["sites:write"],
1814
+ ownership: [{
1815
+ credential: "user_key",
1816
+ rule: "authorized_site"
1817
+ }, {
1818
+ credential: "partner_key",
1819
+ rule: "authorized_site"
1820
+ }]
1821
+ },
1822
+ request: {
1823
+ params: z.strictObject({ siteId: realtimeSchemas.publicSiteId }),
1824
+ query: null,
1825
+ headers: requestHeaders,
1826
+ body: null
1827
+ },
1828
+ responses: { 200: permissionRecoveryResponse },
1829
+ errors: partnerSiteErrors,
1830
+ errorResponse: errorEnvelopeSchemas(partnerSiteErrors, realtimeSchemas.publicRequestId),
1831
+ resources: {
1832
+ reads: [{
1833
+ type: "site.auth",
1834
+ idFrom: "params.siteId"
1835
+ }],
1836
+ changes: [{
1837
+ type: "site.auth",
1838
+ idFrom: "params.siteId"
1839
+ }, {
1840
+ type: "site.lifecycle",
1841
+ idFrom: "params.siteId"
1842
+ }]
1843
+ },
1844
+ lifecycle: { introduced: "1.1.0" },
1845
+ docs: {
1846
+ summary: "Re-check and recover site permission",
1847
+ description: "Force-rechecks Google Search Console access for a permission-lost site; on recovery resets sync state and queues fresh sync jobs.",
1848
+ tags: ["Sites"],
1849
+ examples: {
1850
+ request: { params: { siteId: "s_01" } },
1851
+ response: {
1852
+ data: {
1853
+ success: true,
1854
+ permissionLevel: "siteFullUser",
1855
+ jobsQueued: 3,
1856
+ message: "Permission restored (siteFullUser). Queued 3 sync jobs."
1857
+ },
1858
+ meta: {
1859
+ requestId: "req_01",
1860
+ surface: "partner",
1861
+ version: "1.0"
1862
+ }
1863
+ }
1864
+ }
1865
+ }
1866
+ }),
1867
+ queryKeywordSparklines: defineHttpOperation({
1868
+ id: "partner.sites.keyword.sparklines.query",
1869
+ method: "POST",
1870
+ path: "/sites/{siteId}/keyword-sparklines",
1871
+ visibility: "public",
1872
+ semantics: {
1873
+ kind: "query",
1874
+ sideEffects: "none",
1875
+ idempotent: true,
1876
+ retry: "idempotent",
1877
+ readConsistency: "primary"
1878
+ },
1879
+ auth: {
1880
+ credentials: ["user_key", "partner_key"],
1881
+ scopes: ["analytics:read"],
1882
+ ownership: [{
1883
+ credential: "user_key",
1884
+ rule: "authorized_site"
1885
+ }, {
1886
+ credential: "partner_key",
1887
+ rule: "authorized_site"
1888
+ }]
1889
+ },
1890
+ request: {
1891
+ params: z.strictObject({ siteId: realtimeSchemas.publicSiteId }),
1892
+ query: null,
1893
+ headers: requestHeaders,
1894
+ body: keywordSparklinesRequest
1895
+ },
1896
+ responses: { 200: keywordSparklinesResponse },
1897
+ errors: partnerSiteErrors,
1898
+ errorResponse: errorEnvelopeSchemas(partnerSiteErrors, realtimeSchemas.publicRequestId),
1899
+ resources: {
1900
+ reads: [{
1901
+ type: "site.analytics",
1902
+ idFrom: "params.siteId"
1903
+ }],
1904
+ changes: []
1905
+ },
1906
+ lifecycle: { introduced: "1.1.0" },
1907
+ docs: {
1908
+ summary: "Query keyword sparklines",
1909
+ description: "Returns per-keyword daily click series for up to 20 keywords over the requested window.",
1910
+ tags: ["Analytics"],
1911
+ examples: {
1912
+ request: {
1913
+ params: { siteId: "s_01" },
1914
+ body: {
1915
+ keywords: ["nuxt seo"],
1916
+ startDate: "2026-06-01",
1917
+ endDate: "2026-06-28"
1918
+ }
1919
+ },
1920
+ response: {
1921
+ data: { sparklines: { "nuxt seo": [
1922
+ 0,
1923
+ 1,
1924
+ 2
1925
+ ] } },
1926
+ meta: {
1927
+ requestId: "req_01",
1928
+ surface: "partner",
1929
+ version: "1.0"
1930
+ }
1931
+ }
1932
+ }
1933
+ }
1934
+ }),
1935
+ getQueryTrend: defineHttpOperation({
1936
+ id: "partner.sites.query.trend.get",
1937
+ method: "GET",
1938
+ path: "/sites/{siteId}/query-trend",
1939
+ visibility: "public",
1940
+ semantics: {
1941
+ kind: "query",
1942
+ sideEffects: "none",
1943
+ idempotent: true,
1944
+ retry: "idempotent",
1945
+ readConsistency: "primary"
1946
+ },
1947
+ auth: {
1948
+ credentials: ["user_key", "partner_key"],
1949
+ scopes: ["analytics:read"],
1950
+ ownership: [{
1951
+ credential: "user_key",
1952
+ rule: "authorized_site"
1953
+ }, {
1954
+ credential: "partner_key",
1955
+ rule: "authorized_site"
1956
+ }]
1957
+ },
1958
+ request: {
1959
+ params: z.strictObject({ siteId: realtimeSchemas.publicSiteId }),
1960
+ query: trendQuery,
1961
+ headers: requestHeaders,
1962
+ body: null
1963
+ },
1964
+ responses: { 200: queryTrendResponse },
1965
+ errors: partnerSiteErrors,
1966
+ errorResponse: errorEnvelopeSchemas(partnerSiteErrors, realtimeSchemas.publicRequestId),
1967
+ resources: {
1968
+ reads: [{
1969
+ type: "site.analytics",
1970
+ idFrom: "params.siteId"
1971
+ }],
1972
+ changes: []
1973
+ },
1974
+ lifecycle: { introduced: "1.1.0" },
1975
+ docs: {
1976
+ summary: "Get unique-query trend",
1977
+ description: "Returns the daily unique-query count series for a window, with an optional previous-period total for comparison.",
1978
+ tags: ["Analytics"],
1979
+ examples: {
1980
+ request: {
1981
+ params: { siteId: "s_01" },
1982
+ query: {
1983
+ startDate: "2026-06-01",
1984
+ endDate: "2026-06-28"
1985
+ }
1986
+ },
1987
+ response: {
1988
+ data: {
1989
+ daily: [],
1990
+ total: 0,
1991
+ meta: {
1992
+ siteUrl: "sc-domain:example.com",
1993
+ syncStatus: "synced"
1994
+ }
1995
+ },
1996
+ meta: {
1997
+ requestId: "req_01",
1998
+ surface: "partner",
1999
+ version: "1.0"
2000
+ }
2001
+ }
2002
+ }
2003
+ }
2004
+ }),
2005
+ getPageTrend: defineHttpOperation({
2006
+ id: "partner.sites.page.trend.get",
2007
+ method: "GET",
2008
+ path: "/sites/{siteId}/page-trend",
2009
+ visibility: "public",
2010
+ semantics: {
2011
+ kind: "query",
2012
+ sideEffects: "none",
2013
+ idempotent: true,
2014
+ retry: "idempotent",
2015
+ readConsistency: "primary"
2016
+ },
2017
+ auth: {
2018
+ credentials: ["user_key", "partner_key"],
2019
+ scopes: ["analytics:read"],
2020
+ ownership: [{
2021
+ credential: "user_key",
2022
+ rule: "authorized_site"
2023
+ }, {
2024
+ credential: "partner_key",
2025
+ rule: "authorized_site"
2026
+ }]
2027
+ },
2028
+ request: {
2029
+ params: z.strictObject({ siteId: realtimeSchemas.publicSiteId }),
2030
+ query: trendQuery,
2031
+ headers: requestHeaders,
2032
+ body: null
2033
+ },
2034
+ responses: { 200: pageTrendResponse },
2035
+ errors: partnerSiteErrors,
2036
+ errorResponse: errorEnvelopeSchemas(partnerSiteErrors, realtimeSchemas.publicRequestId),
2037
+ resources: {
2038
+ reads: [{
2039
+ type: "site.analytics",
2040
+ idFrom: "params.siteId"
2041
+ }],
2042
+ changes: []
2043
+ },
2044
+ lifecycle: { introduced: "1.1.0" },
2045
+ docs: {
2046
+ summary: "Get unique-page trend",
2047
+ description: "Returns the daily unique-page count series for a window, with an optional previous-period total for comparison.",
2048
+ tags: ["Analytics"],
2049
+ examples: {
2050
+ request: {
2051
+ params: { siteId: "s_01" },
2052
+ query: {
2053
+ startDate: "2026-06-01",
2054
+ endDate: "2026-06-28"
2055
+ }
2056
+ },
2057
+ response: {
2058
+ data: {
2059
+ daily: [],
2060
+ total: 0,
2061
+ meta: {
2062
+ siteUrl: "sc-domain:example.com",
2063
+ syncStatus: "synced"
2064
+ }
2065
+ },
2066
+ meta: {
2067
+ requestId: "req_01",
2068
+ surface: "partner",
2069
+ version: "1.0"
2070
+ }
2071
+ }
2072
+ }
2073
+ }
2074
+ }),
2075
+ getContentVelocity: defineHttpOperation({
2076
+ id: "partner.sites.content.velocity.get",
2077
+ method: "GET",
2078
+ path: "/sites/{siteId}/content-velocity",
2079
+ visibility: "public",
2080
+ semantics: {
2081
+ kind: "query",
2082
+ sideEffects: "none",
2083
+ idempotent: true,
2084
+ retry: "idempotent",
2085
+ readConsistency: "primary"
2086
+ },
2087
+ auth: {
2088
+ credentials: ["user_key", "partner_key"],
2089
+ scopes: ["analytics:read"],
2090
+ ownership: [{
2091
+ credential: "user_key",
2092
+ rule: "authorized_site"
2093
+ }, {
2094
+ credential: "partner_key",
2095
+ rule: "authorized_site"
2096
+ }]
2097
+ },
2098
+ request: {
2099
+ params: z.strictObject({ siteId: realtimeSchemas.publicSiteId }),
2100
+ query: contentVelocityQuery,
2101
+ headers: requestHeaders,
2102
+ body: null
2103
+ },
2104
+ responses: { 200: contentVelocityResponse },
2105
+ errors: partnerSiteErrors,
2106
+ errorResponse: errorEnvelopeSchemas(partnerSiteErrors, realtimeSchemas.publicRequestId),
2107
+ resources: {
2108
+ reads: [{
2109
+ type: "site.analytics",
2110
+ idFrom: "params.siteId"
2111
+ }],
2112
+ changes: []
2113
+ },
2114
+ lifecycle: { introduced: "1.2.0" },
2115
+ docs: {
2116
+ summary: "Get content velocity",
2117
+ description: "Returns the weekly new-keyword series over the requested window with a summary trend.",
2118
+ tags: ["Analytics"],
2119
+ examples: {
2120
+ request: {
2121
+ params: { siteId: "s_01" },
2122
+ query: { days: 90 }
2123
+ },
2124
+ response: {
2125
+ data: {
2126
+ weekly: [],
2127
+ summary: {
2128
+ totalNewKeywords: 0,
2129
+ avgPerWeek: 0,
2130
+ trend: "stable"
2131
+ },
2132
+ meta: {
2133
+ siteUrl: "sc-domain:example.com",
2134
+ syncStatus: "synced"
2135
+ }
2136
+ },
2137
+ meta: {
2138
+ requestId: "req_01",
2139
+ surface: "partner",
2140
+ version: "1.0"
2141
+ }
2142
+ }
2143
+ }
2144
+ }
2145
+ }),
2146
+ getCtrCurve: defineHttpOperation({
2147
+ id: "partner.sites.ctr.curve.get",
2148
+ method: "GET",
2149
+ path: "/sites/{siteId}/ctr-curve",
2150
+ visibility: "public",
2151
+ semantics: {
2152
+ kind: "query",
2153
+ sideEffects: "none",
2154
+ idempotent: true,
2155
+ retry: "idempotent",
2156
+ readConsistency: "primary"
2157
+ },
2158
+ auth: {
2159
+ credentials: ["user_key", "partner_key"],
2160
+ scopes: ["analytics:read"],
2161
+ ownership: [{
2162
+ credential: "user_key",
2163
+ rule: "authorized_site"
2164
+ }, {
2165
+ credential: "partner_key",
2166
+ rule: "authorized_site"
2167
+ }]
2168
+ },
2169
+ request: {
2170
+ params: z.strictObject({ siteId: realtimeSchemas.publicSiteId }),
2171
+ query: dateRangeQuery,
2172
+ headers: requestHeaders,
2173
+ body: null
2174
+ },
2175
+ responses: { 200: ctrCurveResponse },
2176
+ errors: partnerSiteErrors,
2177
+ errorResponse: errorEnvelopeSchemas(partnerSiteErrors, realtimeSchemas.publicRequestId),
2178
+ resources: {
2179
+ reads: [{
2180
+ type: "site.analytics",
2181
+ idFrom: "params.siteId"
2182
+ }],
2183
+ changes: []
2184
+ },
2185
+ lifecycle: { introduced: "1.2.0" },
2186
+ docs: {
2187
+ summary: "Get CTR curve and outliers",
2188
+ description: "Returns the position-bucketed CTR curve plus over/under-performing query outliers for the window.",
2189
+ tags: ["Analytics"],
2190
+ examples: {
2191
+ request: {
2192
+ params: { siteId: "s_01" },
2193
+ query: {
2194
+ startDate: "2026-06-01",
2195
+ endDate: "2026-06-28"
2196
+ }
2197
+ },
2198
+ response: {
2199
+ data: {
2200
+ curve: [],
2201
+ overperforming: [],
2202
+ underperforming: [],
2203
+ meta: {
2204
+ siteUrl: "sc-domain:example.com",
2205
+ syncStatus: "synced"
2206
+ }
2207
+ },
2208
+ meta: {
2209
+ requestId: "req_01",
2210
+ surface: "partner",
2211
+ version: "1.0"
2212
+ }
2213
+ }
2214
+ }
2215
+ }
2216
+ }),
2217
+ getDarkTraffic: defineHttpOperation({
2218
+ id: "partner.sites.dark.traffic.get",
2219
+ method: "GET",
2220
+ path: "/sites/{siteId}/dark-traffic",
2221
+ visibility: "public",
2222
+ semantics: {
2223
+ kind: "query",
2224
+ sideEffects: "none",
2225
+ idempotent: true,
2226
+ retry: "idempotent",
2227
+ readConsistency: "primary"
2228
+ },
2229
+ auth: {
2230
+ credentials: ["user_key", "partner_key"],
2231
+ scopes: ["analytics:read"],
2232
+ ownership: [{
2233
+ credential: "user_key",
2234
+ rule: "authorized_site"
2235
+ }, {
2236
+ credential: "partner_key",
2237
+ rule: "authorized_site"
2238
+ }]
2239
+ },
2240
+ request: {
2241
+ params: z.strictObject({ siteId: realtimeSchemas.publicSiteId }),
2242
+ query: dateRangeQuery,
2243
+ headers: requestHeaders,
2244
+ body: null
2245
+ },
2246
+ responses: { 200: darkTrafficResponse },
2247
+ errors: partnerSiteErrors,
2248
+ errorResponse: errorEnvelopeSchemas(partnerSiteErrors, realtimeSchemas.publicRequestId),
2249
+ resources: {
2250
+ reads: [{
2251
+ type: "site.analytics",
2252
+ idFrom: "params.siteId"
2253
+ }],
2254
+ changes: []
2255
+ },
2256
+ lifecycle: { introduced: "1.2.0" },
2257
+ docs: {
2258
+ summary: "Get dark traffic breakdown",
2259
+ description: "Returns clicks not attributable to any tracked keyword, in total and per page, for the window.",
2260
+ tags: ["Analytics"],
2261
+ examples: {
2262
+ request: {
2263
+ params: { siteId: "s_01" },
2264
+ query: {
2265
+ startDate: "2026-06-01",
2266
+ endDate: "2026-06-28"
2267
+ }
2268
+ },
2269
+ response: {
2270
+ data: {
2271
+ summary: {
2272
+ totalClicks: 0,
2273
+ attributedClicks: 0,
2274
+ darkClicks: 0,
2275
+ darkPercent: 0,
2276
+ totalImpressions: 0,
2277
+ attributedImpressions: 0
2278
+ },
2279
+ pages: [],
2280
+ meta: {
2281
+ siteUrl: "sc-domain:example.com",
2282
+ syncStatus: "synced"
2283
+ }
2284
+ },
2285
+ meta: {
2286
+ requestId: "req_01",
2287
+ surface: "partner",
2288
+ version: "1.0"
2289
+ }
2290
+ }
2291
+ }
2292
+ }
2293
+ }),
2294
+ getDeviceGap: defineHttpOperation({
2295
+ id: "partner.sites.device.gap.get",
2296
+ method: "GET",
2297
+ path: "/sites/{siteId}/device-gap",
2298
+ visibility: "public",
2299
+ semantics: {
2300
+ kind: "query",
2301
+ sideEffects: "none",
2302
+ idempotent: true,
2303
+ retry: "idempotent",
2304
+ readConsistency: "primary"
2305
+ },
2306
+ auth: {
2307
+ credentials: ["user_key", "partner_key"],
2308
+ scopes: ["analytics:read"],
2309
+ ownership: [{
2310
+ credential: "user_key",
2311
+ rule: "authorized_site"
2312
+ }, {
2313
+ credential: "partner_key",
2314
+ rule: "authorized_site"
2315
+ }]
2316
+ },
2317
+ request: {
2318
+ params: z.strictObject({ siteId: realtimeSchemas.publicSiteId }),
2319
+ query: dateRangeQuery,
2320
+ headers: requestHeaders,
2321
+ body: null
2322
+ },
2323
+ responses: { 200: deviceGapResponse },
2324
+ errors: partnerSiteErrors,
2325
+ errorResponse: errorEnvelopeSchemas(partnerSiteErrors, realtimeSchemas.publicRequestId),
2326
+ resources: {
2327
+ reads: [{
2328
+ type: "site.analytics",
2329
+ idFrom: "params.siteId"
2330
+ }],
2331
+ changes: []
2332
+ },
2333
+ lifecycle: { introduced: "1.2.0" },
2334
+ docs: {
2335
+ summary: "Get desktop/mobile gap",
2336
+ description: "Returns per-day desktop vs mobile CTR/position metrics with gap trends for the window.",
2337
+ tags: ["Analytics"],
2338
+ examples: {
2339
+ request: {
2340
+ params: { siteId: "s_01" },
2341
+ query: {
2342
+ startDate: "2026-06-01",
2343
+ endDate: "2026-06-28"
2344
+ }
2345
+ },
2346
+ response: {
2347
+ data: {
2348
+ daily: [],
2349
+ summary: null,
2350
+ meta: {
2351
+ siteUrl: "sc-domain:example.com",
2352
+ syncStatus: "synced"
2353
+ }
2354
+ },
2355
+ meta: {
2356
+ requestId: "req_01",
2357
+ surface: "partner",
2358
+ version: "1.0"
2359
+ }
2360
+ }
2361
+ }
2362
+ }
2363
+ }),
2364
+ getKeywordBreadth: defineHttpOperation({
2365
+ id: "partner.sites.keyword.breadth.get",
2366
+ method: "GET",
2367
+ path: "/sites/{siteId}/keyword-breadth",
2368
+ visibility: "public",
2369
+ semantics: {
2370
+ kind: "query",
2371
+ sideEffects: "none",
2372
+ idempotent: true,
2373
+ retry: "idempotent",
2374
+ readConsistency: "primary"
2375
+ },
2376
+ auth: {
2377
+ credentials: ["user_key", "partner_key"],
2378
+ scopes: ["analytics:read"],
2379
+ ownership: [{
2380
+ credential: "user_key",
2381
+ rule: "authorized_site"
2382
+ }, {
2383
+ credential: "partner_key",
2384
+ rule: "authorized_site"
2385
+ }]
2386
+ },
2387
+ request: {
2388
+ params: z.strictObject({ siteId: realtimeSchemas.publicSiteId }),
2389
+ query: dateRangeQuery,
2390
+ headers: requestHeaders,
2391
+ body: null
2392
+ },
2393
+ responses: { 200: keywordBreadthResponse },
2394
+ errors: partnerSiteErrors,
2395
+ errorResponse: errorEnvelopeSchemas(partnerSiteErrors, realtimeSchemas.publicRequestId),
2396
+ resources: {
2397
+ reads: [{
2398
+ type: "site.analytics",
2399
+ idFrom: "params.siteId"
2400
+ }],
2401
+ changes: []
2402
+ },
2403
+ lifecycle: { introduced: "1.2.0" },
2404
+ docs: {
2405
+ summary: "Get keyword breadth",
2406
+ description: "Returns the pages-per-keyword-count distribution with fragile and authority page lists for the window.",
2407
+ tags: ["Analytics"],
2408
+ examples: {
2409
+ request: {
2410
+ params: { siteId: "s_01" },
2411
+ query: {
2412
+ startDate: "2026-06-01",
2413
+ endDate: "2026-06-28"
2414
+ }
2415
+ },
2416
+ response: {
2417
+ data: {
2418
+ distribution: [],
2419
+ fragilePages: [],
2420
+ authorityPages: [],
2421
+ summary: {
2422
+ totalPages: 0,
2423
+ avgKeywordsPerPage: 0,
2424
+ fragileCount: 0,
2425
+ authorityCount: 0
2426
+ },
2427
+ meta: {
2428
+ siteUrl: "sc-domain:example.com",
2429
+ syncStatus: "synced"
2430
+ }
2431
+ },
2432
+ meta: {
2433
+ requestId: "req_01",
2434
+ surface: "partner",
2435
+ version: "1.0"
2436
+ }
2437
+ }
2438
+ }
2439
+ }
2440
+ }),
2441
+ getPositionDistribution: defineHttpOperation({
2442
+ id: "partner.sites.position.distribution.get",
2443
+ method: "GET",
2444
+ path: "/sites/{siteId}/position-distribution",
2445
+ visibility: "public",
2446
+ semantics: {
2447
+ kind: "query",
2448
+ sideEffects: "none",
2449
+ idempotent: true,
2450
+ retry: "idempotent",
2451
+ readConsistency: "primary"
2452
+ },
2453
+ auth: {
2454
+ credentials: ["user_key", "partner_key"],
2455
+ scopes: ["analytics:read"],
2456
+ ownership: [{
2457
+ credential: "user_key",
2458
+ rule: "authorized_site"
2459
+ }, {
2460
+ credential: "partner_key",
2461
+ rule: "authorized_site"
2462
+ }]
2463
+ },
2464
+ request: {
2465
+ params: z.strictObject({ siteId: realtimeSchemas.publicSiteId }),
2466
+ query: dateRangeQuery,
2467
+ headers: requestHeaders,
2468
+ body: null
2469
+ },
2470
+ responses: { 200: positionDistributionResponse },
2471
+ errors: partnerSiteErrors,
2472
+ errorResponse: errorEnvelopeSchemas(partnerSiteErrors, realtimeSchemas.publicRequestId),
2473
+ resources: {
2474
+ reads: [{
2475
+ type: "site.analytics",
2476
+ idFrom: "params.siteId"
2477
+ }],
2478
+ changes: []
2479
+ },
2480
+ lifecycle: { introduced: "1.2.0" },
2481
+ docs: {
2482
+ summary: "Get position distribution",
2483
+ description: "Returns the per-day keyword counts bucketed by average position for the window.",
2484
+ tags: ["Analytics"],
2485
+ examples: {
2486
+ request: {
2487
+ params: { siteId: "s_01" },
2488
+ query: {
2489
+ startDate: "2026-06-01",
2490
+ endDate: "2026-06-28"
2491
+ }
2492
+ },
2493
+ response: {
2494
+ data: {
2495
+ distribution: [],
2496
+ meta: {
2497
+ siteUrl: "sc-domain:example.com",
2498
+ syncStatus: "synced"
2499
+ }
2500
+ },
2501
+ meta: {
2502
+ requestId: "req_01",
2503
+ surface: "partner",
2504
+ version: "1.0"
2505
+ }
2506
+ }
2507
+ }
2508
+ }
2509
+ }),
2510
+ getTopAssociation: defineHttpOperation({
2511
+ id: "partner.sites.top.association.get",
2512
+ method: "GET",
2513
+ path: "/sites/{siteId}/top-association",
2514
+ visibility: "public",
2515
+ semantics: {
2516
+ kind: "query",
2517
+ sideEffects: "none",
2518
+ idempotent: true,
2519
+ retry: "idempotent",
2520
+ readConsistency: "primary"
2521
+ },
2522
+ auth: {
2523
+ credentials: ["user_key", "partner_key"],
2524
+ scopes: ["analytics:read"],
2525
+ ownership: [{
2526
+ credential: "user_key",
2527
+ rule: "authorized_site"
2528
+ }, {
2529
+ credential: "partner_key",
2530
+ rule: "authorized_site"
2531
+ }]
2532
+ },
2533
+ request: {
2534
+ params: z.strictObject({ siteId: realtimeSchemas.publicSiteId }),
2535
+ query: topAssociationQuery,
2536
+ headers: requestHeaders,
2537
+ body: null
2538
+ },
2539
+ responses: { 200: topAssociationResponse },
2540
+ errors: partnerSiteErrors,
2541
+ errorResponse: errorEnvelopeSchemas(partnerSiteErrors, realtimeSchemas.publicRequestId),
2542
+ resources: {
2543
+ reads: [{
2544
+ type: "site.analytics",
2545
+ idFrom: "params.siteId"
2546
+ }],
2547
+ changes: []
2548
+ },
2549
+ lifecycle: { introduced: "1.2.0" },
2550
+ docs: {
2551
+ summary: "Get top association",
2552
+ description: "Returns the single best-performing page for a keyword, or keyword for a page, over the window.",
2553
+ tags: ["Analytics"],
2554
+ examples: {
2555
+ request: {
2556
+ params: { siteId: "s_01" },
2557
+ query: {
2558
+ type: "topKeyword",
2559
+ identifier: "/docs",
2560
+ startDate: "2026-06-01",
2561
+ endDate: "2026-06-28"
2562
+ }
2563
+ },
2564
+ response: {
2565
+ data: { value: null },
2566
+ meta: {
2567
+ requestId: "req_01",
2568
+ surface: "partner",
2569
+ version: "1.0"
2570
+ }
2571
+ }
2572
+ }
2573
+ }
2574
+ }),
2575
+ getIndexPercent: defineHttpOperation({
2576
+ id: "partner.sites.index.percent.get",
2577
+ method: "GET",
2578
+ path: "/sites/{siteId}/index-percent",
2579
+ visibility: "public",
2580
+ semantics: {
2581
+ kind: "query",
2582
+ sideEffects: "none",
2583
+ idempotent: true,
2584
+ retry: "idempotent",
2585
+ readConsistency: "primary"
2586
+ },
2587
+ auth: {
2588
+ credentials: ["user_key", "partner_key"],
2589
+ scopes: ["indexing:read"],
2590
+ ownership: [{
2591
+ credential: "user_key",
2592
+ rule: "authorized_site"
2593
+ }, {
2594
+ credential: "partner_key",
2595
+ rule: "authorized_site"
2596
+ }]
2597
+ },
2598
+ request: {
2599
+ params: z.strictObject({ siteId: realtimeSchemas.publicSiteId }),
2600
+ query: indexPercentQuery,
2601
+ headers: requestHeaders,
2602
+ body: null
2603
+ },
2604
+ responses: { 200: indexPercentResponse },
2605
+ errors: partnerSiteErrors,
2606
+ errorResponse: errorEnvelopeSchemas(partnerSiteErrors, realtimeSchemas.publicRequestId),
2607
+ resources: {
2608
+ reads: [{
2609
+ type: "site.indexing",
2610
+ idFrom: "params.siteId"
2611
+ }, {
2612
+ type: "site.sitemaps",
2613
+ idFrom: "params.siteId"
2614
+ }],
2615
+ changes: []
2616
+ },
2617
+ lifecycle: { introduced: "1.2.0" },
2618
+ docs: {
2619
+ summary: "Get sitemap index-percent",
2620
+ description: "Returns the sitemap visibility trend, invisible URLs, orphan pages, and per-sitemap counts.",
2621
+ tags: ["Indexing"],
2622
+ examples: {
2623
+ request: {
2624
+ params: { siteId: "s_01" },
2625
+ query: { invisibleLimit: 100 }
2626
+ },
2627
+ response: {
2628
+ data: {
2629
+ trend: [],
2630
+ invisibleUrls: [],
2631
+ invisibleCount: 0,
2632
+ orphanPages: [],
2633
+ orphanCount: 0,
2634
+ sitemaps: [],
2635
+ summary: {
2636
+ currentPercent: 0,
2637
+ totalSitemapUrls: 0,
2638
+ visibleUrls: 0,
2639
+ change7d: null,
2640
+ change28d: null,
2641
+ dataDate: "2026-06-28"
2642
+ },
2643
+ meta: {
2644
+ siteUrl: "sc-domain:example.com",
2645
+ syncStatus: "synced",
2646
+ newestDateSynced: null
2647
+ }
2648
+ },
2649
+ meta: {
2650
+ requestId: "req_01",
2651
+ surface: "partner",
2652
+ version: "1.0"
2653
+ }
2654
+ }
2655
+ }
2656
+ }
2657
+ }),
2658
+ createSitemapAction: defineHttpOperation({
2659
+ id: "partner.sites.sitemaps.action.create",
2660
+ method: "POST",
2661
+ path: "/sites/{siteId}/sitemaps/actions",
2662
+ visibility: "public",
2663
+ semantics: {
2664
+ kind: "mutation",
2665
+ sideEffects: "state",
2666
+ idempotent: false,
2667
+ retry: "never",
2668
+ readConsistency: null
2669
+ },
2670
+ auth: {
2671
+ credentials: ["user_key", "partner_key"],
2672
+ scopes: ["sitemaps:write"],
2673
+ ownership: [{
2674
+ credential: "user_key",
2675
+ rule: "authorized_site"
2676
+ }, {
2677
+ credential: "partner_key",
2678
+ rule: "authorized_site"
2679
+ }]
2680
+ },
2681
+ request: {
2682
+ params: z.strictObject({ siteId: realtimeSchemas.publicSiteId }),
2683
+ query: null,
2684
+ headers: requestHeaders,
2685
+ body: sitemapActionRequest
2686
+ },
2687
+ responses: { 200: sitemapActionResponse },
2688
+ errors: partnerSiteErrors,
2689
+ errorResponse: errorEnvelopeSchemas(partnerSiteErrors, realtimeSchemas.publicRequestId),
2690
+ resources: {
2691
+ reads: [{
2692
+ type: "site.sitemaps",
2693
+ idFrom: "params.siteId"
2694
+ }],
2695
+ changes: [{
2696
+ type: "site.sitemaps",
2697
+ idFrom: "params.siteId"
2698
+ }]
2699
+ },
2700
+ lifecycle: { introduced: "1.3.0" },
2701
+ docs: {
2702
+ summary: "Run a sitemap action",
2703
+ description: "Submits, deletes, refreshes, or auto-discovers a site's sitemap; the response shape depends on the requested action.",
2704
+ tags: ["Sitemaps"],
2705
+ examples: {
2706
+ request: {
2707
+ params: { siteId: "s_01" },
2708
+ body: { action: "refresh" }
2709
+ },
2710
+ response: {
2711
+ data: {
2712
+ success: true,
2713
+ action: "refreshed",
2714
+ sitemapCount: 0,
2715
+ changed: false
2716
+ },
2717
+ meta: {
2718
+ requestId: "req_01",
2719
+ surface: "partner",
2720
+ version: "1.0"
2721
+ }
2722
+ }
2723
+ }
2724
+ }
2725
+ }),
2726
+ querySitemapMembership: defineHttpOperation({
2727
+ id: "partner.sites.sitemaps.membership.query",
2728
+ method: "POST",
2729
+ path: "/sites/{siteId}/sitemaps/membership",
2730
+ visibility: "public",
2731
+ semantics: {
2732
+ kind: "query",
2733
+ sideEffects: "none",
2734
+ idempotent: true,
2735
+ retry: "idempotent",
2736
+ readConsistency: "primary"
2737
+ },
2738
+ auth: {
2739
+ credentials: ["user_key", "partner_key"],
2740
+ scopes: ["sitemaps:read"],
2741
+ ownership: [{
2742
+ credential: "user_key",
2743
+ rule: "authorized_site"
2744
+ }, {
2745
+ credential: "partner_key",
2746
+ rule: "authorized_site"
2747
+ }]
2748
+ },
2749
+ request: {
2750
+ params: z.strictObject({ siteId: realtimeSchemas.publicSiteId }),
2751
+ query: null,
2752
+ headers: requestHeaders,
2753
+ body: sitemapMembershipRequest
2754
+ },
2755
+ responses: { 200: sitemapMembershipResponse },
2756
+ errors: partnerSiteErrors,
2757
+ errorResponse: errorEnvelopeSchemas(partnerSiteErrors, realtimeSchemas.publicRequestId),
2758
+ resources: {
2759
+ reads: [{
2760
+ type: "site.sitemaps",
2761
+ idFrom: "params.siteId"
2762
+ }],
2763
+ changes: []
2764
+ },
2765
+ lifecycle: { introduced: "1.3.0" },
2766
+ docs: {
2767
+ summary: "Query sitemap membership",
2768
+ description: "Checks whether each requested URL is present in the site's known sitemaps, subject to a freshness cap.",
2769
+ tags: ["Sitemaps"],
2770
+ examples: {
2771
+ request: {
2772
+ params: { siteId: "s_01" },
2773
+ body: {
2774
+ urls: ["https://example.com/"],
2775
+ maxAgeDays: 30
2776
+ }
2777
+ },
2778
+ response: {
2779
+ data: {
2780
+ urls: [],
2781
+ meta: {
2782
+ available: true,
2783
+ reason: null,
2784
+ requested: 1,
2785
+ checked: 1,
2786
+ matched: 0,
2787
+ newestFetchedAt: null
2788
+ }
2789
+ },
2790
+ meta: {
2791
+ requestId: "req_01",
2792
+ surface: "partner",
2793
+ version: "1.0"
2794
+ }
2795
+ }
2796
+ }
2797
+ }
2798
+ }),
2799
+ createTeam: defineHttpOperation({
2800
+ id: "partner.teams.create",
2801
+ method: "POST",
2802
+ path: "/teams",
2803
+ visibility: "public",
2804
+ semantics: {
2805
+ kind: "mutation",
2806
+ sideEffects: "state",
2807
+ idempotent: false,
2808
+ retry: "never",
2809
+ readConsistency: null
2810
+ },
2811
+ auth: {
2812
+ credentials: ["partner_key"],
2813
+ scopes: ["teams:write"],
2814
+ ownership: [{
2815
+ credential: "partner_key",
2816
+ rule: "partner_tenant"
2817
+ }]
2818
+ },
2819
+ request: {
2820
+ params: null,
2821
+ query: null,
2822
+ headers: requestHeaders,
2823
+ body: createTeamRequest
2824
+ },
2825
+ responses: { 200: teamCreatedResponse },
2826
+ errors: partnerUserErrors,
2827
+ errorResponse: errorEnvelopeSchemas(partnerUserErrors, realtimeSchemas.publicRequestId),
2828
+ resources: {
2829
+ reads: [],
2830
+ changes: [{
2831
+ type: "partner.team",
2832
+ idFrom: "principal.id"
2833
+ }]
2834
+ },
2835
+ lifecycle: { introduced: "1.3.0" },
2836
+ docs: {
2837
+ summary: "Create a team",
2838
+ description: "Creates a team owned by a user linked to the authenticated partner tenant.",
2839
+ tags: ["Teams"],
2840
+ examples: {
2841
+ request: { body: {
2842
+ ownerUserId: "u_01",
2843
+ name: "Acme Team"
2844
+ } },
2845
+ response: {
2846
+ data: { team: {
2847
+ id: "t_01",
2848
+ ownerId: 1,
2849
+ name: "Acme Team",
2850
+ personalTeam: false,
2851
+ createdAt: 0,
2852
+ updatedAt: 0
2853
+ } },
2854
+ meta: {
2855
+ requestId: "req_01",
2856
+ surface: "partner",
2857
+ version: "1.0"
2858
+ }
2859
+ }
2860
+ }
2861
+ }
2862
+ }),
2863
+ renameTeam: defineHttpOperation({
2864
+ id: "partner.teams.rename",
2865
+ method: "PATCH",
2866
+ path: "/teams/{teamId}",
2867
+ visibility: "public",
2868
+ semantics: {
2869
+ kind: "mutation",
2870
+ sideEffects: "state",
2871
+ idempotent: true,
2872
+ retry: "idempotent",
2873
+ readConsistency: null
2874
+ },
2875
+ auth: {
2876
+ credentials: ["partner_key"],
2877
+ scopes: ["teams:write"],
2878
+ ownership: [{
2879
+ credential: "partner_key",
2880
+ rule: "partner_tenant"
2881
+ }]
2882
+ },
2883
+ request: {
2884
+ params: z.strictObject({ teamId: realtimeSchemas.publicTeamId }),
2885
+ query: null,
2886
+ headers: requestHeaders,
2887
+ body: renameTeamRequest
2888
+ },
2889
+ responses: { 200: teamRenamedResponse },
2890
+ errors: partnerUserErrors,
2891
+ errorResponse: errorEnvelopeSchemas(partnerUserErrors, realtimeSchemas.publicRequestId),
2892
+ resources: {
2893
+ reads: [{
2894
+ type: "partner.team",
2895
+ idFrom: "params.teamId"
2896
+ }],
2897
+ changes: [{
2898
+ type: "partner.team",
2899
+ idFrom: "params.teamId"
2900
+ }]
2901
+ },
2902
+ lifecycle: { introduced: "1.3.0" },
2903
+ docs: {
2904
+ summary: "Rename a team",
2905
+ description: "Renames a team owned by the authenticated partner tenant.",
2906
+ tags: ["Teams"],
2907
+ examples: {
2908
+ request: {
2909
+ params: { teamId: "t_01" },
2910
+ body: { name: "New name" }
2911
+ },
2912
+ response: {
2913
+ data: {
2914
+ ok: true,
2915
+ name: "New name"
2916
+ },
2917
+ meta: {
2918
+ requestId: "req_01",
2919
+ surface: "partner",
2920
+ version: "1.0"
2921
+ }
2922
+ }
2923
+ }
2924
+ }
2925
+ }),
2926
+ deleteTeam: defineHttpOperation({
2927
+ id: "partner.teams.delete",
2928
+ method: "DELETE",
2929
+ path: "/teams/{teamId}",
2930
+ visibility: "public",
2931
+ semantics: {
2932
+ kind: "mutation",
2933
+ sideEffects: "state",
2934
+ idempotent: true,
2935
+ retry: "idempotent",
2936
+ readConsistency: null
2937
+ },
2938
+ auth: {
2939
+ credentials: ["partner_key"],
2940
+ scopes: ["teams:write"],
2941
+ ownership: [{
2942
+ credential: "partner_key",
2943
+ rule: "partner_tenant"
2944
+ }]
2945
+ },
2946
+ request: {
2947
+ params: z.strictObject({ teamId: realtimeSchemas.publicTeamId }),
2948
+ query: null,
2949
+ headers: requestHeaders,
2950
+ body: null
2951
+ },
2952
+ responses: { 200: teamDeletedResponse },
2953
+ errors: partnerUserErrors,
2954
+ errorResponse: errorEnvelopeSchemas(partnerUserErrors, realtimeSchemas.publicRequestId),
2955
+ resources: {
2956
+ reads: [{
2957
+ type: "partner.team",
2958
+ idFrom: "params.teamId"
2959
+ }],
2960
+ changes: [{
2961
+ type: "partner.team",
2962
+ idFrom: "params.teamId"
2963
+ }]
2964
+ },
2965
+ lifecycle: { introduced: "1.3.0" },
2966
+ docs: {
2967
+ summary: "Delete a team",
2968
+ description: "Deletes a team owned by the authenticated partner tenant.",
2969
+ tags: ["Teams"],
2970
+ examples: {
2971
+ request: { params: { teamId: "t_01" } },
2972
+ response: {
2973
+ data: { ok: true },
2974
+ meta: {
2975
+ requestId: "req_01",
2976
+ surface: "partner",
2977
+ version: "1.0"
2978
+ }
2979
+ }
2980
+ }
2981
+ }
2982
+ }),
2983
+ listTeamMembers: defineHttpOperation({
2984
+ id: "partner.teams.members.list",
2985
+ method: "GET",
2986
+ path: "/teams/{teamId}/members",
2987
+ visibility: "public",
2988
+ semantics: {
2989
+ kind: "query",
2990
+ sideEffects: "none",
2991
+ idempotent: true,
2992
+ retry: "idempotent",
2993
+ readConsistency: "primary"
2994
+ },
2995
+ auth: {
2996
+ credentials: ["partner_key"],
2997
+ scopes: ["teams:read"],
2998
+ ownership: [{
2999
+ credential: "partner_key",
3000
+ rule: "partner_tenant"
3001
+ }]
3002
+ },
3003
+ request: {
3004
+ params: z.strictObject({ teamId: realtimeSchemas.publicTeamId }),
3005
+ query: null,
3006
+ headers: requestHeaders,
3007
+ body: null
3008
+ },
3009
+ responses: { 200: teamMembersResponse },
3010
+ errors: partnerUserErrors,
3011
+ errorResponse: errorEnvelopeSchemas(partnerUserErrors, realtimeSchemas.publicRequestId),
3012
+ resources: {
3013
+ reads: [{
3014
+ type: "partner.team",
3015
+ idFrom: "params.teamId"
3016
+ }],
3017
+ changes: []
3018
+ },
3019
+ lifecycle: { introduced: "1.3.0" },
3020
+ docs: {
3021
+ summary: "List team members",
3022
+ description: "Lists the members of a team owned by the authenticated partner tenant.",
3023
+ tags: ["Teams"],
3024
+ examples: {
3025
+ request: { params: { teamId: "t_01" } },
3026
+ response: {
3027
+ data: { members: [] },
3028
+ meta: {
3029
+ requestId: "req_01",
3030
+ surface: "partner",
3031
+ version: "1.0"
3032
+ }
3033
+ }
3034
+ }
3035
+ }
3036
+ }),
3037
+ addTeamMember: defineHttpOperation({
3038
+ id: "partner.teams.members.add",
3039
+ method: "POST",
3040
+ path: "/teams/{teamId}/members",
3041
+ visibility: "public",
3042
+ semantics: {
3043
+ kind: "mutation",
3044
+ sideEffects: "state",
3045
+ idempotent: true,
3046
+ retry: "idempotent",
3047
+ readConsistency: null
3048
+ },
3049
+ auth: {
3050
+ credentials: ["partner_key"],
3051
+ scopes: ["teams:write"],
3052
+ ownership: [{
3053
+ credential: "partner_key",
3054
+ rule: "partner_tenant"
3055
+ }]
3056
+ },
3057
+ request: {
3058
+ params: z.strictObject({ teamId: realtimeSchemas.publicTeamId }),
3059
+ query: null,
3060
+ headers: requestHeaders,
3061
+ body: addTeamMemberRequest
3062
+ },
3063
+ responses: { 200: teamMemberAddedResponse },
3064
+ errors: partnerUserErrors,
3065
+ errorResponse: errorEnvelopeSchemas(partnerUserErrors, realtimeSchemas.publicRequestId),
3066
+ resources: {
3067
+ reads: [{
3068
+ type: "partner.team",
3069
+ idFrom: "params.teamId"
3070
+ }],
3071
+ changes: [{
3072
+ type: "partner.team",
3073
+ idFrom: "params.teamId"
3074
+ }]
3075
+ },
3076
+ lifecycle: { introduced: "1.3.0" },
3077
+ docs: {
3078
+ summary: "Add a team member",
3079
+ description: "Adds a user to a team owned by the authenticated partner tenant, or reports it was already a member.",
3080
+ tags: ["Teams"],
3081
+ examples: {
3082
+ request: {
3083
+ params: { teamId: "t_01" },
3084
+ body: {
3085
+ userId: "u_02",
3086
+ role: "editor"
3087
+ }
3088
+ },
3089
+ response: {
3090
+ data: {
3091
+ ok: true,
3092
+ role: "editor"
3093
+ },
3094
+ meta: {
3095
+ requestId: "req_01",
3096
+ surface: "partner",
3097
+ version: "1.0"
3098
+ }
3099
+ }
3100
+ }
3101
+ }
3102
+ }),
3103
+ updateTeamMemberRole: defineHttpOperation({
3104
+ id: "partner.teams.members.role.update",
3105
+ method: "PATCH",
3106
+ path: "/teams/{teamId}/members/{userId}",
3107
+ visibility: "public",
3108
+ semantics: {
3109
+ kind: "mutation",
3110
+ sideEffects: "state",
3111
+ idempotent: true,
3112
+ retry: "idempotent",
3113
+ readConsistency: null
3114
+ },
3115
+ auth: {
3116
+ credentials: ["partner_key"],
3117
+ scopes: ["teams:write"],
3118
+ ownership: [{
3119
+ credential: "partner_key",
3120
+ rule: "partner_tenant"
3121
+ }]
3122
+ },
3123
+ request: {
3124
+ params: z.strictObject({
3125
+ teamId: realtimeSchemas.publicTeamId,
3126
+ userId: realtimeSchemas.publicUserId
3127
+ }),
3128
+ query: null,
3129
+ headers: requestHeaders,
3130
+ body: updateTeamMemberRoleRequest
3131
+ },
3132
+ responses: { 200: teamMemberRoleResponse },
3133
+ errors: partnerUserErrors,
3134
+ errorResponse: errorEnvelopeSchemas(partnerUserErrors, realtimeSchemas.publicRequestId),
3135
+ resources: {
3136
+ reads: [{
3137
+ type: "partner.team",
3138
+ idFrom: "params.teamId"
3139
+ }, {
3140
+ type: "partner.user",
3141
+ idFrom: "params.userId"
3142
+ }],
3143
+ changes: [{
3144
+ type: "partner.team",
3145
+ idFrom: "params.teamId"
3146
+ }, {
3147
+ type: "partner.user",
3148
+ idFrom: "params.userId"
3149
+ }]
3150
+ },
3151
+ lifecycle: { introduced: "1.3.0" },
3152
+ docs: {
3153
+ summary: "Update a team member role",
3154
+ description: "Updates the role of an existing member of a team owned by the authenticated partner tenant.",
3155
+ tags: ["Teams"],
3156
+ examples: {
3157
+ request: {
3158
+ params: {
3159
+ teamId: "t_01",
3160
+ userId: "u_02"
3161
+ },
3162
+ body: { role: "admin" }
3163
+ },
3164
+ response: {
3165
+ data: {
3166
+ ok: true,
3167
+ role: "admin"
3168
+ },
3169
+ meta: {
3170
+ requestId: "req_01",
3171
+ surface: "partner",
3172
+ version: "1.0"
3173
+ }
3174
+ }
3175
+ }
3176
+ }
3177
+ }),
3178
+ removeTeamMember: defineHttpOperation({
3179
+ id: "partner.teams.members.remove",
3180
+ method: "DELETE",
3181
+ path: "/teams/{teamId}/members/{userId}",
3182
+ visibility: "public",
3183
+ semantics: {
3184
+ kind: "mutation",
3185
+ sideEffects: "state",
3186
+ idempotent: true,
3187
+ retry: "idempotent",
3188
+ readConsistency: null
3189
+ },
3190
+ auth: {
3191
+ credentials: ["partner_key"],
3192
+ scopes: ["teams:write"],
3193
+ ownership: [{
3194
+ credential: "partner_key",
3195
+ rule: "partner_tenant"
3196
+ }]
3197
+ },
3198
+ request: {
3199
+ params: z.strictObject({
3200
+ teamId: realtimeSchemas.publicTeamId,
3201
+ userId: realtimeSchemas.publicUserId
3202
+ }),
3203
+ query: null,
3204
+ headers: requestHeaders,
3205
+ body: null
3206
+ },
3207
+ responses: { 200: teamMemberRemovedResponse },
3208
+ errors: partnerUserErrors,
3209
+ errorResponse: errorEnvelopeSchemas(partnerUserErrors, realtimeSchemas.publicRequestId),
3210
+ resources: {
3211
+ reads: [{
3212
+ type: "partner.team",
3213
+ idFrom: "params.teamId"
3214
+ }, {
3215
+ type: "partner.user",
3216
+ idFrom: "params.userId"
3217
+ }],
3218
+ changes: [{
3219
+ type: "partner.team",
3220
+ idFrom: "params.teamId"
3221
+ }, {
3222
+ type: "partner.user",
3223
+ idFrom: "params.userId"
3224
+ }]
3225
+ },
3226
+ lifecycle: { introduced: "1.3.0" },
3227
+ docs: {
3228
+ summary: "Remove a team member",
3229
+ description: "Removes a member from a team owned by the authenticated partner tenant.",
3230
+ tags: ["Teams"],
3231
+ examples: {
3232
+ request: { params: {
3233
+ teamId: "t_01",
3234
+ userId: "u_02"
3235
+ } },
3236
+ response: {
3237
+ data: { ok: true },
3238
+ meta: {
3239
+ requestId: "req_01",
3240
+ surface: "partner",
3241
+ version: "1.0"
3242
+ }
3243
+ }
3244
+ }
3245
+ }
3246
+ }),
3247
+ updateSiteTeam: defineHttpOperation({
3248
+ id: "partner.sites.team.update",
3249
+ method: "PATCH",
3250
+ path: "/sites/{siteId}/team",
3251
+ visibility: "public",
3252
+ semantics: {
3253
+ kind: "mutation",
3254
+ sideEffects: "state",
3255
+ idempotent: true,
3256
+ retry: "idempotent",
3257
+ readConsistency: null
3258
+ },
3259
+ auth: {
3260
+ credentials: ["partner_key"],
3261
+ scopes: ["sites:write"],
3262
+ ownership: [{
3263
+ credential: "partner_key",
3264
+ rule: "authorized_site"
3265
+ }]
3266
+ },
3267
+ request: {
3268
+ params: z.strictObject({ siteId: realtimeSchemas.publicSiteId }),
3269
+ query: null,
3270
+ headers: requestHeaders,
3271
+ body: bindSiteTeamRequest
3272
+ },
3273
+ responses: { 200: siteTeamBindingResponse },
3274
+ errors: partnerSiteErrors,
3275
+ errorResponse: errorEnvelopeSchemas(partnerSiteErrors, realtimeSchemas.publicRequestId),
3276
+ resources: {
3277
+ reads: [{
3278
+ type: "site.registration",
3279
+ idFrom: "params.siteId"
3280
+ }],
3281
+ changes: [{
3282
+ type: "site.registration",
3283
+ idFrom: "params.siteId"
3284
+ }]
3285
+ },
3286
+ lifecycle: { introduced: "1.3.0" },
3287
+ docs: {
3288
+ summary: "Bind a site to a team",
3289
+ description: "Binds or unbinds (teamId: null) a partner-owned site to a partner-owned team.",
3290
+ tags: ["Teams"],
3291
+ examples: {
3292
+ request: {
3293
+ params: { siteId: "s_01" },
3294
+ body: { teamId: "t_01" }
3295
+ },
3296
+ response: {
3297
+ data: {
3298
+ ok: true,
3299
+ teamId: "t_01"
3300
+ },
3301
+ meta: {
3302
+ requestId: "req_01",
3303
+ surface: "partner",
3304
+ version: "1.0"
3305
+ }
3306
+ }
3307
+ }
3308
+ }
3309
+ }),
3310
+ getTeamCatalog: defineHttpOperation({
3311
+ id: "partner.teams.catalog.get",
3312
+ method: "GET",
3313
+ path: "/teams/{teamId}/catalog",
3314
+ visibility: "public",
3315
+ semantics: {
3316
+ kind: "query",
3317
+ sideEffects: "none",
3318
+ idempotent: true,
3319
+ retry: "idempotent",
3320
+ readConsistency: "primary"
3321
+ },
3322
+ auth: {
3323
+ credentials: ["partner_key"],
3324
+ scopes: ["teams:read"],
3325
+ ownership: [{
3326
+ credential: "partner_key",
3327
+ rule: "partner_tenant"
3328
+ }]
3329
+ },
3330
+ request: {
3331
+ params: z.strictObject({ teamId: realtimeSchemas.publicTeamId }),
3332
+ query: null,
3333
+ headers: requestHeaders,
3334
+ body: null
3335
+ },
3336
+ responses: { 200: teamCatalogResponse },
3337
+ errors: partnerUserErrors,
3338
+ errorResponse: errorEnvelopeSchemas(partnerUserErrors, realtimeSchemas.publicRequestId),
3339
+ resources: {
3340
+ reads: [{
3341
+ type: "partner.team",
3342
+ idFrom: "params.teamId"
3343
+ }],
3344
+ changes: []
3345
+ },
3346
+ lifecycle: { introduced: "1.3.0" },
3347
+ docs: {
3348
+ summary: "Get a team catalog",
3349
+ description: "Returns the Iceberg catalog reference bound to a partner-owned team, if any.",
3350
+ tags: ["Teams"],
3351
+ examples: {
3352
+ request: { params: { teamId: "t_01" } },
3353
+ response: {
3354
+ data: {
3355
+ teamId: "t_01",
3356
+ catalogUri: null,
3357
+ warehouse: null,
3358
+ bucket: null,
3359
+ namespace: null,
3360
+ provisioningState: null,
3361
+ keyEncoding: null,
3362
+ catalogTablesReady: false,
3363
+ readsEnabled: false
3364
+ },
3365
+ meta: {
3366
+ requestId: "req_01",
3367
+ surface: "partner",
3368
+ version: "1.0"
3369
+ }
3370
+ }
3371
+ }
3372
+ }
3373
+ }),
3374
+ bindTeamCatalog: defineHttpOperation({
3375
+ id: "partner.teams.catalog.bind",
3376
+ method: "POST",
3377
+ path: "/teams/{teamId}/catalog",
3378
+ visibility: "public",
3379
+ semantics: {
3380
+ kind: "mutation",
3381
+ sideEffects: "state",
3382
+ idempotent: true,
3383
+ retry: "idempotent",
3384
+ readConsistency: null
3385
+ },
3386
+ auth: {
3387
+ credentials: ["partner_key"],
3388
+ scopes: ["teams:write"],
3389
+ ownership: [{
3390
+ credential: "partner_key",
3391
+ rule: "partner_tenant"
3392
+ }]
3393
+ },
3394
+ request: {
3395
+ params: z.strictObject({ teamId: realtimeSchemas.publicTeamId }),
3396
+ query: null,
3397
+ headers: requestHeaders,
3398
+ body: bindTeamCatalogRequest
3399
+ },
3400
+ responses: { 200: teamCatalogBindResponse },
3401
+ errors: partnerUserErrors,
3402
+ errorResponse: errorEnvelopeSchemas(partnerUserErrors, realtimeSchemas.publicRequestId),
3403
+ resources: {
3404
+ reads: [{
3405
+ type: "partner.team",
3406
+ idFrom: "params.teamId"
3407
+ }],
3408
+ changes: [{
3409
+ type: "partner.team",
3410
+ idFrom: "params.teamId"
3411
+ }]
3412
+ },
3413
+ lifecycle: { introduced: "1.3.0" },
3414
+ docs: {
3415
+ summary: "Bind a team catalog",
3416
+ description: "Binds an Iceberg catalog (URI, warehouse, and optional bucket/namespace) to a partner-owned team.",
3417
+ tags: ["Teams"],
3418
+ examples: {
3419
+ request: {
3420
+ params: { teamId: "t_01" },
3421
+ body: {
3422
+ catalogUri: "gs://bucket/catalog",
3423
+ warehouse: "primary",
3424
+ namespace: "ns1",
3425
+ bucket: "bucket1"
3426
+ }
3427
+ },
3428
+ response: {
3429
+ data: {
3430
+ teamId: "t_01",
3431
+ status: "ready",
3432
+ catalogUri: "gs://bucket/catalog",
3433
+ warehouse: "primary",
3434
+ bucket: "bucket1",
3435
+ namespace: "ns1"
3436
+ },
3437
+ meta: {
3438
+ requestId: "req_01",
3439
+ surface: "partner",
3440
+ version: "1.0"
3441
+ }
3442
+ }
3443
+ }
3444
+ }
3445
+ }),
3446
+ getSiteIntIdCrosswalk: defineHttpOperation({
3447
+ id: "partner.users.sites.crosswalk.get",
3448
+ method: "GET",
3449
+ path: "/users/{userId}/sites/crosswalk",
3450
+ visibility: "public",
3451
+ semantics: {
3452
+ kind: "query",
3453
+ sideEffects: "none",
3454
+ idempotent: true,
3455
+ retry: "idempotent",
3456
+ readConsistency: "primary"
3457
+ },
3458
+ auth: {
3459
+ credentials: ["user_key", "partner_key"],
3460
+ scopes: ["sites:read"],
3461
+ ownership: [{
3462
+ credential: "user_key",
3463
+ rule: "self"
3464
+ }, {
3465
+ credential: "partner_key",
3466
+ rule: "linked_user"
3467
+ }]
3468
+ },
3469
+ request: {
3470
+ params: z.strictObject({ userId: realtimeSchemas.publicUserId }),
3471
+ query: null,
3472
+ headers: requestHeaders,
3473
+ body: null
3474
+ },
3475
+ responses: { 200: siteIntIdCrosswalkResponse },
3476
+ errors: partnerUserErrors,
3477
+ errorResponse: errorEnvelopeSchemas(partnerUserErrors, realtimeSchemas.publicRequestId),
3478
+ resources: {
3479
+ reads: [{
3480
+ type: "partner.user",
3481
+ idFrom: "params.userId"
3482
+ }, {
3483
+ type: "user.sites",
3484
+ idFrom: "params.userId"
3485
+ }],
3486
+ changes: []
3487
+ },
3488
+ lifecycle: { introduced: "1.3.0" },
3489
+ docs: {
3490
+ summary: "Get the site int-id crosswalk",
3491
+ description: "Maps a user's public site IDs to their internal integer IDs, for partners that need the legacy numeric identifier.",
3492
+ tags: ["Sites"],
3493
+ examples: {
3494
+ request: { params: { userId: "u_01" } },
3495
+ response: {
3496
+ data: {
3497
+ crosswalk: {},
3498
+ sites: []
3499
+ },
3500
+ meta: {
3501
+ requestId: "req_01",
3502
+ surface: "partner",
3503
+ version: "1.0"
3504
+ }
3505
+ }
3506
+ }
3507
+ }
3508
+ }),
3509
+ deleteUser: defineHttpOperation({
3510
+ id: "partner.users.delete",
3511
+ method: "DELETE",
3512
+ path: "/users/{userId}",
3513
+ visibility: "public",
3514
+ semantics: {
3515
+ kind: "mutation",
3516
+ sideEffects: "state",
3517
+ idempotent: true,
3518
+ retry: "idempotent",
3519
+ readConsistency: null
3520
+ },
3521
+ auth: {
3522
+ credentials: ["partner_key"],
3523
+ scopes: ["users:write"],
3524
+ ownership: [{
3525
+ credential: "partner_key",
3526
+ rule: "linked_user"
3527
+ }]
3528
+ },
3529
+ request: {
3530
+ params: z.strictObject({ userId: realtimeSchemas.publicUserId }),
3531
+ query: null,
3532
+ headers: requestHeaders,
3533
+ body: null
3534
+ },
3535
+ responses: { 200: deletePartnerUserResponse },
3536
+ errors: partnerUserErrors,
3537
+ errorResponse: errorEnvelopeSchemas(partnerUserErrors, realtimeSchemas.publicRequestId),
3538
+ resources: {
3539
+ reads: [{
3540
+ type: "partner.user",
3541
+ idFrom: "params.userId"
3542
+ }],
3543
+ changes: [{
3544
+ type: "partner.user",
3545
+ idFrom: "params.userId"
3546
+ }, {
3547
+ type: "user.sites",
3548
+ idFrom: "params.userId"
3549
+ }]
3550
+ },
3551
+ lifecycle: { introduced: "1.3.0" },
3552
+ docs: {
3553
+ summary: "Delete a partner user",
3554
+ description: "Queues cascade deletion of a partner-linked user and their sites.",
3555
+ tags: ["Users"],
3556
+ examples: {
3557
+ request: { params: { userId: "u_01" } },
3558
+ response: {
3559
+ data: {
3560
+ ok: true,
3561
+ queued: true,
3562
+ userId: 1,
3563
+ publicId: "u_01"
3564
+ },
3565
+ meta: {
3566
+ requestId: "req_01",
3567
+ surface: "partner",
3568
+ version: "1.0"
3569
+ }
3570
+ }
3571
+ }
3572
+ }
3573
+ }),
3574
+ createVerificationToken: defineHttpOperation({
3575
+ id: "partner.users.verification.token.create",
3576
+ method: "POST",
3577
+ path: "/users/{userId}/verification-token",
3578
+ visibility: "public",
3579
+ semantics: {
3580
+ kind: "query",
3581
+ sideEffects: "none",
3582
+ idempotent: true,
3583
+ retry: "idempotent",
3584
+ readConsistency: "primary"
3585
+ },
3586
+ auth: {
3587
+ credentials: ["user_key", "partner_key"],
3588
+ scopes: ["sites:write"],
3589
+ ownership: [{
3590
+ credential: "user_key",
3591
+ rule: "self"
3592
+ }, {
3593
+ credential: "partner_key",
3594
+ rule: "linked_user"
3595
+ }]
3596
+ },
3597
+ request: {
3598
+ params: z.strictObject({ userId: realtimeSchemas.publicUserId }),
3599
+ query: null,
3600
+ headers: requestHeaders,
3601
+ body: verificationTokenRequest
3602
+ },
3603
+ responses: { 200: verificationTokenResponse },
3604
+ errors: partnerUserErrors,
3605
+ errorResponse: errorEnvelopeSchemas(partnerUserErrors, realtimeSchemas.publicRequestId),
3606
+ resources: {
3607
+ reads: [{
3608
+ type: "partner.user",
3609
+ idFrom: "params.userId"
3610
+ }, {
3611
+ type: "user.sites",
3612
+ idFrom: "params.userId"
3613
+ }],
3614
+ changes: []
3615
+ },
3616
+ lifecycle: { introduced: "1.3.0" },
3617
+ docs: {
3618
+ summary: "Create a site verification token",
3619
+ description: "Mints a Search Console site-ownership verification token (meta tag or DNS record) for a not-yet-registered site.",
3620
+ tags: ["Sites"],
3621
+ examples: {
3622
+ request: {
3623
+ params: { userId: "u_01" },
3624
+ body: {
3625
+ siteUrl: "https://example.com",
3626
+ method: "DNS_TXT"
3627
+ }
3628
+ },
3629
+ response: {
3630
+ data: {
3631
+ siteUrl: "https://example.com",
3632
+ site: {
3633
+ type: "INET_DOMAIN",
3634
+ identifier: "example.com"
3635
+ },
3636
+ method: "DNS_TXT",
3637
+ token: "abc123",
3638
+ metaContent: null,
3639
+ dnsRecord: {
3640
+ type: "TXT",
3641
+ host: "_gsc.example.com",
3642
+ value: "abc123"
3643
+ }
3644
+ },
3645
+ meta: {
3646
+ requestId: "req_01",
3647
+ surface: "partner",
3648
+ version: "1.0"
3649
+ }
3650
+ }
3651
+ }
3652
+ }
3653
+ }),
3654
+ addAndVerifySite: defineHttpOperation({
3655
+ id: "partner.users.sites.verify.create",
3656
+ method: "POST",
3657
+ path: "/users/{userId}/sites/verify",
3658
+ visibility: "public",
3659
+ semantics: {
3660
+ kind: "mutation",
3661
+ sideEffects: "state",
3662
+ idempotent: true,
3663
+ retry: "idempotent",
3664
+ readConsistency: null
3665
+ },
3666
+ auth: {
3667
+ credentials: ["user_key", "partner_key"],
3668
+ scopes: ["sites:write"],
3669
+ ownership: [{
3670
+ credential: "user_key",
3671
+ rule: "self"
3672
+ }, {
3673
+ credential: "partner_key",
3674
+ rule: "linked_user"
3675
+ }]
3676
+ },
3677
+ request: {
3678
+ params: z.strictObject({ userId: realtimeSchemas.publicUserId }),
3679
+ query: null,
3680
+ headers: requestHeaders,
3681
+ body: addAndVerifySiteRequest
3682
+ },
3683
+ responses: { 200: addAndVerifySiteResponse },
3684
+ errors: partnerUserErrors,
3685
+ errorResponse: errorEnvelopeSchemas(partnerUserErrors, realtimeSchemas.publicRequestId),
3686
+ resources: {
3687
+ reads: [{
3688
+ type: "partner.user",
3689
+ idFrom: "params.userId"
3690
+ }],
3691
+ changes: [{
3692
+ type: "user.sites",
3693
+ idFrom: "params.userId"
3694
+ }]
3695
+ },
3696
+ lifecycle: { introduced: "1.3.0" },
3697
+ docs: {
3698
+ summary: "Add and verify a site",
3699
+ description: "Verifies Search Console ownership of a site for a user via the given method and records the verified ownership.",
3700
+ tags: ["Sites"],
3701
+ examples: {
3702
+ request: {
3703
+ params: { userId: "u_01" },
3704
+ body: {
3705
+ siteUrl: "https://example.com",
3706
+ method: "DNS_TXT"
3707
+ }
3708
+ },
3709
+ response: {
3710
+ data: {
3711
+ siteUrl: "https://example.com",
3712
+ site: {
3713
+ type: "INET_DOMAIN",
3714
+ identifier: "example.com"
3715
+ },
3716
+ method: "DNS_TXT",
3717
+ verified: true,
3718
+ owners: ["owner@example.com"]
3719
+ },
3720
+ meta: {
3721
+ requestId: "req_01",
3722
+ surface: "partner",
3723
+ version: "1.0"
3724
+ }
3725
+ }
3726
+ }
3727
+ }
3728
+ }),
3729
+ queryCrossSource: defineHttpOperation({
3730
+ id: "partner.sites.cross.source.query",
3731
+ method: "POST",
3732
+ path: "/sites/{siteId}/cross-source",
3733
+ visibility: "public",
3734
+ semantics: {
3735
+ kind: "query",
3736
+ sideEffects: "none",
3737
+ idempotent: true,
3738
+ retry: "idempotent",
3739
+ readConsistency: "primary"
3740
+ },
3741
+ auth: {
3742
+ credentials: ["partner_key"],
3743
+ scopes: ["analytics:read"],
3744
+ ownership: [{
3745
+ credential: "partner_key",
3746
+ rule: "authorized_site"
3747
+ }]
3748
+ },
3749
+ request: {
3750
+ params: z.strictObject({ siteId: realtimeSchemas.publicSiteId }),
3751
+ query: null,
3752
+ headers: requestHeaders,
3753
+ body: crossSourceRequest
3754
+ },
3755
+ responses: { 200: crossSourceResponse },
3756
+ errors: partnerSiteErrors,
3757
+ errorResponse: errorEnvelopeSchemas(partnerSiteErrors, realtimeSchemas.publicRequestId),
3758
+ resources: {
3759
+ reads: [{
3760
+ type: "site.analytics",
3761
+ idFrom: "params.siteId"
3762
+ }],
3763
+ changes: []
3764
+ },
3765
+ lifecycle: { introduced: "1.3.0" },
3766
+ docs: {
3767
+ summary: "Query cross-source analysis",
3768
+ description: "Runs one predefined cross-source query joining crawl, CWV, and GSC signals for a site.",
3769
+ tags: ["Analytics"],
3770
+ examples: {
3771
+ request: {
3772
+ params: { siteId: "s_01" },
3773
+ body: {
3774
+ queryKey: "crawl-error-losing-impressions",
3775
+ rangeDays: 28,
3776
+ limit: 50
3777
+ }
3778
+ },
3779
+ response: {
3780
+ data: {
3781
+ sources: ["crawl", "gsc"],
3782
+ rows: []
3783
+ },
3784
+ meta: {
3785
+ requestId: "req_01",
3786
+ surface: "partner",
3787
+ version: "1.0"
3788
+ }
3789
+ }
3790
+ }
3791
+ }
3792
+ }),
3793
+ enrichKeywords: defineHttpOperation({
3794
+ id: "partner.keywords.enrich.query",
3795
+ method: "POST",
3796
+ path: "/keywords/enrich",
3797
+ visibility: "public",
3798
+ semantics: {
3799
+ kind: "query",
3800
+ sideEffects: "none",
3801
+ idempotent: true,
3802
+ retry: "idempotent",
3803
+ readConsistency: "primary"
3804
+ },
3805
+ auth: {
3806
+ credentials: ["user_key", "partner_key"],
3807
+ scopes: ["analytics:read"],
3808
+ ownership: [{
3809
+ credential: "user_key",
3810
+ rule: "self"
3811
+ }, {
3812
+ credential: "partner_key",
3813
+ rule: "partner_tenant"
3814
+ }]
3815
+ },
3816
+ request: {
3817
+ params: null,
3818
+ query: null,
3819
+ headers: requestHeaders,
3820
+ body: enrichKeywordsRequest
3821
+ },
3822
+ responses: { 200: enrichKeywordsResponse },
3823
+ errors: partnerUserErrors,
3824
+ errorResponse: errorEnvelopeSchemas(partnerUserErrors, realtimeSchemas.publicRequestId),
3825
+ resources: {
3826
+ reads: [],
3827
+ changes: []
3828
+ },
3829
+ lifecycle: { introduced: "1.3.0" },
3830
+ docs: {
3831
+ summary: "Enrich keywords with metrics",
3832
+ description: "Returns difficulty, search volume, and CPC estimates for up to 500 keywords.",
3833
+ tags: ["Analytics"],
3834
+ examples: {
3835
+ request: { body: { keywords: ["nuxt seo"] } },
3836
+ response: {
3837
+ data: { metrics: { "nuxt seo": {
3838
+ difficulty: 40,
3839
+ searchVolume: 100,
3840
+ cpc: 1.2
3841
+ } } },
3842
+ meta: {
3843
+ requestId: "req_01",
3844
+ surface: "partner",
3845
+ version: "1.0"
3846
+ }
3847
+ }
3848
+ }
3849
+ }
3850
+ })
3851
+ }
3852
+ });
3853
+ const analytics = defineHttpSurface({
3854
+ name: "analytics",
3855
+ prefix: "/api/analytics/v1",
3856
+ version: "1.0",
3857
+ operations: {
3858
+ queryRows: defineHttpOperation({
3859
+ id: "analytics.rows.query",
3860
+ method: "POST",
3861
+ path: "/sites/{siteId}/rows",
3862
+ visibility: "public",
3863
+ semantics: {
3864
+ kind: "query",
3865
+ sideEffects: "none",
3866
+ idempotent: true,
3867
+ retry: "idempotent",
3868
+ readConsistency: "primary"
3869
+ },
3870
+ auth: {
3871
+ credentials: ["user_key", "partner_key"],
3872
+ scopes: ["analytics:execute"],
3873
+ ownership: [{
3874
+ credential: "user_key",
3875
+ rule: "authorized_site"
3876
+ }, {
3877
+ credential: "partner_key",
3878
+ rule: "authorized_site"
3879
+ }]
3880
+ },
3881
+ request: {
3882
+ params: z.strictObject({ siteId: realtimeSchemas.publicSiteId }),
3883
+ query: null,
3884
+ headers: requestHeaders,
3885
+ body: analyticsRowsRequest
3886
+ },
3887
+ responses: { 200: analyticsRowsResponse },
3888
+ errors: analyticsRowsErrors,
3889
+ errorResponse: errorEnvelopeSchemas(analyticsRowsErrors, realtimeSchemas.publicRequestId),
3890
+ resources: {
3891
+ reads: [{
3892
+ type: "site.analytics",
3893
+ idFrom: "params.siteId"
3894
+ }],
3895
+ changes: []
3896
+ },
3897
+ lifecycle: { introduced: "1.0.0" },
3898
+ docs: {
3899
+ summary: "Query analytics rows",
3900
+ description: "Executes an idempotent primary-consistent analytics row query.",
3901
+ tags: ["Analytics rows"],
3902
+ examples: {
3903
+ request: {
3904
+ params: { siteId: "s_01" },
3905
+ body: {
3906
+ dimensions: ["query"],
3907
+ metrics: ["clicks", "impressions"],
3908
+ rowLimit: 100
3909
+ }
3910
+ },
3911
+ response: {
3912
+ data: { rows: [] },
3913
+ meta: {
3914
+ requestId: "req_01",
3915
+ surface: "analytics",
3916
+ version: "1.0",
3917
+ sourceName: "primary",
3918
+ sourceKind: "sql",
3919
+ queryMs: 12
3920
+ }
3921
+ }
3922
+ }
3923
+ }
3924
+ }),
3925
+ queryReport: defineHttpOperation({
3926
+ id: "analytics.reports.query",
3927
+ method: "POST",
3928
+ path: "/sites/{siteId}/reports",
3929
+ visibility: "public",
3930
+ semantics: {
3931
+ kind: "query",
3932
+ sideEffects: "none",
3933
+ idempotent: true,
3934
+ retry: "idempotent",
3935
+ readConsistency: "primary"
3936
+ },
3937
+ auth: {
3938
+ credentials: ["user_key", "partner_key"],
3939
+ scopes: ["analytics:execute"],
3940
+ ownership: [{
3941
+ credential: "user_key",
3942
+ rule: "authorized_site"
3943
+ }, {
3944
+ credential: "partner_key",
3945
+ rule: "authorized_site"
3946
+ }]
3947
+ },
3948
+ request: {
3949
+ params: z.strictObject({ siteId: realtimeSchemas.publicSiteId }),
3950
+ query: null,
3951
+ headers: requestHeaders,
3952
+ body: analyticsReportRequest
3953
+ },
3954
+ responses: { 200: analyticsReportResponse },
3955
+ errors: analyticsRowsErrors,
3956
+ errorResponse: errorEnvelopeSchemas(analyticsRowsErrors, realtimeSchemas.publicRequestId),
3957
+ resources: {
3958
+ reads: [{
3959
+ type: "site.analytics",
3960
+ idFrom: "params.siteId"
3961
+ }],
3962
+ changes: []
3963
+ },
3964
+ lifecycle: { introduced: "1.0.0" },
3965
+ docs: {
3966
+ summary: "Query an analytics report",
3967
+ description: "Returns the hosted analytics list report with totals, comparison fields, and sync metadata.",
3968
+ tags: ["Analytics reports"],
3969
+ examples: {
3970
+ request: {
3971
+ params: { siteId: "s_01" },
3972
+ body: { state: {
3973
+ dimensions: ["query"],
3974
+ searchType: "web"
3975
+ } }
3976
+ },
3977
+ response: {
3978
+ data: {
3979
+ rows: [],
3980
+ totalCount: 0,
3981
+ totals: {
3982
+ clicks: 0,
3983
+ impressions: 0,
3984
+ ctr: 0,
3985
+ position: 0
3986
+ },
3987
+ meta: {
3988
+ siteUrl: "sc-domain:example.com",
3989
+ syncStatus: "synced",
3990
+ newestDateSynced: null,
3991
+ oldestDateSynced: null,
3992
+ dataDelay: "0 days"
3993
+ }
3994
+ },
3995
+ meta: {
3996
+ requestId: "req_01",
3997
+ surface: "analytics",
3998
+ version: "1.0",
3999
+ sourceName: "hosted-report",
4000
+ sourceKind: "sql",
4001
+ queryMs: 12
4002
+ }
4003
+ }
4004
+ }
4005
+ }
4006
+ }),
4007
+ queryReportDetail: defineHttpOperation({
4008
+ id: "analytics.reports.detail.query",
4009
+ method: "POST",
4010
+ path: "/sites/{siteId}/reports/detail",
4011
+ visibility: "public",
4012
+ semantics: {
4013
+ kind: "query",
4014
+ sideEffects: "none",
4015
+ idempotent: true,
4016
+ retry: "idempotent",
4017
+ readConsistency: "primary"
4018
+ },
4019
+ auth: {
4020
+ credentials: ["user_key", "partner_key"],
4021
+ scopes: ["analytics:execute"],
4022
+ ownership: [{
4023
+ credential: "user_key",
4024
+ rule: "authorized_site"
4025
+ }, {
4026
+ credential: "partner_key",
4027
+ rule: "authorized_site"
4028
+ }]
4029
+ },
4030
+ request: {
4031
+ params: z.strictObject({ siteId: realtimeSchemas.publicSiteId }),
4032
+ query: null,
4033
+ headers: requestHeaders,
4034
+ body: analyticsReportDetailRequest
4035
+ },
4036
+ responses: { 200: analyticsReportDetailResponse },
4037
+ errors: analyticsRowsErrors,
4038
+ errorResponse: errorEnvelopeSchemas(analyticsRowsErrors, realtimeSchemas.publicRequestId),
4039
+ resources: {
4040
+ reads: [{
4041
+ type: "site.analytics",
4042
+ idFrom: "params.siteId"
4043
+ }],
4044
+ changes: []
4045
+ },
4046
+ lifecycle: { introduced: "1.0.0" },
4047
+ docs: {
4048
+ summary: "Query analytics report detail",
4049
+ description: "Returns the hosted analytics timeseries report with current and comparison totals.",
4050
+ tags: ["Analytics reports"],
4051
+ examples: {
4052
+ request: {
4053
+ params: { siteId: "s_01" },
4054
+ body: { state: {
4055
+ dimensions: ["date"],
4056
+ searchType: "web"
4057
+ } }
4058
+ },
4059
+ response: {
4060
+ data: {
4061
+ daily: [],
4062
+ totals: {
4063
+ clicks: 0,
4064
+ impressions: 0,
4065
+ ctr: 0,
4066
+ position: 0
4067
+ },
4068
+ meta: {
4069
+ siteUrl: "sc-domain:example.com",
4070
+ syncStatus: "synced",
4071
+ newestDateSynced: null,
4072
+ oldestDateSynced: null,
4073
+ dataDelay: "0 days"
4074
+ }
4075
+ },
4076
+ meta: {
4077
+ requestId: "req_01",
4078
+ surface: "analytics",
4079
+ version: "1.0",
4080
+ sourceName: "hosted-report",
4081
+ sourceKind: "sql",
4082
+ queryMs: 12
4083
+ }
4084
+ }
4085
+ }
4086
+ }
4087
+ })
4088
+ }
4089
+ });
4090
+ const realtime = defineHttpSurface({
4091
+ name: "realtime",
4092
+ prefix: "/api/realtime/v1",
4093
+ version: "1.0",
4094
+ operations: {
4095
+ getStreamHead: defineHttpOperation({
4096
+ id: "realtime.stream.head.get",
4097
+ method: "GET",
4098
+ path: "/stream/head",
4099
+ visibility: "public",
4100
+ semantics: {
4101
+ kind: "query",
4102
+ sideEffects: "none",
4103
+ idempotent: true,
4104
+ retry: "idempotent",
4105
+ readConsistency: "primary"
4106
+ },
4107
+ auth: {
4108
+ credentials: ["user_key", "partner_key"],
4109
+ scopes: ["realtime:connect"],
4110
+ ownership: [{
4111
+ credential: "user_key",
4112
+ rule: "principal_stream"
4113
+ }, {
4114
+ credential: "partner_key",
4115
+ rule: "principal_stream"
4116
+ }]
4117
+ },
4118
+ request: {
4119
+ params: null,
4120
+ query: null,
4121
+ headers: requestHeaders,
4122
+ body: null
4123
+ },
4124
+ responses: { 200: headResponse },
4125
+ errors: realtimeErrors,
4126
+ errorResponse: errorEnvelopeSchemas(realtimeErrors, realtimeSchemas.publicRequestId),
4127
+ resources: {
4128
+ reads: [],
4129
+ changes: []
4130
+ },
4131
+ lifecycle: { introduced: "1.0.0" },
4132
+ docs: {
4133
+ summary: "Get the inferred stream head",
4134
+ description: "Returns the current durable head for the credential's one exact stream.",
4135
+ tags: ["Realtime"],
4136
+ examples: {
4137
+ request: {},
4138
+ response: {
4139
+ data: { head: {
4140
+ streamId: "user:u_01",
4141
+ sequence: "142"
4142
+ } },
4143
+ meta: {
4144
+ requestId: "req_01",
4145
+ surface: "realtime",
4146
+ version: "1.0"
4147
+ }
4148
+ }
4149
+ }
4150
+ }
4151
+ }),
4152
+ createTicket: defineHttpOperation({
4153
+ id: "realtime.tickets.create",
4154
+ method: "POST",
4155
+ path: "/tickets",
4156
+ visibility: "public",
4157
+ semantics: {
4158
+ kind: "mutation",
4159
+ sideEffects: "state",
4160
+ idempotent: false,
4161
+ retry: "never",
4162
+ readConsistency: null
4163
+ },
4164
+ auth: {
4165
+ credentials: ["user_key", "partner_key"],
4166
+ scopes: ["realtime:connect"],
4167
+ ownership: [{
4168
+ credential: "user_key",
4169
+ rule: "principal_stream"
4170
+ }, {
4171
+ credential: "partner_key",
4172
+ rule: "principal_stream"
4173
+ }]
4174
+ },
4175
+ request: {
4176
+ params: null,
4177
+ query: null,
4178
+ headers: requestHeaders,
4179
+ body: realtimeSchemas.ticketRequest
4180
+ },
4181
+ responses: { 201: ticketResponse },
4182
+ errors: realtimeErrors,
4183
+ errorResponse: errorEnvelopeSchemas(realtimeErrors, realtimeSchemas.publicRequestId),
4184
+ resources: {
4185
+ reads: [],
4186
+ changes: []
4187
+ },
4188
+ lifecycle: { introduced: "1.0.0" },
4189
+ docs: {
4190
+ summary: "Create a realtime ticket",
4191
+ description: "Creates a short-lived single-use ticket for the credential's inferred stream.",
4192
+ tags: ["Realtime"],
4193
+ examples: {
4194
+ request: { body: { origin: "https://nuxtseo.com" } },
4195
+ response: {
4196
+ data: {
4197
+ socketUrl: "wss://gscdump.com/ws/v1",
4198
+ protocol: GSCDUMP_REALTIME_SUBPROTOCOL,
4199
+ protocolVersion: 1,
4200
+ ticket: "gscdump.ticket.v1.cGF5bG9hZA.c2lnbmF0dXJl",
4201
+ expiresAt: "2026-07-14T08:01:00.000Z",
4202
+ head: {
4203
+ streamId: "user:u_01",
4204
+ sequence: "142"
4205
+ },
4206
+ maxConnectionSeconds: 900
4207
+ },
4208
+ meta: {
4209
+ requestId: "req_01",
4210
+ surface: "realtime",
4211
+ version: "1.0"
4212
+ }
4213
+ }
4214
+ }
4215
+ }
4216
+ })
4217
+ }
4218
+ });
4219
+ return {
4220
+ constants: {
4221
+ httpVersion: "1.0",
4222
+ realtimeProtocolVersion: 1,
4223
+ realtimeSubprotocol: GSCDUMP_REALTIME_SUBPROTOCOL,
4224
+ realtimeTicketPrefix: GSCDUMP_REALTIME_TICKET_PREFIX,
4225
+ realtimeTicketIssuer: GSCDUMP_REALTIME_TICKET_ISSUER,
4226
+ realtimeTicketAudience: GSCDUMP_REALTIME_TICKET_AUDIENCE,
4227
+ ping: GSCDUMP_REALTIME_PING,
4228
+ pong: GSCDUMP_REALTIME_PONG,
4229
+ ticketTtlSeconds: 60,
4230
+ maxConnectionSeconds: 900,
4231
+ ticketPolicy: GSCDUMP_REALTIME_TICKET_POLICY,
4232
+ connectionPolicy: GSCDUMP_REALTIME_CONNECTION_POLICY,
4233
+ replayPolicy: GSCDUMP_REALTIME_REPLAY_POLICY,
4234
+ limits: GSCDUMP_REALTIME_LIMITS,
4235
+ ackPolicy: GSCDUMP_REALTIME_ACK_POLICY,
4236
+ closeCodes: GSCDUMP_REALTIME_CLOSE_CODES,
4237
+ eventSemantics: REALTIME_V1_EVENT_SEMANTICS
4238
+ },
4239
+ schemas: {
4240
+ requestMetadata,
4241
+ requestHeaders,
4242
+ responseMeta,
4243
+ errorEnvelope,
4244
+ analyticsReportDetailRequest,
4245
+ analyticsReportDetailResponse,
4246
+ analyticsReportRequest,
4247
+ analyticsReportResponse,
4248
+ analyticsRowsRequest,
4249
+ analyticsRowsResponse,
4250
+ analysisBundleQuery,
4251
+ analysisBundleResponse,
4252
+ analysisQuery,
4253
+ analysisResponse,
4254
+ availableSitesQuery,
4255
+ availableSitesResponse,
4256
+ indexingDiagnosticsQuery,
4257
+ indexingDiagnosticsResponse,
4258
+ indexingSummaryQuery,
4259
+ indexingSummaryResponse,
4260
+ indexingUrlsQuery,
4261
+ indexingUrlsResponse,
4262
+ lifecycleResponse,
4263
+ registerSiteRequest,
4264
+ sitemapChangesQuery,
4265
+ sitemapChangesResponse,
4266
+ sitemapsResponse,
4267
+ siteDeletionResponse,
4268
+ siteRegistrationResponse,
4269
+ streamHeadResponse: headResponse,
4270
+ ticketResponse,
4271
+ userRegistrationResponse,
4272
+ userTokenUpdateResponse,
4273
+ ...realtimeSchemas
4274
+ },
4275
+ surfaces: {
4276
+ partner,
4277
+ analytics,
4278
+ realtime
4279
+ }
4280
+ };
4281
+ }
4282
+ export { GSCDUMP_HTTP_V1_VERSION, createGscdumpV1Protocol };