Streaming Hub API

Manual HTTP testing in PowerShell

Pair, negotiate a session and query Core without writing a .NET application.

Alpha
Developer referenceUpdated 2026-10-25

Use PowerShell 7 on Windows to exercise the local HTTP protocol your application will use. This walkthrough requests only host.status and reads Host information and API schemas. It does not edit or run a project.

Start Core with its Tray first. Paste the blocks into the same PowerShell session. Responses containing handles, credentials or session tokens stay in variables; do not print them or capture them in a transcript.

Read discovery and prepare requests

For a custom data directory, change the discovery path:

$discoveryPath = Join-Path $env:LOCALAPPDATA 'StreamingHub\runtime\api-discovery.json'
$discovery = Get-Content -LiteralPath $discoveryPath -Raw | ConvertFrom-Json
$endpoint = [uri]$discovery.endpoint
if ($endpoint.Scheme -ne 'http' -or $endpoint.Host -ne '127.0.0.1' -or
    $endpoint.UserInfo -or $endpoint.Query -or $endpoint.Fragment -or
    $endpoint.AbsolutePath -ne '/' -or $discovery.apiMajorVersions -notcontains 1) {
    throw 'Unsupported Core discovery endpoint or contract.'
}
$apiBase = $endpoint.AbsoluteUri.TrimEnd('/') + '/api/v1'
$clientId = 'example.powershell-client'
$scopes = @('host.status')

function Send-CoreRequest {
    param(
        [Parameter(Mandatory)][string]$Path,
        [ValidateSet('Get', 'Post')][string]$Method = 'Get',
        [object]$Body,
        [hashtable]$Headers = @{}
    )
    $requestOptions = @{
        Uri = "$apiBase/$Path"
        Method = $Method
        Headers = $Headers
        NoProxy = $true
        MaximumRedirection = 0
        TimeoutSec = 15
        ErrorAction = 'Stop'
    }
    if ($PSBoundParameters.ContainsKey('Body')) {
        $requestOptions.ContentType = 'application/json'
        $requestOptions.Body = $Body | ConvertTo-Json -Depth 20 -Compress
    }
    Invoke-RestMethod @requestOptions
}

Use the discovered literal loopback address. Disabling proxies and redirects keeps credentials on that endpoint. Do not add an Origin header: Core rejects browser-origin requests.

Request access and approve it in Tray

$pairing = Send-CoreRequest -Path 'pairing/requests' -Method Post -Body @{
    clientId = $clientId
    name = 'PowerShell API test'
    publisher = 'Local manual test (claimed)'
    scopes = $scopes
}
Write-Host 'Approve PowerShell API test in the Core tray.'

do {
    if ([datetimeoffset]::UtcNow -ge [datetimeoffset]$pairing.expiresAt) {
        throw 'Pairing request expired. Start a new request.'
    }
    $approval = Send-CoreRequest -Path 'pairing/requests/status' -Method Post -Body @{
        handle = $pairing.handle
    }
    if ($approval.status -eq 'pending') { Start-Sleep -Milliseconds 500 }
} while ($approval.status -eq 'pending')

if ($approval.status -ne 'approved' -or -not $approval.credential) {
    throw "Pairing ended: $($approval.status)"
}
$credential = $approval.credential

Approve PowerShell API test in Tray while polling continues. Requests expire after two minutes. Only approval returns the secret, once. The client cannot approve itself.

Exchange the credential and negotiate

$session = Send-CoreRequest -Path 'credentials/exchange' -Method Post -Body @{
    clientId = $clientId
    secret = $credential.secret
}
$authHeaders = @{ Authorization = "Bearer $($session.accessToken)" }
$handshake = Send-CoreRequest -Path 'handshake' -Method Post -Headers $authHeaders -Body @{
    contractMajor = 1
    requiredCapabilities = $scopes
}
if ($handshake.hostInstanceId -ne $discovery.hostInstanceId) {
    throw 'Core restarted. Read discovery again before continuing.'
}
$handshake | Select-Object contractMajor, hostInstanceId, capabilities

The credential creates sessions; HTTP requests use a short-lived session token in the Authorization header. The handshake is required before normal calls. Do not put either secret in a URL.

Read Host and live API reference

$hostInfo = Send-CoreRequest -Path 'host' -Headers $authHeaders
$hostInfo | Select-Object hostInstanceId, coreVersion

$openApi = Send-CoreRequest -Path 'openapi.json' -Headers $authHeaders
$openApi.paths.PSObject.Properties.Name | Sort-Object

$schema = Send-CoreRequest -Path 'schemas/HandshakeRequest' -Headers $authHeaders
$schema | ConvertTo-Json -Depth 20

These routes require host.status. OpenAPI and named schemas describe the running Core; use them to implement clients in other languages. See the route and method reference.

Renew the session

Sessions last at most two minutes. Repeat exchange and handshake with the still-valid credential, replacing $authHeaders with the new token. This retains the same principal and grants. New approval is required if the credential expired, was revoked or needs additional scopes.

To read a project, include project.read in the original scopes, obtain approval for the new grant and complete exchange/handshake. Then request projects/active/snapshot with a project open. That response can be paginated: a complete client must preserve identity and revision across every page, as the .NET GetCompleteSnapshotAsync helper does.

Expected failures

ResultCheck
Missing discovery or connection refusedStart Core; select the correct data directory and read current discovery
origin_forbiddenUse the discovered literal IPv4 endpoint from a native client
session_required / session_expiredExchange the credential and send the new Bearer token
handshake_requiredComplete the handshake before normal calls
capability_forbiddenRequest the specific scope and obtain approval
credential_invalidCheck client ID, expiration and revocation
pairing_unknownThe handle is unknown or its one-time result was consumed

Finish

Remove-Variable pairing, approval, credential, session, authHeaders, handshake -ErrorAction SilentlyContinue

Removing variables drops console references; it does not revoke access or guarantee memory erasure. Revoke the test client through Core's client-access controls when finished. After a mutation timeout, recover the original command.