Docs / Contact / sdks/go/getting-started
Go SDK: Getting Started
Go from zero to publishing device data in under a minute. The SDK handles message batching, BoltDB offline storage, automatic heartbeats, and connectivity monitoring, so you can focus on your application.
Requirements: Go 1.21+
Install
go get github.com/tendrl-inc-labs/go-sdk
Every client needs an entity API key. If you haven't created one yet, create your first entity and copy its API key from the Connection Instructions dialog.
Your First Message
client, err := tendrl.NewClient(true, "your_api_key")
if err != nil {
panic(err)
}
defer client.Stop()
client.PublishAsync(map[string]interface{}{"temperature": 23.5}, []string{"sensor"})
The message is queued, batched, and delivered. Tags route it to your flows and connectors.
Automate with Tether
Most IoT work is "collect data, publish, repeat." Tether does this in one call: give it a function and an interval, and the SDK handles the rest.
stop := client.Tether("sensors", func() (interface{}, error) {
return map[string]interface{}{
"temperature": readTemp(),
"humidity": readHumidity(),
"pressure": readPressure(),
}, nil
}, []string{"sensor", "environment"}, 10*time.Second)
defer stop()
Every 10 seconds, your function runs and the result is published with the tags you specify. No goroutine management, no tickers, no manual publish calls.
Return an error to skip a cycle without stopping the tether:
stop := client.Tether("sensor", func() (interface{}, error) {
value, err := readSensor()
if err != nil {
return nil, err // Skips this cycle, tries again next interval
}
return map[string]interface{}{"value": value}, nil
}, []string{"sensor"}, 5*time.Second)
Route Inbound Messages
Handle messages sent back from Contact flows. Route by tag with client.On():
client.On(tendrl.MessageRoute{
Tag: "diagnostic",
Handler: func(msg tendrl.IncomingMessage) error {
// run diagnostics and publish result
return nil
},
})
client.On(tendrl.MessageRoute{
Tag: "ai-response",
Handler: func(msg tendrl.IncomingMessage) error {
fmt.Println("AI:", msg.Data)
return nil
},
})
client.On(tendrl.MessageRoute{
Tags: []string{"alert", "anomaly"},
Handler: func(msg tendrl.IncomingMessage) error {
fmt.Println("Alert:", msg.Data)
return nil
},
})
client.SetMessageCheckRate(3 * time.Second)
SetMessageCallback remains available as a catch-all fallback for unmatched messages.
Track Device State
Every entity has a persistent state table, a key-value store accessible from the dashboard and other entities:
// Merge new data (existing keys preserved)
client.UpdateState(map[string]interface{}{
"firmware": "2.1.0",
"status": "active",
}, nil)
// Read it back
state, _ := client.GetState()
// Trigger a flow when state changes
client.UpdateState(map[string]interface{}{
"status": "maintenance",
}, []string{"status-change"})
Receive remote state changes with OnState(), which is polled at the same interval as messages:
client.OnState(func(state map[string]interface{}) error {
if status, _ := state["status"].(string); status == "needs_maintenance" {
runDiagnostics()
}
return nil
})
What You Get for Free
When you create a managed client, the SDK automatically:
- Batches messages: dynamic sizing based on CPU, memory, and queue depth
- Stores offline messages: BoltDB persistence with 1-hour TTL and automatic retry
- Monitors connectivity: detects network changes every 30 seconds
- Sends heartbeats: reports memory and disk usage to Contact
- Adapts to load: batch size formula considers CPU (40%), memory (40%), and queue (20%)
You don't configure any of this. It just works.
Complete Example
package main
"fmt"
"os"
"os/signal"
"time"
"github.com/tendrl-inc-labs/go-sdk/tendrl"
)
func main() {
client, err := tendrl.NewClient(true, "your_api_key")
if err != nil {
panic(err)
}
defer client.Stop()
// Route inbound messages by tag
client.On(tendrl.MessageRoute{
Tag: "ai-response",
Handler: func(msg tendrl.IncomingMessage) error {
fmt.Printf("[%s] %v\n", msg.MsgType, msg.Data)
return nil
},
})
client.SetMessageCheckRate(3 * time.Second)
// Collect and publish sensor data every 10 seconds
stop := client.Tether("sensors", func() (interface{}, error) {
return map[string]interface{}{
"temperature": 23.5,
"humidity": 60,
}, nil
}, []string{"sensor"}, 10*time.Second)
defer stop()
// Wait for interrupt
sig := make(chan os.Signal, 1)
signal.Notify(sig, os.Interrupt)
<-sig
}
Operating Modes
Managed Mode (Default)
client, err := tendrl.NewClient(true, "your_key")
Background goroutines handle batching, offline storage, heartbeats, and connectivity. Recommended for most use cases.
Headless Mode
client, err := tendrl.NewClient(false, "your_key")
Direct HTTP calls only. No background processing. Best for simple tools or when you need full control.
What's Next
- Configuration: Config file format, all options, and tuning
- Examples: Cross-account messaging, state management, and more
- API Reference: Full method documentation
Tendrl