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 the following 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