Skip to main content
More in Learn

Working With Webhooks Programmatically

As part of catalogue maintenance, order fulfilment, subscription management, tenant copy, and other processes, you may want to subscribe to various events (such as order creation) so that you can respond to them in a constructive fashion. In Crystallize, you do this by setting up webhooks. You can use either the Crystallize App or the PIM API to manage what Crystallize-related events you want to listen for and how Crystallize should respond (if at all) when they fire. Here, we’ll discuss how to get the most out of webhooks within your programming.

Webhook Advantages and Examples

There are several advantages to webhooks. For one, you get automated event management without having to use polling, which is almost always wasteful. Perhaps the most important advantage to webhooks is that they enable true decoupling. For example: when you receive payment confirmation on your service API, at that moment, your implementation (code) is probably pushing the order to Crystallize. At the same time, you may also want to send an email to the customer. You could do it within the same implementation:

  • Push order to Crystallize
  • Send email

But by doing it this way, you end up coupling two tasks that ought to remain separate. Modifying either of these processes in the future may have negative impacts on the other. This violates the SOLID paradigm, making your code more complicated and harder to maintain. 

Instead, you should remove pushing orders to Crystallize from your implementation and set up a webhook on onNewOrderCreated. Then, create a separate endpoint in charge of sending the email when the payload is received. This is a basic example, but if you combine it with the order pipeline and many other events that webhooks can listen on, you have the potential to make your code base really simple. Here’s an example of that from our Remix Furniture Boilerplate:

import { ActionFunction, json } from '@remix-run/node';
import { getContext } from '~/use-cases/http/utils';
import { getStoreFront } from '~/use-cases/storefront.server';
import sendOrderCreatedReceipt from '~/use-cases/user/sendOrderCreatedReceipt';
import { createMailer } from '~/use-cases/services.server';

export const action: ActionFunction = async ({ request }) => {
	if (request.method !== 'POST') {
    	return json({ message: 'Method not allowed' }, 405);
	const mailer = createMailer(`${process.env.MAILER_DSN}`);
	const requestContext = getContext(request);
	const { secret } = await getStoreFront(;
	const payload = await request.json();
	await sendOrderCreatedReceipt(mailer, secret.apiClient, payload.order.get);
	return json({ success: true, payload }, 200);

Another good example of a webhook is to purge the HTTP cache when something has changed, as shown in this boilerplate example:

import { ActionFunction, json } from '@remix-run/node';
import { getContext } from '~/use-cases/http/utils';
import { getStoreFront } from '~/use-cases/storefront.server';
import purgeKeys from '~/use-cases/http/fastly/purgeKeys';

export const action: ActionFunction = async ({ request }) => {
	const requestContext = getContext(request);
	const { secret: storefront } = await getStoreFront(;
	// we keep it simple for now and we purge all cache for the tenant identifier
	const keys = [storefront.config.tenantIdentifier];
	const keyPurged = await purgeKeys(keys);
	return json({
    	message: `${Object.keys(keyPurged).length} key(s) soft purged.`,
    	keys: keyPurged,

Some other good use cases for webhooks include:

  • Rebuilding a static website when content has changed
  • Subscription management
  • Managing digital product downloads
  • Notifying customers of new products, blog posts, articles, etc.

Crystallize Webhook Concerns and Events

Here are all of the Crystallize-related concerns and events to which you can subscribe:

Webhook Implementation

Webhook-related mutations and queries are contained within the PIM API. We’re constantly improving Crystallize, so always check the API Docs for the latest schema info.

When defining a webhook, you must specify a callback URL and HTTPS request method (supported types are GET, POST, PUT, PATCH, or DELETE). While your implementation is still in the testing stage, you can set up something like or ngrok to use as a test URL.

You can optionally specify headers and a payload using a GraphQL query (mutations aren’t allowed in the payload). For improved performance, it’s recommended to fetch all the data that your webhook endpoint may need in one go.

For each webhook, Crystallize stores the last 50 invocations sent to the callback URL. For each invocation, you’re able to get the HTTP status code, start and end times, payload, and response body. Webhook requests that fail are not retried.

Deleting a webhook and its invocation history is not reversible, so proceed with care.

Signature Verification

For added security, the requests sent by Crystallize webhooks are signed. It is your responsibility to verify these signatures.

Managing Webhooks Within the Crystallize App

Refer to the User Guide for more information on creating, editing, and deleting webhooks within the Crystallize App.

Check Out Our Webhooks Livestream

People showing thumbs up

Need further assistance?

Ask the Crystallize team or other enthusiasts in our slack community.

Join our slack community