Webhooks

Listen for events from Change so your integration can automatically react to updates.

Webhooks deliver real-time notifications to your server via HTTPS POST requests. Each request body contains a JSON-formatted Event object, and each request includes a signature header you can use to verify its origin.

1. Get started

Webhooks are available on a per-request basis. Reach out to your Change account manager to set up your integration, or contact hello@getchange.io to learn more. Available event types are provided upon request.

2. Create an event handler

Create an endpoint that accepts POST requests and parses the incoming Event. Events have a type property that describes the event, such as donation.status_updated. Events also have an object property which provides a copy of the affected object, such as a Donation.

// An example Event object
{
"id": "evt_1a2b3c4d5e",
"created_at": 803606400,
"sandbox": false,
"type": "donation.status_updated",
"object": {
"id": "d_W5CMj0BBpv5pule6Ach3pScr",
"status": "payout_scheduled",
...
}
}
FieldTypeDescription
typestringThe event type (e.g. donation.status_updated).
idstringUnique identifier for the event.
created_atstringUnix timestamp of when the event was created.
sandboxbooleanWhether the event was generated in sandbox mode.
objectobjectThe full API object associated with the event (e.g. a Donation object).

Example endpoint

The following endpoint reacts to Donation status updates.

const express = require('express');
const app = express();

app.use(express.json());

app.post('/webhooks', (req, res) => {
const event = req.body;

switch (event.type) {
case 'donation.status_updated':
const donation = event.object;
console.log(`Donation ${donation.id} status changed to ${donation.status}`);
// Update your records, notify your user, etc.
break;
default:
console.log(`Unhandled event type: ${event.type}`);
}

res.sendStatus(200);
});

app.listen(3000, () => console.log('Webhook listener running on port 3000'));

3. Test your event handler

We recommend testing your endpoint before going live. With your app running, send yourself a sample webhook to test that your endpoint is working.

curl -X POST -H "Content-Type: application/json" -d '{"id":"evt_1a2b3c4d5e","created_at":803606400,"sandbox":false,"type":"donation.status_updated","object":{"id":"d_W5CMj0BBpv5pule6Ach3pScr","status":"payout_scheduled"}}' http://localhost:3000/webhooks

If your endpoint is working, you should see this message in your server logs:

Donation d_W5CMj0BBpv5pule6Ach3pScr status changed to payout_scheduled

4. Secure your endpoint

Your endpoint should validate that each request was sent by Change. Every webhook request from Change includes an X-Change-Hmac-SHA256 header. This header contains a Base64-encoded HMAC-SHA256 digest of the raw request body, computed using your account’s secret key. To verify that a webhook was sent by Change, recompute the HMAC on your end and compare it to the header value.

const express = require('express');
const crypto = require('crypto');
const app = express();

const secretKey = process.env.CHANGE_SECRET_KEY;

app.post(
'/webhooks',
express.raw({ type: 'application/json' }),
(req, res) => {
const changeHmac = req.headers['x-change-hmac-sha256'];
const digest = crypto
.createHmac('sha256', secretKey)
.update(req.body)
.digest('base64');
const valid = crypto.timingSafeEqual(
Buffer.from(digest, 'base64'),
Buffer.from(changeHmac, 'base64')
);

if (!valid) {
return res.sendStatus(401);
}

// Handle the event

res.sendStatus(200);
}
);

app.listen(3000, () => console.log('Webhook listener running on port 3000'));

5. Register your endpoint

Once your handler is ready, provide your endpoint URL to your Change account manager so Change can begin delivering events.

Event delivery in production

If Change does not receive a 200 OK response back from your endpoint, Change will re-attempt delivery of a webhook. Change will re-send each failed webhook with an exponential back off. In the case of several days of failure, Change will stop re-attempting delivery of that event.

Made with ❤ in San Francisco | Changelog | Status | LLM? Read llms.txt.