justpush

package module
v1.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 5, 2026 License: MIT Imports: 18 Imported by: 0

README

JustPush

Website · Docs · iOS app · Android app · Studio · Recipes

Go Reference CI

JustPush — Go SDK

The official Go SDK for JustPush. Send push notifications to your iOS and Android devices from any Go program, server or CLI.

  • Standard library only, no dependencies
  • Typed options for buttons, button groups, images, sounds and acknowledgements
  • Checks the message before sending it, so you get a clear error instead of a rejected request
  • Errors that work with errors.Is / errors.As, and rate-limit information on every send

Installation

go get github.com/JustPush-io/justpush-go-sdk

Go 1.21 or newer.

Send a message

import "github.com/JustPush-io/justpush-go-sdk"

client, err := justpush.New("YOUR_API_TOKEN")
if err != nil {
    log.Fatal(err)
}

result, err := client.Send(ctx, &justpush.Message{
    Title:   "Backups",
    Message: "The nightly backup finished",
})
if err != nil {
    log.Fatal(err)
}

fmt.Println(result.Key)                   // use it with GetMessage()
fmt.Println(result.RateLimit.Remaining)   // messages left this month

Get your API token from the JustPush app.

Everything a message can do

snapshot, err := justpush.ImageFromFile("snapshot.jpg", "Kitchen")
if err != nil {
    log.Fatal(err)
}

_, err = client.Send(ctx, &justpush.Message{
    Title:    "💧 Leak",
    Message:  "Water detected under the washing machine",
    Topic:    "Home",                      // topic name; created if it doesn't exist yet
    Priority: justpush.PriorityHighest,
    Sound:    justpush.SoundSiren,
    Buttons:  []justpush.Button{{CTA: "Open camera", URL: "https://example.com/cam"}},
    Images:   []justpush.Image{snapshot},
    Expiry:   time.Hour,                   // hide the message after an hour
    Acknowledge: &justpush.Acknowledgement{
        Retry: true, Interval: time.Minute, MaxRetries: 10,
        CallbackURL:    "https://example.com/acknowledged",
        CallbackParams: map[string]any{"sensor": "kitchen"},
    },
})
Field
Message, Title At least one is required. Titles over 255 characters are cut short.
Topic A topic name. An existing topic with that name is used, or a new one is created.
TopicToken Target a topic by its API token instead (Topic.APIToken).
Priority PriorityLowest (-2) … PriorityHighest (2). Use ParsePriority("high") for names.
Sound A Sound constant, or its name in any case. SoundNone is silent.
Buttons Up to 10 Button{CTA, URL, ActionRequired}. Labels over 25 characters are cut.
ButtonGroups Up to 4 ButtonGroup{Name, CTA, Buttons}, each with up to 10 buttons.
Images Up to 10, from ImageFromURL, ImageFromBytes or ImageFromFile. The first one becomes the notification banner.
Expiry How long until the message is hidden, in whole seconds.
Acknowledge &Acknowledgement{} to require acknowledgement; set Retry for retries (Interval 10–65535 s, MaxRetries up to 255) and CallbackURL for a callback.

Zero values aren't sent, so the API's defaults apply (normal priority, default sound, no expiry).

Check a message

details, err := client.GetMessage(ctx, result.Key)
fmt.Println(details.IsAcknowledged, details.ProcessedAt)

Check a token

err := client.VerifyToken(ctx)   // errors.Is(err, justpush.ErrUnauthorized) if the token is invalid

This sends nothing and uses no quota, so it's safe for setup screens.

Topics

topic, err := client.CreateTopic(ctx, justpush.TopicInput{Title: "Servers", AvatarURL: "https://example.com/server.png"})
client.UpdateTopic(ctx, topic.UUID, justpush.TopicInput{Title: "Production servers"})
client.GetTopic(ctx, topic.UUID)
client.Send(ctx, &justpush.Message{Message: "Disk almost full", TopicToken: topic.APIToken})

Configuration

client, err := justpush.New(token,
    justpush.WithTimeout(5*time.Second),        // default 10 s
    justpush.WithHTTPClient(myHTTPClient),      // proxies, tracing, …
    justpush.WithBaseURL("http://localhost:8080"),
)

Every method takes a context.Context, so you can also cancel a call or give it a deadline. A Client is safe to share between goroutines.

Errors

Check errors with errors.Is:

Error When
ErrValidation The message is invalid, either caught before sending (*ValidationError) or a 422 from the API (*APIError with per-field Errors).
ErrUnauthorized 401: the token is missing or wrong.
ErrForbidden 403: not allowed, e.g. a topic you don't own, or a plan limit.
ErrNotFound 404: unknown message key or topic.
ErrSubscriptionExpired 410: the subscription expired.
ErrRateLimited 429: too many requests; see APIError.RetryAfter.
ErrConnection Network error, timeout or cancelled context (*ConnectionError).

Any error status from the API is an *APIError with StatusCode, Message and the raw Body:

var apiErr *justpush.APIError
if errors.As(err, &apiErr) && errors.Is(err, justpush.ErrRateLimited) {
    time.Sleep(apiErr.RetryAfter)
}

JustPush products

Apps

SDKs

Packages

Developer tools

Development

go vet ./... && go test -race ./...

Releases are git tags (v0.1.0, …); the Go module proxy picks them up automatically.

Changelog

See CHANGELOG.md.

Documentation

Overview

Package justpush is the official Go SDK for JustPush (https://justpush.io). It sends push notifications to your iOS and Android devices.

client, err := justpush.New("YOUR_API_TOKEN")
if err != nil { ... }
result, err := client.Send(ctx, &justpush.Message{Title: "Backups", Message: "The nightly backup finished"})
Example
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/JustPush-io/justpush-go-sdk"
)

func main() {
	client, err := justpush.New("YOUR_API_TOKEN")
	if err != nil {
		log.Fatal(err)
	}
	result, err := client.Send(context.Background(), &justpush.Message{
		Title:   "Backups",
		Message: "The nightly backup finished",
	})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(result.Key, result.RateLimit.Remaining)
}

Index

Examples

Constants

View Source
const (
	// Version of this SDK, sent in the User-Agent header.
	Version = "1.0.1"
	// DefaultBaseURL is the JustPush API.
	DefaultBaseURL = "https://api.justpush.io"
	// DefaultTimeout applies when the client creates its own http.Client.
	DefaultTimeout = 10 * time.Second
)

Variables

View Source
var (
	// ErrValidation: the message or topic is invalid, either caught before sending
	// (a *ValidationError) or rejected by the API with a 422 (an *APIError).
	ErrValidation = errors.New("justpush: validation failed")
	// ErrUnauthorized: 401, the token is missing, unknown or revoked.
	ErrUnauthorized = errors.New("justpush: unauthorized")
	// ErrForbidden: 403, the token may not do this, or the plan doesn't allow it.
	ErrForbidden = errors.New("justpush: forbidden")
	// ErrNotFound: 404, no message or topic with that key or UUID for this account.
	ErrNotFound = errors.New("justpush: not found")
	// ErrSubscriptionExpired: 410, the account's subscription has expired.
	ErrSubscriptionExpired = errors.New("justpush: subscription expired")
	// ErrRateLimited: 429, too many requests. See APIError.RetryAfter.
	ErrRateLimited = errors.New("justpush: rate limited")
	// ErrConnection: the API couldn't be reached, or didn't answer in time.
	ErrConnection = errors.New("justpush: connection failed")
)

Sentinel errors. Every error the client returns matches one of these with errors.Is, so callers can branch on the kind of failure without type assertions.

Sounds lists every sound the API accepts.

Functions

This section is empty.

Types

type APIError

type APIError struct {
	// StatusCode is the HTTP status, e.g. 401 or 422.
	StatusCode int
	// Message is the most readable message the API gave, or a generic one.
	Message string
	// Errors maps each field to its messages for a 422, when the API sends them.
	Errors map[string][]string
	// RetryAfter is set for a 429 when the API sends a Retry-After header.
	RetryAfter time.Duration
	// Body is the raw response body.
	Body []byte
}

APIError is returned when the API answers with an error status.

func (*APIError) Error

func (e *APIError) Error() string

func (*APIError) Unwrap

func (e *APIError) Unwrap() error

Unwrap returns the sentinel matching the status code, so errors.Is(err, ErrNotFound) works.

type Acknowledgement

type Acknowledgement struct {
	// Retry keeps re-sending the message until it is acknowledged.
	Retry bool
	// Interval between retries, 10s to 65535s, rounded down to whole seconds. Zero uses the API default.
	Interval time.Duration
	// MaxRetries is the most retries to make, 1 to 255. Zero uses the API default.
	MaxRetries int

	// CallbackURL is called by JustPush once the message is acknowledged, with CallbackParams.
	CallbackURL    string
	CallbackParams map[string]any
}

Acknowledgement configures how a message is acknowledged.

type Button

type Button struct {
	CTA            string
	URL            string
	ActionRequired bool
}

Button opens URL when tapped. With ActionRequired the message stays pending until someone taps it. The API cuts CTA to 25 characters.

type ButtonGroup

type ButtonGroup struct {
	Name           string
	CTA            string
	Buttons        []Button // at most 10; their ActionRequired is ignored
	ActionRequired bool
}

ButtonGroup is a single button (CTA) that opens a named list of buttons.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client sends push notifications through the JustPush API. It is safe for concurrent use.

func New

func New(token string, opts ...Option) (*Client, error)

New creates a client for the given API token. Get your token from the JustPush app.

func (*Client) CreateTopic

func (c *Client) CreateTopic(ctx context.Context, in TopicInput) (*Topic, error)

CreateTopic creates a topic. Title is required.

func (*Client) GetMessage

func (c *Client) GetMessage(ctx context.Context, key string) (*MessageDetails, error)

GetMessage fetches a message you sent, e.g. to check whether it was acknowledged.

func (*Client) GetTopic

func (c *Client) GetTopic(ctx context.Context, uuid string) (*Topic, error)

GetTopic fetches one of your topics by its UUID.

func (*Client) Send

func (c *Client) Send(ctx context.Context, m *Message) (*SendResult, error)

Send queues a push notification and returns its key. The message is checked first, so an invalid one returns a *ValidationError without making a request.

Example
package main

import (
	"context"
	"errors"
	"log"
	"time"

	"github.com/JustPush-io/justpush-go-sdk"
)

func main() {
	client, _ := justpush.New("YOUR_API_TOKEN")
	snapshot, err := justpush.ImageFromFile("snapshot.jpg", "Kitchen")
	if err != nil {
		log.Fatal(err)
	}

	_, err = client.Send(context.Background(), &justpush.Message{
		Title:    "💧 Leak",
		Message:  "Water detected under the washing machine",
		Topic:    "Home",
		Priority: justpush.PriorityHighest,
		Sound:    justpush.SoundSiren,
		Buttons:  []justpush.Button{{CTA: "Open camera", URL: "https://example.com/cam"}},
		Images:   []justpush.Image{snapshot},
		Expiry:   time.Hour,
		Acknowledge: &justpush.Acknowledgement{
			Retry: true, Interval: time.Minute, MaxRetries: 10,
			CallbackURL:    "https://example.com/acknowledged",
			CallbackParams: map[string]any{"sensor": "kitchen"},
		},
	})

	var apiErr *justpush.APIError
	switch {
	case errors.Is(err, justpush.ErrRateLimited) && errors.As(err, &apiErr):
		log.Printf("rate limited, retry in %s", apiErr.RetryAfter)
	case err != nil:
		log.Fatal(err)
	}
}

func (*Client) UpdateTopic

func (c *Client) UpdateTopic(ctx context.Context, uuid string, in TopicInput) (*Topic, error)

UpdateTopic renames a topic or changes its avatar. Empty fields are left unchanged.

func (*Client) VerifyToken

func (c *Client) VerifyToken(ctx context.Context) error

VerifyToken checks that the token is valid without sending anything or using quota. It returns an error matching ErrUnauthorized for an invalid token.

type ConnectionError

type ConnectionError struct {
	Err error
}

ConnectionError wraps a network failure or timeout. It matches ErrConnection, and the underlying error too (so errors.Is(err, context.DeadlineExceeded) still works).

func (*ConnectionError) Error

func (e *ConnectionError) Error() string

func (*ConnectionError) Unwrap

func (e *ConnectionError) Unwrap() []error

type Image

type Image struct {
	URL     string
	Body    string // base64-encoded image data
	Caption string
}

Image is attached to a message, either from a public URL or as base64 image data. Build one with ImageFromURL, ImageFromBytes or ImageFromFile.

func ImageFromBytes

func ImageFromBytes(data []byte, caption string) Image

ImageFromBytes attaches raw image bytes (JPEG, PNG, …), e.g. a camera snapshot.

func ImageFromFile

func ImageFromFile(path, caption string) (Image, error)

ImageFromFile attaches an image file from disk.

func ImageFromURL

func ImageFromURL(url, caption string) Image

ImageFromURL attaches an image that JustPush downloads from a public URL.

type Message

type Message struct {
	Message string
	// Title is cut to 255 characters by the API.
	Title string

	// Topic is a topic name: an existing topic with that name is used, otherwise a new one is created.
	Topic string
	// TopicToken targets a topic by its API token (Topic.APIToken) instead. Without Topic or
	// TopicToken, the message goes to your default topic.
	TopicToken string

	Priority Priority
	Sound    Sound

	// Buttons shown under the message, at most 10.
	Buttons []Button
	// ButtonGroups, at most 4, each opening a list of up to 10 buttons.
	ButtonGroups []ButtonGroup
	// Images, at most 10. The first one becomes the banner of the push notification.
	Images []Image

	// Expiry hides the message after this long, rounded down to whole seconds. Zero means never.
	Expiry time.Duration

	// Acknowledge asks for the message to be acknowledged on a device. A pointer to an empty
	// Acknowledgement just marks it as requiring acknowledgement.
	Acknowledge *Acknowledgement
}

Message is a push notification to send. Message or Title is required; everything else is optional.

type MessageDetails

type MessageDetails struct {
	Key                     string
	Title                   string
	Message                 string
	Priority                Priority
	Sound                   Sound
	Topic                   *Topic
	RequiresAcknowledgement bool
	IsAcknowledged          bool
	PendingActions          json.RawMessage
	// The times are zero when the API leaves them out.
	ReceivedAt  time.Time
	ProcessedAt time.Time
	ExpiresAt   time.Time
	// Raw holds the full API response.
	Raw json.RawMessage
}

MessageDetails is a message as stored by JustPush.

func (*MessageDetails) UnmarshalJSON

func (m *MessageDetails) UnmarshalJSON(data []byte) error

type Option

type Option func(*Client)

Option configures a Client.

func WithBaseURL

func WithBaseURL(baseURL string) Option

WithBaseURL points the client at another API host, e.g. a test server.

func WithHTTPClient

func WithHTTPClient(hc *http.Client) Option

WithHTTPClient uses your own http.Client, e.g. for proxies or tracing. Its timeout applies instead of DefaultTimeout.

func WithTimeout

func WithTimeout(d time.Duration) Option

WithTimeout sets how long a request may take. Use a context deadline for per-call limits.

type Priority

type Priority int

Priority is a message's priority. Higher priorities break through quiet hours on the device.

const (
	PriorityLowest  Priority = -2
	PriorityLow     Priority = -1
	PriorityNormal  Priority = 0 // the default; a zero Priority is not sent at all
	PriorityHigh    Priority = 1
	PriorityHighest Priority = 2
)

func ParsePriority

func ParsePriority(s string) (Priority, error)

ParsePriority accepts a name ("high", "LOWEST") or a number from -2 to 2 ("-1").

func (Priority) String

func (p Priority) String() string

type RateLimit

type RateLimit struct {
	// Known reports whether the API sent rate-limit headers; the other fields are zero otherwise.
	Known     bool
	Limit     int
	Remaining int
	// Reset is the time until the allowance resets.
	Reset time.Duration
}

RateLimit is your plan's monthly message allowance, read from the response headers.

type SendResult

type SendResult struct {
	// Key identifies the message; pass it to Client.GetMessage.
	Key       string
	RateLimit RateLimit
}

SendResult is what the API returns for a queued message.

type Sound

type Sound string

Sound is a notification sound the JustPush apps can play. An empty Sound is not sent, so the app plays its default.

const (
	SoundNone         Sound = "none" // silent
	SoundDefault      Sound = "default"
	SoundBike         Sound = "bike"
	SoundBugle        Sound = "bugle"
	SoundCashRegister Sound = "cashregister"
	SoundClassical    Sound = "classical"
	SoundCosmic       Sound = "cosmic"
	SoundFalling      Sound = "falling"
	SoundGamelan      Sound = "gamelan"
	SoundIncoming     Sound = "incoming"
	SoundIntermission Sound = "intermission"
	SoundMagic        Sound = "magic"
	SoundMechanical   Sound = "mechanical"
	SoundPianoBar     Sound = "pianobar"
	SoundSiren        Sound = "siren"
	SoundSpaceAlarm   Sound = "spacealarm"
	SoundTugboat      Sound = "tugboat"
	SoundAlien        Sound = "alien"
	SoundClimb        Sound = "climb"
	SoundPersistent   Sound = "persistent"
	SoundEcho         Sound = "echo"
	SoundUpDown       Sound = "updown"
	SoundVibrate      Sound = "vibrate"
)

func ParseSound

func ParseSound(s string) (Sound, error)

ParseSound accepts a sound name in any case ("COSMIC", "cosmic").

type Topic

type Topic struct {
	UUID            string
	Title           string
	Slug            string
	Avatar          string
	HasCustomAvatar bool
	APIToken        string
	// Raw holds the full API response.
	Raw json.RawMessage
}

Topic groups messages in the app. APIToken can be passed as Message.TopicToken.

func (*Topic) UnmarshalJSON

func (t *Topic) UnmarshalJSON(data []byte) error

type TopicInput

type TopicInput struct {
	// Title is required when creating a topic, at most 100 characters. The default topic
	// can't be renamed.
	Title       string
	AvatarURL   string
	AvatarImage []byte
}

TopicInput creates or updates a topic. Give it an avatar from a public URL or from image bytes.

type ValidationError

type ValidationError struct {
	Message string
}

ValidationError is returned before any request is made when the SDK can tell on its own that the API would reject the message or topic (for example, more than 10 buttons).

func (*ValidationError) Error

func (e *ValidationError) Error() string

func (*ValidationError) Is

func (e *ValidationError) Is(target error) bool

Is makes errors.Is(err, ErrValidation) true.

Directories

Path Synopsis
examples
send command
Send a push notification.
Send a push notification.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL