@@ -11,6 +11,10 @@ import type {
1111import type { GetWebhookResponseSuccess } from './interfaces/get-webhook.interface' ;
1212import type { ListWebhooksResponseSuccess } from './interfaces/list-webhooks.interface' ;
1313import type { RemoveWebhookResponseSuccess } from './interfaces/remove-webhook.interface' ;
14+ import type {
15+ UpdateWebhookOptions ,
16+ UpdateWebhookResponseSuccess ,
17+ } from './interfaces/update-webhook.interface' ;
1418
1519const mocks = vi . hoisted ( ( ) => {
1620 const verify = vi . fn ( ) ;
@@ -223,6 +227,237 @@ describe('Webhooks', () => {
223227 } ) ;
224228 } ) ;
225229
230+ describe ( 'update' , ( ) => {
231+ const webhookId = '430eed87-632a-4ea6-90db-0aace67ec228' ;
232+
233+ it ( 'updates all webhook fields' , async ( ) => {
234+ const payload : UpdateWebhookOptions = {
235+ endpoint : 'https://new.com/webhook' ,
236+ events : [ 'email.sent' , 'email.delivered' , 'email.bounced' ] ,
237+ status : 'disabled' ,
238+ } ;
239+ const response : UpdateWebhookResponseSuccess = {
240+ object : 'webhook' ,
241+ id : webhookId ,
242+ } ;
243+
244+ fetchMock . mockOnce ( JSON . stringify ( response ) , {
245+ status : 200 ,
246+ headers : {
247+ 'content-type' : 'application/json' ,
248+ Authorization : 'Bearer re_924b3rjh2387fbewf823' ,
249+ } ,
250+ } ) ;
251+
252+ const resend = new Resend ( 're_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop' ) ;
253+
254+ await expect (
255+ resend . webhooks . update ( webhookId , payload ) ,
256+ ) . resolves . toMatchInlineSnapshot ( `
257+ {
258+ "data": {
259+ "id": "430eed87-632a-4ea6-90db-0aace67ec228",
260+ "object": "webhook",
261+ },
262+ "error": null,
263+ }
264+ ` ) ;
265+ } ) ;
266+
267+ it ( 'updates only endpoint field' , async ( ) => {
268+ const payload : UpdateWebhookOptions = {
269+ endpoint : 'https://new.com/webhook' ,
270+ } ;
271+ const response : UpdateWebhookResponseSuccess = {
272+ object : 'webhook' ,
273+ id : webhookId ,
274+ } ;
275+
276+ fetchMock . mockOnce ( JSON . stringify ( response ) , {
277+ status : 200 ,
278+ headers : {
279+ 'content-type' : 'application/json' ,
280+ Authorization : 'Bearer re_924b3rjh2387fbewf823' ,
281+ } ,
282+ } ) ;
283+
284+ const resend = new Resend ( 're_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop' ) ;
285+
286+ await expect (
287+ resend . webhooks . update ( webhookId , payload ) ,
288+ ) . resolves . toMatchInlineSnapshot ( `
289+ {
290+ "data": {
291+ "id": "430eed87-632a-4ea6-90db-0aace67ec228",
292+ "object": "webhook",
293+ },
294+ "error": null,
295+ }
296+ ` ) ;
297+ } ) ;
298+
299+ it ( 'updates only events field' , async ( ) => {
300+ const payload : UpdateWebhookOptions = {
301+ events : [ 'email.sent' , 'email.delivered' ] ,
302+ } ;
303+ const response : UpdateWebhookResponseSuccess = {
304+ object : 'webhook' ,
305+ id : webhookId ,
306+ } ;
307+
308+ fetchMock . mockOnce ( JSON . stringify ( response ) , {
309+ status : 200 ,
310+ headers : {
311+ 'content-type' : 'application/json' ,
312+ Authorization : 'Bearer re_924b3rjh2387fbewf823' ,
313+ } ,
314+ } ) ;
315+
316+ const resend = new Resend ( 're_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop' ) ;
317+
318+ await expect (
319+ resend . webhooks . update ( webhookId , payload ) ,
320+ ) . resolves . toMatchInlineSnapshot ( `
321+ {
322+ "data": {
323+ "id": "430eed87-632a-4ea6-90db-0aace67ec228",
324+ "object": "webhook",
325+ },
326+ "error": null,
327+ }
328+ ` ) ;
329+ } ) ;
330+
331+ it ( 'updates only status field to disabled' , async ( ) => {
332+ const payload : UpdateWebhookOptions = {
333+ status : 'disabled' ,
334+ } ;
335+ const response : UpdateWebhookResponseSuccess = {
336+ object : 'webhook' ,
337+ id : webhookId ,
338+ } ;
339+
340+ fetchMock . mockOnce ( JSON . stringify ( response ) , {
341+ status : 200 ,
342+ headers : {
343+ 'content-type' : 'application/json' ,
344+ Authorization : 'Bearer re_924b3rjh2387fbewf823' ,
345+ } ,
346+ } ) ;
347+
348+ const resend = new Resend ( 're_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop' ) ;
349+
350+ await expect (
351+ resend . webhooks . update ( webhookId , payload ) ,
352+ ) . resolves . toMatchInlineSnapshot ( `
353+ {
354+ "data": {
355+ "id": "430eed87-632a-4ea6-90db-0aace67ec228",
356+ "object": "webhook",
357+ },
358+ "error": null,
359+ }
360+ ` ) ;
361+ } ) ;
362+
363+ it ( 'updates only status field to enabled' , async ( ) => {
364+ const payload : UpdateWebhookOptions = {
365+ status : 'enabled' ,
366+ } ;
367+ const response : UpdateWebhookResponseSuccess = {
368+ object : 'webhook' ,
369+ id : webhookId ,
370+ } ;
371+
372+ fetchMock . mockOnce ( JSON . stringify ( response ) , {
373+ status : 200 ,
374+ headers : {
375+ 'content-type' : 'application/json' ,
376+ Authorization : 'Bearer re_924b3rjh2387fbewf823' ,
377+ } ,
378+ } ) ;
379+
380+ const resend = new Resend ( 're_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop' ) ;
381+
382+ await expect (
383+ resend . webhooks . update ( webhookId , payload ) ,
384+ ) . resolves . toMatchInlineSnapshot ( `
385+ {
386+ "data": {
387+ "id": "430eed87-632a-4ea6-90db-0aace67ec228",
388+ "object": "webhook",
389+ },
390+ "error": null,
391+ }
392+ ` ) ;
393+ } ) ;
394+
395+ it ( 'handles empty payload' , async ( ) => {
396+ const payload : UpdateWebhookOptions = { } ;
397+ const response : UpdateWebhookResponseSuccess = {
398+ object : 'webhook' ,
399+ id : webhookId ,
400+ } ;
401+
402+ fetchMock . mockOnce ( JSON . stringify ( response ) , {
403+ status : 200 ,
404+ headers : {
405+ 'content-type' : 'application/json' ,
406+ Authorization : 'Bearer re_924b3rjh2387fbewf823' ,
407+ } ,
408+ } ) ;
409+
410+ const resend = new Resend ( 're_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop' ) ;
411+
412+ await expect (
413+ resend . webhooks . update ( webhookId , payload ) ,
414+ ) . resolves . toMatchInlineSnapshot ( `
415+ {
416+ "data": {
417+ "id": "430eed87-632a-4ea6-90db-0aace67ec228",
418+ "object": "webhook",
419+ },
420+ "error": null,
421+ }
422+ ` ) ;
423+ } ) ;
424+
425+ describe ( 'when webhook not found' , ( ) => {
426+ it ( 'returns error' , async ( ) => {
427+ const response : ErrorResponse = {
428+ name : 'not_found' ,
429+ message : 'Failed to update webhook endpoint' ,
430+ statusCode : 404 ,
431+ } ;
432+
433+ fetchMock . mockOnce ( JSON . stringify ( response ) , {
434+ status : 404 ,
435+ headers : {
436+ 'content-type' : 'application/json' ,
437+ Authorization : 'Bearer re_924b3rjh2387fbewf823' ,
438+ } ,
439+ } ) ;
440+
441+ const resend = new Resend ( 're_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop' ) ;
442+
443+ const result = resend . webhooks . update ( webhookId , {
444+ endpoint : 'https://new.com/webhook' ,
445+ } ) ;
446+
447+ await expect ( result ) . resolves . toMatchInlineSnapshot ( `
448+ {
449+ "data": null,
450+ "error": {
451+ "message": "Failed to update webhook endpoint",
452+ "name": "not_found",
453+ "statusCode": 404,
454+ },
455+ }
456+ ` ) ;
457+ } ) ;
458+ } ) ;
459+ } ) ;
460+
226461 describe ( 'remove' , ( ) => {
227462 it ( 'removes a webhook' , async ( ) => {
228463 const id = '430eed87-632a-4ea6-90db-0aace67ec228' ;
0 commit comments