Receive and verify FingerPrint Checker webhooks in Go

Updated

Register an HTTPS endpoint in the console, then handle it in Go in four steps: read the raw body, check the FPC-Signature value with fpchecker.VerifyWebhookSignature, ignore an event you have already seen, and answer 2xx within 3 seconds. Do the slow work after you answer. Anything else is retried.

Add the endpoint

In the console, open the project and add a webhook endpoint under Webhooks and retention. The URL must be https on port 443 with a public hostname. The signing secret (whsec_) is shown once when you add the endpoint, so store it in your secret store. Send test event posts a sample marked test: true.

go get github.com/AndrewYukon/FingerPrintChecker/server/[email protected]

All SDKs and install lines →

Verify, then trust

Every request carries FPC-Signature, FPC-Event-Id and FPC-Delivery-Id. Verify the signature on the exact bytes you received before you parse anything: a re-serialised body will not match. The SDK also rejects a request whose time is more than 5 minutes from yours.

package main

import (
	"encoding/json"
	"errors"
	"io"
	"log"
	"net/http"
	"os"
	"time"

	fpchecker "github.com/AndrewYukon/FingerPrintChecker/server/go"
)

var secret = os.Getenv("FPC_WEBHOOK_SECRET") // whsec_..., shown once in the console

type event struct {
	EventID   string `json:"eventId"`
	VisitorID string `json:"visitorId"`
	LinkedID  string `json:"linkedId"`
	Test      bool   `json:"test"`
}

func webhook(w http.ResponseWriter, r *http.Request) {
	body, err := io.ReadAll(http.MaxBytesReader(w, r.Body, 1<<20))
	if err != nil {
		http.Error(w, "bad body", http.StatusBadRequest)
		return
	}

	err = fpchecker.VerifyWebhookSignature(body, r.Header.Get("FPC-Signature"), secret, 5*time.Minute, time.Now())
	if err != nil {
		var bad *fpchecker.WebhookSignatureError
		if errors.As(err, &bad) {
			log.Printf("webhook refused: %s", bad.Code)
		}
		http.Error(w, "unauthorized", http.StatusUnauthorized)
		return
	}

	var ev event
	if err := json.Unmarshal(body, &ev); err != nil {
		http.Error(w, "bad json", http.StatusBadRequest)
		return
	}
	if !firstTime(ev.EventID) { // your store; duplicates are normal
		w.WriteHeader(http.StatusOK)
		return
	}
	go process(ev) // slow work after the answer
	w.WriteHeader(http.StatusOK)
}

Answer fast, expect repeats

Answer any 2xx within 3 seconds. Other answers and timeouts are retried after 1 minute, 5 minutes, 30 minutes, 2 hours and 6 hours, six attempts in all. Redirects are not followed. An endpoint that fails every delivery for 3 days is disabled, and the console shows why and keeps the last 50 deliveries.

Delivery is at least once, so the same event can arrive twice. Use eventId (or FPC-Event-Id) as the key and make the handler safe to run again.

What to do with an event

Each event is the identify that just happened: eventId, visitorId, linkedId, tags, platform and time. Typical uses are to log it next to your own order or user record, to alert on a flagged event, or to keep a risk table up to date. The decision to allow a trial still comes from the eligibility call on your backend, not from a webhook.

  • Skip events with test: true when you only want real traffic.
  • Rotating the secret takes effect at once, with no grace period: deploy the new secret first, then rotate.
  • Answer 401 to a bad signature and do nothing else with the request.

Webhook delivery and signature reference →

Frequently asked questions

Why must I use the raw body?

The signature covers the exact bytes that were sent. Parsing the JSON and writing it back changes spacing and key order, so the check fails.

Can I verify without the SDK?

Yes. The developer page shows the same check in Node.js, Python and Go using only the standard library.

What happens if my service is down?

Failed deliveries are retried for several hours. After 3 days of failures the endpoint is disabled. Events are also stored, so you can read what you missed from the events API.

Try FingerPrint Checker free

Every new account gets 2,000 free checks. No card required to start.

Create a free account