Documentation
¶
Overview ¶
Package types is the public wire contract for the Hanzo Notify service.
Both the HTTP routes (internal/routes) and the consumer SDK (pkg/client) share these structs so a request body in Go is bit-for-bit the JSON the HTTP layer accepts. Nothing else lives here — provider impls live in the notify library at the module root; orchestration lives in internal/.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Channel ¶
type Channel string
Channel is one of the canonical delivery channels notifyd supports. The set is intentionally small; per-provider customization (e.g. SMS short code vs. long code) goes in Options rather than expanding this.
type Event ¶
type Event struct {
ID string `json:"id"`
TenantSlug string `json:"tenant_slug"`
Name string `json:"name"`
Channels []string `json:"channels,omitempty"`
TemplateID string `json:"template_id,omitempty"`
RateLimit int `json:"rate_limit,omitempty"` // per minute, 0 = no limit
Enabled bool `json:"enabled"`
Created string `json:"created"`
Updated string `json:"updated"`
}
Event is an entry in the event catalog. Events drive routing (which channel/provider to use), rate-limit policy, and analytics grouping.
type Message ¶
type Message struct {
ID string `json:"id"`
TenantSlug string `json:"tenant_slug"`
Channel Channel `json:"channel"`
Provider string `json:"provider"`
To string `json:"to"`
Subject string `json:"subject,omitempty"`
Body string `json:"body,omitempty"`
TemplateID string `json:"template_id,omitempty"`
Event string `json:"event,omitempty"`
Status string `json:"status"`
Error string `json:"error,omitempty"`
IdempotencyKey string `json:"idempotency_key,omitempty"`
TaskID string `json:"task_id,omitempty"`
Created string `json:"created"`
Updated string `json:"updated"`
Sent string `json:"sent,omitempty"`
Delivered string `json:"delivered,omitempty"`
Metadata map[string]any `json:"metadata,omitempty"`
}
Message is the persisted record for one send. Every Send() through the library — sync or async, success or failure — writes exactly one Message row per recipient.
type MeterRow ¶
type MeterRow struct {
ID string `json:"id"`
TenantSlug string `json:"tenant_slug"`
Event string `json:"event,omitempty"`
Provider string `json:"provider"`
Channel Channel `json:"channel"`
Units int `json:"units"` // sms segments / email count / voice minutes
VendorCostMicros int64 `json:"vendor_cost_micros"` // provider-side cost in USD micros (1e-6)
RetailPriceMicros int64 `json:"retail_price_micros"` // billed to tenant
SentAt string `json:"sent_at"`
}
MeterRow is the per-send ledger entry written for billing. Daily rollups aggregate by (tenant_slug, provider, channel, day).
type Provider ¶
type Provider struct {
ID string `json:"id"`
TenantSlug string `json:"tenant_slug"`
Service string `json:"service"` // "plivo", "twilio", "sendgrid", …
Mode string `json:"mode"` // "shared" | "byo"
KMSPath string `json:"kms_path"`
Status string `json:"status"` // "pending_test" | "active" | "disabled"
Channel string `json:"channel,omitempty"`
IsDefault bool `json:"is_default,omitempty"`
Created string `json:"created"`
LastTestAt string `json:"last_test_at,omitempty"`
LastTestResult string `json:"last_test_result,omitempty"`
}
Provider is the persisted per-tenant provider configuration. The actual credentials live in KMS at Provider.KMSPath; the row only references them. Mode selects between Hanzo's shared subaccount and a tenant BYO.
type SendRequest ¶
type SendRequest struct {
// To is the destination address(es). Phone number for SMS/voice/
// WhatsApp, email for email, device token for push, channel
// identifier for chat. Multiple recipients fan out into multiple
// provider calls — one Message row per recipient.
To []string `json:"to"`
// Channel selects the delivery channel; the routes layer also exposes
// per-channel convenience endpoints (/v1/notify/send/sms, etc.) that
// set this implicitly.
Channel Channel `json:"channel"`
// Provider, if set, pins a specific provider service name (e.g.
// "plivo", "twilio", "sendgrid"). When omitted the service picks the
// tenant's default for the channel.
Provider string `json:"provider,omitempty"`
// Subject + Body are the raw payload when TemplateID is unset.
Subject string `json:"subject,omitempty"`
Body string `json:"body,omitempty"`
// TemplateID + TemplateVars trigger template rendering. The template
// must be published and approved for the caller's tenant.
TemplateID string `json:"template_id,omitempty"`
TemplateVars map[string]any `json:"template_vars,omitempty"`
// Event is the optional event-catalog identifier. Setting it both
// tags the resulting Message row and lets the catalog policy apply
// rate limits and routing overrides.
Event string `json:"event,omitempty"`
// IdempotencyKey scopes deduplication per tenant. Two requests with
// the same (tenant, idempotency_key) return the same task_id and
// produce a single Message row.
IdempotencyKey string `json:"idempotency_key,omitempty"`
// SendAt, if non-zero, schedules the send via hanzoai/tasks. RFC3339
// string on the wire.
SendAt string `json:"send_at,omitempty"`
// Options is the free-form per-provider knob bag. Each provider
// reads its own subkey (e.g. options["plivo"]["short_code"]).
Options map[string]any `json:"options,omitempty"`
}
SendRequest is the canonical wire shape for POST /v1/notify/send.
At least one of TemplateID + TemplateVars OR Subject+Body must be set. When TemplateID is provided the service renders the template against TemplateVars before handing off to the provider; when omitted the raw Subject/Body are sent through verbatim.
type SendResponse ¶
type SendResponse struct {
// MessageID is the notifyd-generated id used to look up the Message
// row via GET /v1/notify/messages/{id}.
MessageID string `json:"message_id"`
// TaskID is the hanzoai/tasks workflow id when async; empty in sync.
TaskID string `json:"task_id,omitempty"`
// Status is one of: queued, sending, sent, delivered, failed.
Status string `json:"status"`
// Error is set on terminal failure (sync mode only).
Error string `json:"error,omitempty"`
}
SendResponse is the canonical wire shape returned by POST /v1/notify/send regardless of sync vs. async mode. In async mode TaskID is the hanzoai/tasks workflow id and Status starts at "queued"; in sync mode TaskID is empty and Status is the terminal result.
type Template ¶
type Template struct {
ID string `json:"id"`
TenantSlug string `json:"tenant_slug"`
Name string `json:"name"`
Channel Channel `json:"channel"`
Subject string `json:"subject,omitempty"`
Body string `json:"body"`
Vars []string `json:"vars,omitempty"`
Status string `json:"status"` // draft | pending_approval | approved | published | archived
Version int `json:"version"`
Created string `json:"created"`
Updated string `json:"updated"`
ApprovedBy string `json:"approved_by,omitempty"`
}
Template is a re-usable message body keyed by tenant + canonical id. Lifecycle: draft → pending_approval → approved → published → archived. Lifted from the BD just-shipped design (/bd#105).
type Tenant ¶
type Tenant struct {
ID string `json:"id"`
Name string `json:"name"`
Created string `json:"created"`
Updated string `json:"updated"`
}
Tenant is the namespace row every other notify table relates to. The id is the IAM org slug (e.g. "hanzo"), enforced as a kebab-case PRIMARY KEY by the schema migration. One row per organisation. No soft-delete; CascadeDelete on the relation cleans up dependents.