Thanks for summing up, concisely, why webhooks really aren't magic at all, in the fourth paragraph:
"All [webhooks] are is a promise by an application or API: “when this thing happens, I’ll send this HTTP request with this data.”"
It's nice to see that all the hype really just boils down to generalized pingbacks. Now, having a standard is great -- I'm not entirely sure we have a standard yet, though. It's more like a fancy name for pingbacks that aren't just for blog posts.
I'm also not entirely sure if I'd prefer webhooks for eg. processing emails. Emails are naturally "push" -- they get pushed via SMTP. I know that people are scared of email/SMTP -- but it seems quite a lot easier to make sure you don't loose any "events" with SMTP than with HTTP(S).
What if your site is down for a few minutes, just as some service out there tries to "ping" you with a webhook? Email handles that.
I think what most people seem to mix up is that handling "real" email may be hard -- but looking for a well formed subject that exactly matches some regex, then parsing that, isn't that hard. Everything else you can either bounce (a little dangerous) or just drop (not quite as dangerous).
Mailing-lists work. What you need isn't much more complicated. Maybe allow for a mime-part with a json or xml, or some other well-formed body. Use gpg or smime to encrypt data, if you need something more than a short hash/token.
Partly. But you've been able to do the equivalent of:
echo "|magic-program" > .forward
For a long, long time. Granted, it's a little unclear what happens if you manage to overload your server doing that, but in general MTAs are pretty good at taking care of your mail, either queueing and delivering, or giving up somewhat gracefully.
The trick is to make sure that the "magic-program" is simple and robust -- and that isn't quite as hard as most people think, if you don't require it to parse arbitrary emails -- just accept and parse clearly valid ones, and reject everything else.
Now, if you require to handle enough incoming requests that forking a process per mail is a problem ... you probably need to fix your architecture.
The interesting thing about SMTP is that a mail transfer agent is typically built using queues for both outbound and inbound transmission. Those queues help ensure durability of a message. You could implement webhooks with a transmission queue, retrying for a bit, maybe giving up depending on the nature of webhook. The awesome part about SMTP is that there is so much existing infrastructure in place, although some agents aren't as reliable as others at delivery.
I'm new to webhooks - I understand them conceptually, but is there a standard for authenticating the incoming notification? Take the Stripe example - how do you know that the HTTP POST is actually coming from Stripe? Do you use a shared-secret, or some other mechanism to verify the HTTP request is authentic?
1) Receive a webhook and record the ID in your database. If it's a duplicate, stop.
2) Ask Stripe for the event matching the ID they gave you. If they reject it, stop.
3) Process the event.
It requires a roundtrip, but it's the only sane way to validate that the event you got is real and that you haven't seen it before without trying to validate a signature (lots of other APIs make you do that, though)
> I think it's a little irresponsible to send all the data via web-hook without providing a way to verify it came from Stripe.
Well, as zrail said, you can trivially verify it by fetching the associated event from Stripe and ignoring the webhook body.
We've debated this ourselves, though. While you can theoretically get good security with a combination of SSL and a shared secret in the URL, it's a bit ungainly, and we should perhaps encourage the right thing more straightforwardly by dropping the body from the POST.
For Iron.io, all our requests are authenticated using an OAuth token. So only people that know your OAuth token (and thus could use our API anyways) can use your webhook endpoints.
But there are other ways, too. The most common I've seen is to provide an API endpoint to verify events with (Stripe does this). If you use HTTPS to receive the webhook, and verify it with a request over HTTPS to the API you expect it to be coming from, you're ensuring the request is authentic.
For some webhook styles, it doesn't actually matter. Some people use webhooks to just say "Something happened", without actually saying what. In this style, the API is still responsible for the data and authentication, the webhook just says "Hey, wake up, the API has something new for you."
There isn't a standard way of authenticating the incoming notification yet. I like how Mailgun is doing it, to verify the webhook is originating from Mailgun you need to:
* Concatenate timestamp and token values.
* Encode the resulting string with the HMAC algorithm (using your API Key as a key and SHA256 digest mode).
* Compare the resulting hexdigest to the signature.
* Optionally, you can check if the timestamp is not too far from the current time.
So, you essentially get 3 extra parameters from your webhook, `timestamp`, `token` and `signature`. Obviously, the API key is the shared secret here between your app and Mailgun.
However it depends on a shared secret for generating/verifying signatures, and some companies (cough Stripe cough) have yet to implement that. As someone else has already mentioned, thankfully each webhook request from Stripe has an ID in it so you can query their API for verifying a webhook's authenticity.
If it's just a shared secret, why not just make a secret (and complicated) URL for the web hook? It's shared with the deliverer but as long as you're using SSL it should be just as secure.
Speaking of which, the right way to do this is to validate their SSL client certificate, but I doubt many places are easily setup to do that - in fact I would bet the sending endpoint doesn't even use a certificate most of the time.
MediaCrush updates itself through GitHub hooks . When we get the POST, it git pulls and restarts the site automatically. Best thing we ever did. Not worrying about deployment makes our lives a lot easier.
This is pretty sweet, but it seems like it might be more useful for the baked-in reliability to be on the remote end.
That is, the side sending the POSTs should notice that it didn't get a 200 reply and retry some number of times with backoff. Also, the POSTs need to be idempotent (include a sequence number or nonce or something) so that if the "200 OK" gets lost and the sending side retries, you can discard the duplicates on the receiving side.
That's of course a lot harder since you have to convince everybody whose service you want to use that this is worth the time/effort :)
Sure, that works... but what happens when the service sending the webhook has an issue on their end and they can't send webhooks they should have? Then you are still out a webhook, and your life still sucks...
> "I watch people poll APIs or create convoluted connections, and I cry a little on the inside."
If the action being sent by the webhook is really important, there seems to be no way around doing it somewhat like this: Write a polling (or hanging-get) system, but use webhooks as an unreliable but lower-latency push mechanism.
In this setup, webhooks give you low-latency updates (when they work), and polling ensures completeness.
Depending on your requirements the remote end can be responsible for ensuring that you receive and acknowledge the wake up. Once you hit your own timeout you might do any number of things, not necessarily repoll the other end.
Stripe's webhooks are really nice. Everything that happens with your account, be it charges, disputes, transfers, or people changing subscription levels, all generate events that you can receive and do something with. In my book and the app that sells it I use them to trigger emails to myself when important things happen. There's also an iPhone app named Paypad that registers a webhook on your account that sends you push notifications when things happen.
Ok, maybe it's just the terminology that's confusing, but how do webhooks differ from callback requests? Is it that the semantics are standardized, or is there something that I'm missing? The Author is quite excited about them.
As far as I'm concerned, webhooks and callback requests are the same. If we were going to get pedantic, I suppose we could say that for a request to be a callback, it would have to be a response to an earlier request (think asynchronous processing) whereas a webhook is an evented request (it wasn't prompted by a previous request).
But being pedantic is lame. Let's just be excited about both!
Great post, Paddy. One thing I'd love to see in the HUD is a GitHub OAuth + repo webhook config. That is, you log in with GitHub credentials, you see a list of repos and can select which workers will get POSTed to after specific GitHub repo events.
- The "adelevie/MyJekyllBlog" repo will POST to the worker "BuildAndDeploy" after event "push."
- The "adelevie/MyRailsApp" repo will POST to the worker "TestMyRailsApp" after events "push" and "pull_request."
I love this idea. I've been wanting to do something similar to this, making webhooks even easier to hook into the Iron.io platform. I don't know if it will make it into HUD, but I'm definitely going to be trying to setup some push-button webhook configurations for popular services that support configuring webhooks from an API.
Great an' all, but what happens when a request fails? There's no (specified) retry mechanism — and POSTs aren't meant to be idempotent. I guess many applications don't need strong reliability and so this is cool, but I can't help but feel HTTP's design is really being stretched with ideas like this. I totally get that it's really easy to get stuff going if everything just speaks HTTP, but is it really for the best?
I can't speak for other providers, but our practice is to let users set the number of retries they'd like and the space between retries.
We also are close to rolling out error queues, so when a push notification fails, the message is automatically placed into a queue, to be processed at the user's convenience.
I agree, the need for safe retries (as this is a process without user feedback) calls for idempotency, but that's at odds with the need for a webhook to be possible to be triggered multiple times.
One way around this is to use "tickle" webhooks--webhooks that tell the destination that data has changed, but require the destination to pull in that information themselves. Then the webhook call is still idempotent in practice (at worst, you're refreshing data more than you need to, which is still less than polling), but multiple webhook calls can be made.
> There's no (specified) retry mechanism — and POSTs aren't meant to be idempotent.
The semantics of the data exchanged by POST can be such that the request is effectively idempotent even though that's not an HTTP-level expectation (it's not wrong to have a layered-over-HTTP protocol in which methods are safer than HTTP requires, it would be wrong for them to be less safe than HTTP requires -- e.g., non-idempotent GET.)
If you are sending updated notifications over POST with the actual data then needing to be retrieved by a request the other directions, the first POST is effectively idempotent.
If you are sending the content in the POST without a key to prevent duplication, then there is a problem with retries.