Go SDK
go.vadyl.dev/sdkCreate the client
Configure the client once. Same object reaches every product surface — entities, workflows, agents, realtime.
package main
import (
"context"
"log"
"os"
sdk "go.vadyl.dev/sdk"
)
func main() {
vadyl := sdk.New(sdk.Options{
APIURL: "https://api.vadyl.app/v1",
Token: os.Getenv("VADYL_TOKEN"),
Tenant: "acme",
Project: "billing",
Branch: os.Getenv("VADYL_BRANCH"), // optional
})
ctx := context.Background()
me, err := vadyl.Identity.Me(ctx)
if err != nil { log.Fatal(err) }
_ = me
}List, filter, paginate
Typed filter expressions, cursor pagination, eager relation loading.
// Typed filter, sort, pagination, and relation expansion
recent, err := vadyl.Orders.List(ctx, sdk.OrderListReq{
Filter: sdk.OrderFilter{
Status: sdk.In("paid", "fulfilled"),
Total: sdk.Gt(100),
CreatedAt: sdk.Gte("now-30d"),
},
Sort: []sdk.Sort{{Field: "createdAt", Direction: "desc"}},
PageSize: 50,
Include: []string{"customer", "items.product"},
})
if err != nil { return err }
// recent.Data: []Order
// recent.Page: { Number, Size, TotalCount, Next, Prev }
fmt.Printf("%d orders, next: %s\n", len(recent.Data), recent.Page.Next)Create, update, delete with idempotency
Every mutation supports idempotency keys. Retries are safe by default.
// Idempotent create — safe to retry
customer, err := vadyl.Customers.Create(ctx, sdk.CustomerCreateInput{
Email: "ada@example.com",
Name: "Ada Lovelace",
}, sdk.WithIdempotencyKey(fmt.Sprintf("signup:%s", session.ID)))
if err != nil { return err }
// Update with optimistic concurrency
updated, err := vadyl.Customers.Update(ctx, customer.ID,
sdk.CustomerUpdate{Name: sdk.Ptr("Ada L.")},
sdk.WithIfMatch(customer.ConcurrencyToken))
// Delete (soft-delete on entities with that lifecycle)
err = vadyl.Customers.Delete(ctx, customer.ID)Subscribe to entity changes
Field names only — never values. Re-read for values through CRUD with proper access enforcement.
// Channel-based subscription with the same filter AST as queries
sub, err := vadyl.Orders.Subscribe(ctx,
sdk.OrderFilter{Status: sdk.Eq("paid")},
sdk.WithKinds(sdk.KindCreated, sdk.KindUpdated))
if err != nil { return err }
defer sub.Close()
for evt := range sub.Events() {
// evt.Kind, evt.EntityID, evt.ChangedFields, evt.OccurredAt
// Field NAMES only — re-read for values
order, err := vadyl.Orders.Read(ctx, evt.EntityID)
if err != nil { continue }
log.Printf("paid: %v\n", order)
}Start and signal durable workflows
Long-running product behavior that survives process restart. Same client interface; canonical durability underneath.
// Start a durable workflow
run, err := vadyl.Workflows.FulfillOrder.Start(ctx, sdk.FulfillOrderInput{
OrderID: "ord_abc",
})
if err != nil { return err }
// Send a signal (e.g. from a webhook)
err = vadyl.Workflows.FulfillOrder.Signal(ctx, run.ID, "shipped",
map[string]any{"trackingNumber": "1Z..."})
// Query run state
state, err := vadyl.Workflows.FulfillOrder.Query(ctx, run.ID)Run agents against the product model
Typed plan IR, capability-aware model routing, token accounting — all behind one method.
// Run an agent — typed plan IR, capability-aware model routing
run, err := vadyl.Agents.SupportAgent.Run(ctx, sdk.AgentRunInput{
Prompt: "Refund order #12345 due to defect",
UserID: session.UserID,
Budget: sdk.AgentBudget{MaxTokens: 50000, MaxToolCalls: 30},
})
if err != nil { return err }
// Stream the run
for step := range run.Stream(ctx) {
log.Printf("%s: %s\n", step.Kind, step.Summary)
}
// Recall memory
facts, err := vadyl.Agents.SupportAgent.Memory.Recall(ctx, sdk.RecallReq{
Kind: "preference",
Subject: session.UserID,
})Typed error contract
Stable error codes, stable reason codes, correlation IDs, and explainability links.
import "errors"
_, err := vadyl.Orders.Update(ctx, id,
sdk.OrderUpdate{Status: sdk.Ptr("refunded")})
var (
accessErr *sdk.AccessDeniedError
conflictErr *sdk.ConflictError
validErr *sdk.ValidationError
quotaErr *sdk.QuotaExceededError
)
switch {
case errors.As(err, &accessErr):
log.Printf("denied: %s (%s)", accessErr.ReasonCode, accessErr.CorrelationID)
case errors.As(err, &conflictErr):
// optimistic-concurrency mismatch — re-read and retry
case errors.As(err, &validErr):
for _, fe := range validErr.FieldErrors { log.Printf("%s: %s", fe.Field, fe.ReasonCode) }
case errors.As(err, "aErr):
log.Printf("quota %s exceeded; resets %s", quotaErr.Kind, quotaErr.ResetAt)
}Define handlers, workflows, agents
The same package you use as a client also powers your authored runtime — defineCoreHandler, defineWorkflow, defineAgent.
package orders
import "go.vadyl.dev/sdk/handler"
type ChargeInput struct{ OrderID string `json:"orderId"` }
type ChargeResult struct{ OK bool; ChargeID string }
var Charge = handler.Core(func(ctx *handler.Ctx, in ChargeInput) (ChargeResult, error) {
order, err := ctx.Entities.Order.Read(in.OrderID)
if err != nil { return ChargeResult{}, err }
charge, err := ctx.Connections.Stripe.CreateCharge(handler.StripeChargeInput{
Amount: order.Total,
Currency: order.Currency,
IdempotencyKey: fmt.Sprintf("charge:%s", order.ID),
})
if err != nil { return ChargeResult{}, err }
if err := ctx.Entities.Order.Update(order.ID, handler.OrderUpdate{
Status: handler.Ptr("paid"),
ChargeID: handler.Ptr(charge.ID),
}); err != nil { return ChargeResult{}, err }
_ = ctx.Events.Emit("order.paid", map[string]any{"orderId": order.ID})
return ChargeResult{OK: true, ChargeID: charge.ID}, nil
})What you get
Generated typed clients
Per-entity typed methods, struct types for every field. go-vet clean. errcheck clean.
Manifest verification
The SDK refuses to talk to a server whose generated-format version it does not support. Fail closed.
Realtime built-in
WebSocket and SSE subscriptions through nhooyr/websocket. Channel-based event delivery. Reconnect-safe.
Context-first
Every method takes context.Context. Graceful cancellation, deadline propagation, request-scoped values throughout.
Zero CGo
Pure Go. Cross-compiles cleanly to Linux, macOS, Windows, FreeBSD on amd64 and arm64.
Branch-aware
Set Options.Branch or VADYL_BRANCH env. Sandbox testing without leaving prod.
OpenTelemetry
Built-in OTel tracing and metrics. Spans on every canonical operation.
Agent primitives
client.Agents exposes the Agent plane: definitions, runs, memory, MCP exposure.
Connections
Typed governed-connection clients per registered governed connection.
Deep-dive documentation
Build with the Go SDK in minutes.
Install the package and work with your product model through typed entities, workflows, agents, and events.