@snokam/mcp-api 2.34.0 → 2.36.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@snokam/mcp-api",
3
- "version": "2.34.0",
3
+ "version": "2.36.0",
4
4
  "description": "MCP server exposing Snokam backend APIs as tools for Claude Code and other MCP clients",
5
5
  "type": "module",
6
6
  "bin": {
@@ -208,6 +208,278 @@
208
208
  ]
209
209
  }
210
210
  },
211
+ "/v1.0/protected/articles/{slugOrId}/draft-status": {
212
+ "get": {
213
+ "tags": [
214
+ "Articles"
215
+ ],
216
+ "summary": "Get article with draft status",
217
+ "description": "Returns both the published article and any pending draft so authors can compare states. Only authors may call this.",
218
+ "operationId": "GetArticleWithDraft",
219
+ "parameters": [
220
+ {
221
+ "name": "slugOrId",
222
+ "in": "path",
223
+ "required": true,
224
+ "schema": {
225
+ "type": "string"
226
+ },
227
+ "x-ms-summary": "The slug or id of the article"
228
+ }
229
+ ],
230
+ "responses": {
231
+ "200": {
232
+ "description": "Snapshot with published + draft sides",
233
+ "content": {
234
+ "application/json": {
235
+ "schema": {
236
+ "$ref": "#/components/schemas/getArticleWithDraftResult"
237
+ }
238
+ }
239
+ },
240
+ "x-ms-summary": "Success"
241
+ },
242
+ "401": {
243
+ "description": "No description",
244
+ "x-ms-summary": "Unauthorized"
245
+ },
246
+ "404": {
247
+ "description": "No description",
248
+ "x-ms-summary": "Not Found"
249
+ }
250
+ },
251
+ "security": [
252
+ {
253
+ "Implicit": [
254
+ "api://e60092d3-3970-4a7c-a62e-19f17c4bca08/.default"
255
+ ]
256
+ }
257
+ ]
258
+ }
259
+ },
260
+ "/v1.0/protected/articles/drafts": {
261
+ "post": {
262
+ "tags": [
263
+ "Articles"
264
+ ],
265
+ "summary": "Create a new article as a draft",
266
+ "description": "Creates a new article in Sanity's `drafts.*` namespace. The article is not visible on the public site until PublishDraft is called.",
267
+ "operationId": "CreateDraft",
268
+ "requestBody": {
269
+ "description": "The article content to save as a draft",
270
+ "content": {
271
+ "application/json": {
272
+ "schema": {
273
+ "$ref": "#/components/schemas/sanityCreateArticle"
274
+ }
275
+ }
276
+ },
277
+ "required": true
278
+ },
279
+ "responses": {
280
+ "200": {
281
+ "description": "The newly created draft",
282
+ "content": {
283
+ "application/json": {
284
+ "schema": {
285
+ "$ref": "#/components/schemas/getArticlesBySlugOrIdResult"
286
+ }
287
+ }
288
+ },
289
+ "x-ms-summary": "Success"
290
+ },
291
+ "400": {
292
+ "description": "Payload of Object",
293
+ "content": {
294
+ "application/json": {
295
+ "schema": {
296
+ "type": "object"
297
+ }
298
+ }
299
+ },
300
+ "x-ms-summary": "Bad Request"
301
+ },
302
+ "401": {
303
+ "description": "No description",
304
+ "x-ms-summary": "Unauthorized"
305
+ }
306
+ },
307
+ "security": [
308
+ {
309
+ "Implicit": [
310
+ "api://e60092d3-3970-4a7c-a62e-19f17c4bca08/.default"
311
+ ]
312
+ }
313
+ ]
314
+ }
315
+ },
316
+ "/v1.0/protected/articles/{slugOrId}/draft": {
317
+ "patch": {
318
+ "tags": [
319
+ "Articles"
320
+ ],
321
+ "summary": "Write an edit to the draft",
322
+ "description": "Patches the draft for the given article. Creates the draft from the published copy first if none exists. Only authors may call this.",
323
+ "operationId": "PatchDraft",
324
+ "parameters": [
325
+ {
326
+ "name": "slugOrId",
327
+ "in": "path",
328
+ "required": true,
329
+ "schema": {
330
+ "type": "string"
331
+ },
332
+ "x-ms-summary": "The slug or id of the article"
333
+ }
334
+ ],
335
+ "requestBody": {
336
+ "description": "Fields to merge into the draft",
337
+ "content": {
338
+ "application/json": {
339
+ "schema": {
340
+ "$ref": "#/components/schemas/sanityPatchArticle"
341
+ }
342
+ }
343
+ },
344
+ "required": true
345
+ },
346
+ "responses": {
347
+ "200": {
348
+ "description": "The updated draft",
349
+ "content": {
350
+ "application/json": {
351
+ "schema": {
352
+ "$ref": "#/components/schemas/getArticlesBySlugOrIdResult"
353
+ }
354
+ }
355
+ },
356
+ "x-ms-summary": "Success"
357
+ },
358
+ "400": {
359
+ "description": "Payload of Object",
360
+ "content": {
361
+ "application/json": {
362
+ "schema": {
363
+ "type": "object"
364
+ }
365
+ }
366
+ },
367
+ "x-ms-summary": "Bad Request"
368
+ },
369
+ "401": {
370
+ "description": "No description",
371
+ "x-ms-summary": "Unauthorized"
372
+ },
373
+ "404": {
374
+ "description": "No description",
375
+ "x-ms-summary": "Not Found"
376
+ }
377
+ },
378
+ "security": [
379
+ {
380
+ "Implicit": [
381
+ "api://e60092d3-3970-4a7c-a62e-19f17c4bca08/.default"
382
+ ]
383
+ }
384
+ ]
385
+ },
386
+ "delete": {
387
+ "tags": [
388
+ "Articles"
389
+ ],
390
+ "summary": "Delete the draft",
391
+ "description": "Removes the draft without touching the published article. Only authors may call this.",
392
+ "operationId": "DiscardDraft",
393
+ "parameters": [
394
+ {
395
+ "name": "slugOrId",
396
+ "in": "path",
397
+ "required": true,
398
+ "schema": {
399
+ "type": "string"
400
+ },
401
+ "x-ms-summary": "The slug or id of the article"
402
+ }
403
+ ],
404
+ "responses": {
405
+ "200": {
406
+ "description": "The discarded draft (pre-delete snapshot)",
407
+ "content": {
408
+ "application/json": {
409
+ "schema": {
410
+ "$ref": "#/components/schemas/getArticlesBySlugOrIdResult"
411
+ }
412
+ }
413
+ },
414
+ "x-ms-summary": "Success"
415
+ },
416
+ "401": {
417
+ "description": "No description",
418
+ "x-ms-summary": "Unauthorized"
419
+ },
420
+ "404": {
421
+ "description": "No draft exists",
422
+ "x-ms-summary": "Not Found"
423
+ }
424
+ },
425
+ "security": [
426
+ {
427
+ "Implicit": [
428
+ "api://e60092d3-3970-4a7c-a62e-19f17c4bca08/.default"
429
+ ]
430
+ }
431
+ ]
432
+ }
433
+ },
434
+ "/v1.0/protected/articles/{slugOrId}/publish": {
435
+ "post": {
436
+ "tags": [
437
+ "Articles"
438
+ ],
439
+ "summary": "Promote the draft to published",
440
+ "description": "Overwrites the published article with the current draft and removes the draft. Only authors may call this.",
441
+ "operationId": "PublishDraft",
442
+ "parameters": [
443
+ {
444
+ "name": "slugOrId",
445
+ "in": "path",
446
+ "required": true,
447
+ "schema": {
448
+ "type": "string"
449
+ },
450
+ "x-ms-summary": "The slug or id of the article"
451
+ }
452
+ ],
453
+ "responses": {
454
+ "200": {
455
+ "description": "The newly-published article",
456
+ "content": {
457
+ "application/json": {
458
+ "schema": {
459
+ "$ref": "#/components/schemas/getArticlesBySlugOrIdResult"
460
+ }
461
+ }
462
+ },
463
+ "x-ms-summary": "Success"
464
+ },
465
+ "401": {
466
+ "description": "No description",
467
+ "x-ms-summary": "Unauthorized"
468
+ },
469
+ "404": {
470
+ "description": "No draft exists",
471
+ "x-ms-summary": "Not Found"
472
+ }
473
+ },
474
+ "security": [
475
+ {
476
+ "Implicit": [
477
+ "api://e60092d3-3970-4a7c-a62e-19f17c4bca08/.default"
478
+ ]
479
+ }
480
+ ]
481
+ }
482
+ },
211
483
  "/v1.0/protected/{slugOrId}/article-invitation-preview": {
212
484
  "get": {
213
485
  "tags": [
@@ -622,6 +894,32 @@
622
894
  }
623
895
  }
624
896
  },
897
+ "getArticleWithDraftResult": {
898
+ "type": "object",
899
+ "properties": {
900
+ "draft": {
901
+ "$ref": "#/components/schemas/getArticlesBySlugOrIdResult"
902
+ },
903
+ "hasDraft": {
904
+ "type": "boolean"
905
+ },
906
+ "hasPublished": {
907
+ "type": "boolean"
908
+ },
909
+ "published": {
910
+ "$ref": "#/components/schemas/getArticlesBySlugOrIdResult"
911
+ },
912
+ "publishedId": {
913
+ "type": "string"
914
+ },
915
+ "additionalProperties": {
916
+ "type": "object",
917
+ "additionalProperties": {
918
+ "type": "object"
919
+ }
920
+ }
921
+ }
922
+ },
625
923
  "muxVideo": {
626
924
  "type": "object",
627
925
  "properties": {
@@ -2449,6 +2449,53 @@
2449
2449
  ]
2450
2450
  }
2451
2451
  },
2452
+ "/v1.0/tenders/{id}/slack-messages": {
2453
+ "post": {
2454
+ "tags": [
2455
+ "Tenders"
2456
+ ],
2457
+ "summary": "Queue a Slack message for a tender (Service Bus → notifications-function).",
2458
+ "operationId": "SendTenderSlackMessage",
2459
+ "parameters": [
2460
+ {
2461
+ "name": "id",
2462
+ "in": "path",
2463
+ "required": true,
2464
+ "schema": {
2465
+ "type": "string"
2466
+ }
2467
+ }
2468
+ ],
2469
+ "requestBody": {
2470
+ "content": {
2471
+ "application/json": {
2472
+ "schema": {
2473
+ "$ref": "#/components/schemas/sendTenderSlackMessageRequest"
2474
+ }
2475
+ }
2476
+ },
2477
+ "required": true
2478
+ },
2479
+ "responses": {
2480
+ "204": {
2481
+ "description": "No description"
2482
+ },
2483
+ "400": {
2484
+ "description": "No description"
2485
+ },
2486
+ "404": {
2487
+ "description": "No description"
2488
+ }
2489
+ },
2490
+ "security": [
2491
+ {
2492
+ "Implicit": [
2493
+ "api://7e877854-e62d-4637-a655-1da7141f7ac9/.default"
2494
+ ]
2495
+ }
2496
+ ]
2497
+ }
2498
+ },
2452
2499
  "/v1.0/tenders/{id}/consultants/{email}": {
2453
2500
  "delete": {
2454
2501
  "tags": [
@@ -3779,6 +3826,19 @@
3779
3826
  }
3780
3827
  }
3781
3828
  },
3829
+ "sendTenderSlackMessageRequest": {
3830
+ "type": "object",
3831
+ "properties": {
3832
+ "channelId": {
3833
+ "type": "string",
3834
+ "nullable": true
3835
+ },
3836
+ "message": {
3837
+ "type": "string",
3838
+ "nullable": true
3839
+ }
3840
+ }
3841
+ },
3782
3842
  "sourceFile": {
3783
3843
  "type": "object",
3784
3844
  "properties": {
@@ -34406,6 +34406,49 @@
34406
34406
  "type": "object",
34407
34407
  "additionalProperties": true
34408
34408
  },
34409
+ "GetArticleWithDraftResult": {
34410
+ "description": "Snapshot combining the published version and any in-progress\ndraft. Used by the editor UI so authors can see both states and\ndecide when to promote.",
34411
+ "properties": {
34412
+ "publishedId": {
34413
+ "type": "string",
34414
+ "nullable": true,
34415
+ "description": "Canonical published id (no `drafts.` prefix). `null` when the\narticle only exists as a draft (freshly created)."
34416
+ },
34417
+ "published": {
34418
+ "allOf": [
34419
+ {
34420
+ "$ref": "#/components/schemas/GetArticlesBySlugOrIdResult"
34421
+ }
34422
+ ],
34423
+ "nullable": true,
34424
+ "description": "Published article, renamed-field form. `null` if never published."
34425
+ },
34426
+ "draft": {
34427
+ "allOf": [
34428
+ {
34429
+ "$ref": "#/components/schemas/GetArticlesBySlugOrIdResult"
34430
+ }
34431
+ ],
34432
+ "nullable": true,
34433
+ "description": "Draft article (from `drafts.<id>`), renamed-field form. `null`\nwhen the article has no pending draft."
34434
+ },
34435
+ "hasPublished": {
34436
+ "type": "boolean"
34437
+ },
34438
+ "hasDraft": {
34439
+ "type": "boolean"
34440
+ }
34441
+ },
34442
+ "required": [
34443
+ "publishedId",
34444
+ "published",
34445
+ "draft",
34446
+ "hasPublished",
34447
+ "hasDraft"
34448
+ ],
34449
+ "type": "object",
34450
+ "additionalProperties": true
34451
+ },
34409
34452
  "GetAppByKeyResult": {
34410
34453
  "properties": {
34411
34454
  "menu": {
@@ -50017,6 +50060,210 @@
50017
50060
  ]
50018
50061
  }
50019
50062
  },
50063
+ "/v1.0/articles/{slugOrId}/draft-status": {
50064
+ "get": {
50065
+ "operationId": "GetArticleWithDraft",
50066
+ "responses": {
50067
+ "200": {
50068
+ "description": "Ok",
50069
+ "content": {
50070
+ "application/json": {
50071
+ "schema": {
50072
+ "$ref": "#/components/schemas/GetArticleWithDraftResult"
50073
+ }
50074
+ }
50075
+ }
50076
+ }
50077
+ },
50078
+ "description": "Read both states at once. Returns all `null`s if neither exists.\nAccepts a published id or a slug; draft lookup is by id, so we\nresolve slug→id via the published GROQ query first. Brand-new\ndrafts (no published counterpart) can be fetched by passing the\nbare `drafts.<id>` form or the plain `<id>` half — the handler\ntries both.",
50079
+ "tags": [
50080
+ "Articles"
50081
+ ],
50082
+ "security": [
50083
+ {
50084
+ "Implicit": [
50085
+ "api://3358ac8e-681e-4aeb-bfbf-bb008913c423/.default"
50086
+ ]
50087
+ }
50088
+ ],
50089
+ "parameters": [
50090
+ {
50091
+ "in": "path",
50092
+ "name": "slugOrId",
50093
+ "required": true,
50094
+ "schema": {
50095
+ "type": "string"
50096
+ }
50097
+ }
50098
+ ]
50099
+ }
50100
+ },
50101
+ "/v1.0/articles/drafts": {
50102
+ "post": {
50103
+ "operationId": "CreateDraft",
50104
+ "responses": {
50105
+ "200": {
50106
+ "description": "Ok",
50107
+ "content": {
50108
+ "application/json": {
50109
+ "schema": {
50110
+ "$ref": "#/components/schemas/GetArticlesBySlugOrIdResult"
50111
+ }
50112
+ }
50113
+ }
50114
+ }
50115
+ },
50116
+ "description": "Create a brand-new article as a draft. Returns the created\ndraft document (its `sanityId` carries the `drafts.` prefix).\nAuthors use this from the \"New article\" flow so nothing goes\nlive until they press publish.",
50117
+ "tags": [
50118
+ "Articles"
50119
+ ],
50120
+ "security": [
50121
+ {
50122
+ "Implicit": [
50123
+ "api://3358ac8e-681e-4aeb-bfbf-bb008913c423/.default"
50124
+ ]
50125
+ }
50126
+ ],
50127
+ "parameters": [],
50128
+ "requestBody": {
50129
+ "required": true,
50130
+ "content": {
50131
+ "application/json": {
50132
+ "schema": {
50133
+ "$ref": "#/components/schemas/SanityCreateArticle"
50134
+ }
50135
+ }
50136
+ }
50137
+ }
50138
+ }
50139
+ },
50140
+ "/v1.0/articles/{slugOrId}/draft": {
50141
+ "patch": {
50142
+ "operationId": "PatchDraft",
50143
+ "responses": {
50144
+ "200": {
50145
+ "description": "Ok",
50146
+ "content": {
50147
+ "application/json": {
50148
+ "schema": {
50149
+ "$ref": "#/components/schemas/GetArticlesBySlugOrIdResult"
50150
+ }
50151
+ }
50152
+ }
50153
+ }
50154
+ },
50155
+ "description": "Write an edit to the draft for `slugOrId`. If no draft exists\nwe clone the published document first, so partial patches\npreserve existing content. `slugOrId` must resolve to a document\nid — callers coming in via slug should go through blog-function\n(which resolves with GetArticleWithDraft before calling this)\nso the authZ check and slug→id resolution happen together.",
50156
+ "tags": [
50157
+ "Articles"
50158
+ ],
50159
+ "security": [
50160
+ {
50161
+ "Implicit": [
50162
+ "api://3358ac8e-681e-4aeb-bfbf-bb008913c423/.default"
50163
+ ]
50164
+ }
50165
+ ],
50166
+ "parameters": [
50167
+ {
50168
+ "in": "path",
50169
+ "name": "slugOrId",
50170
+ "required": true,
50171
+ "schema": {
50172
+ "type": "string"
50173
+ }
50174
+ }
50175
+ ],
50176
+ "requestBody": {
50177
+ "required": true,
50178
+ "content": {
50179
+ "application/json": {
50180
+ "schema": {
50181
+ "$ref": "#/components/schemas/SanityPatchArticle"
50182
+ }
50183
+ }
50184
+ }
50185
+ }
50186
+ },
50187
+ "delete": {
50188
+ "operationId": "DiscardDraft",
50189
+ "responses": {
50190
+ "200": {
50191
+ "description": "Ok",
50192
+ "content": {
50193
+ "application/json": {
50194
+ "schema": {
50195
+ "allOf": [
50196
+ {
50197
+ "$ref": "#/components/schemas/GetArticlesBySlugOrIdResult"
50198
+ }
50199
+ ],
50200
+ "nullable": true
50201
+ }
50202
+ }
50203
+ }
50204
+ }
50205
+ },
50206
+ "description": "Delete the draft for `slugOrId` without touching the published\ndocument. Returns the discarded draft (pre-delete snapshot) or\n`null` if no draft existed.",
50207
+ "tags": [
50208
+ "Articles"
50209
+ ],
50210
+ "security": [
50211
+ {
50212
+ "Implicit": [
50213
+ "api://3358ac8e-681e-4aeb-bfbf-bb008913c423/.default"
50214
+ ]
50215
+ }
50216
+ ],
50217
+ "parameters": [
50218
+ {
50219
+ "in": "path",
50220
+ "name": "slugOrId",
50221
+ "required": true,
50222
+ "schema": {
50223
+ "type": "string"
50224
+ }
50225
+ }
50226
+ ]
50227
+ }
50228
+ },
50229
+ "/v1.0/articles/{slugOrId}/publish": {
50230
+ "post": {
50231
+ "operationId": "PublishDraft",
50232
+ "responses": {
50233
+ "200": {
50234
+ "description": "Ok",
50235
+ "content": {
50236
+ "application/json": {
50237
+ "schema": {
50238
+ "$ref": "#/components/schemas/GetArticlesBySlugOrIdResult"
50239
+ }
50240
+ }
50241
+ }
50242
+ }
50243
+ },
50244
+ "description": "Promote the draft for `slugOrId` to published. Overwrites the\npublished document with the draft's content and deletes the\ndraft. Returns the newly-published document.",
50245
+ "tags": [
50246
+ "Articles"
50247
+ ],
50248
+ "security": [
50249
+ {
50250
+ "Implicit": [
50251
+ "api://3358ac8e-681e-4aeb-bfbf-bb008913c423/.default"
50252
+ ]
50253
+ }
50254
+ ],
50255
+ "parameters": [
50256
+ {
50257
+ "in": "path",
50258
+ "name": "slugOrId",
50259
+ "required": true,
50260
+ "schema": {
50261
+ "type": "string"
50262
+ }
50263
+ }
50264
+ ]
50265
+ }
50266
+ },
50020
50267
  "/v1.0/apps": {
50021
50268
  "get": {
50022
50269
  "operationId": "GetApps",
@@ -208,6 +208,278 @@
208
208
  ]
209
209
  }
210
210
  },
211
+ "/v1.0/protected/articles/{slugOrId}/draft-status": {
212
+ "get": {
213
+ "tags": [
214
+ "Articles"
215
+ ],
216
+ "summary": "Get article with draft status",
217
+ "description": "Returns both the published article and any pending draft so authors can compare states. Only authors may call this.",
218
+ "operationId": "GetArticleWithDraft",
219
+ "parameters": [
220
+ {
221
+ "name": "slugOrId",
222
+ "in": "path",
223
+ "required": true,
224
+ "schema": {
225
+ "type": "string"
226
+ },
227
+ "x-ms-summary": "The slug or id of the article"
228
+ }
229
+ ],
230
+ "responses": {
231
+ "200": {
232
+ "description": "Snapshot with published + draft sides",
233
+ "content": {
234
+ "application/json": {
235
+ "schema": {
236
+ "$ref": "#/components/schemas/getArticleWithDraftResult"
237
+ }
238
+ }
239
+ },
240
+ "x-ms-summary": "Success"
241
+ },
242
+ "401": {
243
+ "description": "No description",
244
+ "x-ms-summary": "Unauthorized"
245
+ },
246
+ "404": {
247
+ "description": "No description",
248
+ "x-ms-summary": "Not Found"
249
+ }
250
+ },
251
+ "security": [
252
+ {
253
+ "Implicit": [
254
+ "api://c375ccd2-e69d-4262-808d-1a197df95d34/.default"
255
+ ]
256
+ }
257
+ ]
258
+ }
259
+ },
260
+ "/v1.0/protected/articles/drafts": {
261
+ "post": {
262
+ "tags": [
263
+ "Articles"
264
+ ],
265
+ "summary": "Create a new article as a draft",
266
+ "description": "Creates a new article in Sanity's `drafts.*` namespace. The article is not visible on the public site until PublishDraft is called.",
267
+ "operationId": "CreateDraft",
268
+ "requestBody": {
269
+ "description": "The article content to save as a draft",
270
+ "content": {
271
+ "application/json": {
272
+ "schema": {
273
+ "$ref": "#/components/schemas/sanityCreateArticle"
274
+ }
275
+ }
276
+ },
277
+ "required": true
278
+ },
279
+ "responses": {
280
+ "200": {
281
+ "description": "The newly created draft",
282
+ "content": {
283
+ "application/json": {
284
+ "schema": {
285
+ "$ref": "#/components/schemas/getArticlesBySlugOrIdResult"
286
+ }
287
+ }
288
+ },
289
+ "x-ms-summary": "Success"
290
+ },
291
+ "400": {
292
+ "description": "Payload of Object",
293
+ "content": {
294
+ "application/json": {
295
+ "schema": {
296
+ "type": "object"
297
+ }
298
+ }
299
+ },
300
+ "x-ms-summary": "Bad Request"
301
+ },
302
+ "401": {
303
+ "description": "No description",
304
+ "x-ms-summary": "Unauthorized"
305
+ }
306
+ },
307
+ "security": [
308
+ {
309
+ "Implicit": [
310
+ "api://c375ccd2-e69d-4262-808d-1a197df95d34/.default"
311
+ ]
312
+ }
313
+ ]
314
+ }
315
+ },
316
+ "/v1.0/protected/articles/{slugOrId}/draft": {
317
+ "patch": {
318
+ "tags": [
319
+ "Articles"
320
+ ],
321
+ "summary": "Write an edit to the draft",
322
+ "description": "Patches the draft for the given article. Creates the draft from the published copy first if none exists. Only authors may call this.",
323
+ "operationId": "PatchDraft",
324
+ "parameters": [
325
+ {
326
+ "name": "slugOrId",
327
+ "in": "path",
328
+ "required": true,
329
+ "schema": {
330
+ "type": "string"
331
+ },
332
+ "x-ms-summary": "The slug or id of the article"
333
+ }
334
+ ],
335
+ "requestBody": {
336
+ "description": "Fields to merge into the draft",
337
+ "content": {
338
+ "application/json": {
339
+ "schema": {
340
+ "$ref": "#/components/schemas/sanityPatchArticle"
341
+ }
342
+ }
343
+ },
344
+ "required": true
345
+ },
346
+ "responses": {
347
+ "200": {
348
+ "description": "The updated draft",
349
+ "content": {
350
+ "application/json": {
351
+ "schema": {
352
+ "$ref": "#/components/schemas/getArticlesBySlugOrIdResult"
353
+ }
354
+ }
355
+ },
356
+ "x-ms-summary": "Success"
357
+ },
358
+ "400": {
359
+ "description": "Payload of Object",
360
+ "content": {
361
+ "application/json": {
362
+ "schema": {
363
+ "type": "object"
364
+ }
365
+ }
366
+ },
367
+ "x-ms-summary": "Bad Request"
368
+ },
369
+ "401": {
370
+ "description": "No description",
371
+ "x-ms-summary": "Unauthorized"
372
+ },
373
+ "404": {
374
+ "description": "No description",
375
+ "x-ms-summary": "Not Found"
376
+ }
377
+ },
378
+ "security": [
379
+ {
380
+ "Implicit": [
381
+ "api://c375ccd2-e69d-4262-808d-1a197df95d34/.default"
382
+ ]
383
+ }
384
+ ]
385
+ },
386
+ "delete": {
387
+ "tags": [
388
+ "Articles"
389
+ ],
390
+ "summary": "Delete the draft",
391
+ "description": "Removes the draft without touching the published article. Only authors may call this.",
392
+ "operationId": "DiscardDraft",
393
+ "parameters": [
394
+ {
395
+ "name": "slugOrId",
396
+ "in": "path",
397
+ "required": true,
398
+ "schema": {
399
+ "type": "string"
400
+ },
401
+ "x-ms-summary": "The slug or id of the article"
402
+ }
403
+ ],
404
+ "responses": {
405
+ "200": {
406
+ "description": "The discarded draft (pre-delete snapshot)",
407
+ "content": {
408
+ "application/json": {
409
+ "schema": {
410
+ "$ref": "#/components/schemas/getArticlesBySlugOrIdResult"
411
+ }
412
+ }
413
+ },
414
+ "x-ms-summary": "Success"
415
+ },
416
+ "401": {
417
+ "description": "No description",
418
+ "x-ms-summary": "Unauthorized"
419
+ },
420
+ "404": {
421
+ "description": "No draft exists",
422
+ "x-ms-summary": "Not Found"
423
+ }
424
+ },
425
+ "security": [
426
+ {
427
+ "Implicit": [
428
+ "api://c375ccd2-e69d-4262-808d-1a197df95d34/.default"
429
+ ]
430
+ }
431
+ ]
432
+ }
433
+ },
434
+ "/v1.0/protected/articles/{slugOrId}/publish": {
435
+ "post": {
436
+ "tags": [
437
+ "Articles"
438
+ ],
439
+ "summary": "Promote the draft to published",
440
+ "description": "Overwrites the published article with the current draft and removes the draft. Only authors may call this.",
441
+ "operationId": "PublishDraft",
442
+ "parameters": [
443
+ {
444
+ "name": "slugOrId",
445
+ "in": "path",
446
+ "required": true,
447
+ "schema": {
448
+ "type": "string"
449
+ },
450
+ "x-ms-summary": "The slug or id of the article"
451
+ }
452
+ ],
453
+ "responses": {
454
+ "200": {
455
+ "description": "The newly-published article",
456
+ "content": {
457
+ "application/json": {
458
+ "schema": {
459
+ "$ref": "#/components/schemas/getArticlesBySlugOrIdResult"
460
+ }
461
+ }
462
+ },
463
+ "x-ms-summary": "Success"
464
+ },
465
+ "401": {
466
+ "description": "No description",
467
+ "x-ms-summary": "Unauthorized"
468
+ },
469
+ "404": {
470
+ "description": "No draft exists",
471
+ "x-ms-summary": "Not Found"
472
+ }
473
+ },
474
+ "security": [
475
+ {
476
+ "Implicit": [
477
+ "api://c375ccd2-e69d-4262-808d-1a197df95d34/.default"
478
+ ]
479
+ }
480
+ ]
481
+ }
482
+ },
211
483
  "/v1.0/protected/{slugOrId}/article-invitation-preview": {
212
484
  "get": {
213
485
  "tags": [
@@ -622,6 +894,32 @@
622
894
  }
623
895
  }
624
896
  },
897
+ "getArticleWithDraftResult": {
898
+ "type": "object",
899
+ "properties": {
900
+ "draft": {
901
+ "$ref": "#/components/schemas/getArticlesBySlugOrIdResult"
902
+ },
903
+ "hasDraft": {
904
+ "type": "boolean"
905
+ },
906
+ "hasPublished": {
907
+ "type": "boolean"
908
+ },
909
+ "published": {
910
+ "$ref": "#/components/schemas/getArticlesBySlugOrIdResult"
911
+ },
912
+ "publishedId": {
913
+ "type": "string"
914
+ },
915
+ "additionalProperties": {
916
+ "type": "object",
917
+ "additionalProperties": {
918
+ "type": "object"
919
+ }
920
+ }
921
+ }
922
+ },
625
923
  "muxVideo": {
626
924
  "type": "object",
627
925
  "properties": {
@@ -2449,6 +2449,53 @@
2449
2449
  ]
2450
2450
  }
2451
2451
  },
2452
+ "/v1.0/tenders/{id}/slack-messages": {
2453
+ "post": {
2454
+ "tags": [
2455
+ "Tenders"
2456
+ ],
2457
+ "summary": "Queue a Slack message for a tender (Service Bus → notifications-function).",
2458
+ "operationId": "SendTenderSlackMessage",
2459
+ "parameters": [
2460
+ {
2461
+ "name": "id",
2462
+ "in": "path",
2463
+ "required": true,
2464
+ "schema": {
2465
+ "type": "string"
2466
+ }
2467
+ }
2468
+ ],
2469
+ "requestBody": {
2470
+ "content": {
2471
+ "application/json": {
2472
+ "schema": {
2473
+ "$ref": "#/components/schemas/sendTenderSlackMessageRequest"
2474
+ }
2475
+ }
2476
+ },
2477
+ "required": true
2478
+ },
2479
+ "responses": {
2480
+ "204": {
2481
+ "description": "No description"
2482
+ },
2483
+ "400": {
2484
+ "description": "No description"
2485
+ },
2486
+ "404": {
2487
+ "description": "No description"
2488
+ }
2489
+ },
2490
+ "security": [
2491
+ {
2492
+ "Implicit": [
2493
+ "api://d7f15838-af74-4048-88b3-503089de0064/.default"
2494
+ ]
2495
+ }
2496
+ ]
2497
+ }
2498
+ },
2452
2499
  "/v1.0/tenders/{id}/consultants/{email}": {
2453
2500
  "delete": {
2454
2501
  "tags": [
@@ -3779,6 +3826,19 @@
3779
3826
  }
3780
3827
  }
3781
3828
  },
3829
+ "sendTenderSlackMessageRequest": {
3830
+ "type": "object",
3831
+ "properties": {
3832
+ "channelId": {
3833
+ "type": "string",
3834
+ "nullable": true
3835
+ },
3836
+ "message": {
3837
+ "type": "string",
3838
+ "nullable": true
3839
+ }
3840
+ }
3841
+ },
3782
3842
  "sourceFile": {
3783
3843
  "type": "object",
3784
3844
  "properties": {
@@ -34406,6 +34406,49 @@
34406
34406
  "type": "object",
34407
34407
  "additionalProperties": true
34408
34408
  },
34409
+ "GetArticleWithDraftResult": {
34410
+ "description": "Snapshot combining the published version and any in-progress\ndraft. Used by the editor UI so authors can see both states and\ndecide when to promote.",
34411
+ "properties": {
34412
+ "publishedId": {
34413
+ "type": "string",
34414
+ "nullable": true,
34415
+ "description": "Canonical published id (no `drafts.` prefix). `null` when the\narticle only exists as a draft (freshly created)."
34416
+ },
34417
+ "published": {
34418
+ "allOf": [
34419
+ {
34420
+ "$ref": "#/components/schemas/GetArticlesBySlugOrIdResult"
34421
+ }
34422
+ ],
34423
+ "nullable": true,
34424
+ "description": "Published article, renamed-field form. `null` if never published."
34425
+ },
34426
+ "draft": {
34427
+ "allOf": [
34428
+ {
34429
+ "$ref": "#/components/schemas/GetArticlesBySlugOrIdResult"
34430
+ }
34431
+ ],
34432
+ "nullable": true,
34433
+ "description": "Draft article (from `drafts.<id>`), renamed-field form. `null`\nwhen the article has no pending draft."
34434
+ },
34435
+ "hasPublished": {
34436
+ "type": "boolean"
34437
+ },
34438
+ "hasDraft": {
34439
+ "type": "boolean"
34440
+ }
34441
+ },
34442
+ "required": [
34443
+ "publishedId",
34444
+ "published",
34445
+ "draft",
34446
+ "hasPublished",
34447
+ "hasDraft"
34448
+ ],
34449
+ "type": "object",
34450
+ "additionalProperties": true
34451
+ },
34409
34452
  "GetAppByKeyResult": {
34410
34453
  "properties": {
34411
34454
  "menu": {
@@ -50017,6 +50060,210 @@
50017
50060
  ]
50018
50061
  }
50019
50062
  },
50063
+ "/v1.0/articles/{slugOrId}/draft-status": {
50064
+ "get": {
50065
+ "operationId": "GetArticleWithDraft",
50066
+ "responses": {
50067
+ "200": {
50068
+ "description": "Ok",
50069
+ "content": {
50070
+ "application/json": {
50071
+ "schema": {
50072
+ "$ref": "#/components/schemas/GetArticleWithDraftResult"
50073
+ }
50074
+ }
50075
+ }
50076
+ }
50077
+ },
50078
+ "description": "Read both states at once. Returns all `null`s if neither exists.\nAccepts a published id or a slug; draft lookup is by id, so we\nresolve slug→id via the published GROQ query first. Brand-new\ndrafts (no published counterpart) can be fetched by passing the\nbare `drafts.<id>` form or the plain `<id>` half — the handler\ntries both.",
50079
+ "tags": [
50080
+ "Articles"
50081
+ ],
50082
+ "security": [
50083
+ {
50084
+ "Implicit": [
50085
+ "api://5dc55e32-6742-4fe9-b8b6-581cf56af144/.default"
50086
+ ]
50087
+ }
50088
+ ],
50089
+ "parameters": [
50090
+ {
50091
+ "in": "path",
50092
+ "name": "slugOrId",
50093
+ "required": true,
50094
+ "schema": {
50095
+ "type": "string"
50096
+ }
50097
+ }
50098
+ ]
50099
+ }
50100
+ },
50101
+ "/v1.0/articles/drafts": {
50102
+ "post": {
50103
+ "operationId": "CreateDraft",
50104
+ "responses": {
50105
+ "200": {
50106
+ "description": "Ok",
50107
+ "content": {
50108
+ "application/json": {
50109
+ "schema": {
50110
+ "$ref": "#/components/schemas/GetArticlesBySlugOrIdResult"
50111
+ }
50112
+ }
50113
+ }
50114
+ }
50115
+ },
50116
+ "description": "Create a brand-new article as a draft. Returns the created\ndraft document (its `sanityId` carries the `drafts.` prefix).\nAuthors use this from the \"New article\" flow so nothing goes\nlive until they press publish.",
50117
+ "tags": [
50118
+ "Articles"
50119
+ ],
50120
+ "security": [
50121
+ {
50122
+ "Implicit": [
50123
+ "api://5dc55e32-6742-4fe9-b8b6-581cf56af144/.default"
50124
+ ]
50125
+ }
50126
+ ],
50127
+ "parameters": [],
50128
+ "requestBody": {
50129
+ "required": true,
50130
+ "content": {
50131
+ "application/json": {
50132
+ "schema": {
50133
+ "$ref": "#/components/schemas/SanityCreateArticle"
50134
+ }
50135
+ }
50136
+ }
50137
+ }
50138
+ }
50139
+ },
50140
+ "/v1.0/articles/{slugOrId}/draft": {
50141
+ "patch": {
50142
+ "operationId": "PatchDraft",
50143
+ "responses": {
50144
+ "200": {
50145
+ "description": "Ok",
50146
+ "content": {
50147
+ "application/json": {
50148
+ "schema": {
50149
+ "$ref": "#/components/schemas/GetArticlesBySlugOrIdResult"
50150
+ }
50151
+ }
50152
+ }
50153
+ }
50154
+ },
50155
+ "description": "Write an edit to the draft for `slugOrId`. If no draft exists\nwe clone the published document first, so partial patches\npreserve existing content. `slugOrId` must resolve to a document\nid — callers coming in via slug should go through blog-function\n(which resolves with GetArticleWithDraft before calling this)\nso the authZ check and slug→id resolution happen together.",
50156
+ "tags": [
50157
+ "Articles"
50158
+ ],
50159
+ "security": [
50160
+ {
50161
+ "Implicit": [
50162
+ "api://5dc55e32-6742-4fe9-b8b6-581cf56af144/.default"
50163
+ ]
50164
+ }
50165
+ ],
50166
+ "parameters": [
50167
+ {
50168
+ "in": "path",
50169
+ "name": "slugOrId",
50170
+ "required": true,
50171
+ "schema": {
50172
+ "type": "string"
50173
+ }
50174
+ }
50175
+ ],
50176
+ "requestBody": {
50177
+ "required": true,
50178
+ "content": {
50179
+ "application/json": {
50180
+ "schema": {
50181
+ "$ref": "#/components/schemas/SanityPatchArticle"
50182
+ }
50183
+ }
50184
+ }
50185
+ }
50186
+ },
50187
+ "delete": {
50188
+ "operationId": "DiscardDraft",
50189
+ "responses": {
50190
+ "200": {
50191
+ "description": "Ok",
50192
+ "content": {
50193
+ "application/json": {
50194
+ "schema": {
50195
+ "allOf": [
50196
+ {
50197
+ "$ref": "#/components/schemas/GetArticlesBySlugOrIdResult"
50198
+ }
50199
+ ],
50200
+ "nullable": true
50201
+ }
50202
+ }
50203
+ }
50204
+ }
50205
+ },
50206
+ "description": "Delete the draft for `slugOrId` without touching the published\ndocument. Returns the discarded draft (pre-delete snapshot) or\n`null` if no draft existed.",
50207
+ "tags": [
50208
+ "Articles"
50209
+ ],
50210
+ "security": [
50211
+ {
50212
+ "Implicit": [
50213
+ "api://5dc55e32-6742-4fe9-b8b6-581cf56af144/.default"
50214
+ ]
50215
+ }
50216
+ ],
50217
+ "parameters": [
50218
+ {
50219
+ "in": "path",
50220
+ "name": "slugOrId",
50221
+ "required": true,
50222
+ "schema": {
50223
+ "type": "string"
50224
+ }
50225
+ }
50226
+ ]
50227
+ }
50228
+ },
50229
+ "/v1.0/articles/{slugOrId}/publish": {
50230
+ "post": {
50231
+ "operationId": "PublishDraft",
50232
+ "responses": {
50233
+ "200": {
50234
+ "description": "Ok",
50235
+ "content": {
50236
+ "application/json": {
50237
+ "schema": {
50238
+ "$ref": "#/components/schemas/GetArticlesBySlugOrIdResult"
50239
+ }
50240
+ }
50241
+ }
50242
+ }
50243
+ },
50244
+ "description": "Promote the draft for `slugOrId` to published. Overwrites the\npublished document with the draft's content and deletes the\ndraft. Returns the newly-published document.",
50245
+ "tags": [
50246
+ "Articles"
50247
+ ],
50248
+ "security": [
50249
+ {
50250
+ "Implicit": [
50251
+ "api://5dc55e32-6742-4fe9-b8b6-581cf56af144/.default"
50252
+ ]
50253
+ }
50254
+ ],
50255
+ "parameters": [
50256
+ {
50257
+ "in": "path",
50258
+ "name": "slugOrId",
50259
+ "required": true,
50260
+ "schema": {
50261
+ "type": "string"
50262
+ }
50263
+ }
50264
+ ]
50265
+ }
50266
+ },
50020
50267
  "/v1.0/apps": {
50021
50268
  "get": {
50022
50269
  "operationId": "GetApps",