dutybeat.psm1
|
# DutyBeat public API — PowerShell client. # Works on Windows PowerShell 5.1 and PowerShell 7+ (Core). No external dependencies. $script:DefaultBaseUri = 'https://api.dutybeat.com' # Session state set by Connect-DutyBeat (per module load). $script:DutyBeatContext = @{ ApiKey = $null; BaseUri = $script:DefaultBaseUri } function Connect-DutyBeat { <# .SYNOPSIS Stores the API key (and optional base URL) for subsequent calls in this session. .DESCRIPTION Saves your DutyBeat API key for the current PowerShell session so you don't have to pass it to every command. Alternatively, set the DUTYBEAT_API_KEY environment variable and skip this call. .PARAMETER ApiKey Your API key (starts with db_live_). Create one in the app under Settings → API keys. If omitted, the DUTYBEAT_API_KEY environment variable is used. .PARAMETER BaseUri Base URL of the API. Defaults to the production API; override only for testing. .EXAMPLE Connect-DutyBeat -ApiKey 'db_live_...' .LINK https://dutybeat.com/manuales/api #> [CmdletBinding()] param( [string]$ApiKey, [string]$BaseUri = $script:DefaultBaseUri ) $key = if ($ApiKey) { $ApiKey } elseif ($env:DUTYBEAT_API_KEY) { $env:DUTYBEAT_API_KEY } else { $null } if (-not $key) { throw "Provide -ApiKey or set the DUTYBEAT_API_KEY environment variable." } $script:DutyBeatContext = @{ ApiKey = $key; BaseUri = $BaseUri.TrimEnd('/') } Write-Verbose "Connected to $($script:DutyBeatContext.BaseUri)" } function Invoke-DbApi { # Private transport: resolves the key/base URL, builds the URI, retries on 429/5xx honouring # Retry-After, and maps the API error envelope to a terminating error. Not exported. [CmdletBinding()] param( [string]$Method = 'GET', [Parameter(Mandatory)][string]$Path, [hashtable]$Query, [hashtable]$Body, [string]$ApiKey, [string]$BaseUri, [int]$MaxRetries = 2 ) $key = if ($ApiKey) { $ApiKey } elseif ($script:DutyBeatContext.ApiKey) { $script:DutyBeatContext.ApiKey } elseif ($env:DUTYBEAT_API_KEY) { $env:DUTYBEAT_API_KEY } else { $null } if (-not $key) { throw "No API key. Run Connect-DutyBeat -ApiKey '...' or set DUTYBEAT_API_KEY." } $base = if ($BaseUri) { $BaseUri.TrimEnd('/') } elseif ($script:DutyBeatContext.BaseUri) { $script:DutyBeatContext.BaseUri } else { $script:DefaultBaseUri } $uri = "$base$Path" if ($Query -and $Query.Count -gt 0) { $pairs = foreach ($k in $Query.Keys) { "$k=$([uri]::EscapeDataString([string]$Query[$k]))" } $uri += '?' + ($pairs -join '&') } $headers = @{ Authorization = "Bearer $key"; Accept = 'application/json' } # A JSON body for write methods (POST/PATCH). Depth 5 covers the flat payloads we send. $restArgs = @{ Method = $Method; Uri = $uri; Headers = $headers; ErrorAction = 'Stop' } if ($PSBoundParameters.ContainsKey('Body') -and $Body) { $restArgs.Body = ($Body | ConvertTo-Json -Depth 5 -Compress) $restArgs.ContentType = 'application/json' } for ($attempt = 0; ; $attempt++) { try { return Invoke-RestMethod @restArgs } catch { $status = $null try { $status = [int]$_.Exception.Response.StatusCode } catch {} if (($status -in 429, 500, 502, 503, 504) -and $attempt -lt $MaxRetries) { $delay = [Math]::Min([Math]::Pow(2, $attempt), 8) try { $ra = $_.Exception.Response.Headers['Retry-After'] if ($ra) { $delay = [double]$ra } } catch {} Start-Sleep -Seconds $delay continue } $code = $null $message = $_.Exception.Message if ($_.ErrorDetails -and $_.ErrorDetails.Message) { try { $body = $_.ErrorDetails.Message | ConvertFrom-Json if ($body.error) { $code = $body.error.code; $message = $body.error.message } } catch {} } $prefix = 'DutyBeat API error' if ($status) { $prefix += " $status" } if ($code) { $prefix += " ($code)" } throw "${prefix}: $message" } } } function Get-DbMe { <# .SYNOPSIS Returns who the API key is (GET /api/v1/me). .DESCRIPTION Returns the company (tenant) the key belongs to, the methods it can call (scopes) and the user it acts as (acts_as_user, whose permissions authorize the key's writes). Not scope-gated: any valid key can call it, so it's a good first request to verify the key works. The `data` object has `tenant` ({ id, name }), `scopes` (an array of method ids) and `acts_as_user` ({ id, email, name }, or $null for a legacy key with no bound user). .PARAMETER ApiKey API key to use for this call. Defaults to the one from Connect-DutyBeat or DUTYBEAT_API_KEY. .PARAMETER BaseUri Base URL of the API. Defaults to the session value or the production API. .OUTPUTS PSCustomObject with `tenant` ({ id, name }), `scopes` (array of method ids) and `acts_as_user` ({ id, email, name } or $null). .EXAMPLE Get-DbMe .EXAMPLE (Get-DbMe).scopes -join ', ' .EXAMPLE (Get-DbMe).acts_as_user.name .LINK https://dutybeat.com/manuales/api/identidad-clave #> [CmdletBinding()] param( [string]$ApiKey, [string]$BaseUri ) $response = Invoke-DbApi -Method 'GET' -Path '/api/v1/me' -ApiKey $ApiKey -BaseUri $BaseUri $response.data } function Get-DbUser { <# .SYNOPSIS Gets a user's full record (GET /api/v1/users/:user_id). .DESCRIPTION Returns the account and profile data for one user — the same fields shown on the user's profile page in the app (department, work center, DNI, IBAN, SSN, and more). Requires a key with the users.get method enabled. A user from another company (or a non-existent id) raises a not-found error. .PARAMETER UserId The user's id within your company. .PARAMETER IncludeFolders Also return the employee's document folders (metadata only: id, name and document count). .PARAMETER ApiKey API key to use for this call. Defaults to the one from Connect-DutyBeat or DUTYBEAT_API_KEY. .PARAMETER BaseUri Base URL of the API. Defaults to the session value or the production API. .OUTPUTS PSCustomObject with the user's fields: id, full_name, email, role, status, department, work_center, profile (dni, iban, ssn, ...) and, if requested, folders. .EXAMPLE Get-DbUser -UserId '9f1c...' -IncludeFolders .EXAMPLE Connect-DutyBeat -ApiKey 'db_live_...' (Get-DbUser -UserId '9f1c...').profile.iban .LINK https://dutybeat.com/manuales/api/leer-usuario #> [CmdletBinding()] param( [Parameter(Mandatory, Position = 0, ValueFromPipelineByPropertyName)][string]$UserId, [switch]$IncludeFolders, [string]$ApiKey, [string]$BaseUri ) process { $query = @{} if ($IncludeFolders) { $query['include_folders'] = 'true' } $response = Invoke-DbApi -Method 'GET' -Path "/api/v1/users/$UserId" -Query $query -ApiKey $ApiKey -BaseUri $BaseUri $response.data } } function Get-DbUsers { <# .SYNOPSIS Lists users, paginated (GET /api/v1/users). .DESCRIPTION Returns a page of users from your company (25 per page by default, max 100), with optional filters. The result has a `data` array (the users on this page) and a `meta` object with `page`, `page_size` and `total`. Iterate -Page 1, 2, ... until you have seen `meta.total` users. Requires a key with the users.list method enabled. .PARAMETER Page Page to fetch (1-based). Defaults to 1. .PARAMETER PageSize Users per page (default 25, max 100). .PARAMETER Detail Level of detail: 'reduced' (default, basic fields) or 'full' (complete record with profile). .PARAMETER Status Filter by status: 'active' or 'disabled'. .PARAMETER Role Filter by role: 'member' or 'admin'. .PARAMETER DepartmentId Filter by department id. .PARAMETER WorkCenterId Filter by work center (sede) id. .PARAMETER Email Resolve an exact email (case-insensitive) to its user — at most one result. Use it to map your own external identifier (the email) to our id. .PARAMETER Query Search by full name or email (substring). For an exact email, use -Email. .PARAMETER ApiKey API key to use for this call. Defaults to the one from Connect-DutyBeat or DUTYBEAT_API_KEY. .PARAMETER BaseUri Base URL of the API. Defaults to the session value or the production API. .OUTPUTS PSCustomObject with `data` (an array of users) and `meta` (page, page_size, total). .EXAMPLE Get-DbUsers -PageSize 50 -Status active .EXAMPLE (Get-DbUsers -Email 'javier.ortega@atlantica-ing.com').data[0].id .EXAMPLE (Get-DbUsers -Detail full -Role admin).data | ForEach-Object { $_.profile.iban } .LINK https://dutybeat.com/manuales/api/listar-usuarios #> [CmdletBinding()] param( [int]$Page = 1, [ValidateRange(1, 100)][int]$PageSize = 25, [ValidateSet('reduced', 'full')][string]$Detail = 'reduced', [ValidateSet('active', 'disabled')][string]$Status, [ValidateSet('member', 'admin')][string]$Role, [string]$DepartmentId, [string]$WorkCenterId, [string]$Email, [string]$Query, [string]$ApiKey, [string]$BaseUri ) $q = @{ page = $Page; page_size = $PageSize; detail = $Detail } if ($Status) { $q['status'] = $Status } if ($Role) { $q['role'] = $Role } if ($DepartmentId) { $q['department_id'] = $DepartmentId } if ($WorkCenterId) { $q['work_center_id'] = $WorkCenterId } if ($Email) { $q['email'] = $Email } if ($Query) { $q['q'] = $Query } Invoke-DbApi -Method 'GET' -Path '/api/v1/users' -Query $q -ApiKey $ApiKey -BaseUri $BaseUri } function New-DbUser { <# .SYNOPSIS Creates an employee (POST /api/v1/users). .DESCRIPTION Registers an employee (account fields) in your company. Only -Email and -FullName are required. -Password is optional: omit it and the employee sets their own via "forgot password"; if given it must be at least 8 characters. Creating a -Role 'admin' user requires the key to act as an administrator. The email must be unique. Returns the created user's `data` object. .PARAMETER Email Login email (must be unique). .PARAMETER FullName The employee's full name. .PARAMETER Password Optional initial password (min. 8 chars). If omitted, the employee sets it via reset. .PARAMETER Role 'member' (default) or 'admin' (requires an admin key). .PARAMETER DepartmentId Department id the employee belongs to. .PARAMETER WorkCenterId Work center (sede) id. .PARAMETER HireDate Hire date (YYYY-MM-DD). .PARAMETER VacationDays Annual vacation days (defaults to the company's). .PARAMETER WorksHolidays Whether they work on public holidays. .PARAMETER RemoteWorkAllowed Whether they may clock in remotely. .PARAMETER Dni DNI/NIE — the kiosk identifier is derived from its digits. .PARAMETER Phone Phone — the initial kiosk PIN is derived from its last 4 digits. .OUTPUTS PSCustomObject: the created user (same shape as Get-DbUser). .EXAMPLE New-DbUser -Email ana@empresa.com -FullName 'Ana Ruiz' -DepartmentId dept-estm .LINK https://dutybeat.com/manuales/api/crear-usuario #> [CmdletBinding()] param( [Parameter(Mandatory)][string]$Email, [Parameter(Mandatory)][string]$FullName, [string]$Password, [ValidateSet('member', 'admin')][string]$Role, [string]$DepartmentId, [string]$WorkCenterId, [string]$HireDate, [int]$VacationDays, [switch]$WorksHolidays, [switch]$RemoteWorkAllowed, [string]$Dni, [string]$Phone, [string]$ApiKey, [string]$BaseUri ) $body = @{ email = $Email; full_name = $FullName } if ($Password) { $body['password'] = $Password } if ($Role) { $body['role'] = $Role } if ($DepartmentId) { $body['department_id'] = $DepartmentId } if ($WorkCenterId) { $body['work_center_id'] = $WorkCenterId } if ($HireDate) { $body['hire_date'] = $HireDate } if ($PSBoundParameters.ContainsKey('VacationDays')) { $body['vacation_days'] = $VacationDays } if ($WorksHolidays.IsPresent) { $body['works_holidays'] = $true } if ($RemoteWorkAllowed.IsPresent) { $body['remote_work_allowed'] = $true } if ($Dni) { $body['dni'] = $Dni } if ($Phone) { $body['phone'] = $Phone } $response = Invoke-DbApi -Method 'POST' -Path '/api/v1/users' -Body $body -ApiKey $ApiKey -BaseUri $BaseUri $response.data } function Set-DbUser { <# .SYNOPSIS Updates an employee (PATCH /api/v1/users/:user_id). .DESCRIPTION Partial edit: only the fields you pass in -Fields are changed; anything omitted is kept, and a field set to $null is cleared. Accepts both account fields (full_name, role, department_id, work_center_id, hire_date, vacation_days, works_holidays, remote_work_allowed) and any profile column (dni, iban, ssn, address, ...). Changing role to 'admin' requires the key to act as an administrator. Email and status are not editable here. Returns the updated user's `data` object. .PARAMETER UserId The user's id within your company. .PARAMETER Fields A hashtable of the fields to change, e.g. @{ remote_work_allowed = $true; iban = 'ES..' }. .OUTPUTS PSCustomObject: the updated user (same shape as Get-DbUser). .EXAMPLE Set-DbUser -UserId 'u-estm1' -Fields @{ remote_work_allowed = $true; iban = 'ES91...' } .LINK https://dutybeat.com/manuales/api/editar-usuario #> [CmdletBinding()] param( [Parameter(Mandatory)][string]$UserId, [Parameter(Mandatory)][hashtable]$Fields, [string]$ApiKey, [string]$BaseUri ) $response = Invoke-DbApi -Method 'PATCH' -Path "/api/v1/users/$UserId" -Body $Fields -ApiKey $ApiKey -BaseUri $BaseUri $response.data } function Disable-DbUser { <# .SYNOPSIS Deactivates an employee (POST /api/v1/users/:user_id/deactivate). .DESCRIPTION Disables an employee (offboarding): they can no longer sign in and their sessions are revoked. Idempotent — deactivating an already-disabled user also returns the user. The last active admin cannot be deactivated. Returns the user's `data` object, now with status 'disabled'. .PARAMETER UserId The user's id within your company. .OUTPUTS PSCustomObject: the user, now with status 'disabled'. .EXAMPLE Disable-DbUser -UserId 'u-nuevo' .LINK https://dutybeat.com/manuales/api/dar-de-baja-usuario #> [CmdletBinding()] param( [Parameter(Mandatory)][string]$UserId, [string]$ApiKey, [string]$BaseUri ) $response = Invoke-DbApi -Method 'POST' -Path "/api/v1/users/$UserId/deactivate" -ApiKey $ApiKey -BaseUri $BaseUri $response.data } function Get-DbAttendance { <# .SYNOPSIS Lists an employee's workdays (GET /api/v1/attendance). .DESCRIPTION Returns one entry per calendar day of the range [From, To] (both inclusive) for a single employee: the day's marks, the minutes actually worked (pauses already discounted) and the balance against their schedule. The result has a `data` array (the days on this page) and a `meta` object with `page`, `page_size` and `total`. The range cannot exceed 366 days. Requires a key with the attendance.list method enabled. .PARAMETER UserId The employee's id within your company. .PARAMETER From First day of the range, as YYYY-MM-DD. .PARAMETER To Last day of the range, as YYYY-MM-DD. Inclusive. .PARAMETER TimeZone IANA time zone used to bucket marks into days. Defaults to Europe/Madrid. .PARAMETER Page Page to fetch (1-based). Defaults to 1. .PARAMETER PageSize Days per page (default 25, max 100). .PARAMETER ApiKey API key to use for this call. Defaults to the one from Connect-DutyBeat or DUTYBEAT_API_KEY. .PARAMETER BaseUri Base URL of the API. Defaults to the session value or the production API. .OUTPUTS PSCustomObject with `data` (an array of days) and `meta` (page, page_size, total). .EXAMPLE Get-DbAttendance -UserId 'u-delm' -From '2026-06-01' -To '2026-06-30' .EXAMPLE (Get-DbAttendance -UserId 'u-delm' -From '2026-06-01' -To '2026-06-05').data | ForEach-Object { '{0}: {1} min' -f $_.date, $_.worked_minutes } .LINK https://dutybeat.com/manuales/api/listar-fichajes #> [CmdletBinding()] param( [Parameter(Mandatory)][string]$UserId, [Parameter(Mandatory)][string]$From, [Parameter(Mandatory)][string]$To, [string]$TimeZone, [int]$Page = 1, [ValidateRange(1, 100)][int]$PageSize = 25, [string]$ApiKey, [string]$BaseUri ) $q = @{ user_id = $UserId; from = $From; to = $To; page = $Page; page_size = $PageSize } if ($TimeZone) { $q['tz'] = $TimeZone } Invoke-DbApi -Method 'GET' -Path '/api/v1/attendance' -Query $q -ApiKey $ApiKey -BaseUri $BaseUri } function Get-DbAttendanceSummary { <# .SYNOPSIS Consolidated attendance totals per employee over a range (GET /api/v1/attendance/summary). .DESCRIPTION Returns one row per employee with their consolidated totals over [From, To] (both inclusive): minutes worked (pauses already discounted), minutes expected against their schedule and the balance between the two, plus days worked and expected. This is the payroll query ("hours per employee this month"), the company-wide counterpart to Get-DbAttendance (per employee, per day). Filterable by status, department and work center like the employee list. The result has a `data` array (this page of employees) and a `meta` object with `page`, `page_size` and `total`. The range cannot exceed 366 days. Requires a key with the attendance.summary method enabled. .PARAMETER From First day of the range, as YYYY-MM-DD. .PARAMETER To Last day of the range, as YYYY-MM-DD. Inclusive. .PARAMETER TimeZone IANA time zone used to bucket marks into days. Defaults to Europe/Madrid. .PARAMETER Status Filter by employee status: 'active' or 'disabled'. .PARAMETER DepartmentId Only employees of this department. .PARAMETER WorkCenterId Only employees of this work center (sede). .PARAMETER Page Page to fetch (1-based). Defaults to 1. .PARAMETER PageSize Employees per page (default 25, max 100). .PARAMETER ApiKey API key to use for this call. Defaults to the one from Connect-DutyBeat or DUTYBEAT_API_KEY. .PARAMETER BaseUri Base URL of the API. Defaults to the session value or the production API. .OUTPUTS PSCustomObject with `data` (an array of per-employee totals) and `meta` (page, page_size, total). .EXAMPLE Get-DbAttendanceSummary -From '2026-06-01' -To '2026-06-30' .EXAMPLE (Get-DbAttendanceSummary -From '2026-06-01' -To '2026-06-30').data | ForEach-Object { '{0}: {1} min' -f $_.full_name, $_.worked_minutes } .LINK https://dutybeat.com/manuales/api/resumen-jornada #> [CmdletBinding()] param( [Parameter(Mandatory)][string]$From, [Parameter(Mandatory)][string]$To, [string]$TimeZone, [ValidateSet('active', 'disabled')][string]$Status, [string]$DepartmentId, [string]$WorkCenterId, [int]$Page = 1, [ValidateRange(1, 100)][int]$PageSize = 25, [string]$ApiKey, [string]$BaseUri ) $q = @{ from = $From; to = $To; page = $Page; page_size = $PageSize } if ($TimeZone) { $q['tz'] = $TimeZone } if ($Status) { $q['status'] = $Status } if ($DepartmentId) { $q['department_id'] = $DepartmentId } if ($WorkCenterId) { $q['work_center_id'] = $WorkCenterId } Invoke-DbApi -Method 'GET' -Path '/api/v1/attendance/summary' -Query $q -ApiKey $ApiKey -BaseUri $BaseUri } function New-DbAbsence { <# .SYNOPSIS Registers an absence for an employee (POST /api/v1/absences). .DESCRIPTION Creates an absence on behalf of the employee -UserId. It follows the same rules as an in-app request (overlap, vacation balance, hourly slot) and enters pending or approved depending on the type. For an hourly absence pass -StartTime and -EndTime (same day). Requires the key to be able to manage employees. Returns the created absence's `data` object. .PARAMETER UserId The employee the absence is for. .PARAMETER Type Absence type key (see Get-DbAbsenceType). .PARAMETER StartDate First day, YYYY-MM-DD (not in the past). .PARAMETER EndDate Last day, YYYY-MM-DD (inclusive). .PARAMETER StartTime For an hourly absence, start HH:MM (same day as end). .PARAMETER EndTime For an hourly absence, end HH:MM. .PARAMETER Note Optional free-text note. .OUTPUTS PSCustomObject: the created absence (same shape as Get-DbAbsence -Id). .EXAMPLE New-DbAbsence -UserId u-delm -Type vacation -StartDate 2026-08-10 -EndDate 2026-08-14 .LINK https://dutybeat.com/manuales/api/crear-ausencia #> [CmdletBinding()] param( [Parameter(Mandatory)][string]$UserId, [Parameter(Mandatory)][string]$Type, [Parameter(Mandatory)][string]$StartDate, [Parameter(Mandatory)][string]$EndDate, [string]$StartTime, [string]$EndTime, [string]$Note, [string]$ApiKey, [string]$BaseUri ) $body = @{ user_id = $UserId; type = $Type; start_date = $StartDate; end_date = $EndDate } if ($StartTime) { $body['start_time'] = $StartTime } if ($EndTime) { $body['end_time'] = $EndTime } if ($Note) { $body['note'] = $Note } $response = Invoke-DbApi -Method 'POST' -Path '/api/v1/absences' -Body $body -ApiKey $ApiKey -BaseUri $BaseUri $response.data } function Set-DbAbsenceDecision { <# .SYNOPSIS Approves or rejects a pending absence (POST /api/v1/absences/:absence_id/decide). .DESCRIPTION The key's user must be able to approve it (HR department, or supervises the requester's department by hierarchy) and cannot decide their own. Only a still-pending absence can be decided. Returns the absence's `data` object, now resolved. .PARAMETER AbsenceId The absence to resolve. .PARAMETER Decision 'approved' or 'rejected'. .PARAMETER Note Optional free-text note for the decision. .OUTPUTS PSCustomObject: the absence, now resolved. .EXAMPLE Set-DbAbsenceDecision -AbsenceId 'abs-nueva' -Decision approved .LINK https://dutybeat.com/manuales/api/aprobar-rechazar-ausencia #> [CmdletBinding()] param( [Parameter(Mandatory)][string]$AbsenceId, [Parameter(Mandatory)][ValidateSet('approved', 'rejected')][string]$Decision, [string]$Note, [string]$ApiKey, [string]$BaseUri ) $body = @{ decision = $Decision } if ($Note) { $body['note'] = $Note } $response = Invoke-DbApi -Method 'POST' -Path "/api/v1/absences/$AbsenceId/decide" -Body $body -ApiKey $ApiKey -BaseUri $BaseUri $response.data } function Get-DbAbsence { <# .SYNOPSIS Gets one absence by id, or lists absences overlapping a date range (GET /api/v1/absences). .DESCRIPTION With -Id, returns a single absence by its id (GET /api/v1/absences/:absence_id) — the `data` object with its type (key + label), status, dates and, for hourly absences, the time slice; an unknown or cross-company id raises a not-found error. Requires a key with the absences.get method. Otherwise (with -From/-To) returns every absence that overlaps the range [From, To] (both inclusive), newest first — including those that start before the window or end after it. The result has a `data` array and a `meta` object with `page`, `page_size` and `total`. The range cannot exceed 366 days. Requires a key with the absences.list method enabled. .PARAMETER Id The absence's id within your company. Selects the single-absence lookup. .PARAMETER From First day of the range, as YYYY-MM-DD. .PARAMETER To Last day of the range, as YYYY-MM-DD. Inclusive. .PARAMETER UserId Only this employee's absences. Omit for the whole company. .PARAMETER Status Filter by status: 'pending', 'approved', 'rejected' or 'cancelled'. .PARAMETER Type Filter by absence type key (e.g. 'vacation', 'sick'). The catalogue is per-company. .PARAMETER Page Page to fetch (1-based). Defaults to 1. .PARAMETER PageSize Absences per page (default 25, max 100). .PARAMETER ApiKey API key to use for this call. Defaults to the one from Connect-DutyBeat or DUTYBEAT_API_KEY. .PARAMETER BaseUri Base URL of the API. Defaults to the session value or the production API. .OUTPUTS With -Id: the absence object. Otherwise a PSCustomObject with `data` (an array of absences) and `meta` (page, page_size, total). .EXAMPLE Get-DbAbsence -Id 'abs-008' .EXAMPLE Get-DbAbsence -From '2026-06-01' -To '2026-06-30' -Status approved .EXAMPLE (Get-DbAbsence -From '2026-06-01' -To '2026-06-30' -UserId 'u-delm').data | ForEach-Object { '{0} {1}' -f $_.start_date, $_.type.label } .LINK https://dutybeat.com/manuales/api/leer-ausencia #> [CmdletBinding(DefaultParameterSetName = 'List')] param( [Parameter(Mandatory, Position = 0, ParameterSetName = 'Get')][string]$Id, [Parameter(Mandatory, ParameterSetName = 'List')][string]$From, [Parameter(Mandatory, ParameterSetName = 'List')][string]$To, [Parameter(ParameterSetName = 'List')][string]$UserId, [Parameter(ParameterSetName = 'List')][ValidateSet('pending', 'approved', 'rejected', 'cancelled')][string]$Status, [Parameter(ParameterSetName = 'List')][string]$Type, [Parameter(ParameterSetName = 'List')][int]$Page = 1, [Parameter(ParameterSetName = 'List')][ValidateRange(1, 100)][int]$PageSize = 25, [string]$ApiKey, [string]$BaseUri ) if ($PSCmdlet.ParameterSetName -eq 'Get') { $response = Invoke-DbApi -Method 'GET' -Path "/api/v1/absences/$Id" -ApiKey $ApiKey -BaseUri $BaseUri return $response.data } $q = @{ from = $From; to = $To; page = $Page; page_size = $PageSize } if ($UserId) { $q['user_id'] = $UserId } if ($Status) { $q['status'] = $Status } if ($Type) { $q['type'] = $Type } Invoke-DbApi -Method 'GET' -Path '/api/v1/absences' -Query $q -ApiKey $ApiKey -BaseUri $BaseUri } function Get-DbDepartment { <# .SYNOPSIS Lists departments, paginated (GET /api/v1/departments). .DESCRIPTION Returns a page of your company's departments, alphabetical, each with its employee headcount (`employee_count`) and its supervisor department (`supervisor`, the one above it in the org tree, or $null at the root). The result has a `data` array (the departments on this page) and a `meta` object with `page`, `page_size` and `total`. Iterate -Page 1, 2, ... until you have seen `meta.total` departments. Requires a key with the departments.list method enabled. .PARAMETER Page Page to fetch (1-based). Defaults to 1. .PARAMETER PageSize Departments per page (default 25, max 100). .PARAMETER ApiKey API key to use for this call. Defaults to the one from Connect-DutyBeat or DUTYBEAT_API_KEY. .PARAMETER BaseUri Base URL of the API. Defaults to the session value or the production API. .OUTPUTS PSCustomObject with `data` (an array of departments) and `meta` (page, page_size, total). .EXAMPLE Get-DbDepartment -PageSize 50 .EXAMPLE (Get-DbDepartment).data | ForEach-Object { '{0}: {1}' -f $_.name, $_.employee_count } .LINK https://dutybeat.com/manuales/api/listar-departamentos #> [CmdletBinding()] param( [int]$Page = 1, [ValidateRange(1, 100)][int]$PageSize = 25, [string]$ApiKey, [string]$BaseUri ) $q = @{ page = $Page; page_size = $PageSize } Invoke-DbApi -Method 'GET' -Path '/api/v1/departments' -Query $q -ApiKey $ApiKey -BaseUri $BaseUri } function Get-DbWorkCenter { <# .SYNOPSIS Lists work centers / sedes, paginated (GET /api/v1/work-centers). .DESCRIPTION Returns a page of your company's work centers (sedes), alphabetical, each with its location: `country` and — when set — `region`, `province` and `municipality`, each a { code, name } object (or $null). The result has a `data` array (the work centers on this page) and a `meta` object with `page`, `page_size` and `total`. Iterate -Page 1, 2, ... until you have seen `meta.total`. Requires a key with the work-centers.list method enabled. .PARAMETER Page Page to fetch (1-based). Defaults to 1. .PARAMETER PageSize Work centers per page (default 25, max 100). .PARAMETER ApiKey API key to use for this call. Defaults to the one from Connect-DutyBeat or DUTYBEAT_API_KEY. .PARAMETER BaseUri Base URL of the API. Defaults to the session value or the production API. .OUTPUTS PSCustomObject with `data` (an array of work centers) and `meta` (page, page_size, total). .EXAMPLE Get-DbWorkCenter -PageSize 50 .EXAMPLE (Get-DbWorkCenter).data | ForEach-Object { '{0}: {1}' -f $_.name, $_.region.name } .LINK https://dutybeat.com/manuales/api/listar-sedes #> [CmdletBinding()] param( [int]$Page = 1, [ValidateRange(1, 100)][int]$PageSize = 25, [string]$ApiKey, [string]$BaseUri ) $q = @{ page = $Page; page_size = $PageSize } Invoke-DbApi -Method 'GET' -Path '/api/v1/work-centers' -Query $q -ApiKey $ApiKey -BaseUri $BaseUri } function Get-DbExpense { <# .SYNOPSIS Gets one expense by id, or lists expenses / receipts (GET /api/v1/expenses). .DESCRIPTION With -Id, returns a single expense by its id (GET /api/v1/expenses/:expense_id) — the `data` object with its OCR money fields, category, status and reconciled flag; an unknown or cross-company id raises a not-found error. Requires a key with the expenses.get method. Otherwise returns a page of your company's expense receipts (Mis Gastos), newest first. Each expense carries its OCR-extracted money fields (`total_amount`, `tax_amount`, `currency`, `amount_eur`, `fx_rate`), `category`, `status` (processing / imported / error) and `reconciled` (whether a bank transaction is matched to it). The result has a `data` array and a `meta` object with `page`, `page_size` and `total`. Requires a key with the expenses.list method enabled. .PARAMETER Id The expense's id within your company. Selects the single-expense lookup. .PARAMETER UserId Only this employee's expenses. Omit for the whole company. .PARAMETER Status Filter by status: 'processing', 'imported' or 'error'. .PARAMETER Page Page to fetch (1-based). Defaults to 1. .PARAMETER PageSize Expenses per page (default 25, max 100). .PARAMETER ApiKey API key to use for this call. Defaults to the one from Connect-DutyBeat or DUTYBEAT_API_KEY. .PARAMETER BaseUri Base URL of the API. Defaults to the session value or the production API. .OUTPUTS With -Id: the expense object. Otherwise a PSCustomObject with `data` (an array of expenses) and `meta` (page, page_size, total). .EXAMPLE Get-DbExpense -Id 'exp-dem-1' .EXAMPLE Get-DbExpense -Status imported .EXAMPLE (Get-DbExpense -UserId 'u-civm1').data | ForEach-Object { '{0}: {1} {2}' -f $_.merchant, $_.amount_eur, $_.currency } .LINK https://dutybeat.com/manuales/api/leer-gasto #> [CmdletBinding(DefaultParameterSetName = 'List')] param( [Parameter(Mandatory, Position = 0, ParameterSetName = 'Get')][string]$Id, [Parameter(ParameterSetName = 'List')][string]$UserId, [Parameter(ParameterSetName = 'List')][ValidateSet('processing', 'imported', 'error')][string]$Status, [Parameter(ParameterSetName = 'List')][int]$Page = 1, [Parameter(ParameterSetName = 'List')][ValidateRange(1, 100)][int]$PageSize = 25, [string]$ApiKey, [string]$BaseUri ) if ($PSCmdlet.ParameterSetName -eq 'Get') { $response = Invoke-DbApi -Method 'GET' -Path "/api/v1/expenses/$Id" -ApiKey $ApiKey -BaseUri $BaseUri return $response.data } $q = @{ page = $Page; page_size = $PageSize } if ($UserId) { $q['user_id'] = $UserId } if ($Status) { $q['status'] = $Status } Invoke-DbApi -Method 'GET' -Path '/api/v1/expenses' -Query $q -ApiKey $ApiKey -BaseUri $BaseUri } function Get-DbAbsenceType { <# .SYNOPSIS Lists the absence-type catalogue, paginated (GET /api/v1/absence-types). .DESCRIPTION Returns your company's absence-type catalogue: the `key` that each absence's `type.key` references, plus the attributes that interpret it (`paid`, `consumes_vacation`, `requires_approval`, `requires_justification`, `allows_hourly`, `day_count_mode` = working/natural) and `active`. Retired types (`active = $false`) are included so a historical `type.key` always resolves. The result has a `data` array and a `meta` object with `page`, `page_size` and `total`. Requires a key with the absence-types.list method enabled. .PARAMETER Page Page to fetch (1-based). Defaults to 1. .PARAMETER PageSize Types per page (default 25, max 100). The catalogue is small. .PARAMETER ApiKey API key to use for this call. Defaults to the one from Connect-DutyBeat or DUTYBEAT_API_KEY. .PARAMETER BaseUri Base URL of the API. Defaults to the session value or the production API. .OUTPUTS PSCustomObject with `data` (an array of absence types) and `meta` (page, page_size, total). .EXAMPLE Get-DbAbsenceType .EXAMPLE (Get-DbAbsenceType).data | ForEach-Object { '{0}: {1}' -f $_.key, $_.label } .LINK https://dutybeat.com/manuales/api/listar-tipos-ausencia #> [CmdletBinding()] param( [int]$Page = 1, [ValidateRange(1, 100)][int]$PageSize = 25, [string]$ApiKey, [string]$BaseUri ) $q = @{ page = $Page; page_size = $PageSize } Invoke-DbApi -Method 'GET' -Path '/api/v1/absence-types' -Query $q -ApiKey $ApiKey -BaseUri $BaseUri } function Get-DbHoliday { <# .SYNOPSIS Lists a work center's holidays, paginated (GET /api/v1/holidays). .DESCRIPTION Returns the festivo calendar of one work center (sede), oldest first. Each holiday has a `date`, a `name` and a `type` (national / regional / local). The result has a `data` array and a `meta` object with `page`, `page_size` and `total`. Requires a key with the holidays.list method enabled. .PARAMETER WorkCenterId The work center whose calendar to fetch (required). Discover ids with Get-DbWorkCenter. .PARAMETER Year Narrow to a single year (YYYY). Omit for the full calendar. .PARAMETER Page Page to fetch (1-based). Defaults to 1. .PARAMETER PageSize Holidays per page (default 25, max 100). .PARAMETER ApiKey API key to use for this call. Defaults to the one from Connect-DutyBeat or DUTYBEAT_API_KEY. .PARAMETER BaseUri Base URL of the API. Defaults to the session value or the production API. .OUTPUTS PSCustomObject with `data` (an array of holidays) and `meta` (page, page_size, total). .EXAMPLE Get-DbHoliday -WorkCenterId 'wc-madrid' -Year '2026' .EXAMPLE (Get-DbHoliday -WorkCenterId 'wc-madrid').data | ForEach-Object { '{0}: {1}' -f $_.date, $_.name } .LINK https://dutybeat.com/manuales/api/listar-festivos #> [CmdletBinding()] param( [Parameter(Mandatory)][string]$WorkCenterId, [string]$Year, [int]$Page = 1, [ValidateRange(1, 100)][int]$PageSize = 25, [string]$ApiKey, [string]$BaseUri ) $q = @{ work_center_id = $WorkCenterId; page = $Page; page_size = $PageSize } if ($Year) { $q['year'] = $Year } Invoke-DbApi -Method 'GET' -Path '/api/v1/holidays' -Query $q -ApiKey $ApiKey -BaseUri $BaseUri } Export-ModuleMember -Function 'Connect-DutyBeat', 'Get-DbMe', 'Get-DbUser', 'Get-DbUsers', 'New-DbUser', 'Set-DbUser', 'Disable-DbUser', 'Get-DbAttendance', 'Get-DbAttendanceSummary', 'Get-DbAbsence', 'New-DbAbsence', 'Set-DbAbsenceDecision', 'Get-DbAbsenceType', 'Get-DbDepartment', 'Get-DbWorkCenter', 'Get-DbExpense', 'Get-DbHoliday' |