The official Go SDK for the Teamwork.com API
Build powerful integrations with Teamwork's project management platform
- � 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!