[{"data":1,"prerenderedAt":2644},["ShallowReactive",2],{"docs-navigation":3,"\u002Fdocs\u002Fapi\u002Fwebhooks":202,"\u002Fdocs\u002Fapi\u002Fwebhooks-surround":2639},[4],{"title":5,"path":6,"stem":7,"children":8,"page":22},"Docs","\u002Fdocs","docs",[9,23,36,65,86,115,128,172,185],{"title":10,"path":11,"stem":12,"children":13,"page":22},"Getting started","\u002Fdocs\u002Fgetting-started","docs\u002F1.getting-started",[14,18],{"title":15,"path":16,"stem":17},"Quickstart","\u002Fdocs\u002Fgetting-started\u002Fquickstart","docs\u002F1.getting-started\u002F1.quickstart",{"title":19,"path":20,"stem":21},"Concepts","\u002Fdocs\u002Fgetting-started\u002Fconcepts","docs\u002F1.getting-started\u002F2.concepts",false,{"title":24,"path":25,"stem":26,"children":27,"page":22},"Sending domain","\u002Fdocs\u002Fdomains","docs\u002F2.domains",[28,32],{"title":29,"path":30,"stem":31},"Add your domain","\u002Fdocs\u002Fdomains\u002Fadd-your-domain","docs\u002F2.domains\u002F1.add-your-domain",{"title":33,"path":34,"stem":35},"Troubleshooting","\u002Fdocs\u002Fdomains\u002Ftroubleshooting","docs\u002F2.domains\u002F2.troubleshooting",{"title":37,"path":38,"stem":39,"children":40,"page":22},"Audience","\u002Fdocs\u002Faudience","docs\u002F3.audience",[41,45,49,53,57,61],{"title":42,"path":43,"stem":44},"Contacts","\u002Fdocs\u002Faudience\u002Fcontacts","docs\u002F3.audience\u002F1.contacts",{"title":46,"path":47,"stem":48},"Import contacts","\u002Fdocs\u002Faudience\u002Fimport","docs\u002F3.audience\u002F2.import",{"title":50,"path":51,"stem":52},"Lists","\u002Fdocs\u002Faudience\u002Flists","docs\u002F3.audience\u002F3.lists",{"title":54,"path":55,"stem":56},"Segments","\u002Fdocs\u002Faudience\u002Fsegments","docs\u002F3.audience\u002F4.segments",{"title":58,"path":59,"stem":60},"Signup forms","\u002Fdocs\u002Faudience\u002Fsignup-forms","docs\u002F3.audience\u002F5.signup-forms",{"title":62,"path":63,"stem":64},"Unsubscribe and preferences","\u002Fdocs\u002Faudience\u002Funsubscribe-and-preferences","docs\u002F3.audience\u002F6.unsubscribe-and-preferences",{"title":66,"path":67,"stem":68,"children":69,"page":22},"Sending emails","\u002Fdocs\u002Femails","docs\u002F4.emails",[70,74,78,82],{"title":71,"path":72,"stem":73},"Writing emails","\u002Fdocs\u002Femails\u002Fwriting-emails","docs\u002F4.emails\u002F1.writing-emails",{"title":75,"path":76,"stem":77},"Campaigns","\u002Fdocs\u002Femails\u002Fcampaigns","docs\u002F4.emails\u002F2.campaigns",{"title":79,"path":80,"stem":81},"Flows","\u002Fdocs\u002Femails\u002Fflows","docs\u002F4.emails\u002F3.flows",{"title":83,"path":84,"stem":85},"Transactional emails","\u002Fdocs\u002Femails\u002Ftransactional","docs\u002F4.emails\u002F4.transactional",{"title":87,"path":88,"stem":89,"children":90,"page":22},"Deliverability","\u002Fdocs\u002Fdeliverability","docs\u002F5.deliverability",[91,95,99,103,107,111],{"title":92,"path":93,"stem":94},"Overview","\u002Fdocs\u002Fdeliverability\u002Foverview","docs\u002F5.deliverability\u002F1.overview",{"title":96,"path":97,"stem":98},"Warm-up","\u002Fdocs\u002Fdeliverability\u002Fwarm-up","docs\u002F5.deliverability\u002F2.warm-up",{"title":100,"path":101,"stem":102},"Contacts under review","\u002Fdocs\u002Fdeliverability\u002Fimported-contacts","docs\u002F5.deliverability\u002F3.imported-contacts",{"title":104,"path":105,"stem":106},"Reputation","\u002Fdocs\u002Fdeliverability\u002Freputation","docs\u002F5.deliverability\u002F4.reputation",{"title":108,"path":109,"stem":110},"Bounces and suppressions","\u002Fdocs\u002Fdeliverability\u002Fbounces-and-suppressions","docs\u002F5.deliverability\u002F5.bounces-and-suppressions",{"title":112,"path":113,"stem":114},"Deliverability report","\u002Fdocs\u002Fdeliverability\u002Fdeliverability-report","docs\u002F5.deliverability\u002F6.deliverability-report",{"title":116,"path":117,"stem":118,"children":119,"page":22},"AI","\u002Fdocs\u002Fai","docs\u002F6.ai",[120,124],{"title":121,"path":122,"stem":123},"Assistant","\u002Fdocs\u002Fai\u002Fassistant","docs\u002F6.ai\u002F1.assistant",{"title":125,"path":126,"stem":127},"Connect your agent (MCP)","\u002Fdocs\u002Fai\u002Fmcp","docs\u002F6.ai\u002F2.mcp",{"title":129,"path":130,"stem":131,"children":132,"page":22},"API reference","\u002Fdocs\u002Fapi","docs\u002F7.api",[133,137,141,145,148,152,156,160,164,168],{"title":134,"path":135,"stem":136},"Introduction","\u002Fdocs\u002Fapi\u002Fintroduction","docs\u002F7.api\u002F01.introduction",{"title":138,"path":139,"stem":140},"Emails","\u002Fdocs\u002Fapi\u002Femails","docs\u002F7.api\u002F02.emails",{"title":142,"path":143,"stem":144},"Templates","\u002Fdocs\u002Fapi\u002Ftemplates","docs\u002F7.api\u002F03.templates",{"title":42,"path":146,"stem":147},"\u002Fdocs\u002Fapi\u002Fcontacts","docs\u002F7.api\u002F04.contacts",{"title":149,"path":150,"stem":151},"Events","\u002Fdocs\u002Fapi\u002Fevents","docs\u002F7.api\u002F05.events",{"title":153,"path":154,"stem":155},"Lists and segments","\u002Fdocs\u002Fapi\u002Flists-and-segments","docs\u002F7.api\u002F06.lists-and-segments",{"title":157,"path":158,"stem":159},"Campaigns and flows","\u002Fdocs\u002Fapi\u002Fcampaigns-and-flows","docs\u002F7.api\u002F07.campaigns-and-flows",{"title":161,"path":162,"stem":163},"Domains and suppressions","\u002Fdocs\u002Fapi\u002Fdomains-and-suppressions","docs\u002F7.api\u002F08.domains-and-suppressions",{"title":165,"path":166,"stem":167},"Spaces","\u002Fdocs\u002Fapi\u002Fspaces","docs\u002F7.api\u002F09.spaces",{"title":169,"path":170,"stem":171},"Webhooks","\u002Fdocs\u002Fapi\u002Fwebhooks","docs\u002F7.api\u002F10.webhooks",{"title":173,"path":174,"stem":175,"children":176,"page":22},"Account & billing","\u002Fdocs\u002Faccount","docs\u002F8.account",[177,181],{"title":178,"path":179,"stem":180},"Spaces and members","\u002Fdocs\u002Faccount\u002Fspaces-and-members","docs\u002F8.account\u002F1.spaces-and-members",{"title":182,"path":183,"stem":184},"Billing","\u002Fdocs\u002Faccount\u002Fbilling","docs\u002F8.account\u002F2.billing",{"title":186,"path":187,"stem":188,"children":189,"page":22},"Guides","\u002Fdocs\u002Fguides","docs\u002F9.guides",[190,194,198],{"title":191,"path":192,"stem":193},"Welcome new subscribers","\u002Fdocs\u002Fguides\u002Fwelcome-sequence","docs\u002F9.guides\u002F1.welcome-sequence",{"title":195,"path":196,"stem":197},"Onboarding with events","\u002Fdocs\u002Fguides\u002Fonboarding-with-events","docs\u002F9.guides\u002F2.onboarding-with-events",{"title":199,"path":200,"stem":201},"Migrate from another tool","\u002Fdocs\u002Fguides\u002Fmigrate-from-another-tool","docs\u002F9.guides\u002F3.migrate-from-another-tool",{"id":203,"title":169,"body":204,"description":2633,"extension":2634,"links":2635,"meta":2636,"navigation":1306,"path":170,"seo":2637,"stem":171,"__hash__":2638},"docs\u002Fdocs\u002F7.api\u002F10.webhooks.md",{"type":205,"value":206,"toc":2619},"minimark",[207,211,219,224,336,339,343,346,481,486,750,861,865,1045,1121,1125,1198,1202,1205,1254,2022,2032,2036,2043,2058,2061,2100,2104,2295,2299,2304,2308,2313,2316,2346,2422,2528,2538,2579,2583,2615],[208,209,210],"p",{},"Webhooks notify your server of email and contact events within seconds.",[208,212,213,214,218],{},"Create endpoints with the routes below, or in ",[215,216,217],"strong",{},"Settings › Webhooks"," (admins and owners), where you can also send a test event, see each delivery and retry it.",[220,221,223],"h2",{"id":222},"event-types","Event types",[225,226,227,240],"table",{},[228,229,230],"thead",{},[231,232,233,237],"tr",{},[234,235,236],"th",{},"Type",[234,238,239],{},"Sent when",[241,242,243,255,265,283,293,303,316,326],"tbody",{},[231,244,245,252],{},[246,247,248],"td",{},[249,250,251],"code",{},"email.sent",[246,253,254],{},"Kula sent the email; delivery to the inbox comes next",[231,256,257,262],{},[246,258,259],{},[249,260,261],{},"email.delivered",[246,263,264],{},"The receiving server accepted the email",[231,266,267,272],{},[246,268,269],{},[249,270,271],{},"email.bounced",[246,273,274,275,278,279],{},"The email bounced. ",[249,276,277],{},"bounce_type"," tells whether it is permanent. ",[280,281,282],"a",{"href":109},"Bounces explained",[231,284,285,290],{},[246,286,287],{},[249,288,289],{},"email.complained",[246,291,292],{},"The recipient marked the email as spam",[231,294,295,300],{},[246,296,297],{},[249,298,299],{},"email.opened",[246,301,302],{},"First open by a person (privacy proxies and scanners are ignored)",[231,304,305,310],{},[246,306,307],{},[249,308,309],{},"email.clicked",[246,311,312,313],{},"First click by a person, with the ",[249,314,315],{},"url",[231,317,318,323],{},[246,319,320],{},[249,321,322],{},"contact.subscribed",[246,324,325],{},"A contact joined a list (sign-up form, confirmed double opt-in, API, dashboard). Imports don't send it",[231,327,328,333],{},[246,329,330],{},[249,331,332],{},"contact.unsubscribed",[246,334,335],{},"A contact left a list, or every list",[208,337,338],{},"Email events cover every email of the space: transactional, campaigns and flows.",[220,340,342],{"id":341},"payload","Payload",[208,344,345],{},"Every event has the same envelope:",[347,348,353],"pre",{"className":349,"code":350,"language":351,"meta":352,"style":352},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{\n  \"id\": \"evt_5f0c7d2b9a8e4f1c8b3a6d9e2f1c0b7a\",\n  \"type\": \"email.bounced\",\n  \"created_at\": \"2026-10-17T09:30:04Z\",\n  \"space_id\": \"3f1c9a52-7b0e-4d8a-9c61-2e5f8b7a4d10\",\n  \"data\": { ... }\n}\n","json","",[249,354,355,364,392,412,433,454,476],{"__ignoreMap":352},[356,357,360],"span",{"class":358,"line":359},"line",1,[356,361,363],{"class":362},"sMK4o","{\n",[356,365,367,370,374,377,380,383,387,389],{"class":358,"line":366},2,[356,368,369],{"class":362},"  \"",[356,371,373],{"class":372},"spNyl","id",[356,375,376],{"class":362},"\"",[356,378,379],{"class":362},":",[356,381,382],{"class":362}," \"",[356,384,386],{"class":385},"sfazB","evt_5f0c7d2b9a8e4f1c8b3a6d9e2f1c0b7a",[356,388,376],{"class":362},[356,390,391],{"class":362},",\n",[356,393,395,397,400,402,404,406,408,410],{"class":358,"line":394},3,[356,396,369],{"class":362},[356,398,399],{"class":372},"type",[356,401,376],{"class":362},[356,403,379],{"class":362},[356,405,382],{"class":362},[356,407,271],{"class":385},[356,409,376],{"class":362},[356,411,391],{"class":362},[356,413,415,417,420,422,424,426,429,431],{"class":358,"line":414},4,[356,416,369],{"class":362},[356,418,419],{"class":372},"created_at",[356,421,376],{"class":362},[356,423,379],{"class":362},[356,425,382],{"class":362},[356,427,428],{"class":385},"2026-10-17T09:30:04Z",[356,430,376],{"class":362},[356,432,391],{"class":362},[356,434,436,438,441,443,445,447,450,452],{"class":358,"line":435},5,[356,437,369],{"class":362},[356,439,440],{"class":372},"space_id",[356,442,376],{"class":362},[356,444,379],{"class":362},[356,446,382],{"class":362},[356,448,449],{"class":385},"3f1c9a52-7b0e-4d8a-9c61-2e5f8b7a4d10",[356,451,376],{"class":362},[356,453,391],{"class":362},[356,455,457,459,462,464,466,469,473],{"class":358,"line":456},6,[356,458,369],{"class":362},[356,460,461],{"class":372},"data",[356,463,376],{"class":362},[356,465,379],{"class":362},[356,467,468],{"class":362}," {",[356,470,472],{"class":471},"sTEyZ"," ... ",[356,474,475],{"class":362},"}\n",[356,477,479],{"class":358,"line":478},7,[356,480,475],{"class":362},[482,483,485],"h3",{"id":484},"email-events","Email events",[347,487,489],{"className":349,"code":488,"language":351,"meta":352,"style":352},"{\n  \"id\": \"evt_5f0c7d2b9a8e4f1c8b3a6d9e2f1c0b7a\",\n  \"type\": \"email.bounced\",\n  \"created_at\": \"2026-10-17T09:30:04Z\",\n  \"space_id\": \"3f1c9a52-7b0e-4d8a-9c61-2e5f8b7a4d10\",\n  \"data\": {\n    \"message_id\": \"8f6c2a1e-3b4d-4e5f-9a0b-1c2d3e4f5a6b\",\n    \"to\": \"ada@example.com\",\n    \"subject\": \"Reset your password\",\n    \"template_id\": \"c41d7e2a-9b8f-4a6e-b3d2-5f1e0a9c8b7d\",\n    \"tags\": { \"user_id\": \"usr_1042\" },\n    \"bounce_type\": \"HARD\",\n    \"detail\": \"550 5.1.1 The email account that you tried to reach does not exist\"\n  }\n}\n",[249,490,491,495,513,531,549,567,580,602,623,644,665,699,719,739,745],{"__ignoreMap":352},[356,492,493],{"class":358,"line":359},[356,494,363],{"class":362},[356,496,497,499,501,503,505,507,509,511],{"class":358,"line":366},[356,498,369],{"class":362},[356,500,373],{"class":372},[356,502,376],{"class":362},[356,504,379],{"class":362},[356,506,382],{"class":362},[356,508,386],{"class":385},[356,510,376],{"class":362},[356,512,391],{"class":362},[356,514,515,517,519,521,523,525,527,529],{"class":358,"line":394},[356,516,369],{"class":362},[356,518,399],{"class":372},[356,520,376],{"class":362},[356,522,379],{"class":362},[356,524,382],{"class":362},[356,526,271],{"class":385},[356,528,376],{"class":362},[356,530,391],{"class":362},[356,532,533,535,537,539,541,543,545,547],{"class":358,"line":414},[356,534,369],{"class":362},[356,536,419],{"class":372},[356,538,376],{"class":362},[356,540,379],{"class":362},[356,542,382],{"class":362},[356,544,428],{"class":385},[356,546,376],{"class":362},[356,548,391],{"class":362},[356,550,551,553,555,557,559,561,563,565],{"class":358,"line":435},[356,552,369],{"class":362},[356,554,440],{"class":372},[356,556,376],{"class":362},[356,558,379],{"class":362},[356,560,382],{"class":362},[356,562,449],{"class":385},[356,564,376],{"class":362},[356,566,391],{"class":362},[356,568,569,571,573,575,577],{"class":358,"line":456},[356,570,369],{"class":362},[356,572,461],{"class":372},[356,574,376],{"class":362},[356,576,379],{"class":362},[356,578,579],{"class":362}," {\n",[356,581,582,585,589,591,593,595,598,600],{"class":358,"line":478},[356,583,584],{"class":362},"    \"",[356,586,588],{"class":587},"sBMFI","message_id",[356,590,376],{"class":362},[356,592,379],{"class":362},[356,594,382],{"class":362},[356,596,597],{"class":385},"8f6c2a1e-3b4d-4e5f-9a0b-1c2d3e4f5a6b",[356,599,376],{"class":362},[356,601,391],{"class":362},[356,603,605,607,610,612,614,616,619,621],{"class":358,"line":604},8,[356,606,584],{"class":362},[356,608,609],{"class":587},"to",[356,611,376],{"class":362},[356,613,379],{"class":362},[356,615,382],{"class":362},[356,617,618],{"class":385},"ada@example.com",[356,620,376],{"class":362},[356,622,391],{"class":362},[356,624,626,628,631,633,635,637,640,642],{"class":358,"line":625},9,[356,627,584],{"class":362},[356,629,630],{"class":587},"subject",[356,632,376],{"class":362},[356,634,379],{"class":362},[356,636,382],{"class":362},[356,638,639],{"class":385},"Reset your password",[356,641,376],{"class":362},[356,643,391],{"class":362},[356,645,647,649,652,654,656,658,661,663],{"class":358,"line":646},10,[356,648,584],{"class":362},[356,650,651],{"class":587},"template_id",[356,653,376],{"class":362},[356,655,379],{"class":362},[356,657,382],{"class":362},[356,659,660],{"class":385},"c41d7e2a-9b8f-4a6e-b3d2-5f1e0a9c8b7d",[356,662,376],{"class":362},[356,664,391],{"class":362},[356,666,668,670,673,675,677,679,681,685,687,689,691,694,696],{"class":358,"line":667},11,[356,669,584],{"class":362},[356,671,672],{"class":587},"tags",[356,674,376],{"class":362},[356,676,379],{"class":362},[356,678,468],{"class":362},[356,680,382],{"class":362},[356,682,684],{"class":683},"sbssI","user_id",[356,686,376],{"class":362},[356,688,379],{"class":362},[356,690,382],{"class":362},[356,692,693],{"class":385},"usr_1042",[356,695,376],{"class":362},[356,697,698],{"class":362}," },\n",[356,700,702,704,706,708,710,712,715,717],{"class":358,"line":701},12,[356,703,584],{"class":362},[356,705,277],{"class":587},[356,707,376],{"class":362},[356,709,379],{"class":362},[356,711,382],{"class":362},[356,713,714],{"class":385},"HARD",[356,716,376],{"class":362},[356,718,391],{"class":362},[356,720,722,724,727,729,731,733,736],{"class":358,"line":721},13,[356,723,584],{"class":362},[356,725,726],{"class":587},"detail",[356,728,376],{"class":362},[356,730,379],{"class":362},[356,732,382],{"class":362},[356,734,735],{"class":385},"550 5.1.1 The email account that you tried to reach does not exist",[356,737,738],{"class":362},"\"\n",[356,740,742],{"class":358,"line":741},14,[356,743,744],{"class":362},"  }\n",[356,746,748],{"class":358,"line":747},15,[356,749,475],{"class":362},[225,751,752,762],{},[228,753,754],{},[231,755,756,759],{},[234,757,758],{},"Field",[234,760,761],{},"Present",[241,763,764,778,790,805,817,839,850],{},[231,765,766,770],{},[246,767,768],{},[249,769,588],{},[246,771,772,773],{},"Always: the id returned by ",[280,774,775],{"href":139},[249,776,777],{},"POST \u002Fv1\u002Femails",[231,779,780,787],{},[246,781,782,784,785],{},[249,783,609],{},", ",[249,786,630],{},[246,788,789],{},"Always",[231,791,792,802],{},[246,793,794,784,797,784,800],{},[249,795,796],{},"campaign_id",[249,798,799],{},"flow_id",[249,801,651],{},[246,803,804],{},"When the email came from a campaign, a flow or a template",[231,806,807,811],{},[246,808,809],{},[249,810,672],{},[246,812,813,814,816],{},"Always: the ",[249,815,672],{}," you set when sending (empty object otherwise)",[231,818,819,823],{},[246,820,821],{},[249,822,277],{},[246,824,825,827,828,830,831,834,835,838],{},[249,826,271],{},": ",[249,829,714],{}," (the address does not exist), ",[249,832,833],{},"SOFT"," (temporary, such as a full inbox) or ",[249,836,837],{},"BLOCKED"," (refused by the receiving server)",[231,840,841,845],{},[246,842,843],{},[249,844,726],{},[246,846,847,849],{},[249,848,271],{},": the server's answer",[231,851,852,856],{},[246,853,854],{},[249,855,315],{},[246,857,858,860],{},[249,859,309],{},": the link clicked",[482,862,864],{"id":863},"contact-events","Contact events",[347,866,868],{"className":349,"code":867,"language":351,"meta":352,"style":352},"{\n  \"id\": \"evt_9a1b2c3d4e5f60718293a4b5c6d7e8f9\",\n  \"type\": \"contact.unsubscribed\",\n  \"created_at\": \"2026-10-17T11:02:00Z\",\n  \"space_id\": \"3f1c9a52-7b0e-4d8a-9c61-2e5f8b7a4d10\",\n  \"data\": {\n    \"contact_id\": \"0d9e8f7a-6b5c-4d3e-2f1a-0b9c8d7e6f5a\",\n    \"email\": \"ada@example.com\",\n    \"list_id\": \"5a8e2c1f-0b3d-4e6a-9f7c-2d1e8b4a6c3f\",\n    \"reason\": \"USER\"\n  }\n}\n",[249,869,870,874,893,911,930,948,960,980,999,1019,1037,1041],{"__ignoreMap":352},[356,871,872],{"class":358,"line":359},[356,873,363],{"class":362},[356,875,876,878,880,882,884,886,889,891],{"class":358,"line":366},[356,877,369],{"class":362},[356,879,373],{"class":372},[356,881,376],{"class":362},[356,883,379],{"class":362},[356,885,382],{"class":362},[356,887,888],{"class":385},"evt_9a1b2c3d4e5f60718293a4b5c6d7e8f9",[356,890,376],{"class":362},[356,892,391],{"class":362},[356,894,895,897,899,901,903,905,907,909],{"class":358,"line":394},[356,896,369],{"class":362},[356,898,399],{"class":372},[356,900,376],{"class":362},[356,902,379],{"class":362},[356,904,382],{"class":362},[356,906,332],{"class":385},[356,908,376],{"class":362},[356,910,391],{"class":362},[356,912,913,915,917,919,921,923,926,928],{"class":358,"line":414},[356,914,369],{"class":362},[356,916,419],{"class":372},[356,918,376],{"class":362},[356,920,379],{"class":362},[356,922,382],{"class":362},[356,924,925],{"class":385},"2026-10-17T11:02:00Z",[356,927,376],{"class":362},[356,929,391],{"class":362},[356,931,932,934,936,938,940,942,944,946],{"class":358,"line":435},[356,933,369],{"class":362},[356,935,440],{"class":372},[356,937,376],{"class":362},[356,939,379],{"class":362},[356,941,382],{"class":362},[356,943,449],{"class":385},[356,945,376],{"class":362},[356,947,391],{"class":362},[356,949,950,952,954,956,958],{"class":358,"line":456},[356,951,369],{"class":362},[356,953,461],{"class":372},[356,955,376],{"class":362},[356,957,379],{"class":362},[356,959,579],{"class":362},[356,961,962,964,967,969,971,973,976,978],{"class":358,"line":478},[356,963,584],{"class":362},[356,965,966],{"class":587},"contact_id",[356,968,376],{"class":362},[356,970,379],{"class":362},[356,972,382],{"class":362},[356,974,975],{"class":385},"0d9e8f7a-6b5c-4d3e-2f1a-0b9c8d7e6f5a",[356,977,376],{"class":362},[356,979,391],{"class":362},[356,981,982,984,987,989,991,993,995,997],{"class":358,"line":604},[356,983,584],{"class":362},[356,985,986],{"class":587},"email",[356,988,376],{"class":362},[356,990,379],{"class":362},[356,992,382],{"class":362},[356,994,618],{"class":385},[356,996,376],{"class":362},[356,998,391],{"class":362},[356,1000,1001,1003,1006,1008,1010,1012,1015,1017],{"class":358,"line":625},[356,1002,584],{"class":362},[356,1004,1005],{"class":587},"list_id",[356,1007,376],{"class":362},[356,1009,379],{"class":362},[356,1011,382],{"class":362},[356,1013,1014],{"class":385},"5a8e2c1f-0b3d-4e6a-9f7c-2d1e8b4a6c3f",[356,1016,376],{"class":362},[356,1018,391],{"class":362},[356,1020,1021,1023,1026,1028,1030,1032,1035],{"class":358,"line":646},[356,1022,584],{"class":362},[356,1024,1025],{"class":587},"reason",[356,1027,376],{"class":362},[356,1029,379],{"class":362},[356,1031,382],{"class":362},[356,1033,1034],{"class":385},"USER",[356,1036,738],{"class":362},[356,1038,1039],{"class":358,"line":667},[356,1040,744],{"class":362},[356,1042,1043],{"class":358,"line":701},[356,1044,475],{"class":362},[225,1046,1047,1056],{},[228,1048,1049],{},[231,1050,1051,1053],{},[234,1052,758],{},[234,1054,1055],{},"Values",[241,1057,1058,1074,1098],{},[231,1059,1060,1064],{},[246,1061,1062],{},[249,1063,1005],{},[246,1065,1066,1067,1070,1071,1073],{},"The list joined or left. ",[249,1068,1069],{},"null"," on ",[249,1072,332],{}," when the contact left every list",[231,1075,1076,1081],{},[246,1077,1078],{},[249,1079,1080],{},"source",[246,1082,1083,827,1085,784,1088,784,1091,1094,1095],{},[249,1084,322],{},[249,1086,1087],{},"FORM",[249,1089,1090],{},"API",[249,1092,1093],{},"DASHBOARD"," or ",[249,1096,1097],{},"MCP",[231,1099,1100,1104],{},[246,1101,1102],{},[249,1103,1025],{},[246,1105,1106,827,1108,1110,1111,784,1113,784,1115,1094,1118],{},[249,1107,332],{},[249,1109,1034],{}," (unsubscribe link, one-click button or preferences page), ",[249,1112,1090],{},[249,1114,1093],{},[249,1116,1117],{},"BOUNCE",[249,1119,1120],{},"COMPLAINT",[220,1122,1124],{"id":1123},"headers","Headers",[225,1126,1127,1137],{},[228,1128,1129],{},[231,1130,1131,1134],{},[234,1132,1133],{},"Header",[234,1135,1136],{},"Value",[241,1138,1139,1151,1163,1175,1185],{},[231,1140,1141,1146],{},[246,1142,1143],{},[249,1144,1145],{},"Content-Type",[246,1147,1148],{},[249,1149,1150],{},"application\u002Fjson",[231,1152,1153,1158],{},[246,1154,1155],{},[249,1156,1157],{},"User-Agent",[246,1159,1160],{},[249,1161,1162],{},"Mailler-Webhooks\u002F1.0",[231,1164,1165,1170],{},[246,1166,1167],{},[249,1168,1169],{},"Mailler-Event",[246,1171,1172,1173],{},"The event type, such as ",[249,1174,271],{},[231,1176,1177,1182],{},[246,1178,1179],{},[249,1180,1181],{},"Mailler-Delivery-Id",[246,1183,1184],{},"Id of this delivery, the same on every retry",[231,1186,1187,1192],{},[246,1188,1189],{},[249,1190,1191],{},"Mailler-Signature",[246,1193,1194,1197],{},[249,1195,1196],{},"t=\u003Cunix seconds>,v1=\u003Csignature>",", see below",[220,1199,1201],{"id":1200},"verify-the-signature","Verify the signature",[208,1203,1204],{},"Check the signature to make sure a request comes from Kula:",[1206,1207,1208,1216,1233,1248],"ol",{},[1209,1210,1211,1212,1215],"li",{},"Read the ",[215,1213,1214],{},"raw"," request body, before any JSON parsing.",[1209,1217,1218,1219,1070,1221,1224,1225,1228,1229,1232],{},"Split ",[249,1220,1191],{},[249,1222,1223],{},","," to get ",[249,1226,1227],{},"t"," (a timestamp) and ",[249,1230,1231],{},"v1"," (the signature).",[1209,1234,1235,1236,1239,1240,1243,1244,1247],{},"Compute an HMAC-SHA256 of ",[249,1237,1238],{},"\u003Ct>.\u003Craw body>"," with your endpoint secret as the key, in lowercase hexadecimal. The key is the ",[215,1241,1242],{},"whole"," secret, ",[249,1245,1246],{},"whsec_"," prefix included.",[1209,1249,1250,1251,1253],{},"Compare it to ",[249,1252,1231],{}," in constant time, and reject timestamps more than 5 minutes old to block replays.",[1255,1256,1257,1909],"code-group",{},[347,1258,1263],{"className":1259,"code":1260,"filename":1261,"language":1262,"meta":352,"style":352},"language-js shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import crypto from 'node:crypto'\nimport express from 'express'\n\nconst app = express()\nconst secret = process.env.KULA_WEBHOOK_SECRET \u002F\u002F whsec_...\n\napp.post('\u002Fwebhooks\u002Fkula', express.raw({ type: 'application\u002Fjson' }), (req, res) => {\n  const header = req.get('Mailler-Signature') ?? ''\n  const parts = Object.fromEntries(header.split(',').map(p => p.split('=', 2)))\n  const timestamp = Number(parts.t)\n  const body = req.body.toString('utf8')\n\n  const expected = crypto.createHmac('sha256', secret).update(`${timestamp}.${body}`).digest('hex')\n  const valid = parts.v1?.length === expected.length\n    && crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected))\n  const fresh = Math.abs(Date.now() \u002F 1000 - timestamp) \u003C= 300\n\n  if (!valid || !fresh) return res.sendStatus(400)\n\n  const event = JSON.parse(body)\n  \u002F\u002F Queue the work, then answer fast\n  res.sendStatus(200)\n})\n","Node.js (Express)","js",[249,1264,1265,1286,1302,1308,1326,1353,1357,1430,1466,1537,1561,1593,1597,1674,1705,1753,1803,1808,1850,1855,1879,1885,1902],{"__ignoreMap":352},[356,1266,1267,1271,1274,1277,1280,1283],{"class":358,"line":359},[356,1268,1270],{"class":1269},"s7zQu","import",[356,1272,1273],{"class":471}," crypto ",[356,1275,1276],{"class":1269},"from",[356,1278,1279],{"class":362}," '",[356,1281,1282],{"class":385},"node:crypto",[356,1284,1285],{"class":362},"'\n",[356,1287,1288,1290,1293,1295,1297,1300],{"class":358,"line":366},[356,1289,1270],{"class":1269},[356,1291,1292],{"class":471}," express ",[356,1294,1276],{"class":1269},[356,1296,1279],{"class":362},[356,1298,1299],{"class":385},"express",[356,1301,1285],{"class":362},[356,1303,1304],{"class":358,"line":394},[356,1305,1307],{"emptyLinePlaceholder":1306},true,"\n",[356,1309,1310,1313,1316,1319,1323],{"class":358,"line":414},[356,1311,1312],{"class":372},"const",[356,1314,1315],{"class":471}," app ",[356,1317,1318],{"class":362},"=",[356,1320,1322],{"class":1321},"s2Zo4"," express",[356,1324,1325],{"class":471},"()\n",[356,1327,1328,1330,1333,1335,1338,1341,1344,1346,1349],{"class":358,"line":435},[356,1329,1312],{"class":372},[356,1331,1332],{"class":471}," secret ",[356,1334,1318],{"class":362},[356,1336,1337],{"class":471}," process",[356,1339,1340],{"class":362},".",[356,1342,1343],{"class":471},"env",[356,1345,1340],{"class":362},[356,1347,1348],{"class":471},"KULA_WEBHOOK_SECRET ",[356,1350,1352],{"class":1351},"sHwdD","\u002F\u002F whsec_...\n",[356,1354,1355],{"class":358,"line":456},[356,1356,1307],{"emptyLinePlaceholder":1306},[356,1358,1359,1362,1364,1367,1370,1373,1376,1378,1380,1382,1384,1386,1388,1391,1395,1397,1399,1401,1403,1406,1409,1411,1414,1418,1420,1423,1425,1428],{"class":358,"line":478},[356,1360,1361],{"class":471},"app",[356,1363,1340],{"class":362},[356,1365,1366],{"class":1321},"post",[356,1368,1369],{"class":471},"(",[356,1371,1372],{"class":362},"'",[356,1374,1375],{"class":385},"\u002Fwebhooks\u002Fkula",[356,1377,1372],{"class":362},[356,1379,1223],{"class":362},[356,1381,1322],{"class":471},[356,1383,1340],{"class":362},[356,1385,1214],{"class":1321},[356,1387,1369],{"class":471},[356,1389,1390],{"class":362},"{",[356,1392,1394],{"class":1393},"swJcz"," type",[356,1396,379],{"class":362},[356,1398,1279],{"class":362},[356,1400,1150],{"class":385},[356,1402,1372],{"class":362},[356,1404,1405],{"class":362}," }",[356,1407,1408],{"class":471},")",[356,1410,1223],{"class":362},[356,1412,1413],{"class":362}," (",[356,1415,1417],{"class":1416},"sHdIc","req",[356,1419,1223],{"class":362},[356,1421,1422],{"class":1416}," res",[356,1424,1408],{"class":362},[356,1426,1427],{"class":372}," =>",[356,1429,579],{"class":362},[356,1431,1432,1435,1438,1441,1444,1446,1449,1451,1453,1455,1457,1460,1463],{"class":358,"line":604},[356,1433,1434],{"class":372},"  const",[356,1436,1437],{"class":471}," header",[356,1439,1440],{"class":362}," =",[356,1442,1443],{"class":471}," req",[356,1445,1340],{"class":362},[356,1447,1448],{"class":1321},"get",[356,1450,1369],{"class":1393},[356,1452,1372],{"class":362},[356,1454,1191],{"class":385},[356,1456,1372],{"class":362},[356,1458,1459],{"class":1393},") ",[356,1461,1462],{"class":362},"??",[356,1464,1465],{"class":362}," ''\n",[356,1467,1468,1470,1473,1475,1478,1480,1483,1485,1488,1490,1493,1495,1497,1499,1501,1503,1505,1508,1510,1512,1514,1517,1519,1521,1523,1525,1527,1529,1531,1534],{"class":358,"line":625},[356,1469,1434],{"class":372},[356,1471,1472],{"class":471}," parts",[356,1474,1440],{"class":362},[356,1476,1477],{"class":471}," Object",[356,1479,1340],{"class":362},[356,1481,1482],{"class":1321},"fromEntries",[356,1484,1369],{"class":1393},[356,1486,1487],{"class":471},"header",[356,1489,1340],{"class":362},[356,1491,1492],{"class":1321},"split",[356,1494,1369],{"class":1393},[356,1496,1372],{"class":362},[356,1498,1223],{"class":385},[356,1500,1372],{"class":362},[356,1502,1408],{"class":1393},[356,1504,1340],{"class":362},[356,1506,1507],{"class":1321},"map",[356,1509,1369],{"class":1393},[356,1511,208],{"class":1416},[356,1513,1427],{"class":372},[356,1515,1516],{"class":471}," p",[356,1518,1340],{"class":362},[356,1520,1492],{"class":1321},[356,1522,1369],{"class":1393},[356,1524,1372],{"class":362},[356,1526,1318],{"class":385},[356,1528,1372],{"class":362},[356,1530,1223],{"class":362},[356,1532,1533],{"class":683}," 2",[356,1535,1536],{"class":1393},")))\n",[356,1538,1539,1541,1544,1546,1549,1551,1554,1556,1558],{"class":358,"line":646},[356,1540,1434],{"class":372},[356,1542,1543],{"class":471}," timestamp",[356,1545,1440],{"class":362},[356,1547,1548],{"class":1321}," Number",[356,1550,1369],{"class":1393},[356,1552,1553],{"class":471},"parts",[356,1555,1340],{"class":362},[356,1557,1227],{"class":471},[356,1559,1560],{"class":1393},")\n",[356,1562,1563,1565,1568,1570,1572,1574,1577,1579,1582,1584,1586,1589,1591],{"class":358,"line":667},[356,1564,1434],{"class":372},[356,1566,1567],{"class":471}," body",[356,1569,1440],{"class":362},[356,1571,1443],{"class":471},[356,1573,1340],{"class":362},[356,1575,1576],{"class":471},"body",[356,1578,1340],{"class":362},[356,1580,1581],{"class":1321},"toString",[356,1583,1369],{"class":1393},[356,1585,1372],{"class":362},[356,1587,1588],{"class":385},"utf8",[356,1590,1372],{"class":362},[356,1592,1560],{"class":1393},[356,1594,1595],{"class":358,"line":701},[356,1596,1307],{"emptyLinePlaceholder":1306},[356,1598,1599,1601,1604,1606,1609,1611,1614,1616,1618,1621,1623,1625,1628,1630,1632,1635,1637,1640,1643,1646,1648,1651,1653,1656,1658,1660,1663,1665,1667,1670,1672],{"class":358,"line":721},[356,1600,1434],{"class":372},[356,1602,1603],{"class":471}," expected",[356,1605,1440],{"class":362},[356,1607,1608],{"class":471}," crypto",[356,1610,1340],{"class":362},[356,1612,1613],{"class":1321},"createHmac",[356,1615,1369],{"class":1393},[356,1617,1372],{"class":362},[356,1619,1620],{"class":385},"sha256",[356,1622,1372],{"class":362},[356,1624,1223],{"class":362},[356,1626,1627],{"class":471}," secret",[356,1629,1408],{"class":1393},[356,1631,1340],{"class":362},[356,1633,1634],{"class":1321},"update",[356,1636,1369],{"class":1393},[356,1638,1639],{"class":362},"`${",[356,1641,1642],{"class":471},"timestamp",[356,1644,1645],{"class":362},"}",[356,1647,1340],{"class":385},[356,1649,1650],{"class":362},"${",[356,1652,1576],{"class":471},[356,1654,1655],{"class":362},"}`",[356,1657,1408],{"class":1393},[356,1659,1340],{"class":362},[356,1661,1662],{"class":1321},"digest",[356,1664,1369],{"class":1393},[356,1666,1372],{"class":362},[356,1668,1669],{"class":385},"hex",[356,1671,1372],{"class":362},[356,1673,1560],{"class":1393},[356,1675,1676,1678,1681,1683,1685,1687,1689,1692,1695,1698,1700,1702],{"class":358,"line":741},[356,1677,1434],{"class":372},[356,1679,1680],{"class":471}," valid",[356,1682,1440],{"class":362},[356,1684,1472],{"class":471},[356,1686,1340],{"class":362},[356,1688,1231],{"class":471},[356,1690,1691],{"class":362},"?.",[356,1693,1694],{"class":471},"length",[356,1696,1697],{"class":362}," ===",[356,1699,1603],{"class":471},[356,1701,1340],{"class":362},[356,1703,1704],{"class":471},"length\n",[356,1706,1707,1710,1712,1714,1717,1719,1722,1724,1726,1728,1730,1732,1734,1736,1738,1741,1743,1745,1747,1750],{"class":358,"line":747},[356,1708,1709],{"class":362},"    &&",[356,1711,1608],{"class":471},[356,1713,1340],{"class":362},[356,1715,1716],{"class":1321},"timingSafeEqual",[356,1718,1369],{"class":1393},[356,1720,1721],{"class":471},"Buffer",[356,1723,1340],{"class":362},[356,1725,1276],{"class":1321},[356,1727,1369],{"class":1393},[356,1729,1553],{"class":471},[356,1731,1340],{"class":362},[356,1733,1231],{"class":471},[356,1735,1408],{"class":1393},[356,1737,1223],{"class":362},[356,1739,1740],{"class":471}," Buffer",[356,1742,1340],{"class":362},[356,1744,1276],{"class":1321},[356,1746,1369],{"class":1393},[356,1748,1749],{"class":471},"expected",[356,1751,1752],{"class":1393},"))\n",[356,1754,1756,1758,1761,1763,1766,1768,1771,1773,1776,1778,1781,1784,1787,1790,1793,1795,1797,1800],{"class":358,"line":1755},16,[356,1757,1434],{"class":372},[356,1759,1760],{"class":471}," fresh",[356,1762,1440],{"class":362},[356,1764,1765],{"class":471}," Math",[356,1767,1340],{"class":362},[356,1769,1770],{"class":1321},"abs",[356,1772,1369],{"class":1393},[356,1774,1775],{"class":471},"Date",[356,1777,1340],{"class":362},[356,1779,1780],{"class":1321},"now",[356,1782,1783],{"class":1393},"() ",[356,1785,1786],{"class":362},"\u002F",[356,1788,1789],{"class":683}," 1000",[356,1791,1792],{"class":362}," -",[356,1794,1543],{"class":471},[356,1796,1459],{"class":1393},[356,1798,1799],{"class":362},"\u003C=",[356,1801,1802],{"class":683}," 300\n",[356,1804,1806],{"class":358,"line":1805},17,[356,1807,1307],{"emptyLinePlaceholder":1306},[356,1809,1811,1814,1816,1819,1822,1825,1828,1831,1833,1836,1838,1840,1843,1845,1848],{"class":358,"line":1810},18,[356,1812,1813],{"class":1269},"  if",[356,1815,1413],{"class":1393},[356,1817,1818],{"class":362},"!",[356,1820,1821],{"class":471},"valid",[356,1823,1824],{"class":362}," ||",[356,1826,1827],{"class":362}," !",[356,1829,1830],{"class":471},"fresh",[356,1832,1459],{"class":1393},[356,1834,1835],{"class":1269},"return",[356,1837,1422],{"class":471},[356,1839,1340],{"class":362},[356,1841,1842],{"class":1321},"sendStatus",[356,1844,1369],{"class":1393},[356,1846,1847],{"class":683},"400",[356,1849,1560],{"class":1393},[356,1851,1853],{"class":358,"line":1852},19,[356,1854,1307],{"emptyLinePlaceholder":1306},[356,1856,1858,1860,1863,1865,1868,1870,1873,1875,1877],{"class":358,"line":1857},20,[356,1859,1434],{"class":372},[356,1861,1862],{"class":471}," event",[356,1864,1440],{"class":362},[356,1866,1867],{"class":471}," JSON",[356,1869,1340],{"class":362},[356,1871,1872],{"class":1321},"parse",[356,1874,1369],{"class":1393},[356,1876,1576],{"class":471},[356,1878,1560],{"class":1393},[356,1880,1882],{"class":358,"line":1881},21,[356,1883,1884],{"class":1351},"  \u002F\u002F Queue the work, then answer fast\n",[356,1886,1888,1891,1893,1895,1897,1900],{"class":358,"line":1887},22,[356,1889,1890],{"class":471},"  res",[356,1892,1340],{"class":362},[356,1894,1842],{"class":1321},[356,1896,1369],{"class":1393},[356,1898,1899],{"class":683},"200",[356,1901,1560],{"class":1393},[356,1903,1905,1907],{"class":358,"line":1904},23,[356,1906,1645],{"class":362},[356,1908,1560],{"class":471},[347,1910,1915],{"className":1911,"code":1912,"filename":1913,"language":1914,"meta":352,"style":352},"language-python shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import hashlib, hmac, json, os, time\nfrom flask import Flask, request, abort\n\napp = Flask(__name__)\nSECRET = os.environ[\"KULA_WEBHOOK_SECRET\"]  # whsec_...\n\n@app.post(\"\u002Fwebhooks\u002Fkula\")\ndef kula_webhook():\n    header = request.headers.get(\"Mailler-Signature\", \"\")\n    parts = dict(p.split(\"=\", 1) for p in header.split(\",\") if \"=\" in p)\n    timestamp = int(parts.get(\"t\", \"0\"))\n    body = request.get_data(as_text=True)\n\n    expected = hmac.new(SECRET.encode(), f\"{timestamp}.{body}\".encode(), hashlib.sha256).hexdigest()\n    if not hmac.compare_digest(parts.get(\"v1\", \"\"), expected):\n        abort(400)\n    if abs(time.time() - timestamp) > 300:\n        abort(400)\n\n    event = json.loads(body)\n    # Queue the work, then answer fast\n    return \"\", 200\n","Python (Flask)","python",[249,1916,1917,1922,1927,1931,1936,1941,1945,1950,1955,1960,1965,1970,1975,1979,1984,1989,1994,1999,2003,2007,2012,2017],{"__ignoreMap":352},[356,1918,1919],{"class":358,"line":359},[356,1920,1921],{},"import hashlib, hmac, json, os, time\n",[356,1923,1924],{"class":358,"line":366},[356,1925,1926],{},"from flask import Flask, request, abort\n",[356,1928,1929],{"class":358,"line":394},[356,1930,1307],{"emptyLinePlaceholder":1306},[356,1932,1933],{"class":358,"line":414},[356,1934,1935],{},"app = Flask(__name__)\n",[356,1937,1938],{"class":358,"line":435},[356,1939,1940],{},"SECRET = os.environ[\"KULA_WEBHOOK_SECRET\"]  # whsec_...\n",[356,1942,1943],{"class":358,"line":456},[356,1944,1307],{"emptyLinePlaceholder":1306},[356,1946,1947],{"class":358,"line":478},[356,1948,1949],{},"@app.post(\"\u002Fwebhooks\u002Fkula\")\n",[356,1951,1952],{"class":358,"line":604},[356,1953,1954],{},"def kula_webhook():\n",[356,1956,1957],{"class":358,"line":625},[356,1958,1959],{},"    header = request.headers.get(\"Mailler-Signature\", \"\")\n",[356,1961,1962],{"class":358,"line":646},[356,1963,1964],{},"    parts = dict(p.split(\"=\", 1) for p in header.split(\",\") if \"=\" in p)\n",[356,1966,1967],{"class":358,"line":667},[356,1968,1969],{},"    timestamp = int(parts.get(\"t\", \"0\"))\n",[356,1971,1972],{"class":358,"line":701},[356,1973,1974],{},"    body = request.get_data(as_text=True)\n",[356,1976,1977],{"class":358,"line":721},[356,1978,1307],{"emptyLinePlaceholder":1306},[356,1980,1981],{"class":358,"line":741},[356,1982,1983],{},"    expected = hmac.new(SECRET.encode(), f\"{timestamp}.{body}\".encode(), hashlib.sha256).hexdigest()\n",[356,1985,1986],{"class":358,"line":747},[356,1987,1988],{},"    if not hmac.compare_digest(parts.get(\"v1\", \"\"), expected):\n",[356,1990,1991],{"class":358,"line":1755},[356,1992,1993],{},"        abort(400)\n",[356,1995,1996],{"class":358,"line":1805},[356,1997,1998],{},"    if abs(time.time() - timestamp) > 300:\n",[356,2000,2001],{"class":358,"line":1810},[356,2002,1993],{},[356,2004,2005],{"class":358,"line":1852},[356,2006,1307],{"emptyLinePlaceholder":1306},[356,2008,2009],{"class":358,"line":1857},[356,2010,2011],{},"    event = json.loads(body)\n",[356,2013,2014],{"class":358,"line":1881},[356,2015,2016],{},"    # Queue the work, then answer fast\n",[356,2018,2019],{"class":358,"line":1887},[356,2020,2021],{},"    return \"\", 200\n",[2023,2024,2025],"warning",{},[208,2026,2027,2028,2031],{},"Frameworks that parse JSON before your handler (",[249,2029,2030],{},"express.json()",", for example) change the bytes and break the signature. Always verify the raw body.",[220,2033,2035],{"id":2034},"retries","Retries",[208,2037,2038,2039,2042],{},"A delivery succeeds when your endpoint answers any ",[249,2040,2041],{},"2xx"," status within 10 seconds. Redirects are not followed.",[208,2044,2045,2046,2049,2050,2053,2054,2057],{},"Otherwise it retries after 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours and 24 hours, then gives up. After ",[215,2047,2048],{},"50 failed deliveries in a row",", the endpoint is disabled and owners and admins get an email. Enable it again in the dashboard (or with ",[249,2051,2052],{},"PATCH"," and ",[249,2055,2056],{},"\"enabled\": true",") once fixed; failed deliveries can be retried from the dashboard.",[208,2059,2060],{},"Best practices:",[2062,2063,2064,2073,2085],"ul",{},[1209,2065,2066,2069,2070,2072],{},[215,2067,2068],{},"Answer fast."," Store the event or put it in a queue, answer ",[249,2071,1899],{},", then do the work.",[1209,2074,2075,2078,2079,2081,2082,2084],{},[215,2076,2077],{},"Expect duplicates."," A delivery can arrive twice. Deduplicate with the event ",[249,2080,373],{}," (or ",[249,2083,1181],{},").",[1209,2086,2087,2090,2091,2093,2094,2096,2097,2099],{},[215,2088,2089],{},"Don't rely on order."," ",[249,2092,299],{}," can arrive before ",[249,2095,261],{},". Use ",[249,2098,419],{}," when order matters.",[220,2101,2103],{"id":2102},"the-endpoint-object","The endpoint object",[347,2105,2107],{"className":349,"code":2106,"language":351,"meta":352,"style":352},"{\n  \"id\": \"1f2e3d4c-5b6a-4987-8a7b-6c5d4e3f2a1b\",\n  \"url\": \"https:\u002F\u002Fapi.acme.com\u002Fwebhooks\u002Fkula\",\n  \"description\": \"Bounces to our user database\",\n  \"eventTypes\": [\"email.bounced\", \"email.complained\", \"contact.unsubscribed\"],\n  \"enabled\": true,\n  \"consecutiveFailures\": 0,\n  \"disabledReason\": null,\n  \"createdAt\": \"2026-10-17T09:30:00Z\",\n  \"updatedAt\": \"2026-10-17T09:30:00Z\"\n}\n",[249,2108,2109,2113,2132,2151,2171,2210,2224,2240,2254,2274,2291],{"__ignoreMap":352},[356,2110,2111],{"class":358,"line":359},[356,2112,363],{"class":362},[356,2114,2115,2117,2119,2121,2123,2125,2128,2130],{"class":358,"line":366},[356,2116,369],{"class":362},[356,2118,373],{"class":372},[356,2120,376],{"class":362},[356,2122,379],{"class":362},[356,2124,382],{"class":362},[356,2126,2127],{"class":385},"1f2e3d4c-5b6a-4987-8a7b-6c5d4e3f2a1b",[356,2129,376],{"class":362},[356,2131,391],{"class":362},[356,2133,2134,2136,2138,2140,2142,2144,2147,2149],{"class":358,"line":394},[356,2135,369],{"class":362},[356,2137,315],{"class":372},[356,2139,376],{"class":362},[356,2141,379],{"class":362},[356,2143,382],{"class":362},[356,2145,2146],{"class":385},"https:\u002F\u002Fapi.acme.com\u002Fwebhooks\u002Fkula",[356,2148,376],{"class":362},[356,2150,391],{"class":362},[356,2152,2153,2155,2158,2160,2162,2164,2167,2169],{"class":358,"line":414},[356,2154,369],{"class":362},[356,2156,2157],{"class":372},"description",[356,2159,376],{"class":362},[356,2161,379],{"class":362},[356,2163,382],{"class":362},[356,2165,2166],{"class":385},"Bounces to our user database",[356,2168,376],{"class":362},[356,2170,391],{"class":362},[356,2172,2173,2175,2178,2180,2182,2185,2187,2189,2191,2193,2195,2197,2199,2201,2203,2205,2207],{"class":358,"line":435},[356,2174,369],{"class":362},[356,2176,2177],{"class":372},"eventTypes",[356,2179,376],{"class":362},[356,2181,379],{"class":362},[356,2183,2184],{"class":362}," [",[356,2186,376],{"class":362},[356,2188,271],{"class":385},[356,2190,376],{"class":362},[356,2192,1223],{"class":362},[356,2194,382],{"class":362},[356,2196,289],{"class":385},[356,2198,376],{"class":362},[356,2200,1223],{"class":362},[356,2202,382],{"class":362},[356,2204,332],{"class":385},[356,2206,376],{"class":362},[356,2208,2209],{"class":362},"],\n",[356,2211,2212,2214,2217,2219,2221],{"class":358,"line":456},[356,2213,369],{"class":362},[356,2215,2216],{"class":372},"enabled",[356,2218,376],{"class":362},[356,2220,379],{"class":362},[356,2222,2223],{"class":362}," true,\n",[356,2225,2226,2228,2231,2233,2235,2238],{"class":358,"line":478},[356,2227,369],{"class":362},[356,2229,2230],{"class":372},"consecutiveFailures",[356,2232,376],{"class":362},[356,2234,379],{"class":362},[356,2236,2237],{"class":683}," 0",[356,2239,391],{"class":362},[356,2241,2242,2244,2247,2249,2251],{"class":358,"line":604},[356,2243,369],{"class":362},[356,2245,2246],{"class":372},"disabledReason",[356,2248,376],{"class":362},[356,2250,379],{"class":362},[356,2252,2253],{"class":362}," null,\n",[356,2255,2256,2258,2261,2263,2265,2267,2270,2272],{"class":358,"line":625},[356,2257,369],{"class":362},[356,2259,2260],{"class":372},"createdAt",[356,2262,376],{"class":362},[356,2264,379],{"class":362},[356,2266,382],{"class":362},[356,2268,2269],{"class":385},"2026-10-17T09:30:00Z",[356,2271,376],{"class":362},[356,2273,391],{"class":362},[356,2275,2276,2278,2281,2283,2285,2287,2289],{"class":358,"line":646},[356,2277,369],{"class":362},[356,2279,2280],{"class":372},"updatedAt",[356,2282,376],{"class":362},[356,2284,379],{"class":362},[356,2286,382],{"class":362},[356,2288,2269],{"class":385},[356,2290,738],{"class":362},[356,2292,2293],{"class":358,"line":667},[356,2294,475],{"class":362},[220,2296,2298],{"id":2297},"list-endpoints","List endpoints",[208,2300,2301],{},[249,2302,2303],{},"GET \u002Fv1\u002Fwebhooks",[220,2305,2307],{"id":2306},"create-an-endpoint","Create an endpoint",[208,2309,2310],{},[249,2311,2312],{},"POST \u002Fv1\u002Fwebhooks",[208,2314,2315],{},"A space has at most 20 endpoints.",[2317,2318,2319,2331,2336],"field-group",{},[2320,2321,2324],"field",{"name":315,"type":2322,":required":2323},"string","true",[208,2325,2326,2327,2330],{},"A public ",[249,2328,2329],{},"https"," URL, 2,000 characters at most. Private and local addresses are refused.",[2320,2332,2333],{"name":2157,"type":2322},[208,2334,2335],{},"200 characters at most.",[2320,2337,2339],{"name":2177,"type":2338,":required":2323},"string[]",[208,2340,2341,2342,1340],{},"At least one ",[280,2343,2345],{"href":2344},"#event-types","event type",[347,2347,2351],{"className":2348,"code":2349,"language":2350,"meta":352,"style":352},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","curl https:\u002F\u002Fapi.kulamail.com\u002Fv1\u002Fwebhooks \\\n  -H \"Authorization: Bearer ml_your_api_key\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"url\": \"https:\u002F\u002Fapi.acme.com\u002Fwebhooks\u002Fkula\",\n    \"description\": \"Bounces to our user database\",\n    \"eventTypes\": [\"email.bounced\", \"email.complained\", \"contact.unsubscribed\"]\n  }'\n","bash",[249,2352,2353,2364,2378,2391,2400,2405,2410,2415],{"__ignoreMap":352},[356,2354,2355,2358,2361],{"class":358,"line":359},[356,2356,2357],{"class":587},"curl",[356,2359,2360],{"class":385}," https:\u002F\u002Fapi.kulamail.com\u002Fv1\u002Fwebhooks",[356,2362,2363],{"class":471}," \\\n",[356,2365,2366,2369,2371,2374,2376],{"class":358,"line":366},[356,2367,2368],{"class":385},"  -H",[356,2370,382],{"class":362},[356,2372,2373],{"class":385},"Authorization: Bearer ml_your_api_key",[356,2375,376],{"class":362},[356,2377,2363],{"class":471},[356,2379,2380,2382,2384,2387,2389],{"class":358,"line":394},[356,2381,2368],{"class":385},[356,2383,382],{"class":362},[356,2385,2386],{"class":385},"Content-Type: application\u002Fjson",[356,2388,376],{"class":362},[356,2390,2363],{"class":471},[356,2392,2393,2396,2398],{"class":358,"line":414},[356,2394,2395],{"class":385},"  -d",[356,2397,1279],{"class":362},[356,2399,363],{"class":385},[356,2401,2402],{"class":358,"line":435},[356,2403,2404],{"class":385},"    \"url\": \"https:\u002F\u002Fapi.acme.com\u002Fwebhooks\u002Fkula\",\n",[356,2406,2407],{"class":358,"line":456},[356,2408,2409],{"class":385},"    \"description\": \"Bounces to our user database\",\n",[356,2411,2412],{"class":358,"line":478},[356,2413,2414],{"class":385},"    \"eventTypes\": [\"email.bounced\", \"email.complained\", \"contact.unsubscribed\"]\n",[356,2416,2417,2420],{"class":358,"line":604},[356,2418,2419],{"class":385},"  }",[356,2421,1285],{"class":362},[347,2423,2426],{"className":349,"code":2424,"filename":2425,"language":351,"meta":352,"style":352},"{\n  \"endpoint\": { \"id\": \"1f2e3d4c-…\", \"url\": \"https:\u002F\u002Fapi.acme.com\u002Fwebhooks\u002Fkula\", \"enabled\": true, \"…\": \"…\" },\n  \"secret\": \"whsec_k3J9x…\"\n}\n","201 Created",[249,2427,2428,2432,2506,2524],{"__ignoreMap":352},[356,2429,2430],{"class":358,"line":359},[356,2431,363],{"class":362},[356,2433,2434,2436,2439,2441,2443,2445,2447,2449,2451,2453,2455,2458,2460,2462,2464,2466,2468,2470,2472,2474,2476,2478,2480,2482,2484,2486,2489,2491,2494,2496,2498,2500,2502,2504],{"class":358,"line":366},[356,2435,369],{"class":362},[356,2437,2438],{"class":372},"endpoint",[356,2440,376],{"class":362},[356,2442,379],{"class":362},[356,2444,468],{"class":362},[356,2446,382],{"class":362},[356,2448,373],{"class":587},[356,2450,376],{"class":362},[356,2452,379],{"class":362},[356,2454,382],{"class":362},[356,2456,2457],{"class":385},"1f2e3d4c-…",[356,2459,376],{"class":362},[356,2461,1223],{"class":362},[356,2463,382],{"class":362},[356,2465,315],{"class":587},[356,2467,376],{"class":362},[356,2469,379],{"class":362},[356,2471,382],{"class":362},[356,2473,2146],{"class":385},[356,2475,376],{"class":362},[356,2477,1223],{"class":362},[356,2479,382],{"class":362},[356,2481,2216],{"class":587},[356,2483,376],{"class":362},[356,2485,379],{"class":362},[356,2487,2488],{"class":362}," true,",[356,2490,382],{"class":362},[356,2492,2493],{"class":587},"…",[356,2495,376],{"class":362},[356,2497,379],{"class":362},[356,2499,382],{"class":362},[356,2501,2493],{"class":385},[356,2503,376],{"class":362},[356,2505,698],{"class":362},[356,2507,2508,2510,2513,2515,2517,2519,2522],{"class":358,"line":394},[356,2509,369],{"class":362},[356,2511,2512],{"class":372},"secret",[356,2514,376],{"class":362},[356,2516,379],{"class":362},[356,2518,382],{"class":362},[356,2520,2521],{"class":385},"whsec_k3J9x…",[356,2523,738],{"class":362},[356,2525,2526],{"class":358,"line":414},[356,2527,475],{"class":362},[208,2529,2530,2531,2533,2534,2537],{},"The signing ",[249,2532,2512],{}," is returned ",[215,2535,2536],{},"only here",": store it right away. Rotate it in the dashboard.",[225,2539,2540,2553],{},[228,2541,2542],{},[231,2543,2544,2547,2550],{},[234,2545,2546],{},"Status",[234,2548,2549],{},"Code",[234,2551,2552],{},"When",[241,2554,2555,2567],{},[231,2556,2557,2559,2564],{},[246,2558,1847],{},[246,2560,2561],{},[249,2562,2563],{},"invalid_url",[246,2565,2566],{},"Not https, or a private address",[231,2568,2569,2571,2576],{},[246,2570,1847],{},[246,2572,2573],{},[249,2574,2575],{},"bad_request",[246,2577,2578],{},"Unknown event type, or 20 endpoints already",[220,2580,2582],{"id":2581},"get-update-and-delete-an-endpoint","Get, update and delete an endpoint",[2062,2584,2585,2591,2606],{},[1209,2586,2587,2590],{},[249,2588,2589],{},"GET \u002Fv1\u002Fwebhooks\u002F{id}"," returns the endpoint.",[1209,2592,2593,2596,2597,784,2599,784,2601,1094,2603,2605],{},[249,2594,2595],{},"PATCH \u002Fv1\u002Fwebhooks\u002F{id}"," changes ",[249,2598,315],{},[249,2600,2157],{},[249,2602,2177],{},[249,2604,2216],{},". Fields left out stay as they are. Enabling again resets the failure streak.",[1209,2607,2608,2611,2612,1340],{},[249,2609,2610],{},"DELETE \u002Fv1\u002Fwebhooks\u002F{id}"," answers ",[249,2613,2614],{},"204",[2616,2617,2618],"style",{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}",{"title":352,"searchDepth":359,"depth":366,"links":2620},[2621,2622,2626,2627,2628,2629,2630,2631,2632],{"id":222,"depth":366,"text":223},{"id":341,"depth":366,"text":342,"children":2623},[2624,2625],{"id":484,"depth":394,"text":485},{"id":863,"depth":394,"text":864},{"id":1123,"depth":366,"text":1124},{"id":1200,"depth":366,"text":1201},{"id":2034,"depth":366,"text":2035},{"id":2102,"depth":366,"text":2103},{"id":2297,"depth":366,"text":2298},{"id":2306,"depth":366,"text":2307},{"id":2581,"depth":366,"text":2582},"Receive email and contact events on your server","md",null,{},{"title":169,"description":2633},"MZ7kqlKO1yvYqLxhRF71La7vWSthsLpVDAPKcw6i904",[2640,2642],{"title":165,"path":166,"stem":167,"description":2641,"children":-1},"Create and manage spaces with an account key",{"title":178,"path":179,"stem":180,"description":2643,"children":-1},"Spaces, members, roles and account API keys",1792265664525]