# Webhooks

# Introduction

Webhooks are currently invitation only. If you would like access to Webhooks, please email support@chuffed.org.

Listen for events on your Campaign so your integration can automatically react to data changes in real time.

Chuffed uses Webhooks to notify your application when an event happens in your campaign.

# Steps to receive Webhooks

You can start receiving event notifications in your app using the steps in this section:

  1. Identify the event you want to monitor.
  2. Create a webhook endpoint that accepts a POST request via HTTPS.
    1. Should handle the request by returning a 2XX response.
    2. Payloads are sent as JSON
  3. Register the webhook endpoint by emailing us at support@chuffed.org.
  4. We will setup the endpoint, along with your signed secret key.
  5. You should begin to receive requests shortly.

# Security

Every Webhook is signed with a SHA256 value in the header of the request via the Signature header. This value is a signed version of the payload, using a secret you provide to us to act as the key. When receiving incoming requests, you are able to verify the request came from us by creating the same SHA256 value using the key you've provided.

If the keys match, you can be confident the request came from us.

# Pseudo code

signature = request.getHeader('Signature')
mySignature = hash('sha256', request.bodyAsString(), 'my super cool secret')
if (signature != mySignature) {
  // not from chuffed.org
  return respond(400, 'signature does not match')
}
// process webhook request

# Error handling and retries

# Status codes

If you respond with a non 2XX response from your endpoint, we will assume an error on your end and re-schedule a attempt at a later date. We use an exponential back-off strategy, allowing up to 5 attempts before we stop sending a request (this allows approximately 2 days to process the Webhook). If you're receiving a request you don't want to process, you are still required to provide a 2XX response to stop our retry mechanism.

# Timeouts

There's also a maximum timeout of 2 seconds per request (otherwise we assume the request has failed). If your endpoint is time intensive, we suggest you process the webhook call asynchronously to avoid re-attempts being made.

# Events

# Donation: Payment Confirmed

Trigger: When we have confirmed payment from a donation to our platform.

Payload:

{
  "data": {
    // MANDATORY FIELDS
    "id": 1, // donation id (uint)
    "created_at": // donation created at (string, iso8601 zulu)
    "donation": {"amount", 1295, "currency": "AUD"}, // donation amount (money object),
    "total": {"amount", 1495, "currency": "AUD"}, // total paid by donor (money object)
    "donation_converted": {"amount", 895, "currency": "USD"}, // donation, converted into campaign currency (money object)
    "recurring": true, // is a recurring donation (bool)
    "recurring_period": "weekly", // how often the donation repeats (enum, weekly, monthly, yearly)
    "recurring_iteration": 1, // donation sequence foor recurring donation (uint)
    "payment_type": "stripe", // payment service (enum, offline, stripe, stripe_direct_debit, paypal_marketplace)

    "visibility": "anonymous", // donation visibility (enum, anonymous, visible)
    "payment_vendor": {
      "name": "stripe", // payment service (enum, offline, stripe, stripe_direct_debit, paypal_marketplace)
      "reference": "py_1234abcd", // payment service reference (string)
    }
    "recipient": {
      "type": "campaign", // donation recipient (enum, campaign, fundraiser)
      "id": 12345, // recipient id (uint)
    },
    "campaign": {
      "id": 12345, // campaign id (uint)
      "title": "My Campaign", // campaign title (string)
    },
    "identity": {
      "email": "user@domain.com", // donor email (string),
      "first_name": "Ben", // donor first/given name (string),
      "last_name": "Rowe", // donor last/family name (string),
      "subscriptions": {
        "campaign": true, // donor subscription status to this campaign (bool)
      }
      "address": "123 fake street, fakeville, FA, 12345, MadeUpCountry", // donor address (string)
      "phone": "12345678", // donor phone (string)
    },
    // CONDITIONAL
    "perk": null, // no perk selected
    // OR
    "perk": {
      "title": "Selected perk name", // perk title (string),
      "multiplier": 2, // perk instances (uint),
      "answer": "My perk answer", // perk answer, associated to donation (string|null)
    }
  }
}