Go Examples: Complete Integration Guide
Production-ready Go code for integrating FetchHook webhooks using standard library and Go idioms.
#Basic Fetch
Fetch webhooks using Go's standard library http package with proper error handling.
Simple Fetch
go
package main
import (
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
"time"
)
type WebhookEvent struct {
EventID string `json:"event_id"`
Provider string `json:"provider"`
Payload map[string]interface{} `json:"payload"`
Headers map[string]string `json:"headers"`
ReceivedAt time.Time `json:"received_at"`
}
type WebhookResponse struct {
Webhooks []WebhookEvent `json:"webhooks"`
Count int `json:"count"`
HasMore bool `json:"has_more"`
}
func fetchWebhooks(sourceID, apiKey string) (*WebhookResponse, error) {
url := fmt.Sprintf("https://api.fetchhook.app/api/v1/%s", sourceID)
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", apiKey))
client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("API error %d: %s", resp.StatusCode, body)
}
var result WebhookResponse
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return nil, err
}
return &result, nil
}
func main() {
webhooks, err := fetchWebhooks(
os.Getenv("FETCHHOOK_SOURCE_ID"),
os.Getenv("FETCHHOOK_API_KEY"),
)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Received %d webhooks\n", webhooks.Count)
for _, wh := range webhooks.Webhooks {
fmt.Printf(" %s from %s\n", wh.EventID, wh.Provider)
}
}#Polling Loop with Context
Continuously poll for webhooks with graceful shutdown using context cancellation.
Production Polling
go
package main
import (
"context"
"log"
"os"
"os/signal"
"syscall"
"time"
)
type Poller struct {
sourceID string
apiKey string
client *http.Client
interval time.Duration
lastPoll time.Time
}
func NewPoller(sourceID, apiKey string, interval time.Duration) *Poller {
return &Poller{
sourceID: sourceID,
apiKey: apiKey,
client: &http.Client{Timeout: 10 * time.Second},
interval: interval,
lastPoll: time.Now(),
}
}
func (p *Poller) fetchSince(ctx context.Context) (*WebhookResponse, error) {
url := fmt.Sprintf(
"https://api.fetchhook.app/api/v1/%s?since=%s",
p.sourceID,
p.lastPoll.Format(time.RFC3339),
)
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", p.apiKey))
resp, err := p.client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var result WebhookResponse
json.NewDecoder(resp.Body).Decode(&result)
return &result, nil
}
func (p *Poller) Start(ctx context.Context) error {
ticker := time.NewTicker(p.interval)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return ctx.Err()
case <-ticker.C:
webhooks, err := p.fetchSince(ctx)
if err != nil {
log.Printf("Error: %v", err)
continue
}
if webhooks.Count > 0 {
log.Printf("Received %d webhooks", webhooks.Count)
for _, wh := range webhooks.Webhooks {
processWebhook(wh)
}
p.lastPoll = time.Now()
}
}
}
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
go func() {
<-sigChan
cancel()
}()
poller := NewPoller(
os.Getenv("FETCHHOOK_SOURCE_ID"),
os.Getenv("FETCHHOOK_API_KEY"),
10*time.Second,
)
poller.Start(ctx)
}#Stripe Integration
Process Stripe webhooks with signature verification for security.
Stripe + FetchHook
go
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"strings"
)
func verifyStripeSignature(payload, sigHeader, secret string) bool {
parts := strings.Split(sigHeader, ",")
var timestamp, signature string
for _, part := range parts {
kv := strings.SplitN(part, "=", 2)
if len(kv) != 2 {
continue
}
if kv[0] == "t" {
timestamp = kv[1]
} else if kv[0] == "v1" {
signature = kv[1]
}
}
signedPayload := fmt.Sprintf("%s.%s", timestamp, payload)
mac := hmac.New(sha256.New, []byte(secret))
mac.Write([]byte(signedPayload))
expected := hex.EncodeToString(mac.Sum(nil))
return hmac.Equal([]byte(signature), []byte(expected))
}
func processStripeWebhook(wh WebhookEvent, secret string) error {
payload, _ := json.Marshal(wh.Payload)
sigHeader := wh.Headers["stripe-signature"]
if !verifyStripeSignature(string(payload), sigHeader, secret) {
return fmt.Errorf("invalid signature")
}
log.Printf("Processing Stripe event: %s", wh.EventID)
// Handle different event types here
return nil
}
func main() {
webhooks, _ := fetchWebhooks(
os.Getenv("FETCHHOOK_SOURCE_ID"),
os.Getenv("FETCHHOOK_API_KEY"),
)
for _, wh := range webhooks.Webhooks {
if wh.Provider == "stripe" {
processStripeWebhook(wh, os.Getenv("STRIPE_WEBHOOK_SECRET"))
}
}
}#Concurrent Processing
Process webhooks concurrently using goroutines and channels for maximum throughput.
Worker Pool
go
package main
import (
"context"
"sync"
)
type WebhookProcessor struct {
workers int
webhookCh chan WebhookEvent
wg sync.WaitGroup
}
func NewWebhookProcessor(workers int) *WebhookProcessor {
return &WebhookProcessor{
workers: workers,
webhookCh: make(chan WebhookEvent, 100),
}
}
func (p *WebhookProcessor) Start(ctx context.Context) {
for i := 0; i < p.workers; i++ {
p.wg.Add(1)
go p.worker(ctx, i)
}
}
func (p *WebhookProcessor) worker(ctx context.Context, id int) {
defer p.wg.Done()
for {
select {
case <-ctx.Done():
return
case wh := <-p.webhookCh:
log.Printf("Worker %d processing %s", id, wh.EventID)
// Process webhook here
}
}
}
func (p *WebhookProcessor) Submit(wh WebhookEvent) {
p.webhookCh <- wh
}
func (p *WebhookProcessor) Stop() {
close(p.webhookCh)
p.wg.Wait()
}
func main() {
processor := NewWebhookProcessor(5)
ctx, cancel := context.WithCancel(context.Background())
processor.Start(ctx)
webhooks, _ := fetchWebhooks(
os.Getenv("FETCHHOOK_SOURCE_ID"),
os.Getenv("FETCHHOOK_API_KEY"),
)
for _, wh := range webhooks.Webhooks {
processor.Submit(wh)
}
cancel()
processor.Stop()
}Agent Protocol Instruction
For production: Use context for cancellation and timeouts, implement exponential backoff for retries, add proper logging with structured logs, handle rate limits by checking response headers, use worker pools for concurrent processing, and store secrets in environment variables.
←All Resources
Verified for Agentic Workflowsv1.0.4