Go SDK

Official Go SDK for TruthMark invisible watermarking.

Installation

go get github.com/truthmark/sdk

Quick Example

package main

import (
    "fmt"
    "log"
    truthmark "github.com/truthmark/sdk"
)

func main() {
    client := truthmark.NewClient(&truthmark.Config{
        APIKey:  "tm_live_your_key",
        BaseURL: "https://truthmark-api.onrender.com",
    })

    // Encode
    result, err := client.Encode("image.png", "Copyright 2025")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Download: %s\n", result.DownloadURL)
    fmt.Printf("PSNR: %.1f dB\n", result.Metadata.PSNR)

    // Decode
    decoded, err := client.Decode("watermarked.png")
    if err != nil {
        log.Fatal(err)
    }
    if decoded.Found {
        fmt.Printf("Message: %s\n", decoded.Message)
        fmt.Printf("Confidence: %.1f%%\n", decoded.Confidence*100)
    }
}

API Reference

NewClient(config *Config)

Create a new client. Pass nil for defaults.

type Config struct {
    APIKey  string
    BaseURL string
    Timeout time.Duration
}

Encode(imagePath, message string) (*EncodeResult, error)

Embed an invisible watermark into an image.

type EncodeResult struct {
    Status      string
    DownloadURL string
    Filename    string
    Metadata    struct {
        PSNR         float64
        BitsEmbedded int
    }
}

Decode(imagePath string) (*DecodeResult, error)

Extract watermark from an image.

type DecodeResult struct {
    Found      bool
    Message    string
    Confidence float64
}

Error Handling

result, err := client.Encode("image.png", "message")
if err != nil {
    var apiErr *truthmark.APIError
    if errors.As(err, &apiErr) {
        switch apiErr.StatusCode {
        case 401:
            log.Println("Invalid API key")
        case 429:
            log.Printf("Rate limited. Retry after %ds", apiErr.RetryAfter)
        default:
            log.Printf("API error %d: %s", apiErr.StatusCode, apiErr.Message)
        }
    } else {
        log.Printf("Network error: %v", err)
    }
}

Advanced Examples

HTTP Handler

func watermarkHandler(w http.ResponseWriter, r *http.Request) {
    file, _, err := r.FormFile("image")
    if err != nil {
        http.Error(w, "Missing image", http.StatusBadRequest)
        return
    }
    defer file.Close()

    msg := r.FormValue("message")
    result, err := client.Encode(file, msg)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    json.NewEncoder(w).Encode(result)
}

Concurrent Batch Processing

func batchWatermark(files []string, message string) []EncodeResult {
    var wg sync.WaitGroup
    results := make([]EncodeResult, len(files))
    sem := make(chan struct{}, 5) // concurrency limit

    for i, f := range files {
        wg.Add(1)
        go func(idx int, path string) {
            defer wg.Done()
            sem <- struct{}{}
            defer func() { <-sem }()

            result, err := client.Encode(path, message)
            if err != nil {
                log.Printf("Failed %s: %v", path, err)
                return
            }
            results[idx] = *result
            fmt.Printf("✓ %s: PSNR=%.1f dB\n", path, result.Metadata.PSNR)
        }(i, f)
    }
    wg.Wait()
    return results
}

Best Practices

Use os.Getenv for API keys

Never hardcode credentials — read from environment.

Handle errors idiomatically

Use errors.As() to inspect *truthmark.APIError for status codes.

Reuse client across goroutines

The client is safe for concurrent use — create once, share everywhere.

Need Help?

Check out the API reference or reach out to support.