The App SDK is the Go package app code compiles against:
import "github.com/hapyco/dygo/pkg/dygo"Everything under internal/ is private framework implementation. App-owned hooks and Jobs should only depend on pkg/dygo and normal Go packages.
The supported public package is pkg/dygo.
Go SDK - Go code imported by dygo apps
HTTP API - Network API used by clients and StudioApp SDK code is trusted server-side code. It does not run the same permission path as a browser or HTTP client calling the dygo HTTP API.
The current SDK exposes:
- Record lifecycle hook types and registration
- transactional Record reads and writes inside hooks
- durable Job handler types and registration
- Job enqueueing from hooks and Jobs
- best-effort and strict persisted Log helpers
- durable in-app notifications with optional email delivery
- project runner integration types
Record hooks register functions for Entity lifecycle events:
func Register(registry dygo.RecordHookRegistry) error {
return registry.RegisterEntity("crm", "contact", dygo.RecordAfterCreate, "send-welcome", SendWelcome)
}
func SendWelcome(ctx context.Context, hook dygo.RecordHook) error {
return nil
}Supported events:
before-validate
validate
before-create
after-create
before-update
after-update
before-delete
after-deleteHooks receive dygo.RecordHook, which includes the Entity identity, current input, old/new Record snapshots, changes, and SDK services.
Hooks read and write metadata-backed Records through hook.Records:
record, err := hook.Records.Get(ctx, "crm", "contact", 42)
created, err := hook.Records.Create(ctx, "crm", "activity", dygo.RecordInput{
"subject": json.RawMessage(`"Welcome"`),
})
updated, err := hook.Records.Update(ctx, "crm", "contact", 42, dygo.RecordInput{
"status": json.RawMessage(`"Active"`),
})
err := hook.Records.Delete(ctx, "crm", "contact", 42)Record access uses app-scoped Entity identity:
<app>, <entity>Do not use route slugs as SDK Entity identity.
Hook Record writes run dygo framework hooks, such as Activity, but do not re-enter app hooks.
RecordData also provides permission-aware Count, Exists, Aggregate,
GroupBy, relationship filters, ordered Lock, and Transaction. Use
AsActor for user access. Use AsSystem only with a non-empty audit reason.
Entity actions receive the actor and transaction-scoped Records, Jobs, Files,
Timeline, and Notifications services. Register one action on one Entity with
EntityActionRegistry.RegisterEntity.
FileData uploads, attaches, opens, and removes private files. TimelineData
adds append-only comments and events to Core Activity.
Generated Job files expose one Run function:
func Run(ctx context.Context, job dygo.JobExecution) error {
return nil
}Job handlers and transactional Record hooks can enqueue durable background work:
execution, err := job.Jobs.Enqueue(ctx, "crm", "send-welcome-email", payload, dygo.EnqueueOptions{
IdempotencyKey: "email:welcome:contact-42",
Priority: 0,
RunAfter: time.Now().Add(10 * time.Minute),
})Inside a Record hook, use hook.Jobs.Enqueue with the same arguments.
Job access uses app-scoped Job identity:
<app>, <job>Do not use labels or routes as SDK Job identity.
App code can write persisted diagnostic Logs through package helpers:
dygo.Info(ctx, "Customer import started")
dygo.Error(ctx, "Customer import failed", err)The helper functions are best-effort. Use dygo.Log(ctx, dygo.LogEntry{...}) when code needs to handle persistence errors. See Logs for the Log Entity contract and field mapping.
Actions, Hooks, and Jobs send user notifications through their Notifications service:
_, err := call.Notifications.Send(ctx, dygo.NotificationMessage{
Recipient: "person@example.com",
Title: "Leave approved",
Message: "Your leave request was approved.",
DeepLink: "/hr-leave-request/HRL-2026-00001",
Email: true,
IdempotencyKey: "hr:leave-approved:HRL-2026-00001",
})Recipient is the Core User Record name. The idempotency key is unique for that recipient. Send creates the in-app Notification and, when requested, enqueues the Core email Job in the current transaction. SMTP delivery and retries happen after commit. Email failure does not remove the in-app Notification.
hooks - run inside the current Record transaction
jobs - run outside user requests
pages - metadata contract; rendering remains framework-owned
reports - coming soonPlanned SDK surfaces include:
dygo.Config - app/runtime config reads
dygo.Secrets - controlled secret reads
dygo.Metadata - Entity, Field, and Page metadata reads