Private/Invoke-PiholeApi.ps1

function Invoke-PiholeApi {
    <#
    .SYNOPSIS
        Sends an authenticated request to the Pi-hole v6 API.

    .DESCRIPTION
        The single HTTP boundary for the module. Resolves and lazily auths
        the supplied context, attaches the sid header, and sends the request.

        On a 401 response (sid expired or revoked), re-auths once and retries
        the request. Any other error is rethrown as-is.

    .PARAMETER Context
        A PSPiHole.Context (typically from Resolve-PiholeContext).

    .PARAMETER Method
        HTTP method. GET, POST, PUT, DELETE, or PATCH.

    .PARAMETER Path
        API path relative to /api/ (e.g. 'config/dns').

    .PARAMETER Body
        Optional request body. Serialised to JSON.

    .EXAMPLE
        Invoke-PiholeApi -Context $ctx -Method GET -Path 'config/dns'
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [psobject]
        $Context,

        [Parameter(Mandatory)]
        [ValidateSet('GET', 'POST', 'PUT', 'DELETE', 'PATCH')]
        [string]
        $Method,

        [Parameter(Mandatory)]
        [string]
        $Path,

        [object]
        $Body
    )

    if (-not $Context.Session) {
        $Context.Session = Get-PiholeSession -Context $Context
    }

    $hasBody = $PSBoundParameters.ContainsKey('Body')

    $sendRequest = {
        $splat = @{
            Uri     = "$($Context.BaseUri)/$Path"
            Method  = $Method
            Headers = @{sid = $Context.Session.Sid}
        }
        if ($hasBody) {
            $splat.Body        = ($Body | ConvertTo-Json -Compress)
            $splat.ContentType = 'application/json'
        }
        if ($Context.SkipCertificateCheck) {
            $splat.SkipCertificateCheck = $true
        }
        Invoke-RestMethod @splat
    }

    try {
        & $sendRequest
    }
    catch {
        $status = $null
        if ($_.Exception.PSObject.Properties['Response'] -and $_.Exception.Response) {
            $status = [int]$_.Exception.Response.StatusCode
        }

        if ($status -eq 401) {
            $Context.Session = Get-PiholeSession -Context $Context
            & $sendRequest
        }
        else {
            throw
        }
    }
}