{"id":30052591,"url":"https://github.com/teamwork/twapi-go-sdk","last_synced_at":"2026-02-20T11:06:14.999Z","repository":{"id":306106166,"uuid":"1016115485","full_name":"Teamwork/twapi-go-sdk","owner":"Teamwork","description":"Teamwork.com API - Go SDK","archived":false,"fork":false,"pushed_at":"2026-02-10T02:15:34.000Z","size":256,"stargazers_count":2,"open_issues_count":0,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-10T07:19:54.935Z","etag":null,"topics":["api","go-library","go-sdk","golang","teamwork","teamwork-api"],"latest_commit_sha":null,"homepage":"https://apidocs.teamwork.com","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Teamwork.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-07-08T14:07:12.000Z","updated_at":"2026-02-10T02:15:36.000Z","dependencies_parsed_at":"2026-01-02T23:00:28.815Z","dependency_job_id":null,"html_url":"https://github.com/Teamwork/twapi-go-sdk","commit_stats":null,"previous_names":["teamwork/twapi-go-sdk"],"tags_count":36,"template":false,"template_full_name":null,"purl":"pkg:github/Teamwork/twapi-go-sdk","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Teamwork%2Ftwapi-go-sdk","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Teamwork%2Ftwapi-go-sdk/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Teamwork%2Ftwapi-go-sdk/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Teamwork%2Ftwapi-go-sdk/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Teamwork","download_url":"https://codeload.github.com/Teamwork/twapi-go-sdk/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Teamwork%2Ftwapi-go-sdk/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29648563,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-20T09:27:29.698Z","status":"ssl_error","status_checked_at":"2026-02-20T09:26:12.373Z","response_time":59,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","go-library","go-sdk","golang","teamwork","teamwork-api"],"created_at":"2025-08-07T17:07:48.207Z","updated_at":"2026-02-20T11:06:14.986Z","avatar_url":"https://github.com/Teamwork.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n# 🚀 Teamwork.com API - Go SDK\n\n[![Go Version](https://img.shields.io/badge/Go-1.26+-00ADD8?style=for-the-badge\u0026logo=go)](https://golang.org/)\n[![Go Reference](https://img.shields.io/badge/Go-Reference-00ADD8?style=for-the-badge\u0026logo=go)](https://pkg.go.dev/github.com/Teamwork/twapi-go-sdk)\n[![License](https://img.shields.io/badge/License-MIT-blue?style=for-the-badge)](LICENSE)\n[![Go Report Card](https://goreportcard.com/badge/github.com/teamwork/twapi-go-sdk?style=for-the-badge)](https://goreportcard.com/report/github.com/teamwork/twapi-go-sdk)\n\n**The official Go SDK for the Teamwork.com API**\n\n*Build powerful integrations with Teamwork's project management platform*\n\n[📖 API Documentation](https://apidocs.teamwork.com/) • [🎯 Examples](examples/) • [🐛 Report Issues](https://github.com/teamwork/twapi-go-sdk/issues)\n\n\u003c/div\u003e\n\n---\n\n## ✨ Features\n\n- � **Multiple Authentication Methods** - Bearer token, Basic auth, and OAuth2\n- 🏗️ **Type-Safe API** - Fully typed requests and responses\n- 🌐 **Context Support** - Built-in context.Context support for cancellation and timeouts\n- 📦 **Zero Dependencies** - Minimal external dependencies\n- 🧪 **Thoroughly Tested** - Comprehensive test coverage\n- 📱 **Cross-Platform** - Works on Windows, macOS, and Linux\n\n## 📦 Installation\n\nAdd this library as a dependency to your Go module:\n\n```bash\ngo get github.com/teamwork/twapi-go-sdk\n```\n\n**Requirements:**\n- Go 1.26 or later\n- A Teamwork.com account with API access\n\n## 🔐 Authentication\n\nThe SDK supports multiple authentication methods to suit different use cases:\n\n### 🎫 Bearer Token (Recommended)\nPerfect for server-to-server integrations and scripts:\n\n```go\nimport \"github.com/teamwork/twapi-go-sdk/session\"\n\nsession := session.NewBearerToken(\"your_api_token\", \"https://yourdomain.teamwork.com\")\n```\n\n### 🔑 Basic Authentication\nUse with API tokens or user credentials:\n\n```go\n// With API token\nsession := session.NewBasicAuth(\"your_api_token\", \"\", \"https://yourdomain.teamwork.com\")\n\n// With username/password\nsession := session.NewBasicAuth(\"username\", \"password\", \"https://yourdomain.teamwork.com\")\n```\n\n### 🌐 OAuth2\nIdeal for user-facing applications (opens browser for authorization):\n\n```go\nsession := session.NewOAuth2(\"client_id\", \"client_secret\",\n  session.WithOAuth2Server(\"https://teamwork.com\"),\n  session.WithOAuth2CallbackServerAddr(\"127.0.0.1:6275\"),\n)\n```\n\n\u003e [!CAUTION]\n\u003e ⚠️ **Note:** OAuth2 opens a browser window and is not suitable for headless environments.\n\n## 🏁 Quick Start\n\nHere's a simple example to get you started:\n\n```go\npackage main\n\nimport (\n  \"context\"\n  \"fmt\"\n  \"log\"\n\n  twapi \"github.com/teamwork/twapi-go-sdk\"\n  \"github.com/teamwork/twapi-go-sdk/projects\"\n  \"github.com/teamwork/twapi-go-sdk/session\"\n)\n\nfunc main() {\n  ctx := context.Background()\n  \n  // Initialize the SDK with bearer token authentication\n  engine := twapi.NewEngine(session.NewBearerToken(\"your_token\", \"https://yourdomain.teamwork.com\"))\n\n  // Create a new project\n  project, err := projects.ProjectCreate(ctx, engine, projects.NewProjectCreateRequest(\"My Awesome Project\"))\n  if err != nil {\n    log.Fatalf(\"Failed to create project: %v\", err)\n  }\n  \n  fmt.Printf(\"✅ Created project '%s' with ID: %d\\n\", project.Name, project.ID)\n}\n```\n\n## 📚 Examples\n\n### Working with Projects\n\n```go\npackage main\n\nimport (\n  \"context\"\n  \"fmt\"\n  \"time\"\n\n  twapi \"github.com/teamwork/twapi-go-sdk\"\n  \"github.com/teamwork/twapi-go-sdk/projects\"\n  \"github.com/teamwork/twapi-go-sdk/session\"\n)\n\nfunc main() {\n  ctx := context.Background()\n  engine := twapi.NewEngine(session.NewBearerToken(\"your_token\", \"https://yourdomain.teamwork.com\"))\n\n  project, err := projects.ProjectCreate(ctx, engine, projects.ProjectCreateRequest{\n    Name:        \"Q1 Marketing Campaign\",\n    Description: twapi.Ptr(\"Marketing campaign for Q1 product launch\"),\n    StartAt:     twapi.Ptr(time.Now()),\n    EndAt:       twapi.Ptr(time.Now().AddDate(0, 3, 0)), // 3 months from now\n  })\n  if err != nil {\n    fmt.Fprintf(os.Stderr, \"❌ Failed to create project: %v\\n\", err)\n    os.Exit(1)\n  }\n\n  // Retrieve the project\n  retrievedProject, err := projects.ProjectGet(ctx, engine, projects.NewProjectRetrieveRequest(int64(project.ID)))\n  if err != nil {\n    fmt.Fprintf(os.Stderr, \"❌ Failed to retrieve project: %v\\n\", err)\n    os.Exit(1)\n  }\n\n  fmt.Printf(\"✅ Project: %s (ID: %d)\\n\", retrievedProject.Name, retrievedProject.ID)\n  \n  // List all projects\n  projectsList, err := projects.ProjectList(ctx, engine, projects.NewProjectRetrieveManyRequest())\n  if err != nil {\n    fmt.Fprintf(os.Stderr, \"❌ Failed to list projects: %v\\n\", err)\n    os.Exit(1)\n  }\n  \n  fmt.Printf(\"✅ Found %d projects\\n\", len(projectsList.Projects))\n  \n  // Update the project\n  updatedProject, err := projects.ProjectUpdate(ctx, engine, projects.ProjectUpdateRequest{\n    Path:  projects.ProjectUpdateRequestPath{\n      ID: int64(project.ID),\n    },\n    Name: \"Q1 Marketing Campaign - Updated\",\n  })\n  if err != nil {\n    fmt.Fprintf(os.Stderr, \"❌ Failed to update project: %v\\n\", err)\n    os.Exit(1)\n  }\n  \n  fmt.Printf(\"✅ Updated project name to: %s\\n\", updatedProject.Name)\n\n  // Delete the project\n  err = projects.ProjectDelete(ctx, engine, projects.NewProjectDeleteRequest(int64(project.ID)))\n  if err != nil {\n    fmt.Fprintf(os.Stderr, \"❌ Failed to delete project: %v\\n\", err)\n    os.Exit(1)\n  }\n\n  fmt.Println(\"✅ Project deleted successfully\")\n}\n```\n\n### OAuth2 Authentication Example\n\n```go\npackage main\n\nimport (\n  \"context\"\n  \"flag\"\n  \"fmt\"\n  \"os\"\n\n  twapi \"github.com/teamwork/twapi-go-sdk\"\n  \"github.com/teamwork/twapi-go-sdk/projects\"\n  \"github.com/teamwork/twapi-go-sdk/session\"\n)\n\nfunc main() {\n  clientID := flag.String(\"client-id\", \"\", \"OAuth2 Client ID\")\n  clientSecret := flag.String(\"client-secret\", \"\", \"OAuth2 Client Secret\")\n  flag.Parse()\n\n  if *clientID == \"\" || *clientSecret == \"\" {\n    fmt.Fprintln(os.Stderr, \"❌ client-id and client-secret are required\")\n    os.Exit(1)\n  }\n\n  // Create OAuth2 session (will open browser for authorization)\n  session := session.NewOAuth2(*clientID, *clientSecret,\n    session.WithOAuth2CallbackServerAddr(\"127.0.0.1:6275\"),\n  )\n  \n  engine := twapi.NewEngine(session)\n\n  // Test the connection by creating a project\n  project, err := projects.ProjectCreate(context.Background(), engine, projects.NewProjectCreateRequest(\"OAuth2 Test Project\"))\n  if err != nil {\n    fmt.Fprintf(os.Stderr, \"❌ Failed to create project: %v\\n\", err)\n    os.Exit(1)\n  }\n\n  fmt.Printf(\"✅ OAuth2 authentication successful! Created project: %s (ID: %d)\\n\", project.Name, project.ID)\n}\n```\n\n### Error Handling Best Practices\n\n```go\npackage main\n\nimport (\n  \"context\"\n  \"errors\"\n  \"fmt\"\n  \"net/http\"\n\n  twapi \"github.com/teamwork/twapi-go-sdk\"\n  \"github.com/teamwork/twapi-go-sdk/projects\"\n  \"github.com/teamwork/twapi-go-sdk/session\"\n)\n\nfunc main() {\n  ctx := context.Background()\n  engine := twapi.NewEngine(session.NewBearerToken(\"your_token\", \"https://yourdomain.teamwork.com\"))\n\n  project, err := projects.ProjectCreate(ctx, engine, projects.NewProjectCreateRequest(\"Test Project\"))\n  if err != nil {\n    // Handle different types of errors\n    var httpErr *twapi.HTTPError\n    if errors.As(err, \u0026httpErr) {\n      switch httpErr.StatusCode {\n      case http.StatusUnauthorized:\n        fmt.Println(\"❌ Authentication failed - check your API token\")\n      case http.StatusForbidden:\n        fmt.Println(\"❌ Access denied - insufficient permissions\")\n      case http.StatusTooManyRequests:\n        fmt.Println(\"❌ Rate limit exceeded - please retry later\")\n      default:\n        fmt.Printf(\"❌ HTTP error %d: %s\\n\", httpErr.StatusCode, httpErr.Message)\n      }\n    } else {\n      fmt.Printf(\"❌ Unexpected error: %v\\n\", err)\n    }\n    return\n  }\n\n  fmt.Printf(\"✅ Success! Created project: %s\\n\", project.Name)\n}\n```\n\n## 🔧 Configuration\n\n### Context and Timeouts\n\nThe SDK supports Go's `context.Context` for request cancellation and timeouts:\n\n```go\nimport \"time\"\n\n// Create a context with timeout\nctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)\ndefer cancel()\n\n// Use the context in API calls\nproject, err := projects.ProjectCreate(ctx, engine, request)\n```\n\n### Custom HTTP Client\n\nYou can customize the underlying HTTP client:\n\n```go\nimport (\n  \"net/http\"\n  \"time\"\n)\n\n// Create engine with custom HTTP client\nhttpClient := \u0026http.Client{\n  Timeout: 60 * time.Second,\n  Transport: \u0026http.Transport{\n    MaxIdleConns:        10,\n    IdleConnTimeout:     30 * time.Second,\n    DisableCompression:  true,\n  },\n}\n\nengine := twapi.NewEngine(session,\n  twapi.WithHTTPClient(httpClient),\n)\n```\n\n### Middleware\n\nYou can add custom middleware to intercept and modify HTTP requests/responses. Middlewares are executed in the order they are added:\n\n```go\nimport (\n  \"fmt\"\n  \"net/http\"\n  \"time\"\n\n  twapi \"github.com/teamwork/twapi-go-sdk\"\n  \"github.com/teamwork/twapi-go-sdk/session\"\n)\n\n// Logging middleware\nfunc loggingMiddleware(next twapi.HTTPClient) twapi.HTTPClient {\n  return twapi.HTTPClientFunc(func(req *http.Request) (*http.Response, error) {\n    start := time.Now()\n    fmt.Printf(\"➡️  %s %s\", req.Method, req.URL)\n\n    resp, err := next.Do(req)\n    duration := time.Since(start)\n\n    switch {\n    case err != nil:\n      fmt.Printf(\" ❌ %s (took %v)\\n\", err.Error(), duration)\n    case resp.StatusCode \u003e= 400:\n      fmt.Printf(\" ❌ %s (took %v)\\n\", resp.Status, duration)\n    default:\n      fmt.Printf(\" ✅ %s (took %v)\\n\", resp.Status, duration)\n    }\n    return resp, err\n  })\n}\n\n// Rate limiting middleware\nfunc rateLimitingMiddleware(next twapi.HTTPClient) twapi.HTTPClient {\n  return twapi.HTTPClientFunc(func(req *http.Request) (*http.Response, error) {\n    // Add rate limiting logic here\n    time.Sleep(100 * time.Millisecond) // Simple delay example\n    return next.Do(req)\n  })\n}\n\n// Authentication header middleware\nfunc authHeaderMiddleware(apiKey string) func(twapi.HTTPClient) twapi.HTTPClient {\n  return func(next twapi.HTTPClient) twapi.HTTPClient {\n    return twapi.HTTPClientFunc(func(req *http.Request) (*http.Response, error) {\n      req.Header.Set(\"X-Custom-Auth\", apiKey)\n      return next.Do(req)\n    })\n  }\n}\n\nfunc main() {\n  session := session.NewBearerToken(\"your_token\", \"https://yourdomain.teamwork.com\")\n\n  // Chain multiple middlewares\n  engine := twapi.NewEngine(session,\n    twapi.WithMiddleware(loggingMiddleware),\n    twapi.WithMiddleware(rateLimitingMiddleware),\n    twapi.WithMiddleware(authHeaderMiddleware(\"custom-key\")),\n  )\n\n  // Now all requests will go through the middleware chain\n  // ...use engine for API calls...\n}\n```\n\n### Iterator for Paginated Results\n\nThe SDK provides an iterator function to easily handle paginated API responses:\n\n```go\nimport (\n  \"context\"\n  \"fmt\"\n\n  twapi \"github.com/teamwork/twapi-go-sdk\"\n  \"github.com/teamwork/twapi-go-sdk/projects\"\n  \"github.com/teamwork/twapi-go-sdk/session\"\n)\n\nfunc main() {\n  ctx := context.Background()\n  engine := twapi.NewEngine(session.NewBearerToken(\"your_token\", \"https://yourdomain.teamwork.com\"))\n\n  // Create an iterator for paginated project results\n  next, err := twapi.Iterate[projects.ProjectListRequest, *projects.ProjectListResponse](\n    ctx,\n    engine,\n    projects.NewProjectListRequest(),\n  )\n  if err != nil {\n    fmt.Printf(\"Failed to create iterator: %v\\n\", err)\n    return\n  }\n\n  // Iterate through all pages\n  var iteration int\n  for {\n    iteration++\n    fmt.Printf(\"📄 Page %d\\n\", iteration)\n\n    response, hasNext, err := next()\n    if err != nil {\n      fmt.Printf(\"Error fetching page: %v\\n\", err)\n      break\n    }\n    if response == nil {\n      break\n    }\n\n    // Process projects from current page\n    for _, project := range response.Projects {\n      fmt.Printf(\"  ➢ %s (ID: %d)\\n\", project.Name, project.ID)\n    }\n\n    // Check if there are more pages\n    if !hasNext {\n      break\n    }\n  }\n}\n```\n\n## 🐛 Error Handling\n\nThe SDK provides structured error handling:\n\n```go\nimport \"errors\"\n\nproject, err := projects.ProjectCreate(ctx, engine, request)\nif err != nil {\n  var httpErr *twapi.HTTPError\n  if errors.As(err, \u0026httpErr) {\n    fmt.Printf(\"HTTP %d: %s\\n\", httpErr.StatusCode, httpErr.Message)\n    // Handle specific status codes\n  }\n}\n```\n\n## 🧪 Testing\n\nRun the test suite:\n\n```bash\ngo test ./...\n```\n\nRun integration tests:\n\n```bash\nTWAPI_SERVER=https://yourdomain.teamwork.com/ TWAPI_TOKEN=your_api_token go test ./...\n```\n\nRun tests with coverage:\n\n```bash\ngo test -race -coverprofile=coverage.out ./...\ngo tool cover -html=coverage.out\n```\n\n## 📋 Requirements\n\n- **Go Version:** 1.26 or later\n- **Dependencies:** Minimal external dependencies (see `go.mod`)\n- **Teamwork Account:** Valid Teamwork.com account with API access\n\n## 🤝 Contributing\n\nWe welcome contributions! Please see our [Contributing Guidelines](CONTRIBUTING.md) for details.\n\n1. Fork the repository\n2. Create your feature branch (`git checkout -b feature/amazing-feature`)\n3. Commit your changes (`git commit -m 'Add some amazing feature'`)\n4. Push to the branch (`git push origin feature/amazing-feature`)\n5. Open a Pull Request\n\n## 📄 License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n\n## 🆘 Support\n\n- 📖 [API Documentation](https://apidocs.teamwork.com/)\n- 🐛 [Report Issues](https://github.com/teamwork/twapi-go-sdk/issues)\n- 💬 [Community Support](https://teamwork.com/support)\n\n---\n\n\u003cdiv align=\"center\"\u003e\n\n**Made with ❤️ by the Teamwork.com team**\n\n⭐ Star us on GitHub if this project helped you!\n\n\u003c/div\u003e","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fteamwork%2Ftwapi-go-sdk","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fteamwork%2Ftwapi-go-sdk","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fteamwork%2Ftwapi-go-sdk/lists"}