GolfManager Logo V3 API
Version 2025-07-21

Webhooks

Webhooks allow your application to receive real-time notifications when events occur in the system. Configure webhook endpoints through the UI to start receiving events.

Webhook Types

By default, all webhooks are asynchronous unless otherwise specified in the event documentation. There are two types:

Authentication

All webhook requests include a signature for verification. The signature is generated using HMAC-SHA256 with your webhook signature key.

Headers

Every webhook request includes this headers:

Request Format

Response

Your endpoint must return:

Retry Policy

Failed asynchronous webhooks are retried up to 3 times with exponential backoff:

Synchronous webhooks are not retried. A non-200 response will abort the operation immediately.

Error Handling

Testing Webhooks

We provide a test endpoint to help you validate your webhook implementation before going live:

Endpoint: POST /webhooks/test.json

This endpoint:

Use this endpoint to:

  1. Test your signature calculation
  2. Verify header formatting
  3. Debug payload issues
  4. Ensure your implementation is correct before receiving live webhooks

Best Practices

  1. Always verify signatures to ensure requests come from our servers
  2. Use HTTPS for your webhook endpoints
  3. Store your signature key securely and rotate it periodically
  4. Implement idempotency - the same webhook may be delivered multiple times
  5. Respond quickly - acknowledge receipt immediately, then process asynchronously
  6. Test first - use the test endpoint to validate your implementation

Examples

Node.js (Express)

const express = require('express');
const crypto = require('crypto');

const app = express();
app.use(express.raw({ type: 'application/json' }));

function verifyWebhookSignature(payload, signature, timestamp, secretKey) {
  const expectedSignature = crypto
    .createHmac('sha256', secretKey)
    .update(timestamp + payload)
    .digest('base64');
  
  return signature === expectedSignature;
}

app.post('/webhook', (req, res) => {
  const signature = req.headers['x-webhook-signature'];
  const timestamp = req.headers['x-webhook-timestamp'];
  const payload = req.body.toString();
  
  if (!verifyWebhookSignature(payload, signature, timestamp, process.env.WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }
  
  // Process the webhook
  const data = JSON.parse(payload);
  console.log('Webhook received:', data);
  
  res.status(200).send('OK');
});

app.listen(3000);

Go

package main

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/base64"
    "encoding/json"
    "io"
    "net/http"
    "os"
)

func verifyWebhookSignature(payload, signature, timestamp, secretKey string) bool {
    h := hmac.New(sha256.New, []byte(secretKey))
    h.Write([]byte(timestamp + payload))
    expectedSignature := base64.StdEncoding.EncodeToString(h.Sum(nil))
    return signature == expectedSignature
}

func webhookHandler(w http.ResponseWriter, r *http.Request) {
    body, err := io.ReadAll(r.Body)
    if err != nil {
        http.Error(w, "Failed to read body", http.StatusBadRequest)
        return
    }
    
    signature := r.Header.Get("x-webhook-signature")
    timestamp := r.Header.Get("x-webhook-timestamp")
    secretKey := os.Getenv("WEBHOOK_SECRET")
    
    if !verifyWebhookSignature(string(body), signature, timestamp, secretKey) {
        http.Error(w, "Invalid signature", http.StatusUnauthorized)
        return
    }
    
    // Process the webhook
    var data map[string]interface{}
    json.Unmarshal(body, &data)
    
    w.WriteHeader(http.StatusOK)
    w.Write([]byte("OK"))
}

func main() {
    http.HandleFunc("/webhook", webhookHandler)
    http.ListenAndServe(":3000", nil)
}