client-go provides a typed Go client for the SMSGate 3rd-party API and a separate client for the SMSGate Certificate Authority API. It is built on the Go standard library and has no runtime dependencies.
The module contains three packages:
smsgatewayprovides the SMSGate 3rd-party API client for messages, inbox, devices, health, logs, settings, webhooks, and JWT tokens.casubmits Certificate Signing Requests (CSRs) and retrieves their status.restprovides the shared low-level HTTP client and error classification used by the other packages.
- 📱 SMSGate Go Client
- Text, data, and outgoing MMS messages with base64 attachment support
- Message priority, device and SIM selection, delivery reports, TTL, validity windows, and scheduled delivery
- Message state retrieval, listing, filtering, sorting, content inclusion, and cancellation
- Inbox listing and refresh with pagination, filters, MMS attachment metadata, encryption-state flags, and disabled, individual, or batch webhook delivery
- Device listing and deletion, health checks, logs, and device settings with get, patch, and replace operations
- Webhook registration with typed event constants and payload DTOs
- Webhook payload signature verification for incoming webhook requests
- JWT access-token generation, refresh, and revocation with scopes and TTL
- Optional versioned public-key fields on device and mobile request DTOs for E2E key metadata
- CSR submission and status retrieval through the
capackage - Custom HTTP clients and base URLs for testing or private deployments
- Go 1.22+ and zero runtime dependencies
- Go 1.22 or newer
- Credentials for the target SMSGate API
go get github.com/android-sms-gateway/client-goThe module version is available on pkg.go.dev. The Go version requirement is declared in go.mod.
The smsgateway client supports Basic authentication and JWT bearer tokens. A configured token takes priority over Basic credentials. The ca client does not use these SMSGate 3rd-party credentials.
client := smsgateway.NewClient(smsgateway.Config{
User: os.Getenv("ASG_USERNAME"),
Password: os.Getenv("ASG_PASSWORD"),
})The ASG_USERNAME and ASG_PASSWORD names are examples for the consuming application. The library itself does not read environment variables.
Create a Basic-authenticated client to request a scoped token, then create a client with the returned access token:
client := smsgateway.NewClient(smsgateway.Config{
User: os.Getenv("ASG_USERNAME"),
Password: os.Getenv("ASG_PASSWORD"),
})
token, err := client.GenerateToken(context.Background(), smsgateway.TokenRequest{
Scopes: []smsgateway.JWTScope{
smsgateway.ScopeMessagesSend,
smsgateway.ScopeMessagesRead,
},
TTL: 3600,
})
if err != nil {
panic(err)
}
jwtClient := smsgateway.NewClient(smsgateway.Config{
Token: token.AccessToken,
})The available scopes are defined in smsgateway/domain_auth.go.
The following example sends a text message using credentials supplied by the application:
package main
import (
"context"
"log"
"os"
"github.com/android-sms-gateway/client-go/smsgateway"
)
func main() {
client := smsgateway.NewClient(smsgateway.Config{
User: os.Getenv("ASG_USERNAME"),
Password: os.Getenv("ASG_PASSWORD"),
})
message := smsgateway.Message{
PhoneNumbers: []string{"+15555550100"},
TextMessage: &smsgateway.TextMessage{Text: "Hello from Go"},
}
state, err := client.Send(context.Background(), message)
if err != nil {
log.Fatal(err)
}
log.Printf("message queued: %s", state.ID)
}Client.Send validates SendOptions before making the request.
Use send options to control phone-number validation and active-device filtering:
state, err := client.Send(ctx, message,
smsgateway.WithSkipPhoneValidation(false),
smsgateway.WithDeviceActiveWithin(24),
)
if err != nil {
return err
}
log.Printf("message state: %s", state.State)List messages with shared date, pagination, state, device, sort, and content options:
limit := 50
includeContent := true
messages, total, err := client.ListMessages(ctx, smsgateway.ListMessagesOptions{
PaginationOptions: smsgateway.PaginationOptions{Limit: &limit},
IncludeContent: &includeContent,
})
if err != nil {
return err
}
log.Printf("loaded %d of %d messages", len(messages), total)SMSGate signs every webhook request with the device signing key. The signature is the
hexadecimal HMAC-SHA256 digest of the raw request body followed by the X-Timestamp value,
and it is sent in the X-Signature header.
Verify the signature of an incoming request before parsing its body:
func handler(w http.ResponseWriter, r *http.Request) {
body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "cannot read body", http.StatusBadRequest)
return
}
signature := r.Header.Get(smsgateway.WebhookSignatureHeader)
timestamp := r.Header.Get(smsgateway.WebhookTimestampHeader)
if !smsgateway.VerifyWebhookSignature(signingKey, body, timestamp, signature) {
http.Error(w, "invalid signature", http.StatusUnauthorized)
return
}
// The body is authenticated, parse it.
}The body must be the bytes as received, before any JSON decoding. VerifyWebhookSignature
rejects an empty signing key, an empty timestamp, a timestamp that is not a Unix timestamp in
seconds, and a signature that does not match the body.
To also reject replayed requests, use VerifyWebhookSignatureWithin, which requires the
timestamp to be no more than maxAge away from the current time:
if !smsgateway.VerifyWebhookSignatureWithin(signingKey, body, timestamp, signature, 5*time.Minute) {
http.Error(w, "invalid signature", http.StatusUnauthorized)
return
}The freshness check rejects requests older than maxAge, but a signed request replayed within
that window still verifies — deduplicate events if repeated processing matters.
The signing key is the value from Settings → Webhooks → Signing Key on the device.
| Field | Type | Default | Purpose |
|---|---|---|---|
Client |
*http.Client |
http.DefaultClient |
Custom HTTP transport, proxy, TLS, or timeout. |
BaseURL |
string |
https://api.sms-gate.app/3rdparty/v1 |
Base URL for the 3rd-party API. |
User |
string |
"" |
Basic-authentication username. |
Password |
string |
"" |
Basic-authentication password. |
Token |
string |
"" |
JWT bearer token; takes priority over Basic authentication. |
The exported WithClient, WithBaseURL, WithBasicAuth, and WithJWTAuth methods return updated Config values.
The ca package accepts functional options:
ca.WithClientsupplies a custom*http.Clientand defaults tohttp.DefaultClient.ca.WithBaseURLoverrides the CA API base URL, which defaults tohttps://ca.sms-gate.app/api/v1.
VersionedPublicKey models optional E2E key metadata:
PublicKeyis a base64-encoded RSA public key.KeyVersionidentifies the key version and is intended for rotation tracking.
The type is embedded in Device and MobileRegisterRequest; MobileUpdateRequest receives the fields through its embedded MobileRegisterRequest. These fields provide API schema and serialization support only. The client does not generate keys, encrypt or decrypt message payloads, or automatically attach keys to messages.
- Official API Reference — endpoints, payloads, and error codes
- Authentication Guide — scopes and token management
- Client libraries overview
smsgateway— third-party API client and request/response typesca— Certificate Authority clientrest— shared HTTP client and error types
Contributions are welcome. Pull requests target the master branch. Before submitting a change, run:
make lint
make testThe CI workflow also runs linting and race-enabled tests with coverage.
Distributed under the Apache License 2.0. See LICENSE.