@wevion/cli 1.0.3027 → 1.0.3033

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.
Files changed (2) hide show
  1. package/openapi.json +3436 -13
  2. package/package.json +1 -1
package/openapi.json CHANGED
@@ -128477,6 +128477,3371 @@
128477
128477
  }
128478
128478
  }
128479
128479
  },
128480
+ "/api/v1/google/search-term-campaigns": {
128481
+ "get": {
128482
+ "operationId": "listGoogleSearchTermCampaigns",
128483
+ "summary": "List Google Ads campaigns with synced search terms",
128484
+ "tags": [
128485
+ "google"
128486
+ ],
128487
+ "description": "Returns campaigns discovered from the cached Google Ads search terms. Used as the campaign picker before listing semantic buckets, so buckets and apply-all actions stay campaign-scoped.",
128488
+ "parameters": [
128489
+ {
128490
+ "schema": {
128491
+ "type": "string"
128492
+ },
128493
+ "in": "query",
128494
+ "name": "customer_id",
128495
+ "required": false,
128496
+ "description": "Google Ads customer (account) id."
128497
+ }
128498
+ ],
128499
+ "security": [
128500
+ {
128501
+ "bearerAuth": []
128502
+ }
128503
+ ],
128504
+ "responses": {
128505
+ "200": {
128506
+ "description": "Successful response",
128507
+ "content": {
128508
+ "application/json": {
128509
+ "schema": {
128510
+ "additionalProperties": false,
128511
+ "type": "object",
128512
+ "required": [
128513
+ "items"
128514
+ ],
128515
+ "properties": {
128516
+ "items": {
128517
+ "type": "array",
128518
+ "items": {
128519
+ "additionalProperties": false,
128520
+ "type": "object",
128521
+ "required": [
128522
+ "campaign_id",
128523
+ "campaign_name",
128524
+ "advertising_channel_type",
128525
+ "term_count",
128526
+ "unreviewed_count",
128527
+ "clicks",
128528
+ "cost_micros",
128529
+ "cost",
128530
+ "conversions"
128531
+ ],
128532
+ "properties": {
128533
+ "campaign_id": {
128534
+ "type": "string"
128535
+ },
128536
+ "campaign_name": {
128537
+ "anyOf": [
128538
+ {
128539
+ "type": "string"
128540
+ },
128541
+ {
128542
+ "type": "null"
128543
+ }
128544
+ ]
128545
+ },
128546
+ "advertising_channel_type": {
128547
+ "anyOf": [
128548
+ {
128549
+ "type": "string"
128550
+ },
128551
+ {
128552
+ "type": "null"
128553
+ }
128554
+ ]
128555
+ },
128556
+ "term_count": {
128557
+ "type": "number"
128558
+ },
128559
+ "unreviewed_count": {
128560
+ "type": "number"
128561
+ },
128562
+ "clicks": {
128563
+ "type": "number"
128564
+ },
128565
+ "cost_micros": {
128566
+ "type": "number"
128567
+ },
128568
+ "cost": {
128569
+ "type": "number"
128570
+ },
128571
+ "conversions": {
128572
+ "type": "number"
128573
+ }
128574
+ }
128575
+ }
128576
+ }
128577
+ }
128578
+ }
128579
+ }
128580
+ }
128581
+ },
128582
+ "401": {
128583
+ "description": "Unauthorized - missing or invalid bearer token",
128584
+ "content": {
128585
+ "application/json": {
128586
+ "schema": {
128587
+ "$ref": "#/components/schemas/ErrorResponse"
128588
+ }
128589
+ }
128590
+ }
128591
+ },
128592
+ "500": {
128593
+ "description": "Internal server error",
128594
+ "content": {
128595
+ "application/json": {
128596
+ "schema": {
128597
+ "type": "object",
128598
+ "required": [
128599
+ "error"
128600
+ ],
128601
+ "properties": {
128602
+ "error": {
128603
+ "type": "string"
128604
+ },
128605
+ "code": {
128606
+ "type": "string"
128607
+ }
128608
+ }
128609
+ }
128610
+ }
128611
+ }
128612
+ },
128613
+ "503": {
128614
+ "description": "Service unavailable",
128615
+ "content": {
128616
+ "application/json": {
128617
+ "schema": {
128618
+ "type": "object",
128619
+ "required": [
128620
+ "error"
128621
+ ],
128622
+ "properties": {
128623
+ "error": {
128624
+ "type": "string"
128625
+ },
128626
+ "code": {
128627
+ "type": "string"
128628
+ }
128629
+ }
128630
+ }
128631
+ }
128632
+ }
128633
+ }
128634
+ }
128635
+ }
128636
+ },
128637
+ "/api/v1/google/search-term-buckets": {
128638
+ "get": {
128639
+ "operationId": "listGoogleSearchTermBuckets",
128640
+ "summary": "List Google Ads search-term semantic buckets",
128641
+ "tags": [
128642
+ "google"
128643
+ ],
128644
+ "description": "Returns semantic buckets built from cached Google Ads search terms. Buckets are denormalized with counts, cost, conversions, representative terms, and AI-generated hints so the UI can review intent groups instead of raw term rows.",
128645
+ "parameters": [
128646
+ {
128647
+ "schema": {
128648
+ "type": "string"
128649
+ },
128650
+ "in": "query",
128651
+ "name": "customer_id",
128652
+ "required": false,
128653
+ "description": "Google Ads customer (account) id."
128654
+ },
128655
+ {
128656
+ "schema": {
128657
+ "type": "string"
128658
+ },
128659
+ "in": "query",
128660
+ "name": "campaign_id",
128661
+ "required": false,
128662
+ "description": "Google Ads campaign id. Required by the UI."
128663
+ },
128664
+ {
128665
+ "schema": {
128666
+ "anyOf": [
128667
+ {
128668
+ "type": "string",
128669
+ "enum": [
128670
+ "OPEN"
128671
+ ]
128672
+ },
128673
+ {
128674
+ "type": "string",
128675
+ "enum": [
128676
+ "EXCLUDED"
128677
+ ]
128678
+ },
128679
+ {
128680
+ "type": "string",
128681
+ "enum": [
128682
+ "KEPT"
128683
+ ]
128684
+ },
128685
+ {
128686
+ "type": "string",
128687
+ "enum": [
128688
+ "SNOOZED"
128689
+ ]
128690
+ },
128691
+ {
128692
+ "type": "string",
128693
+ "enum": [
128694
+ "ARCHIVED"
128695
+ ]
128696
+ }
128697
+ ]
128698
+ },
128699
+ "in": "query",
128700
+ "name": "status",
128701
+ "required": false
128702
+ },
128703
+ {
128704
+ "schema": {
128705
+ "minimum": 1,
128706
+ "maximum": 200,
128707
+ "default": 100,
128708
+ "type": "integer"
128709
+ },
128710
+ "in": "query",
128711
+ "name": "limit",
128712
+ "required": false
128713
+ },
128714
+ {
128715
+ "schema": {
128716
+ "minimum": 0,
128717
+ "default": 0,
128718
+ "type": "integer"
128719
+ },
128720
+ "in": "query",
128721
+ "name": "offset",
128722
+ "required": false
128723
+ }
128724
+ ],
128725
+ "security": [
128726
+ {
128727
+ "bearerAuth": []
128728
+ }
128729
+ ],
128730
+ "responses": {
128731
+ "200": {
128732
+ "description": "Successful response",
128733
+ "content": {
128734
+ "application/json": {
128735
+ "schema": {
128736
+ "additionalProperties": false,
128737
+ "type": "object",
128738
+ "required": [
128739
+ "items",
128740
+ "total"
128741
+ ],
128742
+ "properties": {
128743
+ "items": {
128744
+ "type": "array",
128745
+ "items": {
128746
+ "additionalProperties": false,
128747
+ "type": "object",
128748
+ "required": [
128749
+ "id",
128750
+ "customer_id",
128751
+ "campaign_id",
128752
+ "campaign_name",
128753
+ "advertising_channel_type",
128754
+ "label",
128755
+ "hint",
128756
+ "recommended_action",
128757
+ "status",
128758
+ "representative_terms",
128759
+ "term_count",
128760
+ "unreviewed_count",
128761
+ "excluded_count",
128762
+ "kept_count",
128763
+ "snoozed_count",
128764
+ "impressions",
128765
+ "clicks",
128766
+ "cost_micros",
128767
+ "cost",
128768
+ "conversions",
128769
+ "conversions_value",
128770
+ "similarity_threshold",
128771
+ "max_terms",
128772
+ "ai_model",
128773
+ "created_at",
128774
+ "updated_at"
128775
+ ],
128776
+ "properties": {
128777
+ "id": {
128778
+ "type": "string"
128779
+ },
128780
+ "customer_id": {
128781
+ "type": "string"
128782
+ },
128783
+ "campaign_id": {
128784
+ "anyOf": [
128785
+ {
128786
+ "type": "string"
128787
+ },
128788
+ {
128789
+ "type": "null"
128790
+ }
128791
+ ]
128792
+ },
128793
+ "campaign_name": {
128794
+ "anyOf": [
128795
+ {
128796
+ "type": "string"
128797
+ },
128798
+ {
128799
+ "type": "null"
128800
+ }
128801
+ ]
128802
+ },
128803
+ "advertising_channel_type": {
128804
+ "anyOf": [
128805
+ {
128806
+ "type": "string"
128807
+ },
128808
+ {
128809
+ "type": "null"
128810
+ }
128811
+ ]
128812
+ },
128813
+ "label": {
128814
+ "type": "string"
128815
+ },
128816
+ "hint": {
128817
+ "anyOf": [
128818
+ {
128819
+ "type": "string"
128820
+ },
128821
+ {
128822
+ "type": "null"
128823
+ }
128824
+ ]
128825
+ },
128826
+ "recommended_action": {
128827
+ "type": "string"
128828
+ },
128829
+ "status": {
128830
+ "type": "string"
128831
+ },
128832
+ "representative_terms": {
128833
+ "type": "array",
128834
+ "items": {
128835
+ "type": "string"
128836
+ }
128837
+ },
128838
+ "term_count": {
128839
+ "type": "number"
128840
+ },
128841
+ "unreviewed_count": {
128842
+ "type": "number"
128843
+ },
128844
+ "excluded_count": {
128845
+ "type": "number"
128846
+ },
128847
+ "kept_count": {
128848
+ "type": "number"
128849
+ },
128850
+ "snoozed_count": {
128851
+ "type": "number"
128852
+ },
128853
+ "impressions": {
128854
+ "type": "number"
128855
+ },
128856
+ "clicks": {
128857
+ "type": "number"
128858
+ },
128859
+ "cost_micros": {
128860
+ "type": "number"
128861
+ },
128862
+ "cost": {
128863
+ "type": "number"
128864
+ },
128865
+ "conversions": {
128866
+ "type": "number"
128867
+ },
128868
+ "conversions_value": {
128869
+ "type": "number"
128870
+ },
128871
+ "similarity_threshold": {
128872
+ "type": "number"
128873
+ },
128874
+ "max_terms": {
128875
+ "type": "number"
128876
+ },
128877
+ "ai_model": {
128878
+ "anyOf": [
128879
+ {
128880
+ "type": "string"
128881
+ },
128882
+ {
128883
+ "type": "null"
128884
+ }
128885
+ ]
128886
+ },
128887
+ "created_at": {
128888
+ "type": "string"
128889
+ },
128890
+ "updated_at": {
128891
+ "type": "string"
128892
+ }
128893
+ }
128894
+ }
128895
+ },
128896
+ "total": {
128897
+ "type": "number"
128898
+ }
128899
+ }
128900
+ }
128901
+ }
128902
+ }
128903
+ },
128904
+ "401": {
128905
+ "description": "Unauthorized - missing or invalid bearer token",
128906
+ "content": {
128907
+ "application/json": {
128908
+ "schema": {
128909
+ "$ref": "#/components/schemas/ErrorResponse"
128910
+ }
128911
+ }
128912
+ }
128913
+ },
128914
+ "500": {
128915
+ "description": "Internal server error",
128916
+ "content": {
128917
+ "application/json": {
128918
+ "schema": {
128919
+ "type": "object",
128920
+ "required": [
128921
+ "error"
128922
+ ],
128923
+ "properties": {
128924
+ "error": {
128925
+ "type": "string"
128926
+ },
128927
+ "code": {
128928
+ "type": "string"
128929
+ }
128930
+ }
128931
+ }
128932
+ }
128933
+ }
128934
+ },
128935
+ "503": {
128936
+ "description": "Service unavailable",
128937
+ "content": {
128938
+ "application/json": {
128939
+ "schema": {
128940
+ "type": "object",
128941
+ "required": [
128942
+ "error"
128943
+ ],
128944
+ "properties": {
128945
+ "error": {
128946
+ "type": "string"
128947
+ },
128948
+ "code": {
128949
+ "type": "string"
128950
+ }
128951
+ }
128952
+ }
128953
+ }
128954
+ }
128955
+ }
128956
+ }
128957
+ }
128958
+ },
128959
+ "/api/v1/google/search-term-buckets/{id}/terms": {
128960
+ "get": {
128961
+ "operationId": "listGoogleSearchTermBucketTerms",
128962
+ "summary": "List terms inside a Google Ads search-term bucket",
128963
+ "tags": [
128964
+ "google"
128965
+ ],
128966
+ "description": "Returns the raw search-term rows assigned to one semantic bucket. Used by the Review drawer before creating a negative-keyword batch.",
128967
+ "parameters": [
128968
+ {
128969
+ "schema": {
128970
+ "minimum": 1,
128971
+ "maximum": 500,
128972
+ "default": 100,
128973
+ "type": "integer"
128974
+ },
128975
+ "in": "query",
128976
+ "name": "limit",
128977
+ "required": false
128978
+ },
128979
+ {
128980
+ "schema": {
128981
+ "minimum": 0,
128982
+ "default": 0,
128983
+ "type": "integer"
128984
+ },
128985
+ "in": "query",
128986
+ "name": "offset",
128987
+ "required": false
128988
+ },
128989
+ {
128990
+ "schema": {
128991
+ "format": "uuid",
128992
+ "type": "string"
128993
+ },
128994
+ "in": "path",
128995
+ "name": "id",
128996
+ "required": true
128997
+ }
128998
+ ],
128999
+ "security": [
129000
+ {
129001
+ "bearerAuth": []
129002
+ }
129003
+ ],
129004
+ "responses": {
129005
+ "200": {
129006
+ "description": "Successful response",
129007
+ "content": {
129008
+ "application/json": {
129009
+ "schema": {
129010
+ "additionalProperties": false,
129011
+ "type": "object",
129012
+ "required": [
129013
+ "items",
129014
+ "total"
129015
+ ],
129016
+ "properties": {
129017
+ "items": {
129018
+ "type": "array",
129019
+ "items": {
129020
+ "additionalProperties": false,
129021
+ "type": "object",
129022
+ "required": [
129023
+ "id",
129024
+ "customer_id",
129025
+ "source_view",
129026
+ "search_term",
129027
+ "match_type",
129028
+ "review_status",
129029
+ "campaign_id",
129030
+ "campaign_name",
129031
+ "advertising_channel_type",
129032
+ "ad_group_id",
129033
+ "ad_group_name",
129034
+ "clicks",
129035
+ "cost_micros",
129036
+ "conversions",
129037
+ "similarity"
129038
+ ],
129039
+ "properties": {
129040
+ "id": {
129041
+ "type": "string"
129042
+ },
129043
+ "customer_id": {
129044
+ "type": "string"
129045
+ },
129046
+ "source_view": {
129047
+ "type": "string"
129048
+ },
129049
+ "search_term": {
129050
+ "type": "string"
129051
+ },
129052
+ "match_type": {
129053
+ "anyOf": [
129054
+ {
129055
+ "type": "string"
129056
+ },
129057
+ {
129058
+ "type": "null"
129059
+ }
129060
+ ]
129061
+ },
129062
+ "review_status": {
129063
+ "type": "string"
129064
+ },
129065
+ "campaign_id": {
129066
+ "anyOf": [
129067
+ {
129068
+ "type": "string"
129069
+ },
129070
+ {
129071
+ "type": "null"
129072
+ }
129073
+ ]
129074
+ },
129075
+ "campaign_name": {
129076
+ "anyOf": [
129077
+ {
129078
+ "type": "string"
129079
+ },
129080
+ {
129081
+ "type": "null"
129082
+ }
129083
+ ]
129084
+ },
129085
+ "advertising_channel_type": {
129086
+ "anyOf": [
129087
+ {
129088
+ "type": "string"
129089
+ },
129090
+ {
129091
+ "type": "null"
129092
+ }
129093
+ ]
129094
+ },
129095
+ "ad_group_id": {
129096
+ "anyOf": [
129097
+ {
129098
+ "type": "string"
129099
+ },
129100
+ {
129101
+ "type": "null"
129102
+ }
129103
+ ]
129104
+ },
129105
+ "ad_group_name": {
129106
+ "anyOf": [
129107
+ {
129108
+ "type": "string"
129109
+ },
129110
+ {
129111
+ "type": "null"
129112
+ }
129113
+ ]
129114
+ },
129115
+ "clicks": {
129116
+ "type": "number"
129117
+ },
129118
+ "cost_micros": {
129119
+ "type": "number"
129120
+ },
129121
+ "conversions": {
129122
+ "type": "number"
129123
+ },
129124
+ "similarity": {
129125
+ "anyOf": [
129126
+ {
129127
+ "type": "number"
129128
+ },
129129
+ {
129130
+ "type": "null"
129131
+ }
129132
+ ]
129133
+ }
129134
+ }
129135
+ }
129136
+ },
129137
+ "total": {
129138
+ "type": "number"
129139
+ }
129140
+ }
129141
+ }
129142
+ }
129143
+ }
129144
+ },
129145
+ "401": {
129146
+ "description": "Unauthorized - missing or invalid bearer token",
129147
+ "content": {
129148
+ "application/json": {
129149
+ "schema": {
129150
+ "$ref": "#/components/schemas/ErrorResponse"
129151
+ }
129152
+ }
129153
+ }
129154
+ },
129155
+ "500": {
129156
+ "description": "Internal server error",
129157
+ "content": {
129158
+ "application/json": {
129159
+ "schema": {
129160
+ "type": "object",
129161
+ "required": [
129162
+ "error"
129163
+ ],
129164
+ "properties": {
129165
+ "error": {
129166
+ "type": "string"
129167
+ },
129168
+ "code": {
129169
+ "type": "string"
129170
+ }
129171
+ }
129172
+ }
129173
+ }
129174
+ }
129175
+ },
129176
+ "503": {
129177
+ "description": "Service unavailable",
129178
+ "content": {
129179
+ "application/json": {
129180
+ "schema": {
129181
+ "type": "object",
129182
+ "required": [
129183
+ "error"
129184
+ ],
129185
+ "properties": {
129186
+ "error": {
129187
+ "type": "string"
129188
+ },
129189
+ "code": {
129190
+ "type": "string"
129191
+ }
129192
+ }
129193
+ }
129194
+ }
129195
+ }
129196
+ }
129197
+ }
129198
+ },
129199
+ "post": {
129200
+ "operationId": "assignGoogleSearchTermsToBucket",
129201
+ "summary": "Save Google Ads search terms in a semantic bucket",
129202
+ "tags": [
129203
+ "google"
129204
+ ],
129205
+ "description": "Manually assigns cached search terms to the given bucket (a term belongs to at most one bucket, so this moves them). Bucket and terms must share session and Google account. When the bucket cannot fit the request the call fails with 409 BUCKET_FULL unless create_overflow is set, in which case continuation buckets (\"<label> · 2\") are created on the same account+campaign. Every touched bucket gets its counters refreshed.",
129206
+ "requestBody": {
129207
+ "required": true,
129208
+ "content": {
129209
+ "application/json": {
129210
+ "schema": {
129211
+ "additionalProperties": false,
129212
+ "type": "object",
129213
+ "required": [
129214
+ "search_term_ids"
129215
+ ],
129216
+ "properties": {
129217
+ "search_term_ids": {
129218
+ "minItems": 1,
129219
+ "maxItems": 500,
129220
+ "type": "array",
129221
+ "items": {
129222
+ "format": "uuid",
129223
+ "type": "string"
129224
+ }
129225
+ },
129226
+ "create_overflow": {
129227
+ "description": "When the bucket cannot fit the request, create continuation buckets (\"<label> · 2\") instead of failing with BUCKET_FULL. The UI asks the human first.",
129228
+ "type": "boolean"
129229
+ }
129230
+ }
129231
+ }
129232
+ }
129233
+ }
129234
+ },
129235
+ "parameters": [
129236
+ {
129237
+ "schema": {
129238
+ "format": "uuid",
129239
+ "type": "string"
129240
+ },
129241
+ "in": "path",
129242
+ "name": "id",
129243
+ "required": true
129244
+ }
129245
+ ],
129246
+ "security": [
129247
+ {
129248
+ "bearerAuth": []
129249
+ }
129250
+ ],
129251
+ "responses": {
129252
+ "200": {
129253
+ "description": "Successful response",
129254
+ "content": {
129255
+ "application/json": {
129256
+ "schema": {
129257
+ "additionalProperties": false,
129258
+ "type": "object",
129259
+ "required": [
129260
+ "bucket_id",
129261
+ "assigned",
129262
+ "created_buckets"
129263
+ ],
129264
+ "properties": {
129265
+ "bucket_id": {
129266
+ "type": "string"
129267
+ },
129268
+ "assigned": {
129269
+ "description": "Terms actually moved into the bucket (idempotent re-adds excluded).",
129270
+ "type": "number"
129271
+ },
129272
+ "created_buckets": {
129273
+ "description": "Continuation buckets created to absorb the overflow (empty when everything fit).",
129274
+ "type": "array",
129275
+ "items": {
129276
+ "additionalProperties": false,
129277
+ "type": "object",
129278
+ "required": [
129279
+ "id",
129280
+ "label"
129281
+ ],
129282
+ "properties": {
129283
+ "id": {
129284
+ "type": "string"
129285
+ },
129286
+ "label": {
129287
+ "type": "string"
129288
+ }
129289
+ }
129290
+ }
129291
+ }
129292
+ }
129293
+ }
129294
+ }
129295
+ }
129296
+ },
129297
+ "400": {
129298
+ "description": "Invalid request - schema validation failed",
129299
+ "content": {
129300
+ "application/json": {
129301
+ "schema": {
129302
+ "type": "object",
129303
+ "required": [
129304
+ "error"
129305
+ ],
129306
+ "properties": {
129307
+ "error": {
129308
+ "type": "string"
129309
+ },
129310
+ "code": {
129311
+ "type": "string"
129312
+ }
129313
+ }
129314
+ }
129315
+ }
129316
+ }
129317
+ },
129318
+ "401": {
129319
+ "description": "Unauthorized - missing or invalid bearer token",
129320
+ "content": {
129321
+ "application/json": {
129322
+ "schema": {
129323
+ "$ref": "#/components/schemas/ErrorResponse"
129324
+ }
129325
+ }
129326
+ }
129327
+ },
129328
+ "404": {
129329
+ "description": "Resource not found",
129330
+ "content": {
129331
+ "application/json": {
129332
+ "schema": {
129333
+ "type": "object",
129334
+ "required": [
129335
+ "error"
129336
+ ],
129337
+ "properties": {
129338
+ "error": {
129339
+ "type": "string"
129340
+ },
129341
+ "code": {
129342
+ "type": "string"
129343
+ }
129344
+ }
129345
+ }
129346
+ }
129347
+ }
129348
+ },
129349
+ "409": {
129350
+ "description": "Conflict - resource state prevents the operation",
129351
+ "content": {
129352
+ "application/json": {
129353
+ "schema": {
129354
+ "additionalProperties": false,
129355
+ "type": "object",
129356
+ "required": [
129357
+ "error",
129358
+ "code",
129359
+ "capacity_left",
129360
+ "overflow_count",
129361
+ "next_label"
129362
+ ],
129363
+ "properties": {
129364
+ "error": {
129365
+ "type": "string"
129366
+ },
129367
+ "code": {
129368
+ "type": "string",
129369
+ "enum": [
129370
+ "BUCKET_FULL"
129371
+ ]
129372
+ },
129373
+ "capacity_left": {
129374
+ "type": "number"
129375
+ },
129376
+ "overflow_count": {
129377
+ "type": "number"
129378
+ },
129379
+ "next_label": {
129380
+ "description": "Label of the continuation bucket the retry would create.",
129381
+ "type": "string"
129382
+ }
129383
+ }
129384
+ }
129385
+ }
129386
+ }
129387
+ },
129388
+ "500": {
129389
+ "description": "Internal server error",
129390
+ "content": {
129391
+ "application/json": {
129392
+ "schema": {
129393
+ "type": "object",
129394
+ "required": [
129395
+ "error"
129396
+ ],
129397
+ "properties": {
129398
+ "error": {
129399
+ "type": "string"
129400
+ },
129401
+ "code": {
129402
+ "type": "string"
129403
+ }
129404
+ }
129405
+ }
129406
+ }
129407
+ }
129408
+ },
129409
+ "503": {
129410
+ "description": "Service unavailable",
129411
+ "content": {
129412
+ "application/json": {
129413
+ "schema": {
129414
+ "type": "object",
129415
+ "required": [
129416
+ "error"
129417
+ ],
129418
+ "properties": {
129419
+ "error": {
129420
+ "type": "string"
129421
+ },
129422
+ "code": {
129423
+ "type": "string"
129424
+ }
129425
+ }
129426
+ }
129427
+ }
129428
+ }
129429
+ }
129430
+ }
129431
+ }
129432
+ },
129433
+ "/api/v1/google/search-terms": {
129434
+ "get": {
129435
+ "operationId": "listGoogleSearchTerms",
129436
+ "summary": "List Google Ads search terms",
129437
+ "tags": [
129438
+ "google"
129439
+ ],
129440
+ "description": "Cross-account paginated read of cached Google Ads search terms for the authenticated user, with optional filters (account, campaign, ad group, match type, review status, text, min cost/clicks).",
129441
+ "parameters": [
129442
+ {
129443
+ "schema": {
129444
+ "type": "string"
129445
+ },
129446
+ "in": "query",
129447
+ "name": "customer_id",
129448
+ "required": false,
129449
+ "description": "Google Ads customer (account) id."
129450
+ },
129451
+ {
129452
+ "schema": {
129453
+ "type": "string"
129454
+ },
129455
+ "in": "query",
129456
+ "name": "campaign_id",
129457
+ "required": false
129458
+ },
129459
+ {
129460
+ "schema": {
129461
+ "type": "string"
129462
+ },
129463
+ "in": "query",
129464
+ "name": "ad_group_id",
129465
+ "required": false
129466
+ },
129467
+ {
129468
+ "schema": {
129469
+ "type": "string"
129470
+ },
129471
+ "in": "query",
129472
+ "name": "match_type",
129473
+ "required": false,
129474
+ "description": "EXACT | PHRASE | BROAD (search_term_view only)."
129475
+ },
129476
+ {
129477
+ "schema": {
129478
+ "anyOf": [
129479
+ {
129480
+ "type": "string",
129481
+ "enum": [
129482
+ "search_term_view"
129483
+ ]
129484
+ },
129485
+ {
129486
+ "type": "string",
129487
+ "enum": [
129488
+ "campaign_search_term_view"
129489
+ ]
129490
+ }
129491
+ ]
129492
+ },
129493
+ "in": "query",
129494
+ "name": "source_view",
129495
+ "required": false
129496
+ },
129497
+ {
129498
+ "schema": {
129499
+ "anyOf": [
129500
+ {
129501
+ "type": "string",
129502
+ "enum": [
129503
+ "UNREVIEWED"
129504
+ ]
129505
+ },
129506
+ {
129507
+ "type": "string",
129508
+ "enum": [
129509
+ "KEPT"
129510
+ ]
129511
+ },
129512
+ {
129513
+ "type": "string",
129514
+ "enum": [
129515
+ "EXCLUDED"
129516
+ ]
129517
+ },
129518
+ {
129519
+ "type": "string",
129520
+ "enum": [
129521
+ "SNOOZED"
129522
+ ]
129523
+ }
129524
+ ]
129525
+ },
129526
+ "in": "query",
129527
+ "name": "review_status",
129528
+ "required": false
129529
+ },
129530
+ {
129531
+ "schema": {
129532
+ "type": "string"
129533
+ },
129534
+ "in": "query",
129535
+ "name": "search",
129536
+ "required": false,
129537
+ "description": "Substring match on the normalized term."
129538
+ },
129539
+ {
129540
+ "schema": {
129541
+ "minimum": 0,
129542
+ "type": "integer"
129543
+ },
129544
+ "in": "query",
129545
+ "name": "min_cost_micros",
129546
+ "required": false
129547
+ },
129548
+ {
129549
+ "schema": {
129550
+ "minimum": 0,
129551
+ "type": "integer"
129552
+ },
129553
+ "in": "query",
129554
+ "name": "min_clicks",
129555
+ "required": false
129556
+ },
129557
+ {
129558
+ "schema": {
129559
+ "minimum": 1,
129560
+ "maximum": 500,
129561
+ "default": 100,
129562
+ "type": "integer"
129563
+ },
129564
+ "in": "query",
129565
+ "name": "limit",
129566
+ "required": false
129567
+ },
129568
+ {
129569
+ "schema": {
129570
+ "minimum": 0,
129571
+ "default": 0,
129572
+ "type": "integer"
129573
+ },
129574
+ "in": "query",
129575
+ "name": "offset",
129576
+ "required": false
129577
+ }
129578
+ ],
129579
+ "security": [
129580
+ {
129581
+ "bearerAuth": []
129582
+ }
129583
+ ],
129584
+ "responses": {
129585
+ "200": {
129586
+ "description": "Successful response",
129587
+ "content": {
129588
+ "application/json": {
129589
+ "schema": {
129590
+ "additionalProperties": false,
129591
+ "type": "object",
129592
+ "required": [
129593
+ "items",
129594
+ "total"
129595
+ ],
129596
+ "properties": {
129597
+ "items": {
129598
+ "type": "array",
129599
+ "items": {
129600
+ "additionalProperties": false,
129601
+ "type": "object",
129602
+ "required": [
129603
+ "id",
129604
+ "ai_action",
129605
+ "ai_bucket_id",
129606
+ "ai_bucket_label",
129607
+ "ai_hint",
129608
+ "customer_id",
129609
+ "source_view",
129610
+ "search_term",
129611
+ "normalized_term",
129612
+ "match_type",
129613
+ "term_status",
129614
+ "review_status",
129615
+ "campaign_id",
129616
+ "campaign_name",
129617
+ "advertising_channel_type",
129618
+ "ad_group_id",
129619
+ "ad_group_name",
129620
+ "impressions",
129621
+ "clicks",
129622
+ "cost_micros",
129623
+ "cost",
129624
+ "conversions",
129625
+ "conversions_value",
129626
+ "date_from",
129627
+ "date_to",
129628
+ "synced_at"
129629
+ ],
129630
+ "properties": {
129631
+ "id": {
129632
+ "type": "string"
129633
+ },
129634
+ "ai_action": {
129635
+ "anyOf": [
129636
+ {
129637
+ "type": "string"
129638
+ },
129639
+ {
129640
+ "type": "null"
129641
+ }
129642
+ ]
129643
+ },
129644
+ "ai_bucket_id": {
129645
+ "anyOf": [
129646
+ {
129647
+ "type": "string"
129648
+ },
129649
+ {
129650
+ "type": "null"
129651
+ }
129652
+ ]
129653
+ },
129654
+ "ai_bucket_label": {
129655
+ "anyOf": [
129656
+ {
129657
+ "type": "string"
129658
+ },
129659
+ {
129660
+ "type": "null"
129661
+ }
129662
+ ]
129663
+ },
129664
+ "ai_hint": {
129665
+ "anyOf": [
129666
+ {
129667
+ "type": "string"
129668
+ },
129669
+ {
129670
+ "type": "null"
129671
+ }
129672
+ ]
129673
+ },
129674
+ "customer_id": {
129675
+ "type": "string"
129676
+ },
129677
+ "source_view": {
129678
+ "type": "string"
129679
+ },
129680
+ "search_term": {
129681
+ "type": "string"
129682
+ },
129683
+ "normalized_term": {
129684
+ "type": "string"
129685
+ },
129686
+ "match_type": {
129687
+ "anyOf": [
129688
+ {
129689
+ "type": "string"
129690
+ },
129691
+ {
129692
+ "type": "null"
129693
+ }
129694
+ ]
129695
+ },
129696
+ "term_status": {
129697
+ "anyOf": [
129698
+ {
129699
+ "type": "string"
129700
+ },
129701
+ {
129702
+ "type": "null"
129703
+ }
129704
+ ]
129705
+ },
129706
+ "review_status": {
129707
+ "type": "string"
129708
+ },
129709
+ "campaign_id": {
129710
+ "anyOf": [
129711
+ {
129712
+ "type": "string"
129713
+ },
129714
+ {
129715
+ "type": "null"
129716
+ }
129717
+ ]
129718
+ },
129719
+ "campaign_name": {
129720
+ "anyOf": [
129721
+ {
129722
+ "type": "string"
129723
+ },
129724
+ {
129725
+ "type": "null"
129726
+ }
129727
+ ]
129728
+ },
129729
+ "advertising_channel_type": {
129730
+ "anyOf": [
129731
+ {
129732
+ "type": "string"
129733
+ },
129734
+ {
129735
+ "type": "null"
129736
+ }
129737
+ ]
129738
+ },
129739
+ "ad_group_id": {
129740
+ "anyOf": [
129741
+ {
129742
+ "type": "string"
129743
+ },
129744
+ {
129745
+ "type": "null"
129746
+ }
129747
+ ]
129748
+ },
129749
+ "ad_group_name": {
129750
+ "anyOf": [
129751
+ {
129752
+ "type": "string"
129753
+ },
129754
+ {
129755
+ "type": "null"
129756
+ }
129757
+ ]
129758
+ },
129759
+ "impressions": {
129760
+ "type": "number"
129761
+ },
129762
+ "clicks": {
129763
+ "type": "number"
129764
+ },
129765
+ "cost_micros": {
129766
+ "type": "number"
129767
+ },
129768
+ "cost": {
129769
+ "type": "number"
129770
+ },
129771
+ "conversions": {
129772
+ "type": "number"
129773
+ },
129774
+ "conversions_value": {
129775
+ "type": "number"
129776
+ },
129777
+ "date_from": {
129778
+ "anyOf": [
129779
+ {
129780
+ "type": "string"
129781
+ },
129782
+ {
129783
+ "type": "null"
129784
+ }
129785
+ ]
129786
+ },
129787
+ "date_to": {
129788
+ "anyOf": [
129789
+ {
129790
+ "type": "string"
129791
+ },
129792
+ {
129793
+ "type": "null"
129794
+ }
129795
+ ]
129796
+ },
129797
+ "synced_at": {
129798
+ "type": "string"
129799
+ }
129800
+ }
129801
+ }
129802
+ },
129803
+ "total": {
129804
+ "type": "number"
129805
+ }
129806
+ }
129807
+ }
129808
+ }
129809
+ }
129810
+ },
129811
+ "401": {
129812
+ "description": "Unauthorized - missing or invalid bearer token",
129813
+ "content": {
129814
+ "application/json": {
129815
+ "schema": {
129816
+ "$ref": "#/components/schemas/ErrorResponse"
129817
+ }
129818
+ }
129819
+ }
129820
+ },
129821
+ "500": {
129822
+ "description": "Internal server error",
129823
+ "content": {
129824
+ "application/json": {
129825
+ "schema": {
129826
+ "type": "object",
129827
+ "required": [
129828
+ "error"
129829
+ ],
129830
+ "properties": {
129831
+ "error": {
129832
+ "type": "string"
129833
+ },
129834
+ "code": {
129835
+ "type": "string"
129836
+ }
129837
+ }
129838
+ }
129839
+ }
129840
+ }
129841
+ },
129842
+ "503": {
129843
+ "description": "Service unavailable",
129844
+ "content": {
129845
+ "application/json": {
129846
+ "schema": {
129847
+ "type": "object",
129848
+ "required": [
129849
+ "error"
129850
+ ],
129851
+ "properties": {
129852
+ "error": {
129853
+ "type": "string"
129854
+ },
129855
+ "code": {
129856
+ "type": "string"
129857
+ }
129858
+ }
129859
+ }
129860
+ }
129861
+ }
129862
+ }
129863
+ }
129864
+ }
129865
+ },
129866
+ "/api/v1/google/search-terms/review": {
129867
+ "post": {
129868
+ "operationId": "reviewGoogleSearchTerms",
129869
+ "summary": "Set the review status of Google Ads search terms",
129870
+ "tags": [
129871
+ "google"
129872
+ ],
129873
+ "description": "Marks the given cached search terms as KEPT, SNOOZED or back to UNREVIEWED (per-row human decision). EXCLUDED cannot be set here: it is only produced by an applied negative-keyword batch. Rows outside the caller’s accessible sessions are silently skipped.",
129874
+ "requestBody": {
129875
+ "required": true,
129876
+ "content": {
129877
+ "application/json": {
129878
+ "schema": {
129879
+ "additionalProperties": false,
129880
+ "type": "object",
129881
+ "required": [
129882
+ "ids",
129883
+ "status"
129884
+ ],
129885
+ "properties": {
129886
+ "ids": {
129887
+ "minItems": 1,
129888
+ "maxItems": 500,
129889
+ "type": "array",
129890
+ "items": {
129891
+ "format": "uuid",
129892
+ "type": "string"
129893
+ }
129894
+ },
129895
+ "status": {
129896
+ "description": "Target review status. EXCLUDED is intentionally absent: it only comes from an applied batch.",
129897
+ "anyOf": [
129898
+ {
129899
+ "type": "string",
129900
+ "enum": [
129901
+ "KEPT"
129902
+ ]
129903
+ },
129904
+ {
129905
+ "type": "string",
129906
+ "enum": [
129907
+ "SNOOZED"
129908
+ ]
129909
+ },
129910
+ {
129911
+ "type": "string",
129912
+ "enum": [
129913
+ "UNREVIEWED"
129914
+ ]
129915
+ }
129916
+ ]
129917
+ }
129918
+ }
129919
+ }
129920
+ }
129921
+ }
129922
+ },
129923
+ "security": [
129924
+ {
129925
+ "bearerAuth": []
129926
+ }
129927
+ ],
129928
+ "responses": {
129929
+ "200": {
129930
+ "description": "Successful response",
129931
+ "content": {
129932
+ "application/json": {
129933
+ "schema": {
129934
+ "additionalProperties": false,
129935
+ "type": "object",
129936
+ "required": [
129937
+ "updated"
129938
+ ],
129939
+ "properties": {
129940
+ "updated": {
129941
+ "description": "Rows actually updated (accessible, not deleted, not EXCLUDED).",
129942
+ "type": "number"
129943
+ }
129944
+ }
129945
+ }
129946
+ }
129947
+ }
129948
+ },
129949
+ "401": {
129950
+ "description": "Unauthorized - missing or invalid bearer token",
129951
+ "content": {
129952
+ "application/json": {
129953
+ "schema": {
129954
+ "$ref": "#/components/schemas/ErrorResponse"
129955
+ }
129956
+ }
129957
+ }
129958
+ },
129959
+ "500": {
129960
+ "description": "Internal server error",
129961
+ "content": {
129962
+ "application/json": {
129963
+ "schema": {
129964
+ "type": "object",
129965
+ "required": [
129966
+ "error"
129967
+ ],
129968
+ "properties": {
129969
+ "error": {
129970
+ "type": "string"
129971
+ },
129972
+ "code": {
129973
+ "type": "string"
129974
+ }
129975
+ }
129976
+ }
129977
+ }
129978
+ }
129979
+ },
129980
+ "503": {
129981
+ "description": "Service unavailable",
129982
+ "content": {
129983
+ "application/json": {
129984
+ "schema": {
129985
+ "type": "object",
129986
+ "required": [
129987
+ "error"
129988
+ ],
129989
+ "properties": {
129990
+ "error": {
129991
+ "type": "string"
129992
+ },
129993
+ "code": {
129994
+ "type": "string"
129995
+ }
129996
+ }
129997
+ }
129998
+ }
129999
+ }
130000
+ }
130001
+ }
130002
+ }
130003
+ },
130004
+ "/api/v1/google/search-term-buckets/{id}/terms/{term_id}": {
130005
+ "delete": {
130006
+ "operationId": "removeGoogleSearchTermFromBucket",
130007
+ "summary": "Remove a Google Ads search term from a semantic bucket",
130008
+ "tags": [
130009
+ "google"
130010
+ ],
130011
+ "description": "Detaches one cached search term from the bucket (the term itself is untouched — only the membership is removed). The bucket counters are refreshed.",
130012
+ "parameters": [
130013
+ {
130014
+ "schema": {
130015
+ "format": "uuid",
130016
+ "type": "string"
130017
+ },
130018
+ "in": "path",
130019
+ "name": "id",
130020
+ "required": true
130021
+ },
130022
+ {
130023
+ "schema": {
130024
+ "format": "uuid",
130025
+ "type": "string"
130026
+ },
130027
+ "in": "path",
130028
+ "name": "term_id",
130029
+ "required": true
130030
+ }
130031
+ ],
130032
+ "security": [
130033
+ {
130034
+ "bearerAuth": []
130035
+ }
130036
+ ],
130037
+ "responses": {
130038
+ "200": {
130039
+ "description": "Successful response",
130040
+ "content": {
130041
+ "application/json": {
130042
+ "schema": {
130043
+ "additionalProperties": false,
130044
+ "type": "object",
130045
+ "required": [
130046
+ "removed"
130047
+ ],
130048
+ "properties": {
130049
+ "removed": {
130050
+ "type": "number"
130051
+ }
130052
+ }
130053
+ }
130054
+ }
130055
+ }
130056
+ },
130057
+ "401": {
130058
+ "description": "Unauthorized - missing or invalid bearer token",
130059
+ "content": {
130060
+ "application/json": {
130061
+ "schema": {
130062
+ "$ref": "#/components/schemas/ErrorResponse"
130063
+ }
130064
+ }
130065
+ }
130066
+ },
130067
+ "404": {
130068
+ "description": "Resource not found",
130069
+ "content": {
130070
+ "application/json": {
130071
+ "schema": {
130072
+ "type": "object",
130073
+ "required": [
130074
+ "error"
130075
+ ],
130076
+ "properties": {
130077
+ "error": {
130078
+ "type": "string"
130079
+ },
130080
+ "code": {
130081
+ "type": "string"
130082
+ }
130083
+ }
130084
+ }
130085
+ }
130086
+ }
130087
+ },
130088
+ "500": {
130089
+ "description": "Internal server error",
130090
+ "content": {
130091
+ "application/json": {
130092
+ "schema": {
130093
+ "type": "object",
130094
+ "required": [
130095
+ "error"
130096
+ ],
130097
+ "properties": {
130098
+ "error": {
130099
+ "type": "string"
130100
+ },
130101
+ "code": {
130102
+ "type": "string"
130103
+ }
130104
+ }
130105
+ }
130106
+ }
130107
+ }
130108
+ },
130109
+ "503": {
130110
+ "description": "Service unavailable",
130111
+ "content": {
130112
+ "application/json": {
130113
+ "schema": {
130114
+ "type": "object",
130115
+ "required": [
130116
+ "error"
130117
+ ],
130118
+ "properties": {
130119
+ "error": {
130120
+ "type": "string"
130121
+ },
130122
+ "code": {
130123
+ "type": "string"
130124
+ }
130125
+ }
130126
+ }
130127
+ }
130128
+ }
130129
+ }
130130
+ }
130131
+ }
130132
+ },
130133
+ "/api/v1/google/search-terms/sync": {
130134
+ "post": {
130135
+ "operationId": "syncGoogleSearchTerms",
130136
+ "summary": "Sync Google Ads search terms for an account",
130137
+ "tags": [
130138
+ "google"
130139
+ ],
130140
+ "description": "Fetches search_term_view (Search/Shopping) and campaign_search_term_view (Performance Max) for the given connected account and date range, and upserts them into the workspace cache. Preserves prior review decisions.",
130141
+ "requestBody": {
130142
+ "required": true,
130143
+ "content": {
130144
+ "application/json": {
130145
+ "schema": {
130146
+ "additionalProperties": false,
130147
+ "type": "object",
130148
+ "required": [
130149
+ "account_id",
130150
+ "date_from",
130151
+ "date_to"
130152
+ ],
130153
+ "properties": {
130154
+ "account_id": {
130155
+ "description": "Google Ads customer (account) id to sync.",
130156
+ "type": "string"
130157
+ },
130158
+ "date_from": {
130159
+ "description": "Inclusive start date (YYYY-MM-DD).",
130160
+ "type": "string"
130161
+ },
130162
+ "date_to": {
130163
+ "description": "Inclusive end date (YYYY-MM-DD).",
130164
+ "type": "string"
130165
+ }
130166
+ }
130167
+ }
130168
+ }
130169
+ }
130170
+ },
130171
+ "security": [
130172
+ {
130173
+ "bearerAuth": []
130174
+ }
130175
+ ],
130176
+ "responses": {
130177
+ "200": {
130178
+ "description": "Successful response",
130179
+ "content": {
130180
+ "application/json": {
130181
+ "schema": {
130182
+ "additionalProperties": false,
130183
+ "type": "object",
130184
+ "required": [
130185
+ "synced",
130186
+ "customer_id",
130187
+ "classification_queued"
130188
+ ],
130189
+ "properties": {
130190
+ "synced": {
130191
+ "description": "Number of search-term rows upserted.",
130192
+ "type": "number"
130193
+ },
130194
+ "customer_id": {
130195
+ "type": "string"
130196
+ },
130197
+ "classification_queued": {
130198
+ "type": "boolean"
130199
+ }
130200
+ }
130201
+ }
130202
+ }
130203
+ }
130204
+ },
130205
+ "400": {
130206
+ "description": "Invalid request - schema validation failed",
130207
+ "content": {
130208
+ "application/json": {
130209
+ "schema": {
130210
+ "type": "object",
130211
+ "required": [
130212
+ "error"
130213
+ ],
130214
+ "properties": {
130215
+ "error": {
130216
+ "type": "string"
130217
+ },
130218
+ "code": {
130219
+ "type": "string"
130220
+ }
130221
+ }
130222
+ }
130223
+ }
130224
+ }
130225
+ },
130226
+ "401": {
130227
+ "description": "Unauthorized - missing or invalid bearer token",
130228
+ "content": {
130229
+ "application/json": {
130230
+ "schema": {
130231
+ "$ref": "#/components/schemas/ErrorResponse"
130232
+ }
130233
+ }
130234
+ }
130235
+ },
130236
+ "500": {
130237
+ "description": "Internal server error",
130238
+ "content": {
130239
+ "application/json": {
130240
+ "schema": {
130241
+ "type": "object",
130242
+ "required": [
130243
+ "error"
130244
+ ],
130245
+ "properties": {
130246
+ "error": {
130247
+ "type": "string"
130248
+ },
130249
+ "code": {
130250
+ "type": "string"
130251
+ }
130252
+ }
130253
+ }
130254
+ }
130255
+ }
130256
+ },
130257
+ "503": {
130258
+ "description": "Service unavailable",
130259
+ "content": {
130260
+ "application/json": {
130261
+ "schema": {
130262
+ "type": "object",
130263
+ "required": [
130264
+ "error"
130265
+ ],
130266
+ "properties": {
130267
+ "error": {
130268
+ "type": "string"
130269
+ },
130270
+ "code": {
130271
+ "type": "string"
130272
+ }
130273
+ }
130274
+ }
130275
+ }
130276
+ }
130277
+ }
130278
+ }
130279
+ }
130280
+ },
130281
+ "/api/v1/google/term-batches": {
130282
+ "get": {
130283
+ "operationId": "listGoogleTermBatches",
130284
+ "summary": "List Google Ads exclusion batches",
130285
+ "tags": [
130286
+ "google"
130287
+ ],
130288
+ "description": "Returns a paginated list of exclusion batches for the authenticated user, with optional account/status filters.",
130289
+ "parameters": [
130290
+ {
130291
+ "schema": {
130292
+ "type": "string"
130293
+ },
130294
+ "in": "query",
130295
+ "name": "customer_id",
130296
+ "required": false
130297
+ },
130298
+ {
130299
+ "schema": {
130300
+ "type": "string"
130301
+ },
130302
+ "in": "query",
130303
+ "name": "status",
130304
+ "required": false
130305
+ },
130306
+ {
130307
+ "schema": {
130308
+ "minimum": 1,
130309
+ "maximum": 100,
130310
+ "default": 50,
130311
+ "type": "integer"
130312
+ },
130313
+ "in": "query",
130314
+ "name": "limit",
130315
+ "required": false
130316
+ },
130317
+ {
130318
+ "schema": {
130319
+ "minimum": 0,
130320
+ "default": 0,
130321
+ "type": "integer"
130322
+ },
130323
+ "in": "query",
130324
+ "name": "offset",
130325
+ "required": false
130326
+ }
130327
+ ],
130328
+ "security": [
130329
+ {
130330
+ "bearerAuth": []
130331
+ }
130332
+ ],
130333
+ "responses": {
130334
+ "200": {
130335
+ "description": "Successful response",
130336
+ "content": {
130337
+ "application/json": {
130338
+ "schema": {
130339
+ "additionalProperties": false,
130340
+ "type": "object",
130341
+ "required": [
130342
+ "items",
130343
+ "total"
130344
+ ],
130345
+ "properties": {
130346
+ "items": {
130347
+ "type": "array",
130348
+ "items": {
130349
+ "additionalProperties": false,
130350
+ "type": "object",
130351
+ "required": [
130352
+ "id",
130353
+ "customer_id",
130354
+ "status",
130355
+ "name",
130356
+ "total_operations",
130357
+ "succeeded_operations",
130358
+ "failed_operations",
130359
+ "created_at",
130360
+ "applied_at"
130361
+ ],
130362
+ "properties": {
130363
+ "id": {
130364
+ "type": "string"
130365
+ },
130366
+ "customer_id": {
130367
+ "type": "string"
130368
+ },
130369
+ "status": {
130370
+ "type": "string"
130371
+ },
130372
+ "name": {
130373
+ "anyOf": [
130374
+ {
130375
+ "type": "string"
130376
+ },
130377
+ {
130378
+ "type": "null"
130379
+ }
130380
+ ]
130381
+ },
130382
+ "total_operations": {
130383
+ "type": "number"
130384
+ },
130385
+ "succeeded_operations": {
130386
+ "type": "number"
130387
+ },
130388
+ "failed_operations": {
130389
+ "type": "number"
130390
+ },
130391
+ "created_at": {
130392
+ "type": "string"
130393
+ },
130394
+ "applied_at": {
130395
+ "anyOf": [
130396
+ {
130397
+ "type": "string"
130398
+ },
130399
+ {
130400
+ "type": "null"
130401
+ }
130402
+ ]
130403
+ }
130404
+ }
130405
+ }
130406
+ },
130407
+ "total": {
130408
+ "type": "number"
130409
+ }
130410
+ }
130411
+ }
130412
+ }
130413
+ }
130414
+ },
130415
+ "401": {
130416
+ "description": "Unauthorized - missing or invalid bearer token",
130417
+ "content": {
130418
+ "application/json": {
130419
+ "schema": {
130420
+ "$ref": "#/components/schemas/ErrorResponse"
130421
+ }
130422
+ }
130423
+ }
130424
+ },
130425
+ "500": {
130426
+ "description": "Internal server error",
130427
+ "content": {
130428
+ "application/json": {
130429
+ "schema": {
130430
+ "type": "object",
130431
+ "required": [
130432
+ "error"
130433
+ ],
130434
+ "properties": {
130435
+ "error": {
130436
+ "type": "string"
130437
+ },
130438
+ "code": {
130439
+ "type": "string"
130440
+ }
130441
+ }
130442
+ }
130443
+ }
130444
+ }
130445
+ },
130446
+ "503": {
130447
+ "description": "Service unavailable",
130448
+ "content": {
130449
+ "application/json": {
130450
+ "schema": {
130451
+ "type": "object",
130452
+ "required": [
130453
+ "error"
130454
+ ],
130455
+ "properties": {
130456
+ "error": {
130457
+ "type": "string"
130458
+ },
130459
+ "code": {
130460
+ "type": "string"
130461
+ }
130462
+ }
130463
+ }
130464
+ }
130465
+ }
130466
+ }
130467
+ }
130468
+ },
130469
+ "post": {
130470
+ "operationId": "createGoogleTermBatch",
130471
+ "summary": "Create a Google Ads negative-keyword exclusion batch (DRAFT)",
130472
+ "tags": [
130473
+ "google"
130474
+ ],
130475
+ "description": "Creates a DRAFT exclusion batch from selected search terms and the chosen scope (ad_group or campaign). Nothing is sent to Google until the batch is validated and approved.",
130476
+ "requestBody": {
130477
+ "required": true,
130478
+ "content": {
130479
+ "application/json": {
130480
+ "schema": {
130481
+ "additionalProperties": false,
130482
+ "type": "object",
130483
+ "required": [
130484
+ "customer_id",
130485
+ "operations"
130486
+ ],
130487
+ "properties": {
130488
+ "customer_id": {
130489
+ "description": "Google Ads customer (account) id the negatives apply to.",
130490
+ "type": "string"
130491
+ },
130492
+ "name": {
130493
+ "maxLength": 255,
130494
+ "type": "string"
130495
+ },
130496
+ "source": {
130497
+ "anyOf": [
130498
+ {
130499
+ "type": "string",
130500
+ "enum": [
130501
+ "manual"
130502
+ ]
130503
+ },
130504
+ {
130505
+ "type": "string",
130506
+ "enum": [
130507
+ "import"
130508
+ ]
130509
+ },
130510
+ {
130511
+ "type": "string",
130512
+ "enum": [
130513
+ "rule"
130514
+ ]
130515
+ },
130516
+ {
130517
+ "type": "string",
130518
+ "enum": [
130519
+ "semantic_selection"
130520
+ ]
130521
+ }
130522
+ ]
130523
+ },
130524
+ "operations": {
130525
+ "minItems": 1,
130526
+ "maxItems": 500,
130527
+ "description": "Negative keywords to create (hard cap 500 per batch — a human cannot review more meaningfully).",
130528
+ "type": "array",
130529
+ "items": {
130530
+ "additionalProperties": false,
130531
+ "type": "object",
130532
+ "required": [
130533
+ "scope",
130534
+ "keyword_text",
130535
+ "match_type"
130536
+ ],
130537
+ "properties": {
130538
+ "scope": {
130539
+ "anyOf": [
130540
+ {
130541
+ "type": "string",
130542
+ "enum": [
130543
+ "ad_group"
130544
+ ]
130545
+ },
130546
+ {
130547
+ "type": "string",
130548
+ "enum": [
130549
+ "campaign"
130550
+ ]
130551
+ }
130552
+ ]
130553
+ },
130554
+ "search_term_id": {
130555
+ "format": "uuid",
130556
+ "type": "string"
130557
+ },
130558
+ "search_term": {
130559
+ "type": "string"
130560
+ },
130561
+ "keyword_text": {
130562
+ "minLength": 1,
130563
+ "maxLength": 80,
130564
+ "type": "string"
130565
+ },
130566
+ "match_type": {
130567
+ "anyOf": [
130568
+ {
130569
+ "type": "string",
130570
+ "enum": [
130571
+ "EXACT"
130572
+ ]
130573
+ },
130574
+ {
130575
+ "type": "string",
130576
+ "enum": [
130577
+ "PHRASE"
130578
+ ]
130579
+ },
130580
+ {
130581
+ "type": "string",
130582
+ "enum": [
130583
+ "BROAD"
130584
+ ]
130585
+ }
130586
+ ]
130587
+ },
130588
+ "campaign_id": {
130589
+ "type": "string"
130590
+ },
130591
+ "ad_group_id": {
130592
+ "type": "string"
130593
+ }
130594
+ }
130595
+ }
130596
+ }
130597
+ }
130598
+ }
130599
+ }
130600
+ }
130601
+ },
130602
+ "security": [
130603
+ {
130604
+ "bearerAuth": []
130605
+ }
130606
+ ],
130607
+ "responses": {
130608
+ "201": {
130609
+ "description": "Resource created",
130610
+ "content": {
130611
+ "application/json": {
130612
+ "schema": {
130613
+ "additionalProperties": false,
130614
+ "type": "object",
130615
+ "required": [
130616
+ "id",
130617
+ "customer_id",
130618
+ "login_customer_id",
130619
+ "status",
130620
+ "source",
130621
+ "name",
130622
+ "total_operations",
130623
+ "succeeded_operations",
130624
+ "failed_operations",
130625
+ "validate_result",
130626
+ "created_at",
130627
+ "updated_at",
130628
+ "applied_at",
130629
+ "operations"
130630
+ ],
130631
+ "properties": {
130632
+ "id": {
130633
+ "type": "string"
130634
+ },
130635
+ "customer_id": {
130636
+ "type": "string"
130637
+ },
130638
+ "login_customer_id": {
130639
+ "anyOf": [
130640
+ {
130641
+ "type": "string"
130642
+ },
130643
+ {
130644
+ "type": "null"
130645
+ }
130646
+ ]
130647
+ },
130648
+ "status": {
130649
+ "type": "string"
130650
+ },
130651
+ "source": {
130652
+ "type": "string"
130653
+ },
130654
+ "name": {
130655
+ "anyOf": [
130656
+ {
130657
+ "type": "string"
130658
+ },
130659
+ {
130660
+ "type": "null"
130661
+ }
130662
+ ]
130663
+ },
130664
+ "total_operations": {
130665
+ "type": "number"
130666
+ },
130667
+ "succeeded_operations": {
130668
+ "type": "number"
130669
+ },
130670
+ "failed_operations": {
130671
+ "type": "number"
130672
+ },
130673
+ "validate_result": {
130674
+ "anyOf": [
130675
+ {
130676
+ "type": "array",
130677
+ "items": {
130678
+ "additionalProperties": false,
130679
+ "type": "object",
130680
+ "required": [
130681
+ "index",
130682
+ "status",
130683
+ "resourceName",
130684
+ "errorMessage"
130685
+ ],
130686
+ "properties": {
130687
+ "index": {
130688
+ "type": "number"
130689
+ },
130690
+ "status": {
130691
+ "anyOf": [
130692
+ {
130693
+ "type": "string",
130694
+ "enum": [
130695
+ "SUCCESS"
130696
+ ]
130697
+ },
130698
+ {
130699
+ "type": "string",
130700
+ "enum": [
130701
+ "FAILURE"
130702
+ ]
130703
+ }
130704
+ ]
130705
+ },
130706
+ "resourceName": {
130707
+ "anyOf": [
130708
+ {
130709
+ "type": "string"
130710
+ },
130711
+ {
130712
+ "type": "null"
130713
+ }
130714
+ ]
130715
+ },
130716
+ "errorMessage": {
130717
+ "anyOf": [
130718
+ {
130719
+ "type": "string"
130720
+ },
130721
+ {
130722
+ "type": "null"
130723
+ }
130724
+ ]
130725
+ }
130726
+ }
130727
+ }
130728
+ },
130729
+ {
130730
+ "type": "null"
130731
+ }
130732
+ ]
130733
+ },
130734
+ "created_at": {
130735
+ "type": "string"
130736
+ },
130737
+ "updated_at": {
130738
+ "type": "string"
130739
+ },
130740
+ "applied_at": {
130741
+ "anyOf": [
130742
+ {
130743
+ "type": "string"
130744
+ },
130745
+ {
130746
+ "type": "null"
130747
+ }
130748
+ ]
130749
+ },
130750
+ "operations": {
130751
+ "type": "array",
130752
+ "items": {
130753
+ "additionalProperties": false,
130754
+ "type": "object",
130755
+ "required": [
130756
+ "id",
130757
+ "scope",
130758
+ "search_term_id",
130759
+ "search_term",
130760
+ "keyword_text",
130761
+ "match_type",
130762
+ "campaign_id",
130763
+ "ad_group_id",
130764
+ "status",
130765
+ "google_resource_name",
130766
+ "error_message"
130767
+ ],
130768
+ "properties": {
130769
+ "id": {
130770
+ "type": "string"
130771
+ },
130772
+ "scope": {
130773
+ "type": "string"
130774
+ },
130775
+ "search_term_id": {
130776
+ "anyOf": [
130777
+ {
130778
+ "type": "string"
130779
+ },
130780
+ {
130781
+ "type": "null"
130782
+ }
130783
+ ]
130784
+ },
130785
+ "search_term": {
130786
+ "anyOf": [
130787
+ {
130788
+ "type": "string"
130789
+ },
130790
+ {
130791
+ "type": "null"
130792
+ }
130793
+ ]
130794
+ },
130795
+ "keyword_text": {
130796
+ "type": "string"
130797
+ },
130798
+ "match_type": {
130799
+ "type": "string"
130800
+ },
130801
+ "campaign_id": {
130802
+ "anyOf": [
130803
+ {
130804
+ "type": "string"
130805
+ },
130806
+ {
130807
+ "type": "null"
130808
+ }
130809
+ ]
130810
+ },
130811
+ "ad_group_id": {
130812
+ "anyOf": [
130813
+ {
130814
+ "type": "string"
130815
+ },
130816
+ {
130817
+ "type": "null"
130818
+ }
130819
+ ]
130820
+ },
130821
+ "status": {
130822
+ "type": "string"
130823
+ },
130824
+ "google_resource_name": {
130825
+ "anyOf": [
130826
+ {
130827
+ "type": "string"
130828
+ },
130829
+ {
130830
+ "type": "null"
130831
+ }
130832
+ ]
130833
+ },
130834
+ "error_message": {
130835
+ "anyOf": [
130836
+ {
130837
+ "type": "string"
130838
+ },
130839
+ {
130840
+ "type": "null"
130841
+ }
130842
+ ]
130843
+ }
130844
+ }
130845
+ }
130846
+ }
130847
+ }
130848
+ }
130849
+ }
130850
+ }
130851
+ },
130852
+ "400": {
130853
+ "description": "Invalid request - schema validation failed",
130854
+ "content": {
130855
+ "application/json": {
130856
+ "schema": {
130857
+ "type": "object",
130858
+ "required": [
130859
+ "error"
130860
+ ],
130861
+ "properties": {
130862
+ "error": {
130863
+ "type": "string"
130864
+ },
130865
+ "code": {
130866
+ "type": "string"
130867
+ }
130868
+ }
130869
+ }
130870
+ }
130871
+ }
130872
+ },
130873
+ "401": {
130874
+ "description": "Unauthorized - missing or invalid bearer token",
130875
+ "content": {
130876
+ "application/json": {
130877
+ "schema": {
130878
+ "$ref": "#/components/schemas/ErrorResponse"
130879
+ }
130880
+ }
130881
+ }
130882
+ },
130883
+ "404": {
130884
+ "description": "Resource not found",
130885
+ "content": {
130886
+ "application/json": {
130887
+ "schema": {
130888
+ "type": "object",
130889
+ "required": [
130890
+ "error"
130891
+ ],
130892
+ "properties": {
130893
+ "error": {
130894
+ "type": "string"
130895
+ },
130896
+ "code": {
130897
+ "type": "string"
130898
+ }
130899
+ }
130900
+ }
130901
+ }
130902
+ }
130903
+ },
130904
+ "409": {
130905
+ "description": "Conflict - resource state prevents the operation",
130906
+ "content": {
130907
+ "application/json": {
130908
+ "schema": {
130909
+ "type": "object",
130910
+ "required": [
130911
+ "error"
130912
+ ],
130913
+ "properties": {
130914
+ "error": {
130915
+ "type": "string"
130916
+ },
130917
+ "code": {
130918
+ "type": "string"
130919
+ }
130920
+ }
130921
+ }
130922
+ }
130923
+ }
130924
+ },
130925
+ "500": {
130926
+ "description": "Internal server error",
130927
+ "content": {
130928
+ "application/json": {
130929
+ "schema": {
130930
+ "type": "object",
130931
+ "required": [
130932
+ "error"
130933
+ ],
130934
+ "properties": {
130935
+ "error": {
130936
+ "type": "string"
130937
+ },
130938
+ "code": {
130939
+ "type": "string"
130940
+ }
130941
+ }
130942
+ }
130943
+ }
130944
+ }
130945
+ },
130946
+ "502": {
130947
+ "description": "Bad gateway - upstream provider failed",
130948
+ "content": {
130949
+ "application/json": {
130950
+ "schema": {
130951
+ "type": "object",
130952
+ "required": [
130953
+ "error"
130954
+ ],
130955
+ "properties": {
130956
+ "error": {
130957
+ "type": "string"
130958
+ },
130959
+ "code": {
130960
+ "type": "string"
130961
+ }
130962
+ }
130963
+ }
130964
+ }
130965
+ }
130966
+ },
130967
+ "503": {
130968
+ "description": "Service unavailable",
130969
+ "content": {
130970
+ "application/json": {
130971
+ "schema": {
130972
+ "type": "object",
130973
+ "required": [
130974
+ "error"
130975
+ ],
130976
+ "properties": {
130977
+ "error": {
130978
+ "type": "string"
130979
+ },
130980
+ "code": {
130981
+ "type": "string"
130982
+ }
130983
+ }
130984
+ }
130985
+ }
130986
+ }
130987
+ }
130988
+ }
130989
+ }
130990
+ },
130991
+ "/api/v1/google/term-batches/{id}/validate": {
130992
+ "post": {
130993
+ "operationId": "validateGoogleTermBatch",
130994
+ "summary": "Validate a Google Ads exclusion batch (validate_only preflight)",
130995
+ "tags": [
130996
+ "google"
130997
+ ],
130998
+ "description": "Runs Google validate_only on the batch and stores the per-operation result. Sets the batch to VALIDATED. No mutation is applied.",
130999
+ "parameters": [
131000
+ {
131001
+ "schema": {
131002
+ "format": "uuid",
131003
+ "type": "string"
131004
+ },
131005
+ "in": "path",
131006
+ "name": "id",
131007
+ "required": true
131008
+ }
131009
+ ],
131010
+ "security": [
131011
+ {
131012
+ "bearerAuth": []
131013
+ }
131014
+ ],
131015
+ "responses": {
131016
+ "200": {
131017
+ "description": "Successful response",
131018
+ "content": {
131019
+ "application/json": {
131020
+ "schema": {
131021
+ "additionalProperties": false,
131022
+ "type": "object",
131023
+ "required": [
131024
+ "batch_id",
131025
+ "status",
131026
+ "results"
131027
+ ],
131028
+ "properties": {
131029
+ "batch_id": {
131030
+ "type": "string"
131031
+ },
131032
+ "status": {
131033
+ "type": "string"
131034
+ },
131035
+ "results": {
131036
+ "type": "array",
131037
+ "items": {
131038
+ "additionalProperties": false,
131039
+ "type": "object",
131040
+ "required": [
131041
+ "index",
131042
+ "status",
131043
+ "resourceName",
131044
+ "errorMessage"
131045
+ ],
131046
+ "properties": {
131047
+ "index": {
131048
+ "type": "number"
131049
+ },
131050
+ "status": {
131051
+ "anyOf": [
131052
+ {
131053
+ "type": "string",
131054
+ "enum": [
131055
+ "SUCCESS"
131056
+ ]
131057
+ },
131058
+ {
131059
+ "type": "string",
131060
+ "enum": [
131061
+ "FAILURE"
131062
+ ]
131063
+ }
131064
+ ]
131065
+ },
131066
+ "resourceName": {
131067
+ "anyOf": [
131068
+ {
131069
+ "type": "string"
131070
+ },
131071
+ {
131072
+ "type": "null"
131073
+ }
131074
+ ]
131075
+ },
131076
+ "errorMessage": {
131077
+ "anyOf": [
131078
+ {
131079
+ "type": "string"
131080
+ },
131081
+ {
131082
+ "type": "null"
131083
+ }
131084
+ ]
131085
+ }
131086
+ }
131087
+ }
131088
+ }
131089
+ }
131090
+ }
131091
+ }
131092
+ }
131093
+ },
131094
+ "400": {
131095
+ "description": "Invalid request - schema validation failed",
131096
+ "content": {
131097
+ "application/json": {
131098
+ "schema": {
131099
+ "type": "object",
131100
+ "required": [
131101
+ "error"
131102
+ ],
131103
+ "properties": {
131104
+ "error": {
131105
+ "type": "string"
131106
+ },
131107
+ "code": {
131108
+ "type": "string"
131109
+ }
131110
+ }
131111
+ }
131112
+ }
131113
+ }
131114
+ },
131115
+ "401": {
131116
+ "description": "Unauthorized - missing or invalid bearer token",
131117
+ "content": {
131118
+ "application/json": {
131119
+ "schema": {
131120
+ "$ref": "#/components/schemas/ErrorResponse"
131121
+ }
131122
+ }
131123
+ }
131124
+ },
131125
+ "404": {
131126
+ "description": "Resource not found",
131127
+ "content": {
131128
+ "application/json": {
131129
+ "schema": {
131130
+ "type": "object",
131131
+ "required": [
131132
+ "error"
131133
+ ],
131134
+ "properties": {
131135
+ "error": {
131136
+ "type": "string"
131137
+ },
131138
+ "code": {
131139
+ "type": "string"
131140
+ }
131141
+ }
131142
+ }
131143
+ }
131144
+ }
131145
+ },
131146
+ "409": {
131147
+ "description": "Conflict - resource state prevents the operation",
131148
+ "content": {
131149
+ "application/json": {
131150
+ "schema": {
131151
+ "type": "object",
131152
+ "required": [
131153
+ "error"
131154
+ ],
131155
+ "properties": {
131156
+ "error": {
131157
+ "type": "string"
131158
+ },
131159
+ "code": {
131160
+ "type": "string"
131161
+ }
131162
+ }
131163
+ }
131164
+ }
131165
+ }
131166
+ },
131167
+ "500": {
131168
+ "description": "Internal server error",
131169
+ "content": {
131170
+ "application/json": {
131171
+ "schema": {
131172
+ "type": "object",
131173
+ "required": [
131174
+ "error"
131175
+ ],
131176
+ "properties": {
131177
+ "error": {
131178
+ "type": "string"
131179
+ },
131180
+ "code": {
131181
+ "type": "string"
131182
+ }
131183
+ }
131184
+ }
131185
+ }
131186
+ }
131187
+ },
131188
+ "502": {
131189
+ "description": "Bad gateway - upstream provider failed",
131190
+ "content": {
131191
+ "application/json": {
131192
+ "schema": {
131193
+ "type": "object",
131194
+ "required": [
131195
+ "error"
131196
+ ],
131197
+ "properties": {
131198
+ "error": {
131199
+ "type": "string"
131200
+ },
131201
+ "code": {
131202
+ "type": "string"
131203
+ }
131204
+ }
131205
+ }
131206
+ }
131207
+ }
131208
+ },
131209
+ "503": {
131210
+ "description": "Service unavailable",
131211
+ "content": {
131212
+ "application/json": {
131213
+ "schema": {
131214
+ "type": "object",
131215
+ "required": [
131216
+ "error"
131217
+ ],
131218
+ "properties": {
131219
+ "error": {
131220
+ "type": "string"
131221
+ },
131222
+ "code": {
131223
+ "type": "string"
131224
+ }
131225
+ }
131226
+ }
131227
+ }
131228
+ }
131229
+ }
131230
+ }
131231
+ }
131232
+ },
131233
+ "/api/v1/google/term-batches/{id}/approve": {
131234
+ "post": {
131235
+ "operationId": "approveGoogleTermBatch",
131236
+ "summary": "Approve a Google Ads exclusion batch and apply it asynchronously",
131237
+ "tags": [
131238
+ "google"
131239
+ ],
131240
+ "description": "Freezes the batch and enqueues the async apply worker (BULK lane). Idempotency-keyed to dedupe re-submits. Poll GET /term-batches/:id for the terminal status (DONE / PARTIAL / FAILED).",
131241
+ "requestBody": {
131242
+ "required": true,
131243
+ "content": {
131244
+ "application/json": {
131245
+ "schema": {
131246
+ "additionalProperties": false,
131247
+ "type": "object",
131248
+ "properties": {
131249
+ "idempotency_key": {
131250
+ "maxLength": 255,
131251
+ "type": "string"
131252
+ }
131253
+ }
131254
+ }
131255
+ }
131256
+ }
131257
+ },
131258
+ "parameters": [
131259
+ {
131260
+ "schema": {
131261
+ "format": "uuid",
131262
+ "type": "string"
131263
+ },
131264
+ "in": "path",
131265
+ "name": "id",
131266
+ "required": true
131267
+ }
131268
+ ],
131269
+ "security": [
131270
+ {
131271
+ "bearerAuth": []
131272
+ }
131273
+ ],
131274
+ "responses": {
131275
+ "200": {
131276
+ "description": "Successful response",
131277
+ "content": {
131278
+ "application/json": {
131279
+ "schema": {
131280
+ "additionalProperties": false,
131281
+ "type": "object",
131282
+ "required": [
131283
+ "batch_id",
131284
+ "status"
131285
+ ],
131286
+ "properties": {
131287
+ "batch_id": {
131288
+ "type": "string"
131289
+ },
131290
+ "status": {
131291
+ "type": "string"
131292
+ }
131293
+ }
131294
+ }
131295
+ }
131296
+ }
131297
+ },
131298
+ "400": {
131299
+ "description": "Invalid request - schema validation failed",
131300
+ "content": {
131301
+ "application/json": {
131302
+ "schema": {
131303
+ "type": "object",
131304
+ "required": [
131305
+ "error"
131306
+ ],
131307
+ "properties": {
131308
+ "error": {
131309
+ "type": "string"
131310
+ },
131311
+ "code": {
131312
+ "type": "string"
131313
+ }
131314
+ }
131315
+ }
131316
+ }
131317
+ }
131318
+ },
131319
+ "401": {
131320
+ "description": "Unauthorized - missing or invalid bearer token",
131321
+ "content": {
131322
+ "application/json": {
131323
+ "schema": {
131324
+ "$ref": "#/components/schemas/ErrorResponse"
131325
+ }
131326
+ }
131327
+ }
131328
+ },
131329
+ "404": {
131330
+ "description": "Resource not found",
131331
+ "content": {
131332
+ "application/json": {
131333
+ "schema": {
131334
+ "type": "object",
131335
+ "required": [
131336
+ "error"
131337
+ ],
131338
+ "properties": {
131339
+ "error": {
131340
+ "type": "string"
131341
+ },
131342
+ "code": {
131343
+ "type": "string"
131344
+ }
131345
+ }
131346
+ }
131347
+ }
131348
+ }
131349
+ },
131350
+ "409": {
131351
+ "description": "Conflict - resource state prevents the operation",
131352
+ "content": {
131353
+ "application/json": {
131354
+ "schema": {
131355
+ "type": "object",
131356
+ "required": [
131357
+ "error"
131358
+ ],
131359
+ "properties": {
131360
+ "error": {
131361
+ "type": "string"
131362
+ },
131363
+ "code": {
131364
+ "type": "string"
131365
+ }
131366
+ }
131367
+ }
131368
+ }
131369
+ }
131370
+ },
131371
+ "500": {
131372
+ "description": "Internal server error",
131373
+ "content": {
131374
+ "application/json": {
131375
+ "schema": {
131376
+ "type": "object",
131377
+ "required": [
131378
+ "error"
131379
+ ],
131380
+ "properties": {
131381
+ "error": {
131382
+ "type": "string"
131383
+ },
131384
+ "code": {
131385
+ "type": "string"
131386
+ }
131387
+ }
131388
+ }
131389
+ }
131390
+ }
131391
+ },
131392
+ "502": {
131393
+ "description": "Bad gateway - upstream provider failed",
131394
+ "content": {
131395
+ "application/json": {
131396
+ "schema": {
131397
+ "type": "object",
131398
+ "required": [
131399
+ "error"
131400
+ ],
131401
+ "properties": {
131402
+ "error": {
131403
+ "type": "string"
131404
+ },
131405
+ "code": {
131406
+ "type": "string"
131407
+ }
131408
+ }
131409
+ }
131410
+ }
131411
+ }
131412
+ },
131413
+ "503": {
131414
+ "description": "Service unavailable",
131415
+ "content": {
131416
+ "application/json": {
131417
+ "schema": {
131418
+ "type": "object",
131419
+ "required": [
131420
+ "error"
131421
+ ],
131422
+ "properties": {
131423
+ "error": {
131424
+ "type": "string"
131425
+ },
131426
+ "code": {
131427
+ "type": "string"
131428
+ }
131429
+ }
131430
+ }
131431
+ }
131432
+ }
131433
+ }
131434
+ }
131435
+ }
131436
+ },
131437
+ "/api/v1/google/term-batches/{id}": {
131438
+ "get": {
131439
+ "operationId": "getGoogleTermBatch",
131440
+ "summary": "Get a Google Ads exclusion batch with its operations",
131441
+ "tags": [
131442
+ "google"
131443
+ ],
131444
+ "description": "Returns the batch, its status/counters, the stored validate_only result and per-operation results.",
131445
+ "parameters": [
131446
+ {
131447
+ "schema": {
131448
+ "format": "uuid",
131449
+ "type": "string"
131450
+ },
131451
+ "in": "path",
131452
+ "name": "id",
131453
+ "required": true
131454
+ }
131455
+ ],
131456
+ "security": [
131457
+ {
131458
+ "bearerAuth": []
131459
+ }
131460
+ ],
131461
+ "responses": {
131462
+ "200": {
131463
+ "description": "Successful response",
131464
+ "content": {
131465
+ "application/json": {
131466
+ "schema": {
131467
+ "additionalProperties": false,
131468
+ "type": "object",
131469
+ "required": [
131470
+ "id",
131471
+ "customer_id",
131472
+ "login_customer_id",
131473
+ "status",
131474
+ "source",
131475
+ "name",
131476
+ "total_operations",
131477
+ "succeeded_operations",
131478
+ "failed_operations",
131479
+ "validate_result",
131480
+ "created_at",
131481
+ "updated_at",
131482
+ "applied_at",
131483
+ "operations"
131484
+ ],
131485
+ "properties": {
131486
+ "id": {
131487
+ "type": "string"
131488
+ },
131489
+ "customer_id": {
131490
+ "type": "string"
131491
+ },
131492
+ "login_customer_id": {
131493
+ "anyOf": [
131494
+ {
131495
+ "type": "string"
131496
+ },
131497
+ {
131498
+ "type": "null"
131499
+ }
131500
+ ]
131501
+ },
131502
+ "status": {
131503
+ "type": "string"
131504
+ },
131505
+ "source": {
131506
+ "type": "string"
131507
+ },
131508
+ "name": {
131509
+ "anyOf": [
131510
+ {
131511
+ "type": "string"
131512
+ },
131513
+ {
131514
+ "type": "null"
131515
+ }
131516
+ ]
131517
+ },
131518
+ "total_operations": {
131519
+ "type": "number"
131520
+ },
131521
+ "succeeded_operations": {
131522
+ "type": "number"
131523
+ },
131524
+ "failed_operations": {
131525
+ "type": "number"
131526
+ },
131527
+ "validate_result": {
131528
+ "anyOf": [
131529
+ {
131530
+ "type": "array",
131531
+ "items": {
131532
+ "additionalProperties": false,
131533
+ "type": "object",
131534
+ "required": [
131535
+ "index",
131536
+ "status",
131537
+ "resourceName",
131538
+ "errorMessage"
131539
+ ],
131540
+ "properties": {
131541
+ "index": {
131542
+ "type": "number"
131543
+ },
131544
+ "status": {
131545
+ "anyOf": [
131546
+ {
131547
+ "type": "string",
131548
+ "enum": [
131549
+ "SUCCESS"
131550
+ ]
131551
+ },
131552
+ {
131553
+ "type": "string",
131554
+ "enum": [
131555
+ "FAILURE"
131556
+ ]
131557
+ }
131558
+ ]
131559
+ },
131560
+ "resourceName": {
131561
+ "anyOf": [
131562
+ {
131563
+ "type": "string"
131564
+ },
131565
+ {
131566
+ "type": "null"
131567
+ }
131568
+ ]
131569
+ },
131570
+ "errorMessage": {
131571
+ "anyOf": [
131572
+ {
131573
+ "type": "string"
131574
+ },
131575
+ {
131576
+ "type": "null"
131577
+ }
131578
+ ]
131579
+ }
131580
+ }
131581
+ }
131582
+ },
131583
+ {
131584
+ "type": "null"
131585
+ }
131586
+ ]
131587
+ },
131588
+ "created_at": {
131589
+ "type": "string"
131590
+ },
131591
+ "updated_at": {
131592
+ "type": "string"
131593
+ },
131594
+ "applied_at": {
131595
+ "anyOf": [
131596
+ {
131597
+ "type": "string"
131598
+ },
131599
+ {
131600
+ "type": "null"
131601
+ }
131602
+ ]
131603
+ },
131604
+ "operations": {
131605
+ "type": "array",
131606
+ "items": {
131607
+ "additionalProperties": false,
131608
+ "type": "object",
131609
+ "required": [
131610
+ "id",
131611
+ "scope",
131612
+ "search_term_id",
131613
+ "search_term",
131614
+ "keyword_text",
131615
+ "match_type",
131616
+ "campaign_id",
131617
+ "ad_group_id",
131618
+ "status",
131619
+ "google_resource_name",
131620
+ "error_message"
131621
+ ],
131622
+ "properties": {
131623
+ "id": {
131624
+ "type": "string"
131625
+ },
131626
+ "scope": {
131627
+ "type": "string"
131628
+ },
131629
+ "search_term_id": {
131630
+ "anyOf": [
131631
+ {
131632
+ "type": "string"
131633
+ },
131634
+ {
131635
+ "type": "null"
131636
+ }
131637
+ ]
131638
+ },
131639
+ "search_term": {
131640
+ "anyOf": [
131641
+ {
131642
+ "type": "string"
131643
+ },
131644
+ {
131645
+ "type": "null"
131646
+ }
131647
+ ]
131648
+ },
131649
+ "keyword_text": {
131650
+ "type": "string"
131651
+ },
131652
+ "match_type": {
131653
+ "type": "string"
131654
+ },
131655
+ "campaign_id": {
131656
+ "anyOf": [
131657
+ {
131658
+ "type": "string"
131659
+ },
131660
+ {
131661
+ "type": "null"
131662
+ }
131663
+ ]
131664
+ },
131665
+ "ad_group_id": {
131666
+ "anyOf": [
131667
+ {
131668
+ "type": "string"
131669
+ },
131670
+ {
131671
+ "type": "null"
131672
+ }
131673
+ ]
131674
+ },
131675
+ "status": {
131676
+ "type": "string"
131677
+ },
131678
+ "google_resource_name": {
131679
+ "anyOf": [
131680
+ {
131681
+ "type": "string"
131682
+ },
131683
+ {
131684
+ "type": "null"
131685
+ }
131686
+ ]
131687
+ },
131688
+ "error_message": {
131689
+ "anyOf": [
131690
+ {
131691
+ "type": "string"
131692
+ },
131693
+ {
131694
+ "type": "null"
131695
+ }
131696
+ ]
131697
+ }
131698
+ }
131699
+ }
131700
+ }
131701
+ }
131702
+ }
131703
+ }
131704
+ }
131705
+ },
131706
+ "400": {
131707
+ "description": "Invalid request - schema validation failed",
131708
+ "content": {
131709
+ "application/json": {
131710
+ "schema": {
131711
+ "type": "object",
131712
+ "required": [
131713
+ "error"
131714
+ ],
131715
+ "properties": {
131716
+ "error": {
131717
+ "type": "string"
131718
+ },
131719
+ "code": {
131720
+ "type": "string"
131721
+ }
131722
+ }
131723
+ }
131724
+ }
131725
+ }
131726
+ },
131727
+ "401": {
131728
+ "description": "Unauthorized - missing or invalid bearer token",
131729
+ "content": {
131730
+ "application/json": {
131731
+ "schema": {
131732
+ "$ref": "#/components/schemas/ErrorResponse"
131733
+ }
131734
+ }
131735
+ }
131736
+ },
131737
+ "404": {
131738
+ "description": "Resource not found",
131739
+ "content": {
131740
+ "application/json": {
131741
+ "schema": {
131742
+ "type": "object",
131743
+ "required": [
131744
+ "error"
131745
+ ],
131746
+ "properties": {
131747
+ "error": {
131748
+ "type": "string"
131749
+ },
131750
+ "code": {
131751
+ "type": "string"
131752
+ }
131753
+ }
131754
+ }
131755
+ }
131756
+ }
131757
+ },
131758
+ "409": {
131759
+ "description": "Conflict - resource state prevents the operation",
131760
+ "content": {
131761
+ "application/json": {
131762
+ "schema": {
131763
+ "type": "object",
131764
+ "required": [
131765
+ "error"
131766
+ ],
131767
+ "properties": {
131768
+ "error": {
131769
+ "type": "string"
131770
+ },
131771
+ "code": {
131772
+ "type": "string"
131773
+ }
131774
+ }
131775
+ }
131776
+ }
131777
+ }
131778
+ },
131779
+ "500": {
131780
+ "description": "Internal server error",
131781
+ "content": {
131782
+ "application/json": {
131783
+ "schema": {
131784
+ "type": "object",
131785
+ "required": [
131786
+ "error"
131787
+ ],
131788
+ "properties": {
131789
+ "error": {
131790
+ "type": "string"
131791
+ },
131792
+ "code": {
131793
+ "type": "string"
131794
+ }
131795
+ }
131796
+ }
131797
+ }
131798
+ }
131799
+ },
131800
+ "502": {
131801
+ "description": "Bad gateway - upstream provider failed",
131802
+ "content": {
131803
+ "application/json": {
131804
+ "schema": {
131805
+ "type": "object",
131806
+ "required": [
131807
+ "error"
131808
+ ],
131809
+ "properties": {
131810
+ "error": {
131811
+ "type": "string"
131812
+ },
131813
+ "code": {
131814
+ "type": "string"
131815
+ }
131816
+ }
131817
+ }
131818
+ }
131819
+ }
131820
+ },
131821
+ "503": {
131822
+ "description": "Service unavailable",
131823
+ "content": {
131824
+ "application/json": {
131825
+ "schema": {
131826
+ "type": "object",
131827
+ "required": [
131828
+ "error"
131829
+ ],
131830
+ "properties": {
131831
+ "error": {
131832
+ "type": "string"
131833
+ },
131834
+ "code": {
131835
+ "type": "string"
131836
+ }
131837
+ }
131838
+ }
131839
+ }
131840
+ }
131841
+ }
131842
+ }
131843
+ }
131844
+ },
128480
131845
  "/api/v1/hourly-sync": {
128481
131846
  "post": {
128482
131847
  "operationId": "triggerHourlySync",
@@ -136414,7 +139779,7 @@
136414
139779
  "tags": [
136415
139780
  "kb-admin"
136416
139781
  ],
136417
- "description": "Admin-only variant of `GET /kb/articles/:articleKey` that ignores the end-user locale preference and always returns the canonical English version. Intended for the KB admin UI that inspects the pipeline state of a single article. Returns 404 when no published, approved version exists.",
139782
+ "description": "Admin-only pipeline-state inspector for a single KB article, independent of the `GET /kb/articles/:articleKey` public endpoint. Unlike the public route, this returns the article regardless of its status (draft, review_required, blocked, promoted_pending_policy, published, ...) and reports the article-level `status`/`riskLevel` plus its latest version (any policy_state) for the canonical locale. Returns 404 only when no article with this key exists at all.",
136418
139783
  "parameters": [
136419
139784
  {
136420
139785
  "schema": {
@@ -136440,11 +139805,13 @@
136440
139805
  "required": [
136441
139806
  "articleKey",
136442
139807
  "title",
136443
- "summary",
139808
+ "status",
139809
+ "riskLevel",
136444
139810
  "productArea",
136445
139811
  "audience",
136446
139812
  "surface",
136447
- "locale"
139813
+ "canonicalLocale",
139814
+ "latestVersion"
136448
139815
  ],
136449
139816
  "properties": {
136450
139817
  "articleKey": {
@@ -136453,15 +139820,11 @@
136453
139820
  "title": {
136454
139821
  "type": "string"
136455
139822
  },
136456
- "summary": {
136457
- "anyOf": [
136458
- {
136459
- "type": "string"
136460
- },
136461
- {
136462
- "type": "null"
136463
- }
136464
- ]
139823
+ "status": {
139824
+ "type": "string"
139825
+ },
139826
+ "riskLevel": {
139827
+ "type": "string"
136465
139828
  },
136466
139829
  "productArea": {
136467
139830
  "type": "string"
@@ -136472,8 +139835,50 @@
136472
139835
  "surface": {
136473
139836
  "type": "string"
136474
139837
  },
136475
- "locale": {
139838
+ "canonicalLocale": {
136476
139839
  "type": "string"
139840
+ },
139841
+ "latestVersion": {
139842
+ "anyOf": [
139843
+ {
139844
+ "type": "object",
139845
+ "required": [
139846
+ "versionNumber",
139847
+ "locale",
139848
+ "policyState",
139849
+ "confidenceScore",
139850
+ "createdAt"
139851
+ ],
139852
+ "properties": {
139853
+ "versionNumber": {
139854
+ "type": "number"
139855
+ },
139856
+ "locale": {
139857
+ "type": "string"
139858
+ },
139859
+ "policyState": {
139860
+ "type": "string"
139861
+ },
139862
+ "confidenceScore": {
139863
+ "anyOf": [
139864
+ {
139865
+ "type": "number"
139866
+ },
139867
+ {
139868
+ "type": "null"
139869
+ }
139870
+ ]
139871
+ },
139872
+ "createdAt": {
139873
+ "format": "date-time",
139874
+ "type": "string"
139875
+ }
139876
+ }
139877
+ },
139878
+ {
139879
+ "type": "null"
139880
+ }
139881
+ ]
136477
139882
  }
136478
139883
  }
136479
139884
  }
@@ -211712,6 +215117,12 @@
211712
215117
  "enum": [
211713
215118
  "binom_v2"
211714
215119
  ]
215120
+ },
215121
+ {
215122
+ "type": "string",
215123
+ "enum": [
215124
+ "clickflare"
215125
+ ]
211715
215126
  }
211716
215127
  ]
211717
215128
  },
@@ -211926,6 +215337,12 @@
211926
215337
  "enum": [
211927
215338
  "binom_v2"
211928
215339
  ]
215340
+ },
215341
+ {
215342
+ "type": "string",
215343
+ "enum": [
215344
+ "clickflare"
215345
+ ]
211929
215346
  }
211930
215347
  ]
211931
215348
  },
@@ -212265,6 +215682,12 @@
212265
215682
  "enum": [
212266
215683
  "binom_v2"
212267
215684
  ]
215685
+ },
215686
+ {
215687
+ "type": "string",
215688
+ "enum": [
215689
+ "clickflare"
215690
+ ]
212268
215691
  }
212269
215692
  ]
212270
215693
  },