Go SDK
The official Go SDK for Sent provides a lightweight, high-performance client with minimal dependencies. Built for microservices, CLI tools, and high-throughput applications with full context support.
Requirements
This library requires Go 1.22 or later.
Installation
go get github.com/sentdm/sent-dm-goTo pin a specific version:
go get github.com/sentdm/sent-dm-go@v0.7.0Quick Start
Initialize the client
package main
import (
"context"
"os"
"github.com/sentdm/sent-dm-go"
"github.com/sentdm/sent-dm-go/option"
)
func main() {
client := sentdm.NewClient(
option.WithAPIKey(os.Getenv("SENT_DM_API_KEY")), // defaults to os.LookupEnv("SENT_DM_API_KEY")
)
// Use the client...
}Send your first message
package main
import (
"context"
"fmt"
"log"
"os"
"github.com/sentdm/sent-dm-go"
"github.com/sentdm/sent-dm-go/option"
)
func main() {
client := sentdm.NewClient(
option.WithAPIKey(os.Getenv("SENT_DM_API_KEY")),
)
ctx := context.Background()
response, err := client.Messages.Send(ctx, sentdm.MessageSendParams{
To: []string{"+1234567890"},
Template: sentdm.MessageSendParamsTemplate{
ID: sentdm.String("7ba7b820-9dad-11d1-80b4-00c04fd430c8"),
Name: sentdm.String("welcome"),
Parameters: map[string]string{
"name": "John Doe",
"order_id": "12345",
},
},
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Message sent: %s\n", response.Data.Messages[0].ID)
fmt.Printf("Status: %s\n", response.Data.Messages[0].Status)
}Authentication
The client can be configured using environment variables or explicitly using functional options:
import (
"github.com/sentdm/sent-dm-go"
"github.com/sentdm/sent-dm-go/option"
)
// Using environment variables (SENT_DM_API_KEY)
client := sentdm.NewClient()
// Or explicit configuration
client := sentdm.NewClient(
option.WithAPIKey("your_api_key"),
)Request fields
The sentdm library uses the omitzero semantics from Go 1.24+ encoding/json release for request fields.
Required primitive fields feature the tag json:"...,required". These fields are always serialized, even their zero values.
Optional primitive types are wrapped in a param.Opt[T]. These fields can be set with the provided constructors, sentdm.String(), sentdm.Int(), etc.
params := sentdm.MessageSendParams{
To: []string{"+1234567890"}, // required property
Template: sentdm.MessageSendParamsTemplate{
ID: sentdm.String("template-id"),
Name: sentdm.String("welcome"),
},
Channels: sentdm.StringSlice([]string{"whatsapp"}), // optional property
}To send null instead of a param.Opt[T], use param.Null[T](). To check if a field is omitted, use param.IsOmitted().
Send Messages
Send a message
response, err := client.Messages.Send(ctx, sentdm.MessageSendParams{
To: []string{"+1234567890"},
Template: sentdm.MessageSendParamsTemplate{
ID: sentdm.String("7ba7b820-9dad-11d1-80b4-00c04fd430c8"),
Name: sentdm.String("welcome"),
Parameters: map[string]string{
"name": "John Doe",
"order_id": "12345",
},
},
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Sent: %s\n", response.Data.Messages[0].ID)
fmt.Printf("Status: %s\n", response.Data.Messages[0].Status)Test mode
Use TestMode to validate requests without sending real messages:
response, err := client.Messages.Send(ctx, sentdm.MessageSendParams{
To: []string{"+1234567890"},
Template: sentdm.MessageSendParamsTemplate{
ID: sentdm.String("7ba7b820-9dad-11d1-80b4-00c04fd430c8"),
Name: sentdm.String("welcome"),
},
TestMode: sentdm.Bool(true), // Validates but doesn't send
})
if err != nil {
log.Fatal(err)
}
// Response will have test data
fmt.Printf("Validation passed: %s\n", response.Data.Messages[0].ID)Error handling
When the API returns a non-success status code, we return an error of type *sentdm.Error:
response, err := client.Messages.Send(ctx, params)
if err != nil {
var apiErr *sentdm.Error
if errors.As(err, &apiErr) {
fmt.Printf("API error: %s\n", apiErr.Message)
fmt.Printf("Status: %d\n", apiErr.StatusCode)
} else {
fmt.Printf("Other error: %v\n", err)
}
}Pagination
For paginated list endpoints, you can use .ListAutoPaging() methods to iterate through items across all pages:
// List all contacts
iter := client.Contacts.ListAutoPaging(ctx, sentdm.ContactListParams{})
for iter.Next() {
contact := iter.Current()
fmt.Printf("%s - %s\n", contact.PhoneNumber, contact.AvailableChannels)
}
if err := iter.Err(); err != nil {
log.Fatal(err)
}Or use simple .List() methods to fetch a single page:
page, err := client.Contacts.List(ctx, sentdm.ContactListParams{
Limit: sentdm.Int(100),
})
if err != nil {
log.Fatal(err)
}
for _, contact := range page.Data {
fmt.Printf("%s\n", contact.PhoneNumber)
}
// Get next page
if page.HasMore {
nextPage, err := page.GetNextPage()
// ...
}Contacts
Create and manage contacts:
// Create a contact
response, err := client.Contacts.Create(ctx, sentdm.ContactCreateParams{
PhoneNumber: "+1234567890",
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Contact ID: %s\n", response.Data.ID)
// List contacts
page, err := client.Contacts.List(ctx, sentdm.ContactListParams{
Limit: sentdm.Int(100),
})
// Get a contact
contact, err := client.Contacts.Get(ctx, "contact-uuid")
// Update a contact
response, err = client.Contacts.Update(ctx, "contact-uuid", sentdm.ContactUpdateParams{
PhoneNumber: sentdm.String("+1987654321"),
})
// Delete a contact
err = client.Contacts.Delete(ctx, "contact-uuid")Templates
List and retrieve templates:
// List templates
templates, err := client.Templates.List(ctx, sentdm.TemplateListParams{})
if err != nil {
log.Fatal(err)
}
for _, template := range templates.Data {
fmt.Printf("%s (%s): %s\n", template.Name, template.Status, template.ID)
}
// Get a template
template, err := client.Templates.Get(ctx, "template-uuid")
fmt.Printf("Name: %s\n", template.Name)
fmt.Printf("Status: %s\n", template.Status)RequestOptions
This library uses the functional options pattern. Functions defined in the option package return a RequestOption, which is a closure that mutates a RequestConfig. These options can be supplied to the client or at individual requests:
client := sentdm.NewClient(
// Adds a header to every request made by the client
option.WithHeader("X-Some-Header", "custom_header_info"),
)
// Override per-request
response, err := client.Messages.Send(ctx, params,
option.WithHeader("X-Some-Header", "some_other_value"),
option.WithJSONSet("custom.field", map[string]string{"my": "object"}),
)The request option option.WithDebugLog(nil) may be helpful while debugging.
See the full list of request options.
Gin Framework
package main
import (
"context"
"net/http"
"os"
"time"
"github.com/gin-gonic/gin"
"github.com/sentdm/sent-dm-go"
"github.com/sentdm/sent-dm-go/option"
)
func main() {
client := sentdm.NewClient(
option.WithAPIKey(os.Getenv("SENT_DM_API_KEY")),
)
r := gin.Default()
r.POST("/send", func(c *gin.Context) {
var req struct {
To []string `json:"to"`
Template sentdm.MessageSendParamsTemplate `json:"template"`
}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second)
defer cancel()
response, err := client.Messages.Send(ctx, sentdm.MessageSendParams{
To: req.To,
Template: req.Template,
})
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{
"status": "sent",
"message_id": response.Data.Messages[0].ID,
})
})
r.Run(":8080")
}Echo Framework
package main
import (
"context"
"net/http"
"os"
"time"
"github.com/labstack/echo/v4"
"github.com/sentdm/sent-dm-go"
"github.com/sentdm/sent-dm-go/option"
)
func main() {
client := sentdm.NewClient(
option.WithAPIKey(os.Getenv("SENT_DM_API_KEY")),
)
e := echo.New()
e.POST("/send", func(c echo.Context) error {
req := new(sentdm.MessageSendParams)
if err := c.Bind(req); err != nil {
return err
}
ctx, cancel := context.WithTimeout(c.Request().Context(), 30*time.Second)
defer cancel()
response, err := client.Messages.Send(ctx, *req)
if err != nil {
return c.JSON(http.StatusInternalServerError, map[string]string{
"error": err.Error(),
})
}
return c.JSON(http.StatusOK, map[string]string{
"status": "sent",
"message_id": response.Data.Messages[0].ID,
})
})
e.Logger.Fatal(e.Start(":8080"))
}Response objects
All fields in response structs are ordinary value types. Response structs also include a special JSON field containing metadata about each property.
response, err := client.Templates.Get(ctx, "template-uuid")
if err != nil {
log.Fatal(err)
}
fmt.Println(response.Name) // Access the field directly
// Check if field was present in response
if response.JSON.Name.Valid() {
fmt.Println("Name was present")
}
// Access raw JSON
fmt.Println(response.JSON.Name.Raw())Concurrent Sending
Leverage Go's concurrency for high-throughput:
func sendBulkMessages(
client *sentdm.Client,
phoneNumbers []string,
templateID string,
) error {
var wg sync.WaitGroup
errChan := make(chan error, len(phoneNumbers))
// Semaphore to limit concurrency
sem := make(chan struct{}, 10)
for _, phone := range phoneNumbers {
wg.Add(1)
go func(p string) {
defer wg.Done()
sem <- struct{}{}
defer func() { <-sem }()
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
response, err := client.Messages.Send(ctx, sentdm.MessageSendParams{
To: []string{p},
Template: sentdm.MessageSendParamsTemplate{
ID: sentdm.String(templateID),
},
})
if err != nil {
errChan <- fmt.Errorf("failed to send to %s: %w", p, err)
}
}(phone)
}
wg.Wait()
close(errChan)
var errs []error
for err := range errChan {
errs = append(errs, err)
}
if len(errs) > 0 {
return fmt.Errorf("failed to send %d messages", len(errs))
}
return nil
}Source & Issues
- Version: 0.7.0
- GitHub: sentdm/sent-dm-go
- GoDoc: pkg.go.dev/github.com/sentdm/sent-dm-go
- Issues: Report a bug
Getting Help
- Documentation: API Reference
- Troubleshooting: Common Issues
- Support: Email support@sent.dm with your request ID