Skip to main content

Verifying Webhook Signature

Verify the events that Tiltify sends to your Webhook Endpoints.

Tiltify will sign every Webhook request with a signature found in the X-Tiltify-Signature header. By verifying this signature you can verify that the events were sent by Tiltify, and not a malicious third party.

Manual Verification

You can manaually verify the signature by following these steps.

tip

All of the examples on this page use real values, so you can test your verification locally.

1. Extract the Signature

This is found in the X-Tiltify-Signature header. This is base64 encoded, and you will need to decode it to compare signatures.

Example Signature:

4OSwlhTt0EcrlSQFlqgE18FOtT+EKX4qTJdJeC8oV/o=

2. Extract the Timestamp

This is found in the X-Tiltify-Timestamp header. This is an ISO-8601 encoded timestamp of when the Webhook Attempt was made. It should be recent to the current timestamp (within the last minute).

Example Timestamp:

2023-04-18T16:49:00.617031Z

3. Extract the Request Body

This is the JSON request body of the POST request to your endpoint.

Example Request Body:

{"data":{"amount":{"currency":"USD","value":"82.95"},"campaign_id":"a4fd5207-bd9f-4712-920a-85f8d92cf4e6","completed_at":"2023-04-18T16:48:26.510702Z","created_at":"2023-04-18T03:36:36.510717Z","donor_comment":"Rerum quo necessitatibus voluptas provident ad molestiae ipsam.","donor_name":"Jirachi","fundraising_event_id":null,"id":"dfa25dcc-2026-4320-a5b7-5da076efeb05","legacy_id":0,"poll_id":null,"poll_option_id":null,"reward_id":null,"sustained":false,"target_id":null,"team_event_id":null},"meta":{"attempted_at":"2023-04-18T16:49:00.617031Z","event_type":"public:direct:donation_updated","generated_at":"2023-04-18T16:48:59.510758Z","id":"d8768e26-1092-4f4c-a829-a2698cd19664","subscription_source_id":"00000000-0000-0000-0000-000000000000","subscription_source_type":"test"}}
caution

You will need the raw string that we send in the request body, do not parse and regenerate the string, as this can change with the spacing and ordering of the string.

4. Generate Signed Payload

This is a string that contains the following parts:

  • The extracted Timestamp in ISO-8601 format
  • The character .
  • The extracted Request Body as a raw string

Signed Payload Format:

"#{timestamp}.#{request_body}"

Example Signed Payload

2023-04-18T16:49:00.617031Z.{"data":{"amount":{"currency":"USD","value":"82.95"},"campaign_id":"a4fd5207-bd9f-4712-920a-85f8d92cf4e6","completed_at":"2023-04-18T16:48:26.510702Z","created_at":"2023-04-18T03:36:36.510717Z","donor_comment":"Rerum quo necessitatibus voluptas provident ad molestiae ipsam.","donor_name":"Jirachi","fundraising_event_id":null,"id":"dfa25dcc-2026-4320-a5b7-5da076efeb05","legacy_id":0,"poll_id":null,"poll_option_id":null,"reward_id":null,"sustained":false,"target_id":null,"team_event_id":null},"meta":{"attempted_at":"2023-04-18T16:49:00.617031Z","event_type":"public:direct:donation_updated","generated_at":"2023-04-18T16:48:59.510758Z","id":"d8768e26-1092-4f4c-a829-a2698cd19664","subscription_source_id":"00000000-0000-0000-0000-000000000000","subscription_source_type":"test"}}

5. Determine the expected signature

Compute an HMAC with the SHA256 function using your Secret Webhook Signing Key found on the Webhook Endpoint's Overview page. Then you will base 64 encode the resulting signature.

Example Expected Signature:

4OSwlhTt0EcrlSQFlqgE18FOtT+EKX4qTJdJeC8oV/o=

6. Compare the Signatures

Now compare the X-Tiltify-Signature with your base 64 encoded expected signature. If they are equal the webhook is verified as coming from Tiltify.

Example Verification

This will show a complete example verification using the values found in this guide:

Secret Webhook Signing Key
13c3b68914487acd1c68d85857ee1cfc308f15510f2d8e71273ee0f8a42d9d00
X-Tiltify-Signature
4OSwlhTt0EcrlSQFlqgE18FOtT+EKX4qTJdJeC8oV/o=
X-Tiltify-Timestamp
2023-04-18T16:49:00.617031Z
Request Body
{"data":{"amount":{"currency":"USD","value":"82.95"},"campaign_id":"a4fd5207-bd9f-4712-920a-85f8d92cf4e6","completed_at":"2023-04-18T16:48:26.510702Z","created_at":"2023-04-18T03:36:36.510717Z","donor_comment":"Rerum quo necessitatibus voluptas provident ad molestiae ipsam.","donor_name":"Jirachi","fundraising_event_id":null,"id":"dfa25dcc-2026-4320-a5b7-5da076efeb05","legacy_id":0,"poll_id":null,"poll_option_id":null,"reward_id":null,"sustained":false,"target_id":null,"team_event_id":null},"meta":{"attempted_at":"2023-04-18T16:49:00.617031Z","event_type":"public:direct:donation_updated","generated_at":"2023-04-18T16:48:59.510758Z","id":"d8768e26-1092-4f4c-a829-a2698cd19664","subscription_source_id":"00000000-0000-0000-0000-000000000000","subscription_source_type":"test"}}
@doc """
Verifies a Tiltify Webhook Signature is valid
"""
@spec verify_signature(
secret :: String.t(),
signature :: String.t(),
timestamp :: String.t(),
body :: String.t()
) :: boolean()
def verify_signature(secret, signature, timestamp, body) do
:hmac
|> :crypto.mac(:sha256, secret, "#{timestamp}.#{body}")
|> Base.encode64()
|> Kernel.==(signature)
end