[{"data":1,"prerenderedAt":630},["ShallowReactive",2],{"content-query-HWM1ecekDw":3},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"layout":10,"crumb":11,"term":11,"summary":12,"group":13,"order":14,"updated":15,"cta":16,"related":24,"faq":42,"body":58,"_type":623,"_id":624,"_source":625,"_file":626,"_stem":627,"_extension":628,"sitemap":629},"/glossary/webhook-url","glossary",false,"","What is a webhook URL? Definition and how to get one","A webhook URL is the public HTTPS address a service sends webhook requests to. What it looks like, the two kinds of webhook URL, and how to get one.","reference","Webhook URL","The public address a provider sends webhook requests to.","Basics",2,"2026-10-28",{"eyebrow":17,"title":18,"text":19,"to":20,"label":21,"secondaryTo":22,"secondaryLabel":23},"Webhook Relay","Get a webhook URL in one click, or a permanent one for localhost","Webhook Bin gives you a free public webhook URL that records every request, with no signup. Webhook Relay gives you a permanent URL that forwards each webhook to localhost, a private network or any server, with retries and logs.","/webhook-bin/","Get a free webhook URL","/webhook-gateway/","Webhook forwarding",[25,28,31,34,37,40],{"title":26,"to":27},"Webhook endpoint","/glossary/webhook-endpoint/",{"title":29,"to":30},"Callback URL","/glossary/callback-url/",{"title":32,"to":33},"Webhook payload","/glossary/webhook-payload/",{"title":35,"to":36},"How to get a Slack webhook URL","/blog/how-to-get-slack-webhook-url/",{"title":38,"to":39},"How to get a Discord webhook URL","/blog/how-to-get-discord-webhook-url/",{"title":23,"to":41},"/glossary/webhook-forwarding/",[43,46,49,52,55],{"q":44,"a":45},"What is a webhook URL?","A webhook URL is the public HTTPS address that a service sends webhook requests to. You paste it into the provider's settings, and every time a subscribed event happens the provider sends an HTTP POST with the event data to that address.",{"q":47,"a":48},"How do I get a webhook URL?","If you are receiving webhooks, the URL is the public address of a route on your own server, for example https://api.example.com/webhooks/stripe. For quick testing, a free request bin such as Webhook Bin gives you a public URL instantly. If you want to post messages into Slack or Discord, those apps generate an incoming webhook URL for you in their settings.",{"q":50,"a":51},"Is a webhook URL the same as a webhook endpoint?","In practice, yes. The webhook URL is the address you give the provider, and the webhook endpoint is the code on your server that handles requests sent to it. Most documentation uses the two terms interchangeably.",{"q":53,"a":54},"Can I use a localhost URL as a webhook URL?","No. The provider sends requests from its own servers over the internet and cannot reach localhost or a private IP address. Use a forwarding tool that gives you a public URL and delivers the requests to your local server over an outbound connection.",{"q":56,"a":57},"Should I keep my webhook URL secret?","Treat it as semi-private. For incoming webhook URLs such as Slack and Discord, the URL itself is the credential, so anyone who has it can post messages. For URLs you host, verify the webhook signature on every request rather than relying on the URL being hard to guess.",{"type":59,"children":60,"toc":616},"root",[61,92,101,106,113,118,225,231,239,286,309,327,370,389,411,417,493,499,582,610],{"type":62,"tag":63,"props":64,"children":65},"element","p",{},[66,73,75,82,84,90],{"type":62,"tag":67,"props":68,"children":69},"strong",{},[70],{"type":71,"value":72},"text","A webhook URL is the public HTTPS address that a service sends webhook requests to.",{"type":71,"value":74}," You register it with a provider such as Stripe, GitHub or Shopify, and whenever a subscribed event happens, the provider sends an HTTP ",{"type":62,"tag":76,"props":77,"children":79},"code",{"className":78},[],[80],{"type":71,"value":81},"POST",{"type":71,"value":83}," with the event data to that address. The code that handles those requests is the ",{"type":62,"tag":85,"props":86,"children":87},"a",{"href":27},[88],{"type":71,"value":89},"webhook endpoint",{"type":71,"value":91},".",{"type":62,"tag":93,"props":94,"children":96},"pre",{"code":95},"https://api.example.com/webhooks/stripe\nhttps://hooks.slack.com/services/T0000000/B0000000/XXXXXXXXXXXXXXXXXXXXXXXX\nhttps://discord.com/api/webhooks/123456789012345678/abcDEF...\n",[97],{"type":62,"tag":76,"props":98,"children":99},{"__ignoreMap":7},[100],{"type":71,"value":95},{"type":62,"tag":63,"props":102,"children":103},{},[104],{"type":71,"value":105},"The first is a URL you host to receive events. The other two are generated by Slack and Discord so that you can send messages into them.",{"type":62,"tag":107,"props":108,"children":110},"h2",{"id":109},"two-kinds-of-webhook-url",[111],{"type":71,"value":112},"Two kinds of webhook URL",{"type":62,"tag":63,"props":114,"children":115},{},[116],{"type":71,"value":117},"People say \"webhook URL\" for two opposite things, and it helps to know which one a guide means.",{"type":62,"tag":119,"props":120,"children":121},"table",{},[122,144],{"type":62,"tag":123,"props":124,"children":125},"thead",{},[126],{"type":62,"tag":127,"props":128,"children":129},"tr",{},[130,134,139],{"type":62,"tag":131,"props":132,"children":133},"th",{},[],{"type":62,"tag":131,"props":135,"children":136},{},[137],{"type":71,"value":138},"Receiving webhook URL",{"type":62,"tag":131,"props":140,"children":141},{},[142],{"type":71,"value":143},"Incoming webhook URL",{"type":62,"tag":145,"props":146,"children":147},"tbody",{},[148,167,185,203],{"type":62,"tag":127,"props":149,"children":150},{},[151,157,162],{"type":62,"tag":152,"props":153,"children":154},"td",{},[155],{"type":71,"value":156},"Who hosts it",{"type":62,"tag":152,"props":158,"children":159},{},[160],{"type":71,"value":161},"You",{"type":62,"tag":152,"props":163,"children":164},{},[165],{"type":71,"value":166},"The destination app (Slack, Discord, Teams)",{"type":62,"tag":127,"props":168,"children":169},{},[170,175,180],{"type":62,"tag":152,"props":171,"children":172},{},[173],{"type":71,"value":174},"Who sends to it",{"type":62,"tag":152,"props":176,"children":177},{},[178],{"type":71,"value":179},"A provider like Stripe or GitHub",{"type":62,"tag":152,"props":181,"children":182},{},[183],{"type":71,"value":184},"You, your script or another service",{"type":62,"tag":127,"props":186,"children":187},{},[188,193,198],{"type":62,"tag":152,"props":189,"children":190},{},[191],{"type":71,"value":192},"What it's for",{"type":62,"tag":152,"props":194,"children":195},{},[196],{"type":71,"value":197},"Being notified of events",{"type":62,"tag":152,"props":199,"children":200},{},[201],{"type":71,"value":202},"Posting a message into a channel",{"type":62,"tag":127,"props":204,"children":205},{},[206,211,220],{"type":62,"tag":152,"props":207,"children":208},{},[209],{"type":71,"value":210},"How you secure it",{"type":62,"tag":152,"props":212,"children":213},{},[214],{"type":62,"tag":85,"props":215,"children":217},{"href":216},"/glossary/webhook-signature/",[218],{"type":71,"value":219},"Signature verification",{"type":62,"tag":152,"props":221,"children":222},{},[223],{"type":71,"value":224},"Keep the URL secret, since it is the credential",{"type":62,"tag":107,"props":226,"children":228},{"id":227},"how-to-get-a-webhook-url",[229],{"type":71,"value":230},"How to get a webhook URL",{"type":62,"tag":63,"props":232,"children":233},{},[234],{"type":62,"tag":67,"props":235,"children":236},{},[237],{"type":71,"value":238},"To receive webhooks from a provider:",{"type":62,"tag":240,"props":241,"children":242},"ol",{},[243,263,268,273],{"type":62,"tag":244,"props":245,"children":246},"li",{},[247,249,254,256,262],{"type":71,"value":248},"Create a route in your app that accepts ",{"type":62,"tag":76,"props":250,"children":252},{"className":251},[],[253],{"type":71,"value":81},{"type":71,"value":255}," requests, for example ",{"type":62,"tag":76,"props":257,"children":259},{"className":258},[],[260],{"type":71,"value":261},"/webhooks/stripe",{"type":71,"value":91},{"type":62,"tag":244,"props":264,"children":265},{},[266],{"type":71,"value":267},"Deploy it somewhere publicly reachable over HTTPS. The full address, including the path, is your webhook URL.",{"type":62,"tag":244,"props":269,"children":270},{},[271],{"type":71,"value":272},"Paste it into the provider's webhook settings and pick the events you want.",{"type":62,"tag":244,"props":274,"children":275},{},[276,278,284],{"type":71,"value":277},"Copy the ",{"type":62,"tag":85,"props":279,"children":281},{"href":280},"/glossary/webhook-secret/",[282],{"type":71,"value":283},"webhook secret",{"type":71,"value":285}," the provider shows you, and use it to verify each request.",{"type":62,"tag":63,"props":287,"children":288},{},[289,294,296,301,303,308],{"type":62,"tag":67,"props":290,"children":291},{},[292],{"type":71,"value":293},"To test before you have a server:",{"type":71,"value":295}," open ",{"type":62,"tag":85,"props":297,"children":298},{"href":20},[299],{"type":71,"value":300},"Webhook Bin",{"type":71,"value":302}," and you get a public URL straight away. Point the provider at it and you'll see every request it sends, with headers and the full ",{"type":62,"tag":85,"props":304,"children":305},{"href":33},[306],{"type":71,"value":307},"payload",{"type":71,"value":91},{"type":62,"tag":63,"props":310,"children":311},{},[312,317,319,325],{"type":62,"tag":67,"props":313,"children":314},{},[315],{"type":71,"value":316},"To receive webhooks on your own machine:",{"type":71,"value":318}," providers can't reach ",{"type":62,"tag":76,"props":320,"children":322},{"className":321},[],[323],{"type":71,"value":324},"localhost",{"type":71,"value":326},", so you need a public URL that forwards to it. With the relay CLI:",{"type":62,"tag":93,"props":328,"children":332},{"code":329,"language":330,"meta":7,"className":331,"style":7},"relay forward --bucket my-app http://localhost:3000/webhooks\n","bash","language-bash shiki shiki-themes github-dark",[333],{"type":62,"tag":76,"props":334,"children":335},{"__ignoreMap":7},[336],{"type":62,"tag":337,"props":338,"children":341},"span",{"class":339,"line":340},"line",1,[342,348,354,360,365],{"type":62,"tag":337,"props":343,"children":345},{"style":344},"--shiki-default:#B392F0",[346],{"type":71,"value":347},"relay",{"type":62,"tag":337,"props":349,"children":351},{"style":350},"--shiki-default:#9ECBFF",[352],{"type":71,"value":353}," forward",{"type":62,"tag":337,"props":355,"children":357},{"style":356},"--shiki-default:#79B8FF",[358],{"type":71,"value":359}," --bucket",{"type":62,"tag":337,"props":361,"children":362},{"style":350},[363],{"type":71,"value":364}," my-app",{"type":62,"tag":337,"props":366,"children":367},{"style":350},[368],{"type":71,"value":369}," http://localhost:3000/webhooks\n",{"type":62,"tag":63,"props":371,"children":372},{},[373,375,381,383,388],{"type":71,"value":374},"The command prints a permanent public URL. Give that to the provider, and every request is delivered to your local server. See ",{"type":62,"tag":85,"props":376,"children":378},{"href":377},"/docs/webhooks/internal/localhost/",[379],{"type":71,"value":380},"receiving webhooks on localhost",{"type":71,"value":382}," and ",{"type":62,"tag":85,"props":384,"children":385},{"href":41},[386],{"type":71,"value":387},"webhook forwarding",{"type":71,"value":91},{"type":62,"tag":63,"props":390,"children":391},{},[392,397,399,404,405,410],{"type":62,"tag":67,"props":393,"children":394},{},[395],{"type":71,"value":396},"To post into Slack or Discord:",{"type":71,"value":398}," the app generates the URL for you. Follow the step-by-step guides for ",{"type":62,"tag":85,"props":400,"children":401},{"href":36},[402],{"type":71,"value":403},"getting a Slack webhook URL",{"type":71,"value":382},{"type":62,"tag":85,"props":406,"children":407},{"href":39},[408],{"type":71,"value":409},"getting a Discord webhook URL",{"type":71,"value":91},{"type":62,"tag":107,"props":412,"children":414},{"id":413},"what-a-good-webhook-url-looks-like",[415],{"type":71,"value":416},"What a good webhook URL looks like",{"type":62,"tag":418,"props":419,"children":420},"ul",{},[421,442,452,476],{"type":62,"tag":244,"props":422,"children":423},{},[424,429,431,441],{"type":62,"tag":67,"props":425,"children":426},{},[427],{"type":71,"value":428},"HTTPS with a valid certificate.",{"type":71,"value":430}," Many providers refuse plain HTTP, and self-signed certificates fail with errors like ",{"type":62,"tag":85,"props":432,"children":434},{"href":433},"/http-errors/err-cert-authority-invalid/",[435],{"type":62,"tag":76,"props":436,"children":438},{"className":437},[],[439],{"type":71,"value":440},"ERR_CERT_AUTHORITY_INVALID",{"type":71,"value":91},{"type":62,"tag":244,"props":443,"children":444},{},[445,450],{"type":62,"tag":67,"props":446,"children":447},{},[448],{"type":71,"value":449},"Stable.",{"type":71,"value":451}," If the URL changes every time you restart a tool, you have to update the provider's settings each time and you lose events sent in between.",{"type":62,"tag":244,"props":453,"children":454},{},[455,460,462,467,468,474],{"type":62,"tag":67,"props":456,"children":457},{},[458],{"type":71,"value":459},"One path per provider.",{"type":71,"value":461}," ",{"type":62,"tag":76,"props":463,"children":465},{"className":464},[],[466],{"type":71,"value":261},{"type":71,"value":382},{"type":62,"tag":76,"props":469,"children":471},{"className":470},[],[472],{"type":71,"value":473},"/webhooks/github",{"type":71,"value":475}," keep signature checks and parsing separate.",{"type":62,"tag":244,"props":477,"children":478},{},[479,484,486,491],{"type":62,"tag":67,"props":480,"children":481},{},[482],{"type":71,"value":483},"No secrets in the query string.",{"type":71,"value":485}," Query strings end up in logs. Use a ",{"type":62,"tag":85,"props":487,"children":488},{"href":216},[489],{"type":71,"value":490},"signature",{"type":71,"value":492}," instead.",{"type":62,"tag":107,"props":494,"children":496},{"id":495},"common-mistakes",[497],{"type":71,"value":498},"Common mistakes",{"type":62,"tag":418,"props":500,"children":501},{},[502,519,529,562,572],{"type":62,"tag":244,"props":503,"children":504},{},[505,517],{"type":62,"tag":67,"props":506,"children":507},{},[508,510,515],{"type":71,"value":509},"Using ",{"type":62,"tag":76,"props":511,"children":513},{"className":512},[],[514],{"type":71,"value":324},{"type":71,"value":516}," or a private IP.",{"type":71,"value":518}," The provider's servers can't reach it, and deliveries fail or time out.",{"type":62,"tag":244,"props":520,"children":521},{},[522,527],{"type":62,"tag":67,"props":523,"children":524},{},[525],{"type":71,"value":526},"A temporary tunnel URL that changes.",{"type":71,"value":528}," Free tunnel tools often give a new random hostname on each restart, so the URL in the provider's settings quietly goes stale.",{"type":62,"tag":244,"props":530,"children":531},{},[532,537,539,545,547,553,555,560],{"type":62,"tag":67,"props":533,"children":534},{},[535],{"type":71,"value":536},"A redirect.",{"type":71,"value":538}," Many senders don't follow ",{"type":62,"tag":76,"props":540,"children":542},{"className":541},[],[543],{"type":71,"value":544},"301",{"type":71,"value":546}," or ",{"type":62,"tag":76,"props":548,"children":550},{"className":549},[],[551],{"type":71,"value":552},"308",{"type":71,"value":554}," redirects for ",{"type":62,"tag":76,"props":556,"children":558},{"className":557},[],[559],{"type":71,"value":81},{"type":71,"value":561},", so a missing trailing slash or an HTTP-to-HTTPS redirect can break delivery. Register the final URL.",{"type":62,"tag":244,"props":563,"children":564},{},[565,570],{"type":62,"tag":67,"props":566,"children":567},{},[568],{"type":71,"value":569},"Pointing at the wrong environment.",{"type":71,"value":571}," Keep separate URLs and secrets for production, staging and local development.",{"type":62,"tag":244,"props":573,"children":574},{},[575,580],{"type":62,"tag":67,"props":576,"children":577},{},[578],{"type":71,"value":579},"Leaking an incoming webhook URL.",{"type":71,"value":581}," A Slack or Discord webhook URL committed to a public repository lets anyone post into your channel. Rotate it if that happens.",{"type":62,"tag":63,"props":583,"children":584},{},[585,587,593,595,601,603,609],{"type":71,"value":586},"If you need one URL that several services receive, or a URL in front of an endpoint that is sometimes down, put a ",{"type":62,"tag":85,"props":588,"children":590},{"href":589},"/glossary/webhook-gateway/",[591],{"type":71,"value":592},"webhook gateway",{"type":71,"value":594}," in front: it accepts the request, stores it, and ",{"type":62,"tag":85,"props":596,"children":598},{"href":597},"/glossary/webhook-fan-out/",[599],{"type":71,"value":600},"fans it out",{"type":71,"value":602}," with ",{"type":62,"tag":85,"props":604,"children":606},{"href":605},"/glossary/webhook-retries/",[607],{"type":71,"value":608},"retries",{"type":71,"value":91},{"type":62,"tag":611,"props":612,"children":613},"style",{},[614],{"type":71,"value":615},"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);}",{"title":7,"searchDepth":617,"depth":617,"links":618},3,[619,620,621,622],{"id":109,"depth":14,"text":112},{"id":227,"depth":14,"text":230},{"id":413,"depth":14,"text":416},{"id":495,"depth":14,"text":498},"markdown","content:glossary:webhook-url.md","content","glossary/webhook-url.md","glossary/webhook-url","md",{"loc":4},1793199804459]