[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"docs-\u002Fdocs\u002Fintegrations\u002Fwebhook":3},{"id":4,"title":5,"body":6,"description":1498,"extension":1499,"meta":1500,"navigation":711,"order":1070,"path":1501,"seo":1502,"stem":1503,"summary":1504,"__hash__":1505},"docs\u002Fdocs\u002Fintegrations\u002Fwebhook.md","Universal webhook integration",{"type":7,"value":8,"toc":1482},"minimark",[9,13,17,20,33,36,41,44,70,73,137,140,144,147,155,158,162,165,239,242,246,253,504,515,571,575,578,581,616,630,634,637,671,674,678,685,889,896,900,1115,1119,1122,1216,1220,1223,1261,1264,1268,1271,1285,1288,1292,1295,1376,1380,1451,1455,1458,1475,1478],[10,11,5],"h1",{"id":12},"universal-webhook-integration",[14,15,16],"p",{},"Use the universal webhook when the customer website is not WordPress, Shopify, or a clean GitHub\u002FNuxt Content file-backed site.",[14,18,19],{},"It is the recommended path for:",[21,22,23,27,30],"ul",{},[24,25,26],"li",{},"Next.js apps with a database, CMS, ISR, or custom page model.",[24,28,29],{},"SvelteKit, Astro, Remix, Express, Hono, Cloudflare Workers, Laravel, Rails, Django, Go, PHP, or any custom stack.",[24,31,32],{},"Customers who want to own validation, persistence, build triggers, and cache purge logic.",[14,34,35],{},"42flows sends a signed HTTPS POST. The customer handler receives a canonical article payload and decides how to store and render it.",[37,38,40],"h2",{"id":39},"npm-package","npm package",[14,42,43],{},"For JavaScript and TypeScript stacks, install:",[45,46,51],"pre",{"className":47,"code":48,"language":49,"meta":50,"style":50},"language-bash shiki shiki-themes github-light github-dark","npm install @42flowsdotcom\u002Fwebhook\n","bash","",[52,53,54],"code",{"__ignoreMap":50},[55,56,59,63,67],"span",{"class":57,"line":58},"line",1,[55,60,62],{"class":61},"sScJk","npm",[55,64,66],{"class":65},"sZZnC"," install",[55,68,69],{"class":65}," @42flowsdotcom\u002Fwebhook\n",[14,71,72],{},"The package provides adapters for:",[74,75,76,89],"table",{},[77,78,79],"thead",{},[80,81,82,86],"tr",{},[83,84,85],"th",{},"Import",[83,87,88],{},"Fit",[90,91,92,103,113,127],"tbody",{},[80,93,94,100],{},[95,96,97],"td",{},[52,98,99],{},"@42flowsdotcom\u002Fwebhook\u002Fnext",[95,101,102],{},"Next.js app router route handlers.",[80,104,105,110],{},[95,106,107],{},[52,108,109],{},"@42flowsdotcom\u002Fwebhook\u002Fexpress",[95,111,112],{},"Express and Connect-style middleware.",[80,114,115,120],{},[95,116,117],{},[52,118,119],{},"@42flowsdotcom\u002Fwebhook\u002Fweb",[95,121,122,123,126],{},"Web standard ",[52,124,125],{},"Request"," handlers for SvelteKit, Astro, Hono, Workers, Bun, and Deno.",[80,128,129,134],{},[95,130,131],{},[52,132,133],{},"@42flowsdotcom\u002Fwebhook",[95,135,136],{},"Core validation, auth, and type helpers.",[14,138,139],{},"Non-JS stacks can implement the raw HTTP contract directly.",[37,141,143],{"id":142},"endpoint-contract","Endpoint contract",[14,145,146],{},"The customer creates one HTTPS endpoint:",[45,148,153],{"className":149,"code":151,"language":152,"meta":50},[150],"language-text","POST https:\u002F\u002Fcustomer.com\u002Fapi\u002F42flows\u002Fwebhook\nAuthorization: Bearer \u003CFLOWS42_API_KEY>\nContent-Type: application\u002Fjson\nUser-Agent: 42flows-delivery\u002F1.0 (+https:\u002F\u002F42flows.com)\n","text",[52,154,151],{"__ignoreMap":50},[14,156,157],{},"42flows rejects non-HTTPS webhook URLs during setup. During connection, 42flows generates a 64-character token unless the customer provides one.",[37,159,161],{"id":160},"test-ping","Test ping",[14,163,164],{},"Before publishing real content, 42flows sends:",[45,166,170],{"className":167,"code":168,"language":169,"meta":50,"style":50},"language-json shiki shiki-themes github-light github-dark","{\n  \"event_type\": \"ping\",\n  \"timestamp\": \"2026-06-13T00:00:00.000Z\",\n  \"data\": {\n    \"message\": \"42flows webhook connection test\"\n  }\n}\n","json",[52,171,172,178,194,207,216,227,233],{"__ignoreMap":50},[55,173,174],{"class":57,"line":58},[55,175,177],{"class":176},"sVt8B","{\n",[55,179,181,185,188,191],{"class":57,"line":180},2,[55,182,184],{"class":183},"sj4cs","  \"event_type\"",[55,186,187],{"class":176},": ",[55,189,190],{"class":65},"\"ping\"",[55,192,193],{"class":176},",\n",[55,195,197,200,202,205],{"class":57,"line":196},3,[55,198,199],{"class":183},"  \"timestamp\"",[55,201,187],{"class":176},[55,203,204],{"class":65},"\"2026-06-13T00:00:00.000Z\"",[55,206,193],{"class":176},[55,208,210,213],{"class":57,"line":209},4,[55,211,212],{"class":183},"  \"data\"",[55,214,215],{"class":176},": {\n",[55,217,219,222,224],{"class":57,"line":218},5,[55,220,221],{"class":183},"    \"message\"",[55,223,187],{"class":176},[55,225,226],{"class":65},"\"42flows webhook connection test\"\n",[55,228,230],{"class":57,"line":229},6,[55,231,232],{"class":176},"  }\n",[55,234,236],{"class":57,"line":235},7,[55,237,238],{"class":176},"}\n",[14,240,241],{},"The endpoint should return any 2xx response.",[37,243,245],{"id":244},"publish-payload","Publish payload",[14,247,248,249,252],{},"Real delivery uses ",[52,250,251],{},"event_type: \"publish_article\"",".",[45,254,256],{"className":167,"code":255,"language":169,"meta":50,"style":50},"{\n  \"event_type\": \"publish_article\",\n  \"timestamp\": \"2026-06-13T00:00:00.000Z\",\n  \"flow_type\": \"origin\",\n  \"data\": {\n    \"article\": {\n      \"slug\": \"best-ai-seo-workflow-for-dentists\",\n      \"title\": \"Best AI SEO workflow for dentists\",\n      \"content_type\": \"article\",\n      \"meta_title\": \"Best AI SEO workflow for dentists\",\n      \"meta_description\": \"A practical guide to AI SEO workflows for dental clinics.\",\n      \"primary_keyword\": \"ai seo for dentists\",\n      \"secondary_keywords\": [\"dental seo\", \"clinic seo\"],\n      \"published_at\": \"2026-06-13T00:00:00.000Z\",\n      \"content_markdown\": \"# Best AI SEO workflow for dentists\\n\\n...\",\n      \"content_html\": \"\u003Carticle>...\u003C\u002Farticle>\",\n      \"json_ld\": [{ \"@context\": \"https:\u002F\u002Fschema.org\", \"@type\": \"Article\" }],\n      \"backbone\": {}\n    }\n  }\n}\n",[52,257,258,262,273,283,295,301,308,320,333,346,358,371,384,405,417,436,449,479,488,494,499],{"__ignoreMap":50},[55,259,260],{"class":57,"line":58},[55,261,177],{"class":176},[55,263,264,266,268,271],{"class":57,"line":180},[55,265,184],{"class":183},[55,267,187],{"class":176},[55,269,270],{"class":65},"\"publish_article\"",[55,272,193],{"class":176},[55,274,275,277,279,281],{"class":57,"line":196},[55,276,199],{"class":183},[55,278,187],{"class":176},[55,280,204],{"class":65},[55,282,193],{"class":176},[55,284,285,288,290,293],{"class":57,"line":209},[55,286,287],{"class":183},"  \"flow_type\"",[55,289,187],{"class":176},[55,291,292],{"class":65},"\"origin\"",[55,294,193],{"class":176},[55,296,297,299],{"class":57,"line":218},[55,298,212],{"class":183},[55,300,215],{"class":176},[55,302,303,306],{"class":57,"line":229},[55,304,305],{"class":183},"    \"article\"",[55,307,215],{"class":176},[55,309,310,313,315,318],{"class":57,"line":235},[55,311,312],{"class":183},"      \"slug\"",[55,314,187],{"class":176},[55,316,317],{"class":65},"\"best-ai-seo-workflow-for-dentists\"",[55,319,193],{"class":176},[55,321,323,326,328,331],{"class":57,"line":322},8,[55,324,325],{"class":183},"      \"title\"",[55,327,187],{"class":176},[55,329,330],{"class":65},"\"Best AI SEO workflow for dentists\"",[55,332,193],{"class":176},[55,334,336,339,341,344],{"class":57,"line":335},9,[55,337,338],{"class":183},"      \"content_type\"",[55,340,187],{"class":176},[55,342,343],{"class":65},"\"article\"",[55,345,193],{"class":176},[55,347,349,352,354,356],{"class":57,"line":348},10,[55,350,351],{"class":183},"      \"meta_title\"",[55,353,187],{"class":176},[55,355,330],{"class":65},[55,357,193],{"class":176},[55,359,361,364,366,369],{"class":57,"line":360},11,[55,362,363],{"class":183},"      \"meta_description\"",[55,365,187],{"class":176},[55,367,368],{"class":65},"\"A practical guide to AI SEO workflows for dental clinics.\"",[55,370,193],{"class":176},[55,372,374,377,379,382],{"class":57,"line":373},12,[55,375,376],{"class":183},"      \"primary_keyword\"",[55,378,187],{"class":176},[55,380,381],{"class":65},"\"ai seo for dentists\"",[55,383,193],{"class":176},[55,385,387,390,393,396,399,402],{"class":57,"line":386},13,[55,388,389],{"class":183},"      \"secondary_keywords\"",[55,391,392],{"class":176},": [",[55,394,395],{"class":65},"\"dental seo\"",[55,397,398],{"class":176},", ",[55,400,401],{"class":65},"\"clinic seo\"",[55,403,404],{"class":176},"],\n",[55,406,408,411,413,415],{"class":57,"line":407},14,[55,409,410],{"class":183},"      \"published_at\"",[55,412,187],{"class":176},[55,414,204],{"class":65},[55,416,193],{"class":176},[55,418,420,423,425,428,431,434],{"class":57,"line":419},15,[55,421,422],{"class":183},"      \"content_markdown\"",[55,424,187],{"class":176},[55,426,427],{"class":65},"\"# Best AI SEO workflow for dentists",[55,429,430],{"class":183},"\\n\\n",[55,432,433],{"class":65},"...\"",[55,435,193],{"class":176},[55,437,439,442,444,447],{"class":57,"line":438},16,[55,440,441],{"class":183},"      \"content_html\"",[55,443,187],{"class":176},[55,445,446],{"class":65},"\"\u003Carticle>...\u003C\u002Farticle>\"",[55,448,193],{"class":176},[55,450,452,455,458,461,463,466,468,471,473,476],{"class":57,"line":451},17,[55,453,454],{"class":183},"      \"json_ld\"",[55,456,457],{"class":176},": [{ ",[55,459,460],{"class":183},"\"@context\"",[55,462,187],{"class":176},[55,464,465],{"class":65},"\"https:\u002F\u002Fschema.org\"",[55,467,398],{"class":176},[55,469,470],{"class":183},"\"@type\"",[55,472,187],{"class":176},[55,474,475],{"class":65},"\"Article\"",[55,477,478],{"class":176}," }],\n",[55,480,482,485],{"class":57,"line":481},18,[55,483,484],{"class":183},"      \"backbone\"",[55,486,487],{"class":176},": {}\n",[55,489,491],{"class":57,"line":490},19,[55,492,493],{"class":176},"    }\n",[55,495,497],{"class":57,"line":496},20,[55,498,232],{"class":176},[55,500,502],{"class":57,"line":501},21,[55,503,238],{"class":176},[14,505,506,507,510,511,514],{},"Both ",[52,508,509],{},"content_markdown"," and ",[52,512,513],{},"content_html"," are populated.",[74,516,517,527],{},[77,518,519],{},[80,520,521,524],{},[83,522,523],{},"Field",[83,525,526],{},"Use it when",[90,528,529,538,547,561],{},[80,530,531,535],{},[95,532,533],{},[52,534,513],{},[95,536,537],{},"The site stores rendered HTML, uses a database-backed blog, or does not support MDC\u002FMDX components.",[80,539,540,544],{},[95,541,542],{},[52,543,509],{},[95,545,546],{},"The site renders markdown\u002FMDC\u002FMDX and can map 42flows rich blocks to components.",[80,548,549,554],{},[95,550,551],{},[52,552,553],{},"json_ld",[95,555,556,557,560],{},"The site controls its own ",[52,558,559],{},"\u003Cscript type=\"application\u002Fld+json\">"," output.",[80,562,563,568],{},[95,564,565],{},[52,566,567],{},"backbone",[95,569,570],{},"The site wants the original structured 42flows content data for custom rendering.",[37,572,574],{"id":573},"success-response","Success response",[14,576,577],{},"Return 2xx when the article is accepted.",[14,579,580],{},"Recommended response:",[45,582,584],{"className":167,"code":583,"language":169,"meta":50,"style":50},"{\n  \"ok\": true,\n  \"postUrl\": \"https:\u002F\u002Fcustomer.com\u002Fblog\u002Fbest-ai-seo-workflow-for-dentists\"\n}\n",[52,585,586,590,602,612],{"__ignoreMap":50},[55,587,588],{"class":57,"line":58},[55,589,177],{"class":176},[55,591,592,595,597,600],{"class":57,"line":180},[55,593,594],{"class":183},"  \"ok\"",[55,596,187],{"class":176},[55,598,599],{"class":183},"true",[55,601,193],{"class":176},[55,603,604,607,609],{"class":57,"line":196},[55,605,606],{"class":183},"  \"postUrl\"",[55,608,187],{"class":176},[55,610,611],{"class":65},"\"https:\u002F\u002Fcustomer.com\u002Fblog\u002Fbest-ai-seo-workflow-for-dentists\"\n",[55,613,614],{"class":57,"line":209},[55,615,238],{"class":176},[14,617,618,619,398,622,625,626,629],{},"42flows looks for ",[52,620,621],{},"postUrl",[52,623,624],{},"post_url",", or ",[52,627,628],{},"url",". If one is present, it can be used for delivery status and verification.",[37,631,633],{"id":632},"failure-response","Failure response",[14,635,636],{},"Return a non-2xx status when the article was not accepted.",[45,638,640],{"className":167,"code":639,"language":169,"meta":50,"style":50},"{\n  \"ok\": false,\n  \"error\": \"Missing required category\"\n}\n",[52,641,642,646,657,667],{"__ignoreMap":50},[55,643,644],{"class":57,"line":58},[55,645,177],{"class":176},[55,647,648,650,652,655],{"class":57,"line":180},[55,649,594],{"class":183},[55,651,187],{"class":176},[55,653,654],{"class":183},"false",[55,656,193],{"class":176},[55,658,659,662,664],{"class":57,"line":196},[55,660,661],{"class":183},"  \"error\"",[55,663,187],{"class":176},[55,665,666],{"class":65},"\"Missing required category\"\n",[55,668,669],{"class":57,"line":209},[55,670,238],{"class":176},[14,672,673],{},"42flows records the failure so the delivery can be reviewed. Make the customer handler idempotent by slug, so sending the same article again updates the existing article instead of creating a duplicate.",[37,675,677],{"id":676},"nextjs-app-router-example","Next.js app router example",[14,679,680,681,684],{},"Create ",[52,682,683],{},"app\u002Fapi\u002F42flows\u002Fwebhook\u002Froute.ts",":",[45,686,690],{"className":687,"code":688,"language":689,"meta":50,"style":50},"language-ts shiki shiki-themes github-light github-dark","import { flows42NextRoute } from '@42flowsdotcom\u002Fwebhook\u002Fnext'\n\nexport const POST = flows42NextRoute({\n  apiKey: process.env.FLOWS42_API_KEY!,\n  async onPublish({ article }) {\n    await upsertArticle({\n      slug: article.slug,\n      title: article.title,\n      html: article.content_html,\n      markdown: article.content_markdown,\n      metaTitle: article.meta_title,\n      metaDescription: article.meta_description,\n      jsonLd: article.json_ld,\n    })\n\n    await revalidateArticlePath(`\u002Fblog\u002F${article.slug}`)\n\n    return {\n      postUrl: `https:\u002F\u002Fexample.com\u002Fblog\u002F${article.slug}`,\n    }\n  },\n})\n","ts",[52,691,692,707,713,733,746,764,774,779,784,789,794,799,804,809,814,818,844,848,856,874,878,883],{"__ignoreMap":50},[55,693,694,698,701,704],{"class":57,"line":58},[55,695,697],{"class":696},"szBVR","import",[55,699,700],{"class":176}," { flows42NextRoute } ",[55,702,703],{"class":696},"from",[55,705,706],{"class":65}," '@42flowsdotcom\u002Fwebhook\u002Fnext'\n",[55,708,709],{"class":57,"line":180},[55,710,712],{"emptyLinePlaceholder":711},true,"\n",[55,714,715,718,721,724,727,730],{"class":57,"line":196},[55,716,717],{"class":696},"export",[55,719,720],{"class":696}," const",[55,722,723],{"class":183}," POST",[55,725,726],{"class":696}," =",[55,728,729],{"class":61}," flows42NextRoute",[55,731,732],{"class":176},"({\n",[55,734,735,738,741,744],{"class":57,"line":209},[55,736,737],{"class":176},"  apiKey: process.env.",[55,739,740],{"class":183},"FLOWS42_API_KEY",[55,742,743],{"class":696},"!",[55,745,193],{"class":176},[55,747,748,751,754,757,761],{"class":57,"line":218},[55,749,750],{"class":696},"  async",[55,752,753],{"class":61}," onPublish",[55,755,756],{"class":176},"({ ",[55,758,760],{"class":759},"s4XuR","article",[55,762,763],{"class":176}," }) {\n",[55,765,766,769,772],{"class":57,"line":229},[55,767,768],{"class":696},"    await",[55,770,771],{"class":61}," upsertArticle",[55,773,732],{"class":176},[55,775,776],{"class":57,"line":235},[55,777,778],{"class":176},"      slug: article.slug,\n",[55,780,781],{"class":57,"line":322},[55,782,783],{"class":176},"      title: article.title,\n",[55,785,786],{"class":57,"line":335},[55,787,788],{"class":176},"      html: article.content_html,\n",[55,790,791],{"class":57,"line":348},[55,792,793],{"class":176},"      markdown: article.content_markdown,\n",[55,795,796],{"class":57,"line":360},[55,797,798],{"class":176},"      metaTitle: article.meta_title,\n",[55,800,801],{"class":57,"line":373},[55,802,803],{"class":176},"      metaDescription: article.meta_description,\n",[55,805,806],{"class":57,"line":386},[55,807,808],{"class":176},"      jsonLd: article.json_ld,\n",[55,810,811],{"class":57,"line":407},[55,812,813],{"class":176},"    })\n",[55,815,816],{"class":57,"line":419},[55,817,712],{"emptyLinePlaceholder":711},[55,819,820,822,825,828,831,833,835,838,841],{"class":57,"line":438},[55,821,768],{"class":696},[55,823,824],{"class":61}," revalidateArticlePath",[55,826,827],{"class":176},"(",[55,829,830],{"class":65},"`\u002Fblog\u002F${",[55,832,760],{"class":176},[55,834,252],{"class":65},[55,836,837],{"class":176},"slug",[55,839,840],{"class":65},"}`",[55,842,843],{"class":176},")\n",[55,845,846],{"class":57,"line":451},[55,847,712],{"emptyLinePlaceholder":711},[55,849,850,853],{"class":57,"line":481},[55,851,852],{"class":696},"    return",[55,854,855],{"class":176}," {\n",[55,857,858,861,864,866,868,870,872],{"class":57,"line":490},[55,859,860],{"class":176},"      postUrl: ",[55,862,863],{"class":65},"`https:\u002F\u002Fexample.com\u002Fblog\u002F${",[55,865,760],{"class":176},[55,867,252],{"class":65},[55,869,837],{"class":176},[55,871,840],{"class":65},[55,873,193],{"class":176},[55,875,876],{"class":57,"line":496},[55,877,493],{"class":176},[55,879,880],{"class":57,"line":501},[55,881,882],{"class":176},"  },\n",[55,884,886],{"class":57,"line":885},22,[55,887,888],{"class":176},"})\n",[14,890,891,892,895],{},"The route should also be able to answer the ",[52,893,894],{},"ping"," event. The package handles auth, ping parsing, validation, and response shaping.",[37,897,899],{"id":898},"express-example","Express example",[45,901,903],{"className":687,"code":902,"language":689,"meta":50,"style":50},"import express from 'express'\nimport { flows42Express } from '@42flowsdotcom\u002Fwebhook\u002Fexpress'\n\nconst app = express()\n\napp.post('\u002Fapi\u002F42flows\u002Fwebhook', flows42Express({\n  apiKey: process.env.FLOWS42_API_KEY!,\n  async onPublish({ article }) {\n    await db.article.upsert({\n      where: { slug: article.slug },\n      update: {\n        title: article.title,\n        html: article.content_html,\n        metaTitle: article.meta_title,\n        metaDescription: article.meta_description,\n      },\n      create: {\n        slug: article.slug,\n        title: article.title,\n        html: article.content_html,\n        metaTitle: article.meta_title,\n        metaDescription: article.meta_description,\n      },\n    })\n\n    return { postUrl: `https:\u002F\u002Fexample.com\u002Fblog\u002F${article.slug}` }\n  },\n}))\n",[52,904,905,917,929,933,949,953,973,983,995,1007,1012,1017,1022,1027,1032,1037,1042,1047,1052,1056,1060,1064,1068,1073,1078,1083,1104,1109],{"__ignoreMap":50},[55,906,907,909,912,914],{"class":57,"line":58},[55,908,697],{"class":696},[55,910,911],{"class":176}," express ",[55,913,703],{"class":696},[55,915,916],{"class":65}," 'express'\n",[55,918,919,921,924,926],{"class":57,"line":180},[55,920,697],{"class":696},[55,922,923],{"class":176}," { flows42Express } ",[55,925,703],{"class":696},[55,927,928],{"class":65}," '@42flowsdotcom\u002Fwebhook\u002Fexpress'\n",[55,930,931],{"class":57,"line":196},[55,932,712],{"emptyLinePlaceholder":711},[55,934,935,938,941,943,946],{"class":57,"line":209},[55,936,937],{"class":696},"const",[55,939,940],{"class":183}," app",[55,942,726],{"class":696},[55,944,945],{"class":61}," express",[55,947,948],{"class":176},"()\n",[55,950,951],{"class":57,"line":218},[55,952,712],{"emptyLinePlaceholder":711},[55,954,955,958,961,963,966,968,971],{"class":57,"line":229},[55,956,957],{"class":176},"app.",[55,959,960],{"class":61},"post",[55,962,827],{"class":176},[55,964,965],{"class":65},"'\u002Fapi\u002F42flows\u002Fwebhook'",[55,967,398],{"class":176},[55,969,970],{"class":61},"flows42Express",[55,972,732],{"class":176},[55,974,975,977,979,981],{"class":57,"line":235},[55,976,737],{"class":176},[55,978,740],{"class":183},[55,980,743],{"class":696},[55,982,193],{"class":176},[55,984,985,987,989,991,993],{"class":57,"line":322},[55,986,750],{"class":696},[55,988,753],{"class":61},[55,990,756],{"class":176},[55,992,760],{"class":759},[55,994,763],{"class":176},[55,996,997,999,1002,1005],{"class":57,"line":335},[55,998,768],{"class":696},[55,1000,1001],{"class":176}," db.article.",[55,1003,1004],{"class":61},"upsert",[55,1006,732],{"class":176},[55,1008,1009],{"class":57,"line":348},[55,1010,1011],{"class":176},"      where: { slug: article.slug },\n",[55,1013,1014],{"class":57,"line":360},[55,1015,1016],{"class":176},"      update: {\n",[55,1018,1019],{"class":57,"line":373},[55,1020,1021],{"class":176},"        title: article.title,\n",[55,1023,1024],{"class":57,"line":386},[55,1025,1026],{"class":176},"        html: article.content_html,\n",[55,1028,1029],{"class":57,"line":407},[55,1030,1031],{"class":176},"        metaTitle: article.meta_title,\n",[55,1033,1034],{"class":57,"line":419},[55,1035,1036],{"class":176},"        metaDescription: article.meta_description,\n",[55,1038,1039],{"class":57,"line":438},[55,1040,1041],{"class":176},"      },\n",[55,1043,1044],{"class":57,"line":451},[55,1045,1046],{"class":176},"      create: {\n",[55,1048,1049],{"class":57,"line":481},[55,1050,1051],{"class":176},"        slug: article.slug,\n",[55,1053,1054],{"class":57,"line":490},[55,1055,1021],{"class":176},[55,1057,1058],{"class":57,"line":496},[55,1059,1026],{"class":176},[55,1061,1062],{"class":57,"line":501},[55,1063,1031],{"class":176},[55,1065,1066],{"class":57,"line":885},[55,1067,1036],{"class":176},[55,1069,1071],{"class":57,"line":1070},23,[55,1072,1041],{"class":176},[55,1074,1076],{"class":57,"line":1075},24,[55,1077,813],{"class":176},[55,1079,1081],{"class":57,"line":1080},25,[55,1082,712],{"emptyLinePlaceholder":711},[55,1084,1086,1088,1091,1093,1095,1097,1099,1101],{"class":57,"line":1085},26,[55,1087,852],{"class":696},[55,1089,1090],{"class":176}," { postUrl: ",[55,1092,863],{"class":65},[55,1094,760],{"class":176},[55,1096,252],{"class":65},[55,1098,837],{"class":176},[55,1100,840],{"class":65},[55,1102,1103],{"class":176}," }\n",[55,1105,1107],{"class":57,"line":1106},27,[55,1108,882],{"class":176},[55,1110,1112],{"class":57,"line":1111},28,[55,1113,1114],{"class":176},"}))\n",[37,1116,1118],{"id":1117},"web-standard-handler","Web standard handler",[14,1120,1121],{},"Use the web adapter for SvelteKit, Astro, Hono, Workers, Bun, and Deno-style runtimes:",[45,1123,1125],{"className":687,"code":1124,"language":689,"meta":50,"style":50},"import { flows42WebHandler } from '@42flowsdotcom\u002Fwebhook\u002Fweb'\n\nexport const POST = flows42WebHandler({\n  apiKey: process.env.FLOWS42_API_KEY!,\n  async onPublish({ article }) {\n    await saveArticle(article)\n    return { postUrl: `https:\u002F\u002Fexample.com\u002Fblog\u002F${article.slug}` }\n  },\n})\n",[52,1126,1127,1139,1143,1158,1168,1180,1190,1208,1212],{"__ignoreMap":50},[55,1128,1129,1131,1134,1136],{"class":57,"line":58},[55,1130,697],{"class":696},[55,1132,1133],{"class":176}," { flows42WebHandler } ",[55,1135,703],{"class":696},[55,1137,1138],{"class":65}," '@42flowsdotcom\u002Fwebhook\u002Fweb'\n",[55,1140,1141],{"class":57,"line":180},[55,1142,712],{"emptyLinePlaceholder":711},[55,1144,1145,1147,1149,1151,1153,1156],{"class":57,"line":196},[55,1146,717],{"class":696},[55,1148,720],{"class":696},[55,1150,723],{"class":183},[55,1152,726],{"class":696},[55,1154,1155],{"class":61}," flows42WebHandler",[55,1157,732],{"class":176},[55,1159,1160,1162,1164,1166],{"class":57,"line":209},[55,1161,737],{"class":176},[55,1163,740],{"class":183},[55,1165,743],{"class":696},[55,1167,193],{"class":176},[55,1169,1170,1172,1174,1176,1178],{"class":57,"line":218},[55,1171,750],{"class":696},[55,1173,753],{"class":61},[55,1175,756],{"class":176},[55,1177,760],{"class":759},[55,1179,763],{"class":176},[55,1181,1182,1184,1187],{"class":57,"line":229},[55,1183,768],{"class":696},[55,1185,1186],{"class":61}," saveArticle",[55,1188,1189],{"class":176},"(article)\n",[55,1191,1192,1194,1196,1198,1200,1202,1204,1206],{"class":57,"line":235},[55,1193,852],{"class":696},[55,1195,1090],{"class":176},[55,1197,863],{"class":65},[55,1199,760],{"class":176},[55,1201,252],{"class":65},[55,1203,837],{"class":176},[55,1205,840],{"class":65},[55,1207,1103],{"class":176},[55,1209,1210],{"class":57,"line":322},[55,1211,882],{"class":176},[55,1213,1214],{"class":57,"line":335},[55,1215,888],{"class":176},[37,1217,1219],{"id":1218},"what-the-customer-handler-must-do","What the customer handler must do",[14,1221,1222],{},"A production handler should:",[1224,1225,1226,1229,1234,1240,1247,1250,1253,1258],"ol",{},[24,1227,1228],{},"Verify the bearer token.",[24,1230,1231,1232,252],{},"Accept ",[52,1233,894],{},[24,1235,1236,1237,252],{},"Validate ",[52,1238,1239],{},"publish_article",[24,1241,1242,1243,1246],{},"Upsert by ",[52,1244,1245],{},"article.slug"," so redelivery is safe.",[24,1248,1249],{},"Store either HTML or markdown plus metadata.",[24,1251,1252],{},"Trigger revalidation, build, or cache purge if the framework needs it.",[24,1254,1255,1256,252],{},"Return a public ",[52,1257,621],{},[24,1259,1260],{},"Log the delivery in the customer app.",[14,1262,1263],{},"42flows can send the content. The customer app owns the final render route, storage, CDN purge, and build behavior.",[37,1265,1267],{"id":1266},"static-build-recipe","Static build recipe",[14,1269,1270],{},"For static builds, the webhook handler should usually:",[1224,1272,1273,1276,1279,1282],{},[24,1274,1275],{},"Store the article in the CMS, database, or file store.",[24,1277,1278],{},"Trigger the build or incremental revalidation.",[24,1280,1281],{},"Return the final public URL if it is predictable.",[24,1283,1284],{},"Make sure the rendered page contains the 42flows marker from the article body.",[14,1286,1287],{},"If the site cannot publish until a build finishes, return 202 plus the predictable URL, then rely on delivery verification after deployment.",[37,1289,1291],{"id":1290},"seo-fields","SEO fields",[14,1293,1294],{},"The customer renderer should map:",[74,1296,1297,1307],{},[77,1298,1299],{},[80,1300,1301,1304],{},[83,1302,1303],{},"42flows field",[83,1305,1306],{},"Render target",[90,1308,1309,1319,1332,1342,1356,1366],{},[80,1310,1311,1316],{},[95,1312,1313],{},[52,1314,1315],{},"article.title",[95,1317,1318],{},"Page heading.",[80,1320,1321,1326],{},[95,1322,1323],{},[52,1324,1325],{},"article.meta_title",[95,1327,1328,1331],{},[52,1329,1330],{},"\u003Ctitle>"," and Open Graph title.",[80,1333,1334,1339],{},[95,1335,1336],{},[52,1337,1338],{},"article.meta_description",[95,1340,1341],{},"Meta description and social description.",[80,1343,1344,1349],{},[95,1345,1346],{},[52,1347,1348],{},"article.json_ld",[95,1350,1351,1352,1355],{},"JSON-LD scripts in ",[52,1353,1354],{},"\u003Chead>"," or page body.",[80,1357,1358,1363],{},[95,1359,1360],{},[52,1361,1362],{},"article.content_html",[95,1364,1365],{},"Main article body for HTML renderers.",[80,1367,1368,1373],{},[95,1369,1370],{},[52,1371,1372],{},"article.content_markdown",[95,1374,1375],{},"Main article body for markdown\u002FMDC\u002FMDX renderers.",[37,1377,1379],{"id":1378},"verification-and-troubleshooting","Verification and troubleshooting",[74,1381,1382,1392],{},[77,1383,1384],{},[80,1385,1386,1389],{},[83,1387,1388],{},"Symptom",[83,1390,1391],{},"Likely issue",[90,1393,1394,1406,1419,1427,1435,1443],{},[80,1395,1396,1399],{},[95,1397,1398],{},"Connection test fails with 401",[95,1400,1401,1402,1405],{},"Token mismatch or missing ",[52,1403,1404],{},"Authorization: Bearer ..."," handling.",[80,1407,1408,1411],{},[95,1409,1410],{},"Connection test fails with validation error",[95,1412,1413,1414,1416,1417,252],{},"The handler only accepts ",[52,1415,1239],{}," and forgot to accept ",[52,1418,894],{},[80,1420,1421,1424],{},[95,1422,1423],{},"Delivery succeeds but no page appears",[95,1425,1426],{},"The handler stored the article but did not trigger revalidation\u002Fbuild or the route cannot read the stored record.",[80,1428,1429,1432],{},[95,1430,1431],{},"Delivery fails after 30 seconds",[95,1433,1434],{},"The handler is doing slow work inline. Store quickly, trigger background work, and return.",[80,1436,1437,1440],{},[95,1438,1439],{},"Duplicate articles appear",[95,1441,1442],{},"The handler inserts instead of upserting by slug.",[80,1444,1445,1448],{},[95,1446,1447],{},"Verification cannot find the marker",[95,1449,1450],{},"The rendered page strips article body comments\u002Fclasses or serves a stale cached version.",[37,1452,1454],{"id":1453},"what-your-app-owns","What your app owns",[14,1456,1457],{},"The webhook is deliberately simple. Your app remains responsible for:",[21,1459,1460,1463,1466,1469,1472],{},[24,1461,1462],{},"Listing or searching its own pages.",[24,1464,1465],{},"Editing existing pages outside the webhook article flow.",[24,1467,1468],{},"Undoing a published article if the customer asks for removal.",[24,1470,1471],{},"Triggering builds, cache purge, or ISR revalidation.",[24,1473,1474],{},"Making sure the returned URL becomes public.",[14,1476,1477],{},"That is the tradeoff: one endpoint is easy to add, and the website keeps control of how content is stored and rendered.",[1479,1480,1481],"style",{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}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 .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":50,"searchDepth":180,"depth":180,"links":1483},[1484,1485,1486,1487,1488,1489,1490,1491,1492,1493,1494,1495,1496,1497],{"id":39,"depth":180,"text":40},{"id":142,"depth":180,"text":143},{"id":160,"depth":180,"text":161},{"id":244,"depth":180,"text":245},{"id":573,"depth":180,"text":574},{"id":632,"depth":180,"text":633},{"id":676,"depth":180,"text":677},{"id":898,"depth":180,"text":899},{"id":1117,"depth":180,"text":1118},{"id":1218,"depth":180,"text":1219},{"id":1266,"depth":180,"text":1267},{"id":1290,"depth":180,"text":1291},{"id":1378,"depth":180,"text":1379},{"id":1453,"depth":180,"text":1454},"How Next.js, Express, SvelteKit, Astro, Hono, Workers, and other custom stacks receive 42flows content through a signed HTTPS webhook.","md",{},"\u002Fdocs\u002Fintegrations\u002Fwebhook",{"title":5,"description":1498},"docs\u002Fintegrations\u002Fwebhook","The universal customer-owned integration for stacks that are not WordPress, Shopify, or file-backed Nuxt Content.","SmVwwOZMJciIiI3mRT5K3-Jki_UA6IHyihRWxjNRnTU"]