SDKs
Go SDK
Go watermarking SDK for Etchv. Watermark images, PDFs and videos, detect watermarks, and handle assets, async jobs and webhooks from your Go service.
Needs Go 1.25 or later. Import the package as etchv. Create one client and reuse it; it is safe to use from many goroutines at once. MIT licensed.
Install
go get github.com/etchv-labs/go-sdk@v1.0.0Embed a file
Set ETCHV_API_KEY on your server. This example needs an API key with the watermarks:embed scope, an active plan and available credits.
package main
import (
"context"
"log"
"os"
etchv "github.com/etchv-labs/go-sdk"
)
func main() {
ctx := context.Background()
client, err := etchv.New(os.Getenv("ETCHV_API_KEY"))
if err != nil { log.Fatal(err) }
key, err := client.CheckAPIKey(ctx) // no credits used
if err != nil { log.Fatal(err) }
log.Printf("organization %s, scopes %v", key.OrganizationID, key.Scopes)
file, err := os.ReadFile("photo.jpg")
if err != nil { log.Fatal(err) }
result, err := client.EmbedImage(ctx, file,
map[string]any{"asset": "photo-123"}, etchv.Options{Filename: "photo.jpg"})
if err != nil { log.Fatal(err) }
if err := os.WriteFile(result.Filename, result.Bytes, 0o600); err != nil { log.Fatal(err) }
}CheckAPIKey checks your API key and uses no credits. Save the output bytes as they are. Do not re-encode the file.
Images, documents and video
For every media type, result.Bytes holds the watermarked file in its original format. Embedding also returns WatermarkID, RequestID, ContentType, Filename, AssetID, SourceAssetID and StorageDeliveryID.
Detection returns the watermark it found, a confidence score, and results for each frame or page. It does not return the JSON you sent when you embedded the file. Result fields · Credits.
Client configuration
etchv.New(key, etchv.WithBaseURL(url), etchv.WithTimeout(d), etchv.WithHTTPClient(c))
The default timeout is 120 seconds. Every call takes a context, so you can cancel it. Set Options.Filename and Options.IdempotencyKey on each media request. The examples below assume you already have a client and ctx.
Retries and recovery
Embedding and video detection retry and check job status for you. Image and PDF detection do not. Before you upload, save an idempotency key (a value you choose so a retry isn't run twice). With it, you can recover the request even after your program restarts.
| Situation | Action |
|---|---|
| Client timeout | The accepted job keeps running. Resume it, or resend the same inputs with the same key |
| Resume a job | GetEmbedResult( / GetDetectionResult( |
| Changed inputs | Use a new key. Reusing a key with different inputs returns 409 |
| Collect a saved result | Within 24 hours. See retention |
- Client timeout
- Action
- The accepted job keeps running. Resume it, or resend the same inputs with the same key
- Resume a job
- Action
GetEmbedResult(/ctx, requestID) GetDetectionResult(ctx, requestID)
- Changed inputs
- Action
- Use a new key. Reusing a key with different inputs returns
409
- Collect a saved result
- Action
- Within 24 hours. See retention
Error types and handling
Errors have the type *etchv.Error. Each one has StatusCode, Detail, RequestID and IdempotencyKey. For jobs, it also has JobStatus and ErrorCode when they are available. Status 0 means a network failure, a cancellation or a client deadline. errors.Is(err, context.DeadlineExceeded) works.
var apiErr *etchv.Error
if errors.As(err, &apiErr) {
log.Printf("HTTP %d, request %s: %s", apiErr.StatusCode, apiErr.RequestID, apiErr.Detail)
}See HTTP errors and retry behavior. When you report a failure, include the request ID.
Asset library
These calls use no credits. Downloads need the assets:read scope, and edits need assets:write. Deleting needs assets:delete and the owner or admin role.
List, edit and download assets
page, err := client.ListAssets(ctx, etchv.AssetListOptions{Kind: "watermarked", Limit: 25})
if err != nil { return err }
for _, item := range page.Items {
asset, err := client.GetAsset(ctx, item.ID)
if err != nil { return err }
_, err = client.UpdateAsset(ctx, asset.ID, etchv.AssetUpdate{
Version: asset.Version, Metadata: map[string]any{"campaign": "spring"},
})
if err != nil { return err }
}
// Continue with AssetListOptions{Cursor: *page.NextCursor} and the same filters until nil.
file, err := client.DownloadAsset(ctx, assetID) // file.Bytes, file.ContentType, file.FilenameAn edit replaces the metadata and must send the asset's current version. A 409 conflict means the asset changed since you read it, so fetch it again and redo your edit. DeleteAsset deletes one asset. DeleteAssets deletes 1–50 assets as one action: either all are deleted or none are. Asset fields, filters and retention.
Async jobs and webhooks
To start a job without waiting for the result, call SubmitEmbed or SubmitDetection. You get a receipt back. Set the media type with etchv.MediaImages, etchv.MediaDocuments or etchv.MediaVideos.
Check an embedding job with GetEmbedJob and a detection job with GetDetectionJob.
Submit and collect an async job
Read the file into memory before you submit it.
job, err := client.SubmitEmbed(ctx, etchv.MediaDocuments, pdfBytes,
map[string]any{"delivery": "delivery_001"},
etchv.Options{Filename: "document.pdf", IdempotencyKey: "delivery_001", WebhookID: webhookID})
if err != nil { return err }
status, err := client.GetEmbedJob(ctx, job.RequestID)
if err != nil { return err }
if status.Status == "succeeded" {
result, err := client.GetEmbedResult(ctx, job.RequestID)
// ...
}The webhook is optional. In Java or Rust, pass null or None to skip it. Save the receipt. Job handling.
Manage endpoints and verify webhooks
Methods: CreateWebhook, ListWebhooks, UpdateWebhook, DeleteWebhook, ListWebhookDeliveries, RedeliverWebhook. Viewing webhooks needs webhooks:read. Managing them needs webhooks:write and the owner or admin role. The signing secret is shown only once, so save it.
body, err := io.ReadAll(io.LimitReader(r.Body, 1<<20))
if err != nil { http.Error(w, "bad request", 400); return }
event, err := etchv.VerifyWebhook(body, r.Header, os.Getenv("ETCHV_WEBHOOK_SECRET"))
if err != nil { http.Error(w, "invalid signature", 400); return }
// Deduplicate on event.ID, enqueue work, then return 2xx.VerifyWebhook checks the signature and timestamp. It also checks that the event ID in the body matches the header. Pass the raw request bytes and use a 300-second timestamp tolerance. Then skip events you already handled, queue the work and return 2xx. Verification rules.
GPU processing
On Business or Enterprise plans, pass etchv.Options{Accelerator: etchv.AcceleratorGPU}. GPU processing uses 3× the credits. If processing falls back to CPU, it uses normal credits. result.Accelerator shows which one was used. See
GPU processing.
Choose where results are stored
Connect your bucket. Then pass its destination ID and, if you like, an object key (the file's path in the bucket). Leave both out to use Etchv storage.
Deliver to your bucket
job, err := client.SubmitEmbed(ctx, etchv.MediaDocuments, pdfBytes,
map[string]any{"recipient": "customer-123"}, etchv.Options{
Filename: "report.pdf", IdempotencyKey: "report-export-001",
StorageDestinationID: destinationID, StorageKey: "reports/watermarked.pdf",
})
if err != nil { return err }
// After the watermark job reports succeeded:
delivery, err := client.GetStorageDelivery(ctx, *job.StorageDeliveryID)After the job succeeds, check the delivery with the storage:read scope. Checking before then returns 404. When the status is stored, download the file with DownloadStorageDelivery.
RetryStorageDelivery retries a failed or cancelled delivery at no cost. It needs storage:write and the owner or admin role. To manage destinations and deliveries, use CreateStorageDestination, VerifyStorageDestination, UpdateStorageDestination, DeleteStorageDestination, CreateStorageDelivery. See delivery recovery.