thingscloud

package module
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Jul 7, 2026 License: MIT Imports: 15 Imported by: 0

README

things cloud sdk

Things comes with a cloud based API, which can be used to synchronize data between devices. This is a golang SDK to interact with that API, opening the API so that you can enhance your Things experience on iOS and Mac.

Go

Getting Started

Installation
go get github.com/arthursoares/things-cloud-sdk
Quick Start

1. Set up your credentials:

export THINGS_USERNAME='your@email.com'
export THINGS_PASSWORD='yourpassword'

2. Create a simple Go program:

package main

import (
    "fmt"
    "os"
    "time"

    things "github.com/arthursoares/things-cloud-sdk"
)

func main() {
    client := things.New(
        things.APIEndpoint,
        os.Getenv("THINGS_USERNAME"),
        os.Getenv("THINGS_PASSWORD"),
    )

    // Verify credentials
    resp, err := client.Verify()
    if err != nil {
        panic(err)
    }
    fmt.Printf("✓ Connected: %s\n", resp.Email)

    // Get your account's history and sync it (required before writing —
    // the commit's ancestor-index comes from the synced head)
    history, err := client.OwnHistory()
    if err != nil {
        panic(err)
    }
    if err := history.Sync(); err != nil {
        panic(err)
    }

    // Create a task. NewUUID() produces the canonical Base58 identifier
    // format Things requires; Write rejects anything else.
    task := things.TaskActionItem{
        Item: things.Item{
            UUID:   things.NewUUID(),
            Kind:   things.ItemKindTask,
            Action: things.ItemActionCreated,
        },
        P: things.TaskActionItemPayload{
            Title:        things.String("My first task from the SDK!"),
            Status:       things.Status(things.TaskStatusPending),
            Schedule:     things.Schedule(things.TaskScheduleInbox),
            CreationDate: things.Time(time.Now()),
        },
    }

    if err := history.Write(task); err != nil {
        panic(err)
    }
    fmt.Println("✓ Created task")
}

Tip: for writes, prefer things-cli (below) — its payloads replicate real Things.app traffic field-for-field and are verified against HAR captures. The raw SDK write API is lower-level and sends sparse payloads.

3. Run it:

go run main.go
CLI Quick Start

Install and use the command-line tool:

# Install
go install github.com/arthursoares/things-cloud-sdk/cmd/things-cli@latest

# Create a task
things-cli create "Buy groceries" --when today

# List today's tasks
things-cli list --today

# Complete a task
things-cli complete <task-uuid>

Features

  • Verify Credentials — validate account access
  • Account Management — signup, confirmation, password change, deletion
  • History Management — list, create, delete, sync histories
  • Item Read/Write — full event-sourced CRUD for tasks, areas, tags, checklist items (supports batching multiple items in one request)
  • Task Types — tasks, projects, and headings (action groups within projects)
  • Structured Notes — full-text and delta patch support for task notes
  • Recurring Tasks — neverending, end on date, end after N times
  • Tombstone Deletion — explicit deletion records via Tombstone2 entities
  • Device Registration — register app instances for APNS push notifications
  • Alarm/Reminders — alarm time offset support on tasks
  • State Aggregation — in-memory state built from history items, with queries for projects, headings, subtasks, areas, tags, and checklist items
  • Persistent Sync Engine — SQLite-backed incremental sync with semantic change detection

CLI

things-cli is a command-line tool for interacting with Things Cloud directly.

Setup
export THINGS_USERNAME='your@email.com'
export THINGS_PASSWORD='yourpassword'
go build -o things-cli ./cmd/things-cli/
Commands
# Read
things-cli list [--today] [--inbox] [--anytime] [--someday] [--upcoming] [--search QUERY] [--area NAME] [--project NAME]
things-cli today
things-cli inbox
things-cli anytime
things-cli someday
things-cli upcoming
things-cli search <query>
things-cli show <uuid>
things-cli areas
things-cli projects
things-cli tags

# Optional read-state cache location
export THINGS_CLI_CACHE=/path/to/things-cli-state.json

# Create
things-cli create "Title" [--note ...] [--when today|anytime|someday|inbox] \
  [--deadline YYYY-MM-DD] [--scheduled YYYY-MM-DD] \
  [--project UUID] [--heading UUID] [--area UUID] \
  [--tags UUID,...] [--type task|project|heading]
things-cli create-area "Name"
things-cli create-tag "Name" [--shorthand KEY] [--parent UUID]

# Modify
things-cli edit <uuid> [--title ...] [--note ...] [--when ...] [--deadline ...]
things-cli complete <uuid>
things-cli trash <uuid>
things-cli purge <uuid>
things-cli move-to-today <uuid>

# Batch (all operations in one HTTP request - much faster!)
# UUIDs must be canonical Base58 identifiers, as returned by create/list
echo '[{"cmd":"complete","uuid":"BXmAcvS6yK1eDhW31MuZrL"},{"cmd":"trash","uuid":"VJ1edXTP9q3PmFDUuy8EQh"}]' | things-cli batch
Examples
# Create a project with tasks
things-cli create "My Project" --type project --when anytime
# → {"status":"created","uuid":"BXmAcvS6yK1eDhW31MuZrL","title":"My Project"}

things-cli create "First Task" --project BXmAcvS6yK1eDhW31MuZrL --when today --note "Details here"

# Create an area and assign tasks
things-cli create-area "Work"
things-cli create "Review PR" --area <area-uuid> --when today --deadline 2026-02-15

# Batch operations (50 ops in ~2 sec instead of ~2-3 min)
echo '[
  {"cmd": "create", "title": "Task 1"},
  {"cmd": "create", "title": "Task 2"},
  {"cmd": "move-to-project", "uuid": "VJ1edXTP9q3PmFDUuy8EQh", "project": "BXmAcvS6yK1eDhW31MuZrL"},
  {"cmd": "complete", "uuid": "FQxaqvLBkbR5q2Q5oRoknc"}
]' | things-cli batch

Advanced SDK Usage

Working with Histories and Items
package main

import (
    "fmt"
    "os"

    things "github.com/arthursoares/things-cloud-sdk"
)

func main() {
    client := things.New(
        things.APIEndpoint,
        os.Getenv("THINGS_USERNAME"),
        os.Getenv("THINGS_PASSWORD"),
    )

    history, _ := client.OwnHistory()
    _ = history.Sync()

    // Create a project, then a task referencing it
    projectID := things.NewUUID()
    project := things.TaskActionItem{
        Item: things.Item{UUID: projectID, Kind: things.ItemKindTask, Action: things.ItemActionCreated},
        P: things.TaskActionItemPayload{
            Title:    things.String("My Project"),
            Type:     things.TaskTypePtr(things.TaskTypeProject),
            Schedule: things.Schedule(things.TaskScheduleAnytime),
        },
    }

    task := things.TaskActionItem{
        Item: things.Item{UUID: things.NewUUID(), Kind: things.ItemKindTask, Action: things.ItemActionCreated},
        P: things.TaskActionItemPayload{
            Title:         things.String("First task"),
            ParentTaskIDs: &[]string{projectID},
            Schedule:      things.Schedule(things.TaskScheduleAnytime),
        },
    }

    // One commit per Write; each item needs a unique canonical UUID —
    // Write() validates both and refuses anything that would corrupt
    // the sync history.
    if err := history.Write(project, task); err != nil {
        panic(err)
    }
    fmt.Println("✓ Created project with a task")
}

See the example/ directory for more complete examples including history sync, task creation, and state aggregation.

Persistent Sync Engine

The sync package provides a SQLite-backed sync engine that tracks "what changed since last sync" — perfect for building agents, automations, or dashboards that react to Things changes.

package main

import (
    "fmt"
    "os"
    things "github.com/arthursoares/things-cloud-sdk"
    "github.com/arthursoares/things-cloud-sdk/sync"
)

func main() {
    client := things.New(
        things.APIEndpoint,
        os.Getenv("THINGS_USERNAME"),
        os.Getenv("THINGS_PASSWORD"),
    )

    // Open persistent sync database
    syncer, _ := sync.Open("things.db", client)
    defer syncer.Close()

    // Fetch changes since last sync
    changes, _ := syncer.Sync()

    for _, c := range changes {
        switch v := c.(type) {
        case sync.TaskCreated:
            fmt.Printf("New task: %s\n", v.Task.Title)
        case sync.TaskCompleted:
            fmt.Printf("Completed: %s\n", v.Task.Title)
        case sync.TaskMovedToToday:
            fmt.Printf("Moved to Today: %s\n", v.Task.Title)
        }
    }

    // Query current state
    state := syncer.State()
    inbox, _ := state.TasksInInbox(sync.QueryOpts{})
    projects, _ := state.AllProjects(sync.QueryOpts{})
}
Semantic Change Types

The sync engine detects 40+ semantic change types:

Category Changes
Task Lifecycle TaskCreated, TaskCompleted, TaskUncompleted, TaskTrashed, TaskDeleted
Task Movement TaskMovedToInbox, TaskMovedToToday, TaskMovedToAnytime, TaskMovedToSomeday, TaskMovedToUpcoming
Task Organization TaskMovedToProject, TaskMovedToArea, TaskMovedUnderHeading, TaskTagsChanged
Task Details TaskTitleChanged, TaskNoteChanged, TaskDeadlineSet, TaskDeadlineRemoved
Projects ProjectCreated, ProjectCompleted, ProjectTrashed, ProjectDeleted
Areas & Tags AreaCreated, AreaDeleted, TagCreated, TagDeleted
Checklists ChecklistItemCreated, ChecklistItemCompleted, ChecklistItemDeleted
State Queries
state := syncer.State()

// Query by location
inbox, _ := state.TasksInInbox(sync.QueryOpts{})
today, _ := state.TasksInToday(sync.QueryOpts{})
anytime, _ := state.TasksInAnytime(sync.QueryOpts{})
someday, _ := state.TasksInSomeday(sync.QueryOpts{})
upcoming, _ := state.TasksInUpcoming(sync.QueryOpts{})

// Query by container
tasks, _ := state.TasksInProject(projectUUID, sync.QueryOpts{})
tasks, _ := state.TasksInArea(areaUUID, sync.QueryOpts{})
tasks, _ := state.TasksUnderHeading(headingUUID, sync.QueryOpts{})
headings, _ := state.HeadingsInProject(projectUUID, sync.QueryOpts{})

// Query by tag or text
tasks, _ := state.TasksWithTag(tagUUID, sync.QueryOpts{})
tasks, _ := state.SearchTasks("invoice", sync.QueryOpts{})

// List all
projects, _ := state.AllProjects(sync.QueryOpts{})
headings, _ := state.AllHeadings(sync.QueryOpts{})
areas, _ := state.AllAreas(sync.QueryOpts{})
tags, _ := state.AllTags(sync.QueryOpts{})
Change Log Queries
// Changes in last hour
changes, _ := syncer.ChangesSince(time.Now().Add(-1 * time.Hour))

// Changes for a specific task
changes, _ := syncer.ChangesForEntity(taskUUID)

// Changes since server index
changes, _ := syncer.ChangesSinceIndex(150)

Wire Format Notes

Key findings from reverse engineering the Things Cloud sync protocol:

  • UUIDs must be Base58-encoded (Bitcoin alphabet: 123456789ABCDEFGH...). Standard UUID strings or other encodings will crash Things.app during sync.
  • md (modification date) must be null on creates. Set timestamps only on updates.
  • Schedule field (st): 0 = Inbox, 1 = Anytime/Today (with sr/tir dates = Today), 2 = Someday/Upcoming (with dates = Upcoming).
  • Status field (ss): 0 = Pending, 2 = Canceled, 3 = Completed. Don't confuse with st (schedule)!
  • Headings (tp=2) must have st=1 (anytime). st=0 (inbox) crashes Things.app.
  • Tasks in projects, headings, or areas should default to st=1 (anytime) — they've been triaged out of inbox.
  • Kind strings: Task6, Tag4, ChecklistItem3, Area3, Tombstone2

Since v0.3.0 the SDK enforces the identifier rules instead of trusting callers: things.NewUUID() generates canonical Base58 identifiers (one leading 1 per leading zero byte — a subtlety whose absence used to corrupt ~1 in 256 creates), things.ValidateUUID() checks any identifier, and History.Write() refuses items with invalid or duplicate UUIDs before anything reaches the server.

See docs/client-side-bugs.md for the full investigation and crash analysis.

Architecture

The SDK models all changes as immutable Items (events). A History is a sync stream identified by a UUID. The client pushes/pulls Items through Histories, inspired by operational transformations and Git's internals.

TODO

  • Repeat after completion
  • Persistent state storage (see sync package)

Note

As there is no official API documentation available all requests need to be reverse engineered, which takes some time. Feel free to contribute and improve & extend this implementation.

Documentation

Index

Examples

Constants

View Source
const (
	// APIEndpoint is the public culturedcode https endpoint
	APIEndpoint = "https://cloud.culturedcode.com"
)
View Source
const NoteTypeDelta = 2

NoteTypeDelta indicates a note with incremental patches

View Source
const NoteTypeFullText = 1

NoteTypeFullText indicates a note with complete text

View Source
const ThingsUserAgent = "ThingsMac/32209501"

ThingsUserAgent is the http user-agent header set by things for mac

Variables

View Source
var (
	// ErrUnauthorized is returned by the API when the credentials are wrong
	ErrUnauthorized = errors.New("unauthorized")
)

Functions

func ApplyPatches added in v0.3.0

func ApplyPatches(original string, patches []NotePatch) string

ApplyPatches applies a series of text patches to an original string

func DecodeUUID added in v0.3.0

func DecodeUUID(s string) (uuid.UUID, error)

DecodeUUID decodes a canonical Base58 identifier back into a UUID. It rejects strings that are not canonical encodings of exactly 16 bytes.

func EncodeUUID added in v0.3.0

func EncodeUUID(u uuid.UUID) string

EncodeUUID encodes a UUID as canonical Base58: one leading '1' per leading zero byte, matching the identifiers Things.app itself emits.

func NewUUID added in v0.3.0

func NewUUID() string

NewUUID returns a new random identifier in the canonical Base58 wire format expected by Things Cloud.

func String added in v0.3.0

func String(str string) *string

String returns a pointer to a string

func ValidateUUID added in v0.3.0

func ValidateUUID(s string) error

ValidateUUID reports whether s is a canonical Base58-encoded 16-byte identifier safe to write to Things Cloud.

Types

type AccountService

type AccountService service

AccountService allows account specific interaction with thingscloud

func (*AccountService) AcceptSLA added in v0.3.0

func (s *AccountService) AcceptSLA() error

func (*AccountService) ChangePassword

func (s *AccountService) ChangePassword(newPassword string) (*Client, error)

ChangePassword allows you to change your account password. Because things does not work with sessions you need to create a new client instance after executing this method

func (*AccountService) Confirm

func (s *AccountService) Confirm(code string) error

Confirm finishes the account creation by providing the email token send by thingscloud

func (*AccountService) Delete

func (s *AccountService) Delete() error

Delete deletes your current thingscloud account. This cannot be reversed

func (*AccountService) SignUp

func (s *AccountService) SignUp(email, password string) (*Client, error)

SignUp creates a new thingscloud account and returns a configured client

type AccountStatus

type AccountStatus string

AccountStatus describes possible thingscloud account statuses

const (
	// AccountStatusActive is for active accounts
	AccountStatusActive AccountStatus = "SYAccountStatusActive"
)

type AppInstanceRequest added in v0.3.0

type AppInstanceRequest struct {
	AppInstanceID string `json:"-"`
	HistoryKey    string `json:"history-key"`
	APNSToken     string `json:"apns-token"`
	AppID         string `json:"app-id"`
	Dev           bool   `json:"dev"`
}

AppInstanceRequest describes the payload for registering a device for push notifications

type Area

type Area struct {
	UUID  string
	Title string
	Tags  []*Tag
	Tasks []*Task
}

Area describes an Area inside things. An Area is a container for tasks 0|uuid|TEXT|0||1 1|title|TEXT|0||0 2|visible|INTEGER|0||0 3|index|INTEGER|0||0

type AreaActionItem

type AreaActionItem struct {
	Item
	P AreaActionItemPayload `json:"p"`
}

AreaActionItem describes an event on an Area

func (AreaActionItem) UUID

func (item AreaActionItem) UUID() string

UUID returns the UUID of the modified Area

type AreaActionItemPayload

type AreaActionItemPayload struct {
	IX     *int     `json:"ix,omitempty"`
	Title  *string  `json:"tt,omitempty"`
	TagIDs []string `json:"tg,omitempty"`
}

AreaActionItemPayload describes the payload for modifying Areas

type Boolean

type Boolean bool

Boolean allows integers to be parsed into booleans, where 1 means true and 0 means false

func (*Boolean) MarshalJSON

func (b *Boolean) MarshalJSON() ([]byte, error)

MarshalJSON takes a boolean and serializes it as an integer

func (*Boolean) UnmarshalJSON

func (b *Boolean) UnmarshalJSON(bs []byte) error

UnmarshalJSON takes an int and creates a boolean instance

type CheckListActionItem

type CheckListActionItem struct {
	Item
	P CheckListActionItemPayload `json:"p"`
}

CheckListActionItem describes an event on a check list item

func (CheckListActionItem) UUID

func (item CheckListActionItem) UUID() string

UUID returns the UUID of the modified CheckListItem

type CheckListActionItemPayload

type CheckListActionItemPayload struct {
	CreationDate     *Timestamp      `json:"cd,omitempty"`
	ModificationDate *Timestamp      `json:"md,omitempty"`
	Index            *int            `json:"ix"`
	Status           *TaskStatus     `json:"ss,omitempty"`
	Title            *string         `json:"tt,omitempty"`
	CompletionDate   *Timestamp      `json:"sp,omitempty"`
	TaskIDs          *[]string       `json:"ts,omitempty"`
	Leavable         *bool           `json:"lt,omitempty"`
	ExtensionData    json.RawMessage `json:"xx,omitempty"`
}

CheckListActionItemPayload describes the payload for modifying CheckListItems

type CheckListItem

type CheckListItem struct {
	UUID             string
	CreationDate     time.Time
	ModificationDate *time.Time
	Status           TaskStatus
	Title            string
	Index            int
	CompletionDate   *time.Time
	TaskIDs          []string
}

CheckListItem describes a check list item 0|uuid|TEXT|0||1 1|userModificationDate|REAL|0||0 2|creationDate|REAL|0||0 3|title|TEXT|0||0 4|status|INTEGER|0||0 5|stopDate|REAL|0||0 6|index|INTEGER|0||0 7|task|TEXT|0||0

type Client

type Client struct {
	Endpoint string
	EMail    string

	ClientInfo ClientInfo
	Debug      bool

	Accounts *AccountService
	// contains filtered or unexported fields
}

Client is a culturedcode cloud client. It can be used to interact with the things cloud to manage your data.

func New

func New(endpoint, email, password string) *Client

New initializes a things client

func (*Client) CreateHistory

func (c *Client) CreateHistory() (*History, error)

CreateHistory requests a new history key

func (*Client) Histories

func (c *Client) Histories() ([]*History, error)

Histories requests all known history keys

Example
client := New(APIEndpoint, os.Getenv("THINGS_USERNAME"), os.Getenv("THINGS_PASSWORD"))

histories, err := client.Histories()
if err != nil {
	log.Printf("Failed loading histories: %q", err.Error())
}

for _, history := range histories {
	if err := history.Sync(); err != nil {
		log.Printf("Failed syncing history: %q", err.Error())
	}
}

func (*Client) History added in v0.1.1

func (c *Client) History(id string) (*History, error)

History requests a specific history

func (*Client) HistoryWithID added in v0.3.0

func (c *Client) HistoryWithID(id string) *History

HistoryWithID creates a History object with the given ID without making a network call. Use this when you already know the history ID (e.g., from a previous sync).

func (*Client) OwnHistory added in v0.3.0

func (c *Client) OwnHistory() (*History, error)

OwnHistory returns the clients own history

func (*Client) RegisterAppInstance added in v0.3.0

func (c *Client) RegisterAppInstance(req AppInstanceRequest) error

RegisterAppInstance registers a device for push notifications via APNS

func (*Client) Verify

func (c *Client) Verify() (*VerifyResponse, error)

Verify checks that the provided API credentials are valid.

Example
client := New(APIEndpoint, os.Getenv("THINGS_USERNAME"), os.Getenv("THINGS_PASSWORD"))

_, err := client.Verify()
if err != nil {
	log.Printf("Invalid Credentials: %q", err.Error())
}

type ClientInfo added in v0.3.0

type ClientInfo struct {
	DeviceModel string `json:"dm"`
	LocalRegion string `json:"lr"`
	NF          bool   `json:"nf"`
	NK          bool   `json:"nk"`
	AppName     string `json:"nn"`
	AppVersion  string `json:"nv"`
	OSName      string `json:"on"`
	OSVersion   string `json:"ov"`
	PrimaryLang string `json:"pl"`
	UserLocale  string `json:"ul"`
}

ClientInfo represents the device metadata sent in the things-client-info header.

func DefaultClientInfo added in v0.3.0

func DefaultClientInfo() ClientInfo

DefaultClientInfo returns a ClientInfo with default values matching a typical Mac client.

type FrequencyUnit added in v0.1.1

type FrequencyUnit int64

FrequencyUnit describes recurring frequencies

var (
	// FrequencyUnitDaily occurs every n days
	FrequencyUnitDaily FrequencyUnit = 16
	// FrequencyUnitWeekly occurs every n weeks
	FrequencyUnitWeekly FrequencyUnit = 256
	// FrequencyUnitMonthly occurs every n months
	FrequencyUnitMonthly FrequencyUnit = 8
	// FrequencyUnitYearly occurs every n years
	FrequencyUnitYearly FrequencyUnit = 4
)

type HTTPError added in v0.3.0

type HTTPError struct {
	StatusCode int
	Status     string
}

HTTPError is returned when the Things Cloud server responds with an unexpected status code. Callers can inspect StatusCode with errors.As to decide whether a request is worth retrying.

func (*HTTPError) Error added in v0.3.0

func (e *HTTPError) Error() string

type History

type History struct {
	ID                     string
	Client                 *Client
	LatestServerIndex      int
	LoadedServerIndex      int
	LatestSchemaVersion    int
	EndTotalContentSize    int
	LatestTotalContentSize int
}

History represents a synchronization stream. It's identified with a uuid v4

func (*History) Delete

func (h *History) Delete() error

Delete destroys a history Note that thingscloud will always return 202, even if the key is unknown

func (*History) Items

func (h *History) Items(opts ItemsOptions) ([]Item, bool, error)

Items fetches changes from thingscloud. Every change contains multiple items which have been modified. The Items method unwraps these objects and returns a list instead.

Note that if a item was changed multiple times it will be present multiple times in the result too.

Example
client := New(APIEndpoint, os.Getenv("THINGS_USERNAME"), os.Getenv("THINGS_PASSWORD"))

histories, err := client.Histories()
if err != nil {
	log.Printf("Failed loading histories: %q", err.Error())
}
history := histories[0]

items, _, err := history.Items(ItemsOptions{})
if err != nil {
	log.Printf("Failed loading items: %q", err.Error())
}

log.Printf("got %d items.", len(items))

func (*History) Sync

func (h *History) Sync() error

Sync ensures the history object is able to write to things

func (*History) Write

func (h *History) Write(items ...Identifiable) error

type Identifiable

type Identifiable interface {
	UUID() string
}

Identifiable abstracts different thingscloud write requests. As we need to provide a map indexed by UUID, all we care about is the ID of the change, not the change itself

type Item

type Item struct {
	UUID           string          `json:"-"`
	P              json.RawMessage `json:"p"`
	Kind           ItemKind        `json:"e"`
	Action         ItemAction      `json:"t"`
	ServerIndex    int             `json:"-"`
	HasServerIndex bool            `json:"-"`
}

Item is an event in thingscloud. Every action inside things generates an Item. Common items are the creation of a task, area or checklist, as well as modifying attributes or marking things as done.

type ItemAction

type ItemAction int

ItemAction describes possible actions on Items

const (
	// ItemActionCreated is used to indicate a new Item was created
	ItemActionCreated ItemAction = iota
	// ItemActionModified is used to indicate an existing Item was modified
	ItemActionModified ItemAction = 1
	// ItemActionDeleted is used as a tombstone for an Item
	ItemActionDeleted ItemAction = 2
)

func (ItemAction) String

func (i ItemAction) String() string

type ItemKind

type ItemKind string

ItemKind describes the different types things cloud supports

var (
	// ItemKindChecklistItem identifies a CheckList
	ItemKindChecklistItem  ItemKind = "ChecklistItem"
	ItemKindChecklistItem2 ItemKind = "ChecklistItem2"
	ItemKindChecklistItem3 ItemKind = "ChecklistItem3"
	// ItemKindTask identifies a Task or Subtask
	ItemKindTask      ItemKind = "Task6"
	ItemKindTask4     ItemKind = "Task4"
	ItemKindTask3     ItemKind = "Task3"
	ItemKindTaskPlain ItemKind = "Task"
	// ItemKindArea identifies an Area in the legacy Area2 encoding.
	//
	// Deprecated: never WRITE items with this kind — Things.app silently
	// ignores Area2 creates and manufactures a duplicate area with a new
	// UUID. Use ItemKindArea3. Reading Area2 items from old histories is
	// still supported.
	ItemKindArea      ItemKind = "Area2"
	ItemKindArea3     ItemKind = "Area3"
	ItemKindAreaPlain ItemKind = "Area"
	// ItemKindSettings  identifies a setting
	ItemKindSettings ItemKind = "Settings3"
	// ItemKindTag identifies a Tag
	ItemKindTag       ItemKind = "Tag3"
	ItemKindTag4      ItemKind = "Tag4"
	ItemKindTagPlain  ItemKind = "Tag"
	ItemKindTombstone ItemKind = "Tombstone2"
)

type ItemsOptions

type ItemsOptions struct {
	StartIndex int
}

ItemsOptions allows a client to pickup changes from a specific index

type Note added in v0.3.0

type Note struct {
	TypeTag  string      `json:"_t"`
	Type     int         `json:"t"`
	Checksum int64       `json:"ch,omitempty"`
	Value    string      `json:"v,omitempty"`
	Patches  []NotePatch `json:"ps,omitempty"`
}

Note describes a structured note as used by the Things API

type NotePatch added in v0.3.0

type NotePatch struct {
	Replacement string `json:"r"`
	Position    int    `json:"p"`
	Length      int    `json:"l"`
	Checksum    int64  `json:"ch"`
}

NotePatch describes a single text replacement operation

type RepeaterConfiguration added in v0.1.1

type RepeaterConfiguration struct {
	FirstScheduledAt    *Timestamp                    `json:"ia,omitempty"`
	RepeatCount         *int64                        `json:"rc,omitempty"`
	FrequencyUnit       FrequencyUnit                 `json:"fu"`
	FrequencyAmplitude  int64                         `json:"fa"`
	DetailConfiguration []RepeaterDetailConfiguration `json:"of"`
	LastScheduledAt     *Timestamp                    `json:"ed,omitempty"`
	Version             int                           `json:"rrv,omitempty"`
	Type                int                           `json:"tp,omitempty"`
	TimeShift           int                           `json:"ts,omitempty"`
	StartReference      *Timestamp                    `json:"sr,omitempty"`
}

RepeaterConfiguration configures the recurring rules of a task/ project

func (RepeaterConfiguration) ComputeFirstScheduledAt added in v0.1.1

func (c RepeaterConfiguration) ComputeFirstScheduledAt(t time.Time) time.Time

ComputeFirstScheduledAt calculates the first occurrence of a recurring rule based on the pattern This value has to be stored as FirstScheduledAt per thingscloud convention

func (RepeaterConfiguration) IsNeverending added in v0.1.1

func (c RepeaterConfiguration) IsNeverending() bool

IsNeverending determines if a recurring rule has a specific end

func (RepeaterConfiguration) NextScheduledAt added in v0.1.1

func (c RepeaterConfiguration) NextScheduledAt(repeat int) time.Time

NextScheduledAt returns the next Nth date a rule should occur. Note that things generates these ToDos as necessary.

type RepeaterDetailConfiguration added in v0.1.1

type RepeaterDetailConfiguration struct {
	Day     *int64        `json:"dy,omitempty"`
	Month   *int64        `json:"mo,omitempty"`
	Weekday *time.Weekday `json:"wd,omitempty"`
	MonthOf *int64        `json:"wdo,omitempty"`
}

RepeaterDetailConfiguration configures specifics of a repeater configuration.

type Setting

type Setting struct{}

Setting describes things settings 0|uuid|TEXT|0||1 1|logInterval|INTEGER|0||0 2|manualLogDate|REAL|0||0 3|groupTodayByParent|INTEGER|0||0

type Tag

type Tag struct {
	UUID         string
	Title        string
	ParentTagIDs []string
	ShortHand    string
}

Tag describes the aggregated state of an Tag 0|uuid|TEXT|0||1 1|title|TEXT|0||0 2|shortcut|TEXT|0||0 3|usedDate|REAL|0||0 4|parent|TEXT|0||0 5|index|INTEGER|0||0

type TagActionItem

type TagActionItem struct {
	Item
	P TagActionItemPayload `json:"p"`
}

TagActionItem describes an event on a tag

func (TagActionItem) UUID

func (t TagActionItem) UUID() string

UUID returns the UUID of the modified Tag

type TagActionItemPayload

type TagActionItemPayload struct {
	IX            *int            `json:"ix"`
	Title         *string         `json:"tt"`
	ShortHand     *string         `json:"sh"`
	ParentTagIDs  *[]string       `json:"pn"`
	ExtensionData json.RawMessage `json:"xx,omitempty"`
}

TagActionItemPayload describes the payload for modifying Areas

type Task

type Task struct {
	UUID             string
	CreationDate     time.Time
	ModificationDate *time.Time
	Status           TaskStatus
	Title            string
	Note             string
	ScheduledDate    *time.Time
	CompletionDate   *time.Time
	DeadlineDate     *time.Time
	Index            int
	AreaIDs          []string
	ParentTaskIDs    []string
	ActionGroupIDs   []string
	InTrash          bool
	Schedule         TaskSchedule
	Type             TaskType
	TodayIndex       int
	DueOrder         int
	AlarmTimeOffset  *int
	TagIDs           []string
	RecurrenceIDs    []string
	DelegateIDs      []string
}

Task describes a Task inside things. 0|uuid|TEXT|0||1 1|userModificationDate|REAL|0||0 2|creationDate|REAL|0||0 3|trashed|INTEGER|0||0 4|type|INTEGER|0||0 5|title|TEXT|0||0 6|notes|TEXT|0||0 7|dueDate|REAL|0||0 8|dueDateOffset|INTEGER|0||0 9|status|INTEGER|0||0 10|stopDate|REAL|0||0 11|start|INTEGER|0||0 12|startDate|REAL|0||0 13|index|INTEGER|0||0 14|todayIndex|INTEGER|0||0 15|area|TEXT|0||0 16|project|TEXT|0||0 17|repeatingTemplate|TEXT|0||0 18|delegate|TEXT|0||0 19|recurrenceRule|BLOB|0||0 20|instanceCreationStartDate|REAL|0||0 21|instanceCreationPaused|INTEGER|0||0 22|instanceCreationCount|INTEGER|0||0 23|afterCompletionReferenceDate|REAL|0||0 24|actionGroup|TEXT|0||0 25|untrashedLeafActionsCount|INTEGER|0||0 26|openUntrashedLeafActionsCount|INTEGER|0||0 27|checklistItemsCount|INTEGER|0||0 28|openChecklistItemsCount|INTEGER|0||0 29|startBucket|INTEGER|0||0 30|alarmTimeOffset|REAL|0||0 31|lastAlarmInteractionDate|REAL|0||0 32|todayIndexReferenceDate|REAL|0||0 33|nextInstanceStartDate|REAL|0||0 34|dueDateSuppressionDate|REAL|0||0

type TaskActionItem

type TaskActionItem struct {
	Item
	P TaskActionItemPayload `json:"p"`
}

TaskActionItem describes an event on a Task

func (TaskActionItem) UUID

func (t TaskActionItem) UUID() string

UUID returns the UUID of the modified Task

type TaskActionItemPayload

type TaskActionItemPayload struct {
	Index                     *int                   `json:"ix,omitempty"`
	CreationDate              *Timestamp             `json:"cd,omitempty"`
	ModificationDate          *Timestamp             `json:"md,omitempty"` // ok
	ScheduledDate             *Timestamp             `json:"sr,omitempty"`
	CompletionDate            *Timestamp             `json:"sp,omitempty"`
	DeadlineDate              *Timestamp             `json:"dd,omitempty"`  //
	TaskIR                    *Timestamp             `json:"tir,omitempty"` // hm, not sure what tir stands for
	Status                    *TaskStatus            `json:"ss,omitempty"`
	Type                      *TaskType              `json:"tp,omitempty"`
	Title                     *string                `json:"tt,omitempty"`
	Note                      json.RawMessage        `json:"nt,omitempty"`
	AreaIDs                   *[]string              `json:"ar,omitempty"`
	ParentTaskIDs             *[]string              `json:"pr,omitempty"`
	TagIDs                    []string               `json:"tg,omitempty"`
	InTrash                   *bool                  `json:"tr,omitempty"`
	TaskIndex                 *int                   `json:"ti,omitempty"`
	RecurrenceTaskIDs         *[]string              `json:"rt,omitempty"`
	Schedule                  *TaskSchedule          `json:"st,omitempty"`
	ActionGroupIDs            *[]string              `json:"agr,omitempty"`
	Repeater                  *RepeaterConfiguration `json:"rr,omitempty"`
	DueOrder                  *int                   `json:"do,omitempty"`
	Leavable                  *bool                  `json:"lt,omitempty"`
	IsCompletedByChildren     *bool                  `json:"icp,omitempty"`
	IsCompletedCount          *int                   `json:"icc,omitempty"`
	InstanceCreationStartDate *Timestamp             `json:"icsd,omitempty"`
	SubtaskBehavior           *int                   `json:"sb,omitempty"`
	DelegateIDs               *[]string              `json:"dl,omitempty"`
	LastActionItemID          *Timestamp             `json:"lai,omitempty"`
	ReminderDate              *Timestamp             `json:"rmd,omitempty"`
	AlarmTimeOffset           *int                   `json:"ato,omitempty"`
	ActionRequiredDate        *Timestamp             `json:"acrd,omitempty"`
	DeadlineSuppression       *Timestamp             `json:"dds,omitempty"`
	ExtensionData             json.RawMessage        `json:"xx,omitempty"`
}

TaskActionItemPayload describes the payload for modifying Tasks, and also Projects, as projects are special kind of Tasks

type TaskSchedule

type TaskSchedule int

TaskSchedule describes when a task is scheduled

const (
	// TaskScheduleInbox indicates unprocessed tasks in the Inbox (st=0)
	TaskScheduleInbox TaskSchedule = 0
	// TaskScheduleAnytime indicates started tasks; displayed in Today when sr/tir
	// are set to today's date, or in Anytime when sr/tir are null (st=1)
	TaskScheduleAnytime TaskSchedule = 1
	// TaskScheduleSomeday indicates deferred tasks; displayed in Upcoming when
	// sr/tir have a future date, or in Someday when sr/tir are null (st=2)
	TaskScheduleSomeday TaskSchedule = 2

	// TaskScheduleToday is deprecated: use TaskScheduleAnytime with sr/tir dates.
	// The "st" field value 0 actually means Inbox, not Today.
	// Kept for backward compatibility.
	TaskScheduleToday = TaskScheduleInbox
)

func Schedule added in v0.3.0

func Schedule(val TaskSchedule) *TaskSchedule

Schedule returns a pointer to a TaskSchedule

func (TaskSchedule) String

func (i TaskSchedule) String() string

type TaskStatus

type TaskStatus int

TaskStatus describes if a thing is completed or not

const (
	// TaskStatusPending indicates a new task
	TaskStatusPending TaskStatus = iota
	// TaskStatusCompleted indicates a completed task
	TaskStatusCompleted TaskStatus = 3
	// TaskStatusCanceled indicates a canceled task
	TaskStatusCanceled TaskStatus = 2
)

func Status added in v0.3.0

func Status(val TaskStatus) *TaskStatus

Status returns a pointer to a TaskStatus

func (TaskStatus) String

func (i TaskStatus) String() string

type TaskType added in v0.3.0

type TaskType int

TaskType describes the type of a task entity

const (
	TaskTypeTask    TaskType = 0
	TaskTypeProject TaskType = 1
	TaskTypeHeading TaskType = 2
)

func TaskTypePtr added in v0.3.0

func TaskTypePtr(val TaskType) *TaskType

TaskTypePtr returns a pointer to a TaskType

type Timestamp

type Timestamp time.Time

Timestamp allows unix epochs represented as float or ints to be unmarshalled into time.Time objects

func Time added in v0.3.0

func Time(val time.Time) *Timestamp

Time returns a pointer to a Time. The zero time returns nil: its UnixNano is out of range and would marshal to a garbage 1754 date.

func (*Timestamp) Format

func (t *Timestamp) Format(layout string) string

Format returns a textual representation of the time value formatted according to layout

func (*Timestamp) MarshalJSON

func (t *Timestamp) MarshalJSON() ([]byte, error)

MarshalJSON converts a timestamp into a fractional unix epoch (seconds with sub-second precision), matching the format used by the Things Cloud API (e.g. 1770713623.4716659).

func (*Timestamp) Time

func (t *Timestamp) Time() *time.Time

Time returns the underlying time.Time instance

func (*Timestamp) UnmarshalJSON

func (t *Timestamp) UnmarshalJSON(bs []byte) error

UnmarshalJSON takes a unix epoch from float/ int and creates a time.Time instance, preserving sub-second precision.

type TombstoneActionItem added in v0.3.0

type TombstoneActionItem struct {
	Item
	P TombstoneActionItemPayload `json:"p"`
}

TombstoneActionItem describes a tombstone deletion event

func (TombstoneActionItem) UUID added in v0.3.0

func (t TombstoneActionItem) UUID() string

UUID returns the UUID of the TombstoneActionItem

type TombstoneActionItemPayload added in v0.3.0

type TombstoneActionItemPayload struct {
	DeletedObjectID string  `json:"dloid"`
	DeletionDate    float64 `json:"dld"`
}

TombstoneActionItemPayload describes the payload for tombstone deletion records

type VerifyResponse

type VerifyResponse struct {
	SLAVersionAccepted string          `json:"SLA-version-accepted"`
	Issues             json.RawMessage `json:"issues"`
	Email              string          `json:"email"`
	MaildropEmail      string          `json:"maildrop-email"`
	Status             AccountStatus   `json:"status"`
	HistoryKey         string          `json:"history-key"`
}

VerifyResponse contains details about your account

Directories

Path Synopsis
cmd
debug command
debugupdate command
findtask command
fullstate command
list command
rawitem command
rawtask command
recent command
soak command
Command soak stress-tests the write path against a REAL Things Cloud account by driving the things-cli binary through many create/edit/move/ complete/trash cycles, including identifiers deliberately built from UUIDs with leading zero bytes — the ~1/256 case that used to corrupt sync histories before the canonical Base58 fix.
Command soak stress-tests the write path against a REAL Things Cloud account by driving the things-cli binary through many create/edit/move/ complete/trash cycles, including identifiers deliberately built from UUIDs with leading zero bytes — the ~1/256 case that used to corrupt sync histories before the canonical Base58 fix.
statedebug command
synctest command
things-cli command
thingsync command
trace command
state
Package sync provides a persistent sync engine for Things Cloud.
Package sync provides a persistent sync engine for Things Cloud.
Package syncutil provides shared utilities for sync-based CLI tools.
Package syncutil provides shared utilities for sync-based CLI tools.

Jump to

Keyboard shortcuts

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