Skip to main content

Go SDK

The Propper Click Go SDK wraps the public Click REST API with typed structs, idiomatic error handling, and a Stripe-compatible webhook signature verifier.


Install

go get github.com/mypropper/click-sdk-go

Requires Go 1.21+. Uses only the standard library — no external dependencies.


Quick start

package main

import (
"context"
"log"
"os"

click "github.com/mypropper/click-sdk-go"
)

func main() {
c, err := click.NewClient(click.Config{
APIKey: os.Getenv("PROPPER_API_KEY"),
})
if err != nil {
log.Fatal(err)
}

receipt, err := c.Acceptances.Create(context.Background(), click.AcceptConsentInput{
TemplateVersionID: "tv_...",
DeploymentID: "dep_...",
Checksum: "sha256-hex...",
ConsentMethod: click.ConsentBUTTON,
Email: "user@example.com",
}, click.CreateOptions{})
if err != nil {
log.Fatal(err)
}
log.Printf("Receipt: %s", receipt.ReceiptID)
}

Authentication

// API key (default when APIKey is set)
click.NewClient(click.Config{APIKey: "sk_live_..."})

// OAuth bearer
click.NewClient(click.Config{
AuthType: click.AuthBearer,
BearerToken: "eyJ...",
})

Resources

ServiceMethods
AcceptancesCreate(ctx, input, opts)
TemplatesList, Get, Create, Update, Delete
DeploymentsList, Get, Create
RenderByDeployment(ctx, id, opts), ByRoute(ctx, input)
VerificationVerifyReceipt(ctx, input)

Idempotency

c.Acceptances.Create(ctx, input, click.CreateOptions{IdempotencyKey: requestID})

The SDK auto-generates a UUID v4 when IdempotencyKey is empty.


Webhook verification

package main

import (
"errors"
"io"
"net/http"

click "github.com/mypropper/click-sdk-go"
)

func webhookHandler(secret string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
body, _ := io.ReadAll(r.Body)
defer r.Body.Close()

if err := click.VerifyWebhookSignature(click.VerifyWebhookOptions{
Body: body,
SignatureHeader: r.Header.Get("X-Propper-Signature"),
Secrets: []string{secret},
}); err != nil {
var verr *click.WebhookVerificationError
if errors.As(err, &verr) {
http.Error(w, "invalid signature", http.StatusBadRequest)
return
}
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// ... parse body and handle event
w.WriteHeader(http.StatusOK)
}
}

Read body bytes before parsing JSON — the signature is computed over the exact bytes the sender wrote.


Error handling

import "errors"

receipt, err := c.Acceptances.Create(ctx, input, click.CreateOptions{})

var authErr *click.AuthError
var rateLimitErr *click.RateLimitError

switch {
case errors.As(err, &authErr):
// rotate your API key
case errors.As(err, &rateLimitErr):
// back off rateLimitErr.RetryAfterSeconds
default:
log.Print(err)
}

Configuration reference

click.Config{
APIKey: "sk_live_...", // or BearerToken with AuthType: click.AuthBearer
Environment: click.EnvProduction,
BaseURL: "https://api.propper.ai/v1/click",
MaxRetries: 3,
Timeout: 30 * time.Second,
HTTPClient: &http.Client{}, // bring your own transport if needed
}