Manual HTTP testing in PowerShell
Pair, negotiate a session and query Core without writing a .NET application.
AlphaUse 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
| Result | Check |
|---|---|
| Missing discovery or connection refused | Start Core; select the correct data directory and read current discovery |
origin_forbidden | Use the discovered literal IPv4 endpoint from a native client |
session_required / session_expired | Exchange the credential and send the new Bearer token |
handshake_required | Complete the handshake before normal calls |
capability_forbidden | Request the specific scope and obtain approval |
credential_invalid | Check client ID, expiration and revocation |
pairing_unknown | The 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.