Webhook Notifications

What is a Webhook?

A webhook is an HTTP request triggered by an event in a source system (Passwire) and sent to a destination system (your backend), with a payload of data. They are automated; in other words, they are automatically sent out when their event is fired in the source system.

It provides a way for one system (the source) to send data via an HTTP request to another system (the destination) when an event occurs and share information (request payload) about the event that occurred.

Setting up Webhooks

Passwire manages Webhooks at the team level. Passwire will send a HTTP POST request to your endpoint with a JSON payload containing information about the event.

Webhook Setup

Webhook Event Types

Passwire webhooks are triggered by the following events:

Pass issued

Fired when a new pass record is created (e.g., membership card, loyalty card, coupon, ticket, etc.). Contains information about where and when the pass was issued.

Example Use Case: Update your CRM, send a welcome message, or activate a coupon/ticket in your system.

Pass saved

Fired when a user installs a pass in their mobile wallet. Contains information about the wallet type, IP address, and installation time.

Example Use Case: Track pass installation status and wallet type in your CRM.

Pass removed

Fired when a user removes a pass from their mobile wallet. Contains information about the wallet type, IP address, and country where the pass was installed.

Example Use Case: Track pass removal and update your system accordingly.

Pass updated

Fired when a pass record is updated in Passwire. Contains the updated pass data.

Example Use Case: Sync updated pass data with your CRM or other systems.

Ensuring the authenticity of Webhooks

All webhooks sent by Passwire include a X-Passwire-Signature header. Passwire generates this header using a secret signing key that only you and Passwire know. Your team's signing key is setup during sign-up and will never expire. You can access it from the Team >> Integrations page (see above).

To verify the authenticity of webhooks, you can use the signing key to generate your own signature for each webhook. Since only you and Passwire know the key, if both signatures match, then you can be sure that a received event came from us. This step is optional but highly recommended.

Verification

1. Get the value of the X-Passwire-Signature header

It should look like this:

nonce=1671552777;hash=eb4d0dc8853be92b7f063b9f3ba5233eb920a09459b6e6b2c26705b4364db151
2. Extract timestamp and signature from header

Now that you have the header value, parse it to extract the timestamp (nonce) and signature values (hash).

You can do this by splitting using a semicolon character (;) to get elements, then splitting again using an equal sign character = to get key-value pairs.

3. Build signed payload

Passwire creates a signature by first concatenating the timestamp (nonce) with the body of the request, joined with a colon (:).

Build your own signed payload by concatenating:

  • The extracted timestamp (nonce) +
  • A colon (:) +
  • The raw body of the request
4. Hash signed payload

Next, hash your signed payload to generate a signature.

Passwire generates signatures using a keyed-hash message authentication code (HMAC) with SHA256 and a secret key. Compute the HMAC of your signed payload using the SHA256 algorithm, using the secret key for this notification destination as the key. Be sure to convert the signing key from base64 before hashing.

This should give you the expected signature of the webhook event.

5. Compare signatures

Finally, compare the signature value we extracted in Step 2 to the signature you just computed.

If they don't match, you should reject the webhook event. Someone may be sending malicious requests to your webhook endpoint.

Python Example Implementation
import hmac
import hashlib
import base64

def create_signed_payload(timestamp: str, request_body: str) -> str:
  return f"{timestamp}:{request_body}"

def generate_signature(signed_payload: str, base64_signing_key: str) -> str:
  signing_key = base64.b64decode(base64_signing_key)
  signature = hmac.new(signing_key, signed_payload.encode('utf-8'), hashlib.sha256).hexdigest()
  return signature

if __name__ == "__main__":
  nonce = "1742591709280"  # Example timestamp/nonce
  body = '{"user":"john","action":"purchase"}'  # Example request body
  signing_key_b64 = "XXZP2p5b2VLdryBcqSo1+14cwtD6tv/XoDT45MXJwo4="

  signed_payload = create_signed_payload(nonce, body)
  signature = generate_signature(signed_payload, signing_key_b64)

  print("Signed Payload:", signed_payload)
  print("Signature:", signature)
C# Example Implementation
using System;
using System.Security.Cryptography;
using System.Text;

class Program
{
    static string CreateSignedPayload(string timestamp, string requestBody)
    {
        return $"{timestamp}:{requestBody}";
    }

    static string GenerateSignature(string signedPayload, string base64SigningKey)
    {
        var signingKey = Convert.FromBase64String(base64SigningKey);
        using (var hmac = new HMACSHA256(signingKey))
        {
            var payloadBytes = Encoding.UTF8.GetBytes(signedPayload);
            var hash = hmac.ComputeHash(payloadBytes);
            return BitConverter.ToString(hash).Replace("-", "").ToLower();
        }
    }

    static void Main(string[] args)
    {
        string nonce = "1742591709280"; // Example timestamp/nonce
        string body = "{\"user\":\"john\",\"action\":\"purchase\"}"; // Example request body
        string signingKeyB64 = "XXZP2p5b2VLdryBcqSo1+14cwtD6tv/XoDT45MXJwo4=";

        var signedPayload = CreateSignedPayload(nonce, body);
        var signature = GenerateSignature(signedPayload, signingKeyB64);

        Console.WriteLine($"Signed Payload: {signedPayload}");
        Console.WriteLine($"Signature: {signature}");
    }
}

Authenticating Webhooks

As part of every webhook request, we can send a user configurable token value as standard Bearer authentication header, allowing you to authenticate incoming webhook requests. Consider this an additional security layer on top of signature verification.

Frequently asked questions

Q: What happens if we miss a Webhook because of an intermittent issue with our servers?

A: We will retry failed requests up 20 times over several days with exponentially increasing backoff.

Q: How much time do you allow for processing Webhooks on our end?

A: Each individual webhook is expected to be handled within 30 seconds. If the request is not completed within this timeframe, it is aborted and considered a failure. The request will then be queued for a retry. If you need more time than that to process a Webhook, you should consider handling Webhooks asynchronously through a queue.

Top