@convertrilo/sdk 0.0.9 → 0.0.11

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/README.md CHANGED
@@ -41,6 +41,38 @@ For a complete server-to-server walkthrough covering URL, S3, folder ingest, Goo
41
41
  OAuth tokens, polling, and webhooks, see
42
42
  [`docs/API-INTEGRATION-GUIDE.md`](docs/API-INTEGRATION-GUIDE.md).
43
43
 
44
+ ## Idempotent Job Creation
45
+
46
+ Use an idempotency key when retrying create calls from your backend. Reusing the same key with the
47
+ same body returns the original response instead of creating duplicate jobs.
48
+
49
+ ```ts
50
+ const job = await client.createJob({
51
+ externalId: "upload-123",
52
+ metadata: { customerId: "cus_123" },
53
+ codec: "h264",
54
+ resolution: "1080p",
55
+ fps: 30,
56
+ }, {
57
+ idempotencyKey: "job-upload-123",
58
+ });
59
+
60
+ const batch = await client.createJobsBulk({
61
+ jobs: [
62
+ {
63
+ externalId: "batch-42:clip-1",
64
+ codec: "h264",
65
+ resolution: "1080p",
66
+ fps: 30,
67
+ sourceS3: { bucket: "source", key: "clip-1.mp4" },
68
+ },
69
+ ],
70
+ settings: { confirm: true },
71
+ }, {
72
+ idempotencyKey: "bulk-batch-42",
73
+ });
74
+ ```
75
+
44
76
  ## URL Source To CDN Output
45
77
 
46
78
  ```ts
@@ -54,6 +86,8 @@ const job = await client.onDemandEncode({
54
86
  codec: "h264",
55
87
  resolution: "1080p",
56
88
  quality: "better",
89
+ }, {
90
+ idempotencyKey: "encode-customer-video-123",
57
91
  });
58
92
 
59
93
  let finalStatus;
@@ -144,6 +178,8 @@ const batch = await client.onDemandIngestFolder({
144
178
  },
145
179
  codec: "h264",
146
180
  resolution: "1080p",
181
+ }, {
182
+ idempotencyKey: "folder-batch-2026-06-09",
147
183
  });
148
184
 
149
185
  for (const job of batch.jobs || []) {
@@ -15,6 +15,9 @@ export type ClientConfig = {
15
15
  apiKey?: string;
16
16
  fetchImpl?: typeof fetch;
17
17
  };
18
+ export type RequestOptions = {
19
+ idempotencyKey?: string;
20
+ };
18
21
  export declare class ConvertriloClient {
19
22
  private baseUrl;
20
23
  private getToken?;
@@ -32,8 +35,13 @@ export declare class ConvertriloClient {
32
35
  reserveTokens(body: paths["/tokens/reserve"]["post"]["requestBody"]["content"]["application/json"]): Promise<unknown>;
33
36
  releaseTokens(body: paths["/tokens/release"]["post"]["requestBody"]["content"]["application/json"]): Promise<unknown>;
34
37
  deductTokens(body: paths["/tokens/deduct"]["post"]["requestBody"]["content"]["application/json"]): Promise<unknown>;
35
- createJob(body: paths["/jobs"]["post"]["requestBody"]["content"]["application/json"]): Promise<{
38
+ createJob(body: paths["/jobs"]["post"]["requestBody"]["content"]["application/json"], options?: RequestOptions): Promise<{
36
39
  jobId: string;
40
+ externalId?: string | null;
41
+ metadata?: {
42
+ [key: string]: unknown;
43
+ } | null;
44
+ status?: string;
37
45
  upload: {
38
46
  url?: string;
39
47
  key?: string;
@@ -44,6 +52,8 @@ export declare class ConvertriloClient {
44
52
  };
45
53
  estimate?: {
46
54
  neu?: number;
55
+ totalNeu?: number;
56
+ reserved?: number;
47
57
  };
48
58
  }>;
49
59
  probeDuration(id: string): Promise<{
@@ -70,14 +80,22 @@ export declare class ConvertriloClient {
70
80
  encoder?: string | null;
71
81
  pct?: number | null;
72
82
  }>;
73
- createJobsBulk(body: paths["/jobs/bulk"]["post"]["requestBody"]["content"]["application/json"]): Promise<{
83
+ createJobsBulk(body: paths["/jobs/bulk"]["post"]["requestBody"]["content"]["application/json"], options?: RequestOptions): Promise<{
74
84
  totalJobs?: number;
75
85
  totalEstimatedNeu?: number;
86
+ reserved?: number;
76
87
  jobs?: {
77
88
  index?: number;
78
89
  jobId?: string;
90
+ externalId?: string | null;
91
+ metadata?: {
92
+ [key: string]: unknown;
93
+ } | null;
79
94
  status?: string;
80
95
  error?: string;
96
+ estimate?: {
97
+ neu?: number;
98
+ };
81
99
  }[];
82
100
  }>;
83
101
  bulkStatus(ids: string[]): Promise<{
@@ -143,7 +161,7 @@ export declare class ConvertriloClient {
143
161
  };
144
162
  } ? B : any): Promise<unknown>;
145
163
  abortStream(id: string): Promise<unknown>;
146
- onDemandEncode(body: paths["/ondemand/encode"]["post"]["requestBody"]["content"]["application/json"]): Promise<{
164
+ onDemandEncode(body: paths["/ondemand/encode"]["post"]["requestBody"]["content"]["application/json"], options?: RequestOptions): Promise<{
147
165
  jobId: string;
148
166
  externalId?: string | null;
149
167
  metadata?: {
@@ -161,7 +179,7 @@ export declare class ConvertriloClient {
161
179
  statusUrl: string;
162
180
  webhook?: string | null;
163
181
  }>;
164
- onDemandIngestFolder(body: paths["/ondemand/ingest/folder"]["post"]["requestBody"]["content"]["application/json"]): Promise<{
182
+ onDemandIngestFolder(body: paths["/ondemand/ingest/folder"]["post"]["requestBody"]["content"]["application/json"], options?: RequestOptions): Promise<{
165
183
  message?: string;
166
184
  jobs?: {
167
185
  jobId?: string;
package/dist/src/index.js CHANGED
@@ -68,10 +68,13 @@ export class ConvertriloClient {
68
68
  });
69
69
  }
70
70
  // Jobs
71
- async createJob(body) {
71
+ async createJob(body, options = {}) {
72
72
  return this.request(`/jobs`, {
73
73
  method: "POST",
74
74
  body: JSON.stringify(body),
75
+ headers: options.idempotencyKey
76
+ ? { "Idempotency-Key": options.idempotencyKey }
77
+ : undefined,
75
78
  });
76
79
  }
77
80
  async probeDuration(id) {
@@ -96,10 +99,13 @@ export class ConvertriloClient {
96
99
  return this.request(`/jobs/${id}/status`);
97
100
  }
98
101
  // Bulk Jobs
99
- async createJobsBulk(body) {
102
+ async createJobsBulk(body, options = {}) {
100
103
  return this.request(`/jobs/bulk`, {
101
104
  method: "POST",
102
105
  body: JSON.stringify(body),
106
+ headers: options.idempotencyKey
107
+ ? { "Idempotency-Key": options.idempotencyKey }
108
+ : undefined,
103
109
  });
104
110
  }
105
111
  async bulkStatus(ids) {
@@ -170,16 +176,22 @@ export class ConvertriloClient {
170
176
  return this.request(`/jobs/${id}/stream/abort`, { method: "POST" });
171
177
  }
172
178
  // On-Demand Encoding
173
- async onDemandEncode(body) {
179
+ async onDemandEncode(body, options = {}) {
174
180
  return this.request(`/ondemand/encode`, {
175
181
  method: "POST",
176
182
  body: JSON.stringify(body),
183
+ headers: options.idempotencyKey
184
+ ? { "Idempotency-Key": options.idempotencyKey }
185
+ : undefined,
177
186
  });
178
187
  }
179
- async onDemandIngestFolder(body) {
188
+ async onDemandIngestFolder(body, options = {}) {
180
189
  return this.request(`/ondemand/ingest/folder`, {
181
190
  method: "POST",
182
191
  body: JSON.stringify(body),
192
+ headers: options.idempotencyKey
193
+ ? { "Idempotency-Key": options.idempotencyKey }
194
+ : undefined,
183
195
  });
184
196
  }
185
197
  async onDemandStatus(jobId) {
@@ -204,12 +204,15 @@ export interface paths {
204
204
  put?: never;
205
205
  /**
206
206
  * Create a new encode job
207
- * @description Create a job for upload ingest (returns a presigned PUT) or direct URL/S3 ingest.
207
+ * @description Create a job for upload ingest (returns a presigned PUT) or direct URL/S3 ingest. Send `Idempotency-Key` when retrying from your backend to avoid duplicate jobs.
208
208
  */
209
209
  post: {
210
210
  parameters: {
211
211
  query?: never;
212
- header?: never;
212
+ header?: {
213
+ /** @description Optional key for safely retrying job creation requests. Reusing the same key with the same request body replays the original response; reusing it with a different body returns 409. */
214
+ "Idempotency-Key"?: components["parameters"]["IdempotencyKey"];
215
+ };
213
216
  path?: never;
214
217
  cookie?: never;
215
218
  };
@@ -773,11 +776,17 @@ export interface paths {
773
776
  };
774
777
  get?: never;
775
778
  put?: never;
776
- /** Bulk create jobs */
779
+ /**
780
+ * Bulk create jobs
781
+ * @description Send `Idempotency-Key` when retrying from your backend to avoid duplicate bulk batches, duplicate token reservations, or duplicate queue entries.
782
+ */
777
783
  post: {
778
784
  parameters: {
779
785
  query?: never;
780
- header?: never;
786
+ header?: {
787
+ /** @description Optional key for safely retrying job creation requests. Reusing the same key with the same request body replays the original response; reusing it with a different body returns 409. */
788
+ "Idempotency-Key"?: components["parameters"]["IdempotencyKey"];
789
+ };
781
790
  path?: never;
782
791
  cookie?: never;
783
792
  };
@@ -1224,11 +1233,15 @@ export interface paths {
1224
1233
  * Submit video for immediate on-demand encoding
1225
1234
  * @description Simplified API for immediate video encoding. Automatically probes, reserves tokens,
1226
1235
  * and starts encoding. Premium pricing (1.5x-2x) for on-demand convenience.
1236
+ * Send `Idempotency-Key` when retrying from your backend to avoid duplicate jobs.
1227
1237
  */
1228
1238
  post: {
1229
1239
  parameters: {
1230
1240
  query?: never;
1231
- header?: never;
1241
+ header?: {
1242
+ /** @description Optional key for safely retrying job creation requests. Reusing the same key with the same request body replays the original response; reusing it with a different body returns 409. */
1243
+ "Idempotency-Key"?: components["parameters"]["IdempotencyKey"];
1244
+ };
1232
1245
  path?: never;
1233
1246
  cookie?: never;
1234
1247
  };
@@ -1311,11 +1324,15 @@ export interface paths {
1311
1324
  * - Include `refreshToken` when jobs may outlive a short access token.
1312
1325
  *
1313
1326
  * Your users do not need to connect Google Drive inside the Convertrilo dashboard for API usage.
1327
+ * Send `Idempotency-Key` when retrying from your backend to avoid duplicate folder batches.
1314
1328
  */
1315
1329
  post: {
1316
1330
  parameters: {
1317
1331
  query?: never;
1318
- header?: never;
1332
+ header?: {
1333
+ /** @description Optional key for safely retrying job creation requests. Reusing the same key with the same request body replays the original response; reusing it with a different body returns 409. */
1334
+ "Idempotency-Key"?: components["parameters"]["IdempotencyKey"];
1335
+ };
1319
1336
  path?: never;
1320
1337
  cookie?: never;
1321
1338
  };
@@ -1497,6 +1514,12 @@ export interface components {
1497
1514
  amount: number;
1498
1515
  };
1499
1516
  JobCreateRequest: {
1517
+ /** @description Your stable job identifier for reconciliation in status responses and webhooks. */
1518
+ externalId?: string;
1519
+ /** @description Integration-owned JSON object returned in status responses and webhooks. */
1520
+ metadata?: {
1521
+ [key: string]: unknown;
1522
+ };
1500
1523
  /** @enum {string} */
1501
1524
  codec: "h264" | "h265" | "av1";
1502
1525
  /** @enum {string} */
@@ -1551,6 +1574,12 @@ export interface components {
1551
1574
  };
1552
1575
  JobCreateResponse: {
1553
1576
  jobId: string;
1577
+ externalId?: string | null;
1578
+ metadata?: {
1579
+ [key: string]: unknown;
1580
+ } | null;
1581
+ /** @description Present when the job is auto-confirmed and queued. */
1582
+ status?: string;
1554
1583
  upload: {
1555
1584
  url?: string;
1556
1585
  key?: string;
@@ -1561,6 +1590,8 @@ export interface components {
1561
1590
  };
1562
1591
  estimate?: {
1563
1592
  neu?: number;
1593
+ totalNeu?: number;
1594
+ reserved?: number;
1564
1595
  };
1565
1596
  };
1566
1597
  ProbeDurationResponse: {
@@ -1640,12 +1671,20 @@ export interface components {
1640
1671
  BulkCreateResponse: {
1641
1672
  totalJobs?: number;
1642
1673
  totalEstimatedNeu?: number;
1674
+ reserved?: number;
1643
1675
  jobs?: {
1644
1676
  index?: number;
1645
1677
  /** Format: uuid */
1646
1678
  jobId?: string;
1679
+ externalId?: string | null;
1680
+ metadata?: {
1681
+ [key: string]: unknown;
1682
+ } | null;
1647
1683
  status?: string;
1648
1684
  error?: string;
1685
+ estimate?: {
1686
+ neu?: number;
1687
+ };
1649
1688
  }[];
1650
1689
  };
1651
1690
  BulkStatusResponse: {
@@ -2025,7 +2064,10 @@ export interface components {
2025
2064
  };
2026
2065
  };
2027
2066
  responses: never;
2028
- parameters: never;
2067
+ parameters: {
2068
+ /** @description Optional key for safely retrying job creation requests. Reusing the same key with the same request body replays the original response; reusing it with a different body returns 409. */
2069
+ IdempotencyKey: string;
2070
+ };
2029
2071
  requestBodies: never;
2030
2072
  headers: never;
2031
2073
  pathItems: never;
@@ -46,6 +46,39 @@ const client = new ConvertriloClient({
46
46
 
47
47
  SDK source and examples: https://github.com/serkandrgn/convertrilo-js
48
48
 
49
+ ## Idempotency
50
+
51
+ When your backend retries `POST /jobs`, `POST /jobs/bulk`, `POST /ondemand/encode`, or
52
+ `POST /ondemand/ingest/folder`, send an idempotency key. The API replays the original response for
53
+ the same key and body, and returns `409` if the key is reused with a different body.
54
+
55
+ ```ts
56
+ const job = await client.createJob({
57
+ externalId: "upload-123",
58
+ metadata: { customerId: "cus_123" },
59
+ codec: "h264",
60
+ resolution: "1080p",
61
+ fps: 30,
62
+ }, {
63
+ idempotencyKey: "job-upload-123",
64
+ });
65
+
66
+ const batch = await client.createJobsBulk({
67
+ jobs: [
68
+ {
69
+ externalId: "batch-42:clip-1",
70
+ codec: "h264",
71
+ resolution: "1080p",
72
+ fps: 30,
73
+ sourceS3: { bucket: "source", key: "clip-1.mp4" },
74
+ },
75
+ ],
76
+ settings: { confirm: true },
77
+ }, {
78
+ idempotencyKey: "bulk-batch-42",
79
+ });
80
+ ```
81
+
49
82
  ## Flow 1: URL Source To CDN Output
50
83
 
51
84
  Use this when the source video is already available over HTTP(S), and you want Convertrilo to return a signed CDN download URL.
@@ -61,6 +94,8 @@ const job = await client.onDemandEncode({
61
94
  codec: "h264",
62
95
  resolution: "1080p",
63
96
  quality: "better",
97
+ }, {
98
+ idempotencyKey: "encode-customer-video-123",
64
99
  });
65
100
 
66
101
  console.log(job.jobId);
@@ -72,6 +107,7 @@ Equivalent curl:
72
107
  curl https://api.convertrilo.com/ondemand/encode \
73
108
  -H "Content-Type: application/json" \
74
109
  -H "X-API-Key: $CONVERTRILO_API_KEY" \
110
+ -H "Idempotency-Key: encode-customer-video-123" \
75
111
  -d '{
76
112
  "sourceUrl": "https://example.com/input.mp4",
77
113
  "externalId": "customer-video-123",
@@ -89,6 +125,8 @@ Poll `/ondemand/status/{jobId}` until the job reaches `success`, then read `down
89
125
 
90
126
  Use `externalId` and `metadata` to reconcile Convertrilo jobs with your own database. They are returned by status responses and managed webhook payloads.
91
127
 
128
+ Use `Idempotency-Key` on creation requests that your backend may retry. If the same key and request body are received again, Convertrilo returns the original response instead of queuing another job. If the same key is reused with a different body, the API returns `409`.
129
+
92
130
  ## Flow 2: URL Source To S3 Output
93
131
 
94
132
  Use this when your customer wants the encoded output in their own S3-compatible bucket.
@@ -148,6 +186,8 @@ const batch = await client.onDemandIngestFolder({
148
186
  },
149
187
  codec: "h264",
150
188
  resolution: "1080p",
189
+ }, {
190
+ idempotencyKey: "folder-batch-2026-06-09",
151
191
  });
152
192
 
153
193
  for (const job of batch.jobs || []) {
package/openapi.yaml CHANGED
@@ -37,6 +37,15 @@ components:
37
37
  type: apiKey
38
38
  in: header
39
39
  name: X-API-Key
40
+ parameters:
41
+ IdempotencyKey:
42
+ in: header
43
+ name: Idempotency-Key
44
+ required: false
45
+ schema:
46
+ type: string
47
+ maxLength: 255
48
+ description: Optional key for safely retrying job creation requests. Reusing the same key with the same request body replays the original response; reusing it with a different body returns 409.
40
49
  schemas:
41
50
  AuthLoginRequest:
42
51
  type: object
@@ -72,6 +81,14 @@ components:
72
81
  type: object
73
82
  required: [codec, resolution, fps]
74
83
  properties:
84
+ externalId:
85
+ type: string
86
+ maxLength: 255
87
+ description: Your stable job identifier for reconciliation in status responses and webhooks.
88
+ metadata:
89
+ type: object
90
+ additionalProperties: true
91
+ description: Integration-owned JSON object returned in status responses and webhooks.
75
92
  codec:
76
93
  type: string
77
94
  enum: [h264, h265, av1]
@@ -127,6 +144,14 @@ components:
127
144
  required: [jobId, upload, output]
128
145
  properties:
129
146
  jobId: { type: string }
147
+ externalId: { type: string, nullable: true }
148
+ metadata:
149
+ type: object
150
+ additionalProperties: true
151
+ nullable: true
152
+ status:
153
+ type: string
154
+ description: Present when the job is auto-confirmed and queued.
130
155
  upload:
131
156
  nullable: true
132
157
  type: object
@@ -142,6 +167,8 @@ components:
142
167
  type: object
143
168
  properties:
144
169
  neu: { type: number }
170
+ totalNeu: { type: number }
171
+ reserved: { type: number }
145
172
  ProbeDurationResponse:
146
173
  type: object
147
174
  properties:
@@ -221,6 +248,7 @@ components:
221
248
  properties:
222
249
  totalJobs: { type: integer }
223
250
  totalEstimatedNeu: { type: number }
251
+ reserved: { type: number }
224
252
  jobs:
225
253
  type: array
226
254
  items:
@@ -228,8 +256,17 @@ components:
228
256
  properties:
229
257
  index: { type: integer }
230
258
  jobId: { type: string, format: uuid }
259
+ externalId: { type: string, nullable: true }
260
+ metadata:
261
+ type: object
262
+ additionalProperties: true
263
+ nullable: true
231
264
  status: { type: string }
232
265
  error: { type: string }
266
+ estimate:
267
+ type: object
268
+ properties:
269
+ neu: { type: number }
233
270
  BulkStatusResponse:
234
271
  type: object
235
272
  properties:
@@ -705,7 +742,9 @@ paths:
705
742
  post:
706
743
  security: [{ BearerAuth: [] }, { ApiKeyAuth: [] }]
707
744
  summary: Create a new encode job
708
- description: Create a job for upload ingest (returns a presigned PUT) or direct URL/S3 ingest.
745
+ description: Create a job for upload ingest (returns a presigned PUT) or direct URL/S3 ingest. Send `Idempotency-Key` when retrying from your backend to avoid duplicate jobs.
746
+ parameters:
747
+ - $ref: "#/components/parameters/IdempotencyKey"
709
748
  requestBody:
710
749
  required: true
711
750
  content:
@@ -984,6 +1023,9 @@ paths:
984
1023
  post:
985
1024
  security: [{ BearerAuth: [] }, { ApiKeyAuth: [] }]
986
1025
  summary: Bulk create jobs
1026
+ description: Send `Idempotency-Key` when retrying from your backend to avoid duplicate bulk batches, duplicate token reservations, or duplicate queue entries.
1027
+ parameters:
1028
+ - $ref: "#/components/parameters/IdempotencyKey"
987
1029
  requestBody:
988
1030
  required: true
989
1031
  content:
@@ -1189,6 +1231,9 @@ paths:
1189
1231
  description: |
1190
1232
  Simplified API for immediate video encoding. Automatically probes, reserves tokens,
1191
1233
  and starts encoding. Premium pricing (1.5x-2x) for on-demand convenience.
1234
+ Send `Idempotency-Key` when retrying from your backend to avoid duplicate jobs.
1235
+ parameters:
1236
+ - $ref: "#/components/parameters/IdempotencyKey"
1192
1237
  requestBody:
1193
1238
  required: true
1194
1239
  content:
@@ -1275,6 +1320,9 @@ paths:
1275
1320
  - Include `refreshToken` when jobs may outlive a short access token.
1276
1321
 
1277
1322
  Your users do not need to connect Google Drive inside the Convertrilo dashboard for API usage.
1323
+ Send `Idempotency-Key` when retrying from your backend to avoid duplicate folder batches.
1324
+ parameters:
1325
+ - $ref: "#/components/parameters/IdempotencyKey"
1278
1326
  requestBody:
1279
1327
  required: true
1280
1328
  content:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@convertrilo/sdk",
3
- "version": "0.0.9",
3
+ "version": "0.0.11",
4
4
  "description": "TypeScript client for the Convertrilo video encoding API",
5
5
  "private": false,
6
6
  "license": "MIT",