How to POST to a webhook in 5 different programming languages

With the release of inbound webhooks for CrowdSync workflows last week, it
seemed fitting to do a write up on how to POST to a webhook.

So what is a webhook, anyway?

The Wikipedia definition is that webhooks are “used-defined HTTP callbacks”.

Kind of a stuffy definition if you ask me. Webhooks are endpoints (URLs) that
allow you to bridge services together programmatically. You can setup your
system to automatically send data to another service via a webhook.

A real world example would be if you were collecting information for event
attendees via Google Forms. Let’s say you want to put each person into a
CrowdSync workflow to make it easier to onboard them into you event, you could
setup webooks to tie things together!

Now that we know what a webhook is, let’s talk about how you actually implement
a webhook. In effort to cover as many bases as possible, I’m going to cover
POSTing to a webhook in 5 popular, open source languages, Node.js,
Python, Go, PHP and Ruby.

For the sake of consistency and to show off our latest addition, the following
examples will be for CrowdSync’s inbound webhook. The code snippets themselves
can easily be modified to support webhooks from other platforms like Zapier or

Please note that these examples make a couple of hard assumptions about the
webhook you are working with.

First, it’s assumed that the webhook you are hitting is served up over HTTPS.
Just about everybody has made this shift, so it shouldn’t be a big deal.

The other assumption is that the webhook is going to talk back in JSON format.
Most modern systems default to returning JSON so also not a big thing.

If you are working with a webhook that isn’t using HTTPS and/or doesn’t return
JSON you will need to make some adjustments to the code samples.

Also, one last note. I strived to use as little external dependencies as
possible for these examples. You are welcome.


package main

import (

func main() {
	var data map[string]interface{}

	endpoint := ""

	form := url.Values{
		"primaryFirstName": []string{"CrowdSync"},
		"primaryLastName":  []string{"Support"},
		"primaryEmail":     []string{""},

	req, err := http.NewRequest("POST", endpoint, strings.NewReader(form.Encode()))

	if err != nil {

	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")

	client := &http.Client{}
	res, err := client.Do(req)

	if err != nil {

	defer res.Body.Close()
	body, err := ioutil.ReadAll(res.Body)

	if err != nil {

	json.Unmarshal([]byte(string(body)), &data)



const https = require('https'
const querystring = require('querystring'

const payload = querystring.stringify({
  primaryFirstName: 'CrowdSync',
  primaryLastName: 'Support',
  primaryEmail: '',

const options = {
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded',
    'Content-Length': Buffer.byteLength(payload),
  hostname: '',
  method: 'POST',
  path: '/hooks/test',

const request = https.request(options, function (res) {
  res.on('data', function (data) {
    if (res.statusCode !== 200) {
      throw new Error('Not OK (non-200 status code received)'

    if (data) {
      data = JSON.parse(data

      console.log('Response:', data

      // Do some other awesome Node.js stuff here...

request.on('error', function (e) {
  throw new Error(e.message




$curl = curl_init
$url = ''

curl_setopt_array($curl, [
    CURLOPT_URL => $url,
    CURLOPT_HEADER => false,
      'primaryFirstName' => 'CrowdSync',
      'primaryLastName' => 'Support',
      'primaryEmail' => '',

$response = curl_exec($curl

if ($error = curl_error($curl)) {
  throw new Exception($error

$response = json_decode($response, true

var_dump('Response:', $response

// Do some other awesome PHP stuff here...


import json
import requests

url = ''
payload = {
  'primaryFirstName': 'CrowdSync',
  'primaryLastName': 'Support',
  'primaryEmail': '',

response =, data=payload

if response.status_code != 200:
  raise Exception('Not OK (non-200 status code received)')

response = json.loads(response.text

print 'Response:', response


require 'json'
require 'net/http'

response = Net::HTTP.post_form(
  'primaryFirstName' => 'CrowdSync',
  'primaryLastName' => 'Support',
  'primaryEmail' => '',

if response.code != '200'
  raise 'Not OK (non-200 status code received)'

response = JSON.parse(response.body)

puts "Response: #{response}"

That’s all folks, enjoy!

Josh Sherman - The Man, The Myth, The Avatar

About Josh

Husband. Father. Pug dad. Musician. Founder of Holiday API, Head of Engineering and Emoji Specialist at Mailshake, and author of the best damn Lorem Ipsum Library for PHP.

If you found this article helpful, please consider buying me a coffee.