Webhooks, Service Hooks, and Captain Hooks
- Overview
- Hookable Cheddar Events
- Configure Webhooks
- Integrations with Zapier
- Custom URLs
- Fault Tolerance, Retries, and Reporting
1. Overview
Webhooks make it possible to automatically share the valuable data about customers, transactions, and billing activity contained in your Cheddar product account with external services. This feature gives you the ability to distribute your Cheddar data to other apps in your tech stack or easily customize Cheddar's default billing logic.
You should consider using webhooks if you'd like to do things like:
- Add customers to a CRM system when they subscribe
- Update your accounting system when a transaction runs in Cheddar
- Compile billing data to create custom reporting
- Use an external payment processor
- Add custom charges or credits to customer's invoice when it becomes due
Webhook functionality is included with all Cheddar service levels. No additional fees apply. Some hooks may require a third-party account.
1.1 What are Webhooks?
Webhooks, or service hooks, are basically event notifications sent by Cheddar to external services with helpful data attached. If you have this feature enabled in your Cheddar product account, we'll automatically send webhooks to any hook listeners (the external service that receives the data) you have configured when certain events happen in your product account.
There are two types of listeners available: Zapier and Custom URLs. Zapier is a tool you can use to build custom integrations without having to write code. Use a Custom URL to customize Cheddar's billing behavior or to utilize your own listener logic. See the Zapier and Custom URL sections below for more information.
3. Hookable Cheddar Events
Below is a list of events which are "actionable" via the webhooks system. These events occur naturally within Cheddar during the course of its regular duties. When you enable a listener, you can choose which of these events Cheddar should automatically send to your listener.
3.1 New Subscription
A New Subscription event occurs when a new customer is created in Cheddar. This could occur via any one of several possible actions: API call, your Cheddar dashboard , Hosted Payment Create Page, etc.
3.2 Subscription Changed
This event occurs when a customer's subscribed pricing plan is changed.
3.3 Subscription Canceled
The Subscription Canceled event occurs upon any cancellation. Cancellations can occur automatically - as a result of non-payment for example - but also can be explicitly initiated via the API or GUI.
3.4 Subscription Reactivated
A Subscription Reactivated event occurs when a previously canceled subscription is successfully reactivated.
3.5 Customer Deleted
If a customer is deleted via the API or Cheddar dashboard, a Customer Deleted event occurs.
3.6 Transaction
The Transaction event is triggered by any transaction. A transaction could be for a recurring subscription invoice, one-time invoice, refund, or void. This event is triggered when any transaction is executed with any result. It is also triggered when a transaction is updated with settlement information (e.g., PayPal Echeck or normal ACH).
Differentiating the types of transactions is accomplished by examining the hook payload:
- Refund: The
subscription[invoice][transaction][parentId]
will contain a value (the id of the refunded transaction) and thesubscription[invoice][transaction][amount]
will contain a negative value. - Void: The
subscription[invoice][transaction][response]
will have a value of "voided". - One-time Invoice: The
subscription[invoice][type]
will have a value of "one-time". - Recurring Invoice: The
subscription[invoice][type]
will have a value of "subscription".
For more information about the data your listener will receive from a Cheddar hook, see the data common to all hooks section below.
3.7 Subscription Billable
The Subscription Billable event is triggered by the recurring engine when an invoice becomes billable. If a hook is registered for the Subscription Billable event, the recurring engine will not execute the invoice immediately. Rather, the hook is sent which gives you (or more accurately your hook listener) the opportunity to execute arbitrary actions prior to transacting the invoice. Only after all Billable hooks are successfully processed by your listeners will the invoice be transacted. For more information, see the Invoice Review article.
3.8 Bill Reminder
The Bill Reminder event is triggered according to your settings.
3. Configure Webhooks in Cheddar
Configure webhooks in your Cheddar product dashboard by visiting the "Configuration" menu and selecting Service Hooks. From there, select your hook listener: Custom URL or Zapier.
If you're using a Custom URL, enter the URL that will receive hook data (listener) and choose the events that will trigger a hook being sent to that listener. You can configure as many hook listeners as you'd like. Once your listeners are configured, hit save and Cheddar will begin sending hooks right away.
Custom URL listeners can be turned on and off. When turned back on, they pick up right where they left off with your credentials intact. To toggle a service on or off, just check or uncheck their "Active" box
If you're using Zapier, you'll manage hooks from your Zapier dashboard rather than your Cheddar dashboard.
Once hooks are activated, Cheddar will log all the events sent on your behalf in the Hooked Activity Log. You can view this log by visiting the "Activity" menu in your Cheddar dashboard and selecting "Hooked Activity".
Cheddar has fault tolerance built into the hooks system that will automatically try to resend a hook if it fails to reach your configured listener, but you also have the option to manually retry the hook from the hooked activity log. Select the check box next to the hooks you’d like to resend and retry.
4. Integrations with Zapier
Zapier is a service that routes webhooks between platforms. Without writing any code, you can configure Zaps that will automatically take hooks fired by Cheddar and direct them to a 3rd party application to perform an action based on the data that was received.
In practical terms, this tool allows you to build custom integrations with any of the thousands of apps supported by Zapier with ease. Our customers often use this tool to automate actions like:
- Transferring transaction data from Cheddar to accounting systems like Quickbooks
- Updating custom reports in Google Sheets
- Creating a Slack notification when a new customer signs-up for a subscription
- Updating your CRM, like Salesforce or Agile, when customers sign-up or change their subscriptions
4.1 Getting Started with Zapier
Sign-up for a Zapier account here.
Then create your first Zap! You'll create and manage Zaps within your Zapier account. Zaps are like recipes for automating tasks between two web applications. They are made up of 2 primary components:
- Triggers
- Actions
You can use Cheddar's hookable events as triggers, then select any of Zapier's supported apps where the action will take place. Once the Zap is live, Zapier will begin automatically performing the actions you've configured whenever a trigger is received.
To connect your Cheddar product account to Zapier you'll need your Cheddar username, which is the email address you use to log in, and your product account's secret key. You can find your product secret key here.
When you're configuring an action, you may also have the option to apply specific fields from the trigger data to go to the action.
For example, let's say you'd like approved transactions from Cheddar to trigger an action to update to the customer record in your CRM platform. The transaction trigger is fired by Cheddar when a transaction with any result takes places (approved, declined, error, refund, void).
To ensure the customer update action only happens when a transaction is approved you can filter the action based on the subscription[invoice][transaction][response]
field having a value of approved
. To see other data contained in Cheddar triggers see the Data Common to All Hooks section.
Those are the basics, but if you're looking for in-depth documentation, see Zapier's 'How to Create A Zap Guide' here.
5. Custom URLs
If you'd rather route your webhooks yourself or create your own listener logic, you can create a custom hook listener. Cheddar provides the capability to send hook data to any URL you choose. Configure a custom hook URL within your Cheddar dashboard. When the Custom URL hook is enabled, Cheddar automatically sends HTTP POST data to the URL whenever the trigger events you've configured occur. You may configure an unlimited number of listeners for webhooks.
5.1 Data Format and Definition
Data is sent in either application/x-www-form-urlencoded
or application/json
format (your choice!) to your listener via HTTP POST.
5.1.1 Data Common to All Hooks
Name | Description |
---|---|
activityType | The event type: newSubscription, subscriptionChanged, subscriptionCanceled, subscriptionReactivated, subscriptionBillable, customerDeleted, transaction |
activityDatetime | The moment the event occured in ISO 8601 format |
activityActor | The email address of the authenticated user who initiated the action, if any. |
product[id] | The custom code for the product account |
product[code] | Your custom code for the product account |
product[name] | The name of your product account |
product[subdomain] | Your subdomain, if any. |
customer[id] | Cheddar's unique id for the customer |
customer[code] | Your custom code for the customer |
customer[key] | Customer key hash (primarily for constructing a link to hosted update/status) |
customer[gatewayToken] | The gateway reference for this customer, if any |
customer[firstName] | Customer given name |
customer[lastName] | Customer family name |
customer[email] | Customer email address |
customer[company] | Customer's company |
customer[notes] | Notes regarding this customer |
customer[isTaxExempt] | Flag (1,0) indicating tax exemption status |
customer[taxNumber] | Customer's tax number, if any |
customer[firstContactDatetime] | The moment the customer first contacted you |
customer[referer] | The refering URI for the customer |
customer[campaignSource] | (utm_source) |
customer[campaignMedium] | (utm_medium) |
customer[campaignTerm] | (utm_term) |
customer[campaignContent] | (utm_content) |
customer[campaignName] | (utm_campaign) |
customer[createdDatetime] | The moment this customer was created in Cheddar |
subscription[id] | Cheddar's unique id for the subscription |
subscription[gatewayToken] | Gateway ref for this subscription, if any |
subscription[method] | The payment method type ('none', 'cc', 'paypal', or 'apple') |
subscription[ccType] | The credit card type, if any (visa, mc, disc, amex, diners, jcb) |
subscription[ccLastFour] | Last 4 digits of credit card, if any |
subscription[ccFirstName] | Billing first name (given) |
subscription[ccLastName] | Billing last name (family) |
subscription[ccEmail] | Billing email (paypal only) |
subscription[ccExpirationDate] | Payment method expiration date, if any |
subscription[ccCompany] | Billing company |
subscription[ccCountry] | Billing country |
subscription[ccAddress] | Billing address |
subscription[ccCity] | Billing city |
subscription[ccState] | Billing state/province |
subscription[ccZip] | Billing postal code |
subscription[canceledDatetime] | The moment of cancelation, if any |
subscription[cancelType] | The cancel type, if any (unknown, declined, expired, paypal-wait, customer) |
subscription[cancelReason] | Description of reason for cancelation, if any |
subscription[createdDatetime] | Moment of subscription creation |
subscription[plan][id] | Cheddar's unique id for the plan |
subscription[plan][code] | Your custom code for the plan |
subscription[plan][name] | Plan name |
subscription[plan][description] | Plan description |
subscription[plan][isActive] | Flag (1,0) for the plan |
subscription[plan][isFree] | Flag (1,0) indicating whether or not the plan is free |
subscription[plan][billingFrequencyUnit] | (months, days, none) |
subscription[plan][billingFrequencyQuantity] | Billing frequency per unit |
subscription[plan][recurringChargeCode] | Your code for the flat recurring charge |
subscription[plan][recurringChargeAmount] | Amount of the flat recurring charge |
subscription[plan][setupChargeCode] | Your code for the flat setup fee charge |
subscription[plan][setupChargeAmount] | The setup charge amount |
subscription[plan][createdDatetime] | The moment the plan was created |
subscription[plan][items][<n>][id] | Cheddar's unique id for the item |
subscription[plan][items][<n>][code] | Your custom code for the item |
subscription[plan][items][<n>][name] | Item name |
subscription[plan][items][<n>][quantityIncluded] | Quantity of the item included in the plan |
subscription[plan][items][<n>][isPeriodic] | Flag (1,0) indicating if Cheddar resets item quantity to zero to start a billing period |
subscription[plan][items][<n>][overageAmount] | The amount charged for overage, if any |
subscription[invoice][id] | Cheddar's unique id for the invoice |
subscription[invoice][invoiceNumber] | Invoice number sequence |
subscription[invoice][type] | Invoice type (subscription, setup, one-time) |
subscription[invoice][billingDatetime] | The billable moment for the invoice |
subscription[invoice][previousBillingDatetime] | The date and time of the last billable invoice if applicable. The billing period is between `billingDatetime` and `previousBillingDatetime`. |
subscription[invoice][paidTransactionId] | Cheddar's unique id for the transaction which "paid" this invoice |
subscription[invoice][taxRate] | Tax rate for this invoice |
subscription[invoice][isInitial] | Flag (1,0) indicating whether or not this is the initial recurring invoice |
subscription[invoice][createdDatetime] | The moment this invoice was created |
subscription[invoice][items][<n>][id] | Cheddar's unique id for the item |
subscription[invoice][items][<n>][code] | Your custom code for the item |
subscription[invoice][items][<n>][name] | Item name |
subscription[invoice][items][<n>][quantity] | The customer's quantity of this item as of the hook moment |
subscription[invoice][charges][<n>][id] | Cheddar's unique id for the charge |
subscription[invoice][charges][<n>][code] | Charge code for the charge |
subscription[invoice][charges][<n>][type] | Charge type (custom, setup, recurring, item) |
subscription[invoice][charges][<n>][quantity] | Charge quantity |
subscription[invoice][charges][<n>][eachAmount] | Charge amount for each quantity |
subscription[invoice][charges][<n>][description] | Charge description |
subscription[invoice][charges][<n>][createdDatetime] | Creation date of the charge |
subscription[invoice][transaction][id] | Cheddar's unique id for the transaction |
subscription[invoice][transaction][parentId] | Cheddar's unique id for the parent transaction, if any (if this is a refund) |
subscription[invoice][transaction][gatewayToken] | Gateway reference for this transaction |
subscription[invoice][transaction][amount] | Transaction amount (including tax, if any) |
subscription[invoice][transaction][taxAmount] | Amount of tax, if any |
subscription[invoice][transaction][memo] | Transaction memo |
subscription[invoice][transaction][response] | Transaction status (approved, declined, failed, error, voided) |
subscription[invoice][transaction][responseReason] | Description of the reason for the status |
subscription[invoice][transaction][transactedDatetime] | The moment this transaction was transacted |
5.1.2 Hook-specific Supplemental Data
5.1.2.1 Subscription Changed
The Subscription Changed hook includes the previous subscription information in addition to the data common to all hooks:
Name | Description |
---|---|
previousSubscription[id] | Cheddar's unique id for the subscription |
previousSubscription[gatewayToken] | Gateway ref for this subscription, if any |
previousSubscription[ccType] | The credit card type, if any (visa, mc, disc, amex, diners, jcb) |
previousSubscription[ccLastFour] | Last 4 digits of credit card, if any |
previousSubscription[ccFirstName] | Billing first name (given) |
previousSubscription[ccLastName] | Billing last name (family) |
previousSubscription[ccEmail] | Billing email (paypal only) |
previousSubscription[ccExpirationDate] | Payment method expiration date, if any |
previousSubscription[ccCompany] | Billing company |
previousSubscription[ccCountry] | Billing country |
previousSubscription[ccAddress] | Billing address |
previousSubscription[ccCity] | Billing city |
previousSubscription[ccState] | Billing state/province |
previousSubscription[ccZip] | Billing postal code |
previousSubscription[canceledDatetime] | The moment of cancelation, if any |
previousSubscription[cancelType] | The cancel type, if any (unknown, declined, expired, paypal-wait, customer) |
previousSubscription[cancelReason] | Description of reason for cancelation, if any |
previousSubscription[createdDatetime] | Moment of subscription creation |
previousSubscription[plan][id] | Cheddar's unique id for the plan |
previousSubscription[plan][code] | Your custom code for the plan |
previousSubscription[plan][name] | Plan name |
previousSubscription[plan][description] | Plan description |
previousSubscription[plan][isActive] | Flag (1,0) for the plan |
previousSubscription[plan][isFree] | Flag (1,0) indicating whether or not the plan is free |
previousSubscription[plan][billingFrequencyUnit] | (months, days, none) |
previousSubscription[plan][billingFrequencyQuantity] | Billing frequency per unit |
previousSubscription[plan][recurringChargeCode] | Your code for the flat recurring charge |
previousSubscription[plan][recurringChargeAmount] | Amount of the flat recurring charge |
previousSubscription[plan][setupChargeCode] | Your code for the flat setup fee charge |
previousSubscription[plan][setupChargeAmount] | The setup charge amount |
previousSubscription[plan][createdDatetime] | The moment the plan was created |
previousSubscription[plan][items][<n>][id] | Cheddar's unique id for the item |
previousSubscription[plan][items][<n>][code] | Your custom code for the item |
previousSubscription[plan][items][<n>][name] | Item name |
previousSubscription[plan][items][<n>][quantityIncluded] | Quantity of the item included in the plan |
previousSubscription[plan][items][<n>][isPeriodic] | Flag (1,0) indicating if Cheddar resets item quantity to zero to start a billing period |
previousSubscription[plan][items][<n>][overageAmount] | The amount charged for overage, if any |
5.2 Security
Webhooks contain sensitive customer information. While hooks never include a customer's full credit card number, they do contain some payment information (ccLastFour
, ccType
, billing address) and personally identifiable information (name and email address). Before you enable hooks, you'll want to take a few steps to ensure that your hook data and listeners are protected from potential security vulnerabilities.
5.2.1 Use SSL
You may use an HTTP or an HTTPS URL, but we strongly recommend using an HTTPS URL to secure your hook data in transit and protect against replay attacks for example.
5.2.2 Request Signature Verification
Hook requests are signed for additional security. You may wish to validate a request's signature to ensure that the hook is coming from Cheddar and is well-formed. This is not a requirement but is highly recommended. HTTP POST requests from Cheddar's Custom URL hooks include a special header: X-CG-SIGNATURE. The value of this header is an HMAC sha256 keyed hex hash of an MD5 hash of the request body using your product secret key as the key.
Here are the generic steps to validate a request:
- Get the raw body of the request
- This is the raw content of the HTTP POST request not including the headers
- Calculate the MD5 of the raw request body (we refer to this as the "request token")
- The MD5 hash should be a 32-character hexadecimal number
- Use the sha256 algorithm to generate an HMAC hash of the request token
- use your product secret key as the salt
- The hash should result in a 64-character hexadecimal number
- Compare the result from step 3 to the value of the X-CG-SIGNATURE header in the request.
- If they match, the request can be considered well-formed and from the expected source.
You can find your product secret key here. The key is called a secret key for a reason: it should be kept a secret. It is up to you to protect your secret key.
3.2.2.1 A Ruby example for validating a request:
3.2.2.2 A raw PHP example for validating a request:
Note that in PHP the super global $SERVER keys are the header names altered to be prepended with `HTTP` and dashes are underscores.
3.2.2.3 A basic Zend Framework 1 controller for handling hooks:
3.2.2.4 A basic C# example for validating a request:
3.2.3 X-CG-TOKEN Header
You may have noticed that there is also an X-CG-TOKEN header in hook requests. This header is provided as a convenience. It is the value of the request MD5 hash as calculated prior to request transit. This value should not be used in a production environment for validation of a request. It is provided so that you may compare it to your own calculation of the request hash while in development of your hook listener. Using the value of this header for validation rather than calculating the request token in your listener make a man-in-the-middle attack possible.
3.2.4 Restrict by IP
If you'd like to ensure that hooks are coming from Cheddar's IPs, you can do that, too. Currently, Cheddar uses the following IPs. These are subject to change. If they do, we'll let you know via email, in-app message, or by posting on our status page.
- 3.210.189.123
- 54.89.51.187
- 146.88.119.59
- 198.90.23.194
- 198.90.23.195
- 198.90.23.190
- 198.90.23.192
- 198.90.21.112
- 162.218.137.254
5.3 Testing
5.3.1 See a Post
One of the easiest ways to test a hook is using a basic POST receiver like PostCatcher. Set up a bin, configure the URL in Cheddar, then cause an event to happen in your Cheddar account.
5.3.2 Rerunning Hooks
Often it's useful to trigger resending the same hook payload multiple times for development purposes. You can use the Hooked Activity Log to manually resend (or stop automatic retries) of individual events or events in bulk.
5.3.3 Testing in Your Development Environment
If you’d like to test webhook functionality within your app in a development environment, make sure your webhook endpoint is available on the public internet (Cheddar needs to send hooks to a public location). We recommend you use a tunneling service, such as Ngrok, to make your endpoint publicly available if you’re developing on your local machine.
6 Fault Tolerance, Retries, and Reporting
Hooked events are reported within your Cheddar dashboard in your Hooked Activity log. Information is available to you via this searchable interface that doubles as a workspace for manipulating individual hooks or in bulk.
6.1 Affirmative Response
Your hook listener must respond with an HTTP status of 200-299. If a hook listener responds with an HTTP status code of < 200 or >=300 (or does not respond), the hook is considered to have failed and will be automatically reissued.
It's important to note that if your hook listener throws a 404, Cheddar will consider the hook failed and retry it later. Often a 404 can mean that a dependent entity is not found when processing the hook. Depending on your listener configuration, it could be that this is a normal condition and the hook processing is actually successful. If so, your listener should return a >=200 response status to prevent unnecessary retries.
6.1.1 Timeouts
Hooks are set with a connect timeout of 10 seconds and a read timeout of 30 seconds. That means that if your hook listener doesn't respond within the timeout period, it will be considered a failure with result "Unable to connect" or "Read timed out" in your hooked activity log and it will be retried. If you expect your listener to take more than 10+30 seconds to respond during normal processing, you might consider issuing the response before your processing is complete. Or, do some background processing of lengthy jobs. Or, make your hook tolerant of the retries.
6.2 Automatic Retries
Failed hooks are automatically retried on an exponential backoff schedule. After 16 total attempts over approximately 18 hours, the hook will be marked as "fatal". If a hook fails too many times, it will be auto-disabled. You may manually reenable a hook but any events occurring between auto-disable and reenable cannot be rerun.
6.2.1 Idempotence
Due to the potential for retries as mentioned, it is highly recommended that any logic executed in your hook listener is idempotent. In short, you'll want to make your listener logic tolerant of any retry (unexpected or otherwise) so that you do not have any adverse effects of your logic running more than once.
6.3 Manual Retries and Canceling Retries
Cheddar includes a hooked activity log for you to manually rerun a hook or cancel automatic retries. The hooked activity log can be found here.
Any hook may be retried manually no matter its current state. You may also cancel or stop automatic retries for an individual hook event or in bulk by marking a hook or hooks as "complete" or "fatal".