{"id":51430417,"url":"https://github.com/raykavin/tangerino-go","last_synced_at":"2026-07-05T03:30:33.350Z","repository":{"id":364069186,"uuid":"1265461089","full_name":"raykavin/tangerino-go","owner":"raykavin","description":"Go SDK for the Tangerino Employer API. Manage employees, companies, holiday calendars, and work schedules with a lightweight, production-ready client built on the Go standard library.","archived":false,"fork":false,"pushed_at":"2026-06-23T18:22:40.000Z","size":53,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-23T20:15:01.807Z","etag":null,"topics":["api-client","employee-management","go","golang","human-resources","library","rest-api","sdk","solides","tangerino"],"latest_commit_sha":null,"homepage":"","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/raykavin.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"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":"2026-06-10T19:50:00.000Z","updated_at":"2026-06-23T18:21:41.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/raykavin/tangerino-go","commit_stats":null,"previous_names":["raykavin/tangerino-go"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/raykavin/tangerino-go","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raykavin%2Ftangerino-go","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raykavin%2Ftangerino-go/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raykavin%2Ftangerino-go/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raykavin%2Ftangerino-go/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/raykavin","download_url":"https://codeload.github.com/raykavin/tangerino-go/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raykavin%2Ftangerino-go/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35142824,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-05T02:00:06.290Z","response_time":100,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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-client","employee-management","go","golang","human-resources","library","rest-api","sdk","solides","tangerino"],"created_at":"2026-07-05T03:30:32.848Z","updated_at":"2026-07-05T03:30:33.336Z","avatar_url":"https://github.com/raykavin.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Unofficial Tangerino Employer API\n\n[![Go Reference](https://pkg.go.dev/badge/github.com/raykavin/tangerino-go.svg)](https://pkg.go.dev/github.com/raykavin/tangerino-go)\n[![Go Version](https://img.shields.io/badge/go-1.22+-blue)](https://golang.org/dl/)\n[![Go Report Card](https://goreportcard.com/badge/github.com/raykavin/tangerino-go)](https://goreportcard.com/report/github.com/raykavin/tangerino-go)\n[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)\n\nA Go client library for the **Tangerino Employer API**.\n\nCovers the core employer modules: Employees, Companies, Holiday Calendars, Work Schedules, Workplaces, and Punches.\n\n---\n\n## Installation\n\n```bash\ngo get github.com/raykavin/tangerino-go\n```\n\nRequires **Go 1.22+**. No external dependencies — uses only the Go standard library.\n\n---\n\n## Quick Start\n\n```go\nimport tangerino \"github.com/raykavin/tangerino-go\"\n\nclient, err := tangerino.NewClient(\"your-username\", \"your-password\")\nif err != nil {\n    log.Fatal(err)\n}\n\nctx := context.Background()\n\n// List employees (first page)\npage, err := client.Employees.List(ctx, tangerino.ListEmployeesParams{Size: 20})\nif err != nil {\n    log.Fatal(err)\n}\n\nfor _, e := range page.Content {\n    fmt.Println(e.ID, e.Name, e.AdmissionDate.Format(\"02/01/2006\"))\n}\n```\n\n---\n\n## Configuration\n\n```go\n// Production environment (default)\nclient, err := tangerino.NewClient(\"username\", \"password\")\n\n// Custom base URL (e.g. for testing)\nopt, err := tangerino.WithBaseURL(\"https://custom.tangerino.example.com\")\nif err != nil {\n    log.Fatal(err)\n}\nclient, err = tangerino.NewClient(\"username\", \"password\", opt)\n\n// Staging environment\nclient, err = tangerino.NewClient(\"username\", \"password\", tangerino.WithStagingEnv())\n\n// Custom HTTP client (for TLS, proxies, custom timeouts, etc.)\nhttpClient := \u0026http.Client{Timeout: 60 * time.Second}\nclient, err = tangerino.NewClient(\"username\", \"password\", tangerino.WithHTTPClient(httpClient))\n```\n\n---\n\n## Authentication\n\nThe Tangerino API uses **HTTP Basic Authentication**. Credentials are encoded and\nsent automatically on every request — no additional setup is required after creating\nthe client.\n\n```go\nclient, err := tangerino.NewClient(\"your-username\", \"your-password\")\n```\n\n---\n\n## Modules\n\n### Employees\n\n```go\n// List employees (paginated)\npage, err := client.Employees.List(ctx, tangerino.ListEmployeesParams{\n    Size: 20,\n})\nfmt.Printf(\"Page 1 of %d (%d total)\\n\", page.TotalPages, page.TotalElements)\n\n// Apply filters\npage, err = client.Employees.List(ctx, tangerino.ListEmployeesParams{\n    Size:              20,\n    BranchExternalID:  \"branch-001\",\n    ManagerExternalID: \"manager-042\",\n    ShowFired:         1, // include terminated employees\n})\n\n// Filter by last update (Unix timestamp in milliseconds)\npage, err = client.Employees.List(ctx, tangerino.ListEmployeesParams{\n    LastUpdate: time.Now().Add(-24 * time.Hour).UnixMilli(),\n    Size:       50,\n})\n\n// Iterate all pages\nparams := tangerino.ListEmployeesParams{Size: 50}\nfor {\n    page, err := client.Employees.List(ctx, params)\n    if err != nil {\n        log.Fatal(err)\n    }\n    for _, e := range page.Content {\n        fmt.Println(e.ID, e.Name)\n    }\n    if !page.HasNext() {\n        break\n    }\n    params.Page = page.NextPageNumber()\n}\n\n// Access typed date fields\nfor _, e := range page.Content {\n    fmt.Println(e.AdmissionDate.Format(\"02/01/2006\")) // \"01/04/2025\"\n    fmt.Println(e.AdmissionDate.Raw())                // 1743462000000\n    fmt.Println(e.AdmissionDate.Time())               // time.Time\n\n    if e.BirthDate != nil {\n        fmt.Println(e.BirthDate.Format(\"02/01/2006\"))\n    }\n}\n```\n\n---\n\n### Companies\n\n```go\n// List companies (paginated)\npage, err := client.Companies.List(ctx, tangerino.ListCompaniesParams{\n    Size: 20,\n})\n\n// Explicit page navigation\npage, err = client.Companies.List(ctx, tangerino.ListCompaniesParams{\n    Page: 1,\n    Size: 10,\n})\n\nfor _, c := range page.Content {\n    fmt.Println(c.ID, c.CNPJ, c.SocialReason, c.FantasyName)\n}\n```\n\n---\n\n### Holiday Calendars\n\n```go\n// List all holiday calendars for the employer\ncalendars, err := client.HolidayCalendars.List(ctx)\nif err != nil {\n    log.Fatal(err)\n}\n\nfor _, cal := range calendars {\n    fmt.Printf(\"[%d] %s (%d)\\n\", cal.ID, cal.Name, cal.Year)\n    for _, h := range cal.Holidays {\n        fmt.Printf(\"  %s - %s\\n\", h.Date, h.Description)\n    }\n}\n```\n\n---\n\n### Work Schedules\n\n```go\n// List all work schedules\npage, err := client.WorkSchedules.List(ctx, tangerino.ListWorkSchedulesParams{\n    Size: 50,\n})\nif err != nil {\n    log.Fatal(err)\n}\n\nfor _, ws := range page.Content {\n    fmt.Printf(\"[%d] %s (standard: %v, inactive: %v)\\n\",\n        ws.ID, ws.Name, ws.Standard, ws.Inactive)\n\n    for _, tt := range ws.Timetable {\n        fmt.Printf(\"  Day %d: %s - %s\",\n            tt.Day,\n            tt.StartShift1.String(), // \"08:00\"\n            tt.EndShift1,            // *DayOffset, check nil before use\n        )\n        if tt.StartShift2 != nil {\n            fmt.Printf(\" | %s - %s\",\n                tt.StartShift2.String(),\n                tt.EndShift2,\n            )\n        }\n        fmt.Println()\n    }\n\n    fmt.Println(\"Last modified:\", ws.AlterationDate.Format(\"02/01/2006\"))\n}\n```\n\n---\n\n### Workplaces\n\n```go\n// List workplaces (paginated)\npage, err := client.Workplaces.List(ctx, tangerino.ListWorkplacesParams{\n    Size: 20,\n})\nif err != nil {\n    log.Fatal(err)\n}\n\nfor _, w := range page.Content {\n    fmt.Printf(\"[%d] %s — %s, %s\\n\", w.ID, w.Name, w.City, w.State)\n}\n\n// Iterate all pages\nparams := tangerino.ListWorkplacesParams{Size: 50}\nfor {\n    page, err := client.Workplaces.List(ctx, params)\n    if err != nil {\n        log.Fatal(err)\n    }\n    for _, w := range page.Content {\n        fmt.Println(w.ID, w.Name)\n    }\n    if !page.HasNext() {\n        break\n    }\n    params.Page = page.NextPageNumber()\n}\n```\n\n---\n\n### Punches\n\nThe punch endpoints live on a separate host (`apis.tangerino.com.br`) — the client handles routing automatically.\n\n```go\nadj := true\npending := false\n\n// List punch records for an employee\npunches, err := client.Punches.List(ctx, employeeID, tangerino.PunchesParams{\n    Status:     3,\n    Adjustment: \u0026adj,\n    StartDate:  time.Now().AddDate(0, -1, 0), // converted to Unix seconds\n    EndDate:    time.Now(),\n    Pending:    \u0026pending,\n})\nif err != nil {\n    log.Fatal(err)\n}\n\nfor _, p := range punches {\n    end := \"open\"\n    if p.EndDate != nil {\n        end = p.EndDate.String() // \"2026-06-01T18:02:00\"\n    }\n    fmt.Printf(\"[%d] %s  %s → %s  (%s manual: start=%v end=%v)\\n\",\n        p.ID, p.Date,\n        p.StartDate.String(), end,\n        formatMillis(p.TotalHours),\n        p.StartManual, p.EndManual,\n    )\n}\n\n// Get aggregate summary for an employee\nsummary, err := client.Punches.Summary(ctx, employeeID, tangerino.PunchesParams{\n    StartDate: time.Now().AddDate(0, -1, 0),\n    EndDate:   time.Now(),\n})\nif err != nil {\n    log.Fatal(err)\n}\nfmt.Printf(\"Worked: %dms  Expected: %dms  Balance: %dms\\n\",\n    summary.TotalWorked, summary.TotalExpected, summary.Balance)\n```\n\n**`StartDate` and `EndDate`** are `time.Time` values and are automatically converted to Unix second timestamps in the request query string.\n\n**`Adjustment` and `Pending`** are `*bool` — use a pointer so that `false` is distinguishable from \"not set\":\n\n```go\nt, f := true, false\ntangerino.PunchesParams{Adjustment: \u0026t, Pending: \u0026f}\n```\n\n---\n\n## Custom Types\n\n### `UnixMilliTime`\n\nUsed for absolute timestamps (`AdmissionDate`, `BirthDate`, `EffectiveDate`,\n`AlterationDate`, `StartDate`). Preserves the original Unix millisecond value and\nprovides conversion helpers.\n\n```go\nts := employee.AdmissionDate\n\nts.Raw()                     // int64   → 1776049200000  (original API value)\nts.Time()                    // time.Time (UTC)\nts.Format(\"02/01/2006\")      // string  → \"01/04/2026\"\nts.Format(\"15:04\")           // string  → \"03:00\" (UTC hour)\nts.Format(time.RFC3339)      // string  → \"2026-04-01T03:00:00Z\"\nts.String()                  // string  → \"2026-04-01 03:00:00 UTC\"\n```\n\n### `DayOffset`\n\nUsed for time-of-day offsets in work schedule timetables (`StartShift1`,\n`EndShift1`, `StartShift2`, `EndShift2`, `StartMainInterval`, `EndMainInterval`).\nValues are milliseconds from midnight and may exceed 86400000 (24 h) for shifts\nthat extend into the next day.\n\n```go\nd := timetable.StartShift1\n\nd.Raw()       // int64         → 39600000    (original API value)\nd.Duration()  // time.Duration → 11h0m0s\nd.String()    // string        → \"11:00\"\n\n// Shifts past midnight (e.g. 12x36 schedule):\n// EndShift2 = 100800000 → \"28:00\"\n```\n\n### `LocalDateTime`\n\nUsed for punch timestamps (`StartDate`, `EndDate` in `Punch`). Represents a\nwall-clock datetime without timezone in the format `\"2006-01-02T15:04:05\"`.\n\n```go\np := punches[0]\n\np.StartDate.String()  // \"2026-06-01T14:06:00\"\np.StartDate.Time()    // time.Time (no timezone — server local time)\n\n// EndDate is *LocalDateTime, nil when the employee hasn't clocked out yet\nif p.EndDate != nil {\n    fmt.Println(p.EndDate.String())\n}\n```\n\n---\n\n## Pagination\n\nPaginated endpoints return `*Page[T]`, a generic type wrapping the Spring-style\n`content` envelope.\n\n```go\ntype Page[T any] struct {\n    Content          []T\n    First            bool\n    Last             bool\n    TotalElements    int\n    TotalPages       int\n    NumberOfElements int\n    Size             int\n    Number           int\n}\n```\n\nHelper methods:\n\n```go\npage.HasNext()         // bool: whether a next page exists\npage.NextPageNumber()  // int: page number to use in the next request (-1 on last page)\n```\n\nPagination parameters follow a consistent naming convention across all services:\n\n| Field  | Query param | Description                    |\n|--------|-------------|--------------------------------|\n| `Page` | `page`      | Zero-based page index          |\n| `Size` | `size`      | Number of items per page       |\n\nFull iteration pattern:\n\n```go\nparams := tangerino.ListEmployeesParams{Size: 50}\nfor {\n    page, err := client.Employees.List(ctx, params)\n    if err != nil {\n        log.Fatal(err)\n    }\n    // process page.Content ...\n    if !page.HasNext() {\n        break\n    }\n    params.Page = page.NextPageNumber()\n}\n```\n\n---\n\n## Error Handling\n\nAll methods return `*APIError` on HTTP-level failures.\n\n```go\npage, err := client.Employees.List(ctx, tangerino.ListEmployeesParams{})\nif err != nil {\n    switch {\n    case tangerino.IsUnauthorized(err):\n        // HTTP 401 — invalid credentials\n    case tangerino.IsForbidden(err):\n        // HTTP 403 — insufficient permissions\n    case tangerino.IsNotFound(err):\n        // HTTP 404 — resource not found\n    case tangerino.IsRateLimited(err):\n        // HTTP 429 — too many requests, back off and retry\n    case tangerino.IsServerError(err):\n        // HTTP 5xx — transient server error\n    default:\n        if apiErr, ok := err.(*tangerino.APIError); ok {\n            fmt.Printf(\"Status: %d\\n\", apiErr.StatusCode)\n            fmt.Printf(\"Body:   %s\\n\", apiErr.Body)\n        }\n    }\n}\n```\n\n---\n\n## Running Tests\n\n```bash\ngo test ./... -v\n```\n\nAll tests use `net/http/httptest` — no external services or environment variables\nare required.\n\n---\n\n## Endpoints Coverage\n\n### Employees\n- [x] `GET /employee/find-all` — List employees (paginated, with filters)\n\n### Companies\n- [x] `GET /companies` — List companies (paginated)\n\n### Holiday Calendars\n- [x] `GET /holiday-calendar/` — List holiday calendars\n\n### Work Schedules\n- [x] `GET /work-schedule` — List work schedules (paginated)\n\n### Workplaces\n- [x] `GET /workplace/find-all` — List workplaces (paginated)\n\n### Punches\n- [x] `GET /punch/v2/punches/employees/{id}` — List punch records for an employee\n- [x] `GET /punch/v2/punches/employees/{id}/summary` — Get aggregate time summary for an employee\n\n**Total: 7 endpoints covered**\n\n---\n\n## Contributing\n\nContributions to tangerino-go are welcome! Here are some ways you can help:\n\n- **Report bugs and suggest features** by opening issues on GitHub\n- **Submit pull requests** with bug fixes or new features\n- **Improve documentation** to help other users and developers\n\n---\n\n## License\n\ntangerino-go is distributed under the **MIT License**.  \nFor complete license terms and conditions, see the [LICENSE](LICENSE) file in the repository.\n\n---\n\n## Contact\n\nFor support, collaboration, or questions about tangerino-go:\n\n**Email**: [raykavin.meireles@gmail.com](mailto:raykavin.meireles@gmail.com)  \n**GitHub**: [@raykavin](https://github.com/raykavin)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fraykavin%2Ftangerino-go","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fraykavin%2Ftangerino-go","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fraykavin%2Ftangerino-go/lists"}