clockify

package
v0.4.4 Latest Latest
Warning

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

Go to latest
Published: Jun 19, 2026 License: MIT Imports: 22 Imported by: 0

Documentation

Index

Constants

View Source
const (
	DocumentedHostMain     = "https://api.clockify.me/api/v1"
	DocumentedHostReports  = "https://reports.api.clockify.me/v1"
	DocumentedHostAuditLog = "https://auditlog-api.api.clockify.me/v1"
)

Documented Clockify API hosts. Raw fallback is fenced to these hosts: the main v1 API, the reports API, and the audit-log API.

View Source
const DefaultMaxResponseBodyBytes = 10 * 1024 * 1024 // 10 MB

DefaultMaxResponseBodyBytes is the default maximum number of bytes read from API responses to prevent OOM on unexpectedly large or malicious responses.

Variables

View Source
var ErrCircuitBreakerOpen = errors.New("clockify upstream circuit breaker open")

ErrCircuitBreakerOpen is the sentinel that CircuitBreakerOpenError matches via errors.Is, letting callers detect a tripped breaker without depending on the concrete error type.

Functions

func CentsFromReportNumber

func CentsFromReportNumber(v float64) int64

CentsFromReportNumber converts a JSON number coming from the reports host (always integer-cents-as-float, per findings/rates-and-reports.md) to int64 cents using banker's rounding. The raw float is preserved by the caller for audit.

func ListAll

func ListAll[T any](ctx context.Context, c *Client, path string, baseQuery map[string]string) ([]T, error)

ListAll fetches every page into a single slice, bounded by the default pagination row cap.

func ListAllFuncWithOptions

func ListAllFuncWithOptions[T any](ctx context.Context, c *Client, path string, baseQuery map[string]string, opts ListAllOptions, onPage func([]T) error) error

ListAllFuncWithOptions scans pages using opts and invokes onPage for each non-empty page without retaining earlier pages.

func ListAllWithOptions

func ListAllWithOptions[T any](ctx context.Context, c *Client, path string, baseQuery map[string]string, opts ListAllOptions) ([]T, error)

ListAllWithOptions fetches every page into a single slice using opts.

Types

type APIError

type APIError struct {
	Method      string
	Path        string
	StatusCode  int
	Status      string
	Body        string
	RetryAfter  time.Duration
	Translation *ErrorTranslation
}

APIError is a structured non-2xx response from the Clockify HTTP API. It carries the request method/path, HTTP status, the (secret-redacted) response body, any Retry-After hint, and an optional precomputed Translation. Error() renders the full diagnostic for server-side logs; Sanitized, ClientFacingMessage, and ErrorTranslation produce client-safe views.

func (*APIError) ClientFacingMessage

func (e *APIError) ClientFacingMessage() string

ClientFacingMessage returns a clean, human-readable message for the MCP client: the upstream message text without the HTTP method/path prefix or the internal clockify_error_code suffix. Server-side logs still call Error() for the full diagnostic.

func (*APIError) Error

func (e *APIError) Error() string

func (*APIError) ErrorTranslation

func (e *APIError) ErrorTranslation() any

ErrorTranslation returns the client-facing translation for this error: the precomputed Translation when set, otherwise one derived from the status code and body via TranslateAPIError. The return type is any to satisfy the wire error-translation interface used by the MCP layer.

func (*APIError) Sanitized

func (e *APIError) Sanitized() string

Sanitized returns the error string without the upstream response body, suitable for hosted/shared deployments where the body might leak per-tenant information across tenants. Server-side logs still call Error() so operators can see the full diagnostic; the sanitised form is only what we hand to the MCP client.

type CircuitBreaker

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

CircuitBreaker is a per-endpoint (method+path) circuit breaker for the Clockify HTTP client. It transitions closed -> open after FailureThreshold consecutive upstream failures, half-open after OpenDuration, and back to closed on a successful probe. A nil or disabled breaker is a no-op.

func NewCircuitBreaker

func NewCircuitBreaker(cfg CircuitBreakerConfig) *CircuitBreaker

NewCircuitBreaker builds a CircuitBreaker from cfg, substituting defaults for any zero/negative numeric field (5 failures, 45s open, 1 half-open probe).

func (*CircuitBreaker) After

func (b *CircuitBreaker) After(endpoint, method string, upstreamFailure bool)

After records the outcome of a request admitted by Before. upstreamFailure advances the failure count (and may open the breaker); a success resets it and closes the breaker. Endpoint and method identify the per-endpoint state.

func (*CircuitBreaker) Before

func (b *CircuitBreaker) Before(endpoint, method string) error

Before is called prior to an upstream request for (endpoint, method). It returns a *CircuitBreakerOpenError when the breaker is open (or half-open and already saturated with probes), and nil when the request may proceed.

type CircuitBreakerConfig

type CircuitBreakerConfig struct {
	Enabled          bool
	FailureThreshold int
	OpenDuration     time.Duration
	HalfOpenProbes   int
}

CircuitBreakerConfig configures a CircuitBreaker. Enabled gates the breaker; FailureThreshold is the consecutive-failure count that opens it; OpenDuration is how long it stays open before probing; HalfOpenProbes is the number of concurrent trial requests allowed while half-open. Zero/negative numeric fields fall back to defaults in NewCircuitBreaker.

type CircuitBreakerOpenError

type CircuitBreakerOpenError struct {
	Endpoint   string
	Method     string
	RetryAfter time.Duration
}

CircuitBreakerOpenError is returned by CircuitBreaker.Before when a request is rejected because the per-endpoint breaker is open. RetryAfter is the remaining cool-down before the next probe is allowed.

func (*CircuitBreakerOpenError) Error

func (e *CircuitBreakerOpenError) Error() string

func (*CircuitBreakerOpenError) Is

func (e *CircuitBreakerOpenError) Is(target error) bool

Is reports whether target is ErrCircuitBreakerOpen, enabling errors.Is(err, ErrCircuitBreakerOpen) on a *CircuitBreakerOpenError.

type Client

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

Client is the Clockify HTTP API client. It authenticates with the configured API key, enforces a max response-body size, auto-retries only idempotent/safe requests (GET/HEAD/OPTIONS/PUT) and never auto-retries mutating requests (POST/PATCH/DELETE), and optionally gates calls through a per-endpoint circuit breaker. The verb methods (Get/Post/Put/Patch/Delete and their variants) target the main host; RequestRaw* methods target a documented host explicitly.

func NewClient

func NewClient(apiKey, baseURL string, timeout time.Duration, maxRetries int) *Client

NewClient builds a Client for baseURL with the given API key, request timeout (default 30s when non-positive), and retry budget (clamped to >= 0). The underlying transport refuses cross-host redirects and HTTPS->HTTP downgrades.

func (*Client) AuditLogBaseURL

func (c *Client) AuditLogBaseURL() string

AuditLogBaseURL returns the dedicated Clockify audit-log API base URL. Like ReportsBaseURL, custom/test base URLs remain unchanged.

func (*Client) Close

func (c *Client) Close()

Close releases idle connections held by the client.

func (*Client) Delete

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

Delete issues a DELETE to the main host, discarding any response body.

func (*Client) DeleteReports

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

DeleteReports performs a DELETE against the reports host.

func (*Client) DeleteReportsCaptureValues added in v0.4.3

func (c *Client) DeleteReportsCaptureValues(ctx context.Context, path string, query url.Values, out any) error

DeleteReportsCaptureValues issues a DELETE against the reports host, preserving repeated query keys and decoding any response body into out (some Clockify DELETE routes return the deleted entity; out is left at its zero value when the server sends an empty body). out must be non-nil.

func (*Client) DeleteWithBody

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

DeleteWithBody issues a DELETE with a JSON request body and decodes any response into out, for the Clockify routes that require a delete payload.

func (*Client) DeleteWithQuery

func (c *Client) DeleteWithQuery(ctx context.Context, path string, query map[string]string) error

DeleteWithQuery issues a DELETE with query parameters, discarding any response body.

func (*Client) DeleteWithQueryCapture added in v0.2.0

func (c *Client) DeleteWithQueryCapture(ctx context.Context, path string, query map[string]string, out any) error

DeleteWithQueryCapture issues a DELETE with optional query parameters and decodes any response body into out. Clockify DELETE routes are inconsistent: most reply with an empty body, but some return the deleted entity. doJSON returns nil for a zero-length body, so out is left at its zero value when the server sends nothing. out must be non-nil.

func (*Client) DeleteWithQueryCaptureValues added in v0.4.3

func (c *Client) DeleteWithQueryCaptureValues(ctx context.Context, path string, query url.Values, out any) error

DeleteWithQueryCaptureValues is like DeleteWithQueryCapture but accepts url.Values so repeated query keys (e.g. status=active&status=archived) are preserved instead of being collapsed by a map[string]string.

func (*Client) DocumentedBaseURL

func (c *Client) DocumentedBaseURL(host string) string

DocumentedBaseURL resolves a generated OpenAPI host to the concrete base URL this client should use.

func (*Client) Get

func (c *Client) Get(ctx context.Context, path string, query map[string]string, out any) error

Get issues a GET to the main host and decodes the JSON response into out.

func (*Client) GetReports

func (c *Client) GetReports(ctx context.Context, path string, query map[string]string, out any) error

GetReports performs a GET against the reports host. Use this for shared-report endpoints; everything else should stay on Get.

func (*Client) GetReportsValues added in v0.4.3

func (c *Client) GetReportsValues(ctx context.Context, path string, query url.Values, out any) error

GetReportsValues is like GetReports but accepts url.Values so repeated query keys are preserved on the reports host.

func (*Client) GetValues

func (c *Client) GetValues(ctx context.Context, path string, query url.Values, out any) error

GetValues is like Get but accepts url.Values query parameters, allowing repeated keys.

func (*Client) HTTPTimeout added in v0.4.0

func (c *Client) HTTPTimeout() time.Duration

HTTPTimeout returns the per-request timeout configured on the underlying http.Client, or 0 for a nil client.

func (*Client) MaxResponseBodyBytes added in v0.4.0

func (c *Client) MaxResponseBodyBytes() int64

MaxResponseBodyBytes returns the current response-body cap, falling back to DefaultMaxResponseBodyBytes for a nil client or an unset cap.

func (*Client) Patch

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

Patch issues a PATCH with a JSON body to the main host and decodes the response into out.

func (*Client) Post

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

Post issues a POST with a JSON body to the main host and decodes the response into out.

func (*Client) PostAuditLog

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

PostAuditLog performs a POST against the audit-log host. The Clockify audit-log API lives on a separate host (auditlog-api.api.clockify.me); every other call stays on Post against the primary host.

func (*Client) PostMultipart

func (c *Client) PostMultipart(ctx context.Context, path string, form url.Values, out any) error

PostMultipart performs a POST with a multipart/form-data body. Each form key maps to one or more values; multi-value keys are written as repeated form fields under the same name.

func (*Client) PostMultipartWithFiles

func (c *Client) PostMultipartWithFiles(ctx context.Context, path string, form url.Values, files []MultipartFile, out any) error

PostMultipartWithFiles performs a POST with form fields plus binary file parts. File payloads are bounded to keep MCP requests from becoming an unbounded memory sink.

func (*Client) PostReports

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

PostReports performs a POST against the reports host.

func (*Client) PostReportsRaw

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

PostReportsRaw is the binary-aware sibling of PostReports. It is used for report exportType values such as PDF/CSV/XLSX/ZIP where the reports API may return a file body rather than JSON.

func (*Client) PostWithQuery

func (c *Client) PostWithQuery(ctx context.Context, path string, query map[string]string, body any, out any) error

PostWithQuery is like Post but additionally sends query parameters.

func (*Client) Put

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

Put issues a PUT with a JSON body to the main host and decodes the response into out.

func (*Client) PutMultipart

func (c *Client) PutMultipart(ctx context.Context, path string, form url.Values, out any) error

PutMultipart performs a PUT with a multipart/form-data body. Same encoding rules as PostMultipart.

func (*Client) PutReports

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

PutReports performs a PUT against the reports host.

func (*Client) PutWithQuery

func (c *Client) PutWithQuery(ctx context.Context, path string, query map[string]string, body any, out any) error

PutWithQuery is like Put but additionally sends query parameters.

func (*Client) ReportsBaseURL

func (c *Client) ReportsBaseURL() string

ReportsBaseURL returns the base URL for endpoints that live on Clockify's reports host (reports.api.clockify.me/v1). The reports API is a separate host from api.clockify.me — hitting the reports paths on the primary host returns 404 ("No static resource"), and hitting them on the reports host with the /api/v1 prefix is also 404. See findings/shared-reports.md.

Derivation: when the primary base URL matches the canonical production host, swap to reports.api.clockify.me and drop the /api segment. Otherwise (test stubs, custom proxies, on-prem mirrors), return the primary base URL unchanged so existing fixtures continue to work without per-test wiring.

func (*Client) RequestRawValues

func (c *Client) RequestRawValues(ctx context.Context, reportsHost bool, method, path string, query url.Values, body any) (*RawResponse, error)

RequestRawValues runs a request against a documented host and returns the raw response bytes and headers instead of unmarshalling JSON.

func (*Client) RequestRawValuesForHost

func (c *Client) RequestRawValuesForHost(ctx context.Context, host, method, path string, query url.Values, body any) (*RawResponse, error)

RequestRawValuesForHost is the host-aware binary-aware request transport. Non-canonical test/custom base URLs stay pinned to the configured base so fake servers and proxies do not need per-host wiring.

func (*Client) SetCircuitBreaker

func (c *Client) SetCircuitBreaker(cfg CircuitBreakerConfig)

SetCircuitBreaker installs (or, when cfg.Enabled is false, clears) the per-endpoint circuit breaker applied to subsequent requests.

func (*Client) SetMaxResponseBodyBytes added in v0.4.0

func (c *Client) SetMaxResponseBodyBytes(n int)

SetMaxResponseBodyBytes caps the number of response bytes read per request. A non-positive n resets the cap to DefaultMaxResponseBodyBytes.

func (*Client) SetUserAgent

func (c *Client) SetUserAgent(ua string)

SetUserAgent sets the User-Agent string sent with every request.

type ClientEntity

type ClientEntity struct {
	ID           string   `json:"id"`
	Name         string   `json:"name"`
	Address      string   `json:"address,omitempty"`
	Archived     bool     `json:"archived,omitempty"`
	CCEmails     []string `json:"ccEmails,omitempty"`
	CurrencyCode string   `json:"currencyCode,omitempty"`
	CurrencyID   string   `json:"currencyId,omitempty"`
	Email        string   `json:"email,omitempty"`
	Note         string   `json:"note,omitempty"`
	WorkspaceID  string   `json:"workspaceId,omitempty"`
}

ClientEntity is a Clockify client (billing customer). It is named ClientEntity to avoid colliding with the HTTP Client type in this package.

type CurrencyView added in v0.4.3

type CurrencyView struct {
	ID        string `json:"id,omitempty"`
	Code      string `json:"code,omitempty"`
	IsDefault bool   `json:"isDefault,omitempty"`
}

CurrencyView is a Clockify currency object: a Mongo-style id, an ISO code, and (within a workspace currency list) whether it is the workspace default. It is decode-only on Workspace.Currencies; no handler re-marshals it upstream.

type ErrorTranslation

type ErrorTranslation struct {
	Message     string   `json:"message"`
	Remediation string   `json:"remediation,omitempty"`
	Refs        []string `json:"refs,omitempty"`
}

ErrorTranslation is a client-facing rendering of an upstream failure: a clean Message, an actionable Remediation hint, and optional Refs pointing at the relevant env vars, tools, or Clockify docs.

func TranslateAPIError

func TranslateAPIError(statusCode int, body string) ErrorTranslation

TranslateAPIError maps an upstream HTTP status code and response body to an ErrorTranslation, classifying common cases (auth, permission, not-found, cross-workspace, rate-limit, plan/feature, locked) into a clean message plus a tailored remediation hint.

type ListAllOptions

type ListAllOptions struct {
	// MaxRows bounds total decoded rows before ListAll/ListAllFunc fail
	// closed. 0 uses the default production cap.
	MaxRows int
}

ListAllOptions tunes the auto-paginating ListAll/ListAllFunc helpers.

type MultipartFile

type MultipartFile struct {
	FieldName   string
	Filename    string
	ContentType string
	Data        []byte
}

MultipartFile is a single file part for multipart/form-data requests. Data is already decoded by the caller; callers must not pass filesystem paths because MCP clients should not grant arbitrary server file access.

type PaginationCapError

type PaginationCapError struct {
	Path        string
	RowsScanned int
	MaxRows     int
	Page        int
	PageSize    int
}

PaginationCapError reports that a paginated scan exceeded its row cap.

func (*PaginationCapError) Error

func (e *PaginationCapError) Error() string

type Project

type Project struct {
	ID             string              `json:"id"`
	Name           string              `json:"name"`
	ClientID       string              `json:"clientId,omitempty"`
	ClientName     string              `json:"clientName,omitempty"`
	Client         any                 `json:"client,omitempty"`
	Color          string              `json:"color,omitempty"`
	Archived       bool                `json:"archived"`
	Billable       bool                `json:"billable,omitempty"`
	BudgetEstimate any                 `json:"budgetEstimate,omitempty"`
	CostRate       *Rate               `json:"costRate,omitempty"`
	Currency       any                 `json:"currency,omitempty"`
	CustomFields   any                 `json:"customFields,omitempty"`
	Duration       string              `json:"duration,omitempty"`
	Estimate       any                 `json:"estimate,omitempty"`
	EstimateReset  any                 `json:"estimateReset,omitempty"`
	Expenses       any                 `json:"expenses,omitempty"`
	Favorite       bool                `json:"favorite,omitempty"`
	HourlyRate     *Rate               `json:"hourlyRate,omitempty"`
	Memberships    []ProjectMembership `json:"memberships,omitempty"`
	Note           string              `json:"note,omitempty"`
	Public         bool                `json:"public,omitempty"`
	Tasks          []Task              `json:"tasks,omitempty"`
	Template       bool                `json:"template,omitempty"`
	TimeEstimate   any                 `json:"timeEstimate,omitempty"`
	WorkspaceID    string              `json:"workspaceId,omitempty"`
}

Project is a Clockify project, including its client linkage, rates, estimate and budget settings, memberships, and embedded tasks.

type ProjectMembership

type ProjectMembership struct {
	UserID           string `json:"userId"`
	TargetID         string `json:"targetId,omitempty"`
	MembershipType   string `json:"membershipType,omitempty"`
	MembershipStatus string `json:"membershipStatus,omitempty"`
	HourlyRate       *Rate  `json:"hourlyRate,omitempty"`
	CostRate         *Rate  `json:"costRate,omitempty"`
}

ProjectMembership models the `memberships[]` element returned on projects, tasks, workspaces, and users. Per-member rates here override their parent scope; nil means "fall through to parent."

type Rate

type Rate struct {
	Amount    int64  `json:"amount"`
	Currency  string `json:"currency,omitempty"`
	Inherited bool   `json:"inherited,omitempty"`
}

Rate is Clockify's RateDtoV1 / HourlyRateDtoV1. The Amount is always in the minor unit of the currency (cents for EUR/USD/GBP, etc.), as an integer. e.g. {amount: 4321, currency: "EUR"} represents €43.21.

Clockify can also serialise a rate as the literal string "##default", meaning "inherit from the parent scope (workspace/project/task)." We preserve that as Inherited=true with Amount=0 so callers can render it as "(inherited)" rather than dropping it.

func (*Rate) DecimalString

func (r *Rate) DecimalString() string

DecimalString renders Amount/100 with exactly two decimal places. Negative amounts are preserved verbatim. e.g. 4321 -> "43.21", 100 -> "1.00", -250 -> "-2.50".

func (*Rate) Display

func (r *Rate) Display() string

Display returns the rate in "<symbol><decimal> <CCY>" form for the few currencies we know symbols for, falling back to "<decimal> <CCY>". Examples: 4321 EUR -> "€43.21 EUR"; 4321 USD -> "$43.21 USD"; 4321 XYZ -> "43.21 XYZ".

func (*Rate) MarshalJSON

func (r *Rate) MarshalJSON() ([]byte, error)

MarshalJSON encodes a Rate as null for a nil receiver, as the "##default" sentinel string when Inherited, and otherwise as an {amount, currency} object.

func (*Rate) UnmarshalJSON

func (r *Rate) UnmarshalJSON(b []byte) error

UnmarshalJSON decodes a Clockify rate from either the "##default" inherit sentinel string (yielding Inherited=true) or an {amount, currency} object. Amount is normalized to integer minor units, rounding the float form the reports API sometimes returns to the nearest cent.

type RawResponse

type RawResponse struct {
	Header http.Header
	Body   []byte
}

RawResponse is the binary-aware envelope returned by RequestRawValues and PostReportsRaw — the raw response bytes plus enough header context for callers to recover Content-Type and Content-Disposition (filename). Used by export endpoints where the upstream returns binary (PDF/XLSX) or non-JSON text (CSV).

type SubdomainView added in v0.4.3

type SubdomainView struct {
	Name    string `json:"name,omitempty"`
	Enabled bool   `json:"enabled,omitempty"`
}

SubdomainView is a Clockify workspace subdomain configuration.

type Tag

type Tag struct {
	ID          string `json:"id"`
	Name        string `json:"name"`
	Archived    bool   `json:"archived"`
	WorkspaceID string `json:"workspaceId,omitempty"`
}

Tag is a Clockify tag used to label time entries.

type Task

type Task struct {
	ID             string   `json:"id"`
	Name           string   `json:"name"`
	ProjectID      string   `json:"projectId"`
	AssigneeID     string   `json:"assigneeId,omitempty"`
	AssigneeIDs    []string `json:"assigneeIds,omitempty"`
	Billable       bool     `json:"billable"`
	BudgetEstimate int64    `json:"budgetEstimate,omitempty"`
	CostRate       *Rate    `json:"costRate,omitempty"`
	Duration       string   `json:"duration,omitempty"`
	Estimate       string   `json:"estimate,omitempty"`
	HourlyRate     *Rate    `json:"hourlyRate,omitempty"`
	Status         string   `json:"status,omitempty"`
	UserGroupIDs   []string `json:"userGroupIds,omitempty"`
}

Task is a Clockify task within a project, including assignees, rates, and estimate/status fields.

type TimeEntry

type TimeEntry struct {
	ID                string       `json:"id"`
	Description       string       `json:"description"`
	ProjectID         string       `json:"projectId"`
	ProjectName       string       `json:"projectName,omitempty"`
	TaskID            string       `json:"taskId,omitempty"`
	TagIDs            []string     `json:"tagIds,omitempty"`
	Billable          bool         `json:"billable"`
	BillablePresent   bool         `json:"-"`
	CostRate          *Rate        `json:"costRate,omitempty"`
	CustomFieldValues any          `json:"customFieldValues,omitempty"`
	HourlyRate        *Rate        `json:"hourlyRate,omitempty"`
	IsLocked          bool         `json:"isLocked,omitempty"`
	KioskID           string       `json:"kioskId,omitempty"`
	Type              string       `json:"type,omitempty"`
	UserID            string       `json:"userId,omitempty"`
	WorkspaceID       string       `json:"workspaceId,omitempty"`
	TimeInterval      TimeInterval `json:"timeInterval"`
}

TimeEntry is a Clockify time entry. BillablePresent (excluded from JSON) records whether the upstream payload actually carried a billable field, so callers can distinguish an explicit false from an omitted value; see UnmarshalJSON.

func (TimeEntry) DurationSeconds

func (e TimeEntry) DurationSeconds() int64

DurationSeconds returns the entry's elapsed duration in whole seconds. For a running entry it measures from the start to now (UTC); it returns 0 when the start is unparseable or the computed end precedes the start.

func (TimeEntry) EndTime

func (e TimeEntry) EndTime() (time.Time, error)

EndTime parses the entry's interval end as an RFC3339 time, returning the zero time and a nil error when the entry is still running (no end set).

func (TimeEntry) IsRunning

func (e TimeEntry) IsRunning() bool

IsRunning reports whether the entry has no end time and is therefore still running.

func (TimeEntry) StartTime

func (e TimeEntry) StartTime() (time.Time, error)

StartTime parses the entry's interval start as an RFC3339 time.

func (*TimeEntry) UnmarshalJSON

func (e *TimeEntry) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes a TimeEntry and additionally records whether the payload included a billable field, setting BillablePresent so an omitted billable is distinguishable from an explicit false.

type TimeInterval

type TimeInterval struct {
	Start    string `json:"start"`
	End      string `json:"end,omitempty"`
	Duration string `json:"duration,omitempty"`
}

TimeInterval is a Clockify time-entry interval: RFC3339 start, optional end (empty while running), and an ISO-8601 duration string.

type User

type User struct {
	ID               string `json:"id"`
	Name             string `json:"name"`
	Email            string `json:"email"`
	ActiveWorkspace  string `json:"activeWorkspace,omitempty"`
	CustomFields     any    `json:"customFields,omitempty"`
	DefaultWorkspace string `json:"defaultWorkspace,omitempty"`
	Memberships      any    `json:"memberships,omitempty"`
	ProfilePicture   string `json:"profilePicture,omitempty"`
	Settings         any    `json:"settings,omitempty"`
	Status           string `json:"status,omitempty"`
}

User is a Clockify user, including the active/default workspace and profile settings returned by the users endpoints.

type Workspace

type Workspace struct {
	ID                      string              `json:"id"`
	Name                    string              `json:"name"`
	CakeOrganizationID      string              `json:"cakeOrganizationId,omitempty"`
	CostRate                *Rate               `json:"costRate,omitempty"`
	Currencies              []CurrencyView      `json:"currencies,omitempty"`
	FeatureSubscriptionType string              `json:"featureSubscriptionType,omitempty"`
	Features                []string            `json:"features,omitempty"`
	HourlyRate              *Rate               `json:"hourlyRate,omitempty"`
	ImageURL                string              `json:"imageUrl,omitempty"`
	Memberships             []ProjectMembership `json:"memberships,omitempty"`
	Subdomain               *SubdomainView      `json:"subdomain,omitempty"`
	WorkspaceSettings       any                 `json:"workspaceSettings,omitempty"`
}

Workspace is a Clockify workspace as returned by the API, including its rates, currencies, enabled features, and memberships.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL