GOV.UK.Notify.psm1

<#
    GOV.UK.Notify root module.

    GENERATED FILE - DO NOT EDIT.
    Compiled from the per-function source files under src/GOV.UK.Notify/Private and
    src/GOV.UK.Notify/Public by build.ps1. Edit those files, then re-run:
        pwsh -File ./build.ps1 -Task Build
#>


# -- Module-scoped session context. Populated by Connect-GovUKNotify and read by the API helpers.
$script:GovUKNotifyContext = $null

#region src/GOV.UK.Notify/Private/ConvertTo-GovUKNotifyBase64Url.ps1
function ConvertTo-GovUKNotifyBase64Url {
    <#
        .SYNOPSIS
        Encodes a byte array as a base64url string (RFC 7515) with padding removed.

        .DESCRIPTION
        Internal helper used when building JSON Web Tokens. Converts standard base64 to the
        URL-safe alphabet ('+' -> '-', '/' -> '_') and strips '=' padding.

        .PARAMETER Bytes
        The bytes to encode.

        .OUTPUTS
        System.String
    #>

    [CmdletBinding()]
    [OutputType([string])]
    param(
        [Parameter(Mandatory = $true)]
        [byte[]]$Bytes
    )

    return [Convert]::ToBase64String($Bytes).TrimEnd('=').Replace('+', '-').Replace('/', '_')
}
#endregion src/GOV.UK.Notify/Private/ConvertTo-GovUKNotifyBase64Url.ps1

#region src/GOV.UK.Notify/Private/Invoke-GovUKNotifyApi.ps1
function Invoke-GovUKNotifyApi {
    <#
        .SYNOPSIS
        Sends an authenticated request to the GOV.UK Notify REST API.

        .DESCRIPTION
        Internal request engine used by every public cmdlet. It resolves the API key and base URL,
        generates a fresh JWT for each attempt, serialises the request body to JSON, and parses the
        response. Transient failures (HTTP 429, 500, 502, 503, 504) are retried with exponential
        backoff, honouring a Retry-After header when present. GOV.UK Notify error responses are parsed
        and surfaced as a single, descriptive terminating error containing the HTTP status code and the
        Notify error type.

        .PARAMETER Method
        The HTTP method, 'GET' or 'POST'.

        .PARAMETER Path
        The request path appended to the base URL, for example '/v2/notifications/email'.

        .PARAMETER Body
        An optional hashtable serialised to a JSON request body for POST requests.

        .PARAMETER ApiKey
        An explicit API key. If omitted, the connected session context is used.

        .PARAMETER BaseUrl
        An explicit base URL. If omitted, the connected session context or the production default is used.

        .PARAMETER Raw
        Return the raw response bytes instead of parsed JSON. Used for the letter PDF endpoint.

        .PARAMETER MaxRetry
        Maximum number of retries for transient failures. Defaults to 3.

        .PARAMETER RetryDelaySeconds
        Base delay, in seconds, used for exponential backoff between retries. Defaults to 1.

        .OUTPUTS
        The parsed JSON response object, or System.Byte[] when -Raw is specified.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [ValidateSet('GET', 'POST')]
        [string]$Method,

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

        [hashtable]$Body,

        [string]$ApiKey,

        [string]$BaseUrl,

        [switch]$Raw,

        [int]$MaxRetry = 3,

        [int]$RetryDelaySeconds = 1
    )

    $Context = Resolve-GovUKNotifyContext -ApiKey $ApiKey -BaseUrl $BaseUrl
    $Uri = '{0}{1}' -f $Context.BaseUrl, $Path

    $JsonBody = $null
    if ($PSBoundParameters.ContainsKey('Body') -and $null -ne $Body) {
        $JsonBody = $Body | ConvertTo-Json -Depth 10
    }

    $RetryableStatus = @(429, 500, 502, 503, 504)
    $Attempt = 0

    while ($true) {
        $Attempt++

        # -- A fresh token per attempt: GOV.UK Notify tokens expire ~30 seconds after issue.
        $Token = New-GovUKNotifyJwt -ApiKey $Context.ApiKey
        $Headers = @{ Authorization = "Bearer $Token" }

        try {
            if ($Raw) {
                $Response = Invoke-WebRequest -Uri $Uri -Method $Method -Headers $Headers -UseBasicParsing -ErrorAction Stop
                # -- Return the byte array as a single object so the pipeline does not unroll it.
                return , $Response.RawContentStream.ToArray()
            }

            $RequestParams = @{
                Uri         = $Uri
                Method      = $Method
                Headers     = $Headers
                ErrorAction = 'Stop'
            }
            if ($null -ne $JsonBody) {
                $RequestParams.Body = $JsonBody
                $RequestParams.ContentType = 'application/json'
            }

            return Invoke-RestMethod @RequestParams
        }
        catch {
            $ErrorRecordItem = $_
            $StatusCode = $null
            $ResponseBody = $null

            # -- Extract the HTTP status code (the StatusCode enum type is shared across PS editions).
            if ($ErrorRecordItem.Exception.Response) {
                try { $StatusCode = [int]$ErrorRecordItem.Exception.Response.StatusCode } catch { $StatusCode = $null }
            }

            # -- Extract the response body. PowerShell 7 populates ErrorDetails.Message; Windows
            # PowerShell 5.1 requires reading the response stream.
            if ($ErrorRecordItem.ErrorDetails -and $ErrorRecordItem.ErrorDetails.Message) {
                $ResponseBody = $ErrorRecordItem.ErrorDetails.Message
            }
            elseif ($ErrorRecordItem.Exception.Response -and ($ErrorRecordItem.Exception.Response | Get-Member -Name GetResponseStream -ErrorAction SilentlyContinue)) {
                try {
                    $Stream = $ErrorRecordItem.Exception.Response.GetResponseStream()
                    $Reader = [System.IO.StreamReader]::new($Stream)
                    try { $ResponseBody = $Reader.ReadToEnd() } finally { $Reader.Dispose() }
                }
                catch {
                    $ResponseBody = $null
                }
            }

            # -- Parse the Notify error payload (status_code + errors[]) for a friendly message.
            $ErrorSummary = $null
            if (-not [string]::IsNullOrWhiteSpace($ResponseBody)) {
                try {
                    $Parsed = $ResponseBody | ConvertFrom-Json -ErrorAction Stop
                    if ($Parsed.status_code) { $StatusCode = [int]$Parsed.status_code }
                    if ($Parsed.errors) {
                        $ErrorSummary = ($Parsed.errors | ForEach-Object { "$($_.error): $($_.message)" }) -join '; '
                    }
                }
                catch {
                    $ErrorSummary = $ResponseBody
                }
            }
            if ([string]::IsNullOrWhiteSpace($ErrorSummary)) {
                $ErrorSummary = $ErrorRecordItem.Exception.Message
            }

            # -- Retry transient failures with exponential backoff.
            if ($StatusCode -in $RetryableStatus -and $Attempt -le $MaxRetry) {
                $Delay = [Math]::Min([Math]::Pow(2, $Attempt - 1) * $RetryDelaySeconds, 30)

                # -- Honour Retry-After when the server provides it.
                try {
                    $ResponseHeaders = $ErrorRecordItem.Exception.Response.Headers
                    if ($ResponseHeaders) {
                        if ($ResponseHeaders['Retry-After']) {
                            $Delay = [int]$ResponseHeaders['Retry-After']
                        }
                        elseif ($ResponseHeaders.RetryAfter -and $ResponseHeaders.RetryAfter.Delta) {
                            $Delay = [int]$ResponseHeaders.RetryAfter.Delta.TotalSeconds
                        }
                    }
                }
                catch {
                    Write-Verbose 'Unable to read the Retry-After header; using the computed backoff delay.'
                }

                Write-Verbose ("GOV.UK Notify request failed (HTTP {0}). Retry {1}/{2} in {3}s." -f $StatusCode, $Attempt, $MaxRetry, $Delay)
                Start-Sleep -Seconds $Delay
                continue
            }

            # -- Non-retryable, or retries exhausted: throw a single descriptive terminating error.
            $StatusText = if ($StatusCode) { " (HTTP $StatusCode)" } else { '' }
            $Message = "GOV.UK Notify API request '$Method $Path' failed${StatusText}: $ErrorSummary"
            $Exception = [System.Exception]::new($Message, $ErrorRecordItem.Exception)
            $NewError = [System.Management.Automation.ErrorRecord]::new(
                $Exception,
                'GovUKNotifyApiError',
                [System.Management.Automation.ErrorCategory]::InvalidOperation,
                $Path
            )
            throw $NewError
        }
    }
}
#endregion src/GOV.UK.Notify/Private/Invoke-GovUKNotifyApi.ps1

#region src/GOV.UK.Notify/Private/New-GovUKNotifyJwt.ps1
function New-GovUKNotifyJwt {
    <#
        .SYNOPSIS
        Creates a short-lived JSON Web Token for GOV.UK Notify API authentication.

        .DESCRIPTION
        GOV.UK Notify authenticates requests with a JWT (HS256) carried in the Authorization header.
        The token's 'iss' claim is the service id and it is signed with the secret key, both of which
        are embedded in the API key using the format '{key_name}-{service_id}-{secret_key}', where
        service_id and secret_key are each UUIDs.

        The token's 'iat' (issued at) claim is the current UTC time in epoch seconds. GOV.UK Notify
        rejects tokens whose 'iat' is more than 30 seconds from its own clock, so a fresh token is
        generated for every request.

        This is an internal helper and is not exported by the module.

        .PARAMETER ApiKey
        The GOV.UK Notify API key, in the format '{key_name}-{service_id}-{secret_key}'.

        .OUTPUTS
        System.String. The encoded JWT.
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = 'Generates a token value in memory; it does not change any system state.')]
    [CmdletBinding()]
    [OutputType([string])]
    param(
        [Parameter(Mandatory = $true)]
        [string]$ApiKey
    )

    # -- The key name may itself contain hyphens, so parse the two UUIDs from the end of the key.
    $Parts = $ApiKey -split '-'
    if ($Parts.Count -lt 11) {
        throw "Invalid GOV.UK Notify API key format. Expected '{key_name}-{service_id}-{secret_key}', where service_id and secret_key are UUIDs."
    }

    $SecretKey = ($Parts[-5..-1]) -join '-'
    $ServiceId = ($Parts[-10..-6]) -join '-'

    # -- Build the JWT header and payload.
    $Header = [ordered]@{ typ = 'JWT'; alg = 'HS256' } | ConvertTo-Json -Compress
    $Payload = [ordered]@{
        iss = $ServiceId
        iat = [DateTimeOffset]::UtcNow.ToUnixTimeSeconds()
    } | ConvertTo-Json -Compress

    $EncodedHeader = ConvertTo-GovUKNotifyBase64Url -Bytes ([System.Text.Encoding]::UTF8.GetBytes($Header))
    $EncodedPayload = ConvertTo-GovUKNotifyBase64Url -Bytes ([System.Text.Encoding]::UTF8.GetBytes($Payload))
    $SigningInput = "$EncodedHeader.$EncodedPayload"

    # -- Sign with HMAC-SHA256 using the secret key.
    $Hmac = [System.Security.Cryptography.HMACSHA256]::new([System.Text.Encoding]::UTF8.GetBytes($SecretKey))
    try {
        $SignatureBytes = $Hmac.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($SigningInput))
    }
    finally {
        $Hmac.Dispose()
    }
    $EncodedSignature = ConvertTo-GovUKNotifyBase64Url -Bytes $SignatureBytes

    return "$SigningInput.$EncodedSignature"
}
#endregion src/GOV.UK.Notify/Private/New-GovUKNotifyJwt.ps1

#region src/GOV.UK.Notify/Private/Resolve-GovUKNotifyContext.ps1
function Resolve-GovUKNotifyContext {
    <#
        .SYNOPSIS
        Resolves the API key and base URL to use for a request.

        .DESCRIPTION
        Internal helper. An explicitly supplied -ApiKey (and optional -BaseUrl) always takes
        precedence. Otherwise the values stored by Connect-GovUKNotify in the module session context
        are used. If neither is available, a terminating error is thrown.

        .PARAMETER ApiKey
        An explicit API key that overrides the connected session, if supplied.

        .PARAMETER BaseUrl
        An explicit base URL that overrides the connected session, if supplied.

        .OUTPUTS
        System.Collections.Hashtable with 'ApiKey' and 'BaseUrl' keys.
    #>

    [CmdletBinding()]
    [OutputType([hashtable])]
    param(
        [string]$ApiKey,
        [string]$BaseUrl
    )

    $DefaultBaseUrl = 'https://api.notifications.service.gov.uk'

    # -- Resolve the API key: explicit parameter first, then the connected session context.
    if ([string]::IsNullOrWhiteSpace($ApiKey)) {
        if ($null -eq $script:GovUKNotifyContext) {
            throw 'Not connected to GOV.UK Notify. Run Connect-GovUKNotify -ApiKey <key> first, or pass -ApiKey to this command.'
        }
        $ApiKey = $script:GovUKNotifyContext.ApiKey
    }

    # -- Resolve the base URL: explicit parameter, then session context, then the production default.
    if ([string]::IsNullOrWhiteSpace($BaseUrl)) {
        if ($null -ne $script:GovUKNotifyContext -and -not [string]::IsNullOrWhiteSpace($script:GovUKNotifyContext.BaseUrl)) {
            $BaseUrl = $script:GovUKNotifyContext.BaseUrl
        }
        else {
            $BaseUrl = $DefaultBaseUrl
        }
    }

    return @{
        ApiKey  = $ApiKey
        BaseUrl = $BaseUrl.TrimEnd('/')
    }
}
#endregion src/GOV.UK.Notify/Private/Resolve-GovUKNotifyContext.ps1

#region src/GOV.UK.Notify/Public/Connect-GovUKNotify.ps1
function Connect-GovUKNotify {
    <#
        .SYNOPSIS
        Stores a GOV.UK Notify API key for the current session.

        .DESCRIPTION
        Validates the supplied API key and stores it, together with the base URL, in the module
        session context. Once connected, the other GOV.UK.Notify cmdlets can be called without passing
        an API key. The key is held in memory only and is never written to disk.

        Each command still accepts an explicit -ApiKey, which overrides the connected session. This is
        useful when you need to work with more than one Notify service in the same session.

        .PARAMETER ApiKey
        The GOV.UK Notify API key, in the format '{key_name}-{service_id}-{secret_key}'. Create API
        keys on the API integration page of the GOV.UK Notify dashboard.

        .PARAMETER BaseUrl
        The API base URL. Defaults to the production endpoint
        'https://api.notifications.service.gov.uk'.

        .PARAMETER PassThru
        Return the resulting (masked) connection context.

        .EXAMPLE
        Connect-GovUKNotify -ApiKey $env:GOVUKNOTIFY_API_KEY

        Connects using an API key held in an environment variable.

        .EXAMPLE
        Connect-GovUKNotify -ApiKey $key -PassThru

        Connects and returns the masked connection context.

        .OUTPUTS
        None by default, or a PSCustomObject describing the connection when -PassThru is used.

        .LINK
        https://docs.notifications.service.gov.uk/rest-api.html#api-keys
    #>

    [CmdletBinding()]
    [OutputType([pscustomobject])]
    param(
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$ApiKey,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string]$BaseUrl = 'https://api.notifications.service.gov.uk',

        [switch]$PassThru
    )

    # -- Validate the key shape and extract the service id and key name for display.
    $Parts = $ApiKey -split '-'
    if ($Parts.Count -lt 11) {
        throw "Invalid GOV.UK Notify API key format. Expected '{key_name}-{service_id}-{secret_key}', where service_id and secret_key are UUIDs."
    }

    $ServiceId = ($Parts[-10..-6]) -join '-'
    $KeyName = ($Parts[0..($Parts.Count - 11)]) -join '-'

    $script:GovUKNotifyContext = @{
        ApiKey    = $ApiKey
        BaseUrl   = $BaseUrl.TrimEnd('/')
        ServiceId = $ServiceId
        KeyName   = $KeyName
    }

    Write-Verbose ("Connected to GOV.UK Notify service '{0}' ({1}) at {2}." -f $KeyName, $ServiceId, $script:GovUKNotifyContext.BaseUrl)

    if ($PassThru) {
        return Get-GovUKNotifyContext
    }
}
#endregion src/GOV.UK.Notify/Public/Connect-GovUKNotify.ps1

#region src/GOV.UK.Notify/Public/Disconnect-GovUKNotify.ps1
function Disconnect-GovUKNotify {
    <#
        .SYNOPSIS
        Clears the stored GOV.UK Notify connection for the current session.

        .DESCRIPTION
        Removes the API key and base URL stored by Connect-GovUKNotify from the module session
        context. After disconnecting, cmdlets require an explicit -ApiKey again.

        .EXAMPLE
        Disconnect-GovUKNotify

        .OUTPUTS
        None.

        .LINK
        https://github.com/deanlongstaff/GOV.UK.Notify
    #>

    [CmdletBinding(SupportsShouldProcess = $true)]
    param()

    if ($null -eq $script:GovUKNotifyContext) {
        Write-Verbose 'No active GOV.UK Notify connection to disconnect.'
        return
    }

    if ($PSCmdlet.ShouldProcess('GOV.UK Notify session', 'Disconnect')) {
        $script:GovUKNotifyContext = $null
        Write-Verbose 'Disconnected from GOV.UK Notify.'
    }
}
#endregion src/GOV.UK.Notify/Public/Disconnect-GovUKNotify.ps1

#region src/GOV.UK.Notify/Public/Get-GovUKNotifyContext.ps1
function Get-GovUKNotifyContext {
    <#
        .SYNOPSIS
        Returns the current GOV.UK Notify connection context.

        .DESCRIPTION
        Returns a summary of the connection established by Connect-GovUKNotify, including the key name,
        service id and base URL. The API key itself is never returned in full; only the last four
        characters are shown. Returns $null when there is no active connection.

        .EXAMPLE
        Get-GovUKNotifyContext

        .OUTPUTS
        System.Management.Automation.PSCustomObject, or $null when not connected.

        .LINK
        https://github.com/deanlongstaff/GOV.UK.Notify
    #>

    [CmdletBinding()]
    [OutputType([pscustomobject])]
    param()

    if ($null -eq $script:GovUKNotifyContext) {
        Write-Verbose 'No active GOV.UK Notify connection.'
        return $null
    }

    $Key = [string]$script:GovUKNotifyContext.ApiKey
    $Ending = if ($Key.Length -ge 4) { $Key.Substring($Key.Length - 4) } else { $Key }

    return [pscustomobject]@{
        PSTypeName   = 'GOV.UK.Notify.Context'
        KeyName      = $script:GovUKNotifyContext.KeyName
        ServiceId    = $script:GovUKNotifyContext.ServiceId
        BaseUrl      = $script:GovUKNotifyContext.BaseUrl
        ApiKeyEnding = $Ending
    }
}
#endregion src/GOV.UK.Notify/Public/Get-GovUKNotifyContext.ps1

#region src/GOV.UK.Notify/Public/Get-GovUKNotifyLetterPdf.ps1
function Get-GovUKNotifyLetterPdf {
    <#
        .SYNOPSIS
        Retrieves the PDF contents of a GOV.UK Notify letter notification.

        .DESCRIPTION
        Calls 'GET /v2/notifications/{id}/pdf' and returns the raw PDF bytes, or writes them to a file
        with -OutFile. The PDF is only available once the letter has finished processing and has passed
        its virus scan.

        .PARAMETER NotificationId
        The id of the letter notification.

        .PARAMETER OutFile
        Optional path to write the PDF to. When supplied, no bytes are returned.

        .PARAMETER ApiKey
        An explicit API key, overriding any connected session.

        .PARAMETER BaseUrl
        An explicit base URL, overriding any connected session.

        .EXAMPLE
        Get-GovUKNotifyLetterPdf -NotificationId $id -OutFile ./letter.pdf

        .OUTPUTS
        System.Byte[] when -OutFile is not used; otherwise nothing.

        .LINK
        https://docs.notifications.service.gov.uk/rest-api.html#get-a-pdf-for-a-letter-notification
    #>

    [CmdletBinding()]
    [OutputType([byte[]])]
    param(
        [Parameter(Mandatory = $true, Position = 0)]
        [ValidateNotNullOrEmpty()]
        [string]$NotificationId,

        [Parameter()]
        [string]$OutFile,

        [Parameter()]
        [string]$ApiKey,

        [Parameter()]
        [string]$BaseUrl
    )

    $ConnectionParams = @{}
    if ($PSBoundParameters.ContainsKey('ApiKey')) { $ConnectionParams['ApiKey'] = $ApiKey }
    if ($PSBoundParameters.ContainsKey('BaseUrl')) { $ConnectionParams['BaseUrl'] = $BaseUrl }

    $Bytes = Invoke-GovUKNotifyApi -Method 'GET' -Path "/v2/notifications/$NotificationId/pdf" -Raw @ConnectionParams

    if ($PSBoundParameters.ContainsKey('OutFile') -and -not [string]::IsNullOrWhiteSpace($OutFile)) {
        $ResolvedPath = $PSCmdlet.GetUnresolvedProviderPathFromPSPath($OutFile)
        [System.IO.File]::WriteAllBytes($ResolvedPath, $Bytes)
        Write-Verbose "Saved letter PDF to $ResolvedPath."
        return
    }

    # -- Return the byte array as a single object rather than streaming individual bytes.
    return , $Bytes
}
#endregion src/GOV.UK.Notify/Public/Get-GovUKNotifyLetterPdf.ps1

#region src/GOV.UK.Notify/Public/Get-GovUKNotifyNotification.ps1
function Get-GovUKNotifyNotification {
    <#
        .SYNOPSIS
        Retrieves the data for one or more GOV.UK Notify messages.

        .DESCRIPTION
        With -NotificationId, calls 'GET /v2/notifications/{id}' and returns a single message.
        Otherwise calls 'GET /v2/notifications' and returns messages, optionally filtered by template
        type, status, and reference. Each page returns up to 250 messages; use -All to follow
        pagination and return every matching message. You can only retrieve messages within your data
        retention period (7 days by default).

        .PARAMETER NotificationId
        The id of a single notification to retrieve.

        .PARAMETER TemplateType
        Filter the list by template type: 'email', 'sms' or 'letter'.

        .PARAMETER Status
        Filter the list by one or more delivery statuses, for example 'delivered' or 'permanent-failure'.

        .PARAMETER Reference
        Filter the list by the reference you supplied when sending.

        .PARAMETER OlderThan
        Return messages older than this notification id (pagination).

        .PARAMETER IncludeJobs
        Include notifications sent as part of a batch (bulk) upload.

        .PARAMETER All
        Follow pagination and return every matching message, not just the first page of up to 250.

        .PARAMETER ApiKey
        An explicit API key, overriding any connected session.

        .PARAMETER BaseUrl
        An explicit base URL, overriding any connected session.

        .EXAMPLE
        Get-GovUKNotifyNotification -NotificationId '740e5834-3a29-46b4-9a6f-16142fde533a'

        .EXAMPLE
        Get-GovUKNotifyNotification -TemplateType email -Status delivered -All

        .OUTPUTS
        A single notification object, or a stream of notification objects for the list.

        .LINK
        https://docs.notifications.service.gov.uk/rest-api.html#get-message-data
    #>

    [CmdletBinding(DefaultParameterSetName = 'List')]
    param(
        [Parameter(Mandatory = $true, ParameterSetName = 'Single', Position = 0)]
        [ValidateNotNullOrEmpty()]
        [string]$NotificationId,

        [Parameter(ParameterSetName = 'List')]
        [ValidateSet('email', 'sms', 'letter')]
        [string]$TemplateType,

        [Parameter(ParameterSetName = 'List')]
        [string[]]$Status,

        [Parameter(ParameterSetName = 'List')]
        [string]$Reference,

        [Parameter(ParameterSetName = 'List')]
        [string]$OlderThan,

        [Parameter(ParameterSetName = 'List')]
        [switch]$IncludeJobs,

        [Parameter(ParameterSetName = 'List')]
        [switch]$All,

        [Parameter()]
        [string]$ApiKey,

        [Parameter()]
        [string]$BaseUrl
    )

    $ConnectionParams = @{}
    if ($PSBoundParameters.ContainsKey('ApiKey')) { $ConnectionParams['ApiKey'] = $ApiKey }
    if ($PSBoundParameters.ContainsKey('BaseUrl')) { $ConnectionParams['BaseUrl'] = $BaseUrl }

    if ($PSCmdlet.ParameterSetName -eq 'Single') {
        return Invoke-GovUKNotifyApi -Method 'GET' -Path "/v2/notifications/$NotificationId" @ConnectionParams
    }

    # -- Build the static part of the query (everything except older_than).
    $BaseQuery = [System.Collections.Generic.List[string]]::new()
    if ($TemplateType) { $BaseQuery.Add("template_type=$TemplateType") }
    foreach ($SingleStatus in $Status) { $BaseQuery.Add("status=$([uri]::EscapeDataString($SingleStatus))") }
    if ($Reference) { $BaseQuery.Add("reference=$([uri]::EscapeDataString($Reference))") }
    if ($IncludeJobs) { $BaseQuery.Add('include_jobs=true') }

    $CurrentOlderThan = $OlderThan
    do {
        $Query = [System.Collections.Generic.List[string]]::new($BaseQuery)
        if ($CurrentOlderThan) { $Query.Add("older_than=$CurrentOlderThan") }

        $Path = '/v2/notifications'
        if ($Query.Count -gt 0) { $Path += '?' + ($Query -join '&') }

        $Response = Invoke-GovUKNotifyApi -Method 'GET' -Path $Path @ConnectionParams
        $Items = @($Response.notifications)

        if ($Items.Count -gt 0) {
            $Items
            $CurrentOlderThan = $Items[-1].id
        }
    } while ($All -and $Items.Count -ge 250)
}
#endregion src/GOV.UK.Notify/Public/Get-GovUKNotifyNotification.ps1

#region src/GOV.UK.Notify/Public/Get-GovUKNotifyReceivedText.ps1
function Get-GovUKNotifyReceivedText {
    <#
        .SYNOPSIS
        Retrieves text messages received by your GOV.UK Notify service.

        .DESCRIPTION
        Calls 'GET /v2/received-text-messages' and returns received text messages, most recent first.
        Each page returns up to 250 messages; use -All to follow pagination and return every message.
        You can only retrieve messages that are 7 days old or newer. Receiving text messages must be
        enabled in your service settings.

        .PARAMETER OlderThan
        Return received text messages older than this message id (pagination).

        .PARAMETER All
        Follow pagination and return every received text message, not just the first page of up to 250.

        .PARAMETER ApiKey
        An explicit API key, overriding any connected session.

        .PARAMETER BaseUrl
        An explicit base URL, overriding any connected session.

        .EXAMPLE
        Get-GovUKNotifyReceivedText -All

        .OUTPUTS
        A stream of received text message objects.

        .LINK
        https://docs.notifications.service.gov.uk/rest-api.html#get-received-text-messages
    #>

    [CmdletBinding()]
    param(
        [Parameter()]
        [string]$OlderThan,

        [Parameter()]
        [switch]$All,

        [Parameter()]
        [string]$ApiKey,

        [Parameter()]
        [string]$BaseUrl
    )

    $ConnectionParams = @{}
    if ($PSBoundParameters.ContainsKey('ApiKey')) { $ConnectionParams['ApiKey'] = $ApiKey }
    if ($PSBoundParameters.ContainsKey('BaseUrl')) { $ConnectionParams['BaseUrl'] = $BaseUrl }

    $CurrentOlderThan = $OlderThan
    do {
        $Path = '/v2/received-text-messages'
        if ($CurrentOlderThan) { $Path += "?older_than=$CurrentOlderThan" }

        $Response = Invoke-GovUKNotifyApi -Method 'GET' -Path $Path @ConnectionParams
        $Items = @($Response.received_text_messages)

        if ($Items.Count -gt 0) {
            $Items
            $CurrentOlderThan = $Items[-1].id
        }
    } while ($All -and $Items.Count -ge 250)
}
#endregion src/GOV.UK.Notify/Public/Get-GovUKNotifyReceivedText.ps1

#region src/GOV.UK.Notify/Public/Get-GovUKNotifyTemplate.ps1
function Get-GovUKNotifyTemplate {
    <#
        .SYNOPSIS
        Retrieves one template, a specific template version, or all templates.

        .DESCRIPTION
        With -TemplateId, calls 'GET /v2/template/{id}' for the latest version, or
        'GET /v2/template/{id}/version/{version}' when -Version is also supplied. Without -TemplateId,
        calls 'GET /v2/templates' and returns all templates, optionally filtered by -Type.

        .PARAMETER TemplateId
        The id of a single template to retrieve.

        .PARAMETER Version
        A specific version number of the template to retrieve.

        .PARAMETER Type
        Filter the list of all templates by type: 'email', 'sms' or 'letter'.

        .PARAMETER ApiKey
        An explicit API key, overriding any connected session.

        .PARAMETER BaseUrl
        An explicit base URL, overriding any connected session.

        .EXAMPLE
        Get-GovUKNotifyTemplate -TemplateId $tid

        .EXAMPLE
        Get-GovUKNotifyTemplate -TemplateId $tid -Version 2

        .EXAMPLE
        Get-GovUKNotifyTemplate -Type email

        .OUTPUTS
        A single template object, or a stream of template objects for the list.

        .LINK
        https://docs.notifications.service.gov.uk/rest-api.html#get-a-template
    #>

    [CmdletBinding(DefaultParameterSetName = 'List')]
    param(
        [Parameter(Mandatory = $true, ParameterSetName = 'Single', Position = 0)]
        [ValidateNotNullOrEmpty()]
        [string]$TemplateId,

        [Parameter(ParameterSetName = 'Single')]
        [ValidateRange(1, 2147483647)]
        [int]$Version,

        [Parameter(ParameterSetName = 'List')]
        [ValidateSet('email', 'sms', 'letter')]
        [string]$Type,

        [Parameter()]
        [string]$ApiKey,

        [Parameter()]
        [string]$BaseUrl
    )

    $ConnectionParams = @{}
    if ($PSBoundParameters.ContainsKey('ApiKey')) { $ConnectionParams['ApiKey'] = $ApiKey }
    if ($PSBoundParameters.ContainsKey('BaseUrl')) { $ConnectionParams['BaseUrl'] = $BaseUrl }

    if ($PSCmdlet.ParameterSetName -eq 'Single') {
        if ($PSBoundParameters.ContainsKey('Version')) {
            return Invoke-GovUKNotifyApi -Method 'GET' -Path "/v2/template/$TemplateId/version/$Version" @ConnectionParams
        }
        return Invoke-GovUKNotifyApi -Method 'GET' -Path "/v2/template/$TemplateId" @ConnectionParams
    }

    $Path = '/v2/templates'
    if ($Type) { $Path += "?type=$Type" }

    $Response = Invoke-GovUKNotifyApi -Method 'GET' -Path $Path @ConnectionParams
    return $Response.templates
}
#endregion src/GOV.UK.Notify/Public/Get-GovUKNotifyTemplate.ps1

#region src/GOV.UK.Notify/Public/Get-GovUKNotifyTemplatePreview.ps1
function Get-GovUKNotifyTemplatePreview {
    <#
        .SYNOPSIS
        Generates a preview of a GOV.UK Notify template with personalisation applied.

        .DESCRIPTION
        Calls 'POST /v2/template/{id}/preview' to render a template with the supplied personalisation
        and returns the resulting subject, body and (for emails) HTML. The keys in -Personalisation
        must match the template's placeholder fields; extra keys are ignored by GOV.UK Notify.

        .PARAMETER TemplateId
        The id of the template to preview.

        .PARAMETER Personalisation
        A hashtable of placeholder values for the template.

        .PARAMETER ApiKey
        An explicit API key, overriding any connected session.

        .PARAMETER BaseUrl
        An explicit base URL, overriding any connected session.

        .EXAMPLE
        Get-GovUKNotifyTemplatePreview -TemplateId $tid -Personalisation @{ first_name = 'Amala' }

        .OUTPUTS
        The rendered template preview object returned by GOV.UK Notify.

        .LINK
        https://docs.notifications.service.gov.uk/rest-api.html#generate-a-preview-template
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true, Position = 0)]
        [ValidateNotNullOrEmpty()]
        [string]$TemplateId,

        [Parameter()]
        [hashtable]$Personalisation,

        [Parameter()]
        [string]$ApiKey,

        [Parameter()]
        [string]$BaseUrl
    )

    $Body = @{
        personalisation = if ($Personalisation) { $Personalisation } else { @{} }
    }

    $ConnectionParams = @{}
    if ($PSBoundParameters.ContainsKey('ApiKey')) { $ConnectionParams['ApiKey'] = $ApiKey }
    if ($PSBoundParameters.ContainsKey('BaseUrl')) { $ConnectionParams['BaseUrl'] = $BaseUrl }

    return Invoke-GovUKNotifyApi -Method 'POST' -Path "/v2/template/$TemplateId/preview" -Body $Body @ConnectionParams
}
#endregion src/GOV.UK.Notify/Public/Get-GovUKNotifyTemplatePreview.ps1

#region src/GOV.UK.Notify/Public/New-GovUKNotifyFileAttachment.ps1
function New-GovUKNotifyFileAttachment {
    <#
        .SYNOPSIS
        Builds a file object for sending a file by email through GOV.UK Notify.

        .DESCRIPTION
        Produces the hashtable that GOV.UK Notify expects for a "send a file by email" placeholder.
        Assign the result to a template placeholder in the -Personalisation argument of
        Send-GovUKNotifyEmail. Supply the file as a path, a byte array, or an already base64-encoded
        string.

        .PARAMETER Path
        Path to the file to attach. The file is read and base64-encoded automatically, and its name is
        used as the filename unless -FileName is given.

        .PARAMETER Bytes
        The file contents as a byte array.

        .PARAMETER Base64Content
        The file contents as an already base64-encoded string.

        .PARAMETER FileName
        The filename shown to the recipient. Should include the correct file extension.

        .PARAMETER ConfirmEmailBeforeDownload
        Whether the recipient must confirm their email address before downloading. GOV.UK Notify
        defaults this to true. Set to $false to turn the check off (not recommended for sensitive files).

        .PARAMETER RetentionPeriod
        How long the file is available to download, for example '4 weeks'. Allowed range is 1 to 78
        weeks. Defaults to 26 weeks at GOV.UK Notify if omitted.

        .EXAMPLE
        $file = New-GovUKNotifyFileAttachment -Path ./report.csv -RetentionPeriod '4 weeks'
        Send-GovUKNotifyEmail -EmailAddress 'amala@example.com' -TemplateId $tid -Personalisation @{ link_to_file = $file }

        .OUTPUTS
        System.Collections.Hashtable representing the file object.

        .LINK
        https://docs.notifications.service.gov.uk/rest-api.html#send-a-file-by-email
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = 'Builds an in-memory personalisation object; it does not change any system state.')]
    [CmdletBinding(DefaultParameterSetName = 'Path')]
    [OutputType([hashtable])]
    param(
        [Parameter(Mandatory = $true, ParameterSetName = 'Path', Position = 0)]
        [ValidateNotNullOrEmpty()]
        [string]$Path,

        [Parameter(Mandatory = $true, ParameterSetName = 'Bytes')]
        [ValidateNotNull()]
        [byte[]]$Bytes,

        [Parameter(Mandatory = $true, ParameterSetName = 'Base64')]
        [ValidateNotNullOrEmpty()]
        [string]$Base64Content,

        [Parameter()]
        [string]$FileName,

        [Parameter()]
        [bool]$ConfirmEmailBeforeDownload,

        [Parameter()]
        [ValidatePattern('^\d+\s+weeks?$')]
        [string]$RetentionPeriod
    )

    switch ($PSCmdlet.ParameterSetName) {
        'Path' {
            $ResolvedPath = (Resolve-Path -LiteralPath $Path -ErrorAction Stop).ProviderPath
            $FileBytes = [System.IO.File]::ReadAllBytes($ResolvedPath)
            $EncodedContent = [Convert]::ToBase64String($FileBytes)
            if (-not $PSBoundParameters.ContainsKey('FileName')) {
                $FileName = [System.IO.Path]::GetFileName($ResolvedPath)
            }
        }
        'Bytes' {
            $EncodedContent = [Convert]::ToBase64String($Bytes)
        }
        'Base64' {
            $EncodedContent = $Base64Content
        }
    }

    $Attachment = @{ file = $EncodedContent }
    if ($FileName) { $Attachment.filename = $FileName }
    if ($PSBoundParameters.ContainsKey('ConfirmEmailBeforeDownload')) {
        $Attachment.confirm_email_before_download = $ConfirmEmailBeforeDownload
    }
    if ($RetentionPeriod) { $Attachment.retention_period = $RetentionPeriod }

    return $Attachment
}
#endregion src/GOV.UK.Notify/Public/New-GovUKNotifyFileAttachment.ps1

#region src/GOV.UK.Notify/Public/Send-GovUKNotifyEmail.ps1
function Send-GovUKNotifyEmail {
    <#
        .SYNOPSIS
        Sends an email using a GOV.UK Notify template.

        .DESCRIPTION
        Calls 'POST /v2/notifications/email' to send an email built from one of your GOV.UK Notify
        templates. To send a file by email, build the value for a template placeholder with
        New-GovUKNotifyFileAttachment and include it in -Personalisation.

        .PARAMETER EmailAddress
        The recipient's email address.

        .PARAMETER TemplateId
        The id of the email template to use. Copy it from the Templates page of the GOV.UK Notify
        dashboard.

        .PARAMETER Personalisation
        A hashtable of placeholder values for the template. Values may be strings, arrays (rendered as
        bullet points), or a file object from New-GovUKNotifyFileAttachment.

        .PARAMETER Reference
        An optional identifier for the message or batch. Must not contain personal data.

        .PARAMETER EmailReplyToId
        An optional reply-to address id configured on your service.

        .PARAMETER OneClickUnsubscribeUrl
        An optional one-click unsubscribe URL added to the email headers for subscription emails.

        .PARAMETER SanitiseContentFor
        An optional list of personalisation keys whose content GOV.UK Notify should sanitise
        (escape Markdown and remove URLs) to reduce the risk of malicious content injection.

        .PARAMETER ApiKey
        An explicit API key, overriding any connected session.

        .PARAMETER BaseUrl
        An explicit base URL, overriding any connected session.

        .EXAMPLE
        Send-GovUKNotifyEmail -EmailAddress 'amala@example.com' -TemplateId $tid -Personalisation @{ first_name = 'Amala' }

        .EXAMPLE
        $file = New-GovUKNotifyFileAttachment -Path ./invite.pdf
        Send-GovUKNotifyEmail -EmailAddress 'amala@example.com' -TemplateId $tid -Personalisation @{ link_to_file = $file }

        .OUTPUTS
        The notification response object returned by GOV.UK Notify.

        .LINK
        https://docs.notifications.service.gov.uk/rest-api.html#send-an-email
    #>

    [CmdletBinding(SupportsShouldProcess = $true)]
    param(
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$EmailAddress,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$TemplateId,

        [Parameter()]
        [hashtable]$Personalisation,

        [Parameter()]
        [string]$Reference,

        [Parameter()]
        [string]$EmailReplyToId,

        [Parameter()]
        [string]$OneClickUnsubscribeUrl,

        [Parameter()]
        [string[]]$SanitiseContentFor,

        [Parameter()]
        [string]$ApiKey,

        [Parameter()]
        [string]$BaseUrl
    )

    $Body = @{
        email_address = $EmailAddress
        template_id   = $TemplateId
    }
    if ($Personalisation) { $Body.personalisation = $Personalisation }
    if ($Reference) { $Body.reference = $Reference }
    if ($EmailReplyToId) { $Body.email_reply_to_id = $EmailReplyToId }
    if ($OneClickUnsubscribeUrl) { $Body.one_click_unsubscribe_url = $OneClickUnsubscribeUrl }
    if ($SanitiseContentFor) { $Body.sanitise_content_for = $SanitiseContentFor }

    $ConnectionParams = @{}
    if ($PSBoundParameters.ContainsKey('ApiKey')) { $ConnectionParams['ApiKey'] = $ApiKey }
    if ($PSBoundParameters.ContainsKey('BaseUrl')) { $ConnectionParams['BaseUrl'] = $BaseUrl }

    if ($PSCmdlet.ShouldProcess($EmailAddress, 'Send GOV.UK Notify email')) {
        return Invoke-GovUKNotifyApi -Method 'POST' -Path '/v2/notifications/email' -Body $Body @ConnectionParams
    }
}
#endregion src/GOV.UK.Notify/Public/Send-GovUKNotifyEmail.ps1

#region src/GOV.UK.Notify/Public/Send-GovUKNotifyLetter.ps1
function Send-GovUKNotifyLetter {
    <#
        .SYNOPSIS
        Sends a letter using a GOV.UK Notify template.

        .DESCRIPTION
        Calls 'POST /v2/notifications/letter' to send a letter built from one of your GOV.UK Notify
        templates. The -Personalisation hashtable must include the recipient's address as
        'address_line_1' through 'address_line_7'. The address must have at least 3 lines; the first
        two lines must contain alphanumeric characters and the last line must be a real UK postcode or
        the name of a country outside the UK.

        Your service must be live to send letters.

        .PARAMETER TemplateId
        The id of the letter template to use.

        .PARAMETER Personalisation
        A hashtable of placeholder values, including the required address lines and any other template
        placeholders.

        .PARAMETER Reference
        An optional identifier for the letter or batch. Must not contain personal data.

        .PARAMETER ApiKey
        An explicit API key, overriding any connected session.

        .PARAMETER BaseUrl
        An explicit base URL, overriding any connected session.

        .EXAMPLE
        $address = @{
            address_line_1 = 'Amala Bird'
            address_line_2 = '123 High Street'
            address_line_3 = 'SW14 6BH'
        }
        Send-GovUKNotifyLetter -TemplateId $tid -Personalisation $address

        .OUTPUTS
        The notification response object returned by GOV.UK Notify.

        .LINK
        https://docs.notifications.service.gov.uk/rest-api.html#send-a-letter
    #>

    [CmdletBinding(SupportsShouldProcess = $true)]
    param(
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$TemplateId,

        [Parameter(Mandatory = $true)]
        [ValidateNotNull()]
        [hashtable]$Personalisation,

        [Parameter()]
        [string]$Reference,

        [Parameter()]
        [string]$ApiKey,

        [Parameter()]
        [string]$BaseUrl
    )

    $Body = @{
        template_id     = $TemplateId
        personalisation = $Personalisation
    }
    if ($Reference) { $Body.reference = $Reference }

    $ConnectionParams = @{}
    if ($PSBoundParameters.ContainsKey('ApiKey')) { $ConnectionParams['ApiKey'] = $ApiKey }
    if ($PSBoundParameters.ContainsKey('BaseUrl')) { $ConnectionParams['BaseUrl'] = $BaseUrl }

    if ($PSCmdlet.ShouldProcess("letter template $TemplateId", 'Send GOV.UK Notify letter')) {
        return Invoke-GovUKNotifyApi -Method 'POST' -Path '/v2/notifications/letter' -Body $Body @ConnectionParams
    }
}
#endregion src/GOV.UK.Notify/Public/Send-GovUKNotifyLetter.ps1

#region src/GOV.UK.Notify/Public/Send-GovUKNotifyPrecompiledLetter.ps1
function Send-GovUKNotifyPrecompiledLetter {
    <#
        .SYNOPSIS
        Sends a precompiled (PDF) letter through GOV.UK Notify.

        .DESCRIPTION
        Calls 'POST /v2/notifications/letter' with a base64-encoded PDF. Supply the letter either as a
        file with -Path (which is read and encoded for you) or as an already base64-encoded string with
        -Content. The PDF must meet the GOV.UK Notify letter specification. Your service must be live
        and you must use a live API key (not a team key).

        .PARAMETER Reference
        A required identifier for the letter or batch. Must not contain personal data.

        .PARAMETER Path
        Path to a PDF file to send. The file is read and base64-encoded automatically.

        .PARAMETER Content
        An already base64-encoded PDF string.

        .PARAMETER Postage
        Optional postage class: 'first', 'second' or 'economy'. Defaults to second class at GOV.UK
        Notify if omitted.

        .PARAMETER ApiKey
        An explicit API key, overriding any connected session.

        .PARAMETER BaseUrl
        An explicit base URL, overriding any connected session.

        .EXAMPLE
        Send-GovUKNotifyPrecompiledLetter -Reference 'batch-001' -Path ./letter.pdf -Postage first

        .OUTPUTS
        The notification response object returned by GOV.UK Notify.

        .LINK
        https://docs.notifications.service.gov.uk/rest-api.html#send-a-precompiled-letter
    #>

    [CmdletBinding(SupportsShouldProcess = $true, DefaultParameterSetName = 'Path')]
    param(
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$Reference,

        [Parameter(Mandatory = $true, ParameterSetName = 'Path')]
        [ValidateNotNullOrEmpty()]
        [string]$Path,

        [Parameter(Mandatory = $true, ParameterSetName = 'Content')]
        [ValidateNotNullOrEmpty()]
        [string]$Content,

        [Parameter()]
        [ValidateSet('first', 'second', 'economy')]
        [string]$Postage,

        [Parameter()]
        [string]$ApiKey,

        [Parameter()]
        [string]$BaseUrl
    )

    if ($PSCmdlet.ParameterSetName -eq 'Path') {
        $ResolvedPath = (Resolve-Path -LiteralPath $Path -ErrorAction Stop).ProviderPath
        $Bytes = [System.IO.File]::ReadAllBytes($ResolvedPath)
        $Content = [Convert]::ToBase64String($Bytes)
    }

    $Body = @{
        reference = $Reference
        content   = $Content
    }
    if ($Postage) { $Body.postage = $Postage }

    $ConnectionParams = @{}
    if ($PSBoundParameters.ContainsKey('ApiKey')) { $ConnectionParams['ApiKey'] = $ApiKey }
    if ($PSBoundParameters.ContainsKey('BaseUrl')) { $ConnectionParams['BaseUrl'] = $BaseUrl }

    if ($PSCmdlet.ShouldProcess("precompiled letter '$Reference'", 'Send GOV.UK Notify precompiled letter')) {
        return Invoke-GovUKNotifyApi -Method 'POST' -Path '/v2/notifications/letter' -Body $Body @ConnectionParams
    }
}
#endregion src/GOV.UK.Notify/Public/Send-GovUKNotifyPrecompiledLetter.ps1

#region src/GOV.UK.Notify/Public/Send-GovUKNotifySms.ps1
function Send-GovUKNotifySms {
    <#
        .SYNOPSIS
        Sends a text message using a GOV.UK Notify template.

        .DESCRIPTION
        Calls 'POST /v2/notifications/sms' to send a text message built from one of your GOV.UK Notify
        templates.

        .PARAMETER PhoneNumber
        The recipient's phone number. This can be a UK or international number.

        .PARAMETER TemplateId
        The id of the text message template to use.

        .PARAMETER Personalisation
        A hashtable of placeholder values for the template.

        .PARAMETER Reference
        An optional identifier for the message or batch. Must not contain personal data.

        .PARAMETER SmsSenderId
        An optional text message sender id configured on your service. Omit to use the default sender.

        .PARAMETER ApiKey
        An explicit API key, overriding any connected session.

        .PARAMETER BaseUrl
        An explicit base URL, overriding any connected session.

        .EXAMPLE
        Send-GovUKNotifySms -PhoneNumber '+447900900123' -TemplateId $tid -Personalisation @{ code = '123456' }

        .OUTPUTS
        The notification response object returned by GOV.UK Notify.

        .LINK
        https://docs.notifications.service.gov.uk/rest-api.html#send-a-text-message
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = 'SMS is an acronym, not a plural noun.')]
    [CmdletBinding(SupportsShouldProcess = $true)]
    param(
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$PhoneNumber,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$TemplateId,

        [Parameter()]
        [hashtable]$Personalisation,

        [Parameter()]
        [string]$Reference,

        [Parameter()]
        [string]$SmsSenderId,

        [Parameter()]
        [string]$ApiKey,

        [Parameter()]
        [string]$BaseUrl
    )

    $Body = @{
        phone_number = $PhoneNumber
        template_id  = $TemplateId
    }
    if ($Personalisation) { $Body.personalisation = $Personalisation }
    if ($Reference) { $Body.reference = $Reference }
    if ($SmsSenderId) { $Body.sms_sender_id = $SmsSenderId }

    $ConnectionParams = @{}
    if ($PSBoundParameters.ContainsKey('ApiKey')) { $ConnectionParams['ApiKey'] = $ApiKey }
    if ($PSBoundParameters.ContainsKey('BaseUrl')) { $ConnectionParams['BaseUrl'] = $BaseUrl }

    if ($PSCmdlet.ShouldProcess($PhoneNumber, 'Send GOV.UK Notify text message')) {
        return Invoke-GovUKNotifyApi -Method 'POST' -Path '/v2/notifications/sms' -Body $Body @ConnectionParams
    }
}
#endregion src/GOV.UK.Notify/Public/Send-GovUKNotifySms.ps1

Export-ModuleMember -Function @(
    'Connect-GovUKNotify'
    'Disconnect-GovUKNotify'
    'Get-GovUKNotifyContext'
    'Get-GovUKNotifyLetterPdf'
    'Get-GovUKNotifyNotification'
    'Get-GovUKNotifyReceivedText'
    'Get-GovUKNotifyTemplate'
    'Get-GovUKNotifyTemplatePreview'
    'New-GovUKNotifyFileAttachment'
    'Send-GovUKNotifyEmail'
    'Send-GovUKNotifyLetter'
    'Send-GovUKNotifyPrecompiledLetter'
    'Send-GovUKNotifySms'
)