Documentation
¶
Index ¶
- Constants
- Variables
- func CentsFromReportNumber(v float64) int64
- func ListAll[T any](ctx context.Context, c *Client, path string, baseQuery map[string]string) ([]T, error)
- func ListAllFuncWithOptions[T any](ctx context.Context, c *Client, path string, baseQuery map[string]string, ...) error
- func ListAllWithOptions[T any](ctx context.Context, c *Client, path string, baseQuery map[string]string, ...) ([]T, error)
- type APIError
- type CircuitBreaker
- type CircuitBreakerConfig
- type CircuitBreakerOpenError
- type Client
- func (c *Client) AuditLogBaseURL() string
- func (c *Client) Close()
- func (c *Client) Delete(ctx context.Context, path string) error
- func (c *Client) DeleteReports(ctx context.Context, path string) error
- func (c *Client) DeleteReportsCaptureValues(ctx context.Context, path string, query url.Values, out any) error
- func (c *Client) DeleteWithBody(ctx context.Context, path string, body any, out any) error
- func (c *Client) DeleteWithQuery(ctx context.Context, path string, query map[string]string) error
- func (c *Client) DeleteWithQueryCapture(ctx context.Context, path string, query map[string]string, out any) error
- func (c *Client) DeleteWithQueryCaptureValues(ctx context.Context, path string, query url.Values, out any) error
- func (c *Client) DocumentedBaseURL(host string) string
- func (c *Client) Get(ctx context.Context, path string, query map[string]string, out any) error
- func (c *Client) GetReports(ctx context.Context, path string, query map[string]string, out any) error
- func (c *Client) GetReportsValues(ctx context.Context, path string, query url.Values, out any) error
- func (c *Client) GetValues(ctx context.Context, path string, query url.Values, out any) error
- func (c *Client) HTTPTimeout() time.Duration
- func (c *Client) MaxResponseBodyBytes() int64
- func (c *Client) Patch(ctx context.Context, path string, body any, out any) error
- func (c *Client) Post(ctx context.Context, path string, body any, out any) error
- func (c *Client) PostAuditLog(ctx context.Context, path string, body any, out any) error
- func (c *Client) PostMultipart(ctx context.Context, path string, form url.Values, out any) error
- func (c *Client) PostMultipartWithFiles(ctx context.Context, path string, form url.Values, files []MultipartFile, ...) error
- func (c *Client) PostReports(ctx context.Context, path string, body any, out any) error
- func (c *Client) PostReportsRaw(ctx context.Context, path string, body any) (*RawResponse, error)
- func (c *Client) PostWithQuery(ctx context.Context, path string, query map[string]string, body any, out any) error
- func (c *Client) Put(ctx context.Context, path string, body any, out any) error
- func (c *Client) PutMultipart(ctx context.Context, path string, form url.Values, out any) error
- func (c *Client) PutReports(ctx context.Context, path string, body any, out any) error
- func (c *Client) PutWithQuery(ctx context.Context, path string, query map[string]string, body any, out any) error
- func (c *Client) ReportsBaseURL() string
- func (c *Client) RequestRawValues(ctx context.Context, reportsHost bool, method, path string, query url.Values, ...) (*RawResponse, error)
- func (c *Client) RequestRawValuesForHost(ctx context.Context, host, method, path string, query url.Values, body any) (*RawResponse, error)
- func (c *Client) SetCircuitBreaker(cfg CircuitBreakerConfig)
- func (c *Client) SetMaxResponseBodyBytes(n int)
- func (c *Client) SetUserAgent(ua string)
- type ClientEntity
- type CurrencyView
- type ErrorTranslation
- type ListAllOptions
- type MultipartFile
- type PaginationCapError
- type Project
- type ProjectMembership
- type Rate
- type RawResponse
- type SubdomainView
- type Tag
- type Task
- type TimeEntry
- type TimeInterval
- type User
- type Workspace
Constants ¶
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.
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 ¶
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 ¶
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.
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 ¶
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) ErrorTranslation ¶
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 ¶
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 ¶
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 ¶
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 ¶
AuditLogBaseURL returns the dedicated Clockify audit-log API base URL. Like ReportsBaseURL, custom/test base URLs remain unchanged.
func (*Client) DeleteReports ¶
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 ¶
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 ¶
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 ¶
DocumentedBaseURL resolves a generated OpenAPI host to the concrete base URL this client should use.
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 ¶
GetValues is like Get but accepts url.Values query parameters, allowing repeated keys.
func (*Client) HTTPTimeout ¶ added in v0.4.0
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
MaxResponseBodyBytes returns the current response-body cap, falling back to DefaultMaxResponseBodyBytes for a nil client or an unset cap.
func (*Client) Patch ¶
Patch issues a PATCH with a JSON body to the main host and decodes the response into out.
func (*Client) Post ¶
Post issues a POST with a JSON body to the main host and decodes the response into out.
func (*Client) PostAuditLog ¶
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 ¶
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 ¶
PostReports performs a POST against the reports host.
func (*Client) PostReportsRaw ¶
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 ¶
Put issues a PUT with a JSON body to the main host and decodes the response into out.
func (*Client) PutMultipart ¶
PutMultipart performs a PUT with a multipart/form-data body. Same encoding rules as PostMultipart.
func (*Client) PutReports ¶
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 ¶
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
SetMaxResponseBodyBytes caps the number of response bytes read per request. A non-positive n resets the cap to DefaultMaxResponseBodyBytes.
func (*Client) SetUserAgent ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
IsRunning reports whether the entry has no end time and is therefore still running.
func (*TimeEntry) UnmarshalJSON ¶
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.