@convertrilo/sdk 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/openapi.yaml ADDED
@@ -0,0 +1,1132 @@
1
+ openapi: 3.0.3
2
+ info:
3
+ title: Convertrilo Encode API
4
+ version: "1.0.0"
5
+ description: |
6
+ JWT-protected API for creating and managing encode jobs. Supports browser-upload, direct URL ingest, or S3 source.
7
+ Public endpoints are limited and rate-limited.
8
+ servers:
9
+ - url: http://localhost:3001
10
+ description: Local backend
11
+ - url: https://api.example.com
12
+ description: Production (replace with your deployed base URL)
13
+ components:
14
+ securitySchemes:
15
+ BearerAuth:
16
+ type: http
17
+ scheme: bearer
18
+ bearerFormat: JWT
19
+ ApiKeyAuth:
20
+ type: apiKey
21
+ in: header
22
+ name: X-API-Key
23
+ schemas:
24
+ AuthLoginRequest:
25
+ type: object
26
+ required: [email, password]
27
+ properties:
28
+ email: { type: string, format: email }
29
+ password: { type: string, minLength: 8 }
30
+ AuthLoginResponse:
31
+ type: object
32
+ properties:
33
+ accessToken: { type: string }
34
+ refreshToken: { type: string }
35
+ TokenBalanceResponse:
36
+ type: object
37
+ properties:
38
+ available: { type: number }
39
+ reserved: { type: number }
40
+ trialGrantRemaining: { type: number }
41
+ trialExpiresAt: { type: string, format: date-time, nullable: true }
42
+ TokenReserveRequest:
43
+ type: object
44
+ required: [jobId, amount]
45
+ properties:
46
+ jobId: { type: string, format: uuid }
47
+ amount: { type: integer, minimum: 1 }
48
+ TokenDeductRequest:
49
+ type: object
50
+ required: [jobId, amount]
51
+ properties:
52
+ jobId: { type: string, format: uuid }
53
+ amount: { type: integer, minimum: 1 }
54
+ JobCreateRequest:
55
+ type: object
56
+ required: [codec, resolution, fps]
57
+ properties:
58
+ codec:
59
+ type: string
60
+ enum: [h264, h265, av1]
61
+ resolution:
62
+ type: string
63
+ enum: [480p, 720p, 1080p, 1440p, 2160p, 4320p]
64
+ fps:
65
+ type: integer
66
+ minimum: 1
67
+ maximum: 240
68
+ container:
69
+ type: string
70
+ enum: [mp4, mkv, webm, mov]
71
+ default: mp4
72
+ preset:
73
+ type: string
74
+ enum: [fast, standard, slow]
75
+ default: fast
76
+ bitrateTier:
77
+ type: string
78
+ enum: [low, medium, high]
79
+ default: medium
80
+ passes:
81
+ type: integer
82
+ enum: [1, 2]
83
+ default: 1
84
+ policy:
85
+ type: string
86
+ enum: [fastest, gpu_first, gpu_only]
87
+ default: gpu_first
88
+ quality:
89
+ type: string
90
+ enum: [good, better, best]
91
+ sourceUrl:
92
+ type: string
93
+ format: uri
94
+ description: Optional direct HTTP(S) ingest. When provided, upload URL is not returned.
95
+ sourceS3:
96
+ type: object
97
+ properties:
98
+ bucket: { type: string }
99
+ key: { type: string }
100
+ endpoint: { type: string }
101
+ region: { type: string }
102
+ accessKeyId: { type: string }
103
+ secretAccessKey: { type: string }
104
+ forcePathStyle: { type: boolean }
105
+ confirm: { type: boolean, default: false }
106
+ optimize: { type: string, enum: [none, vmaf] }
107
+ vmafTarget: { type: number, minimum: 50, maximum: 99 }
108
+ JobCreateResponse:
109
+ type: object
110
+ required: [jobId, upload, output]
111
+ properties:
112
+ jobId: { type: string }
113
+ upload:
114
+ nullable: true
115
+ type: object
116
+ properties:
117
+ url: { type: string }
118
+ key: { type: string }
119
+ output:
120
+ type: object
121
+ properties:
122
+ key: { type: string }
123
+ publicUrl: { type: string }
124
+ estimate:
125
+ type: object
126
+ properties:
127
+ neu: { type: number }
128
+ ProbeDurationResponse:
129
+ type: object
130
+ properties:
131
+ durationSec: { type: number }
132
+ durationMinutes: { type: number }
133
+ ApproveUnifiedRequest:
134
+ type: object
135
+ properties:
136
+ codec: { type: string, enum: [h264, h265, av1] }
137
+ resolution: { type: string }
138
+ fps: { type: integer }
139
+ preset: { type: string, enum: [fast, standard, slow] }
140
+ bitrateTier: { type: string, enum: [low, medium, high] }
141
+ passes: { type: integer, enum: [1, 2] }
142
+ policy: { type: string, enum: [gpu_only, gpu_first, fastest] }
143
+ container: { type: string, enum: [mp4, mkv, webm, mov] }
144
+ quality: { type: string, enum: [good, better, best] }
145
+ optimize: { type: string, enum: [none, vmaf] }
146
+ vmafTarget: { type: number, minimum: 50, maximum: 99 }
147
+ ApproveUnifiedResponse:
148
+ type: object
149
+ properties:
150
+ ok: { type: boolean }
151
+ estimate:
152
+ type: object
153
+ properties:
154
+ perMinuteNeu: { type: number }
155
+ durationSec: { type: number }
156
+ totalNeu: { type: number }
157
+ reserved: { type: number }
158
+ ConfirmRequest:
159
+ type: object
160
+ properties:
161
+ container: { type: string, enum: [mp4, mkv, webm, mov] }
162
+ quality: { type: string, enum: [good, better, best] }
163
+ optimize: { type: string, enum: [none, vmaf] }
164
+ vmafTarget: { type: number, minimum: 1, maximum: 100 }
165
+ ConfirmResponse:
166
+ type: object
167
+ properties:
168
+ ok: { type: boolean }
169
+ StatusResponse:
170
+ type: object
171
+ properties:
172
+ id: { type: string }
173
+ status: { type: string }
174
+ downloadUrl: { type: string, nullable: true }
175
+ encoder: { type: string, nullable: true }
176
+ pct: { type: number, nullable: true }
177
+ ErrorResponse:
178
+ type: object
179
+ properties:
180
+ message: { type: string }
181
+
182
+ # Bulk Jobs
183
+ BulkCreateRequest:
184
+ type: object
185
+ required: [jobs]
186
+ properties:
187
+ jobs:
188
+ type: array
189
+ items:
190
+ $ref: "#/components/schemas/JobCreateRequest"
191
+ settings:
192
+ type: object
193
+ required: []
194
+ properties:
195
+ failFast: { type: boolean, default: false }
196
+ dryRun: { type: boolean, default: false }
197
+ confirm: { type: boolean, default: false }
198
+ container: { type: string, enum: [mp4, mkv, webm, mov] }
199
+ quality: { type: string, enum: [good, better, best] }
200
+ optimize: { type: string, enum: [none, vmaf] }
201
+ vmafTarget: { type: number, minimum: 50, maximum: 99 }
202
+ BulkCreateResponse:
203
+ type: object
204
+ properties:
205
+ totalJobs: { type: integer }
206
+ totalEstimatedNeu: { type: number }
207
+ jobs:
208
+ type: array
209
+ items:
210
+ type: object
211
+ properties:
212
+ index: { type: integer }
213
+ jobId: { type: string, format: uuid }
214
+ status: { type: string }
215
+ error: { type: string }
216
+ BulkStatusResponse:
217
+ type: object
218
+ properties:
219
+ jobs:
220
+ type: array
221
+ items:
222
+ $ref: "#/components/schemas/StatusResponse"
223
+ missing:
224
+ type: array
225
+ items: { type: string }
226
+ BulkConfirmRequest:
227
+ type: object
228
+ required: [jobIds]
229
+ properties:
230
+ jobIds:
231
+ type: array
232
+ items: { type: string, format: uuid }
233
+ settings:
234
+ type: object
235
+ properties:
236
+ container: { type: string, enum: [mp4, mkv, webm, mov] }
237
+ quality: { type: string, enum: [good, better, best] }
238
+
239
+ # API Keys
240
+ ApiKeyCreateRequest:
241
+ type: object
242
+ required: [name]
243
+ properties:
244
+ name: { type: string }
245
+ scopes:
246
+ type: array
247
+ items: { type: string }
248
+ expiresInDays: { type: integer, minimum: 1, maximum: 365 }
249
+ ApiKeyResponse:
250
+ type: object
251
+ properties:
252
+ id: { type: string, format: uuid }
253
+ name: { type: string }
254
+ key: { type: string, description: "Raw key, only returned on creation" }
255
+ prefix: { type: string }
256
+ scopes:
257
+ type: array
258
+ items: { type: string }
259
+ expiresAt: { type: string, format: date-time, nullable: true }
260
+ createdAt: { type: string, format: date-time }
261
+ ApiKeyListResponse:
262
+ type: object
263
+ properties:
264
+ keys:
265
+ type: array
266
+ items:
267
+ $ref: "#/components/schemas/ApiKeyResponse"
268
+
269
+ # Webhooks
270
+ WebhookCreateRequest:
271
+ type: object
272
+ required: [url, events]
273
+ properties:
274
+ url: { type: string, format: uri }
275
+ events:
276
+ type: array
277
+ items:
278
+ type: string
279
+ enum:
280
+ [
281
+ job.created,
282
+ job.queued,
283
+ job.running,
284
+ job.completed,
285
+ job.failed,
286
+ job.canceled,
287
+ ]
288
+ WebhookResponse:
289
+ type: object
290
+ properties:
291
+ id: { type: string, format: uuid }
292
+ url: { type: string }
293
+ events:
294
+ type: array
295
+ items: { type: string }
296
+ secret:
297
+ {
298
+ type: string,
299
+ description: "HMAC secret, only returned on creation",
300
+ }
301
+ isActive: { type: boolean }
302
+ createdAt: { type: string, format: date-time }
303
+ WebhookListResponse:
304
+ type: object
305
+ properties:
306
+ webhooks:
307
+ type: array
308
+ items:
309
+ $ref: "#/components/schemas/WebhookResponse"
310
+
311
+ # On-Demand Encoding
312
+ S3Output:
313
+ type: object
314
+ required: [bucket, key]
315
+ properties:
316
+ bucket: { type: string }
317
+ key: { type: string }
318
+ endpoint: { type: string, format: uri }
319
+ region: { type: string }
320
+ accessKeyId: { type: string }
321
+ secretAccessKey: { type: string, format: password }
322
+ forcePathStyle: { type: boolean }
323
+ S3FolderSource:
324
+ type: object
325
+ required: [bucket]
326
+ properties:
327
+ bucket: { type: string }
328
+ prefix: { type: string, default: "" }
329
+ endpoint: { type: string, format: uri }
330
+ region: { type: string }
331
+ accessKeyId: { type: string }
332
+ secretAccessKey: { type: string, format: password }
333
+ forcePathStyle: { type: boolean, default: true }
334
+ S3FolderOutput:
335
+ type: object
336
+ required: [bucket]
337
+ properties:
338
+ bucket: { type: string }
339
+ prefix: { type: string, default: outputs/ }
340
+ endpoint: { type: string, format: uri }
341
+ region: { type: string }
342
+ accessKeyId: { type: string }
343
+ secretAccessKey: { type: string, format: password }
344
+ forcePathStyle: { type: boolean, default: true }
345
+ GoogleDriveFolderSource:
346
+ type: object
347
+ required: [folderId]
348
+ properties:
349
+ folderId: { type: string }
350
+ accessToken:
351
+ type: string
352
+ description: Short-lived Google OAuth access token supplied by the API caller.
353
+ refreshToken:
354
+ type: string
355
+ description: Optional Google refresh token supplied by the API caller for long-running jobs.
356
+ GoogleDriveOutput:
357
+ type: object
358
+ required: [folderId]
359
+ properties:
360
+ folderId: { type: string }
361
+ fileName: { type: string }
362
+ accessToken:
363
+ type: string
364
+ description: Short-lived Google OAuth access token supplied by the API caller.
365
+ refreshToken:
366
+ type: string
367
+ description: Optional Google refresh token supplied by the API caller for long-running jobs.
368
+ OnDemandEncodeRequest:
369
+ type: object
370
+ required: [sourceUrl]
371
+ properties:
372
+ sourceUrl: { type: string, format: uri }
373
+ codec: { type: string, enum: [h264, h265, av1], default: h264 }
374
+ resolution:
375
+ {
376
+ type: string,
377
+ enum: [480p, 720p, 1080p, 1440p, 2160p],
378
+ default: 1080p,
379
+ }
380
+ fps: { type: integer, minimum: 1, maximum: 120, default: 30 }
381
+ preset: { type: string, enum: [fast, standard, slow], default: fast }
382
+ bitrateTier:
383
+ { type: string, enum: [low, medium, high], default: medium }
384
+ container: { type: string, enum: [mp4, mkv, webm, mov], default: mp4 }
385
+ quality: { type: string, enum: [good, better, best], default: better }
386
+ priority: { type: string, enum: [normal, high], default: normal }
387
+ outputExpiry:
388
+ { type: integer, minimum: 3600, maximum: 604800, default: 86400 }
389
+ webhook: { type: string, format: uri }
390
+ outputS3:
391
+ $ref: "#/components/schemas/S3Output"
392
+ outputGoogleDrive:
393
+ $ref: "#/components/schemas/GoogleDriveOutput"
394
+ outputDropbox:
395
+ deprecated: true
396
+ description: Deprecated. Requests using Dropbox output return 410 Gone.
397
+ type: object
398
+ properties:
399
+ path: { type: string }
400
+ accessToken: { type: string }
401
+ OnDemandEncodeResponse:
402
+ type: object
403
+ required: [jobId, status, statusUrl]
404
+ properties:
405
+ jobId: { type: string, format: uuid }
406
+ status: { type: string }
407
+ estimatedDuration: { type: integer }
408
+ pricing:
409
+ type: object
410
+ properties:
411
+ basePerMinute: { type: number }
412
+ onDemandPerMinute: { type: number }
413
+ multiplier: { type: number }
414
+ totalNeu: { type: number }
415
+ reserved: { type: number }
416
+ statusUrl: { type: string }
417
+ webhook: { type: string, nullable: true }
418
+ OnDemandStatusResponse:
419
+ type: object
420
+ properties:
421
+ jobId: { type: string, format: uuid }
422
+ status: { type: string }
423
+ progress: { type: number, nullable: true }
424
+ encoder: { type: string, nullable: true }
425
+ downloadUrl: { type: string, nullable: true }
426
+ expiresIn: { type: integer, nullable: true }
427
+ destination:
428
+ oneOf:
429
+ - type: object
430
+ properties:
431
+ type: { type: string, enum: [cdn] }
432
+ - type: object
433
+ properties:
434
+ type: { type: string, enum: [s3] }
435
+ bucket: { type: string }
436
+ key: { type: string }
437
+ - type: object
438
+ properties:
439
+ type: { type: string, enum: [google-drive] }
440
+ folderId: { type: string }
441
+ fileName: { type: string, nullable: true }
442
+ nullable: true
443
+ createdAt: { type: string, format: date-time }
444
+ startedAt: { type: string, format: date-time, nullable: true }
445
+ finishedAt: { type: string, format: date-time, nullable: true }
446
+ failureMessage: { type: string, nullable: true }
447
+ OnDemandFolderIngestRequest:
448
+ type: object
449
+ properties:
450
+ codec: { type: string, enum: [h264, h265, av1], default: h264 }
451
+ resolution:
452
+ {
453
+ type: string,
454
+ enum: [480p, 720p, 1080p, 1440p, 2160p, 4320p],
455
+ default: 1080p,
456
+ }
457
+ fps: { type: integer, minimum: 1, maximum: 120, default: 30 }
458
+ preset: { type: string, enum: [fast, standard, slow], default: fast }
459
+ bitrateTier:
460
+ { type: string, enum: [low, medium, high], default: medium }
461
+ passes: { type: integer, minimum: 1, maximum: 2, default: 1 }
462
+ optimize: { type: string, enum: [none, vmaf], default: none }
463
+ vmafTarget: { type: integer, minimum: 50, maximum: 99 }
464
+ container: { type: string, enum: [mp4, mkv, webm, mov], default: mp4 }
465
+ quality: { type: string, enum: [good, better, best], default: better }
466
+ priority: { type: string, enum: [normal, high], default: normal }
467
+ sourceS3:
468
+ $ref: "#/components/schemas/S3FolderSource"
469
+ sourceGoogleDrive:
470
+ $ref: "#/components/schemas/GoogleDriveFolderSource"
471
+ sourceDropbox:
472
+ deprecated: true
473
+ description: Deprecated. Requests using Dropbox ingest return 410 Gone.
474
+ type: object
475
+ properties:
476
+ path: { type: string }
477
+ accessToken: { type: string }
478
+ outputDestination:
479
+ type: string
480
+ enum: [cdn, s3, google-drive]
481
+ default: cdn
482
+ outputS3:
483
+ $ref: "#/components/schemas/S3FolderOutput"
484
+ outputGoogleDrive:
485
+ $ref: "#/components/schemas/GoogleDriveOutput"
486
+ outputDropbox:
487
+ deprecated: true
488
+ description: Deprecated. Requests using Dropbox output return 410 Gone.
489
+ type: object
490
+ properties:
491
+ path: { type: string }
492
+ OnDemandFolderIngestResponse:
493
+ type: object
494
+ properties:
495
+ message: { type: string }
496
+ jobs:
497
+ type: array
498
+ items:
499
+ type: object
500
+ properties:
501
+ jobId: { type: string, format: uuid }
502
+ fileName: { type: string }
503
+
504
+ # Streaming
505
+ StreamInitResponse:
506
+ type: object
507
+ properties:
508
+ ok: { type: boolean }
509
+ uploadId: { type: string }
510
+ inputKey: { type: string }
511
+ StreamFinalizeRequest:
512
+ type: object
513
+ properties:
514
+ parts:
515
+ type: array
516
+ items:
517
+ type: object
518
+ properties:
519
+ PartNumber: { type: integer }
520
+ ETag: { type: string }
521
+ security:
522
+ - BearerAuth: []
523
+ paths:
524
+ /auth/login:
525
+ post:
526
+ summary: Login and receive JWTs
527
+ requestBody:
528
+ required: true
529
+ content:
530
+ application/json:
531
+ schema:
532
+ $ref: "#/components/schemas/AuthLoginRequest"
533
+ responses:
534
+ "200":
535
+ description: Tokens issued
536
+ content:
537
+ application/json:
538
+ schema:
539
+ $ref: "#/components/schemas/AuthLoginResponse"
540
+ /tokens/balance:
541
+ get:
542
+ security: [{ BearerAuth: [] }, { ApiKeyAuth: [] }]
543
+ summary: Get token balance
544
+ responses:
545
+ "200":
546
+ description: Balance
547
+ content:
548
+ application/json:
549
+ schema:
550
+ $ref: "#/components/schemas/TokenBalanceResponse"
551
+ /tokens/reserve:
552
+ post:
553
+ security: [{ BearerAuth: [] }, { ApiKeyAuth: [] }]
554
+ summary: Reserve tokens for a job
555
+ requestBody:
556
+ required: true
557
+ content:
558
+ application/json:
559
+ schema:
560
+ $ref: "#/components/schemas/TokenReserveRequest"
561
+ responses:
562
+ "200": { description: Reserved or insufficient }
563
+ /tokens/release:
564
+ post:
565
+ security: [{ BearerAuth: [] }, { ApiKeyAuth: [] }]
566
+ summary: Release reserved tokens for a job
567
+ requestBody:
568
+ required: true
569
+ content:
570
+ application/json:
571
+ schema:
572
+ $ref: "#/components/schemas/TokenReserveRequest"
573
+ responses:
574
+ "200": { description: Released }
575
+ /tokens/deduct:
576
+ post:
577
+ security: [{ BearerAuth: [] }, { ApiKeyAuth: [] }]
578
+ summary: Deduct tokens after a job
579
+ requestBody:
580
+ required: true
581
+ content:
582
+ application/json:
583
+ schema:
584
+ $ref: "#/components/schemas/TokenDeductRequest"
585
+ responses:
586
+ "200": { description: Deducted }
587
+ /jobs:
588
+ post:
589
+ security: [{ BearerAuth: [] }, { ApiKeyAuth: [] }]
590
+ summary: Create a new encode job
591
+ description: Create a job for upload ingest (returns a presigned PUT) or direct URL/S3 ingest.
592
+ requestBody:
593
+ required: true
594
+ content:
595
+ application/json:
596
+ schema:
597
+ $ref: "#/components/schemas/JobCreateRequest"
598
+ responses:
599
+ "200":
600
+ description: Created
601
+ content:
602
+ application/json:
603
+ schema:
604
+ $ref: "#/components/schemas/JobCreateResponse"
605
+ /jobs/{id}/probe-duration:
606
+ get:
607
+ security: [{ BearerAuth: [] }, { ApiKeyAuth: [] }]
608
+ summary: Probe input duration
609
+ parameters:
610
+ - in: path
611
+ name: id
612
+ required: true
613
+ schema: { type: string }
614
+ responses:
615
+ "200":
616
+ description: Duration probed
617
+ content:
618
+ application/json:
619
+ schema:
620
+ $ref: "#/components/schemas/ProbeDurationResponse"
621
+ /jobs/{id}/approve-unified:
622
+ post:
623
+ security: [{ BearerAuth: [] }, { ApiKeyAuth: [] }]
624
+ summary: Approve, probe, reserve, and return estimate (does not enqueue)
625
+ parameters:
626
+ - in: path
627
+ name: id
628
+ required: true
629
+ schema: { type: string }
630
+ requestBody:
631
+ required: false
632
+ content:
633
+ application/json:
634
+ schema:
635
+ $ref: "#/components/schemas/ApproveUnifiedRequest"
636
+ responses:
637
+ "200":
638
+ description: Approved
639
+ content:
640
+ application/json:
641
+ schema:
642
+ $ref: "#/components/schemas/ApproveUnifiedResponse"
643
+ /jobs/{id}/confirm:
644
+ post:
645
+ security: [{ BearerAuth: [] }, { ApiKeyAuth: [] }]
646
+ summary: Confirm job and enqueue (reserves if not already reserved)
647
+ parameters:
648
+ - in: path
649
+ name: id
650
+ required: true
651
+ schema: { type: string }
652
+ requestBody:
653
+ required: false
654
+ content:
655
+ application/json:
656
+ schema:
657
+ $ref: "#/components/schemas/ConfirmRequest"
658
+ responses:
659
+ "200":
660
+ description: Confirmed
661
+ content:
662
+ application/json:
663
+ schema:
664
+ $ref: "#/components/schemas/ConfirmResponse"
665
+ /jobs/{id}/status:
666
+ get:
667
+ security: [{ BearerAuth: [] }, { ApiKeyAuth: [] }]
668
+ summary: Get status for an authenticated job
669
+ parameters:
670
+ - in: path
671
+ name: id
672
+ required: true
673
+ schema: { type: string }
674
+ responses:
675
+ "200":
676
+ description: Status
677
+ content:
678
+ application/json:
679
+ schema:
680
+ $ref: "#/components/schemas/StatusResponse"
681
+ "401":
682
+ description: Unauthorized
683
+ content:
684
+ application/json:
685
+ schema:
686
+ $ref: "#/components/schemas/ErrorResponse"
687
+ "403":
688
+ description: Forbidden
689
+ content:
690
+ application/json:
691
+ schema:
692
+ $ref: "#/components/schemas/ErrorResponse"
693
+ "404":
694
+ description: Not found
695
+ content:
696
+ application/json:
697
+ schema:
698
+ $ref: "#/components/schemas/ErrorResponse"
699
+ /jobs/{id}/cancel:
700
+ post:
701
+ security: [{ BearerAuth: [] }, { ApiKeyAuth: [] }]
702
+ summary: Cancel a job (created/queued)
703
+ parameters:
704
+ - in: path
705
+ name: id
706
+ required: true
707
+ schema: { type: string }
708
+ responses:
709
+ "200": { description: Canceled or no-op }
710
+
711
+ /api-keys:
712
+ get:
713
+ security: [{ BearerAuth: [] }]
714
+ summary: List API keys
715
+ responses:
716
+ "200":
717
+ description: List of keys
718
+ content:
719
+ application/json:
720
+ schema:
721
+ $ref: "#/components/schemas/ApiKeyListResponse"
722
+ post:
723
+ security: [{ BearerAuth: [] }]
724
+ summary: Create an API key
725
+ requestBody:
726
+ required: true
727
+ content:
728
+ application/json:
729
+ schema:
730
+ $ref: "#/components/schemas/ApiKeyCreateRequest"
731
+ responses:
732
+ "200":
733
+ description: Created
734
+ content:
735
+ application/json:
736
+ schema:
737
+ $ref: "#/components/schemas/ApiKeyResponse"
738
+ /api-keys/{id}:
739
+ delete:
740
+ security: [{ BearerAuth: [] }]
741
+ summary: Revoke an API key
742
+ parameters:
743
+ - in: path
744
+ name: id
745
+ required: true
746
+ schema: { type: string, format: uuid }
747
+ responses:
748
+ "204": { description: Revoked }
749
+
750
+ /webhooks:
751
+ get:
752
+ security: [{ BearerAuth: [] }]
753
+ summary: List webhooks
754
+ responses:
755
+ "200":
756
+ description: List of webhooks
757
+ content:
758
+ application/json:
759
+ schema:
760
+ $ref: "#/components/schemas/WebhookListResponse"
761
+ post:
762
+ security: [{ BearerAuth: [] }]
763
+ summary: Create a webhook
764
+ requestBody:
765
+ required: true
766
+ content:
767
+ application/json:
768
+ schema:
769
+ $ref: "#/components/schemas/WebhookCreateRequest"
770
+ responses:
771
+ "200":
772
+ description: Created
773
+ content:
774
+ application/json:
775
+ schema:
776
+ $ref: "#/components/schemas/WebhookResponse"
777
+ /webhooks/{id}:
778
+ delete:
779
+ security: [{ BearerAuth: [] }]
780
+ summary: Delete a webhook
781
+ parameters:
782
+ - in: path
783
+ name: id
784
+ required: true
785
+ schema: { type: string, format: uuid }
786
+ responses:
787
+ "204": { description: Deleted }
788
+ post:
789
+ security: [{ BearerAuth: [] }]
790
+ summary: Test a webhook
791
+ parameters:
792
+ - in: path
793
+ name: id
794
+ required: true
795
+ schema: { type: string, format: uuid }
796
+ responses:
797
+ "200": { description: Test event sent }
798
+
799
+ /jobs/bulk:
800
+ post:
801
+ security: [{ BearerAuth: [] }, { ApiKeyAuth: [] }]
802
+ summary: Bulk create jobs
803
+ requestBody:
804
+ required: true
805
+ content:
806
+ application/json:
807
+ schema:
808
+ $ref: "#/components/schemas/BulkCreateRequest"
809
+ responses:
810
+ "200":
811
+ description: Created
812
+ content:
813
+ application/json:
814
+ schema:
815
+ $ref: "#/components/schemas/BulkCreateResponse"
816
+ /jobs/bulk/status:
817
+ get:
818
+ security: [{ BearerAuth: [] }, { ApiKeyAuth: [] }]
819
+ summary: Bulk status check
820
+ parameters:
821
+ - in: query
822
+ name: ids
823
+ required: true
824
+ schema: { type: string, description: "Comma-separated job IDs" }
825
+ responses:
826
+ "200":
827
+ description: Statuses
828
+ content:
829
+ application/json:
830
+ schema:
831
+ $ref: "#/components/schemas/BulkStatusResponse"
832
+ /jobs/bulk/confirm:
833
+ post:
834
+ security: [{ BearerAuth: [] }, { ApiKeyAuth: [] }]
835
+ summary: Bulk confirm/enqueue jobs
836
+ requestBody:
837
+ required: true
838
+ content:
839
+ application/json:
840
+ schema:
841
+ $ref: "#/components/schemas/BulkConfirmRequest"
842
+ responses:
843
+ "200":
844
+ description: Confirmed
845
+ content:
846
+ application/json:
847
+ schema:
848
+ type: object
849
+ properties:
850
+ queued: { type: integer }
851
+ jobs:
852
+ type: array
853
+ items: { type: object }
854
+
855
+ /jobs/{id}/stream/init:
856
+ post:
857
+ security: [{ BearerAuth: [] }, { ApiKeyAuth: [] }]
858
+ summary: Initialize streaming ingest
859
+ parameters:
860
+ - in: path
861
+ name: id
862
+ required: true
863
+ schema: { type: string, format: uuid }
864
+ responses:
865
+ "200":
866
+ description: Session created
867
+ content:
868
+ application/json:
869
+ schema:
870
+ $ref: "#/components/schemas/StreamInitResponse"
871
+ /jobs/{id}/stream/chunk/{index}:
872
+ put:
873
+ security: [{ BearerAuth: [] }, { ApiKeyAuth: [] }]
874
+ summary: Upload a streaming chunk
875
+ parameters:
876
+ - in: path
877
+ name: id
878
+ required: true
879
+ schema: { type: string, format: uuid }
880
+ - in: path
881
+ name: index
882
+ required: true
883
+ schema: { type: integer, minimum: 1, maximum: 10000 }
884
+ requestBody:
885
+ required: true
886
+ content:
887
+ application/octet-stream:
888
+ schema: { type: string, format: binary }
889
+ responses:
890
+ "200": { description: Chunk received }
891
+ /jobs/{id}/stream/finalize:
892
+ post:
893
+ security: [{ BearerAuth: [] }, { ApiKeyAuth: [] }]
894
+ summary: Finalize streaming and complete upload
895
+ parameters:
896
+ - in: path
897
+ name: id
898
+ required: true
899
+ schema: { type: string, format: uuid }
900
+ requestBody:
901
+ content:
902
+ application/json:
903
+ schema:
904
+ $ref: "#/components/schemas/StreamFinalizeRequest"
905
+ responses:
906
+ "200": { description: Finalized }
907
+ /jobs/{id}/stream/abort:
908
+ post:
909
+ security: [{ BearerAuth: [] }, { ApiKeyAuth: [] }]
910
+ summary: Abort streaming
911
+ parameters:
912
+ - in: path
913
+ name: id
914
+ required: true
915
+ schema: { type: string, format: uuid }
916
+ responses:
917
+ "200": { description: Aborted }
918
+
919
+ # Public (no auth) limited endpoints
920
+ /public/jobs:
921
+ post:
922
+ summary: Create a public job
923
+ requestBody:
924
+ required: true
925
+ content:
926
+ application/json:
927
+ schema:
928
+ type: object
929
+ required: [codec, resolution, fps]
930
+ properties:
931
+ codec: { type: string, enum: [h264, h265, av1] }
932
+ resolution: { type: string, enum: [480p, 720p] }
933
+ fps: { type: integer, minimum: 1, maximum: 60 }
934
+ container: { type: string, enum: [mp4, mkv, mov, webm] }
935
+ sourceUrl:
936
+ {
937
+ type: string,
938
+ format: uri,
939
+ description: Optional direct URL ingest,
940
+ }
941
+ responses:
942
+ "200":
943
+ description: Created
944
+ content:
945
+ application/json:
946
+ schema:
947
+ $ref: "#/components/schemas/JobCreateResponse"
948
+ /public/jobs/{id}/probe-duration:
949
+ get:
950
+ summary: Probe duration for public job
951
+ parameters:
952
+ - in: path
953
+ name: id
954
+ required: true
955
+ schema: { type: string }
956
+ responses:
957
+ "200":
958
+ description: Duration
959
+ content:
960
+ application/json:
961
+ schema:
962
+ $ref: "#/components/schemas/ProbeDurationResponse"
963
+ /public/jobs/{id}/confirm:
964
+ post:
965
+ summary: Confirm and enqueue a public job
966
+ parameters:
967
+ - in: path
968
+ name: id
969
+ required: true
970
+ schema: { type: string }
971
+ requestBody:
972
+ required: false
973
+ content:
974
+ application/json:
975
+ schema:
976
+ $ref: "#/components/schemas/ConfirmRequest"
977
+ responses:
978
+ "200":
979
+ description: Confirmed
980
+ /public/jobs/{id}/status:
981
+ get:
982
+ summary: Get status for a public job (tokenized)
983
+ parameters:
984
+ - in: path
985
+ name: id
986
+ required: true
987
+ schema: { type: string }
988
+ - in: query
989
+ name: token
990
+ required: true
991
+ schema: { type: string }
992
+ responses:
993
+ "200":
994
+ description: Status
995
+ content:
996
+ application/json:
997
+ schema:
998
+ $ref: "#/components/schemas/StatusResponse"
999
+
1000
+ # On-Demand Encoding API
1001
+ /ondemand/encode:
1002
+ post:
1003
+ security: [{ BearerAuth: [] }, { ApiKeyAuth: [] }]
1004
+ summary: Submit video for immediate on-demand encoding
1005
+ description: |
1006
+ Simplified API for immediate video encoding. Automatically probes, reserves tokens,
1007
+ and starts encoding. Premium pricing (1.5x-2x) for on-demand convenience.
1008
+ requestBody:
1009
+ required: true
1010
+ content:
1011
+ application/json:
1012
+ schema:
1013
+ $ref: "#/components/schemas/OnDemandEncodeRequest"
1014
+ responses:
1015
+ "200":
1016
+ description: Job created and queued
1017
+ content:
1018
+ application/json:
1019
+ schema:
1020
+ $ref: "#/components/schemas/OnDemandEncodeResponse"
1021
+ "400":
1022
+ description: Invalid request
1023
+ content:
1024
+ application/json:
1025
+ schema:
1026
+ $ref: "#/components/schemas/ErrorResponse"
1027
+ "402":
1028
+ description: Insufficient tokens
1029
+ content:
1030
+ application/json:
1031
+ schema:
1032
+ $ref: "#/components/schemas/ErrorResponse"
1033
+ "403":
1034
+ description: Forbidden
1035
+ content:
1036
+ application/json:
1037
+ schema:
1038
+ $ref: "#/components/schemas/ErrorResponse"
1039
+ "410":
1040
+ description: Deprecated Dropbox destination requested
1041
+ content:
1042
+ application/json:
1043
+ schema:
1044
+ $ref: "#/components/schemas/ErrorResponse"
1045
+ /ondemand/ingest/folder:
1046
+ post:
1047
+ security: [{ BearerAuth: [] }, { ApiKeyAuth: [] }]
1048
+ summary: Ingest videos from an S3 or Google Drive folder
1049
+ description: |
1050
+ Crawls an S3 prefix or Google Drive folder, queues one encode job per video file,
1051
+ and optionally delivers outputs to CDN, S3, or Google Drive.
1052
+
1053
+ For API integrations, Google Drive can be used without dashboard OAuth by passing
1054
+ `accessToken` in `sourceGoogleDrive` and/or `outputGoogleDrive`.
1055
+ requestBody:
1056
+ required: true
1057
+ content:
1058
+ application/json:
1059
+ schema:
1060
+ $ref: "#/components/schemas/OnDemandFolderIngestRequest"
1061
+ responses:
1062
+ "200":
1063
+ description: Folder jobs queued
1064
+ content:
1065
+ application/json:
1066
+ schema:
1067
+ $ref: "#/components/schemas/OnDemandFolderIngestResponse"
1068
+ "400":
1069
+ description: Invalid request
1070
+ content:
1071
+ application/json:
1072
+ schema:
1073
+ $ref: "#/components/schemas/ErrorResponse"
1074
+ "401":
1075
+ description: Missing or expired Google Drive token
1076
+ content:
1077
+ application/json:
1078
+ schema:
1079
+ $ref: "#/components/schemas/ErrorResponse"
1080
+ "404":
1081
+ description: No video files found
1082
+ content:
1083
+ application/json:
1084
+ schema:
1085
+ $ref: "#/components/schemas/ErrorResponse"
1086
+ "410":
1087
+ description: Deprecated Dropbox source or destination requested
1088
+ content:
1089
+ application/json:
1090
+ schema:
1091
+ $ref: "#/components/schemas/ErrorResponse"
1092
+ /ondemand/status/{jobId}:
1093
+ get:
1094
+ security: [{ BearerAuth: [] }, { ApiKeyAuth: [] }]
1095
+ summary: Get on-demand job status and download URL
1096
+ parameters:
1097
+ - in: path
1098
+ name: jobId
1099
+ required: true
1100
+ schema: { type: string, format: uuid }
1101
+ responses:
1102
+ "200":
1103
+ description: Job status
1104
+ content:
1105
+ application/json:
1106
+ schema:
1107
+ $ref: "#/components/schemas/OnDemandStatusResponse"
1108
+ "404":
1109
+ description: Not found
1110
+ content:
1111
+ application/json:
1112
+ schema:
1113
+ $ref: "#/components/schemas/ErrorResponse"
1114
+ /ondemand/{jobId}:
1115
+ delete:
1116
+ security: [{ BearerAuth: [] }, { ApiKeyAuth: [] }]
1117
+ summary: Cancel on-demand encoding job
1118
+ parameters:
1119
+ - in: path
1120
+ name: jobId
1121
+ required: true
1122
+ schema: { type: string, format: uuid }
1123
+ responses:
1124
+ "200":
1125
+ description: Job canceled
1126
+ content:
1127
+ application/json:
1128
+ schema:
1129
+ type: object
1130
+ properties:
1131
+ ok: { type: boolean }
1132
+ released: { type: number }