Webhooks and Events
Webhooks
Spaaza provides webhooks and event notifications which can post a standard JSON object to an external API endpoint. Webhooks are triggered by events in Spaaza and are commonly used to trigger another event in an external system - for example triggering an email to be sent when a customer receives a voucher.
Webhook format
A webhook or event notification is a POST that Spaaza sends to a specific URL. The body of the POST is JSON that includes the object.
As with API endpoints, Spaaza may add fields containing new information to the JSON body of webhooks and event notifications. Spaaza will not remove any fields or change the field type of existing fields in webhooks without prior notification.
Configuring webhooks
Some, but not all, webhooks can be configured as "triggers" in Console. Whenever the relevant event occurs we send a simple POST request to the URL. The body of the POST is JSON that includes the object.
We currently support triggers to the following services:
- Standard webhook (a webhook of standard format sent as a POST to a URL of your choosing)
- Klaviyo
- Mailchimp (Mandrill)
- Shopify
Other webhooks and notifications can be configured manually by our team so please get in touch if you would like us to set one up for you.
Sending verification emails from your own email provider
When email address verification is enabled for your chain, Spaaza emails the shopper either a one-time passcode or a verification link, depending on which method your chain uses.
By default these messages are sent by Spaaza's own email service. They can instead be sent through your own email provider, so that verification emails come from your domain and are handled by the same infrastructure and deliverability rules as the rest of your customer email. This is configured per chain as a webhook configuration on the email verification events email_verification_otp and email_verification_deeplink, using the add-webhook and alter-webhook endpoints or by contacting our Support Team. The webhook type names the email provider:
| Type | Delivered by |
|---|---|
ses | Spaaza's own email service. This is the default when your chain has no email provider of its own, and needs no url or credentials. |
smtp | Your own email provider, over SMTP. Every major transactional email provider supports SMTP, as does your own mail server. |
The type names the delivery channel only: the event the webhook is configured on decides whether a passcode or a verification link is sent.
The following webhook fields are used for a webhook of type smtp:
| Field | Description |
|---|---|
url | The address of your SMTP server, for example smtp://mail.example.com:587. Use smtps://mail.example.com:465 if your server expects TLS to be negotiated before the SMTP session starts. If no port is given, 587 is assumed (465 for smtps://). The address may only contain a host and port: credentials, a path, query string or fragment are rejected. |
identifier_key | The username Spaaza authenticates with. Leave both credentials empty if your server accepts the connection without authentication. |
identifier_secret | The password Spaaza authenticates with. |
send_from | The email address the message is sent from. Most providers only accept senders on a domain you have verified with them, so this normally has to be set. If it is not, your chain's own email address is used. |
event_trigger | Optional. Replaces the default subject line, so the message can carry your own wording and language. |
send_from and event_trigger are also accepted on a webhook of type ses, but are normally left unset.
Notes:
- Your server must support TLS, either through STARTTLS on the submission port or with TLS negotiated immediately on port 465. Spaaza encrypts the connection before sending anything, and will not send a verification message - or your credentials - over an unencrypted one. This applies whether or not the connection is authenticated, because the message itself carries the passcode or verification link.
- Messages are sent as HTML.
- If your provider rejects a message, we do not try that provider again for it. Unless you have configured a backup provider, we do not send the message anywhere else either, so that a verification email is never sent from an unexpected address. Please monitor your provider's logs as you would for your other transactional email. With a backup configured, that one rejection is what hands the message to your backup, which is tried once.
- To move between your own provider and Spaaza's email service later, change the
typeof the existing webhook configuration with alter-webhook rather than adding a second one. Two configurations on the same event that are not paired as primary and backup would send the verification email twice. - The
sesandsmtptypes are only accepted on theemail_verification_otpandemail_verification_deeplinkevents.
Backup email provider
You can configure a second provider as a backup. One provider is then the one that sends, and the other is held in reserve: it is used only when the first fails to accept a message - because it rejects the message, or because we cannot reach it at all - and never at the same time, so a shopper still receives exactly one verification email.
The reserve stands in for a provider that will not take the message from us. It cannot stand in for one that accepts a message and then fails to deliver it to the shopper's mailbox: once your provider has accepted it, what happens next is between it and the recipient, and is visible in your provider's logs rather than to us.
This is what makes a second configuration a backup rather than a duplicate, and it is the only supported way to have Spaaza's own email service stand in for your provider. A common arrangement is your own provider sending, with Spaaza's own email service in reserve - accepting that a message sent while your provider is down will come from a Spaaza address, in exchange for your shoppers not being locked out of verifying their accounts.
The reserve does not have to be Spaaza's email service, though. The two configurations are independent, so a second provider of your own can hold the reserve instead - which keeps every verification email on your own domains, at the cost of both of them being yours to keep working.
A backup is configured by giving the two webhook configurations on the same event complementary delivery_role values: primary for the one that sends and standby for the reserve, using the add-webhook and alter-webhook endpoints or by contacting our Support Team. A configuration without a delivery_role sends unconditionally, as every other webhook does, so two unroled configurations on one event would still send the message twice. The standby is fully active - it is simply not the one that sends first - and it can only be added once a primary exists. A primary cannot be deactivated, deleted, unroled or moved to another event while its standby is live, so that a backup is never left standing behind nothing; deactivate or delete the standby first if you want to turn the backup off.
Spaaza Support is notified the first time the reserve is used, and again once your provider starts accepting messages normally, so a provider that has started failing does not go unnoticed while messages quietly go out through the reserve. We may contact you about it, but do not wait to hear from us if you have already seen it in your own provider's logs - we can see that the fallback happened, not why your provider refused the message.
While the reserve is sending, your shoppers are unaffected, but there is nothing left behind it: a second failure would stop verification messages altogether. Treat it as something to fix rather than a steady state. If the outage will be a long one, switch the two around with swap-webhook-delivery-roles, which makes the reserve the sender and puts your provider back in reserve in a single step, or ask our Support Team to do so.
The same arrangement is available for the SMS messages that verify a phone number, on the phone_number_verification event.
Verifying webhooks are from Spaaza
So that you can verify that the POST originates from Spaaza we provide a signature in a request header (X-Spaaza-Hmac-SHA256).
The value of the header is a base64-encoded HMAC-SHA256 hash of the whole body of the request using a shared secret. Your code to handle the event would need to recreate the signature using the shared secret and compare it to the value in the header.
Signature verification is available for webhooks of type https. To use it, first create a webhook signature key using the add-webhook-signature endpoint, and then link it to your webhook configuration using the webhook_signature_id parameter when creating or updating a webhook with the add-webhook or alter-webhook endpoints.
How HMAC signature verification works
- When a webhook is triggered, Spaaza computes an HMAC-SHA256 hash of the JSON payload using the webhook signature key as the secret.
- The hash is base64-encoded and sent as the
X-Spaaza-Hmac-SHA256header. - When you receive the webhook, recompute the HMAC-SHA256 hash of the raw request body using the same shared secret key, base64-encode it, and compare it to the value in the header.
If the values match, the webhook is authentic and has not been tampered with in transit.
Available webhook events
The following webhook events are available in Spaaza:
| Event name | Description |
|---|---|
shopper.opted-in | Triggered when a user opts in to the loyalty programme |
shopper.opted-out | Triggered when a user opts out of the loyalty programme |
shopper.req-pwd-reset | Triggered when a user requests a password reset |
shopper.altered | Triggered when a user's profile is altered |
shopper.notification | Triggered as a result of a send-notification request |
shopper.joined-mail-list | Triggered when a user joins the mailing list |
shopper.transaction | Triggered when a basket transaction is completed |
shopper.loyalty-level-changed | Triggered when a user's loyalty level changes |
shopper.voucher-issued | Triggered when a voucher is issued to a user |
shopper.voucher-unclaimed | Triggered when a voucher is unclaimed |
shopper.voucher-claimed | Triggered when a voucher is claimed by a user |
shopper.voucher-redeemed | Triggered when a voucher is redeemed |
shopper.voucher-deleted | Triggered when a voucher is deleted |
shopper.voucher-expiring | Triggered when a voucher is approaching its expiry date |
shopper.points-mutation | Triggered when a points or wallet balance mutation occurs |
bulk.vouchers_issued | Triggered for each batch of a bulk voucher issuance run |
Webhook management API endpoints
The following API endpoints are available for managing webhook configurations and webhook signature keys:
Webhook configuration endpoints
- add-webhook - Create a new webhook configuration
- alter-webhook - Update an existing webhook configuration
- get-webhook - Retrieve a single webhook configuration
- get-webhooks - List webhook configurations for a chain
- delete-webhook - Delete a webhook configuration
Webhook signature endpoints
- add-webhook-signature - Create a new webhook signature key
- get-webhook-signature - Retrieve a single webhook signature key
- get-webhook-signatures - List webhook signature keys for a chain
- delete-webhook-signature - Delete a webhook signature key