Documentation Index
Fetch the complete documentation index at: https://docs.truthlocks.com/llms.txt
Use this file to discover all available pages before exploring further.
Public module: The Go SDK is available as a public Go module. No special
configuration required.
Installation
go get github.com/truthlocks/sdk-go@latest
Quick Start
package main
import (
"context"
"fmt"
"log"
truthlock "github.com/truthlocks/sdk-go"
)
func main() {
// 1. Create client
client := truthlock.NewClient(truthlock.Config{
BaseURL: "https://api.truthlocks.com",
TenantID: "your-tenant-id",
APIKey: "tl_live_...",
})
ctx := context.Background()
// 2. Create and trust an issuer
issuer, err := client.Issuers.Create(ctx, &truthlock.CreateIssuerRequest{
Name: "My Organization",
LegalName: "My Organization Inc.",
DisplayName: "My Org",
})
if err != nil {
log.Fatal(err)
}
if _, err := client.Issuers.Trust(ctx, issuer.ID); err != nil {
log.Fatal(err)
}
// 3. Register a signing key
_, err = client.Keys.Register(ctx, issuer.ID, &truthlock.RegisterKeyRequest{
KID: "key-2026",
Alg: truthlock.AlgEd25519,
PublicKeyB64: "your-public-key-base64url",
})
if err != nil {
log.Fatal(err)
}
// 4. Mint an attestation
attestation, err := client.Attestations.Mint(ctx, &truthlock.MintRequest{
IssuerID: issuer.ID,
KID: "key-2026",
Alg: truthlock.AlgEd25519,
Schema: "degree",
Claims: map[string]interface{}{
"student_name": "Jane Doe",
"degree_type": "Bachelor of Science",
},
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Attestation ID: %s\n", attestation.ID)
// 5. Verify
result, err := client.Verify.VerifyOnline(ctx, &truthlock.VerifyRequest{
AttestationID: attestation.ID,
})
if err != nil {
log.Fatal(err)
}
if result.Verdict == truthlock.VerdictValid {
fmt.Println("Attestation verified successfully")
}
}
Revoke an attestation
Permanently invalidate an attestation. Once revoked, any verification check returns REVOKED. This action cannot be undone — if you need to issue an updated credential instead, use supersede.
package main
import (
"context"
"fmt"
"log"
truthlock "github.com/truthlocks/sdk-go"
)
func main() {
client := truthlock.NewClient(truthlock.Config{
BaseURL: "https://api.truthlocks.com",
TenantID: "your-tenant-id",
APIKey: "tl_live_...",
})
ctx := context.Background()
revoked, err := client.Attestations.Revoke(ctx,
"660e8400-e29b-41d4-a716-446655440001",
&truthlock.RevokeRequest{
Reason: "Certificate holder no longer employed",
},
)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Status: %s\n", revoked.Status) // "REVOKED"
fmt.Printf("Revoked at: %s\n", revoked.RevokedAt)
// Subsequent verifications reflect the revocation
result, err := client.Verify.VerifyOnline(ctx, &truthlock.VerifyRequest{
AttestationID: "660e8400-e29b-41d4-a716-446655440001",
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Verdict: %s\n", result.Verdict) // "REVOKED"
}
Revocation is permanent and recorded in the transparency log. You cannot undo it. Use supersede if you need to replace a credential with an updated version.
Supersede an attestation
Replace an existing attestation with an updated version. The original is marked as SUPERSEDED and linked to the new one, creating an auditable version chain.
package main
import (
"context"
"encoding/base64"
"encoding/json"
"fmt"
"log"
truthlock "github.com/truthlocks/sdk-go"
)
func main() {
client := truthlock.NewClient(truthlock.Config{
BaseURL: "https://api.truthlocks.com",
TenantID: "your-tenant-id",
APIKey: "tl_live_...",
})
ctx := context.Background()
// Supersede an attestation with updated claims
updatedClaims, _ := json.Marshal(map[string]string{
"student_name": "Jane Doe",
"degree_type": "Master of Science", // Updated from Bachelor to Master
})
payloadB64 := base64.RawURLEncoding.EncodeToString(updatedClaims)
result, err := client.Attestations.Supersede(ctx,
"660e8400-e29b-41d4-a716-446655440001", // Original attestation ID
&truthlock.SupersedeRequest{
PayloadB64URL: payloadB64,
},
)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Old attestation status: %s\n", result.Old.Status) // "SUPERSEDED"
fmt.Printf("New attestation ID: %s\n", result.New.ID)
}
Both the original and new attestation remain in the transparency log. Verifiers can trace the full chain using the superseded_by_attestation_id field on the original.
Configuration
client := truthlock.NewClient(truthlock.Config{
// Required
BaseURL: "https://api.truthlocks.com", // API base URL
TenantID: "your-tenant-id", // From Console > Settings
APIKey: "tl_live_...", // From Console > API Keys
// Optional
Timeout: 30 * time.Second, // Request timeout (default: 30s)
MaxRetries: 3, // Auto-retry with backoff (default: 3)
IdempotencyPrefix: "my-app", // Prefix for auto-generated idempotency keys
HTTPClient: &http.Client{}, // Custom HTTP client
})
Idempotency keys
The SDK generates an Idempotency-Key header automatically on every write operation (mint, revoke, supersede), making retries safe by default. If a request fails and is retried, the server returns the original response instead of performing the action twice.
When multiple applications share the same tenant, set IdempotencyPrefix to namespace the auto-generated keys and prevent collisions:
// Service A
clientA := truthlock.NewClient(truthlock.Config{
BaseURL: "https://api.truthlocks.com",
TenantID: "shared-tenant-id",
APIKey: "tl_live_...",
IdempotencyPrefix: "billing-svc",
})
// Service B
clientB := truthlock.NewClient(truthlock.Config{
BaseURL: "https://api.truthlocks.com",
TenantID: "shared-tenant-id",
APIKey: "tl_live_...",
IdempotencyPrefix: "onboarding-svc",
})
With these prefixes, a key generated by the billing service looks like billing-svc_<uuid>, while onboarding produces onboarding-svc_<uuid> — so concurrent requests from different services never conflict.
Idempotency keys expire after 24 hours. If you omit IdempotencyPrefix, keys are generated without a prefix and are still unique per request.
Retry behavior
When MaxRetries is set (default: 3), the SDK automatically retries failed requests using exponential backoff with jitter. You do not need to implement retry logic yourself for transient failures.
What gets retried
The SDK retries a request when all of the following are true:
- The HTTP status code is retryable:
408, 429, 500, 502, 503, or 504
- The retry count has not exceeded
MaxRetries
- The request context has not been cancelled or timed out
Requests that return 400, 401, 403, 404, or 409 are not retried because these indicate a problem with the request itself.
Backoff schedule
Retry delays use exponential backoff with jitter to avoid thundering-herd effects:
| Attempt | Base delay | With jitter (approx.) |
|---|
| 1st retry | 100 ms | 80–120 ms |
| 2nd retry | 200 ms | 160–240 ms |
| 3rd retry | 400 ms | 320–480 ms |
The delay doubles on each attempt and is capped at 2 seconds. Jitter adds up to ±20% randomization.
When the API returns a 429 response with a Retry-After header, the SDK respects the server-specified delay instead of using the calculated backoff. This ensures you do not retry faster than the rate limit allows.
Disabling retries
Set MaxRetries to 0 to disable automatic retries entirely:
client := truthlock.NewClient(truthlock.Config{
BaseURL: "https://api.truthlocks.com",
TenantID: "your-tenant-id",
APIKey: "tl_live_...",
MaxRetries: 0, // No automatic retries
})
The SDK generates idempotency keys automatically for write operations, so retries for mint and revoke calls are safe. See idempotency keys to configure a prefix for multi-service environments.
Error handling
API errors are returned as *truthlock.Error, which implements the standard error interface.
attestation, err := client.Attestations.Mint(ctx, req)
if err != nil {
var tlErr *truthlock.Error
if errors.As(err, &tlErr) {
fmt.Printf("Code: %s\n", tlErr.Code) // e.g. "ISSUER_NOT_TRUSTED"
fmt.Printf("Message: %s\n", tlErr.Message) // Human-readable
fmt.Printf("Status: %d\n", tlErr.Status) // HTTP status code
// Check for specific error types
if tlErr.IsCode("RATE_LIMITED") {
// Already retried MaxRetries times — back off at the application level
}
if tlErr.NotFoundError() {
// Resource does not exist — do not retry
}
}
return err
}
API Methods
All methods accept a context.Context as the first argument for cancellation and deadline propagation.
Issuers
client.Issuers.Create(ctx, req)
client.Issuers.Get(ctx, id)
client.Issuers.List(ctx)
client.Issuers.Trust(ctx, id)
client.Issuers.Suspend(ctx, id, reason)
client.Issuers.Revoke(ctx, id, reason)
Keys
client.Keys.Register(ctx, issuerId, req)
client.Keys.List(ctx, issuerId)
client.Keys.Rotate(ctx, kid, req)
client.Keys.ReportCompromise(ctx, kid)
Attestations
client.Attestations.Mint(ctx, req)
client.Attestations.Get(ctx, id)
client.Attestations.List(ctx)
client.Attestations.Revoke(ctx, id, req)
client.Attestations.Supersede(ctx, id, req) — see supersede API
client.Attestations.GetProofBundle(ctx, id)
Receipts
client.Receipts.Mint(ctx, req)
client.Receipts.Get(ctx, id)
client.Receipts.List(ctx, filter)
client.Receipts.Revoke(ctx, id, req)
client.Receipts.ListTypes(ctx)
client.Receipts.GetType(ctx, name)
client.Receipts.CreateType(ctx, req)
client.Receipts.GetProofBundle(ctx, id) — see proof bundle API
client.Receipts.Verify(ctx, receiptID) — see verify API
client.Receipts.Search(ctx, query) — see search API
client.Receipts.Export(ctx, req) — see export API
client.Receipts.GetExport(ctx, exportID)
client.Receipts.Redact(ctx, id) — see redact API
Verification
client.Verify.VerifyOnline(ctx, req)
API Keys
client.APIKeys.List(ctx)
client.APIKeys.Create(ctx, req)
client.APIKeys.Revoke(ctx, id)
Audit
client.Audit.Query(ctx, params)
client.Audit.Export(ctx, req)
Governance
client.Governance.ListRequests(ctx)
client.Governance.CreateRequest(ctx, req)
client.Governance.ApproveRequest(ctx, id)
client.Governance.ExecuteRequest(ctx, id)
Querying audit logs
Retrieve audit events to track API activity, monitor security events, and generate compliance reports. Filter by action, actor, resource, or time range.
package main
import (
"context"
"fmt"
"log"
truthlock "github.com/truthlocks/sdk-go"
)
func main() {
client := truthlock.NewClient(truthlock.Config{
BaseURL: "https://api.truthlocks.com",
TenantID: "your-tenant-id",
APIKey: "tl_live_...",
})
ctx := context.Background()
// Query recent attestation events
events, err := client.Audit.Query(ctx, map[string]string{
"action": "attestation.mint",
"from": "2026-01-01T00:00:00Z",
"to": "2026-01-31T23:59:59Z",
"limit": "100",
})
if err != nil {
log.Fatal(err)
}
for _, event := range events {
fmt.Printf("[%s] %s by %s\n", event.Timestamp, event.Action, event.ActorID)
}
// Export audit logs for compliance
exportJob, err := client.Audit.Export(ctx, &truthlock.AuditExportRequest{
StartDate: "2026-01-01",
EndDate: "2026-01-31",
Format: "csv",
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Export started: %s (status: %s)\n", exportJob.ID, exportJob.Status)
}
See the audit API reference for the full list of filter parameters.
Governance workflows
Manage formal issuer actions — suspend, revoke, reinstate, and change trust tier — through a multi-party approval workflow. Create a request, collect approvals from authorized reviewers, then execute.
package main
import (
"context"
"fmt"
"log"
truthlock "github.com/truthlocks/sdk-go"
)
func main() {
client := truthlock.NewClient(truthlock.Config{
BaseURL: "https://api.truthlocks.com",
TenantID: "your-tenant-id",
APIKey: "tl_live_...",
})
ctx := context.Background()
// Create a governance request to suspend an issuer
req, err := client.Governance.CreateRequest(ctx, &truthlock.GovernanceRequest{
Action: "suspend",
IssuerID: "issuer-uuid",
Reason: "Compliance review pending",
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Request created: %s (status: %s)\n", req.ID, req.Status)
// Another authorized user approves the request
if _, err := client.Governance.ApproveRequest(ctx, req.ID); err != nil {
log.Fatal(err)
}
// Execute the approved request
result, err := client.Governance.ExecuteRequest(ctx, req.ID)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Executed: %s — issuer is now %s\n", result.ID, result.IssuerStatus)
// List all governance requests
requests, err := client.Governance.ListRequests(ctx)
if err != nil {
log.Fatal(err)
}
for _, r := range requests {
fmt.Printf("[%s] %s %s — %s\n", r.Status, r.Action, r.IssuerID, r.Reason)
}
}
See the governance API reference for the full request and response schemas.
Receipt operations
Mint, verify, search, export, and redact cryptographically signed receipts. See the receipts guide for an overview of receipt types and the full lifecycle.
package main
import (
"context"
"fmt"
"log"
truthlock "github.com/truthlocks/sdk-go"
)
func main() {
client := truthlock.NewClient(truthlock.Config{
BaseURL: "https://api.truthlocks.com",
TenantID: "your-tenant-id",
APIKey: "tl_live_...",
})
ctx := context.Background()
// Mint a payment receipt
receipt, err := client.Receipts.Mint(ctx, &truthlock.MintReceiptRequest{
IssuerID: "your-issuer-id",
KID: "ed-key-2026",
Alg: "Ed25519",
ReceiptType: "payment_receipt",
Subject: "customer@example.com",
Payload: map[string]interface{}{
"amount": 5000,
"currency": "USD",
"provider": "stripe",
"provider_reference": "ch_3Px...",
},
})
if err != nil {
log.Fatal(err)
}
// Verify a receipt
result, err := client.Receipts.Verify(ctx, receipt.ReceiptID)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Verdict: %s\n", result.Verdict) // "VALID"
// Download a proof bundle for offline verification
bundle, err := client.Receipts.GetProofBundle(ctx, receipt.ReceiptID)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Bundle version: %s\n", bundle["bundle_version"])
// Search receipts
results, err := client.Receipts.Search(ctx, &truthlock.SearchReceiptsQuery{
Q: "stripe",
ReceiptType: "payment_receipt",
FromDate: "2026-01-01T00:00:00Z",
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Found %d receipts\n", len(results.Items))
// Export receipts as JSON
exportJob, err := client.Receipts.Export(ctx, &truthlock.CreateExportRequest{
Format: "json",
Filters: map[string]interface{}{"receipt_type": "payment_receipt"},
})
if err != nil {
log.Fatal(err)
}
// Poll for completion
completed, err := client.Receipts.GetExport(ctx, exportJob.ID)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Export status: %s\n", completed.Status)
// Redact PII from a receipt (permanent, for GDPR erasure requests)
_, err = client.Receipts.Redact(ctx, receipt.ReceiptID)
if err != nil {
log.Fatal(err)
}
}
See the receipts API reference for the full request and response schemas.