The official Go SDK for the Teamwork.com API
Build powerful integrations with Teamwork's project management platform
Warning
🚧 Under Active Development
This library is currently under active development. APIs and interfaces may change without notice in future versions. Please use with caution in production environments and pin to specific versions to avoid breaking changes.
- � Multiple Authentication Methods - Bearer token, Basic auth, and OAuth2
- 🏗️ Type-Safe API - Fully typed requests and responses
- 🌐 Context Support - Built-in context.Context support for cancellation and timeouts
- 📦 Zero Dependencies - Minimal external dependencies
- 🧪 Thoroughly Tested - Comprehensive test coverage
- 📱 Cross-Platform - Works on Windows, macOS, and Linux
Add this library as a dependency to your Go module:
go get github.com/teamwork/twapi-go-sdk
Requirements:
- Go 1.24 or later
- A Teamwork.com account with API access
The SDK supports multiple authentication methods to suit different use cases:
Perfect for server-to-server integrations and scripts:
import "github.com/teamwork/twapi-go-sdk/session"
session := session.NewBearerToken("your_api_token", "https://yourdomain.teamwork.com")
Use with API tokens or user credentials:
// With API token
session := session.NewBasicAuth("your_api_token", "", "https://yourdomain.teamwork.com")
// With username/password
session := session.NewBasicAuth("username", "password", "https://yourdomain.teamwork.com")
Ideal for user-facing applications (opens browser for authorization):
session := session.NewOAuth2("client_id", "client_secret",
session.WithOAuth2Server("https://teamwork.com"),
session.WithOAuth2CallbackServerAddr("127.0.0.1:6275"),
)
Caution
Here's a simple example to get you started:
package main
import (
"context"
"fmt"
"log"
twapi "github.com/teamwork/twapi-go-sdk"
"github.com/teamwork/twapi-go-sdk/projects"
"github.com/teamwork/twapi-go-sdk/session"
)
func main() {
ctx := context.Background()
// Initialize the SDK with bearer token authentication
engine := twapi.NewEngine(session.NewBearerToken("your_token", "https://yourdomain.teamwork.com"))
// Create a new project
project, err := projects.ProjectCreate(ctx, engine, projects.NewProjectCreateRequest("My Awesome Project"))
if err != nil {
log.Fatalf("Failed to create project: %v", err)
}
fmt.Printf("✅ Created project '%s' with ID: %d\n", project.Name, project.ID)
}
package main
import (
"context"
"fmt"
"time"
twapi "github.com/teamwork/twapi-go-sdk"
"github.com/teamwork/twapi-go-sdk/projects"
"github.com/teamwork/twapi-go-sdk/session"
)
func main() {
ctx := context.Background()
engine := twapi.NewEngine(session.NewBearerToken("your_token", "https://yourdomain.teamwork.com"))
project, err := projects.ProjectCreate(ctx, engine, projects.ProjectCreateRequest{
Name: "Q1 Marketing Campaign",
Description: twapi.Ptr("Marketing campaign for Q1 product launch"),
StartAt: twapi.Ptr(time.Now()),
EndAt: twapi.Ptr(time.Now().AddDate(0, 3, 0)), // 3 months from now
})
if err != nil {
fmt.Fprintf(os.Stderr, "❌ Failed to create project: %v\n", err)
os.Exit(1)
}
// Retrieve the project
retrievedProject, err := projects.ProjectGet(ctx, engine, projects.NewProjectRetrieveRequest(int64(project.ID)))
if err != nil {
fmt.Fprintf(os.Stderr, "❌ Failed to retrieve project: %v\n", err)
os.Exit(1)
}
fmt.Printf("✅ Project: %s (ID: %d)\n", retrievedProject.Name, retrievedProject.ID)
// List all projects
projectsList, err := projects.ProjectList(ctx, engine, projects.NewProjectRetrieveManyRequest())
if err != nil {
fmt.Fprintf(os.Stderr, "❌ Failed to list projects: %v\n", err)
os.Exit(1)
}
fmt.Printf("✅ Found %d projects\n", len(projectsList.Projects))
// Update the project
updatedProject, err := projects.ProjectUpdate(ctx, engine, projects.ProjectUpdateRequest{
Path: projects.ProjectUpdateRequestPath{
ID: int64(project.ID),
},
Name: "Q1 Marketing Campaign - Updated",
})
if err != nil {
fmt.Fprintf(os.Stderr, "❌ Failed to update project: %v\n", err)
os.Exit(1)
}
fmt.Printf("✅ Updated project name to: %s\n", updatedProject.Name)
// Delete the project
err = projects.ProjectDelete(ctx, engine, projects.NewProjectDeleteRequest(int64(project.ID)))
if err != nil {
fmt.Fprintf(os.Stderr, "❌ Failed to delete project: %v\n", err)
os.Exit(1)
}
fmt.Println("✅ Project deleted successfully")
}
package main
import (
"context"
"flag"
"fmt"
"os"
twapi "github.com/teamwork/twapi-go-sdk"
"github.com/teamwork/twapi-go-sdk/projects"
"github.com/teamwork/twapi-go-sdk/session"
)
func main() {
clientID := flag.String("client-id", "", "OAuth2 Client ID")
clientSecret := flag.String("client-secret", "", "OAuth2 Client Secret")
flag.Parse()
if *clientID == "" || *clientSecret == "" {
fmt.Fprintln(os.Stderr, "❌ client-id and client-secret are required")
os.Exit(1)
}
// Create OAuth2 session (will open browser for authorization)
session := session.NewOAuth2(*clientID, *clientSecret,
session.WithOAuth2CallbackServerAddr("127.0.0.1:6275"),
)
engine := twapi.NewEngine(session)
// Test the connection by creating a project
project, err := projects.ProjectCreate(context.Background(), engine, projects.NewProjectCreateRequest("OAuth2 Test Project"))
if err != nil {
fmt.Fprintf(os.Stderr, "❌ Failed to create project: %v\n", err)
os.Exit(1)
}
fmt.Printf("✅ OAuth2 authentication successful! Created project: %s (ID: %d)\n", project.Name, project.ID)
}
package main
import (
"context"
"errors"
"fmt"
"net/http"
twapi "github.com/teamwork/twapi-go-sdk"
"github.com/teamwork/twapi-go-sdk/projects"
"github.com/teamwork/twapi-go-sdk/session"
)
func main() {
ctx := context.Background()
engine := twapi.NewEngine(session.NewBearerToken("your_token", "https://yourdomain.teamwork.com"))
project, err := projects.ProjectCreate(ctx, engine, projects.NewProjectCreateRequest("Test Project"))
if err != nil {
// Handle different types of errors
var httpErr *twapi.HTTPError
if errors.As(err, &httpErr) {
switch httpErr.StatusCode {
case http.StatusUnauthorized:
fmt.Println("❌ Authentication failed - check your API token")
case http.StatusForbidden:
fmt.Println("❌ Access denied - insufficient permissions")
case http.StatusTooManyRequests:
fmt.Println("❌ Rate limit exceeded - please retry later")
default:
fmt.Printf("❌ HTTP error %d: %s\n", httpErr.StatusCode, httpErr.Message)
}
} else {
fmt.Printf("❌ Unexpected error: %v\n", err)
}
return
}
fmt.Printf("✅ Success! Created project: %s\n", project.Name)
}
The SDK supports Go's context.Context
for request cancellation and timeouts:
import "time"
// Create a context with timeout
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
// Use the context in API calls
project, err := projects.ProjectCreate(ctx, engine, request)
You can customize the underlying HTTP client:
import (
"net/http"
"time"
)
// Create engine with custom HTTP client
httpClient := &http.Client{
Timeout: 60 * time.Second,
Transport: &http.Transport{
MaxIdleConns: 10,
IdleConnTimeout: 30 * time.Second,
DisableCompression: true,
},
}
engine := twapi.NewEngine(session,
twapi.WithHTTPClient(httpClient),
)
You can add custom middleware to intercept and modify HTTP requests/responses. Middlewares are executed in the order they are added:
import (
"fmt"
"net/http"
"time"
twapi "github.com/teamwork/twapi-go-sdk"
"github.com/teamwork/twapi-go-sdk/session"
)
// Logging middleware
func loggingMiddleware(next twapi.HTTPClient) twapi.HTTPClient {
return twapi.HTTPClientFunc(func(req *http.Request) (*http.Response, error) {
start := time.Now()
fmt.Printf("➡️ %s %s", req.Method, req.URL)
resp, err := next.Do(req)
duration := time.Since(start)
switch {
case err != nil:
fmt.Printf(" ❌ %s (took %v)\n", err.Error(), duration)
case resp.StatusCode >= 400:
fmt.Printf(" ❌ %s (took %v)\n", resp.Status, duration)
default:
fmt.Printf(" ✅ %s (took %v)\n", resp.Status, duration)
}
return resp, err
})
}
// Rate limiting middleware
func rateLimitingMiddleware(next twapi.HTTPClient) twapi.HTTPClient {
return twapi.HTTPClientFunc(func(req *http.Request) (*http.Response, error) {
// Add rate limiting logic here
time.Sleep(100 * time.Millisecond) // Simple delay example
return next.Do(req)
})
}
// Authentication header middleware
func authHeaderMiddleware(apiKey string) func(twapi.HTTPClient) twapi.HTTPClient {
return func(next twapi.HTTPClient) twapi.HTTPClient {
return twapi.HTTPClientFunc(func(req *http.Request) (*http.Response, error) {
req.Header.Set("X-Custom-Auth", apiKey)
return next.Do(req)
})
}
}
func main() {
session := session.NewBearerToken("your_token", "https://yourdomain.teamwork.com")
// Chain multiple middlewares
engine := twapi.NewEngine(session,
twapi.WithMiddleware(loggingMiddleware),
twapi.WithMiddleware(rateLimitingMiddleware),
twapi.WithMiddleware(authHeaderMiddleware("custom-key")),
)
// Now all requests will go through the middleware chain
// ...use engine for API calls...
}
The SDK provides an iterator function to easily handle paginated API responses:
import (
"context"
"fmt"
twapi "github.com/teamwork/twapi-go-sdk"
"github.com/teamwork/twapi-go-sdk/projects"
"github.com/teamwork/twapi-go-sdk/session"
)
func main() {
ctx := context.Background()
engine := twapi.NewEngine(session.NewBearerToken("your_token", "https://yourdomain.teamwork.com"))
// Create an iterator for paginated project results
next, err := twapi.Iterate[projects.ProjectListRequest, *projects.ProjectListResponse](
ctx,
engine,
projects.NewProjectListRequest(),
)
if err != nil {
fmt.Printf("Failed to create iterator: %v\n", err)
return
}
// Iterate through all pages
var iteration int
for {
iteration++
fmt.Printf("📄 Page %d\n", iteration)
response, hasNext, err := next()
if err != nil {
fmt.Printf("Error fetching page: %v\n", err)
break
}
if response == nil {
break
}
// Process projects from current page
for _, project := range response.Projects {
fmt.Printf(" ➢ %s (ID: %d)\n", project.Name, project.ID)
}
// Check if there are more pages
if !hasNext {
break
}
}
}
The SDK provides structured error handling:
import "errors"
project, err := projects.ProjectCreate(ctx, engine, request)
if err != nil {
var httpErr *twapi.HTTPError
if errors.As(err, &httpErr) {
fmt.Printf("HTTP %d: %s\n", httpErr.StatusCode, httpErr.Message)
// Handle specific status codes
}
}
Run the test suite:
go test ./...
Run integration tests:
TWAPI_SERVER=https://yourdomain.teamwork.com/ TWAPI_TOKEN=your_api_token go test ./...
Run tests with coverage:
go test -race -coverprofile=coverage.out ./...
go tool cover -html=coverage.out
- Go Version: 1.24 or later
- Dependencies: Minimal external dependencies (see
go.mod
) - Teamwork Account: Valid Teamwork.com account with API access
We welcome contributions! Please see our Contributing Guidelines for details.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature
) - Commit your changes (
git commit -m 'Add some amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
Made with ❤️ by the Teamwork.com team
⭐ Star us on GitHub if this project helped you!