bannerbear 0.2.0 → 0.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 850108dc51b0e1e90d1e7e6452ce69b65c972638e33d7f6b946f0f81ab4fe1cd
4
- data.tar.gz: f4c1e8883281527d9c515a07d358a251629ec19fb06a2dab4506fb95cfd9576a
3
+ metadata.gz: 29845579050c36919da99fcffdf6ec4d5577dbe243fbf114812500636969b05d
4
+ data.tar.gz: e13320ddb5d91bb4f957cefa72eb4292035f07a0c11f804d6f2b8e5ead471189
5
5
  SHA512:
6
- metadata.gz: de72ce94b734dbcfc73ca872ce5b2ce9f41c23b6f5c1bf40e2cf78bf8e2938b81933ab20113c41b5e8ad0af2dc034ff251b696ef310219063ea066dfdbf759ba
7
- data.tar.gz: 92bfc10f330f5d318084b8e446491678029de74807ef353e2d679279590e22487780e15f4b093fa0bf374c8601fc6e685faf8d9d96f9fa4266f088638080186f
6
+ metadata.gz: 2977c792ce41055fb8248ff4941ab5cccbdc2265c9589004124381c66163ddbcc1ab7a5a49ac811cbee75bef91780483fad8843e09cb26d1c5e5f520c37feaf0
7
+ data.tar.gz: 829e80632d438b428f6f8ed18d3009e0593fa480f96056200623f8b578658117fc5f626dbe5786e8333faa7adeafbb600659cb03a6b1c19d2878a0f6247970e1
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- bannerbear (0.2.0)
4
+ bannerbear (0.4.0)
5
5
  httparty
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -31,6 +31,13 @@ For the **legacy V2 API**, see [Usage](#usage) below — that section is unchang
31
31
  - [Account (V5)](#account-v5)
32
32
  - [Image Templates (V5)](#image-templates-v5)
33
33
  - [Images (V5)](#images-v5)
34
+ - [Animation Templates (V5)](#animation-templates-v5)
35
+ - [Animations (V5)](#animations-v5)
36
+ - [Workflows (V5)](#workflows-v5)
37
+ - [Workflow Runs (V5)](#workflow-runs-v5)
38
+ - [Tools (V5)](#tools-v5)
39
+ - [Assets (V5)](#assets-v5)
40
+ - [Publications (V5)](#publications-v5)
34
41
  - [Batches (V5)](#batches-v5)
35
42
  - [Webhooks (V5)](#webhooks-v5)
36
43
  - [Instant URLs (V5)](#instant-urls-v5)
@@ -55,14 +62,38 @@ bb.account
55
62
 
56
63
  ### Image Templates (V5)
57
64
 
58
- V5 renames V2's `templates` resource to `image_templates`.
65
+ V5 renames V2's `templates` resource to `image_templates`. Templates can be created, updated, and deleted through the API — `config` holds the full canvas.
59
66
 
60
67
  ```ruby
61
68
  bb.list_image_templates(page: 1)
62
69
  bb.get_image_template("template uid")
70
+
71
+ bb.create_image_template(
72
+ name: "My Template",
73
+ description: "Created from the API",
74
+ tags: ["portrait"],
75
+ width: 1080,
76
+ height: 1080,
77
+ config: { objects: [
78
+ { id: "bg", type: "rectangle", left: 0, top: 0, width: 1080, height: 1080, "background-color" => "#0f172a" },
79
+ { id: "headline", type: "text", left: 80, top: 400, width: 920, text: "Hello World!", "font-size" => 72, color: "#ffffff" }
80
+ ] }
81
+ )
82
+
63
83
  bb.update_image_template("template uid", name: "New Name", description: "...", tags: ["portrait"])
84
+ bb.delete_image_template("template uid")
64
85
  ```
65
86
 
87
+ ##### Options for `create_image_template` / `update_image_template`
88
+
89
+ - `name` *required for create* (`string`)
90
+ - `description` (`string`)
91
+ - `tags` (`array`)
92
+ - `width` / `height`: canvas size in pixels (`integer`)
93
+ - `config`: full canvas configuration, `{ objects: [...] }`. Passing it **replaces** the existing config in place (`hash`)
94
+
95
+ Deleting is a soft delete: images already rendered from the template stay intact, but the template no longer appears in list/get calls and cannot be used for new renders.
96
+
66
97
  ### Images (V5)
67
98
 
68
99
  V5's `modifications` is an **object** with two sub-keys:
@@ -95,7 +126,7 @@ bb.create_image("template uid", sync: true, modifications: { objects: [...] })
95
126
  - `scale`: scale multiplier, 1–4 (`integer`)
96
127
  - `dpi`: DPI metadata (`integer`)
97
128
  - `quality`: quality control (`integer`)
98
- - `proxy`: proxy server for asset fetching (`string`)
129
+ - `proxy`: proxy and resize external images before rendering (`boolean`)
99
130
  - `metadata`: include any metadata to reference at a later point (`string`)
100
131
  - `version`: pin template version (`integer`)
101
132
  - `sync`: route to the sync host (`boolean`; Ruby-only, not sent to the API)
@@ -105,13 +136,214 @@ bb.get_image("image uid")
105
136
  bb.list_images(page: 1)
106
137
  ```
107
138
 
139
+ ### Animation Templates (V5)
140
+
141
+ Animation templates render video instead of a still image. They carry a `frame_rate` and a `duration_seconds` in place of the image template's static canvas.
142
+
143
+ ```ruby
144
+ bb.list_animation_templates(page: 1)
145
+ bb.get_animation_template("template uid")
146
+
147
+ bb.create_animation_template(
148
+ name: "My Animation",
149
+ description: "Created from the API",
150
+ tags: ["promo"],
151
+ width: 1080,
152
+ height: 1080,
153
+ frame_rate: 30
154
+ )
155
+
156
+ bb.update_animation_template("template uid", name: "New Name", frame_rate: 60)
157
+ bb.delete_animation_template("template uid")
158
+ ```
159
+
160
+ ##### Options for `create_animation_template` / `update_animation_template`
161
+
162
+ - `name` *required for create* (`string`)
163
+ - `description` (`string`)
164
+ - `tags` (`array`)
165
+ - `width` / `height`: canvas size in pixels, 100–3000 (`integer`)
166
+ - `frame_rate`: `24`, `30`, or `60` (`integer`)
167
+
168
+ ### Animations (V5)
169
+
170
+ Rendering an animation is **always asynchronous** — there is no sync host for animations. Poll `get_animation` until the status is `"completed"` or `"failed"`, or subscribe to a webhook with the resource `"animation"`.
171
+
172
+ ```ruby
173
+ animation = bb.create_animation("animation template uid",
174
+ modifications: {
175
+ template: { width: 1080, height: 1080, fps: 30 },
176
+ objects: [
177
+ { name: "headline", text: "Hello World!" }
178
+ ]
179
+ },
180
+ formats: ["mp4"]
181
+ )
182
+
183
+ animation = bb.get_animation(animation["uid"])
184
+ animation["status"] # => "queued" | "rendering" | "completed" | "failed"
185
+ animation["files"] # => { "mp4" => "https://..." } when completed
186
+
187
+ bb.list_animations(page: 1)
188
+ ```
189
+
190
+ ##### Options for `create_animation`
191
+
192
+ - `modifications`: V5 modifications object (`hash`)
193
+ - `formats`: `["mp4"]` or `["mov"]`. Ignored when `transparent` is set — that always yields MOV (`array`)
194
+ - `metadata`: include any metadata to reference at a later point (`string`)
195
+
196
+ Template-level modification keys for animations: `width`, `height`, `fps` (`24`, `30`, or `60`), and `transparent`. Setting `transparent` renders on a transparent background and forces a MOV output, so the alpha channel survives.
197
+
198
+ ### Workflows (V5)
199
+
200
+ Workflows chain several steps into one named, re-runnable operation. They are read-only through the API — build them in the Bannerbear UI, then run them here.
201
+
202
+ ```ruby
203
+ bb.list_workflows(page: 1)
204
+
205
+ workflow = bb.get_workflow("workflow uid")
206
+ workflow["inputs"] # the inputs this workflow declares
207
+ workflow["steps"]
208
+ ```
209
+
210
+ ### Workflow Runs (V5)
211
+
212
+ A run is **asynchronous**. Poll `get_workflow_run` until the status is `"completed"` or `"failed"`, or subscribe to a webhook with the resource `"workflow_run"`.
213
+
214
+ ```ruby
215
+ run = bb.create_workflow_run("workflow uid",
216
+ inputs: { "headline" => "Hello World!", "photo" => "https://example.com/photo.jpg" }
217
+ )
218
+
219
+ run = bb.get_workflow_run(run["uid"])
220
+ run["status"] # => "queued" | "running" | "completed" | "failed"
221
+ run["outputs"] if run["status"] == "completed"
222
+
223
+ bb.list_workflow_runs(page: 1)
224
+ ```
225
+
226
+ ##### Options for `create_workflow_run`
227
+
228
+ - `inputs`: values for the workflow's declared inputs (`hash`)
229
+
230
+ ### Tools (V5)
231
+
232
+ Tools are standalone media operations that do not use a template. Every tool is **asynchronous**: the call returns a pending *tool job*. Poll `get_tool_job` until the status is `"completed"` or `"failed"`, or subscribe to a webhook with the resource `"tool_job"`.
233
+
234
+ ```ruby
235
+ job = bb.trim_video(video_url: "https://example.com/clip.mp4", start: 2.5, end: 10.0)
236
+
237
+ job = bb.get_tool_job(job["uid"])
238
+ job["status"] # => "pending" | "running" | "completed" | "failed"
239
+ job["outputs"]["video_url"] if job["status"] == "completed"
240
+
241
+ bb.list_tool_jobs(page: 1)
242
+ ```
243
+
244
+ Every tool also accepts an optional `metadata` string.
245
+
246
+ | Method | Required | Optional | Output key |
247
+ | --- | --- | --- | --- |
248
+ | `remove_bg` | `image_url` | — | `image_url` |
249
+ | `create_pdf` | `urls` | — | `pdf_url` |
250
+ | `trim_video` | `video_url`, `start`, `end` | — | `video_url` |
251
+ | `concat_videos` | `video_urls` | `width`, `height`, `fps` | `video_url` |
252
+ | `resize_video` | `video_url`, `width`, `height` | `fit` | `video_url` |
253
+ | `crop_video` | `video_url`, `x`, `y`, `width`, `height` | — | `video_url` |
254
+ | `overlay_video` | `base_video_url`, `overlay_video_url` | `position`, `margin`, `x`, `y`, `scale`, `start` | `video_url` |
255
+ | `overlay_image` | `video_url`, `image_url` | `position`, `margin`, `x`, `y`, `opacity` | `video_url` |
256
+ | `subtitle_video` | `video_url` | `language`, `font`, `font_size`, `color`, `bold`, `italic`, `outline_color`, `outline_width`, `shadow_size`, `shadow_color`, `background_style`, `background_color`, `alignment` | `video_url` |
257
+ | `generate_voiceover` | `text`, `voice` | — | `audio_url` |
258
+ | `add_audio` | `video_url`, `audio_url`, `mode` | `volume`, `loop`, `ducking` | `video_url` |
259
+ | `add_cover_art` | `video_url`, `image_url` | — | `video_url` |
260
+ | `create_video_slideshow` | `image_urls` | `slide_duration`, `transition`, `transition_duration`, `width`, `height` | `video_url` |
261
+ | `apply_color_filter` | `video_url`, `filter` | — | `video_url` |
262
+ | `soften_video` | `video_url`, `strength` | — | `video_url` |
263
+
264
+ Place the two overlay tools with **either** `position` (plus an optional `margin`) **or** `x`/`y` — not both. `position` accepts `top_left`, `top_center`, `top_right`, `center`, `bottom_left`, `bottom_center`, and `bottom_right`. `resize_video` accepts a `fit` of `cover` (crops), `contain` (letterboxes), or `blur` (fills the bars with a blurred copy).
265
+
266
+ A few examples:
267
+
268
+ ```ruby
269
+ bb.remove_bg(image_url: "https://example.com/product.png")
270
+
271
+ # Corner placement, 40px in from each edge
272
+ bb.overlay_image(
273
+ video_url: "https://example.com/clip.mp4",
274
+ image_url: "https://example.com/logo.png",
275
+ position: "bottom_right",
276
+ margin: 40,
277
+ opacity: 0.8
278
+ )
279
+
280
+ bb.subtitle_video(
281
+ video_url: "https://example.com/talk.mp4",
282
+ font: "montserrat",
283
+ font_size: 32,
284
+ color: "#ffffff",
285
+ background_style: "outline",
286
+ alignment: "2"
287
+ )
288
+
289
+ bb.generate_voiceover(text: "Welcome to Bannerbear.", voice: "rachel")
290
+
291
+ bb.create_video_slideshow(
292
+ image_urls: ["https://example.com/1.jpg", "https://example.com/2.jpg"],
293
+ slide_duration: 3,
294
+ transition: "fade",
295
+ width: 1280,
296
+ height: 720
297
+ )
298
+ ```
299
+
300
+ `create_tool_job` calls any tool by name — the escape hatch for tools added after this release:
301
+
302
+ ```ruby
303
+ bb.create_tool_job("remove_bg", image_url: "https://example.com/product.png")
304
+ ```
305
+
306
+ ### Assets (V5)
307
+
308
+ Upload a file (max 5MB) and get back a durable CDN URL you can feed to image modifications or tools. Uploads are deduplicated per workspace by SHA-256, so re-uploading the same bytes returns the existing record instead of creating a duplicate.
309
+
310
+ ```ruby
311
+ asset = bb.upload_asset(File.binread("logo.png"), "image/png")
312
+ asset["url"]
313
+
314
+ bb.get_asset("asset uid")
315
+ bb.list_assets(page: 1)
316
+ ```
317
+
318
+ Accepted mime types: `image/jpeg`, `image/png`, `image/webp`, `image/gif`, `video/mp4`, `video/webm`, `video/quicktime`, `audio/mpeg`, `audio/wav`, `audio/mp4`, `audio/webm`, `audio/ogg`, `application/pdf`.
319
+
320
+ `check_assets` maps each SHA-256 content hash to its existing asset (or `nil`), so a syncing client can skip the upload round-trip for content it already pushed. Max 100 hashes per call.
321
+
322
+ ```ruby
323
+ digest = OpenSSL::Digest::SHA256.hexdigest(File.binread("logo.png"))
324
+ found = bb.check_assets([digest])
325
+ bb.upload_asset(File.binread("logo.png"), "image/png") if found[digest].nil?
326
+ ```
327
+
328
+ ### Publications (V5)
329
+
330
+ Publications are templates published to the public library. Installing one clones it into your workspace as a new image template.
331
+
332
+ ```ruby
333
+ bb.list_publications(page: 1)
334
+ bb.get_publication("publication uid")
335
+
336
+ template = bb.install_publication("publication uid")
337
+ template["uid"]
338
+ ```
339
+
108
340
  ### Batches (V5)
109
341
 
110
342
  Generate multiple images in one request (up to 100).
111
343
 
112
344
  ```ruby
113
345
  bb.create_batch(
114
- type: "image",
346
+ type: "images",
115
347
  items: [
116
348
  { template: "template uid 1", modifications: { objects: [...] } },
117
349
  { template: "template uid 2", modifications: { objects: [...] } }
@@ -127,13 +359,11 @@ Webhooks are managed as a first-class resource in V5 (instead of being a per-req
127
359
 
128
360
  ```ruby
129
361
  hook = bb.create_webhook(
130
- name: "my-webhook",
131
- url: "https://example.com/hook",
132
- resource: "image",
133
- event: "completed",
134
- status: "active",
135
- scope: "all",
136
- templates: []
362
+ name: "my-webhook",
363
+ url: "https://example.com/hook",
364
+ resource: "image",
365
+ event: "completed",
366
+ status: "active"
137
367
  )
138
368
 
139
369
  # IMPORTANT: signing_key is ONLY returned in the create response. Store it now —
@@ -141,6 +371,14 @@ hook = bb.create_webhook(
141
371
  puts hook["signing_key"]
142
372
  ```
143
373
 
374
+ ##### Options for `create_webhook` / `update_webhook`
375
+
376
+ - `name` *required* (`string`)
377
+ - `url` *required* — the URL that receives the events (`string`)
378
+ - `resource`: `"image"`, `"batch"`, `"tool_job"`, `"animation"`, or `"workflow_run"` (`string`)
379
+ - `event`: `"all_events"`, `"completed"`, or `"failed"` (`string`)
380
+ - `status`: `"active"` or `"disabled"` (`string`)
381
+
144
382
  CRUD:
145
383
 
146
384
  ```ruby
@@ -150,8 +388,7 @@ bb.update_webhook("webhook uid",
150
388
  url: "https://example.com/hook",
151
389
  resource: "image",
152
390
  event: "completed",
153
- status: "active",
154
- scope: "all"
391
+ status: "active"
155
392
  )
156
393
  bb.delete_webhook("webhook uid")
157
394
  bb.list_webhooks(page: 1)
@@ -21,8 +21,16 @@ module Bannerbear
21
21
  get_response "/image_templates/#{uid}"
22
22
  end
23
23
 
24
+ def create_image_template(payload = {})
25
+ post_response "/image_templates", payload.slice(:name, :description, :tags, :width, :height, :config)
26
+ end
27
+
24
28
  def update_image_template(uid, payload = {})
25
- patch_response "/image_templates/#{uid}", payload.slice(:name, :description, :tags)
29
+ patch_response "/image_templates/#{uid}", payload.slice(:name, :description, :tags, :width, :height, :config)
30
+ end
31
+
32
+ def delete_image_template(uid)
33
+ delete_response "/image_templates/#{uid}"
26
34
  end
27
35
 
28
36
  # Images
@@ -39,6 +47,162 @@ module Bannerbear
39
47
  post_response "/images", payload.slice(:modifications, :formats, :scale, :dpi, :quality, :proxy, :metadata, :version).merge({:template => uid}), payload[:sync]
40
48
  end
41
49
 
50
+ # Animation Templates
51
+
52
+ def list_animation_templates(params = {})
53
+ get_response "/animation_templates?#{URI.encode_www_form(params.slice(:page))}"
54
+ end
55
+
56
+ def get_animation_template(uid)
57
+ get_response "/animation_templates/#{uid}"
58
+ end
59
+
60
+ def create_animation_template(payload = {})
61
+ post_response "/animation_templates", payload.slice(:name, :description, :tags, :width, :height, :frame_rate)
62
+ end
63
+
64
+ def update_animation_template(uid, payload = {})
65
+ patch_response "/animation_templates/#{uid}", payload.slice(:name, :description, :tags, :width, :height, :frame_rate)
66
+ end
67
+
68
+ def delete_animation_template(uid)
69
+ delete_response "/animation_templates/#{uid}"
70
+ end
71
+
72
+ # Animations
73
+ #
74
+ # Rendering is always asynchronous — there is no synchronous host for
75
+ # animations. Poll get_animation until the status is "completed" or
76
+ # "failed", or subscribe to a webhook with the resource "animation".
77
+
78
+ def list_animations(params = {})
79
+ get_response "/animations?#{URI.encode_www_form(params.slice(:page))}"
80
+ end
81
+
82
+ def get_animation(uid)
83
+ get_response "/animations/#{uid}"
84
+ end
85
+
86
+ def create_animation(uid, payload = {})
87
+ post_response "/animations", payload.slice(:modifications, :formats, :metadata).merge({:template => uid})
88
+ end
89
+
90
+ # Workflows
91
+
92
+ def list_workflows(params = {})
93
+ get_response "/workflows?#{URI.encode_www_form(params.slice(:page))}"
94
+ end
95
+
96
+ def get_workflow(uid)
97
+ get_response "/workflows/#{uid}"
98
+ end
99
+
100
+ # Workflow Runs
101
+ #
102
+ # A run is asynchronous. Poll get_workflow_run until the status is
103
+ # "completed" or "failed", or subscribe to a webhook with the resource
104
+ # "workflow_run".
105
+
106
+ def list_workflow_runs(params = {})
107
+ get_response "/workflow_runs?#{URI.encode_www_form(params.slice(:page))}"
108
+ end
109
+
110
+ def get_workflow_run(uid)
111
+ get_response "/workflow_runs/#{uid}"
112
+ end
113
+
114
+ def create_workflow_run(uid, payload = {})
115
+ post_response "/workflow_runs", payload.slice(:inputs).merge({:workflow => uid})
116
+ end
117
+
118
+ # Tools
119
+ #
120
+ # Every tool is asynchronous: the POST returns a pending tool job. Poll
121
+ # get_tool_job until the status is "completed" or "failed", or subscribe
122
+ # to a webhook with the resource "tool_job".
123
+
124
+ TOOL_PARAMS = {
125
+ "remove_bg" => [:image_url],
126
+ "create_pdf" => [:urls],
127
+ "trim_video" => [:video_url, :start, :end],
128
+ "concat_videos" => [:video_urls, :width, :height, :fps],
129
+ "resize_video" => [:video_url, :width, :height, :fit],
130
+ "crop_video" => [:video_url, :x, :y, :width, :height],
131
+ # Place the overlay with either :position (+ optional :margin) or :x/:y,
132
+ # not both.
133
+ "overlay_video" => [:base_video_url, :overlay_video_url, :x, :y, :position, :margin, :scale, :start],
134
+ "overlay_image" => [:video_url, :image_url, :x, :y, :position, :margin, :opacity],
135
+ "subtitle_video" => [:video_url, :language, :font, :font_size, :color, :bold, :italic,
136
+ :outline_color, :outline_width, :shadow_size, :shadow_color,
137
+ :background_style, :background_color, :alignment],
138
+ "generate_voiceover" => [:text, :voice],
139
+ "add_audio" => [:video_url, :audio_url, :mode, :volume, :loop, :ducking],
140
+ "add_cover_art" => [:video_url, :image_url],
141
+ "create_video_slideshow" => [:image_urls, :slide_duration, :transition, :transition_duration, :width, :height],
142
+ "apply_color_filter" => [:video_url, :filter],
143
+ "soften_video" => [:video_url, :strength]
144
+ }.freeze
145
+
146
+ def create_tool_job(tool, payload = {})
147
+ allowed = TOOL_PARAMS[tool.to_s]
148
+ raise ArgumentError, "unknown tool: #{tool.inspect}" if allowed.nil?
149
+ post_response "/tools/#{tool}", payload.slice(*allowed, :metadata)
150
+ end
151
+
152
+ # Defines one method per tool, e.g. remove_bg(payload) or trim_video(payload).
153
+ TOOL_PARAMS.each_key do |tool|
154
+ define_method(tool) { |payload = {}| create_tool_job(tool, payload) }
155
+ end
156
+
157
+ # Tool Jobs
158
+
159
+ def list_tool_jobs(params = {})
160
+ get_response "/tool_jobs?#{URI.encode_www_form(params.slice(:page))}"
161
+ end
162
+
163
+ def get_tool_job(uid)
164
+ get_response "/tool_jobs/#{uid}"
165
+ end
166
+
167
+ # Assets
168
+
169
+ def list_assets(params = {})
170
+ get_response "/assets?#{URI.encode_www_form(params.slice(:page))}"
171
+ end
172
+
173
+ def get_asset(uid)
174
+ get_response "/assets/#{uid}"
175
+ end
176
+
177
+ # Uploads raw file bytes (max 5MB). content_type must be the mime type of
178
+ # the data, e.g. "image/png" or "video/mp4". Uploads are deduplicated per
179
+ # workspace by SHA-256, so re-uploading the same bytes returns the
180
+ # existing asset instead of creating a duplicate.
181
+ def upload_asset(data, content_type)
182
+ upload_response "/assets", data, content_type
183
+ end
184
+
185
+ # Returns a map of SHA-256 content hash => asset (or nil when the hash is
186
+ # not stored in this workspace). Max 100 hashes per call.
187
+ def check_assets(content_hashes)
188
+ post_response "/assets/check", { :content_hashes => content_hashes }
189
+ end
190
+
191
+ # Publications
192
+
193
+ def list_publications(params = {})
194
+ get_response "/publications?#{URI.encode_www_form(params.slice(:page))}"
195
+ end
196
+
197
+ def get_publication(uid)
198
+ get_response "/publications/#{uid}"
199
+ end
200
+
201
+ # Clones a publication into the workspace as a new image template.
202
+ def install_publication(uid)
203
+ post_response "/publications/#{uid}/install", {}
204
+ end
205
+
42
206
  # Batches
43
207
 
44
208
  def list_batches(params = {})
@@ -64,11 +228,11 @@ module Bannerbear
64
228
  end
65
229
 
66
230
  def create_webhook(payload = {})
67
- post_response "/webhooks", payload.slice(:name, :url, :resource, :event, :status, :scope, :templates)
231
+ post_response "/webhooks", payload.slice(:name, :url, :resource, :event, :status)
68
232
  end
69
233
 
70
234
  def update_webhook(uid, payload = {})
71
- patch_response "/webhooks/#{uid}", payload.slice(:name, :url, :resource, :event, :status, :scope, :templates)
235
+ patch_response "/webhooks/#{uid}", payload.slice(:name, :url, :resource, :event, :status)
72
236
  end
73
237
 
74
238
  def delete_webhook(uid)
@@ -183,6 +347,20 @@ module Bannerbear
183
347
  return body
184
348
  end
185
349
 
350
+ def upload_response(url, data, content_type)
351
+ response = HTTParty.post("#{BB_API_ENDPOINT}#{url}",
352
+ body: data,
353
+ timeout: 30,
354
+ headers: {
355
+ 'Authorization' => "Bearer #{@api_key}",
356
+ 'Content-Type' => content_type
357
+ }
358
+ )
359
+ body = JSON.parse(response.body)
360
+ return {"error" => body['message'], "code" => response.code} if response.code >= 400
361
+ return body
362
+ end
363
+
186
364
  def delete_response(url)
187
365
  response = HTTParty.delete("#{BB_API_ENDPOINT}#{url}",
188
366
  timeout: 5,
@@ -1,3 +1,3 @@
1
1
  module Bannerbear
2
- VERSION = "0.2.0"
2
+ VERSION = "0.4.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bannerbear
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jon Yongfook
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-05-13 00:00:00.000000000 Z
11
+ date: 2026-08-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: httparty