basecamp

package
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Mar 9, 2026 License: MIT Imports: 26 Imported by: 0

Documentation

Overview

Package basecamp provides a Go SDK for the Basecamp API.

The SDK handles authentication, HTTP caching, rate limiting, and retry logic. It supports both OAuth 2.0 authentication and static token authentication.

Installation

To install the SDK, use go get:

go get github.com/basecamp/basecamp-sdk/go/pkg/basecamp

Authentication

The SDK supports two authentication methods:

Static Token Authentication (simplest):

cfg := basecamp.DefaultConfig()
token := &basecamp.StaticTokenProvider{Token: os.Getenv("BASECAMP_TOKEN")}
client := basecamp.NewClient(cfg, token)

// Create an account-scoped client for API operations
account := client.ForAccount("12345")

OAuth 2.0 Authentication (for user-facing apps):

cfg := basecamp.DefaultConfig()
authMgr := basecamp.NewAuthManager(cfg, http.DefaultClient)
client := basecamp.NewClient(cfg, authMgr)

// Discover available accounts
info, _ := client.Authorization().GetInfo(ctx, nil)
account := client.ForAccount(fmt.Sprint(info.Accounts[0].ID))

Configuration

Configuration can be loaded from environment variables or set programmatically:

cfg := basecamp.DefaultConfig()
cfg.LoadConfigFromEnv()  // Loads BASECAMP_PROJECT_ID, etc.

Environment variables:

  • BASECAMP_PROJECT_ID: Default project/bucket ID
  • BASECAMP_TOKEN: Static API token for authentication
  • BASECAMP_CACHE_ENABLED: Enable HTTP caching (default: true)

Services

The SDK provides typed services for each Basecamp resource:

Working with Projects

List all projects:

account := client.ForAccount("12345")
projects, err := account.Projects().List(ctx, nil)
if err != nil {
    log.Fatal(err)
}
for _, p := range projects {
    fmt.Println(p.Name)
}

Create a project:

project, err := account.Projects().Create(ctx, &basecamp.CreateProjectRequest{
    Name:        "New Project",
    Description: "Project description",
})

Working with Todos

List todos in a todolist:

todos, err := account.Todos().List(ctx, projectID, todolistID, nil)

Create a todo:

todo, err := account.Todos().Create(ctx, projectID, todolistID, &basecamp.CreateTodoRequest{
    Content: "Ship the feature",
    DueOn:   "2024-12-31",
})

Complete a todo:

err := account.Todos().Complete(ctx, projectID, todoID)

Searching

Search across your Basecamp account:

results, err := account.Search().Search(ctx, "quarterly report", nil)
for _, r := range results {
    fmt.Printf("%s: %s\n", r.Type, r.Title)
}

Pagination

The SDK handles pagination automatically via GetAll:

// GetAll fetches all pages automatically
account := client.ForAccount("12345")
results, err := account.GetAll(ctx, "/projects.json")

For fine-grained control, use Get with Link headers:

resp, err := client.Get(ctx, "/projects.json")
// Check resp.Headers.Get("Link") for pagination

Error Handling

The SDK returns typed errors that can be inspected:

resp, err := client.Get(ctx, "/projects/999.json")
if err != nil {
    if apiErr, ok := errors.AsType[*basecamp.Error](err); ok {
        switch apiErr.Code {
        case basecamp.CodeNotFound:
            // Handle 404
        case basecamp.CodeAuth:
            // Handle authentication error
        case basecamp.CodeRateLimit:
            // Handle rate limiting (auto-retried by default)
        }
    }
}

Automatic Features

The SDK automatically handles:

  • ETag-based HTTP caching for GET requests
  • Exponential backoff with jitter for retryable errors
  • Token refresh when using OAuth
  • Rate limit handling with automatic retry
  • Pagination via GetAll for list endpoints

Client Options

Customize the client with options:

client := basecamp.NewClient(cfg, token,
    basecamp.WithHTTPClient(customHTTPClient),
    basecamp.WithUserAgent("my-app/1.0"),
    basecamp.WithLogger(slog.Default()),
    basecamp.WithCache(customCache),
)

Thread Safety

The Client is safe for concurrent use. The ForAccount method may be called concurrently from multiple goroutines to create AccountClient instances.

Each AccountClient is also safe for concurrent use. Service accessors (e.g., account.Projects()) use mutex-protected lazy initialization.

Example of concurrent multi-account usage:

acme := client.ForAccount("12345")
initech := client.ForAccount("67890")

go func() { acme.Todos().List(ctx, projectID, todolistID, nil) }()
go func() { initech.Projects().List(ctx, nil) }()

Package basecamp provides a Go SDK for the Basecamp API.

Example (ErrorHandling)
package main

import (
	"context"
	"errors"
	"fmt"
	"os"

	"github.com/basecamp/basecamp-sdk/go/pkg/basecamp"
)

func main() {
	cfg := basecamp.DefaultConfig()
	token := &basecamp.StaticTokenProvider{Token: os.Getenv("BASECAMP_TOKEN")}
	client := basecamp.NewClient(cfg, token)

	ctx := context.Background()

	// Get a project that may not exist
	project, err := client.ForAccount("12345").Projects().Get(ctx, 999999999)
	if err != nil {
		if apiErr, ok := errors.AsType[*basecamp.Error](err); ok {
			switch apiErr.Code {
			case basecamp.CodeNotFound:
				fmt.Println("Project not found")
			case basecamp.CodeAuth:
				fmt.Println("Authentication required - please log in")
			case basecamp.CodeForbidden:
				fmt.Println("Access denied")
			case basecamp.CodeRateLimit:
				fmt.Println("Rate limited - try again later")
			default:
				fmt.Printf("API error: %s\n", apiErr.Message)
			}
		} else {
			fmt.Printf("Error: %v\n", err)
		}
		return
	}

	fmt.Printf("Found project: %s\n", project.Name)
}

Index

Examples

Constants

View Source
const (
	// LineContentTypePlain sends the line as plain text (the default when omitted).
	LineContentTypePlain = "text/plain"
	// LineContentTypeHTML sends the line as rich HTML content.
	LineContentTypeHTML = "text/html"
)

Line content type constants for campfire messages.

View Source
const (
	CodeUsage      = "usage"
	CodeNotFound   = "not_found"
	CodeAuth       = "auth_required"
	CodeForbidden  = "forbidden"
	CodeRateLimit  = "rate_limit"
	CodeNetwork    = "network"
	CodeAPI        = "api_error"
	CodeValidation = "validation"
	CodeAmbiguous  = "ambiguous"
)

Error codes for API responses.

View Source
const (
	ExitOK         = 0 // Success
	ExitUsage      = 1 // Invalid arguments or flags
	ExitNotFound   = 2 // Resource not found
	ExitAuth       = 3 // Not authenticated
	ExitForbidden  = 4 // Access denied (scope issue)
	ExitRateLimit  = 5 // Rate limited (429)
	ExitNetwork    = 6 // Connection/DNS/timeout error
	ExitAPI        = 7 // Server returned error
	ExitAmbiguous  = 8 // Multiple matches for name
	ExitValidation = 9 // Validation error (422)
)

Exit codes for CLI tools.

View Source
const (
	DefaultMaxRetries = 3
	DefaultBaseDelay  = 1 * time.Second
	DefaultMaxJitter  = 100 * time.Millisecond
	DefaultTimeout    = 30 * time.Second
	DefaultMaxPages   = 10000
)

Default values for HTTP client configuration. These can be overridden using functional options.

View Source
const (
	// MaxResponseBodyBytes is the maximum size for successful API response bodies (50 MB).
	MaxResponseBodyBytes int64 = 50 * 1024 * 1024
	// MaxErrorBodyBytes is the maximum size for error response bodies (1 MB).
	MaxErrorBodyBytes int64 = 1 * 1024 * 1024
	// MaxErrorMessageBytes is the maximum length for error messages included in errors (500 bytes).
	MaxErrorMessageBytes = 500
)

Response body size limits.

View Source
const (
	WebhookTypeCheckinReply   = "Checkin::Reply"
	WebhookTypeCloudFile      = "CloudFile"
	WebhookTypeComment        = "Comment"
	WebhookTypeDocument       = "Document"
	WebhookTypeForwardReply   = "Forward::Reply"
	WebhookTypeGoogleDocument = "GoogleDocument"
	WebhookTypeInboxForward   = "Inbox::Forward"
	WebhookTypeMessage        = "Message"
	WebhookTypeQuestion       = "Question"
	WebhookTypeQuestionAnswer = "Question::Answer"
	WebhookTypeScheduleEntry  = "Schedule::Entry"
	WebhookTypeTodo           = "Todo"
	WebhookTypeTodolist       = "Todolist"
	WebhookTypeTodolistGroup  = "Todolist::Group"
	WebhookTypeUpload         = "Upload"
	WebhookTypeVault          = "Vault"
)

Known webhook recording types (convenience constants, not exhaustive).

View Source
const APIVersion = "2026-01-26"

APIVersion is the Basecamp API version this SDK targets.

View Source
const DefaultCommentLimit = 100

DefaultCommentLimit is the default number of comments to return when no limit is specified.

View Source
const DefaultEventLimit = 100

DefaultEventLimit is the default number of events to return when no limit is specified.

View Source
const DefaultMessageLimit = 100

DefaultMessageLimit is the default number of messages to return when no limit is specified.

View Source
const DefaultRecordingLimit = 100

DefaultRecordingLimit is the default number of recordings to return when no limit is specified.

View Source
const DefaultTodoLimit = 100

DefaultTodoLimit is the default number of todos to return when no limit is specified.

View Source
const DefaultUserAgent = "basecamp-sdk-go/" + Version + " (api:" + APIVersion + ")"

DefaultUserAgent is the default User-Agent header value.

View Source
const Version = "0.3.0"

Version is the current version of the Basecamp Go SDK.

Variables

View Source
var (
	// ErrCircuitOpen is returned when the circuit breaker is open.
	ErrCircuitOpen = errors.New("circuit breaker is open")
	// ErrBulkheadFull is returned when the bulkhead has no available slots.
	ErrBulkheadFull = errors.New("bulkhead is full")
	// ErrRateLimited is returned when the rate limiter rejects a request.
	ErrRateLimited = errors.New("rate limit exceeded")
)

Resilience errors for circuit breaker, bulkhead, and rate limiting.

View Source
var DateOf = types.DateOf

DateOf returns the Date portion of a time.Time.

View Source
var ParseDate = types.ParseDate

ParseDate parses a string in YYYY-MM-DD format.

View Source
var Today = types.Today

Today returns today's date in the local timezone.

Functions

func ComputeWebhookSignature

func ComputeWebhookSignature(payload []byte, secret string) string

ComputeWebhookSignature computes the HMAC-SHA256 signature for a webhook payload.

func ExitCodeFor

func ExitCodeFor(code string) int

ExitCodeFor returns the exit code for a given error code.

func NormalizeBaseURL

func NormalizeBaseURL(url string) string

NormalizeBaseURL ensures consistent URL format (no trailing slash).

func ParseEventKind

func ParseEventKind(kind string) (recordingType, action string)

ParseEventKind splits a webhook event kind string into its type and action components. For example, "todo_created" returns ("todo", "created"). For compound types like "question_answer_created", it returns ("question_answer", "created").

func RedactHeaders

func RedactHeaders(headers http.Header) http.Header

RedactHeaders returns a copy of the headers with sensitive values replaced by "[REDACTED]". This is useful for safely logging HTTP requests and responses without exposing tokens.

The following headers are redacted:

  • Authorization
  • Cookie
  • Set-Cookie
  • X-CSRF-Token

Example:

safeHeaders := basecamp.RedactHeaders(req.Header)
logger.Debug("request", "headers", safeHeaders)

func RequireSecureEndpoint

func RequireSecureEndpoint(rawURL string) error

RequireSecureEndpoint validates that an endpoint URL is secure. Secure means HTTPS, or localhost (including .localhost TLD per RFC 6761) which is trusted for local development.

func VerifyWebhookSignature

func VerifyWebhookSignature(payload []byte, signature, secret string) bool

VerifyWebhookSignature checks that the given payload matches the HMAC-SHA256 signature. Returns false if secret or signature is empty.

Types

type APIProvenance

type APIProvenance struct {
	BC3    UpstreamRef `json:"bc3"`
	BC3API UpstreamRef `json:"bc3_api"`
}

APIProvenance describes which upstream API revisions the SDK was built against.

func Provenance

func Provenance() APIProvenance

Provenance returns the upstream API revisions this SDK was built against.

type AccountClient

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

AccountClient is an HTTP client bound to a specific Basecamp account. Create an AccountClient using Client.ForAccount(accountID). AccountClient is safe for concurrent use.

The Basecamp API requires an account ID in the URL path (e.g., https://3.basecampapi.com/12345/projects.json). This SDK passes the account ID as a path parameter to each generated client operation, matching the OpenAPI spec's /{accountId}/... path structure.

AccountClient shares the parent Client's generated API client and HTTP resources. Creating multiple AccountClients via ForAccount is lightweight.

func (*AccountClient) AccountID

func (ac *AccountClient) AccountID() string

AccountID returns the account ID this client is bound to.

func (*AccountClient) Attachments

func (ac *AccountClient) Attachments() *AttachmentsService

Attachments returns the AttachmentsService for file upload operations.

func (*AccountClient) Boosts

func (ac *AccountClient) Boosts() *BoostsService

Boosts returns the BoostsService for boost (emoji reaction) operations.

func (*AccountClient) Campfires

func (ac *AccountClient) Campfires() *CampfiresService

Campfires returns the CampfiresService for campfire chat operations.

func (*AccountClient) CardColumns

func (ac *AccountClient) CardColumns() *CardColumnsService

CardColumns returns the CardColumnsService for card column operations.

func (*AccountClient) CardSteps

func (ac *AccountClient) CardSteps() *CardStepsService

CardSteps returns the CardStepsService for card step operations.

func (*AccountClient) CardTables

func (ac *AccountClient) CardTables() *CardTablesService

CardTables returns the CardTablesService for card table operations.

func (*AccountClient) Cards

func (ac *AccountClient) Cards() *CardsService

Cards returns the CardsService for card operations.

func (*AccountClient) Checkins

func (ac *AccountClient) Checkins() *CheckinsService

Checkins returns the CheckinsService for automatic check-in operations.

func (*AccountClient) ClientApprovals

func (ac *AccountClient) ClientApprovals() *ClientApprovalsService

ClientApprovals returns the ClientApprovalsService for client approval operations.

func (*AccountClient) ClientCorrespondences

func (ac *AccountClient) ClientCorrespondences() *ClientCorrespondencesService

ClientCorrespondences returns the ClientCorrespondencesService for client correspondence operations.

func (*AccountClient) ClientReplies

func (ac *AccountClient) ClientReplies() *ClientRepliesService

ClientReplies returns the ClientRepliesService for client reply operations.

func (*AccountClient) Comments

func (ac *AccountClient) Comments() *CommentsService

Comments returns the CommentsService for comment operations.

func (*AccountClient) Delete

func (ac *AccountClient) Delete(ctx context.Context, path string) (*Response, error)

Delete performs an account-scoped DELETE request.

func (*AccountClient) Documents

func (ac *AccountClient) Documents() *DocumentsService

Documents returns the DocumentsService for document operations.

func (*AccountClient) Events

func (ac *AccountClient) Events() *EventsService

Events returns the EventsService for event operations.

func (*AccountClient) Forwards

func (ac *AccountClient) Forwards() *ForwardsService

Forwards returns the ForwardsService for email forward operations.

func (*AccountClient) Get

func (ac *AccountClient) Get(ctx context.Context, path string) (*Response, error)

Get performs an account-scoped GET request.

func (*AccountClient) GetAll

func (ac *AccountClient) GetAll(ctx context.Context, path string) ([]json.RawMessage, error)

GetAll fetches all pages for an account-scoped paginated resource.

Example
package main

import (
	"context"
	"fmt"
	"log"
	"os"

	"github.com/basecamp/basecamp-sdk/go/pkg/basecamp"
)

func main() {
	cfg := basecamp.DefaultConfig()
	token := &basecamp.StaticTokenProvider{Token: os.Getenv("BASECAMP_TOKEN")}
	client := basecamp.NewClient(cfg, token)

	ctx := context.Background()
	account := client.ForAccount("12345")

	// GetAll automatically handles pagination for account-scoped resources
	results, err := account.GetAll(ctx, "/projects.json")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Fetched %d projects across all pages\n", len(results))
}

func (*AccountClient) GetAllWithLimit

func (ac *AccountClient) GetAllWithLimit(ctx context.Context, path string, limit int) ([]json.RawMessage, error)

GetAllWithLimit fetches pages for an account-scoped paginated resource up to a limit. If limit is 0, it fetches all pages (same as GetAll). If limit > 0, it stops after collecting at least limit items.

func (*AccountClient) Lineup

func (ac *AccountClient) Lineup() *LineupService

Lineup returns the LineupService for lineup marker operations.

func (*AccountClient) MessageBoards

func (ac *AccountClient) MessageBoards() *MessageBoardsService

MessageBoards returns the MessageBoardsService for message board operations.

func (*AccountClient) MessageTypes

func (ac *AccountClient) MessageTypes() *MessageTypesService

MessageTypes returns the MessageTypesService for message type operations.

func (*AccountClient) Messages

func (ac *AccountClient) Messages() *MessagesService

Messages returns the MessagesService for message operations.

func (*AccountClient) People

func (ac *AccountClient) People() *PeopleService

People returns the PeopleService for people operations.

func (*AccountClient) Post

func (ac *AccountClient) Post(ctx context.Context, path string, body any) (*Response, error)

Post performs an account-scoped POST request with a JSON body.

func (*AccountClient) Projects

func (ac *AccountClient) Projects() *ProjectsService

Projects returns the ProjectsService for project operations.

func (*AccountClient) Put

func (ac *AccountClient) Put(ctx context.Context, path string, body any) (*Response, error)

Put performs an account-scoped PUT request with a JSON body.

func (*AccountClient) Recordings

func (ac *AccountClient) Recordings() *RecordingsService

Recordings returns the RecordingsService for recording operations.

func (*AccountClient) Reports

func (ac *AccountClient) Reports() *ReportsService

Reports returns the ReportsService for reports operations.

func (*AccountClient) Schedules

func (ac *AccountClient) Schedules() *SchedulesService

Schedules returns the SchedulesService for schedule operations.

func (*AccountClient) Search

func (ac *AccountClient) Search() *SearchService

Search returns the SearchService for search operations.

func (*AccountClient) Subscriptions

func (ac *AccountClient) Subscriptions() *SubscriptionsService

Subscriptions returns the SubscriptionsService for subscription operations.

func (*AccountClient) Templates

func (ac *AccountClient) Templates() *TemplatesService

Templates returns the TemplatesService for template operations.

func (*AccountClient) Timeline

func (ac *AccountClient) Timeline() *TimelineService

Timeline returns the TimelineService for timeline and progress operations.

func (*AccountClient) Timesheet

func (ac *AccountClient) Timesheet() *TimesheetService

Timesheet returns the TimesheetService for timesheet report operations.

func (*AccountClient) TodolistGroups

func (ac *AccountClient) TodolistGroups() *TodolistGroupsService

TodolistGroups returns the TodolistGroupsService for todolist group operations.

func (*AccountClient) Todolists

func (ac *AccountClient) Todolists() *TodolistsService

Todolists returns the TodolistsService for todolist operations.

func (*AccountClient) Todos

func (ac *AccountClient) Todos() *TodosService

Todos returns the TodosService for todo operations.

func (*AccountClient) Todosets

func (ac *AccountClient) Todosets() *TodosetsService

Todosets returns the TodosetsService for todoset operations.

func (*AccountClient) Tools

func (ac *AccountClient) Tools() *ToolsService

Tools returns the ToolsService for dock tool operations.

func (*AccountClient) Uploads

func (ac *AccountClient) Uploads() *UploadsService

Uploads returns the UploadsService for upload (file) operations.

func (*AccountClient) Vaults

func (ac *AccountClient) Vaults() *VaultsService

Vaults returns the VaultsService for vault (folder) operations.

func (*AccountClient) Webhooks

func (ac *AccountClient) Webhooks() *WebhooksService

Webhooks returns the WebhooksService for webhook operations.

type AnswerListOptions

type AnswerListOptions struct {
	// Limit is the maximum number of answers to return.
	// If 0 (default), returns all answers. Use a positive value to cap results.
	Limit int

	// Page, if non-zero, disables pagination and returns only the first page.
	// NOTE: The page number itself is not yet honored due to OpenAPI client
	// limitations. Use 0 to paginate through all results up to Limit.
	Page int
}

AnswerListOptions specifies options for listing answers.

type AnswerListResult

type AnswerListResult struct {
	// Answers is the list of answers returned.
	Answers []QuestionAnswer
	// Meta contains pagination metadata (total count, etc.).
	Meta ListMeta
}

AnswerListResult contains the results from listing answers.

type Assignable

type Assignable struct {
	ID        int64    `json:"id"`
	Title     string   `json:"title"`
	Type      string   `json:"type"`
	URL       string   `json:"url"`
	AppURL    string   `json:"app_url"`
	Bucket    *Bucket  `json:"bucket,omitempty"`
	Parent    *Parent  `json:"parent,omitempty"`
	DueOn     string   `json:"due_on,omitempty"`
	StartsOn  string   `json:"starts_on,omitempty"`
	Assignees []Person `json:"assignees,omitempty"`
}

Assignable represents an assignable item (todo or schedule entry).

type AssignedTodosOptions

type AssignedTodosOptions struct {
	// GroupBy groups results by "bucket" or "date".
	GroupBy string
}

AssignedTodosOptions specifies options for GetAssignedTodos.

type AssignedTodosResponse

type AssignedTodosResponse struct {
	Person    *Person `json:"person"`
	GroupedBy string  `json:"grouped_by"`
	Todos     []Todo  `json:"todos"`
}

AssignedTodosResponse contains the assigned todos for a person.

type AttachmentResponse

type AttachmentResponse struct {
	AttachableSGID string `json:"attachable_sgid"`
}

AttachmentResponse represents the response from creating an attachment.

type AttachmentsService

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

AttachmentsService handles attachment upload operations.

func NewAttachmentsService

func NewAttachmentsService(client *AccountClient) *AttachmentsService

NewAttachmentsService creates a new AttachmentsService.

func (*AttachmentsService) Create

func (s *AttachmentsService) Create(ctx context.Context, filename, contentType string, data io.Reader) (result *AttachmentResponse, err error)

Create uploads a file and returns an attachable_sgid for embedding in rich text. filename is the name of the file, contentType is the MIME type (e.g., "image/png"), and data is the raw file content.

type AuthManager

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

AuthManager handles OAuth token management.

func NewAuthManager

func NewAuthManager(cfg *Config, httpClient *http.Client) *AuthManager

NewAuthManager creates a new auth manager.

func NewAuthManagerWithStore

func NewAuthManagerWithStore(cfg *Config, httpClient *http.Client, store *CredentialStore) *AuthManager

NewAuthManagerWithStore creates an auth manager with a custom credential store.

func (*AuthManager) AccessToken

func (m *AuthManager) AccessToken(ctx context.Context) (string, error)

AccessToken returns a valid access token, refreshing if needed. If BASECAMP_TOKEN env var is set, it's used directly without OAuth.

func (*AuthManager) GetUserID

func (m *AuthManager) GetUserID() string

GetUserID returns the stored user ID.

func (*AuthManager) IsAuthenticated

func (m *AuthManager) IsAuthenticated() bool

IsAuthenticated checks if there are valid credentials.

func (*AuthManager) Logout

func (m *AuthManager) Logout() error

Logout removes stored credentials.

func (*AuthManager) Refresh

func (m *AuthManager) Refresh(ctx context.Context) error

Refresh forces a token refresh.

func (*AuthManager) SetUserID

func (m *AuthManager) SetUserID(userID string) error

SetUserID stores the user ID.

func (*AuthManager) Store

func (m *AuthManager) Store() *CredentialStore

Store returns the credential store.

type AuthStrategy

type AuthStrategy interface {
	// Authenticate applies authentication to the given HTTP request.
	Authenticate(ctx context.Context, req *http.Request) error
}

AuthStrategy controls how authentication is applied to HTTP requests. The default strategy is BearerAuth, which uses a TokenProvider to set the Authorization header with a Bearer token.

Custom strategies can implement alternative auth schemes such as cookie-based auth, API keys, or mutual TLS.

type AuthorizationInfo

type AuthorizationInfo struct {
	ExpiresAt FlexTime            `json:"expires_at"`
	Identity  Identity            `json:"identity"`
	Accounts  []AuthorizedAccount `json:"accounts"`
}

AuthorizationInfo contains the complete authorization response.

type AuthorizationService

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

AuthorizationService handles authorization operations.

func NewAuthorizationService

func NewAuthorizationService(client *Client) *AuthorizationService

NewAuthorizationService creates a new AuthorizationService.

func (*AuthorizationService) GetInfo

func (s *AuthorizationService) GetInfo(ctx context.Context, opts *GetInfoOptions) (result *AuthorizationInfo, err error)

GetInfo fetches authorization information for the current access token. This includes the user's identity and list of authorized accounts.

type AuthorizedAccount

type AuthorizedAccount struct {
	ID       int64  `json:"id"`
	Name     string `json:"name"`
	Product  string `json:"product"`
	HREF     string `json:"href"`
	AppHREF  string `json:"app_href"`
	Hidden   bool   `json:"hidden,omitempty"`
	Expired  bool   `json:"expired,omitempty"`
	Featured bool   `json:"featured,omitempty"`
}

AuthorizedAccount represents a Basecamp account the user has access to.

type BearerAuth

type BearerAuth struct {
	TokenProvider TokenProvider
}

BearerAuth implements AuthStrategy using OAuth Bearer tokens. This is the default authentication strategy.

func (*BearerAuth) Authenticate

func (b *BearerAuth) Authenticate(ctx context.Context, req *http.Request) error

Authenticate sets the Authorization header with a Bearer token.

type Boost

type Boost struct {
	ID        int64     `json:"id"`
	Content   string    `json:"content"`
	CreatedAt time.Time `json:"created_at"`
	Booster   *Person   `json:"booster,omitempty"`
	Recording *Parent   `json:"recording,omitempty"`
}

Boost represents a Basecamp boost (emoji reaction) on a recording.

type BoostListResult

type BoostListResult struct {
	// Boosts is the list of boosts returned.
	Boosts []Boost
	// Meta contains pagination metadata (total count, etc.).
	Meta ListMeta
}

BoostListResult contains the results from listing boosts.

type BoostsService

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

BoostsService handles boost operations.

func NewBoostsService

func NewBoostsService(client *AccountClient) *BoostsService

NewBoostsService creates a new BoostsService.

func (*BoostsService) CreateEvent

func (s *BoostsService) CreateEvent(ctx context.Context, recordingID, eventID int64, content string) (result *Boost, err error)

CreateEvent creates a boost on a specific event within a recording. content is the emoji content for the boost. Returns the created boost.

func (*BoostsService) CreateRecording

func (s *BoostsService) CreateRecording(ctx context.Context, recordingID int64, content string) (result *Boost, err error)

CreateRecording creates a boost on a recording. content is the emoji content for the boost. Returns the created boost.

func (*BoostsService) Delete

func (s *BoostsService) Delete(ctx context.Context, boostID int64) (err error)

Delete deletes a boost.

func (*BoostsService) Get

func (s *BoostsService) Get(ctx context.Context, boostID int64) (result *Boost, err error)

Get returns a boost by ID.

func (*BoostsService) ListEvent

func (s *BoostsService) ListEvent(ctx context.Context, recordingID, eventID int64) (result *BoostListResult, err error)

ListEvent returns all boosts on a specific event within a recording.

The returned BoostListResult includes pagination metadata (TotalCount from X-Total-Count header) when available.

func (*BoostsService) ListRecording

func (s *BoostsService) ListRecording(ctx context.Context, recordingID int64) (result *BoostListResult, err error)

ListRecording returns all boosts on a recording.

The returned BoostListResult includes pagination metadata (TotalCount from X-Total-Count header) when available.

type Bucket

type Bucket struct {
	ID   int64  `json:"id"`
	Name string `json:"name"`
	Type string `json:"type"`
}

Bucket represents the project (bucket) containing a todo.

type BulkheadConfig

type BulkheadConfig struct {
	// MaxConcurrent is the maximum number of parallel requests.
	// Default: 10
	MaxConcurrent int

	// MaxWait is the maximum time to wait for a slot.
	// If zero, requests are rejected immediately when the bulkhead is full.
	// Default: 5s
	MaxWait time.Duration
}

BulkheadConfig configures concurrency limiting.

func DefaultBulkheadConfig

func DefaultBulkheadConfig() *BulkheadConfig

DefaultBulkheadConfig returns production-ready defaults.

type Cache

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

Cache provides ETag-based HTTP caching.

func NewCache

func NewCache(dir string) *Cache

NewCache creates a new cache with the given directory.

func (*Cache) Clear

func (c *Cache) Clear() error

Clear removes all cached data.

func (*Cache) GetBody

func (c *Cache) GetBody(key string) []byte

GetBody returns the cached response body for a key, or nil if not found.

func (*Cache) GetETag

func (c *Cache) GetETag(key string) string

GetETag returns the cached ETag for a key, or empty string if not found.

func (*Cache) Invalidate

func (c *Cache) Invalidate(key string) error

Invalidate removes cached data for a specific key.

func (*Cache) Key

func (c *Cache) Key(url, accountID, token string) string

Key generates a cache key for a URL, account, and token. The key includes a token hash to ensure different auth contexts don't share cache.

func (*Cache) Set

func (c *Cache) Set(key string, body []byte, etag string) error

Set stores a response body and ETag for a key.

type Campfire

type Campfire struct {
	ID               int64     `json:"id"`
	Status           string    `json:"status"`
	VisibleToClients bool      `json:"visible_to_clients"`
	CreatedAt        time.Time `json:"created_at"`
	UpdatedAt        time.Time `json:"updated_at"`
	Title            string    `json:"title"`
	InheritsStatus   bool      `json:"inherits_status"`
	Type             string    `json:"type"`
	URL              string    `json:"url"`
	AppURL           string    `json:"app_url"`
	LinesURL         string    `json:"lines_url"`
	FilesURL         string    `json:"files_url,omitempty"`
	Bucket           *Bucket   `json:"bucket,omitempty"`
	Creator          *Person   `json:"creator,omitempty"`
	BoostsCount      int       `json:"boosts_count,omitempty"`
}

Campfire represents a Basecamp Campfire (real-time chat room).

type CampfireLine

type CampfireLine struct {
	ID               int64                    `json:"id"`
	Status           string                   `json:"status"`
	VisibleToClients bool                     `json:"visible_to_clients"`
	CreatedAt        time.Time                `json:"created_at"`
	UpdatedAt        time.Time                `json:"updated_at"`
	Title            string                   `json:"title"`
	InheritsStatus   bool                     `json:"inherits_status"`
	Type             string                   `json:"type"`
	URL              string                   `json:"url"`
	AppURL           string                   `json:"app_url"`
	Content          string                   `json:"content,omitempty"`
	Attachments      []CampfireLineAttachment `json:"attachments,omitempty"`
	Parent           *Parent                  `json:"parent,omitempty"`
	Bucket           *Bucket                  `json:"bucket,omitempty"`
	Creator          *Person                  `json:"creator,omitempty"`
	BoostsCount      int                      `json:"boosts_count,omitempty"`
}

CampfireLine represents a message in a Campfire chat.

type CampfireLineAttachment added in v0.2.3

type CampfireLineAttachment struct {
	Title       string `json:"title,omitempty"`
	URL         string `json:"url,omitempty"`
	Filename    string `json:"filename,omitempty"`
	ContentType string `json:"content_type,omitempty"`
	ByteSize    int64  `json:"byte_size,omitempty"`
	DownloadURL string `json:"download_url,omitempty"`
}

CampfireLineAttachment represents a file attached to an upload line.

type CampfireLineListResult

type CampfireLineListResult struct {
	// Lines is the list of campfire lines returned.
	Lines []CampfireLine
	// Meta contains pagination metadata (total count, etc.).
	Meta ListMeta
}

CampfireLineListResult contains the results from listing campfire lines.

type CampfireListResult

type CampfireListResult struct {
	// Campfires is the list of campfires returned.
	Campfires []Campfire
	// Meta contains pagination metadata (total count, etc.).
	Meta ListMeta
}

CampfireListResult contains the results from listing campfires.

type CampfiresService

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

CampfiresService handles campfire operations.

func NewCampfiresService

func NewCampfiresService(client *AccountClient) *CampfiresService

NewCampfiresService creates a new CampfiresService.

func (*CampfiresService) CreateChatbot

func (s *CampfiresService) CreateChatbot(ctx context.Context, campfireID int64, req *CreateChatbotRequest) (result *Chatbot, err error)

CreateChatbot creates a new chatbot for a campfire. Note: Chatbots are account-wide and can only be managed by administrators. Returns the created chatbot with its lines_url for posting.

func (*CampfiresService) CreateLine

func (s *CampfiresService) CreateLine(ctx context.Context, campfireID int64, content string, opts ...*CreateLineOptions) (result *CampfireLine, err error)

CreateLine creates a new line (message) in a campfire. opts is optional; pass a CreateLineOptions to set content_type (text/html or text/plain). Returns the created line.

Example
package main

import (
	"context"
	"fmt"
	"log"
	"os"

	"github.com/basecamp/basecamp-sdk/go/pkg/basecamp"
)

func main() {
	cfg := basecamp.DefaultConfig()
	token := &basecamp.StaticTokenProvider{Token: os.Getenv("BASECAMP_TOKEN")}
	client := basecamp.NewClient(cfg, token)

	ctx := context.Background()

	campfireID := int64(789012)

	// Post a message to a campfire (chat)
	line, err := client.ForAccount("12345").Campfires().CreateLine(ctx, campfireID, "Hello team!")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Message posted: %s\n", line.Content)
}

func (*CampfiresService) CreateUpload added in v0.2.3

func (s *CampfiresService) CreateUpload(ctx context.Context, campfireID int64, filename, contentType string, data io.Reader) (result *CampfireLine, err error)

CreateUpload uploads a file to a campfire. filename is the name of the file, contentType is the MIME type (e.g., "image/png"), and data is the raw file content. Returns the created upload line.

func (*CampfiresService) DeleteChatbot

func (s *CampfiresService) DeleteChatbot(ctx context.Context, campfireID, chatbotID int64) (err error)

DeleteChatbot deletes a chatbot. Note: Deleting a chatbot removes it from the entire account.

func (*CampfiresService) DeleteLine

func (s *CampfiresService) DeleteLine(ctx context.Context, campfireID, lineID int64) (err error)

DeleteLine deletes a line (message) from a campfire.

func (*CampfiresService) Get

func (s *CampfiresService) Get(ctx context.Context, campfireID int64) (result *Campfire, err error)

Get returns a campfire by ID.

func (*CampfiresService) GetChatbot

func (s *CampfiresService) GetChatbot(ctx context.Context, campfireID, chatbotID int64) (result *Chatbot, err error)

GetChatbot returns a chatbot by ID.

func (*CampfiresService) GetLine

func (s *CampfiresService) GetLine(ctx context.Context, campfireID, lineID int64) (result *CampfireLine, err error)

GetLine returns a single line (message) from a campfire.

func (*CampfiresService) List

func (s *CampfiresService) List(ctx context.Context) (result *CampfireListResult, err error)

List returns all campfires across the account.

The returned CampfireListResult includes pagination metadata (TotalCount from X-Total-Count header) when available.

func (*CampfiresService) ListChatbots

func (s *CampfiresService) ListChatbots(ctx context.Context, campfireID int64) (result []Chatbot, err error)

ListChatbots returns all chatbots for a campfire. Note: Chatbots are account-wide but with basecamp-specific callback URLs.

func (*CampfiresService) ListLines

func (s *CampfiresService) ListLines(ctx context.Context, campfireID int64) (result *CampfireLineListResult, err error)

ListLines returns all lines (messages) in a campfire.

The returned CampfireLineListResult includes pagination metadata (TotalCount from X-Total-Count header) when available.

func (*CampfiresService) ListUploads added in v0.2.3

func (s *CampfiresService) ListUploads(ctx context.Context, campfireID int64) (result *CampfireLineListResult, err error)

ListUploads returns all uploaded files in a campfire.

The returned CampfireLineListResult includes pagination metadata (TotalCount from X-Total-Count header) when available.

func (*CampfiresService) UpdateChatbot

func (s *CampfiresService) UpdateChatbot(ctx context.Context, campfireID, chatbotID int64, req *UpdateChatbotRequest) (result *Chatbot, err error)

UpdateChatbot updates an existing chatbot. Note: Updates to chatbots are account-wide. Returns the updated chatbot.

type Card

type Card struct {
	ID                    int64      `json:"id"`
	Status                string     `json:"status"`
	VisibleToClients      bool       `json:"visible_to_clients"`
	CreatedAt             time.Time  `json:"created_at"`
	UpdatedAt             time.Time  `json:"updated_at"`
	Title                 string     `json:"title"`
	InheritsStatus        bool       `json:"inherits_status"`
	Type                  string     `json:"type"`
	URL                   string     `json:"url"`
	AppURL                string     `json:"app_url"`
	BookmarkURL           string     `json:"bookmark_url"`
	SubscriptionURL       string     `json:"subscription_url,omitempty"`
	Position              int        `json:"position"`
	Content               string     `json:"content,omitempty"`
	Description           string     `json:"description,omitempty"`
	DueOn                 string     `json:"due_on,omitempty"`
	Completed             bool       `json:"completed"`
	CompletedAt           *time.Time `json:"completed_at,omitempty"`
	CommentsCount         int        `json:"comments_count"`
	BoostsCount           int        `json:"boosts_count"`
	CommentsURL           string     `json:"comments_url,omitempty"`
	CommentCount          int        `json:"comment_count"`
	CompletionURL         string     `json:"completion_url,omitempty"`
	Parent                *Parent    `json:"parent,omitempty"`
	Bucket                *Bucket    `json:"bucket,omitempty"`
	Creator               *Person    `json:"creator,omitempty"`
	Completer             *Person    `json:"completer,omitempty"`
	Assignees             []Person   `json:"assignees,omitempty"`
	CompletionSubscribers []Person   `json:"completion_subscribers,omitempty"`
	Steps                 []CardStep `json:"steps,omitempty"`
}

Card represents a card in a card table column.

type CardColumn

type CardColumn struct {
	ID               int64     `json:"id"`
	Status           string    `json:"status"`
	VisibleToClients bool      `json:"visible_to_clients"`
	CreatedAt        time.Time `json:"created_at"`
	UpdatedAt        time.Time `json:"updated_at"`
	Title            string    `json:"title"`
	InheritsStatus   bool      `json:"inherits_status"`
	Type             string    `json:"type"`
	URL              string    `json:"url"`
	AppURL           string    `json:"app_url"`
	BookmarkURL      string    `json:"bookmark_url"`
	Position         int       `json:"position,omitempty"`
	Color            string    `json:"color,omitempty"`
	Description      string    `json:"description,omitempty"`
	CardsCount       int       `json:"cards_count"`
	CommentCount     int       `json:"comment_count"`
	CardsURL         string    `json:"cards_url,omitempty"`
	Parent           *Parent   `json:"parent,omitempty"`
	Bucket           *Bucket   `json:"bucket,omitempty"`
	Creator          *Person   `json:"creator,omitempty"`
	Subscribers      []Person  `json:"subscribers,omitempty"`
}

CardColumn represents a column in a card table.

type CardColumnsService

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

CardColumnsService handles card column operations.

func NewCardColumnsService

func NewCardColumnsService(client *AccountClient) *CardColumnsService

NewCardColumnsService creates a new CardColumnsService.

func (*CardColumnsService) Create

func (s *CardColumnsService) Create(ctx context.Context, cardTableID int64, req *CreateColumnRequest) (result *CardColumn, err error)

Create creates a new column in a card table. Returns the created column.

func (*CardColumnsService) DisableOnHold

func (s *CardColumnsService) DisableOnHold(ctx context.Context, columnID int64) (result *CardColumn, err error)

DisableOnHold removes the on-hold section from a column. Returns the updated column.

func (*CardColumnsService) EnableOnHold

func (s *CardColumnsService) EnableOnHold(ctx context.Context, columnID int64) (result *CardColumn, err error)

EnableOnHold adds an on-hold section to a column. Returns the updated column.

func (*CardColumnsService) Get

func (s *CardColumnsService) Get(ctx context.Context, columnID int64) (result *CardColumn, err error)

Get returns a column by ID.

func (*CardColumnsService) Move

func (s *CardColumnsService) Move(ctx context.Context, cardTableID int64, req *MoveColumnRequest) (err error)

Move moves a column within a card table.

func (*CardColumnsService) SetColor

func (s *CardColumnsService) SetColor(ctx context.Context, columnID int64, color string) (result *CardColumn, err error)

SetColor sets the color of a column. Valid colors: white, red, orange, yellow, green, blue, aqua, purple, gray, pink, brown. Returns the updated column.

func (*CardColumnsService) Unwatch

func (s *CardColumnsService) Unwatch(ctx context.Context, columnID int64) (err error)

Unwatch unsubscribes the current user from the column. Returns nil on success (204 No Content).

func (*CardColumnsService) Update

func (s *CardColumnsService) Update(ctx context.Context, columnID int64, req *UpdateColumnRequest) (result *CardColumn, err error)

Update updates an existing column. Returns the updated column.

func (*CardColumnsService) Watch

func (s *CardColumnsService) Watch(ctx context.Context, columnID int64) (result *Subscription, err error)

Watch subscribes the current user to the column. Returns the updated subscription information.

type CardListOptions

type CardListOptions struct {
	// Limit is the maximum number of cards to return.
	// If 0 (default), returns all cards. Use a positive value to cap results.
	Limit int

	// Page, if non-zero, disables pagination and returns only the first page.
	// NOTE: The page number itself is not yet honored due to OpenAPI client
	// limitations. Use 0 to paginate through all results up to Limit.
	Page int
}

CardListOptions specifies options for listing cards.

type CardListResult

type CardListResult struct {
	// Cards is the list of cards returned.
	Cards []Card
	// Meta contains pagination metadata (total count, etc.).
	Meta ListMeta
}

CardListResult contains the results from listing cards.

type CardStep

type CardStep struct {
	ID               int64      `json:"id"`
	Status           string     `json:"status"`
	VisibleToClients bool       `json:"visible_to_clients"`
	CreatedAt        time.Time  `json:"created_at"`
	UpdatedAt        time.Time  `json:"updated_at"`
	Title            string     `json:"title"`
	InheritsStatus   bool       `json:"inherits_status"`
	Type             string     `json:"type"`
	URL              string     `json:"url"`
	AppURL           string     `json:"app_url"`
	BookmarkURL      string     `json:"bookmark_url"`
	Position         int        `json:"position"`
	DueOn            string     `json:"due_on,omitempty"`
	Completed        bool       `json:"completed"`
	CompletedAt      *time.Time `json:"completed_at,omitempty"`
	Parent           *Parent    `json:"parent,omitempty"`
	Bucket           *Bucket    `json:"bucket,omitempty"`
	Creator          *Person    `json:"creator,omitempty"`
	Completer        *Person    `json:"completer,omitempty"`
	Assignees        []Person   `json:"assignees,omitempty"`
}

CardStep represents a step (checklist item) on a card.

type CardStepsService

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

CardStepsService handles card step operations.

func NewCardStepsService

func NewCardStepsService(client *AccountClient) *CardStepsService

NewCardStepsService creates a new CardStepsService.

func (*CardStepsService) Complete

func (s *CardStepsService) Complete(ctx context.Context, stepID int64) (result *CardStep, err error)

Complete marks a step as completed. Returns the updated step.

func (*CardStepsService) Create

func (s *CardStepsService) Create(ctx context.Context, cardID int64, req *CreateStepRequest) (result *CardStep, err error)

Create creates a new step on a card. Returns the created step.

func (*CardStepsService) Delete

func (s *CardStepsService) Delete(ctx context.Context, stepID int64) (err error)

Delete deletes a step (moves it to trash).

func (*CardStepsService) Reposition

func (s *CardStepsService) Reposition(ctx context.Context, cardID, stepID int64, position int) (err error)

Reposition changes the position of a step within a card. position is 0-indexed.

func (*CardStepsService) Uncomplete

func (s *CardStepsService) Uncomplete(ctx context.Context, stepID int64) (result *CardStep, err error)

Uncomplete marks a step as incomplete. Returns the updated step.

func (*CardStepsService) Update

func (s *CardStepsService) Update(ctx context.Context, stepID int64, req *UpdateStepRequest) (result *CardStep, err error)

Update updates an existing step. Returns the updated step.

type CardTable

type CardTable struct {
	ID               int64        `json:"id"`
	Status           string       `json:"status"`
	VisibleToClients bool         `json:"visible_to_clients"`
	CreatedAt        time.Time    `json:"created_at"`
	UpdatedAt        time.Time    `json:"updated_at"`
	Title            string       `json:"title"`
	InheritsStatus   bool         `json:"inherits_status"`
	Type             string       `json:"type"`
	URL              string       `json:"url"`
	AppURL           string       `json:"app_url"`
	BookmarkURL      string       `json:"bookmark_url"`
	SubscriptionURL  string       `json:"subscription_url"`
	Bucket           *Bucket      `json:"bucket,omitempty"`
	Creator          *Person      `json:"creator,omitempty"`
	Subscribers      []Person     `json:"subscribers,omitempty"`
	Lists            []CardColumn `json:"lists,omitempty"`
}

CardTable represents a Basecamp card table (kanban board).

type CardTablesService

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

CardTablesService handles card table operations.

func NewCardTablesService

func NewCardTablesService(client *AccountClient) *CardTablesService

NewCardTablesService creates a new CardTablesService.

func (*CardTablesService) Get

func (s *CardTablesService) Get(ctx context.Context, cardTableID int64) (result *CardTable, err error)

Get returns a card table by ID.

type CardsService

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

CardsService handles card operations.

func NewCardsService

func NewCardsService(client *AccountClient) *CardsService

NewCardsService creates a new CardsService.

func (*CardsService) Create

func (s *CardsService) Create(ctx context.Context, columnID int64, req *CreateCardRequest) (result *Card, err error)

Create creates a new card in a column. Returns the created card.

func (*CardsService) Get

func (s *CardsService) Get(ctx context.Context, cardID int64) (result *Card, err error)

Get returns a card by ID.

func (*CardsService) List

func (s *CardsService) List(ctx context.Context, columnID int64, opts *CardListOptions) (result *CardListResult, err error)

List returns all cards in a column.

By default, returns all cards (no limit). Use Limit to cap results.

Pagination options:

  • Limit: maximum number of cards to return (0 = all)
  • Page: if non-zero, disables pagination and returns first page only

The returned CardListResult includes pagination metadata (TotalCount from X-Total-Count header) when available.

func (*CardsService) Move

func (s *CardsService) Move(ctx context.Context, cardID, columnID int64) (err error)

Move moves a card to a different column.

func (*CardsService) Trash

func (s *CardsService) Trash(ctx context.Context, cardID int64) (err error)

Trash moves a card to the trash. Trashed cards can be recovered from the trash.

func (*CardsService) Update

func (s *CardsService) Update(ctx context.Context, cardID int64, req *UpdateCardRequest) (result *Card, err error)

Update updates an existing card. Returns the updated card.

type ChainHooks

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

ChainHooks combines multiple Hooks implementations. Start events are called in order, end events are called in reverse order. This allows proper nesting of spans/traces.

func (*ChainHooks) OnOperationEnd

func (c *ChainHooks) OnOperationEnd(ctx context.Context, op OperationInfo, err error, duration time.Duration)

OnOperationEnd calls all hooks in reverse order.

func (*ChainHooks) OnOperationGate

func (c *ChainHooks) OnOperationGate(ctx context.Context, op OperationInfo) (context.Context, error)

OnOperationGate calls the first GatingHooks implementation in the chain. Only ONE gater should exist in a chain (typically resilienceHooks which internally manages circuit breaker, bulkhead, and rate limiter). Returns the context from the gater (which may contain cleanup functions) and any error.

func (*ChainHooks) OnOperationStart

func (c *ChainHooks) OnOperationStart(ctx context.Context, op OperationInfo) context.Context

OnOperationStart calls all hooks in order.

func (*ChainHooks) OnRequestEnd

func (c *ChainHooks) OnRequestEnd(ctx context.Context, info RequestInfo, result RequestResult)

OnRequestEnd calls all hooks in reverse order.

func (*ChainHooks) OnRequestStart

func (c *ChainHooks) OnRequestStart(ctx context.Context, info RequestInfo) context.Context

OnRequestStart calls all hooks in order.

func (*ChainHooks) OnRetry

func (c *ChainHooks) OnRetry(ctx context.Context, info RequestInfo, attempt int, err error)

OnRetry calls all hooks in order.

type Chatbot

type Chatbot struct {
	ID          int64     `json:"id"`
	CreatedAt   time.Time `json:"created_at"`
	UpdatedAt   time.Time `json:"updated_at"`
	ServiceName string    `json:"service_name"`
	CommandURL  string    `json:"command_url,omitempty"`
	URL         string    `json:"url"`
	AppURL      string    `json:"app_url"`
	LinesURL    string    `json:"lines_url"`
}

Chatbot represents a Basecamp chatbot integration.

type CheckinsService

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

CheckinsService handles automatic check-in operations.

func NewCheckinsService

func NewCheckinsService(client *AccountClient) *CheckinsService

NewCheckinsService creates a new CheckinsService.

func (*CheckinsService) CreateAnswer

func (s *CheckinsService) CreateAnswer(ctx context.Context, questionID int64, req *CreateAnswerRequest) (result *QuestionAnswer, err error)

CreateAnswer creates a new answer for a question. Returns the created answer.

func (*CheckinsService) CreateQuestion

func (s *CheckinsService) CreateQuestion(ctx context.Context, questionnaireID int64, req *CreateQuestionRequest) (result *Question, err error)

CreateQuestion creates a new question in a questionnaire. Returns the created question.

func (*CheckinsService) GetAnswer

func (s *CheckinsService) GetAnswer(ctx context.Context, answerID int64) (result *QuestionAnswer, err error)

GetAnswer returns a question answer by ID.

func (*CheckinsService) GetQuestion

func (s *CheckinsService) GetQuestion(ctx context.Context, questionID int64) (result *Question, err error)

GetQuestion returns a question by ID.

func (*CheckinsService) GetQuestionnaire

func (s *CheckinsService) GetQuestionnaire(ctx context.Context, questionnaireID int64) (result *Questionnaire, err error)

GetQuestionnaire returns a questionnaire by ID.

func (*CheckinsService) ListAnswers

func (s *CheckinsService) ListAnswers(ctx context.Context, questionID int64, opts *AnswerListOptions) (result *AnswerListResult, err error)

ListAnswers returns all answers for a question.

By default, returns all answers (no limit). Use Limit to cap results.

Pagination options:

  • Limit: maximum number of answers to return (0 = all)
  • Page: if non-zero, disables pagination and returns first page only

The returned AnswerListResult includes pagination metadata (TotalCount from X-Total-Count header) when available.

func (*CheckinsService) ListQuestions

func (s *CheckinsService) ListQuestions(ctx context.Context, questionnaireID int64, opts *QuestionListOptions) (result *QuestionListResult, err error)

ListQuestions returns all questions in a questionnaire.

By default, returns all questions (no limit). Use Limit to cap results.

Pagination options:

  • Limit: maximum number of questions to return (0 = all)
  • Page: if non-zero, disables pagination and returns first page only

The returned QuestionListResult includes pagination metadata (TotalCount from X-Total-Count header) when available.

func (*CheckinsService) UpdateAnswer

func (s *CheckinsService) UpdateAnswer(ctx context.Context, answerID int64, req *UpdateAnswerRequest) (err error)

UpdateAnswer updates an existing question answer. Returns nil on success (204 No Content).

func (*CheckinsService) UpdateQuestion

func (s *CheckinsService) UpdateQuestion(ctx context.Context, questionID int64, req *UpdateQuestionRequest) (result *Question, err error)

UpdateQuestion updates an existing question. Returns the updated question.

type CircuitBreakerConfig

type CircuitBreakerConfig struct {
	// FailureThreshold is the number of failures before the circuit opens.
	// Default: 5
	FailureThreshold int

	// SuccessThreshold is the number of successes to close from half-open.
	// Default: 2
	SuccessThreshold int

	// OpenTimeout is the time before transitioning from open to half-open.
	// Default: 30s
	OpenTimeout time.Duration

	// FailureRateThreshold is the percentage failure rate to trigger opening.
	// Only evaluated when SlidingWindowSize requests have been made.
	// Default: 50 (meaning 50%)
	FailureRateThreshold float64

	// SlidingWindowSize is the number of requests to consider for rate calculation.
	// Default: 10
	SlidingWindowSize int

	// Now is a function that returns the current time. Used for testing.
	// If nil, time.Now is used.
	Now func() time.Time
}

CircuitBreakerConfig configures the circuit breaker.

func DefaultCircuitBreakerConfig

func DefaultCircuitBreakerConfig() *CircuitBreakerConfig

DefaultCircuitBreakerConfig returns production-ready defaults.

type Client

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

Client is an HTTP client for the Basecamp API. Client holds shared resources and is used to create AccountClient instances for specific Basecamp accounts via the ForAccount method.

Client is safe for concurrent use after construction. Do not modify the Config after the client is in use by multiple goroutines.

func NewClient

func NewClient(cfg *Config, tokenProvider TokenProvider, opts ...ClientOption) *Client

NewClient creates a new API client with spec-driven defaults.

The client automatically:

  • Retries failed GET requests with exponential backoff
  • Does NOT retry POST/PUT/DELETE on 429/5xx (to avoid duplicating data)
  • Retries mutations once after successful 401 token refresh
  • Respects Retry-After headers on 429 responses
  • Follows pagination via Link headers

Configuration options:

  • WithTimeout(d) - Request timeout (default: 30s)
  • WithMaxRetries(n) - Max retry attempts for GET (default: 3)
  • WithCache(c) - Enable ETag-based caching
  • WithTransport(t) - Custom http.RoundTripper
  • WithLogger(l) - slog.Logger for debug output
Example (Oauth)
package main

import (
	"fmt"
	"net/http"

	"github.com/basecamp/basecamp-sdk/go/pkg/basecamp"
)

func main() {
	// Create a client with OAuth authentication
	cfg := basecamp.DefaultConfig()

	authMgr := basecamp.NewAuthManager(cfg, http.DefaultClient)
	client := basecamp.NewClient(cfg, authMgr)

	// Use the client to make API calls
	_ = client
	fmt.Println("Client created with OAuth")
}
Output:
Client created with OAuth
Example (Options)
package main

import (
	"fmt"
	"log/slog"
	"os"

	"github.com/basecamp/basecamp-sdk/go/pkg/basecamp"
)

func main() {
	// Create a client with custom options
	cfg := basecamp.DefaultConfig()

	token := &basecamp.StaticTokenProvider{Token: os.Getenv("BASECAMP_TOKEN")}
	client := basecamp.NewClient(cfg, token,
		basecamp.WithUserAgent("my-app/1.0"),
		basecamp.WithLogger(slog.Default()),
	)

	_ = client
	fmt.Println("Client created with options")
}
Output:
Client created with options
Example (StaticToken)
package main

import (
	"fmt"
	"os"

	"github.com/basecamp/basecamp-sdk/go/pkg/basecamp"
)

func main() {
	// Create a client with a static token (simplest authentication method)
	cfg := basecamp.DefaultConfig()

	token := &basecamp.StaticTokenProvider{Token: os.Getenv("BASECAMP_TOKEN")}
	client := basecamp.NewClient(cfg, token)

	// Use the client to make API calls
	_ = client
	fmt.Println("Client created with static token")
}
Output:
Client created with static token

func (*Client) Authorization

func (c *Client) Authorization() *AuthorizationService

Authorization returns the AuthorizationService for authorization operations. This is the only service available directly on Client, as it doesn't require an account context. All other services require an AccountClient via ForAccount.

func (*Client) Config

func (c *Client) Config() Config

Config returns a copy of the client configuration.

Modifying the returned Config has no effect on the client. This prevents race conditions from post-construction modification.

func (*Client) Delete

func (c *Client) Delete(ctx context.Context, path string) (*Response, error)

Delete performs a DELETE request.

func (*Client) FollowPagination

func (c *Client) FollowPagination(ctx context.Context, httpResp *http.Response, firstPageCount, limit int) ([]json.RawMessage, error)

FollowPagination fetches additional pages following Link headers from an HTTP response. This is used after calling the generated client for the first page. The httpResp should be from the generated client's *WithResponse method. firstPageCount is the number of items already collected from the first page. limit is the maximum total items to return (0 = unlimited). Returns raw JSON items from subsequent pages only (first page items are handled by caller).

Request URL requirement: httpResp.Request.URL is required for same-origin validation. If the response has no Request (e.g., manually constructed), pagination returns an error even for absolute Link headers. This fail-closed behavior prevents SSRF and token leakage when the original request origin cannot be verified.

Security: Link headers are resolved against the current page URL and validated for same-origin against the original request to prevent SSRF and token leakage. FollowPagination is the public API — returns items and error only.

func (*Client) ForAccount

func (c *Client) ForAccount(accountID string) *AccountClient

ForAccount returns an AccountClient bound to the specified Basecamp account. The AccountClient shares the parent Client's HTTP transport, token provider, and other resources, but is configured to make API calls for the given account.

The accountID must be a numeric string (e.g., "12345"). ForAccount panics if the accountID is empty or contains non-digit characters.

Example:

client := basecamp.NewClient(cfg, tokenProvider)
account := client.ForAccount("12345")
projects, err := account.Projects().List(ctx, nil)

func (*Client) Get

func (c *Client) Get(ctx context.Context, path string) (*Response, error)

Get performs a GET request.

func (*Client) GetAll

func (c *Client) GetAll(ctx context.Context, path string) ([]json.RawMessage, error)

GetAll fetches all pages for a paginated resource.

func (*Client) GetAllWithLimit

func (c *Client) GetAllWithLimit(ctx context.Context, path string, limit int) ([]json.RawMessage, error)

GetAllWithLimit fetches pages for a paginated resource up to a limit. If limit is 0, it fetches all pages (same as GetAll). If limit > 0, it stops after collecting at least limit items.

func (*Client) Post

func (c *Client) Post(ctx context.Context, path string, body any) (*Response, error)

Post performs a POST request with a JSON body.

func (*Client) Put

func (c *Client) Put(ctx context.Context, path string, body any) (*Response, error)

Put performs a PUT request with a JSON body.

type ClientApproval

type ClientApproval struct {
	ID               int64                    `json:"id"`
	Status           string                   `json:"status"`
	VisibleToClients bool                     `json:"visible_to_clients"`
	CreatedAt        time.Time                `json:"created_at"`
	UpdatedAt        time.Time                `json:"updated_at"`
	Title            string                   `json:"title"`
	InheritsStatus   bool                     `json:"inherits_status"`
	Type             string                   `json:"type"`
	URL              string                   `json:"url"`
	AppURL           string                   `json:"app_url"`
	BookmarkURL      string                   `json:"bookmark_url"`
	SubscriptionURL  string                   `json:"subscription_url"`
	Parent           *Parent                  `json:"parent,omitempty"`
	Bucket           *Bucket                  `json:"bucket,omitempty"`
	Creator          *Person                  `json:"creator,omitempty"`
	Content          string                   `json:"content"`
	Subject          string                   `json:"subject"`
	DueOn            *string                  `json:"due_on,omitempty"`
	RepliesCount     int                      `json:"replies_count"`
	RepliesURL       string                   `json:"replies_url"`
	ApprovalStatus   string                   `json:"approval_status"`
	Approver         *Person                  `json:"approver,omitempty"`
	Responses        []ClientApprovalResponse `json:"responses,omitempty"`
}

ClientApproval represents a Basecamp client approval request.

type ClientApprovalListResult

type ClientApprovalListResult struct {
	// Approvals is the list of client approvals returned.
	Approvals []ClientApproval
	// Meta contains pagination metadata (total count, etc.).
	Meta ListMeta
}

ClientApprovalListResult contains the results from listing client approvals.

type ClientApprovalResponse

type ClientApprovalResponse struct {
	ID               int64     `json:"id"`
	Status           string    `json:"status"`
	VisibleToClients bool      `json:"visible_to_clients"`
	CreatedAt        time.Time `json:"created_at"`
	UpdatedAt        time.Time `json:"updated_at"`
	Title            string    `json:"title"`
	InheritsStatus   bool      `json:"inherits_status"`
	Type             string    `json:"type"`
	AppURL           string    `json:"app_url"`
	BookmarkURL      string    `json:"bookmark_url"`
	Parent           *Parent   `json:"parent,omitempty"`
	Bucket           *Bucket   `json:"bucket,omitempty"`
	Creator          *Person   `json:"creator,omitempty"`
	Content          string    `json:"content"`
	Approved         bool      `json:"approved"`
}

ClientApprovalResponse represents a response to a client approval.

type ClientApprovalsService

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

ClientApprovalsService handles client approval operations.

func NewClientApprovalsService

func NewClientApprovalsService(client *AccountClient) *ClientApprovalsService

NewClientApprovalsService creates a new ClientApprovalsService.

func (*ClientApprovalsService) Get

func (s *ClientApprovalsService) Get(ctx context.Context, approvalID int64) (result *ClientApproval, err error)

Get returns a client approval by ID.

func (*ClientApprovalsService) List

List returns all client approvals in a project.

The returned ClientApprovalListResult includes pagination metadata (TotalCount from X-Total-Count header) when available.

type ClientCompany

type ClientCompany struct {
	ID   int64  `json:"id"`
	Name string `json:"name"`
}

ClientCompany represents a client company associated with a project.

type ClientCorrespondence

type ClientCorrespondence struct {
	ID               int64     `json:"id"`
	Status           string    `json:"status"`
	VisibleToClients bool      `json:"visible_to_clients"`
	CreatedAt        time.Time `json:"created_at"`
	UpdatedAt        time.Time `json:"updated_at"`
	Title            string    `json:"title"`
	InheritsStatus   bool      `json:"inherits_status"`
	Type             string    `json:"type"`
	URL              string    `json:"url"`
	AppURL           string    `json:"app_url"`
	BookmarkURL      string    `json:"bookmark_url"`
	SubscriptionURL  string    `json:"subscription_url"`
	Parent           *Parent   `json:"parent,omitempty"`
	Bucket           *Bucket   `json:"bucket,omitempty"`
	Creator          *Person   `json:"creator,omitempty"`
	Content          string    `json:"content"`
	Subject          string    `json:"subject"`
	RepliesCount     int       `json:"replies_count"`
	RepliesURL       string    `json:"replies_url"`
}

ClientCorrespondence represents a Basecamp client correspondence (message to clients).

type ClientCorrespondenceListResult

type ClientCorrespondenceListResult struct {
	// Correspondences is the list of client correspondences returned.
	Correspondences []ClientCorrespondence
	// Meta contains pagination metadata (total count, etc.).
	Meta ListMeta
}

ClientCorrespondenceListResult contains the results from listing client correspondences.

type ClientCorrespondencesService

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

ClientCorrespondencesService handles client correspondence operations.

func NewClientCorrespondencesService

func NewClientCorrespondencesService(client *AccountClient) *ClientCorrespondencesService

NewClientCorrespondencesService creates a new ClientCorrespondencesService.

func (*ClientCorrespondencesService) Get

func (s *ClientCorrespondencesService) Get(ctx context.Context, correspondenceID int64) (result *ClientCorrespondence, err error)

Get returns a client correspondence by ID.

func (*ClientCorrespondencesService) List

List returns all client correspondences in a project.

The returned ClientCorrespondenceListResult includes pagination metadata (TotalCount from X-Total-Count header) when available.

type ClientOption

type ClientOption func(*Client)

ClientOption configures a Client.

func WithAuthStrategy

func WithAuthStrategy(strategy AuthStrategy) ClientOption

WithAuthStrategy sets a custom authentication strategy. The default strategy is BearerAuth, which sets the Authorization header with a Bearer token from the token provider.

Use this to implement alternative auth schemes such as cookie-based auth, API keys, or mutual TLS.

func WithBaseDelay

func WithBaseDelay(d time.Duration) ClientOption

WithBaseDelay sets the initial backoff delay.

func WithBulkhead

func WithBulkhead(cfg *BulkheadConfig) ClientOption

WithBulkhead enables only the bulkhead (concurrency limiter).

Example:

client := basecamp.NewClient(cfg, tokenProvider,
    basecamp.WithBulkhead(&basecamp.BulkheadConfig{
        MaxConcurrent: 5,
        MaxWait:       10 * time.Second,
    }),
)

func WithCache

func WithCache(cache *Cache) ClientOption

WithCache sets a custom cache.

func WithCircuitBreaker

func WithCircuitBreaker(cfg *CircuitBreakerConfig) ClientOption

WithCircuitBreaker enables only the circuit breaker.

Example:

client := basecamp.NewClient(cfg, tokenProvider,
    basecamp.WithCircuitBreaker(&basecamp.CircuitBreakerConfig{
        FailureThreshold: 10,
    }),
)

func WithHTTPClient

func WithHTTPClient(c *http.Client) ClientOption

WithHTTPClient sets a custom HTTP client.

func WithHooks

func WithHooks(hooks Hooks) ClientOption

WithHooks sets the observability hooks for the client. Pass nil to disable hooks (uses NoopHooks).

func WithLogger

func WithLogger(l *slog.Logger) ClientOption

WithLogger sets a custom slog logger for debug output. By default, the client uses a no-op logger (silent). Passing nil is safe and will use the default no-op logger.

func WithMaxJitter

func WithMaxJitter(d time.Duration) ClientOption

WithMaxJitter sets the maximum random jitter to add to delays.

func WithMaxPages

func WithMaxPages(n int) ClientOption

WithMaxPages sets the maximum pages to fetch in GetAll.

func WithMaxRetries

func WithMaxRetries(n int) ClientOption

WithMaxRetries sets the maximum number of retry attempts for GET requests.

func WithRateLimit

func WithRateLimit(cfg *RateLimitConfig) ClientOption

WithRateLimit enables only client-side rate limiting.

Example:

client := basecamp.NewClient(cfg, tokenProvider,
    basecamp.WithRateLimit(&basecamp.RateLimitConfig{
        RequestsPerSecond: 10,
        BurstSize:         5,
    }),
)

func WithResilience

func WithResilience(cfg *ResilienceConfig) ClientOption

WithResilience enables circuit breaker, bulkhead, and rate limiting. Pass nil to use DefaultResilienceConfig().

Example:

client := basecamp.NewClient(cfg, tokenProvider,
    basecamp.WithResilience(nil), // Uses defaults
)

Or with custom config:

client := basecamp.NewClient(cfg, tokenProvider,
    basecamp.WithResilience(&basecamp.ResilienceConfig{
        CircuitBreaker: &basecamp.CircuitBreakerConfig{
            FailureThreshold: 3,
            OpenTimeout:      10 * time.Second,
        },
        Bulkhead: &basecamp.BulkheadConfig{
            MaxConcurrent: 5,
        },
        RateLimit: &basecamp.RateLimitConfig{
            RequestsPerSecond: 10,
        },
    }),
)

func WithTimeout

func WithTimeout(d time.Duration) ClientOption

WithTimeout sets the HTTP request timeout.

func WithTransport

func WithTransport(t http.RoundTripper) ClientOption

WithTransport sets a custom HTTP transport.

func WithUserAgent

func WithUserAgent(ua string) ClientOption

WithUserAgent sets the User-Agent header.

type ClientRepliesService

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

ClientRepliesService handles client reply operations.

func NewClientRepliesService

func NewClientRepliesService(client *AccountClient) *ClientRepliesService

NewClientRepliesService creates a new ClientRepliesService.

func (*ClientRepliesService) Get

func (s *ClientRepliesService) Get(ctx context.Context, recordingID, replyID int64) (result *ClientReply, err error)

Get returns a specific client reply.

func (*ClientRepliesService) List

func (s *ClientRepliesService) List(ctx context.Context, recordingID int64) (result *ClientReplyListResult, err error)

List returns all replies for a client recording (correspondence or approval).

The returned ClientReplyListResult includes pagination metadata (TotalCount from X-Total-Count header) when available.

type ClientReply

type ClientReply struct {
	ID               int64     `json:"id"`
	Status           string    `json:"status"`
	VisibleToClients bool      `json:"visible_to_clients"`
	CreatedAt        time.Time `json:"created_at"`
	UpdatedAt        time.Time `json:"updated_at"`
	Title            string    `json:"title"`
	InheritsStatus   bool      `json:"inherits_status"`
	Type             string    `json:"type"`
	URL              string    `json:"url"`
	AppURL           string    `json:"app_url"`
	BookmarkURL      string    `json:"bookmark_url"`
	Parent           *Parent   `json:"parent,omitempty"`
	Bucket           *Bucket   `json:"bucket,omitempty"`
	Creator          *Person   `json:"creator,omitempty"`
	Content          string    `json:"content"`
}

ClientReply represents a reply to a client correspondence or approval.

type ClientReplyListResult

type ClientReplyListResult struct {
	// Replies is the list of client replies returned.
	Replies []ClientReply
	// Meta contains pagination metadata (total count, etc.).
	Meta ListMeta
}

ClientReplyListResult contains the results from listing client replies.

type Clientside

type Clientside struct {
	URL    string `json:"url"`
	AppURL string `json:"app_url"`
}

Clientside represents the client-facing portion of a project.

type Comment

type Comment struct {
	ID        int64     `json:"id"`
	Status    string    `json:"status"`
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
	Content   string    `json:"content"`
	Type      string    `json:"type"`
	URL       string    `json:"url"`
	AppURL    string    `json:"app_url"`
	Parent    *Parent   `json:"parent,omitempty"`
	Bucket    *Bucket   `json:"bucket,omitempty"`
	Creator   *Person   `json:"creator,omitempty"`
}

Comment represents a Basecamp comment on a recording.

type CommentListOptions

type CommentListOptions struct {
	// Limit is the maximum number of comments to return.
	// If 0, uses DefaultCommentLimit (100). Use -1 for unlimited.
	Limit int

	// Page, if non-zero, disables pagination and returns only the first page.
	// NOTE: The page number itself is not yet honored due to OpenAPI client
	// limitations. Use 0 to paginate through all results up to Limit.
	Page int
}

CommentListOptions specifies options for listing comments.

type CommentListResult

type CommentListResult struct {
	// Comments is the list of comments returned.
	Comments []Comment
	// Meta contains pagination metadata (total count, etc.).
	Meta ListMeta
}

CommentListResult contains the results from listing comments.

type CommentsService

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

CommentsService handles comment operations.

func NewCommentsService

func NewCommentsService(client *AccountClient) *CommentsService

NewCommentsService creates a new CommentsService.

func (*CommentsService) Create

func (s *CommentsService) Create(ctx context.Context, recordingID int64, req *CreateCommentRequest) (result *Comment, err error)

Create creates a new comment on a recording. Returns the created comment.

Example
package main

import (
	"context"
	"fmt"
	"log"
	"os"

	"github.com/basecamp/basecamp-sdk/go/pkg/basecamp"
)

func main() {
	cfg := basecamp.DefaultConfig()
	token := &basecamp.StaticTokenProvider{Token: os.Getenv("BASECAMP_TOKEN")}
	client := basecamp.NewClient(cfg, token)

	ctx := context.Background()

	recordingID := int64(789012) // Can be a todo, message, etc.

	// Add a comment to any recording
	comment, err := client.ForAccount("12345").Comments().Create(ctx, recordingID, &basecamp.CreateCommentRequest{
		Content: "<p>Looks good to me!</p>",
	})
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Comment added by %s\n", comment.Creator.Name)
}

func (*CommentsService) Get

func (s *CommentsService) Get(ctx context.Context, commentID int64) (result *Comment, err error)

Get returns a comment by ID.

func (*CommentsService) List

func (s *CommentsService) List(ctx context.Context, recordingID int64, opts *CommentListOptions) (result *CommentListResult, err error)

List returns comments on a recording.

By default, returns up to 100 comments. Use Limit: -1 for unlimited.

Pagination options:

  • Limit: maximum number of comments to return (0 = 100, -1 = unlimited)
  • Page: if non-zero, disables pagination and returns first page only

The returned CommentListResult includes pagination metadata (TotalCount from X-Total-Count header) when available.

func (*CommentsService) Trash

func (s *CommentsService) Trash(ctx context.Context, commentID int64) (err error)

Trash moves a comment to the trash. Trashed comments can be recovered from the trash.

func (*CommentsService) Update

func (s *CommentsService) Update(ctx context.Context, commentID int64, req *UpdateCommentRequest) (result *Comment, err error)

Update updates an existing comment. Returns the updated comment.

type Config

type Config struct {
	// BaseURL is the API base URL (e.g., "https://3.basecampapi.com").
	BaseURL string `json:"base_url"`

	// ProjectID is the default project/bucket ID.
	ProjectID string `json:"project_id"`

	// TodolistID is the default todolist ID.
	TodolistID string `json:"todolist_id"`

	// CacheDir is the directory for HTTP cache storage.
	CacheDir string `json:"cache_dir"`

	// CacheEnabled controls whether HTTP caching is enabled.
	CacheEnabled bool `json:"cache_enabled"`
}

Config holds the resolved configuration for API access.

func DefaultConfig

func DefaultConfig() *Config

DefaultConfig returns a Config with sensible defaults.

Example
package main

import (
	"fmt"

	"github.com/basecamp/basecamp-sdk/go/pkg/basecamp"
)

func main() {
	// Create a default configuration
	cfg := basecamp.DefaultConfig()

	// Override with environment variables
	cfg.LoadConfigFromEnv()

	// Or set values programmatically
	cfg.ProjectID = "67890"
	cfg.CacheEnabled = true

	fmt.Println("Configuration ready")
}
Output:
Configuration ready

func LoadConfig

func LoadConfig(path string) (*Config, error)

LoadConfig loads configuration from a JSON file.

func (*Config) LoadConfigFromEnv

func (c *Config) LoadConfigFromEnv()

LoadConfigFromEnv loads configuration from environment variables. Environment variables override any values already set in the config.

type CreateAnswerRequest

type CreateAnswerRequest struct {
	// Content is the answer content in HTML (required).
	Content string `json:"content"`
	// GroupOn is the date to group the answer with (optional, ISO 8601 format).
	GroupOn string `json:"group_on,omitempty"`
}

CreateAnswerRequest specifies the parameters for creating an answer.

type CreateCampfireLineRequest

type CreateCampfireLineRequest struct {
	// Content is the message body (required).
	Content string `json:"content"`
	// ContentType is "text/plain" or "text/html". If empty, the API defaults to plain text.
	ContentType string `json:"content_type,omitempty"`
}

CreateCampfireLineRequest specifies the parameters for creating a campfire line.

type CreateCardRequest

type CreateCardRequest struct {
	// Title is the card title (required).
	Title string `json:"title"`
	// Content is the card body in HTML (optional).
	Content string `json:"content,omitempty"`
	// DueOn is the due date in ISO 8601 format (optional).
	DueOn string `json:"due_on,omitempty"`
	// Notify when true, will notify assignees (optional).
	Notify bool `json:"notify,omitempty"`
}

CreateCardRequest specifies the parameters for creating a card.

type CreateChatbotRequest

type CreateChatbotRequest struct {
	// ServiceName is the chatbot name used to invoke queries and commands (required).
	// No spaces, emoji or non-word characters are allowed.
	ServiceName string `json:"service_name"`
	// CommandURL is the HTTPS URL that Basecamp should call when the bot is addressed (optional).
	CommandURL string `json:"command_url,omitempty"`
}

CreateChatbotRequest specifies the parameters for creating a chatbot.

type CreateColumnRequest

type CreateColumnRequest struct {
	// Title is the column title (required).
	Title string `json:"title"`
	// Description is the column description (optional).
	Description string `json:"description,omitempty"`
}

CreateColumnRequest specifies the parameters for creating a column.

type CreateCommentRequest

type CreateCommentRequest struct {
	// Content is the comment text in HTML (required).
	Content string `json:"content"`
}

CreateCommentRequest specifies the parameters for creating a comment.

type CreateDocumentRequest

type CreateDocumentRequest struct {
	// Title is the document title (required).
	Title string `json:"title"`
	// Content is the document body in HTML (optional).
	Content string `json:"content,omitempty"`
	// Status is either "drafted" or "active" (optional, defaults to active).
	Status string `json:"status,omitempty"`
	// Subscriptions controls who gets notified and subscribed.
	// nil: field omitted (server default). &[]int64{}: subscribe nobody. &[]int64{1,2}: those people.
	Subscriptions *[]int64 `json:"subscriptions,omitempty"`
}

CreateDocumentRequest specifies the parameters for creating a document.

type CreateForwardReplyRequest

type CreateForwardReplyRequest struct {
	// Content is the reply body in HTML (required).
	Content string `json:"content"`
}

CreateForwardReplyRequest specifies the parameters for creating a reply to a forward.

type CreateLineOptions

type CreateLineOptions struct {
	// ContentType is "text/plain" or "text/html". If empty, the API defaults to plain text.
	ContentType string
}

CreateLineOptions specifies optional parameters for creating a campfire line.

type CreateMarkerRequest

type CreateMarkerRequest struct {
	// Name is the marker name (required).
	Name string `json:"name"`
	// Date is the marker date in YYYY-MM-DD format (required).
	Date string `json:"date"`
}

CreateMarkerRequest specifies the parameters for creating a lineup marker.

type CreateMessageRequest

type CreateMessageRequest struct {
	// Subject is the message title (required).
	Subject string `json:"subject"`
	// Content is the message body in HTML (optional).
	Content string `json:"content,omitempty"`
	// Status is either "drafted" or "active" (optional, defaults to active).
	Status string `json:"status,omitempty"`
	// CategoryID is the message type ID (optional).
	CategoryID int64 `json:"category_id,omitempty"`
	// Subscriptions controls who gets notified and subscribed.
	// nil: field omitted (server default). &[]int64{}: subscribe nobody. &[]int64{1,2}: those people.
	Subscriptions *[]int64 `json:"subscriptions,omitempty"`
}

CreateMessageRequest specifies the parameters for creating a message.

type CreateMessageTypeRequest

type CreateMessageTypeRequest struct {
	// Name is the message type name (required).
	Name string `json:"name"`
	// Icon is the message type icon (required).
	Icon string `json:"icon"`
}

CreateMessageTypeRequest specifies the parameters for creating a message type.

type CreatePersonRequest

type CreatePersonRequest struct {
	// Name is the person's full name (required).
	Name string `json:"name"`
	// EmailAddress is the person's email address (required).
	EmailAddress string `json:"email_address"`
	// Title is the person's job title (optional).
	Title string `json:"title,omitempty"`
	// CompanyName is the person's company name (optional).
	CompanyName string `json:"company_name,omitempty"`
}

CreatePersonRequest specifies the parameters for creating a new person.

type CreateProjectFromTemplateRequest

type CreateProjectFromTemplateRequest struct {
	// Name is the project name (required).
	Name string `json:"name"`
	// Description is an optional project description.
	Description string `json:"description,omitempty"`
}

CreateProjectFromTemplateRequest specifies the parameters for creating a project from a template.

type CreateProjectRequest

type CreateProjectRequest struct {
	// Name is the project name (required).
	Name string `json:"name"`
	// Description is an optional project description.
	Description string `json:"description,omitempty"`
}

CreateProjectRequest specifies the parameters for creating a project.

type CreateQuestionRequest

type CreateQuestionRequest struct {
	// Title is the question text (required).
	Title string `json:"title"`
	// Schedule is the question schedule configuration (required).
	Schedule *QuestionSchedule `json:"schedule"`
}

CreateQuestionRequest specifies the parameters for creating a question.

type CreateScheduleEntryRequest

type CreateScheduleEntryRequest struct {
	// Summary is the event title (required).
	Summary string `json:"summary"`
	// StartsAt is the event start time (required, ISO 8601 format).
	StartsAt string `json:"starts_at"`
	// EndsAt is the event end time (required, ISO 8601 format).
	EndsAt string `json:"ends_at"`
	// Description is the event details in HTML (optional).
	Description string `json:"description,omitempty"`
	// ParticipantIDs is a list of people IDs to assign (optional).
	ParticipantIDs []int64 `json:"participant_ids,omitempty"`
	// AllDay indicates if this is an all-day event (optional).
	AllDay bool `json:"all_day,omitempty"`
	// Notify triggers participant notifications when true (optional).
	Notify bool `json:"notify,omitempty"`
	// Subscriptions controls who gets notified and subscribed.
	// nil: field omitted (server default). &[]int64{}: subscribe nobody. &[]int64{1,2}: those people.
	Subscriptions *[]int64 `json:"subscriptions,omitempty"`
}

CreateScheduleEntryRequest specifies the parameters for creating a schedule entry.

type CreateStepRequest

type CreateStepRequest struct {
	// Title is the step title (required).
	Title string `json:"title"`
	// DueOn is the due date in ISO 8601 format (optional).
	DueOn string `json:"due_on,omitempty"`
	// Assignees is a list of person IDs to assign this step to (optional).
	Assignees []int64 `json:"assignees,omitempty"`
}

CreateStepRequest specifies the parameters for creating a step.

type CreateTemplateRequest

type CreateTemplateRequest struct {
	// Name is the template name (required).
	Name string `json:"name"`
	// Description is an optional template description.
	Description string `json:"description,omitempty"`
}

CreateTemplateRequest specifies the parameters for creating a template.

type CreateTimesheetEntryRequest

type CreateTimesheetEntryRequest struct {
	Date        string `json:"date"`
	Hours       string `json:"hours"`
	Description string `json:"description,omitempty"`
	PersonID    int64  `json:"person_id,omitempty"`
}

CreateTimesheetEntryRequest specifies the parameters for creating a timesheet entry.

type CreateTodoRequest

type CreateTodoRequest struct {
	// Content is the todo text (required).
	Content string `json:"content"`
	// Description is an optional extended description (can include HTML).
	Description string `json:"description,omitempty"`
	// AssigneeIDs is a list of person IDs to assign this todo to.
	AssigneeIDs []int64 `json:"assignee_ids,omitempty"`
	// CompletionSubscriberIDs is a list of person IDs to notify on completion.
	CompletionSubscriberIDs []int64 `json:"completion_subscriber_ids,omitempty"`
	// Notify when true, will notify assignees.
	Notify bool `json:"notify,omitempty"`
	// DueOn is the due date in ISO 8601 format (YYYY-MM-DD).
	DueOn string `json:"due_on,omitempty"`
	// StartsOn is the start date in ISO 8601 format (YYYY-MM-DD).
	StartsOn string `json:"starts_on,omitempty"`
}

CreateTodoRequest specifies the parameters for creating a todo.

type CreateTodolistGroupRequest

type CreateTodolistGroupRequest struct {
	// Name is the group name (required).
	Name string `json:"name"`
}

CreateTodolistGroupRequest specifies the parameters for creating a todolist group.

type CreateTodolistRequest

type CreateTodolistRequest struct {
	// Name is the todolist name (required).
	Name string `json:"name"`
	// Description is an optional description (can include HTML).
	Description string `json:"description,omitempty"`
}

CreateTodolistRequest specifies the parameters for creating a todolist.

type CreateUploadRequest

type CreateUploadRequest struct {
	// AttachableSGID is the signed global ID for an uploaded attachment (required).
	// See the Create Attachment endpoint for how to upload files.
	AttachableSGID string `json:"attachable_sgid"`
	// Description is the upload description in HTML (optional).
	Description string `json:"description,omitempty"`
	// BaseName is the filename without extension (optional).
	BaseName string `json:"base_name,omitempty"`
	// Subscriptions controls who gets notified and subscribed.
	// nil: field omitted (server default). &[]int64{}: subscribe nobody. &[]int64{1,2}: those people.
	Subscriptions *[]int64 `json:"subscriptions,omitempty"`
}

CreateUploadRequest specifies the parameters for creating an upload.

type CreateVaultRequest

type CreateVaultRequest struct {
	// Title is the vault name (required).
	Title string `json:"title"`
}

CreateVaultRequest specifies the parameters for creating a vault (folder).

type CreateWebhookRequest

type CreateWebhookRequest struct {
	// PayloadURL is the URL to receive webhook payloads (required).
	PayloadURL string `json:"payload_url"`
	// Types is a list of event types to subscribe to (required).
	// Example: ["Todo", "Todolist", "Comment"]
	Types []string `json:"types"`
	// Active indicates whether the webhook is active (default: true).
	Active *bool `json:"active,omitempty"`
}

CreateWebhookRequest specifies the parameters for creating a webhook.

type CredentialStore

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

CredentialStore handles secure credential storage.

func NewCredentialStore

func NewCredentialStore(fallbackDir string) *CredentialStore

NewCredentialStore creates a credential store. It prefers the system keyring if available, falling back to file storage.

func (*CredentialStore) Delete

func (s *CredentialStore) Delete(origin string) error

Delete removes credentials for the given origin.

func (*CredentialStore) Load

func (s *CredentialStore) Load(origin string) (*Credentials, error)

Load retrieves credentials for the given origin.

func (*CredentialStore) Save

func (s *CredentialStore) Save(origin string, creds *Credentials) error

Save stores credentials for the given origin.

func (*CredentialStore) UsingKeyring

func (s *CredentialStore) UsingKeyring() bool

UsingKeyring returns true if the store is using the system keyring.

type Credentials

type Credentials struct {
	AccessToken   string `json:"access_token"`
	RefreshToken  string `json:"refresh_token"`
	ExpiresAt     int64  `json:"expires_at"`
	Scope         string `json:"scope"`
	TokenEndpoint string `json:"token_endpoint"`
	UserID        string `json:"user_id,omitempty"`
}

Credentials holds OAuth tokens and metadata.

type Date

type Date = types.Date

Date is an alias for types.Date, representing a calendar date without time. Re-exported here for convenience so users can use basecamp.Date.

type DockItem

type DockItem struct {
	ID       int64  `json:"id"`
	Title    string `json:"title"`
	Name     string `json:"name"`
	Enabled  bool   `json:"enabled"`
	Position *int   `json:"position"`
	URL      string `json:"url"`
	AppURL   string `json:"app_url"`
}

DockItem represents a tool in a project's dock.

type Document

type Document struct {
	ID               int64     `json:"id"`
	Status           string    `json:"status"`
	VisibleToClients bool      `json:"visible_to_clients"`
	CreatedAt        time.Time `json:"created_at"`
	UpdatedAt        time.Time `json:"updated_at"`
	Title            string    `json:"title"`
	InheritsStatus   bool      `json:"inherits_status"`
	Type             string    `json:"type"`
	URL              string    `json:"url"`
	AppURL           string    `json:"app_url"`
	BookmarkURL      string    `json:"bookmark_url"`
	SubscriptionURL  string    `json:"subscription_url"`
	CommentsCount    int       `json:"comments_count"`
	BoostsCount      int       `json:"boosts_count,omitempty"`
	CommentsURL      string    `json:"comments_url"`
	Position         int       `json:"position,omitempty"`
	Parent           *Parent   `json:"parent,omitempty"`
	Bucket           *Bucket   `json:"bucket,omitempty"`
	Creator          *Person   `json:"creator,omitempty"`
	Content          string    `json:"content"`
}

Document represents a Basecamp document in a vault.

type DocumentListOptions

type DocumentListOptions struct {
	// Limit is the maximum number of documents to return.
	// If 0 (default), returns all documents. Use a positive value to cap results.
	Limit int

	// Page, if non-zero, disables pagination and returns only the first page.
	// NOTE: The page number itself is not yet honored due to OpenAPI client
	// limitations. Use 0 to paginate through all results up to Limit.
	Page int
}

DocumentListOptions specifies options for listing documents.

type DocumentListResult

type DocumentListResult struct {
	// Documents is the list of documents returned.
	Documents []Document
	// Meta contains pagination metadata (total count, etc.).
	Meta ListMeta
}

DocumentListResult contains the results from listing documents.

type DocumentsService

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

DocumentsService handles document operations.

func NewDocumentsService

func NewDocumentsService(client *AccountClient) *DocumentsService

NewDocumentsService creates a new DocumentsService.

func (*DocumentsService) Create

func (s *DocumentsService) Create(ctx context.Context, vaultID int64, req *CreateDocumentRequest) (result *Document, err error)

Create creates a new document in a vault. Returns the created document.

func (*DocumentsService) Get

func (s *DocumentsService) Get(ctx context.Context, documentID int64) (result *Document, err error)

Get returns a document by ID.

func (*DocumentsService) List

func (s *DocumentsService) List(ctx context.Context, vaultID int64, opts *DocumentListOptions) (result *DocumentListResult, err error)

List returns all documents in a vault.

By default, returns all documents (no limit). Use Limit to cap results.

Pagination options:

  • Limit: maximum number of documents to return (0 = all)
  • Page: if non-zero, disables pagination and returns first page only

The returned DocumentListResult includes pagination metadata (TotalCount from X-Total-Count header) when available.

func (*DocumentsService) Trash

func (s *DocumentsService) Trash(ctx context.Context, documentID int64) (err error)

Trash moves a document to the trash. Trashed documents can be recovered from the trash.

func (*DocumentsService) Update

func (s *DocumentsService) Update(ctx context.Context, documentID int64, req *UpdateDocumentRequest) (result *Document, err error)

Update updates an existing document. Returns the updated document.

type DownloadResult

type DownloadResult struct {
	// Body is the file content. Caller must close this.
	Body io.ReadCloser
	// ContentType is the MIME type of the file.
	ContentType string
	// ContentLength is the size of the file in bytes (-1 if unknown).
	ContentLength int64
	// Filename is the name of the file.
	Filename string
}

DownloadResult contains the result from downloading an upload.

type Error

type Error struct {
	Code       string
	Message    string
	Hint       string
	HTTPStatus int
	Retryable  bool
	RequestID  string
	Cause      error
}

Error is a structured error with code, message, and optional hint.

func AsError

func AsError(err error) *Error

AsError attempts to convert an error to an *Error. If the error is not an *Error, it wraps it in one.

func ErrAPI

func ErrAPI(status int, msg string) *Error

ErrAPI creates an API error with an HTTP status code.

func ErrAmbiguous

func ErrAmbiguous(resource string, matches []string) *Error

ErrAmbiguous creates an ambiguous match error.

func ErrAuth

func ErrAuth(msg string) *Error

ErrAuth creates an authentication error.

func ErrForbidden

func ErrForbidden(msg string) *Error

ErrForbidden creates a forbidden error.

func ErrForbiddenScope

func ErrForbiddenScope() *Error

ErrForbiddenScope creates a forbidden error due to insufficient scope.

func ErrNetwork

func ErrNetwork(cause error) *Error

ErrNetwork creates a network error.

func ErrNotFound

func ErrNotFound(resource, identifier string) *Error

ErrNotFound creates a not-found error.

func ErrNotFoundHint

func ErrNotFoundHint(resource, identifier, hint string) *Error

ErrNotFoundHint creates a not-found error with a hint.

func ErrRateLimit

func ErrRateLimit(retryAfter int) *Error

ErrRateLimit creates a rate-limit error.

func ErrUsage

func ErrUsage(msg string) *Error

ErrUsage creates a usage error.

func ErrUsageHint

func ErrUsageHint(msg, hint string) *Error

ErrUsageHint creates a usage error with a hint.

func (*Error) Error

func (e *Error) Error() string

Error implements the error interface.

func (*Error) ExitCode

func (e *Error) ExitCode() int

ExitCode returns the appropriate exit code for this error.

func (*Error) Unwrap

func (e *Error) Unwrap() error

Unwrap returns the underlying cause for errors.Is/As support.

type Event

type Event struct {
	ID          int64         `json:"id"`
	RecordingID int64         `json:"recording_id"`
	Action      string        `json:"action"`
	Details     *EventDetails `json:"details,omitempty"`
	CreatedAt   time.Time     `json:"created_at"`
	Creator     *Person       `json:"creator,omitempty"`
}

Event represents a recording change event in Basecamp. An event is created any time a recording changes.

type EventDetails

type EventDetails struct {
	// AddedPersonIDs is populated for assignment_changed actions.
	AddedPersonIDs []int64 `json:"added_person_ids,omitempty"`
	// RemovedPersonIDs is populated for assignment_changed actions.
	RemovedPersonIDs []int64 `json:"removed_person_ids,omitempty"`
	// NotifiedRecipientIDs is populated for completion events.
	NotifiedRecipientIDs []int64 `json:"notified_recipient_ids,omitempty"`
}

EventDetails contains action-specific information for an event.

type EventListOptions

type EventListOptions struct {
	// Limit is the maximum number of events to return.
	// If 0, uses DefaultEventLimit (100). Use -1 for unlimited.
	Limit int

	// Page, if non-zero, disables pagination and returns only the first page.
	// NOTE: The page number itself is not yet honored due to OpenAPI client
	// limitations. Use 0 to paginate through all results up to Limit.
	Page int
}

EventListOptions specifies options for listing events.

type EventListResult

type EventListResult struct {
	// Events is the list of events returned.
	Events []Event
	// Meta contains pagination metadata (total count, etc.).
	Meta ListMeta
}

EventListResult contains the results from listing events.

type EventsService

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

EventsService handles event operations.

func NewEventsService

func NewEventsService(client *AccountClient) *EventsService

NewEventsService creates a new EventsService.

func (*EventsService) List

func (s *EventsService) List(ctx context.Context, recordingID int64, opts *EventListOptions) (result *EventListResult, err error)

List returns all events for a recording.

By default, returns up to 100 events. Use Limit: -1 for unlimited.

Pagination options:

  • Limit: maximum number of events to return (0 = 100, -1 = unlimited)
  • Page: if non-zero, disables pagination and returns first page only

The returned EventListResult includes pagination metadata (TotalCount from X-Total-Count header) when available.

type FlexTime

type FlexTime struct {
	time.Time
}

FlexTime is a time.Time that can unmarshal from either a Unix timestamp (integer) or an RFC 3339 string. This supports both BC3 OAuth 2.1 (integer) and Launchpad (string).

func (*FlexTime) UnmarshalJSON

func (ft *FlexTime) UnmarshalJSON(data []byte) error

UnmarshalJSON implements json.Unmarshaler for FlexTime.

type Forward

type Forward struct {
	ID        int64     `json:"id"`
	Status    string    `json:"status"`
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
	Subject   string    `json:"subject"`
	Content   string    `json:"content"`
	From      string    `json:"from"`
	Type      string    `json:"type"`
	URL       string    `json:"url"`
	AppURL    string    `json:"app_url"`
	Parent    *Parent   `json:"parent,omitempty"`
	Bucket    *Bucket   `json:"bucket,omitempty"`
	Creator   *Person   `json:"creator,omitempty"`
}

Forward represents a forwarded email in Basecamp.

type ForwardListOptions

type ForwardListOptions struct {
	// Limit is the maximum number of forwards to return.
	// If 0 (default), returns all forwards. Use a positive value to cap results.
	Limit int

	// Page, if non-zero, disables pagination and returns only the first page.
	// NOTE: The page number itself is not yet honored due to OpenAPI client
	// limitations. Use 0 to paginate through all results up to Limit.
	Page int
}

ForwardListOptions specifies options for listing forwards.

type ForwardListResult

type ForwardListResult struct {
	// Forwards is the list of forwards returned.
	Forwards []Forward
	// Meta contains pagination metadata (total count, etc.).
	Meta ListMeta
}

ForwardListResult contains the results from listing forwards.

type ForwardReply

type ForwardReply struct {
	ID        int64     `json:"id"`
	Status    string    `json:"status"`
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
	Content   string    `json:"content"`
	Type      string    `json:"type"`
	URL       string    `json:"url"`
	AppURL    string    `json:"app_url"`
	Parent    *Parent   `json:"parent,omitempty"`
	Bucket    *Bucket   `json:"bucket,omitempty"`
	Creator   *Person   `json:"creator,omitempty"`
}

ForwardReply represents a reply to a forwarded email.

type ForwardReplyListOptions

type ForwardReplyListOptions struct {
	// Limit is the maximum number of replies to return.
	// If 0 (default), returns all replies. Use a positive value to cap results.
	Limit int

	// Page, if non-zero, disables pagination and returns only the first page.
	// NOTE: The page number itself is not yet honored due to OpenAPI client
	// limitations. Use 0 to paginate through all results up to Limit.
	Page int
}

ForwardReplyListOptions specifies options for listing forward replies.

type ForwardReplyListResult

type ForwardReplyListResult struct {
	// Replies is the list of forward replies returned.
	Replies []ForwardReply
	// Meta contains pagination metadata (total count, etc.).
	Meta ListMeta
}

ForwardReplyListResult contains the results from listing forward replies.

type ForwardsService

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

ForwardsService handles email forward operations.

func NewForwardsService

func NewForwardsService(client *AccountClient) *ForwardsService

NewForwardsService creates a new ForwardsService.

func (*ForwardsService) CreateReply

func (s *ForwardsService) CreateReply(ctx context.Context, forwardID int64, req *CreateForwardReplyRequest) (result *ForwardReply, err error)

CreateReply creates a new reply to a forwarded email. Returns the created reply.

func (*ForwardsService) Get

func (s *ForwardsService) Get(ctx context.Context, forwardID int64) (result *Forward, err error)

Get returns a forward by ID.

func (*ForwardsService) GetInbox

func (s *ForwardsService) GetInbox(ctx context.Context, inboxID int64) (result *Inbox, err error)

GetInbox returns an inbox by ID.

func (*ForwardsService) GetReply

func (s *ForwardsService) GetReply(ctx context.Context, forwardID, replyID int64) (result *ForwardReply, err error)

GetReply returns a forward reply by ID.

func (*ForwardsService) List

func (s *ForwardsService) List(ctx context.Context, inboxID int64, opts *ForwardListOptions) (result *ForwardListResult, err error)

List returns all forwards in an inbox.

By default, returns all forwards (no limit). Use Limit to cap results.

Pagination options:

  • Limit: maximum number of forwards to return (0 = all)
  • Page: if non-zero, disables pagination and returns first page only

The returned ForwardListResult includes pagination metadata (TotalCount from X-Total-Count header) when available.

func (*ForwardsService) ListReplies

func (s *ForwardsService) ListReplies(ctx context.Context, forwardID int64, opts *ForwardReplyListOptions) (result *ForwardReplyListResult, err error)

ListReplies returns all replies to a forward.

By default, returns all replies (no limit). Use Limit to cap results.

Pagination options:

  • Limit: maximum number of replies to return (0 = all)
  • Page: if non-zero, disables pagination and returns first page only

The returned ForwardReplyListResult includes pagination metadata (TotalCount from X-Total-Count header) when available.

type GatingHooks

type GatingHooks interface {
	Hooks
	// OnOperationGate is called before OnOperationStart.
	// Returns a new context (which may contain cleanup functions like bulkhead
	// release) and an error. Return non-nil error to reject the operation.
	// The returned context should be used for the operation and passed to
	// OnOperationEnd for proper cleanup.
	OnOperationGate(ctx context.Context, op OperationInfo) (context.Context, error)
}

GatingHooks extends Hooks with request gating capability. Implementations can reject operations before they execute, enabling patterns like circuit breakers, bulkheads, and rate limiters.

type GetInfoOptions

type GetInfoOptions struct {
	// Endpoint overrides the default authorization endpoint URL.
	// If empty, defaults to "https://launchpad.37signals.com/authorization.json".
	Endpoint string

	// FilterProduct filters accounts to only those matching this product.
	// Common values: "bc3" (Basecamp), "bcx" (Basecamp 2), "hey" (HEY).
	// If empty, all accounts are returned.
	FilterProduct string
}

GetInfoOptions specifies options for fetching authorization info.

type HTTPOptions

type HTTPOptions struct {
	// Timeout is the request timeout (default: 30s).
	Timeout time.Duration

	// MaxRetries is the maximum retry attempts for GET requests (default: 3).
	// POST/PUT/DELETE requests only get 1 retry after successful token refresh.
	MaxRetries int

	// BaseDelay is the initial backoff delay (default: 1s).
	BaseDelay time.Duration

	// MaxJitter is the maximum random jitter to add to delays (default: 100ms).
	MaxJitter time.Duration

	// MaxPages is the maximum pages to fetch in GetAll (default: 10000).
	MaxPages int

	// Transport is the HTTP transport to use. If nil, a default transport
	// with sensible connection pooling is created.
	Transport http.RoundTripper
}

HTTPOptions configures the HTTP client behavior.

func DefaultHTTPOptions

func DefaultHTTPOptions() HTTPOptions

DefaultHTTPOptions returns HTTPOptions with sensible defaults.

type Hooks

type Hooks interface {
	// OnOperationStart is called when a semantic SDK operation begins.
	// Returns a context that will be passed to OnOperationEnd.
	OnOperationStart(ctx context.Context, op OperationInfo) context.Context

	// OnOperationEnd is called when a semantic SDK operation completes.
	// The ctx is the one returned from OnOperationStart.
	OnOperationEnd(ctx context.Context, op OperationInfo, err error, duration time.Duration)

	// OnRequestStart is called before an HTTP request is sent.
	// The returned context is used for the request and passed to OnRequestEnd.
	OnRequestStart(ctx context.Context, info RequestInfo) context.Context

	// OnRequestEnd is called after an HTTP request completes.
	// It receives the same context returned by OnRequestStart.
	OnRequestEnd(ctx context.Context, info RequestInfo, result RequestResult)

	// OnRetry is called before a retry attempt.
	OnRetry(ctx context.Context, info RequestInfo, attempt int, err error)
}

Hooks provides observability callbacks for SDK operations. Implementations can use these hooks for logging, metrics, tracing, etc.

There are two levels of hooks:

  • Operation-level: OnOperationStart/OnOperationEnd for semantic SDK operations
  • Request-level: OnRequestStart/OnRequestEnd for HTTP requests

Operation hooks provide business-meaningful spans (e.g., "Todos.Complete"), while request hooks capture transport-level details (retries, cache, timing).

The context passed to hooks allows correlation of nested operations and requests.

func NewChainHooks

func NewChainHooks(hooks ...Hooks) Hooks

NewChainHooks creates a ChainHooks from the given hooks. Nil hooks are filtered out. If all hooks are nil, returns NoopHooks.

type Identity

type Identity struct {
	ID           int64  `json:"id"`
	FirstName    string `json:"first_name"`
	LastName     string `json:"last_name"`
	EmailAddress string `json:"email_address"`
}

Identity represents the authenticated user's identity from the authorization endpoint.

type Inbox

type Inbox struct {
	ID        int64     `json:"id"`
	Status    string    `json:"status"`
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
	Title     string    `json:"title"`
	Type      string    `json:"type"`
	URL       string    `json:"url"`
	AppURL    string    `json:"app_url"`
	Bucket    *Bucket   `json:"bucket,omitempty"`
	Creator   *Person   `json:"creator,omitempty"`
}

Inbox represents a Basecamp email inbox (forwards tool).

type LineupMarker

type LineupMarker struct {
	ID          int64     `json:"id"`
	Status      string    `json:"status"`
	Color       string    `json:"color"`
	Title       string    `json:"title"`
	StartsOn    string    `json:"starts_on"`
	EndsOn      string    `json:"ends_on"`
	Description string    `json:"description,omitempty"`
	CreatedAt   time.Time `json:"created_at"`
	UpdatedAt   time.Time `json:"updated_at"`
	Type        string    `json:"type"`
	URL         string    `json:"url"`
	AppURL      string    `json:"app_url"`
	Creator     *Person   `json:"creator,omitempty"`
	Parent      *Parent   `json:"parent,omitempty"`
	Bucket      *Bucket   `json:"bucket,omitempty"`
}

LineupMarker represents a marker on the Basecamp Lineup.

type LineupService

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

LineupService handles lineup marker operations.

func NewLineupService

func NewLineupService(client *AccountClient) *LineupService

NewLineupService creates a new LineupService.

func (*LineupService) CreateMarker

func (s *LineupService) CreateMarker(ctx context.Context, req *CreateMarkerRequest) (err error)

CreateMarker creates a new marker on the lineup.

func (*LineupService) DeleteMarker

func (s *LineupService) DeleteMarker(ctx context.Context, markerID int64) (err error)

DeleteMarker deletes a marker. markerID is the marker ID.

func (*LineupService) UpdateMarker

func (s *LineupService) UpdateMarker(ctx context.Context, markerID int64, req *UpdateMarkerRequest) (err error)

UpdateMarker updates an existing marker. markerID is the marker ID.

type ListMeta

type ListMeta struct {
	// TotalCount is the total number of items available (from X-Total-Count header).
	// Zero if the header was not present or could not be parsed.
	TotalCount int
	// Truncated is true when results were capped by MaxPages or Limit, either
	// because more pages are available on the server or because items were
	// dropped within a page due to the limit.
	Truncated bool
}

ListMeta contains pagination metadata from list operations.

type Match

type Match struct {
	// Source indicates how the URL was matched: API route table or structural convention.
	Source MatchSource

	// Operation is the matched API operation name (e.g., "GetTodo", "CreateMessage").
	// Empty for structural matches.
	Operation string

	// Operations lists all API operations for the matched pattern, keyed by HTTP method.
	// Nil for structural matches.
	Operations map[string]string

	// Resource is the API resource group (e.g., "Todos", "Messages").
	// Empty for structural matches.
	Resource string

	// PathType is the resource type segment from the URL path (e.g., "todos",
	// "messages", "cards", "columns"). Always populated when a bucket resource
	// is matched, whether by API route or structural fallback.
	PathType string

	// AccountID is the Basecamp account ID from the URL path.
	AccountID string

	// ProjectID is the bucket/project ID. Present for most resource URLs.
	ProjectID string

	// Params contains all named path parameters extracted from the URL.
	// Keys match the route table parameter names (e.g., "todoId", "messageId").
	// Nil for structural matches.
	Params map[string]string

	// CommentID is the comment ID extracted from a #__recording_{id} fragment.
	CommentID string
	// contains filtered or unexported fields
}

Match holds the components extracted from a Basecamp URL.

func (*Match) ResourceID

func (m *Match) ResourceID() string

ResourceID returns the "primary" resource ID from the match — the last path parameter that isn't accountId or projectId. Returns empty string if no such parameter exists (e.g., for project or list URLs).

type MatchSource

type MatchSource int

MatchSource indicates how a URL was recognized.

const (
	// MatchedAPI means the URL matched a route in the OpenAPI-derived route table.
	// Operation, Resource, and Params are populated.
	MatchedAPI MatchSource = iota + 1

	// MatchedStructural means the URL matched Basecamp's web URL conventions
	// but has no corresponding API route. Only IDs and PathType are populated.
	MatchedStructural
)

type Message

type Message struct {
	ID          int64        `json:"id"`
	Status      string       `json:"status"`
	Subject     string       `json:"subject"`
	Content     string       `json:"content"`
	CreatedAt   time.Time    `json:"created_at"`
	UpdatedAt   time.Time    `json:"updated_at"`
	Type        string       `json:"type"`
	URL         string       `json:"url"`
	AppURL      string       `json:"app_url"`
	Parent      *Parent      `json:"parent,omitempty"`
	Bucket      *Bucket      `json:"bucket,omitempty"`
	Creator     *Person      `json:"creator,omitempty"`
	Category    *MessageType `json:"category,omitempty"`
	BoostsCount int          `json:"boosts_count,omitempty"`
}

Message represents a Basecamp message on a message board.

type MessageBoard

type MessageBoard struct {
	ID            int64     `json:"id"`
	Status        string    `json:"status"`
	Title         string    `json:"title"`
	CreatedAt     time.Time `json:"created_at"`
	UpdatedAt     time.Time `json:"updated_at"`
	Type          string    `json:"type"`
	URL           string    `json:"url"`
	AppURL        string    `json:"app_url"`
	MessagesCount int       `json:"messages_count"`
	MessagesURL   string    `json:"messages_url"`
	Bucket        *Bucket   `json:"bucket,omitempty"`
	Creator       *Person   `json:"creator,omitempty"`
}

MessageBoard represents a Basecamp message board in a project.

type MessageBoardsService

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

MessageBoardsService handles message board operations.

func NewMessageBoardsService

func NewMessageBoardsService(client *AccountClient) *MessageBoardsService

NewMessageBoardsService creates a new MessageBoardsService.

func (*MessageBoardsService) Get

func (s *MessageBoardsService) Get(ctx context.Context, boardID int64) (result *MessageBoard, err error)

Get returns a message board by ID.

type MessageListOptions

type MessageListOptions struct {
	// Limit is the maximum number of messages to return.
	// If 0, uses DefaultMessageLimit (100). Use -1 for unlimited.
	Limit int

	// Page, if non-zero, disables pagination and returns only the first page.
	// NOTE: The page number itself is not yet honored due to OpenAPI client
	// limitations. Use 0 to paginate through all results up to Limit.
	Page int
}

MessageListOptions specifies options for listing messages.

type MessageListResult

type MessageListResult struct {
	// Messages is the list of messages returned.
	Messages []Message
	// Meta contains pagination metadata (total count, etc.).
	Meta ListMeta
}

MessageListResult contains the results from listing messages.

type MessageType

type MessageType struct {
	ID        int64     `json:"id"`
	Name      string    `json:"name"`
	Icon      string    `json:"icon"`
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
}

MessageType represents a Basecamp message type (category) in a project.

type MessageTypeListResult

type MessageTypeListResult struct {
	// MessageTypes is the list of message types returned.
	MessageTypes []MessageType
	// Meta contains pagination metadata (total count, etc.).
	Meta ListMeta
}

MessageTypeListResult contains the results from listing message types.

type MessageTypesService

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

MessageTypesService handles message type operations.

func NewMessageTypesService

func NewMessageTypesService(client *AccountClient) *MessageTypesService

NewMessageTypesService creates a new MessageTypesService.

func (*MessageTypesService) Create

func (s *MessageTypesService) Create(ctx context.Context, req *CreateMessageTypeRequest) (result *MessageType, err error)

Create creates a new message type in a project. Returns the created message type.

func (*MessageTypesService) Delete

func (s *MessageTypesService) Delete(ctx context.Context, typeID int64) (err error)

Delete deletes a message type from a project.

func (*MessageTypesService) Get

func (s *MessageTypesService) Get(ctx context.Context, typeID int64) (result *MessageType, err error)

Get returns a message type by ID.

func (*MessageTypesService) List

func (s *MessageTypesService) List(ctx context.Context) (result *MessageTypeListResult, err error)

List returns all message types for the account.

The returned MessageTypeListResult includes pagination metadata (TotalCount from X-Total-Count header) when available.

func (*MessageTypesService) Update

func (s *MessageTypesService) Update(ctx context.Context, typeID int64, req *UpdateMessageTypeRequest) (result *MessageType, err error)

Update updates an existing message type. Returns the updated message type.

type MessagesService

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

MessagesService handles message operations.

func NewMessagesService

func NewMessagesService(client *AccountClient) *MessagesService

NewMessagesService creates a new MessagesService.

func (*MessagesService) Archive

func (s *MessagesService) Archive(ctx context.Context, messageID int64) (err error)

Archive moves a message to the archive. Archived messages can be unarchived.

func (*MessagesService) Create

func (s *MessagesService) Create(ctx context.Context, boardID int64, req *CreateMessageRequest) (result *Message, err error)

Create creates a new message on a message board. Returns the created message.

Example
package main

import (
	"context"
	"fmt"
	"log"
	"os"

	"github.com/basecamp/basecamp-sdk/go/pkg/basecamp"
)

func main() {
	cfg := basecamp.DefaultConfig()
	token := &basecamp.StaticTokenProvider{Token: os.Getenv("BASECAMP_TOKEN")}
	client := basecamp.NewClient(cfg, token)

	ctx := context.Background()

	boardID := int64(789012)

	// Create a message on a message board
	message, err := client.ForAccount("12345").Messages().Create(ctx, boardID, &basecamp.CreateMessageRequest{
		Subject: "Weekly Update",
		Content: "<p>Here's what we accomplished this week...</p>",
	})
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Posted: %s\n", message.Subject)
}

func (*MessagesService) Get

func (s *MessagesService) Get(ctx context.Context, messageID int64) (result *Message, err error)

Get returns a message by ID.

func (*MessagesService) List

func (s *MessagesService) List(ctx context.Context, boardID int64, opts *MessageListOptions) (result *MessageListResult, err error)

List returns messages on a message board.

By default, returns up to 100 messages. Use Limit: -1 for unlimited.

Pagination options:

  • Limit: maximum number of messages to return (0 = 100, -1 = unlimited)
  • Page: if non-zero, disables pagination and returns first page only

The returned MessageListResult includes pagination metadata (TotalCount from X-Total-Count header) when available.

func (*MessagesService) Pin

func (s *MessagesService) Pin(ctx context.Context, messageID int64) (err error)

Pin pins a message to the top of the message board.

func (*MessagesService) Trash

func (s *MessagesService) Trash(ctx context.Context, messageID int64) (err error)

Trash moves a message to the trash. Trashed messages can be recovered from the trash.

func (*MessagesService) Unarchive

func (s *MessagesService) Unarchive(ctx context.Context, messageID int64) (err error)

Unarchive restores an archived message to active status.

func (*MessagesService) Unpin

func (s *MessagesService) Unpin(ctx context.Context, messageID int64) (err error)

Unpin unpins a message from the top of the message board.

func (*MessagesService) Update

func (s *MessagesService) Update(ctx context.Context, messageID int64, req *UpdateMessageRequest) (result *Message, err error)

Update updates an existing message. Returns the updated message.

type MoveCardRequest

type MoveCardRequest struct {
	// ColumnID is the destination column ID (required).
	ColumnID int64 `json:"column_id"`
}

MoveCardRequest specifies the parameters for moving a card.

type MoveColumnRequest

type MoveColumnRequest struct {
	// SourceID is the column ID to move (required).
	SourceID int64 `json:"source_id"`
	// TargetID is the column ID to move relative to (required).
	TargetID int64 `json:"target_id"`
	// Position is the position relative to target (optional).
	Position int `json:"position,omitempty"`
}

MoveColumnRequest specifies the parameters for moving a column.

type NoopHooks

type NoopHooks struct{}

NoopHooks is a no-op implementation of Hooks. All methods are empty and designed to be inlined by the compiler, resulting in zero overhead when no observability is needed.

func (NoopHooks) OnOperationEnd

OnOperationEnd does nothing.

func (NoopHooks) OnOperationStart

func (NoopHooks) OnOperationStart(ctx context.Context, _ OperationInfo) context.Context

OnOperationStart does nothing and returns the context unchanged.

func (NoopHooks) OnRequestEnd

OnRequestEnd does nothing.

func (NoopHooks) OnRequestStart

func (NoopHooks) OnRequestStart(ctx context.Context, _ RequestInfo) context.Context

OnRequestStart does nothing and returns the context unchanged.

func (NoopHooks) OnRetry

OnRetry does nothing.

type OperationInfo

type OperationInfo struct {
	// Service is the logical service (e.g., "Projects", "Todos").
	Service string
	// Operation is the specific method (e.g., "List", "Create", "Complete").
	Operation string
	// ResourceType is the Basecamp resource type (e.g., "project", "todo").
	ResourceType string
	// IsMutation indicates if this operation modifies state.
	IsMutation bool
	// ResourceID is the specific resource ID if applicable.
	ResourceID int64
}

OperationInfo describes a semantic SDK operation. This carries more meaning than raw HTTP requests, enabling business-level tracing and metrics (e.g., "Todos.Complete" not "POST /url").

type OverdueTodosResponse

type OverdueTodosResponse struct {
	UnderAWeekLate      []Todo `json:"under_a_week_late"`
	OverAWeekLate       []Todo `json:"over_a_week_late"`
	OverAMonthLate      []Todo `json:"over_a_month_late"`
	OverThreeMonthsLate []Todo `json:"over_three_months_late"`
}

OverdueTodosResponse contains overdue todos grouped by lateness.

type Parent

type Parent struct {
	ID     int64  `json:"id"`
	Title  string `json:"title"`
	Type   string `json:"type"`
	URL    string `json:"url"`
	AppURL string `json:"app_url"`
}

Parent represents the parent object of a todo.

type PeopleListOptions

type PeopleListOptions struct {
	// Limit is the maximum number of people to return.
	// If 0 (default), returns all people.
	Limit int

	// Page, if non-zero, disables pagination and returns only the first page.
	// NOTE: The page number itself is not yet honored due to OpenAPI client
	// limitations. Use 0 to paginate through all results up to Limit.
	Page int
}

PeopleListOptions specifies options for listing people.

type PeopleListResult

type PeopleListResult struct {
	// People is the list of people returned.
	People []Person
	// Meta contains pagination metadata (total count, etc.).
	Meta ListMeta
}

PeopleListResult contains the results from listing people.

type PeopleService

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

PeopleService handles people operations.

func NewPeopleService

func NewPeopleService(client *AccountClient) *PeopleService

NewPeopleService creates a new PeopleService.

func (*PeopleService) Get

func (s *PeopleService) Get(ctx context.Context, personID int64) (result *Person, err error)

Get returns a person by ID.

func (*PeopleService) List

func (s *PeopleService) List(ctx context.Context, opts *PeopleListOptions) (result *PeopleListResult, err error)

List returns all people visible to the current user in the account.

Pagination options:

  • Limit: maximum number of people to return (0 = all)
  • Page: if non-zero, disables pagination and returns first page only

The returned PeopleListResult includes pagination metadata (TotalCount from X-Total-Count header) when available.

Example
package main

import (
	"context"
	"fmt"
	"log"
	"os"

	"github.com/basecamp/basecamp-sdk/go/pkg/basecamp"
)

func main() {
	cfg := basecamp.DefaultConfig()
	token := &basecamp.StaticTokenProvider{Token: os.Getenv("BASECAMP_TOKEN")}
	client := basecamp.NewClient(cfg, token)

	ctx := context.Background()

	// List all people in the account
	peopleResult, err := client.ForAccount("12345").People().List(ctx, nil)
	if err != nil {
		log.Fatal(err)
	}

	for _, p := range peopleResult.People {
		fmt.Printf("%s <%s>\n", p.Name, p.EmailAddress)
	}
}

func (*PeopleService) ListProjectPeople

func (s *PeopleService) ListProjectPeople(ctx context.Context, projectID int64, opts *PeopleListOptions) (result *PeopleListResult, err error)

ListProjectPeople returns all active people on a project.

Pagination options:

  • Limit: maximum number of people to return (0 = all)
  • Page: if non-zero, disables pagination and returns first page only

The returned PeopleListResult includes pagination metadata (TotalCount from X-Total-Count header) when available.

func (*PeopleService) Me

func (s *PeopleService) Me(ctx context.Context) (result *Person, err error)

Me returns the current authenticated user's profile.

func (*PeopleService) Pingable

func (s *PeopleService) Pingable(ctx context.Context) (result []Person, err error)

Pingable returns all account users who can be pinged. Note: This endpoint is not paginated in the Basecamp API.

func (*PeopleService) UpdateProjectAccess

func (s *PeopleService) UpdateProjectAccess(ctx context.Context, projectID int64, req *UpdateProjectAccessRequest) (result *UpdateProjectAccessResponse, err error)

UpdateProjectAccess grants or revokes project access for people. Returns the list of people who were granted and revoked access.

type Person

type Person struct {
	ID                int64          `json:"id"`
	AttachableSGID    string         `json:"attachable_sgid,omitempty"`
	Name              string         `json:"name"`
	EmailAddress      string         `json:"email_address,omitempty"`
	PersonableType    string         `json:"personable_type,omitempty"`
	Title             string         `json:"title,omitempty"`
	Bio               string         `json:"bio,omitempty"`
	Location          string         `json:"location,omitempty"`
	CreatedAt         string         `json:"created_at,omitempty"`
	UpdatedAt         string         `json:"updated_at,omitempty"`
	Admin             bool           `json:"admin,omitempty"`
	Owner             bool           `json:"owner,omitempty"`
	Client            bool           `json:"client,omitempty"`
	Employee          bool           `json:"employee,omitempty"`
	TimeZone          string         `json:"time_zone,omitempty"`
	AvatarURL         string         `json:"avatar_url,omitempty"`
	CanPing           bool           `json:"can_ping,omitempty"`
	Company           *PersonCompany `json:"company,omitempty"`
	CanManageProjects bool           `json:"can_manage_projects,omitempty"`
	CanManagePeople   bool           `json:"can_manage_people,omitempty"`
}

Person represents a Basecamp user.

type PersonCompany

type PersonCompany struct {
	ID   int64  `json:"id"`
	Name string `json:"name"`
}

PersonCompany represents a company associated with a person.

type PersonProgressResponse

type PersonProgressResponse struct {
	Person *Person         `json:"person"`
	Events []TimelineEvent `json:"events"`
}

PersonProgressResponse contains a person's activity timeline.

type Project

type Project struct {
	ID             int64          `json:"id"`
	Status         string         `json:"status"`
	CreatedAt      time.Time      `json:"created_at"`
	UpdatedAt      time.Time      `json:"updated_at"`
	Name           string         `json:"name"`
	Description    string         `json:"description"`
	Purpose        string         `json:"purpose"`
	ClientsEnabled bool           `json:"clients_enabled"`
	BookmarkURL    string         `json:"bookmark_url"`
	URL            string         `json:"url"`
	AppURL         string         `json:"app_url"`
	Dock           []DockItem     `json:"dock,omitempty"`
	Bookmarked     bool           `json:"bookmarked"`
	ClientCompany  *ClientCompany `json:"client_company,omitempty"`
	Clientside     *Clientside    `json:"clientside,omitempty"`
}

Project represents a Basecamp project.

type ProjectConstruction

type ProjectConstruction struct {
	ID      int64    `json:"id"`
	Status  string   `json:"status"`
	URL     string   `json:"url"`
	Project *Project `json:"project,omitempty"`
}

ProjectConstruction represents the status of a project being created from a template.

type ProjectListOptions

type ProjectListOptions struct {
	// Status filters by project status (active, archived, trashed).
	// If empty, defaults to active projects.
	Status ProjectStatus

	// Limit is the maximum number of projects to return.
	// If 0 (default), returns all projects.
	Limit int

	// Page, if non-zero, disables pagination and returns only the first page.
	// NOTE: The page number itself is not yet honored due to OpenAPI client
	// limitations. Use 0 to paginate through all results up to Limit.
	Page int
}

ProjectListOptions specifies options for listing projects.

type ProjectListResult

type ProjectListResult struct {
	// Projects is the list of projects returned.
	Projects []Project
	// Meta contains pagination metadata (total count, etc.).
	Meta ListMeta
}

ProjectListResult contains the results from listing projects.

type ProjectStatus

type ProjectStatus string

ProjectStatus represents valid project statuses.

const (
	ProjectStatusActive   ProjectStatus = "active"
	ProjectStatusArchived ProjectStatus = "archived"
	ProjectStatusTrashed  ProjectStatus = "trashed"
)

type ProjectsService

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

ProjectsService handles project operations.

func NewProjectsService

func NewProjectsService(client *AccountClient) *ProjectsService

NewProjectsService creates a new ProjectsService.

func (*ProjectsService) Create

func (s *ProjectsService) Create(ctx context.Context, req *CreateProjectRequest) (result *Project, err error)

Create creates a new project. Returns the created project.

Example
package main

import (
	"context"
	"fmt"
	"log"
	"os"

	"github.com/basecamp/basecamp-sdk/go/pkg/basecamp"
)

func main() {
	cfg := basecamp.DefaultConfig()
	token := &basecamp.StaticTokenProvider{Token: os.Getenv("BASECAMP_TOKEN")}
	client := basecamp.NewClient(cfg, token)

	ctx := context.Background()

	// Create a new project
	project, err := client.ForAccount("12345").Projects().Create(ctx, &basecamp.CreateProjectRequest{
		Name:        "Q1 Planning",
		Description: "Planning for the first quarter",
	})
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Created project: %s (ID: %d)\n", project.Name, project.ID)
}

func (*ProjectsService) Get

func (s *ProjectsService) Get(ctx context.Context, id int64) (result *Project, err error)

Get returns a project by ID.

func (*ProjectsService) List

func (s *ProjectsService) List(ctx context.Context, opts *ProjectListOptions) (result *ProjectListResult, err error)

List returns all projects visible to the current user. By default, returns active projects sorted by most recently created first.

Pagination options:

  • Limit: maximum number of projects to return (0 = all)
  • Page: if non-zero, disables pagination and returns first page only

The returned ProjectListResult includes pagination metadata (TotalCount from X-Total-Count header) when available.

Example
package main

import (
	"context"
	"fmt"
	"log"
	"os"

	"github.com/basecamp/basecamp-sdk/go/pkg/basecamp"
)

func main() {
	cfg := basecamp.DefaultConfig()
	token := &basecamp.StaticTokenProvider{Token: os.Getenv("BASECAMP_TOKEN")}
	client := basecamp.NewClient(cfg, token)

	ctx := context.Background()

	// List all active projects
	result, err := client.ForAccount("12345").Projects().List(ctx, nil)
	if err != nil {
		log.Fatal(err)
	}

	for _, p := range result.Projects {
		fmt.Printf("Project: %s (ID: %d)\n", p.Name, p.ID)
	}
}
Example (Archived)
package main

import (
	"context"
	"fmt"
	"log"
	"os"

	"github.com/basecamp/basecamp-sdk/go/pkg/basecamp"
)

func main() {
	cfg := basecamp.DefaultConfig()
	token := &basecamp.StaticTokenProvider{Token: os.Getenv("BASECAMP_TOKEN")}
	client := basecamp.NewClient(cfg, token)

	ctx := context.Background()

	// List archived projects
	result, err := client.ForAccount("12345").Projects().List(ctx, &basecamp.ProjectListOptions{
		Status: basecamp.ProjectStatusArchived,
	})
	if err != nil {
		log.Fatal(err)
	}

	for _, p := range result.Projects {
		fmt.Printf("Archived: %s\n", p.Name)
	}
}

func (*ProjectsService) Trash

func (s *ProjectsService) Trash(ctx context.Context, id int64) (err error)

Trash moves a project to the trash. Trashed projects are deleted after 30 days.

func (*ProjectsService) Update

func (s *ProjectsService) Update(ctx context.Context, id int64, req *UpdateProjectRequest) (result *Project, err error)

Update updates an existing project. Returns the updated project.

type Question

type Question struct {
	ID               int64             `json:"id"`
	Status           string            `json:"status"`
	VisibleToClients bool              `json:"visible_to_clients"`
	CreatedAt        time.Time         `json:"created_at"`
	UpdatedAt        time.Time         `json:"updated_at"`
	Title            string            `json:"title"`
	InheritsStatus   bool              `json:"inherits_status"`
	Type             string            `json:"type"`
	URL              string            `json:"url"`
	AppURL           string            `json:"app_url"`
	BookmarkURL      string            `json:"bookmark_url"`
	SubscriptionURL  string            `json:"subscription_url"`
	Parent           *Parent           `json:"parent,omitempty"`
	Bucket           *Bucket           `json:"bucket,omitempty"`
	Creator          *Person           `json:"creator,omitempty"`
	Paused           bool              `json:"paused"`
	Schedule         *QuestionSchedule `json:"schedule,omitempty"`
	AnswersCount     int               `json:"answers_count"`
	AnswersURL       string            `json:"answers_url"`
}

Question represents a Basecamp automatic check-in question.

type QuestionAnswer

type QuestionAnswer struct {
	ID               int64     `json:"id"`
	Status           string    `json:"status"`
	VisibleToClients bool      `json:"visible_to_clients"`
	CreatedAt        time.Time `json:"created_at"`
	UpdatedAt        time.Time `json:"updated_at"`
	Title            string    `json:"title"`
	InheritsStatus   bool      `json:"inherits_status"`
	Type             string    `json:"type"`
	URL              string    `json:"url"`
	AppURL           string    `json:"app_url"`
	BookmarkURL      string    `json:"bookmark_url"`
	SubscriptionURL  string    `json:"subscription_url"`
	CommentsCount    int       `json:"comments_count"`
	CommentsURL      string    `json:"comments_url"`
	Content          string    `json:"content"`
	GroupOn          string    `json:"group_on"`
	Parent           *Parent   `json:"parent,omitempty"`
	Bucket           *Bucket   `json:"bucket,omitempty"`
	Creator          *Person   `json:"creator,omitempty"`
}

QuestionAnswer represents an answer to a Basecamp check-in question.

type QuestionListOptions

type QuestionListOptions struct {
	// Limit is the maximum number of questions to return.
	// If 0 (default), returns all questions. Use a positive value to cap results.
	Limit int

	// Page, if non-zero, disables pagination and returns only the first page.
	// NOTE: The page number itself is not yet honored due to OpenAPI client
	// limitations. Use 0 to paginate through all results up to Limit.
	Page int
}

QuestionListOptions specifies options for listing questions.

type QuestionListResult

type QuestionListResult struct {
	// Questions is the list of questions returned.
	Questions []Question
	// Meta contains pagination metadata (total count, etc.).
	Meta ListMeta
}

QuestionListResult contains the results from listing questions.

type QuestionSchedule

type QuestionSchedule struct {
	Frequency     string `json:"frequency"`
	Days          []int  `json:"days"`
	Hour          int    `json:"hour"`
	Minute        int    `json:"minute"`
	WeekInstance  *int   `json:"week_instance,omitempty"`
	WeekInterval  *int   `json:"week_interval,omitempty"`
	MonthInterval *int   `json:"month_interval,omitempty"`
	StartDate     string `json:"start_date,omitempty"`
	EndDate       string `json:"end_date,omitempty"`
}

QuestionSchedule represents the schedule configuration for a question.

type Questionnaire

type Questionnaire struct {
	ID               int64     `json:"id"`
	Status           string    `json:"status"`
	VisibleToClients bool      `json:"visible_to_clients"`
	CreatedAt        time.Time `json:"created_at"`
	UpdatedAt        time.Time `json:"updated_at"`
	Title            string    `json:"title"`
	InheritsStatus   bool      `json:"inherits_status"`
	Type             string    `json:"type"`
	URL              string    `json:"url"`
	AppURL           string    `json:"app_url"`
	BookmarkURL      string    `json:"bookmark_url"`
	QuestionsURL     string    `json:"questions_url"`
	QuestionsCount   int       `json:"questions_count"`
	Name             string    `json:"name"`
	Bucket           *Bucket   `json:"bucket,omitempty"`
	Creator          *Person   `json:"creator,omitempty"`
}

Questionnaire represents a Basecamp automatic check-in questionnaire.

type RateLimitConfig

type RateLimitConfig struct {
	// RequestsPerSecond is the sustained rate of requests allowed.
	// Default: 50
	RequestsPerSecond float64

	// BurstSize is the maximum number of requests allowed in a burst.
	// Default: 10
	BurstSize int

	// RespectRetryAfter honors 429 Retry-After headers by blocking requests
	// until the server-specified time has passed.
	// Default: true
	RespectRetryAfter bool

	// Now is a function that returns the current time. Used for testing.
	// If nil, time.Now is used.
	Now func() time.Time
}

RateLimitConfig configures client-side rate limiting.

func DefaultRateLimitConfig

func DefaultRateLimitConfig() *RateLimitConfig

DefaultRateLimitConfig returns production-ready defaults.

type Recording

type Recording struct {
	ID               int64     `json:"id"`
	Status           string    `json:"status"`
	VisibleToClients bool      `json:"visible_to_clients"`
	CreatedAt        time.Time `json:"created_at"`
	UpdatedAt        time.Time `json:"updated_at"`
	Title            string    `json:"title"`
	InheritsStatus   bool      `json:"inherits_status"`
	Type             string    `json:"type"`
	URL              string    `json:"url"`
	AppURL           string    `json:"app_url"`
	BookmarkURL      string    `json:"bookmark_url"`
	Parent           *Parent   `json:"parent,omitempty"`
	Bucket           *Bucket   `json:"bucket,omitempty"`
	Creator          *Person   `json:"creator,omitempty"`
}

Recording represents a generic Basecamp recording. Recordings are the base type for most content in Basecamp including messages, todos, comments, documents, and more.

type RecordingListResult

type RecordingListResult struct {
	// Recordings is the list of recordings returned.
	Recordings []Recording
	// Meta contains pagination metadata (total count, etc.).
	Meta ListMeta
}

RecordingListResult contains the results from listing recordings.

type RecordingType

type RecordingType string

RecordingType represents a type of recording in Basecamp.

const (
	RecordingTypeComment        RecordingType = "Comment"
	RecordingTypeDocument       RecordingType = "Document"
	RecordingTypeKanbanCard     RecordingType = "Kanban::Card"
	RecordingTypeKanbanStep     RecordingType = "Kanban::Step"
	RecordingTypeMessage        RecordingType = "Message"
	RecordingTypeQuestionAnswer RecordingType = "Question::Answer"
	RecordingTypeScheduleEntry  RecordingType = "Schedule::Entry"
	RecordingTypeTodo           RecordingType = "Todo"
	RecordingTypeTodolist       RecordingType = "Todolist"
	RecordingTypeUpload         RecordingType = "Upload"
	RecordingTypeVault          RecordingType = "Vault"
)

Recording types supported by the Basecamp API.

type RecordingsListOptions

type RecordingsListOptions struct {
	// Bucket filters by project IDs (comma-separated or slice).
	// Defaults to all active projects visible to the user.
	Bucket []int64

	// Status filters by recording status: "active", "archived", or "trashed".
	// Defaults to "active".
	Status string

	// Sort specifies the sort field: "created_at" or "updated_at".
	// Defaults to "created_at".
	Sort string

	// Direction specifies the sort direction: "desc" or "asc".
	// Defaults to "desc".
	Direction string

	// Limit is the maximum number of recordings to return.
	// If 0, uses DefaultRecordingLimit (100). Use -1 for unlimited.
	Limit int

	// Page, if non-zero, disables pagination and returns only the first page.
	// NOTE: The page number itself is not yet honored due to OpenAPI client
	// limitations. Use 0 to paginate through all results up to Limit.
	Page int
}

RecordingsListOptions specifies options for listing recordings.

type RecordingsService

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

RecordingsService handles recording operations. Recordings are the base type for most content in Basecamp.

func NewRecordingsService

func NewRecordingsService(client *AccountClient) *RecordingsService

NewRecordingsService creates a new RecordingsService.

func (*RecordingsService) Archive

func (s *RecordingsService) Archive(ctx context.Context, recordingID int64) (err error)

Archive archives a recording. Archived recordings are hidden but not deleted.

func (*RecordingsService) Get

func (s *RecordingsService) Get(ctx context.Context, recordingID int64) (result *Recording, err error)

Get returns a recording by ID.

func (*RecordingsService) List

func (s *RecordingsService) List(ctx context.Context, recordingType RecordingType, opts *RecordingsListOptions) (result *RecordingListResult, err error)

List returns all recordings of a given type across projects. recordingType is required and specifies what type of recordings to list. Use the RecordingType constants (e.g., RecordingTypeTodo, RecordingTypeMessage).

By default, returns up to 100 recordings. Use Limit: -1 for unlimited.

Pagination options:

  • Limit: maximum number of recordings to return (0 = 100, -1 = unlimited)
  • Page: if non-zero, disables pagination and returns first page only

The returned RecordingListResult includes pagination metadata (TotalCount from X-Total-Count header) when available.

func (*RecordingsService) SetClientVisibility

func (s *RecordingsService) SetClientVisibility(ctx context.Context, recordingID int64, visible bool) (result *Recording, err error)

SetClientVisibility sets whether a recording is visible to clients. visible specifies whether the recording should be visible to clients. Returns the updated recording. Note: Not all recordings support client visibility. Some inherit visibility from their parent.

func (*RecordingsService) Trash

func (s *RecordingsService) Trash(ctx context.Context, recordingID int64) (err error)

Trash moves a recording to the trash. Trashed recordings can be recovered from the trash.

func (*RecordingsService) Unarchive

func (s *RecordingsService) Unarchive(ctx context.Context, recordingID int64) (err error)

Unarchive restores an archived recording to active status.

type ReportsService

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

ReportsService handles reports operations.

func NewReportsService

func NewReportsService(client *AccountClient) *ReportsService

NewReportsService creates a new ReportsService.

func (*ReportsService) AssignablePeople

func (s *ReportsService) AssignablePeople(ctx context.Context) (result []Person, err error)

AssignablePeople returns people who can be assigned todos.

func (*ReportsService) AssignedTodos

func (s *ReportsService) AssignedTodos(ctx context.Context, personID int64, opts *AssignedTodosOptions) (result *AssignedTodosResponse, err error)

AssignedTodos returns todos assigned to a specific person.

func (*ReportsService) OverdueTodos

func (s *ReportsService) OverdueTodos(ctx context.Context) (result *OverdueTodosResponse, err error)

OverdueTodos returns all overdue todos grouped by lateness.

func (*ReportsService) UpcomingSchedule

func (s *ReportsService) UpcomingSchedule(ctx context.Context, startDate, endDate string) (result *UpcomingScheduleResponse, err error)

UpcomingSchedule returns schedule entries within a date window. startDate and endDate should be in YYYY-MM-DD format.

type RequestInfo

type RequestInfo struct {
	Method string
	URL    string
	// Attempt is the current attempt number (1-indexed).
	Attempt int
}

RequestInfo contains information about an HTTP request.

type RequestResult

type RequestResult struct {
	// StatusCode is the HTTP status code (0 if request failed before response).
	StatusCode int
	// Duration is the time taken for the request.
	Duration time.Duration
	// Error is non-nil if the request failed.
	Error error
	// FromCache indicates the response was served from cache.
	FromCache bool
	// Retryable indicates whether this error will be retried.
	Retryable bool
	// RetryAfter is the Retry-After header value in seconds (0 if not present).
	// Used by resilience hooks to respect server-requested backoff on 429/503.
	RetryAfter int
}

RequestResult contains the result of an HTTP request.

type ResilienceConfig

type ResilienceConfig struct {
	// CircuitBreaker configuration. If nil, circuit breaker is disabled.
	CircuitBreaker *CircuitBreakerConfig

	// Bulkhead configuration. If nil, bulkhead is disabled.
	Bulkhead *BulkheadConfig

	// RateLimit configuration. If nil, rate limiting is disabled.
	RateLimit *RateLimitConfig
}

ResilienceConfig combines all resilience settings. Use DefaultResilienceConfig() for production-ready defaults.

func DefaultResilienceConfig

func DefaultResilienceConfig() *ResilienceConfig

DefaultResilienceConfig returns production-ready defaults for all resilience features.

type Response

type Response struct {
	Data       json.RawMessage
	StatusCode int
	Headers    http.Header
	FromCache  bool
}

Response wraps an API response.

func (*Response) UnmarshalData

func (r *Response) UnmarshalData(v any) error

UnmarshalData unmarshals the response data into the given value.

type Router

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

Router matches Basecamp URLs against the OpenAPI-derived route table, with structural fallbacks for web-only URLs.

func DefaultRouter

func DefaultRouter() *Router

DefaultRouter returns a shared Router instance using the embedded route table.

func NewRouter

func NewRouter(tableJSON []byte) (*Router, error)

NewRouter creates a Router from a JSON route table.

func (*Router) Match

func (r *Router) Match(rawURL string) *Match

Match parses a Basecamp URL and returns the matched route and extracted parameters. Returns nil if the URL does not look like a Basecamp URL.

First tries the spec-derived API route table for a rich match (Source=MatchedAPI). Falls back to structural pattern matching for web-only URLs (Source=MatchedStructural).

func (*Router) MatchAPI

func (r *Router) MatchAPI(rawURL string) *Match

MatchAPI matches only against the spec-derived API route table. Returns nil if the URL doesn't correspond to a known API route. Use this when you need to resolve a URL to a specific API operation.

func (*Router) MatchStructural

func (r *Router) MatchStructural(rawURL string) *Match

MatchStructural matches only against Basecamp's web URL conventions. Returns nil if the URL doesn't look like a Basecamp URL. Use this when you know you have a web URL and only need ID extraction.

type Schedule

type Schedule struct {
	ID                    int64     `json:"id"`
	Status                string    `json:"status"`
	VisibleToClients      bool      `json:"visible_to_clients"`
	CreatedAt             time.Time `json:"created_at"`
	UpdatedAt             time.Time `json:"updated_at"`
	Title                 string    `json:"title"`
	InheritsStatus        bool      `json:"inherits_status"`
	Type                  string    `json:"type"`
	URL                   string    `json:"url"`
	AppURL                string    `json:"app_url"`
	BookmarkURL           string    `json:"bookmark_url"`
	Position              int       `json:"position"`
	Bucket                *Bucket   `json:"bucket,omitempty"`
	Creator               *Person   `json:"creator,omitempty"`
	IncludeDueAssignments bool      `json:"include_due_assignments"`
	EntriesCount          int       `json:"entries_count"`
	EntriesURL            string    `json:"entries_url"`
}

Schedule represents a Basecamp schedule (calendar) within a project.

type ScheduleAttributes

type ScheduleAttributes struct {
	// StartDate is the project start date (ISO 8601 format, e.g., "2022-01-01").
	StartDate string `json:"start_date"`
	// EndDate is the project end date (ISO 8601 format).
	EndDate string `json:"end_date"`
}

ScheduleAttributes specifies project schedule dates.

type ScheduleEntry

type ScheduleEntry struct {
	ID               int64     `json:"id"`
	Status           string    `json:"status"`
	VisibleToClients bool      `json:"visible_to_clients"`
	CreatedAt        time.Time `json:"created_at"`
	UpdatedAt        time.Time `json:"updated_at"`
	Title            string    `json:"title"`
	Summary          string    `json:"summary"`
	InheritsStatus   bool      `json:"inherits_status"`
	Type             string    `json:"type"`
	URL              string    `json:"url"`
	AppURL           string    `json:"app_url"`
	BookmarkURL      string    `json:"bookmark_url"`
	SubscriptionURL  string    `json:"subscription_url"`
	CommentsURL      string    `json:"comments_url"`
	CommentsCount    int       `json:"comments_count"`
	StartsAt         time.Time `json:"starts_at"`
	EndsAt           time.Time `json:"ends_at"`
	AllDay           bool      `json:"all_day"`
	Description      string    `json:"description"`
	Parent           *Parent   `json:"parent,omitempty"`
	Bucket           *Bucket   `json:"bucket,omitempty"`
	Creator          *Person   `json:"creator,omitempty"`
	Participants     []Person  `json:"participants,omitempty"`
}

ScheduleEntry represents an event on a Basecamp schedule.

type ScheduleEntryListOptions

type ScheduleEntryListOptions struct {
	// Limit is the maximum number of entries to return.
	// If 0 (default), returns all entries. Use a positive value to cap results.
	Limit int

	// Page, if non-zero, disables pagination and returns only the first page.
	// NOTE: The page number itself is not yet honored due to OpenAPI client
	// limitations. Use 0 to paginate through all results up to Limit.
	Page int

	// Status filters entries by status: "active", "archived", or "trashed".
	// If empty, returns active entries (API default).
	Status string
}

ScheduleEntryListOptions specifies options for listing schedule entries.

type ScheduleEntryListResult

type ScheduleEntryListResult struct {
	// Entries is the list of schedule entries returned.
	Entries []ScheduleEntry
	// Meta contains pagination metadata (total count, etc.).
	Meta ListMeta
}

ScheduleEntryListResult contains the results from listing schedule entries.

type SchedulesService

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

SchedulesService handles schedule operations.

func NewSchedulesService

func NewSchedulesService(client *AccountClient) *SchedulesService

NewSchedulesService creates a new SchedulesService.

func (*SchedulesService) CreateEntry

func (s *SchedulesService) CreateEntry(ctx context.Context, scheduleID int64, req *CreateScheduleEntryRequest) (result *ScheduleEntry, err error)

CreateEntry creates a new entry on a schedule. Returns the created schedule entry.

func (*SchedulesService) Get

func (s *SchedulesService) Get(ctx context.Context, scheduleID int64) (result *Schedule, err error)

Get returns a schedule by ID.

func (*SchedulesService) GetEntry

func (s *SchedulesService) GetEntry(ctx context.Context, entryID int64) (result *ScheduleEntry, err error)

GetEntry returns a schedule entry by ID.

func (*SchedulesService) GetEntryOccurrence

func (s *SchedulesService) GetEntryOccurrence(ctx context.Context, entryID int64, date string) (result *ScheduleEntry, err error)

GetEntryOccurrence returns a specific occurrence of a recurring schedule entry.

func (*SchedulesService) ListEntries

func (s *SchedulesService) ListEntries(ctx context.Context, scheduleID int64, opts *ScheduleEntryListOptions) (result *ScheduleEntryListResult, err error)

ListEntries returns all entries on a schedule.

By default, returns all entries (no limit). Use Limit to cap results.

Pagination options:

  • Limit: maximum number of entries to return (0 = all)
  • Page: if non-zero, disables pagination and returns first page only

The returned ScheduleEntryListResult includes pagination metadata (TotalCount from X-Total-Count header) when available.

func (*SchedulesService) TrashEntry

func (s *SchedulesService) TrashEntry(ctx context.Context, entryID int64) (err error)

TrashEntry moves a schedule entry to the trash. Trashed entries can be recovered from the trash.

func (*SchedulesService) UpdateEntry

func (s *SchedulesService) UpdateEntry(ctx context.Context, entryID int64, req *UpdateScheduleEntryRequest) (result *ScheduleEntry, err error)

UpdateEntry updates an existing schedule entry. Returns the updated schedule entry.

func (*SchedulesService) UpdateSettings

func (s *SchedulesService) UpdateSettings(ctx context.Context, scheduleID int64, req *UpdateScheduleSettingsRequest) (result *Schedule, err error)

UpdateSettings updates the settings for a schedule. Returns the updated schedule.

type SearchMetadata

type SearchMetadata struct {
	Projects []SearchProject `json:"projects"`
}

SearchMetadata represents metadata about available search scopes.

type SearchOptions

type SearchOptions struct {
	// Sort specifies the sort order: "created_at" or "updated_at" (default: relevance).
	Sort string
}

SearchOptions specifies optional parameters for search.

type SearchProject

type SearchProject struct {
	ID   int64  `json:"id"`
	Name string `json:"name"`
}

SearchProject represents a project available for search scope filtering.

type SearchResult

type SearchResult struct {
	ID               int64     `json:"id"`
	Status           string    `json:"status"`
	VisibleToClients bool      `json:"visible_to_clients"`
	CreatedAt        time.Time `json:"created_at"`
	UpdatedAt        time.Time `json:"updated_at"`
	Title            string    `json:"title"`
	InheritsStatus   bool      `json:"inherits_status"`
	Type             string    `json:"type"`
	URL              string    `json:"url"`
	AppURL           string    `json:"app_url"`
	BookmarkURL      string    `json:"bookmark_url"`
	Parent           *Parent   `json:"parent,omitempty"`
	Bucket           *Bucket   `json:"bucket,omitempty"`
	Creator          *Person   `json:"creator,omitempty"`
	Content          string    `json:"content,omitempty"`
	Description      string    `json:"description,omitempty"`
	Subject          string    `json:"subject,omitempty"`
}

SearchResult represents a single search result from the Basecamp API.

type SearchService

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

SearchService handles search operations.

func NewSearchService

func NewSearchService(client *AccountClient) *SearchService

NewSearchService creates a new SearchService.

func (*SearchService) Metadata

func (s *SearchService) Metadata(ctx context.Context) (result *SearchMetadata, err error)

Metadata returns metadata about available search scopes. This includes the list of projects available for filtering.

func (*SearchService) Search

func (s *SearchService) Search(ctx context.Context, query string, opts *SearchOptions) (result []SearchResult, err error)

Search searches for content across the account. The query parameter is the search string. Returns a list of matching results.

Example
package main

import (
	"context"
	"fmt"
	"log"
	"os"

	"github.com/basecamp/basecamp-sdk/go/pkg/basecamp"
)

func main() {
	cfg := basecamp.DefaultConfig()
	token := &basecamp.StaticTokenProvider{Token: os.Getenv("BASECAMP_TOKEN")}
	client := basecamp.NewClient(cfg, token)

	ctx := context.Background()

	// Search across the account
	results, err := client.ForAccount("12345").Search().Search(ctx, "quarterly report", nil)
	if err != nil {
		log.Fatal(err)
	}

	for _, r := range results {
		fmt.Printf("[%s] %s\n", r.Type, r.Title)
	}
}
Example (Sorted)
package main

import (
	"context"
	"fmt"
	"log"
	"os"

	"github.com/basecamp/basecamp-sdk/go/pkg/basecamp"
)

func main() {
	cfg := basecamp.DefaultConfig()
	token := &basecamp.StaticTokenProvider{Token: os.Getenv("BASECAMP_TOKEN")}
	client := basecamp.NewClient(cfg, token)

	ctx := context.Background()

	// Search with results sorted by creation date
	results, err := client.ForAccount("12345").Search().Search(ctx, "meeting notes", &basecamp.SearchOptions{
		Sort: "created_at",
	})
	if err != nil {
		log.Fatal(err)
	}

	for _, r := range results {
		fmt.Printf("%s: %s\n", r.CreatedAt.Format("2006-01-02"), r.Title)
	}
}

type SetClientVisibilityRequest

type SetClientVisibilityRequest struct {
	VisibleToClients bool `json:"visible_to_clients"`
}

SetClientVisibilityRequest specifies the parameters for setting client visibility.

type SetColumnColorRequest

type SetColumnColorRequest struct {
	// Color is the column color. Valid values: white, red, orange, yellow,
	// green, blue, aqua, purple, gray, pink, brown (required).
	Color string `json:"color"`
}

SetColumnColorRequest specifies the parameters for changing a column color.

type SlogHooks

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

SlogHooks is a Hooks implementation that logs to a *slog.Logger. It provides structured logging for all SDK operations (both semantic and HTTP).

func NewSlogHooks

func NewSlogHooks(logger *slog.Logger, opts ...SlogHooksOption) *SlogHooks

NewSlogHooks creates a new SlogHooks that logs to the given logger. If logger is nil, uses slog.Default().

func (*SlogHooks) OnOperationEnd

func (h *SlogHooks) OnOperationEnd(ctx context.Context, op OperationInfo, err error, duration time.Duration)

OnOperationEnd logs the completion of a semantic SDK operation.

func (*SlogHooks) OnOperationStart

func (h *SlogHooks) OnOperationStart(ctx context.Context, op OperationInfo) context.Context

OnOperationStart logs the start of a semantic SDK operation.

func (*SlogHooks) OnRequestEnd

func (h *SlogHooks) OnRequestEnd(ctx context.Context, info RequestInfo, result RequestResult)

OnRequestEnd logs the completion of an HTTP request.

func (*SlogHooks) OnRequestStart

func (h *SlogHooks) OnRequestStart(ctx context.Context, info RequestInfo) context.Context

OnRequestStart logs the start of an HTTP request.

func (*SlogHooks) OnRetry

func (h *SlogHooks) OnRetry(ctx context.Context, info RequestInfo, attempt int, err error)

OnRetry logs a retry attempt.

type SlogHooksOption

type SlogHooksOption func(*SlogHooks)

SlogHooksOption configures a SlogHooks instance.

func WithLevel

func WithLevel(level slog.Level) SlogHooksOption

WithLevel sets the log level for SlogHooks. Default is slog.LevelDebug.

type StaticTokenProvider

type StaticTokenProvider struct {
	Token string
}

StaticTokenProvider provides a fixed token (e.g., from BASECAMP_TOKEN env var).

func (*StaticTokenProvider) AccessToken

func (p *StaticTokenProvider) AccessToken(ctx context.Context) (string, error)

AccessToken returns the static token.

type Subscription

type Subscription struct {
	Subscribed  bool     `json:"subscribed"`
	Count       int      `json:"count"`
	URL         string   `json:"url"`
	Subscribers []Person `json:"subscribers"`
}

Subscription represents the subscription state for a recording.

type SubscriptionsService

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

SubscriptionsService handles subscription operations on recordings.

func NewSubscriptionsService

func NewSubscriptionsService(client *AccountClient) *SubscriptionsService

NewSubscriptionsService creates a new SubscriptionsService.

func (*SubscriptionsService) Get

func (s *SubscriptionsService) Get(ctx context.Context, recordingID int64) (result *Subscription, err error)

Get returns the subscription information for a recording.

func (*SubscriptionsService) Subscribe

func (s *SubscriptionsService) Subscribe(ctx context.Context, recordingID int64) (result *Subscription, err error)

Subscribe subscribes the current user to the recording. Returns the updated subscription information.

func (*SubscriptionsService) Unsubscribe

func (s *SubscriptionsService) Unsubscribe(ctx context.Context, recordingID int64) (err error)

Unsubscribe unsubscribes the current user from the recording. Returns nil on success (204 No Content).

func (*SubscriptionsService) Update

func (s *SubscriptionsService) Update(ctx context.Context, recordingID int64, req *UpdateSubscriptionRequest) (result *Subscription, err error)

Update batch modifies subscriptions by adding or removing specific users. Returns the updated subscription information.

type Template

type Template struct {
	ID          int64     `json:"id"`
	Status      string    `json:"status"`
	CreatedAt   time.Time `json:"created_at"`
	UpdatedAt   time.Time `json:"updated_at"`
	Name        string    `json:"name"`
	Description string    `json:"description"`
}

Template represents a Basecamp project template.

type TemplateListResult

type TemplateListResult struct {
	// Templates is the list of templates returned.
	Templates []Template
	// Meta contains pagination metadata (total count, etc.).
	Meta ListMeta
}

TemplateListResult contains the results from listing templates.

type TemplatesService

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

TemplatesService handles template operations.

func NewTemplatesService

func NewTemplatesService(client *AccountClient) *TemplatesService

NewTemplatesService creates a new TemplatesService.

func (*TemplatesService) Create

func (s *TemplatesService) Create(ctx context.Context, req *CreateTemplateRequest) (result *Template, err error)

Create creates a new template. Returns the created template.

func (*TemplatesService) CreateProject

func (s *TemplatesService) CreateProject(ctx context.Context, templateID int64, name, description string) (result *ProjectConstruction, err error)

CreateProject creates a new project from a template. This operation is asynchronous; use GetConstruction to check the status.

func (*TemplatesService) Delete

func (s *TemplatesService) Delete(ctx context.Context, templateID int64) (err error)

Delete deletes a template.

func (*TemplatesService) Get

func (s *TemplatesService) Get(ctx context.Context, templateID int64) (result *Template, err error)

Get returns a template by ID.

func (*TemplatesService) GetConstruction

func (s *TemplatesService) GetConstruction(ctx context.Context, templateID, constructionID int64) (result *ProjectConstruction, err error)

GetConstruction returns the status of a project construction.

func (*TemplatesService) List

func (s *TemplatesService) List(ctx context.Context) (result *TemplateListResult, err error)

List returns all templates visible to the current user.

The returned TemplateListResult includes pagination metadata (TotalCount from X-Total-Count header) when available.

func (*TemplatesService) Update

func (s *TemplatesService) Update(ctx context.Context, templateID int64, req *UpdateTemplateRequest) (result *Template, err error)

Update updates an existing template. Returns the updated template.

type TimelineEvent

type TimelineEvent struct {
	ID                int64     `json:"id"`
	CreatedAt         time.Time `json:"created_at"`
	Kind              string    `json:"kind"`
	ParentRecordingID int64     `json:"parent_recording_id"`
	URL               string    `json:"url"`
	AppURL            string    `json:"app_url"`
	Creator           *Person   `json:"creator,omitempty"`
	Action            string    `json:"action"`
	Target            string    `json:"target"`
	Title             string    `json:"title"`
	SummaryExcerpt    string    `json:"summary_excerpt"`
	Bucket            *Bucket   `json:"bucket,omitempty"`
}

TimelineEvent represents an activity event in the timeline.

type TimelineService

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

TimelineService handles timeline and progress operations.

func NewTimelineService

func NewTimelineService(client *AccountClient) *TimelineService

NewTimelineService creates a new TimelineService.

func (*TimelineService) PersonProgress

func (s *TimelineService) PersonProgress(ctx context.Context, personID int64) (result *PersonProgressResponse, err error)

PersonProgress returns the activity timeline for a specific person.

func (*TimelineService) Progress

func (s *TimelineService) Progress(ctx context.Context) (result []TimelineEvent, err error)

Progress returns the account-wide activity feed. This shows recent activity across all projects.

func (*TimelineService) ProjectTimeline

func (s *TimelineService) ProjectTimeline(ctx context.Context, projectID int64) (result []TimelineEvent, err error)

ProjectTimeline returns the activity timeline for a specific project.

type TimesheetEntry

type TimesheetEntry struct {
	ID             int64     `json:"id"`
	Date           string    `json:"date"`
	Hours          string    `json:"hours"`
	Description    string    `json:"description,omitempty"`
	Creator        *Person   `json:"creator,omitempty"`
	Person         *Person   `json:"person,omitempty"`
	Parent         *Parent   `json:"parent,omitempty"`
	Bucket         *Bucket   `json:"bucket,omitempty"`
	BillableStatus string    `json:"billable_status,omitempty"`
	CreatedAt      time.Time `json:"created_at"`
	UpdatedAt      time.Time `json:"updated_at"`
}

TimesheetEntry represents a single time entry in a Basecamp timesheet report.

type TimesheetReportOptions

type TimesheetReportOptions struct {
	// From filters entries on or after this date (ISO 8601 format, e.g., "2024-01-01").
	From string
	// To filters entries on or before this date (ISO 8601 format, e.g., "2024-01-31").
	To string
	// PersonID filters entries by a specific person.
	PersonID int64
}

TimesheetReportOptions specifies options for timesheet reports.

type TimesheetService

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

TimesheetService handles timesheet report operations.

func NewTimesheetService

func NewTimesheetService(client *AccountClient) *TimesheetService

NewTimesheetService creates a new TimesheetService.

func (*TimesheetService) Create

func (s *TimesheetService) Create(ctx context.Context, recordingID int64, req *CreateTimesheetEntryRequest) (result *TimesheetEntry, err error)

Create creates a timesheet entry on a recording.

func (*TimesheetService) Get

func (s *TimesheetService) Get(ctx context.Context, entryID int64) (result *TimesheetEntry, err error)

Get returns a single timesheet entry.

func (*TimesheetService) ProjectReport

func (s *TimesheetService) ProjectReport(ctx context.Context, projectID int64, opts *TimesheetReportOptions) (result []TimesheetEntry, err error)

ProjectReport returns the timesheet report for a specific project. projectID is the project (bucket) ID.

func (*TimesheetService) RecordingReport

func (s *TimesheetService) RecordingReport(ctx context.Context, recordingID int64, opts *TimesheetReportOptions) (result []TimesheetEntry, err error)

RecordingReport returns the timesheet report for a specific recording. recordingID is the recording ID (e.g., a todo).

func (*TimesheetService) Report

func (s *TimesheetService) Report(ctx context.Context, opts *TimesheetReportOptions) (result []TimesheetEntry, err error)

Report returns the account-wide timesheet report. This includes time entries across all projects in the account.

func (*TimesheetService) Trash

func (s *TimesheetService) Trash(ctx context.Context, entryID int64) (err error)

Trash moves a timesheet entry to the trash.

func (*TimesheetService) Update

func (s *TimesheetService) Update(ctx context.Context, entryID int64, req *UpdateTimesheetEntryRequest) (result *TimesheetEntry, err error)

Update updates an existing timesheet entry.

type Todo

type Todo struct {
	ID          int64      `json:"id"`
	Status      string     `json:"status"`
	VisibleTo   []int64    `json:"visible_to"`
	CreatedAt   time.Time  `json:"created_at"`
	UpdatedAt   time.Time  `json:"updated_at"`
	Title       string     `json:"title"`
	InheritsVis bool       `json:"inherits_status"`
	Type        string     `json:"type"`
	URL         string     `json:"url"`
	AppURL      string     `json:"app_url"`
	BookmarkURL string     `json:"bookmark_url"`
	Parent      *Parent    `json:"parent,omitempty"`
	Bucket      *Bucket    `json:"bucket,omitempty"`
	Creator     *Person    `json:"creator,omitempty"`
	Content     string     `json:"content"`
	Description string     `json:"description"`
	StartsOn    string     `json:"starts_on,omitempty"`
	DueOn       string     `json:"due_on,omitempty"`
	Completed   bool       `json:"completed"`
	BoostsCount int        `json:"boosts_count,omitempty"`
	CompletedAt *time.Time `json:"completed_at,omitempty"`
	Completer   *Person    `json:"completer,omitempty"`
	Assignees   []Person   `json:"assignees,omitempty"`
	Position    int        `json:"position"`
}

Todo represents a Basecamp todo item.

type TodoListOptions

type TodoListOptions struct {
	// Status filters by completion status.
	// "completed" returns completed todos, "pending" returns pending todos.
	// Empty returns all todos.
	Status string

	// Limit is the maximum number of todos to return.
	// If 0, uses DefaultTodoLimit (100). Use -1 for unlimited.
	Limit int

	// Page, if non-zero, disables pagination and returns only the first page.
	// NOTE: The page number itself is not yet honored due to OpenAPI client
	// limitations. Use 0 to paginate through all results up to Limit.
	Page int
}

TodoListOptions specifies options for listing todos.

type TodoListResult

type TodoListResult struct {
	// Todos is the list of todos returned.
	Todos []Todo
	// Meta contains pagination metadata (total count, etc.).
	Meta ListMeta
}

TodoListResult contains the results from listing todos.

type Todolist

type Todolist struct {
	ID               int64     `json:"id"`
	Status           string    `json:"status"`
	VisibleToClients bool      `json:"visible_to_clients"`
	CreatedAt        time.Time `json:"created_at"`
	UpdatedAt        time.Time `json:"updated_at"`
	Title            string    `json:"title"`
	InheritsStatus   bool      `json:"inherits_status"`
	Type             string    `json:"type"`
	URL              string    `json:"url"`
	AppURL           string    `json:"app_url"`
	BookmarkURL      string    `json:"bookmark_url"`
	SubscriptionURL  string    `json:"subscription_url"`
	CommentsCount    int       `json:"comments_count"`
	CommentsURL      string    `json:"comments_url"`
	Position         int       `json:"position"`
	Parent           *Parent   `json:"parent,omitempty"`
	Bucket           *Bucket   `json:"bucket,omitempty"`
	Creator          *Person   `json:"creator,omitempty"`
	Description      string    `json:"description"`
	Completed        bool      `json:"completed"`
	CompletedRatio   string    `json:"completed_ratio"`
	Name             string    `json:"name"`
	TodosURL         string    `json:"todos_url"`
	GroupsURL        string    `json:"groups_url"`
	AppTodosURL      string    `json:"app_todos_url"`
}

Todolist represents a Basecamp todolist.

type TodolistGroup

type TodolistGroup struct {
	ID               int64     `json:"id"`
	Status           string    `json:"status"`
	VisibleToClients bool      `json:"visible_to_clients"`
	CreatedAt        time.Time `json:"created_at"`
	UpdatedAt        time.Time `json:"updated_at"`
	Title            string    `json:"title"`
	InheritsStatus   bool      `json:"inherits_status"`
	Type             string    `json:"type"`
	URL              string    `json:"url"`
	AppURL           string    `json:"app_url"`
	BookmarkURL      string    `json:"bookmark_url"`
	SubscriptionURL  string    `json:"subscription_url"`
	CommentsCount    int       `json:"comments_count"`
	CommentsURL      string    `json:"comments_url"`
	Position         int       `json:"position"`
	Parent           *Parent   `json:"parent,omitempty"`
	Bucket           *Bucket   `json:"bucket,omitempty"`
	Creator          *Person   `json:"creator,omitempty"`
	Name             string    `json:"name"`
	Completed        bool      `json:"completed"`
	CompletedRatio   string    `json:"completed_ratio"`
	TodosURL         string    `json:"todos_url"`
	AppTodosURL      string    `json:"app_todos_url"`
}

TodolistGroup represents a Basecamp todolist group (organizational folder within a todolist).

type TodolistGroupListResult

type TodolistGroupListResult struct {
	// Groups is the list of todolist groups returned.
	Groups []TodolistGroup
	// Meta contains pagination metadata (total count, etc.).
	Meta ListMeta
}

TodolistGroupListResult contains the results from listing todolist groups.

type TodolistGroupsService

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

TodolistGroupsService handles todolist group operations.

func NewTodolistGroupsService

func NewTodolistGroupsService(client *AccountClient) *TodolistGroupsService

NewTodolistGroupsService creates a new TodolistGroupsService.

func (*TodolistGroupsService) Create

func (s *TodolistGroupsService) Create(ctx context.Context, todolistID int64, req *CreateTodolistGroupRequest) (result *TodolistGroup, err error)

Create creates a new group in a todolist. Returns the created group.

func (*TodolistGroupsService) Get

func (s *TodolistGroupsService) Get(ctx context.Context, groupID int64) (result *TodolistGroup, err error)

Get returns a todolist group by ID.

func (*TodolistGroupsService) List

func (s *TodolistGroupsService) List(ctx context.Context, todolistID int64) (result *TodolistGroupListResult, err error)

List returns all groups in a todolist.

The returned TodolistGroupListResult includes pagination metadata (TotalCount from X-Total-Count header) when available.

func (*TodolistGroupsService) Reposition

func (s *TodolistGroupsService) Reposition(ctx context.Context, groupID int64, position int) (err error)

Reposition changes the position of a group within its todolist. position is 1-based (1 = first position).

func (*TodolistGroupsService) Trash

func (s *TodolistGroupsService) Trash(ctx context.Context, groupID int64) (err error)

Trash moves a todolist group to the trash. Trashed groups can be recovered from the trash.

func (*TodolistGroupsService) Update

func (s *TodolistGroupsService) Update(ctx context.Context, groupID int64, req *UpdateTodolistGroupRequest) (result *TodolistGroup, err error)

Update updates an existing todolist group. Returns the updated group.

type TodolistListOptions

type TodolistListOptions struct {
	// Status filters by status: "archived" or "trashed".
	// Empty returns active todolists.
	Status string

	// Limit is the maximum number of todolists to return.
	// If 0 (default), returns all todolists. Use a positive value to cap results.
	Limit int

	// Page, if non-zero, disables pagination and returns only the first page.
	// NOTE: The page number itself is not yet honored due to OpenAPI client
	// limitations. Use 0 to paginate through all results up to Limit.
	Page int
}

TodolistListOptions specifies options for listing todolists.

type TodolistListResult

type TodolistListResult struct {
	// Todolists is the list of todolists returned.
	Todolists []Todolist
	// Meta contains pagination metadata (total count, etc.).
	Meta ListMeta
}

TodolistListResult contains the results from listing todolists.

type TodolistsService

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

TodolistsService handles todolist operations.

func NewTodolistsService

func NewTodolistsService(client *AccountClient) *TodolistsService

NewTodolistsService creates a new TodolistsService.

func (*TodolistsService) Create

func (s *TodolistsService) Create(ctx context.Context, todosetID int64, req *CreateTodolistRequest) (result *Todolist, err error)

Create creates a new todolist in a todoset. Returns the created todolist.

func (*TodolistsService) Get

func (s *TodolistsService) Get(ctx context.Context, todolistID int64) (result *Todolist, err error)

Get returns a todolist by ID.

func (*TodolistsService) List

func (s *TodolistsService) List(ctx context.Context, todosetID int64, opts *TodolistListOptions) (result *TodolistListResult, err error)

List returns todolists in a todoset.

By default, returns all todolists (no limit). Use Limit to cap results.

Pagination options:

  • Limit: maximum number of todolists to return (0 = all)
  • Page: if non-zero, disables pagination and returns first page only

The returned TodolistListResult includes pagination metadata (TotalCount from X-Total-Count header) when available.

func (*TodolistsService) Trash

func (s *TodolistsService) Trash(ctx context.Context, todolistID int64) (err error)

Trash moves a todolist to the trash. Trashed todolists can be recovered from the trash.

func (*TodolistsService) Update

func (s *TodolistsService) Update(ctx context.Context, todolistID int64, req *UpdateTodolistRequest) (result *Todolist, err error)

Update updates an existing todolist. Returns the updated todolist.

type TodosService

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

TodosService handles todo operations.

func NewTodosService

func NewTodosService(client *AccountClient) *TodosService

NewTodosService creates a new TodosService.

func (*TodosService) Complete

func (s *TodosService) Complete(ctx context.Context, todoID int64) (err error)

Complete marks a todo as completed.

Example
package main

import (
	"context"
	"fmt"
	"log"
	"os"

	"github.com/basecamp/basecamp-sdk/go/pkg/basecamp"
)

func main() {
	cfg := basecamp.DefaultConfig()
	token := &basecamp.StaticTokenProvider{Token: os.Getenv("BASECAMP_TOKEN")}
	client := basecamp.NewClient(cfg, token)

	ctx := context.Background()

	todoID := int64(789012)

	// Mark a todo as complete
	err := client.ForAccount("12345").Todos().Complete(ctx, todoID)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("Todo completed")
}

func (*TodosService) Create

func (s *TodosService) Create(ctx context.Context, todolistID int64, req *CreateTodoRequest) (result *Todo, err error)

Create creates a new todo in a todolist. Returns the created todo.

Example
package main

import (
	"context"
	"fmt"
	"log"
	"os"

	"github.com/basecamp/basecamp-sdk/go/pkg/basecamp"
)

func main() {
	cfg := basecamp.DefaultConfig()
	token := &basecamp.StaticTokenProvider{Token: os.Getenv("BASECAMP_TOKEN")}
	client := basecamp.NewClient(cfg, token)

	ctx := context.Background()

	todolistID := int64(789012)

	// Create a new todo with assignees and due date
	todo, err := client.ForAccount("12345").Todos().Create(ctx, todolistID, &basecamp.CreateTodoRequest{
		Content:     "Review pull request",
		Description: "<strong>Priority:</strong> High",
		DueOn:       "2024-12-31",
		AssigneeIDs: []int64{111, 222},
		Notify:      true,
	})
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Created todo: %s (ID: %d)\n", todo.Content, todo.ID)
}

func (*TodosService) Get

func (s *TodosService) Get(ctx context.Context, todoID int64) (result *Todo, err error)

Get returns a todo by ID.

func (*TodosService) List

func (s *TodosService) List(ctx context.Context, todolistID int64, opts *TodoListOptions) (result *TodoListResult, err error)

List returns todos in a todolist.

By default, returns up to 100 todos. Use Limit: -1 for unlimited.

Pagination options:

  • Limit: maximum number of todos to return (0 = 100, -1 = unlimited)
  • Page: if non-zero, disables pagination and returns first page only

The returned TodoListResult includes pagination metadata (TotalCount from X-Total-Count header) when available.

Example
package main

import (
	"context"
	"fmt"
	"log"
	"os"

	"github.com/basecamp/basecamp-sdk/go/pkg/basecamp"
)

func main() {
	cfg := basecamp.DefaultConfig()
	token := &basecamp.StaticTokenProvider{Token: os.Getenv("BASECAMP_TOKEN")}
	client := basecamp.NewClient(cfg, token)

	ctx := context.Background()

	todolistID := int64(789012)

	// List all todos in a todolist
	todosResult, err := client.ForAccount("12345").Todos().List(ctx, todolistID, nil)
	if err != nil {
		log.Fatal(err)
	}

	for _, t := range todosResult.Todos {
		status := "[ ]"
		if t.Completed {
			status = "[x]"
		}
		fmt.Printf("%s %s\n", status, t.Content)
	}
}

func (*TodosService) Reposition

func (s *TodosService) Reposition(ctx context.Context, todoID int64, position int) (err error)

Reposition changes the position of a todo within its todolist. position is 1-based (1 = first position).

func (*TodosService) Trash

func (s *TodosService) Trash(ctx context.Context, todoID int64) (err error)

Trash moves a todo to the trash. Trashed todos can be recovered from the trash.

func (*TodosService) Uncomplete

func (s *TodosService) Uncomplete(ctx context.Context, todoID int64) (err error)

Uncomplete marks a completed todo as incomplete (reopens it).

func (*TodosService) Update

func (s *TodosService) Update(ctx context.Context, todoID int64, req *UpdateTodoRequest) (result *Todo, err error)

Update updates an existing todo. Returns the updated todo.

type Todoset

type Todoset struct {
	ID                int64     `json:"id"`
	Status            string    `json:"status"`
	VisibleToClients  bool      `json:"visible_to_clients"`
	CreatedAt         time.Time `json:"created_at"`
	UpdatedAt         time.Time `json:"updated_at"`
	Title             string    `json:"title"`
	InheritsStatus    bool      `json:"inherits_status"`
	Type              string    `json:"type"`
	URL               string    `json:"url"`
	AppURL            string    `json:"app_url"`
	BookmarkURL       string    `json:"bookmark_url"`
	Position          *int      `json:"position,omitempty"`
	Bucket            *Bucket   `json:"bucket,omitempty"`
	Creator           *Person   `json:"creator,omitempty"`
	Name              string    `json:"name"`
	TodolistsCount    int       `json:"todolists_count"`
	TodolistsURL      string    `json:"todolists_url"`
	CompletedRatio    string    `json:"completed_ratio"`
	Completed         bool      `json:"completed"`
	CompletedCount    int       `json:"completed_count"`
	OnScheduleCount   int       `json:"on_schedule_count"`
	OverScheduleCount int       `json:"over_schedule_count"`
	AppTodolistsURL   string    `json:"app_todolists_url"`
}

Todoset represents a Basecamp todoset (container for todolists in a project). Each project has exactly one todoset in its dock.

type TodosetsService

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

TodosetsService handles todoset operations.

func NewTodosetsService

func NewTodosetsService(client *AccountClient) *TodosetsService

NewTodosetsService creates a new TodosetsService.

func (*TodosetsService) Get

func (s *TodosetsService) Get(ctx context.Context, todosetID int64) (result *Todoset, err error)

Get returns a todoset by ID.

type TokenProvider

type TokenProvider interface {
	// AccessToken returns a valid access token, refreshing if needed.
	AccessToken(ctx context.Context) (string, error)
}

TokenProvider is the interface for obtaining access tokens.

type Tool

type Tool struct {
	ID        int64     `json:"id"`
	Status    string    `json:"status"`
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
	Title     string    `json:"title"`
	Name      string    `json:"name"`
	Enabled   bool      `json:"enabled"`
	Position  *int      `json:"position"`
	URL       string    `json:"url"`
	AppURL    string    `json:"app_url"`
	Bucket    *Bucket   `json:"bucket,omitempty"`
}

Tool represents a dock tool in a Basecamp project.

type ToolsService

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

ToolsService handles dock tool operations.

func NewToolsService

func NewToolsService(client *AccountClient) *ToolsService

NewToolsService creates a new ToolsService.

func (*ToolsService) Create

func (s *ToolsService) Create(ctx context.Context, sourceToolID int64) (result *Tool, err error)

Create clones an existing tool to create a new one. Returns the newly created tool.

func (*ToolsService) Delete

func (s *ToolsService) Delete(ctx context.Context, toolID int64) (err error)

Delete moves a tool to the trash. Trashed tools can be recovered from the trash.

func (*ToolsService) Disable

func (s *ToolsService) Disable(ctx context.Context, toolID int64) (err error)

Disable disables (hides) a tool from the project dock. The tool is not deleted, just hidden from the dock.

func (*ToolsService) Enable

func (s *ToolsService) Enable(ctx context.Context, toolID int64) (err error)

Enable enables (shows) a tool on the project dock. The tool will be placed at the end of the dock.

func (*ToolsService) Get

func (s *ToolsService) Get(ctx context.Context, toolID int64) (result *Tool, err error)

Get returns a tool by ID.

func (*ToolsService) Reposition

func (s *ToolsService) Reposition(ctx context.Context, toolID int64, position int) (err error)

Reposition changes the position of a tool on the project dock. position is 1-based (1 = first position on dock).

func (*ToolsService) Update

func (s *ToolsService) Update(ctx context.Context, toolID int64, title string) (result *Tool, err error)

Update updates (renames) an existing tool. Returns the updated tool.

type UpcomingScheduleResponse

type UpcomingScheduleResponse struct {
	ScheduleEntries      []ScheduleEntry `json:"schedule_entries"`
	RecurringOccurrences []ScheduleEntry `json:"recurring_schedule_entry_occurrences"`
	Assignables          []Assignable    `json:"assignables"`
}

UpcomingScheduleResponse contains upcoming schedule entries.

type UpdateAnswerRequest

type UpdateAnswerRequest struct {
	// Content is the updated answer content in HTML (required).
	Content string `json:"content"`
}

UpdateAnswerRequest specifies the parameters for updating an answer.

type UpdateCardRequest

type UpdateCardRequest struct {
	// Title is the card title (optional).
	Title string `json:"title,omitempty"`
	// Content is the card body in HTML (optional).
	Content string `json:"content,omitempty"`
	// DueOn is the due date in ISO 8601 format (optional).
	DueOn string `json:"due_on,omitempty"`
	// AssigneeIDs is a list of person IDs to assign this card to (optional).
	AssigneeIDs []int64 `json:"assignee_ids,omitempty"`
}

UpdateCardRequest specifies the parameters for updating a card.

type UpdateChatbotRequest

type UpdateChatbotRequest struct {
	// ServiceName is the chatbot name used to invoke queries and commands (required).
	// No spaces, emoji or non-word characters are allowed.
	ServiceName string `json:"service_name"`
	// CommandURL is the HTTPS URL that Basecamp should call when the bot is addressed (optional).
	CommandURL string `json:"command_url,omitempty"`
}

UpdateChatbotRequest specifies the parameters for updating a chatbot.

type UpdateColumnRequest

type UpdateColumnRequest struct {
	// Title is the column title (optional).
	Title string `json:"title,omitempty"`
	// Description is the column description (optional).
	Description string `json:"description,omitempty"`
}

UpdateColumnRequest specifies the parameters for updating a column.

type UpdateCommentRequest

type UpdateCommentRequest struct {
	// Content is the comment text in HTML (required).
	Content string `json:"content"`
}

UpdateCommentRequest specifies the parameters for updating a comment.

type UpdateDocumentRequest

type UpdateDocumentRequest struct {
	// Title is the document title.
	Title string `json:"title,omitempty"`
	// Content is the document body in HTML.
	Content string `json:"content,omitempty"`
}

UpdateDocumentRequest specifies the parameters for updating a document.

type UpdateMarkerRequest

type UpdateMarkerRequest struct {
	// Name is the marker name (optional).
	Name string `json:"name,omitempty"`
	// Date is the marker date in YYYY-MM-DD format (optional).
	Date string `json:"date,omitempty"`
}

UpdateMarkerRequest specifies the parameters for updating a lineup marker.

type UpdateMessageRequest

type UpdateMessageRequest struct {
	// Subject is the message title (optional).
	Subject string `json:"subject,omitempty"`
	// Content is the message body in HTML (optional).
	Content string `json:"content,omitempty"`
	// Status is either "drafted" or "active" (optional).
	Status string `json:"status,omitempty"`
	// CategoryID is the message type ID (optional).
	CategoryID int64 `json:"category_id,omitempty"`
}

UpdateMessageRequest specifies the parameters for updating a message.

type UpdateMessageTypeRequest

type UpdateMessageTypeRequest struct {
	// Name is the message type name (optional).
	Name string `json:"name,omitempty"`
	// Icon is the message type icon (optional).
	Icon string `json:"icon,omitempty"`
}

UpdateMessageTypeRequest specifies the parameters for updating a message type.

type UpdateProjectAccessRequest

type UpdateProjectAccessRequest struct {
	// Grant is a list of person IDs to grant access to the project.
	Grant []int64 `json:"grant,omitempty"`
	// Revoke is a list of person IDs to revoke access from the project.
	Revoke []int64 `json:"revoke,omitempty"`
	// Create is a list of new people to create and grant access.
	Create []CreatePersonRequest `json:"create,omitempty"`
}

UpdateProjectAccessRequest specifies the parameters for updating project access.

type UpdateProjectAccessResponse

type UpdateProjectAccessResponse struct {
	// Granted is the list of people who were granted access.
	Granted []Person `json:"granted"`
	// Revoked is the list of people whose access was revoked.
	Revoked []Person `json:"revoked"`
}

UpdateProjectAccessResponse is the response from updating project access.

type UpdateProjectRequest

type UpdateProjectRequest struct {
	// Name is the project name (required for update).
	Name string `json:"name"`
	// Description is an optional project description.
	Description string `json:"description,omitempty"`
	// Admissions specifies access policy (invite, employee, team).
	Admissions string `json:"admissions,omitempty"`
	// ScheduleAttributes sets project start and end dates.
	ScheduleAttributes *ScheduleAttributes `json:"schedule_attributes,omitempty"`
}

UpdateProjectRequest specifies the parameters for updating a project.

type UpdateQuestionRequest

type UpdateQuestionRequest struct {
	// Title is the question text.
	Title string `json:"title,omitempty"`
	// Schedule is the question schedule configuration.
	Schedule *QuestionSchedule `json:"schedule,omitempty"`
	// Paused indicates whether the question is paused.
	Paused *bool `json:"paused,omitempty"`
}

UpdateQuestionRequest specifies the parameters for updating a question.

type UpdateScheduleEntryRequest

type UpdateScheduleEntryRequest struct {
	// Summary is the event title (optional).
	Summary string `json:"summary,omitempty"`
	// StartsAt is the event start time (optional, ISO 8601 format).
	StartsAt string `json:"starts_at,omitempty"`
	// EndsAt is the event end time (optional, ISO 8601 format).
	EndsAt string `json:"ends_at,omitempty"`
	// Description is the event details in HTML (optional).
	Description string `json:"description,omitempty"`
	// ParticipantIDs is a list of people IDs to assign (optional).
	ParticipantIDs []int64 `json:"participant_ids,omitempty"`
	// AllDay indicates if this is an all-day event (optional).
	AllDay bool `json:"all_day,omitempty"`
	// Notify triggers participant notifications when true (optional).
	Notify bool `json:"notify,omitempty"`
}

UpdateScheduleEntryRequest specifies the parameters for updating a schedule entry.

type UpdateScheduleSettingsRequest

type UpdateScheduleSettingsRequest struct {
	// IncludeDueAssignments controls whether to-do due dates appear on the schedule.
	IncludeDueAssignments bool `json:"include_due_assignments"`
}

UpdateScheduleSettingsRequest specifies the parameters for updating schedule settings.

type UpdateStepRequest

type UpdateStepRequest struct {
	// Title is the step title (optional).
	Title string `json:"title,omitempty"`
	// DueOn is the due date in ISO 8601 format (optional).
	DueOn string `json:"due_on,omitempty"`
	// Assignees is a list of person IDs to assign this step to (optional).
	Assignees []int64 `json:"assignees,omitempty"`
}

UpdateStepRequest specifies the parameters for updating a step.

type UpdateSubscriptionRequest

type UpdateSubscriptionRequest struct {
	// Subscriptions is a list of person IDs to subscribe to the recording.
	Subscriptions []int64 `json:"subscriptions,omitempty"`
	// Unsubscriptions is a list of person IDs to unsubscribe from the recording.
	Unsubscriptions []int64 `json:"unsubscriptions,omitempty"`
}

UpdateSubscriptionRequest specifies the parameters for updating subscriptions.

type UpdateTemplateRequest

type UpdateTemplateRequest struct {
	// Name is the template name (required for update).
	Name string `json:"name"`
	// Description is an optional template description.
	Description string `json:"description,omitempty"`
}

UpdateTemplateRequest specifies the parameters for updating a template.

type UpdateTimesheetEntryRequest

type UpdateTimesheetEntryRequest struct {
	Date        string `json:"date,omitempty"`
	Hours       string `json:"hours,omitempty"`
	Description string `json:"description,omitempty"`
	PersonID    int64  `json:"person_id,omitempty"`
}

UpdateTimesheetEntryRequest specifies the parameters for updating a timesheet entry.

type UpdateTodoRequest

type UpdateTodoRequest struct {
	// Content is the todo text.
	Content string `json:"content,omitempty"`
	// Description is an optional extended description (can include HTML).
	Description string `json:"description,omitempty"`
	// AssigneeIDs is a list of person IDs to assign this todo to.
	AssigneeIDs []int64 `json:"assignee_ids,omitempty"`
	// CompletionSubscriberIDs is a list of person IDs to notify on completion.
	CompletionSubscriberIDs []int64 `json:"completion_subscriber_ids,omitempty"`
	// Notify when true, will notify assignees.
	Notify bool `json:"notify,omitempty"`
	// DueOn is the due date in ISO 8601 format (YYYY-MM-DD).
	DueOn string `json:"due_on,omitempty"`
	// StartsOn is the start date in ISO 8601 format (YYYY-MM-DD).
	StartsOn string `json:"starts_on,omitempty"`
}

UpdateTodoRequest specifies the parameters for updating a todo.

type UpdateTodolistGroupRequest

type UpdateTodolistGroupRequest struct {
	// Name is the group name.
	Name string `json:"name,omitempty"`
}

UpdateTodolistGroupRequest specifies the parameters for updating a todolist group.

type UpdateTodolistRequest

type UpdateTodolistRequest struct {
	// Name is the todolist name.
	Name string `json:"name,omitempty"`
	// Description is an optional description (can include HTML).
	Description string `json:"description,omitempty"`
}

UpdateTodolistRequest specifies the parameters for updating a todolist.

type UpdateToolRequest

type UpdateToolRequest struct {
	// Title is the new title for the tool (required).
	Title string `json:"title"`
}

UpdateToolRequest specifies the parameters for updating (renaming) a tool.

type UpdateUploadRequest

type UpdateUploadRequest struct {
	// Description is the upload description.
	Description string `json:"description,omitempty"`
	// BaseName is the filename without extension.
	BaseName string `json:"base_name,omitempty"`
}

UpdateUploadRequest specifies the parameters for updating an upload.

type UpdateVaultRequest

type UpdateVaultRequest struct {
	// Title is the vault name.
	Title string `json:"title,omitempty"`
}

UpdateVaultRequest specifies the parameters for updating a vault.

type UpdateWebhookRequest

type UpdateWebhookRequest struct {
	// PayloadURL is the URL to receive webhook payloads.
	PayloadURL string `json:"payload_url,omitempty"`
	// Types is a list of event types to subscribe to.
	Types []string `json:"types,omitempty"`
	// Active indicates whether the webhook is active.
	Active *bool `json:"active,omitempty"`
}

UpdateWebhookRequest specifies the parameters for updating a webhook.

type Upload

type Upload struct {
	ID               int64     `json:"id"`
	Status           string    `json:"status"`
	VisibleToClients bool      `json:"visible_to_clients"`
	CreatedAt        time.Time `json:"created_at"`
	UpdatedAt        time.Time `json:"updated_at"`
	Title            string    `json:"title"`
	InheritsStatus   bool      `json:"inherits_status"`
	Type             string    `json:"type"`
	URL              string    `json:"url"`
	AppURL           string    `json:"app_url"`
	BookmarkURL      string    `json:"bookmark_url"`
	SubscriptionURL  string    `json:"subscription_url"`
	CommentsCount    int       `json:"comments_count"`
	BoostsCount      int       `json:"boosts_count,omitempty"`
	CommentsURL      string    `json:"comments_url"`
	Position         int       `json:"position,omitempty"`
	Parent           *Parent   `json:"parent,omitempty"`
	Bucket           *Bucket   `json:"bucket,omitempty"`
	Creator          *Person   `json:"creator,omitempty"`
	Description      string    `json:"description"`
	ContentType      string    `json:"content_type"`
	ByteSize         int64     `json:"byte_size"`
	Width            int       `json:"width,omitempty"`
	Height           int       `json:"height,omitempty"`
	DownloadURL      string    `json:"download_url"`
	Filename         string    `json:"filename"`
}

Upload represents a Basecamp uploaded file in a vault.

type UploadListOptions

type UploadListOptions struct {
	// Limit is the maximum number of uploads to return.
	// If 0 (default), returns all uploads. Use a positive value to cap results.
	Limit int

	// Page, if non-zero, disables pagination and returns only the first page.
	// NOTE: The page number itself is not yet honored due to OpenAPI client
	// limitations. Use 0 to paginate through all results up to Limit.
	Page int
}

UploadListOptions specifies options for listing uploads.

type UploadListResult

type UploadListResult struct {
	// Uploads is the list of uploads returned.
	Uploads []Upload
	// Meta contains pagination metadata (total count, etc.).
	Meta ListMeta
}

UploadListResult contains the results from listing uploads.

type UploadsService

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

UploadsService handles upload (file) operations.

func NewUploadsService

func NewUploadsService(client *AccountClient) *UploadsService

NewUploadsService creates a new UploadsService.

func (*UploadsService) Create

func (s *UploadsService) Create(ctx context.Context, vaultID int64, req *CreateUploadRequest) (result *Upload, err error)

Create creates a new upload in a vault. The attachable_sgid must be obtained from the Create Attachment endpoint. Returns the created upload.

func (*UploadsService) Download

func (s *UploadsService) Download(ctx context.Context, uploadID int64) (result *DownloadResult, err error)

Download fetches the file content from an upload's download URL. The caller is responsible for closing the returned Body.

This method first fetches the upload metadata to get the download URL, then fetches the file content from that URL. The download URL is a signed S3 URL that doesn't require authentication headers.

func (*UploadsService) Get

func (s *UploadsService) Get(ctx context.Context, uploadID int64) (result *Upload, err error)

Get returns an upload by ID.

func (*UploadsService) List

func (s *UploadsService) List(ctx context.Context, vaultID int64, opts *UploadListOptions) (result *UploadListResult, err error)

List returns all uploads in a vault.

By default, returns all uploads (no limit). Use Limit to cap results.

Pagination options:

  • Limit: maximum number of uploads to return (0 = all)
  • Page: if non-zero, disables pagination and returns first page only

The returned UploadListResult includes pagination metadata (TotalCount from X-Total-Count header) when available.

func (*UploadsService) ListVersions

func (s *UploadsService) ListVersions(ctx context.Context, uploadID int64) (result []Upload, err error)

ListVersions returns all versions of an upload.

func (*UploadsService) Trash

func (s *UploadsService) Trash(ctx context.Context, uploadID int64) (err error)

Trash moves an upload to the trash. Trashed uploads can be recovered from the trash.

func (*UploadsService) Update

func (s *UploadsService) Update(ctx context.Context, uploadID int64, req *UpdateUploadRequest) (result *Upload, err error)

Update updates an existing upload. Returns the updated upload.

type UpstreamRef

type UpstreamRef struct {
	Revision string `json:"revision"`
	Date     string `json:"date"`
}

UpstreamRef is a git revision and the date it was synced.

type Vault

type Vault struct {
	ID               int64     `json:"id"`
	Status           string    `json:"status"`
	VisibleToClients bool      `json:"visible_to_clients"`
	CreatedAt        time.Time `json:"created_at"`
	UpdatedAt        time.Time `json:"updated_at"`
	Title            string    `json:"title"`
	InheritsStatus   bool      `json:"inherits_status"`
	Type             string    `json:"type"`
	URL              string    `json:"url"`
	AppURL           string    `json:"app_url"`
	BookmarkURL      string    `json:"bookmark_url"`
	Position         int       `json:"position,omitempty"`
	Parent           *Parent   `json:"parent,omitempty"`
	Bucket           *Bucket   `json:"bucket,omitempty"`
	Creator          *Person   `json:"creator,omitempty"`
	DocumentsCount   int       `json:"documents_count"`
	DocumentsURL     string    `json:"documents_url"`
	UploadsCount     int       `json:"uploads_count"`
	UploadsURL       string    `json:"uploads_url"`
	VaultsCount      int       `json:"vaults_count"`
	VaultsURL        string    `json:"vaults_url"`
}

Vault represents a Basecamp vault (folder) in the Files tool.

type VaultListOptions

type VaultListOptions struct {
	// Limit is the maximum number of vaults to return.
	// If 0 (default), returns all vaults. Use a positive value to cap results.
	Limit int

	// Page, if non-zero, disables pagination and returns only the first page.
	// NOTE: The page number itself is not yet honored due to OpenAPI client
	// limitations. Use 0 to paginate through all results up to Limit.
	Page int
}

VaultListOptions specifies options for listing vaults.

type VaultListResult

type VaultListResult struct {
	// Vaults is the list of vaults returned.
	Vaults []Vault
	// Meta contains pagination metadata (total count, etc.).
	Meta ListMeta
}

VaultListResult contains the results from listing vaults.

type VaultsService

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

VaultsService handles vault (folder) operations.

func NewVaultsService

func NewVaultsService(client *AccountClient) *VaultsService

NewVaultsService creates a new VaultsService.

func (*VaultsService) Create

func (s *VaultsService) Create(ctx context.Context, vaultID int64, req *CreateVaultRequest) (result *Vault, err error)

Create creates a new subfolder (child vault) in a vault. Returns the created vault.

func (*VaultsService) Get

func (s *VaultsService) Get(ctx context.Context, vaultID int64) (result *Vault, err error)

Get returns a vault by ID.

func (*VaultsService) List

func (s *VaultsService) List(ctx context.Context, vaultID int64, opts *VaultListOptions) (result *VaultListResult, err error)

List returns all subfolders (child vaults) in a vault.

By default, returns all vaults (no limit). Use Limit to cap results.

Pagination options:

  • Limit: maximum number of vaults to return (0 = all)
  • Page: if non-zero, disables pagination and returns first page only

The returned VaultListResult includes pagination metadata (TotalCount from X-Total-Count header) when available.

func (*VaultsService) Update

func (s *VaultsService) Update(ctx context.Context, vaultID int64, req *UpdateVaultRequest) (result *Vault, err error)

Update updates an existing vault. Returns the updated vault.

type Webhook

type Webhook struct {
	ID               int64             `json:"id"`
	Active           bool              `json:"active"`
	CreatedAt        time.Time         `json:"created_at"`
	UpdatedAt        time.Time         `json:"updated_at"`
	PayloadURL       string            `json:"payload_url"`
	Types            []string          `json:"types"`
	AppURL           string            `json:"app_url,omitempty"`
	URL              string            `json:"url,omitempty"`
	RecentDeliveries []WebhookDelivery `json:"recent_deliveries,omitempty"`
}

Webhook represents a Basecamp webhook subscription.

type WebhookCopy

type WebhookCopy struct {
	ID     int64             `json:"id"`
	URL    string            `json:"url"`
	AppURL string            `json:"app_url"`
	Bucket WebhookCopyBucket `json:"bucket"`
}

WebhookCopy contains copy/move reference data.

type WebhookCopyBucket

type WebhookCopyBucket struct {
	ID int64 `json:"id"`
}

WebhookCopyBucket is the bucket reference in a webhook copy.

type WebhookDelivery

type WebhookDelivery struct {
	ID        int64                   `json:"id"`
	CreatedAt time.Time               `json:"created_at"`
	Request   WebhookDeliveryRequest  `json:"request"`
	Response  WebhookDeliveryResponse `json:"response"`
}

WebhookDelivery represents a recent delivery attempt for a webhook.

type WebhookDeliveryRequest

type WebhookDeliveryRequest struct {
	Headers map[string]string `json:"headers"`
	Body    WebhookEvent      `json:"body"`
}

WebhookDeliveryRequest contains the outbound request details.

type WebhookDeliveryResponse

type WebhookDeliveryResponse struct {
	Headers map[string]string `json:"headers"`
	Code    int               `json:"code"`
	Message string            `json:"message"`
}

WebhookDeliveryResponse contains the response from the webhook endpoint.

type WebhookEvent

type WebhookEvent struct {
	ID        int64                 `json:"id"`
	Kind      string                `json:"kind"`
	Details   any                   `json:"details"`
	CreatedAt string                `json:"created_at"`
	Recording WebhookEventRecording `json:"recording"`
	Creator   WebhookEventPerson    `json:"creator"`
	Copy      *WebhookCopy          `json:"copy,omitempty"`
}

WebhookEvent is the payload delivered by Basecamp webhooks.

type WebhookEventBucket

type WebhookEventBucket struct {
	ID   int64  `json:"id"`
	Name string `json:"name"`
	Type string `json:"type"`
}

WebhookEventBucket is the bucket (project) in webhook event payloads.

type WebhookEventCompany

type WebhookEventCompany struct {
	ID   int64  `json:"id"`
	Name string `json:"name"`
}

WebhookEventCompany is a company embedded in a webhook event person.

type WebhookEventHandler

type WebhookEventHandler func(event *WebhookEvent) error

WebhookEventHandler handles a parsed webhook event.

type WebhookEventParent

type WebhookEventParent struct {
	ID     int64  `json:"id"`
	Title  string `json:"title"`
	Type   string `json:"type"`
	URL    string `json:"url"`
	AppURL string `json:"app_url"`
}

WebhookEventParent is the parent recording in webhook event payloads.

type WebhookEventPerson

type WebhookEventPerson struct {
	ID                  int64                `json:"id"`
	AttachableSGID      string               `json:"attachable_sgid"`
	Name                string               `json:"name"`
	EmailAddress        string               `json:"email_address"`
	PersonableType      string               `json:"personable_type"`
	Title               string               `json:"title"`
	Bio                 *string              `json:"bio"`
	Location            *string              `json:"location"`
	CreatedAt           string               `json:"created_at"`
	UpdatedAt           string               `json:"updated_at"`
	Admin               bool                 `json:"admin"`
	Owner               bool                 `json:"owner"`
	Client              bool                 `json:"client"`
	Employee            bool                 `json:"employee"`
	TimeZone            string               `json:"time_zone"`
	AvatarURL           string               `json:"avatar_url"`
	Company             *WebhookEventCompany `json:"company,omitempty"`
	CanManageProjects   bool                 `json:"can_manage_projects"`
	CanManagePeople     bool                 `json:"can_manage_people"`
	CanPing             bool                 `json:"can_ping"`
	CanAccessTimesheet  bool                 `json:"can_access_timesheet"`
	CanAccessHillCharts bool                 `json:"can_access_hill_charts"`
}

WebhookEventPerson is a person in webhook event payloads.

type WebhookEventRecording

type WebhookEventRecording struct {
	ID               int64               `json:"id"`
	Status           string              `json:"status"`
	VisibleToClients bool                `json:"visible_to_clients"`
	CreatedAt        string              `json:"created_at"`
	UpdatedAt        string              `json:"updated_at"`
	Title            string              `json:"title"`
	InheritsStatus   bool                `json:"inherits_status"`
	Type             string              `json:"type"`
	URL              string              `json:"url"`
	AppURL           string              `json:"app_url"`
	BookmarkURL      string              `json:"bookmark_url"`
	Content          string              `json:"content"`
	CommentsCount    int                 `json:"comments_count"`
	CommentsURL      string              `json:"comments_url"`
	SubscriptionURL  string              `json:"subscription_url"`
	Parent           *WebhookEventParent `json:"parent,omitempty"`
	Bucket           *WebhookEventBucket `json:"bucket,omitempty"`
	Creator          *WebhookEventPerson `json:"creator,omitempty"`
}

WebhookEventRecording is the recording included in webhook event payloads. It has additional fields beyond what the API returns for recordings.

type WebhookListResult

type WebhookListResult struct {
	// Webhooks is the list of webhooks returned.
	Webhooks []Webhook
	// Meta contains pagination metadata (total count, etc.).
	Meta ListMeta
}

WebhookListResult contains the results from listing webhooks.

type WebhookMiddleware

type WebhookMiddleware func(event *WebhookEvent, next func() error) error

WebhookMiddleware wraps webhook event processing.

type WebhookReceiver

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

WebhookReceiver receives and routes webhook events from Basecamp. It implements http.Handler for direct use as an HTTP endpoint.

func NewWebhookReceiver

func NewWebhookReceiver(config WebhookReceiverConfig) *WebhookReceiver

NewWebhookReceiver creates a new WebhookReceiver with the given config.

func (*WebhookReceiver) HandleRequest

func (r *WebhookReceiver) HandleRequest(body []byte, getHeader func(string) string) (*WebhookEvent, error)

HandleRequest processes a raw webhook request body and headers. Returns the parsed WebhookEvent, or an error if verification/parsing fails. Duplicate events (by ID) return the parsed event but do not trigger handlers.

func (*WebhookReceiver) On

func (r *WebhookReceiver) On(pattern string, handler WebhookEventHandler)

On registers a handler for a specific event kind pattern. Patterns support simple glob matching: "todo_*" matches "todo_created", "todo_completed", etc. "*_created" matches "todo_created", "message_created", etc.

func (*WebhookReceiver) OnAny

func (r *WebhookReceiver) OnAny(handler WebhookEventHandler)

OnAny registers a handler for all webhook events.

func (*WebhookReceiver) ServeHTTP

func (r *WebhookReceiver) ServeHTTP(w http.ResponseWriter, req *http.Request)

ServeHTTP implements http.Handler.

func (*WebhookReceiver) Use

func (r *WebhookReceiver) Use(middleware WebhookMiddleware)

Use adds a middleware to the processing chain.

type WebhookReceiverConfig

type WebhookReceiverConfig struct {
	// Secret is the HMAC secret for signature verification. If empty, verification is skipped.
	Secret string
	// SignatureHeader is the HTTP header containing the signature (default: "X-Basecamp-Signature").
	SignatureHeader string
	// MaxBodyBytes limits the request body size (default: 1MB).
	MaxBodyBytes int64
	// DedupWindowSize is the number of recent event IDs to track for deduplication (default: 1000, -1 to disable).
	DedupWindowSize int
}

WebhookReceiverConfig configures a WebhookReceiver.

type WebhookVerificationError

type WebhookVerificationError struct {
	Message string
}

WebhookVerificationError indicates a signature verification failure.

func (*WebhookVerificationError) Error

func (e *WebhookVerificationError) Error() string

type WebhooksService

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

WebhooksService handles webhook operations.

func NewWebhooksService

func NewWebhooksService(client *AccountClient) *WebhooksService

NewWebhooksService creates a new WebhooksService.

func (*WebhooksService) Create

func (s *WebhooksService) Create(ctx context.Context, bucketID int64, req *CreateWebhookRequest) (result *Webhook, err error)

Create creates a new webhook for a project (bucket). Returns the created webhook.

Example
package main

import (
	"context"
	"fmt"
	"log"
	"os"

	"github.com/basecamp/basecamp-sdk/go/pkg/basecamp"
)

func main() {
	cfg := basecamp.DefaultConfig()
	token := &basecamp.StaticTokenProvider{Token: os.Getenv("BASECAMP_TOKEN")}
	client := basecamp.NewClient(cfg, token)

	ctx := context.Background()

	// Create a webhook to receive notifications
	var bucketID int64 = 67890
	webhook, err := client.ForAccount("12345").Webhooks().Create(ctx, bucketID, &basecamp.CreateWebhookRequest{
		PayloadURL: "https://example.com/webhooks/basecamp",
		Types:      []string{"Todo", "Comment"},
	})
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Webhook created: %s\n", webhook.PayloadURL)
}

func (*WebhooksService) Delete

func (s *WebhooksService) Delete(ctx context.Context, webhookID int64) (err error)

Delete removes a webhook.

func (*WebhooksService) Get

func (s *WebhooksService) Get(ctx context.Context, webhookID int64) (result *Webhook, err error)

Get returns a webhook by ID.

func (*WebhooksService) List

func (s *WebhooksService) List(ctx context.Context, bucketID int64) (result *WebhookListResult, err error)

List returns all webhooks for a project (bucket).

The returned WebhookListResult includes pagination metadata (TotalCount from X-Total-Count header) when available.

func (*WebhooksService) Update

func (s *WebhooksService) Update(ctx context.Context, webhookID int64, req *UpdateWebhookRequest) (result *Webhook, err error)

Update updates an existing webhook. Returns the updated webhook.

Directories

Path Synopsis
Package oauth provides OAuth 2.0 discovery, token exchange, and refresh functionality.
Package oauth provides OAuth 2.0 discovery, token exchange, and refresh functionality.
Package otel provides OpenTelemetry integration for the Basecamp SDK.
Package otel provides OpenTelemetry integration for the Basecamp SDK.
Package prometheus provides Prometheus metrics integration for the Basecamp SDK.
Package prometheus provides Prometheus metrics integration for the Basecamp SDK.

Jump to

Keyboard shortcuts

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