Using pyhall from Any Language
pyhall is language-agnostic. The Python, TypeScript, and Go SDKs are convenience wrappers around a plain HTTP API. If your language has an HTTP client — and every language does — you can use pyhall directly.
The Registry API
All pyhall operations go through api.pyhall.dev. Authentication uses your API key as a Bearer token.
Base URL: https://api.pyhall.devAuth: Authorization: Bearer phk_live_<your-key>Format: application/jsonCore Operations
1. Route a Request
Ask the registry whether a capability is allowed to run.
POST /api/v1/dispatchBody:
{ "namespace": "org.yourco", "capability": "llm.text.generate", "worker_id": "wkr_abc123", "context": { "risk_tier": "low", "correlation_id": "req-456" }}Response (allowed):
{ "outcome": "allow", "worker_id": "wkr_abc123", "capability": "llm.text.generate", "correlation_id": "req-456", "decision_id": "dec_xyz"}Response (denied):
{ "outcome": "deny", "deny_code": "POLICY_BLOCKED", "reason": "Capability is restricted by namespace policy", "correlation_id": "req-456"}2. Verify a Worker
Check whether a worker is registered, attested, and not banned.
GET /api/v1/verify/{worker_id}Response:
{ "worker_id": "wkr_abc123", "namespace": "org.yourco", "status": "active", "attested": true, "attest_sha256": "a1b2c3...", "ban_status": "clean"}3. Enroll a Worker (Hall API)
The local Hall Server handles enrollment — this is the POST /api/v1/hall/enroll endpoint on api.pyhall.dev, called with your API key.
POST /api/v1/hall/enrollAuthorization: Bearer phk_live_<your-key>Body:
{ "namespace": "org.yourco", "display_name": "My Worker", "capabilities": ["llm.text.generate"], "sha256": "<package-hash>"}Code Examples
# Route a requestcurl -X POST https://api.pyhall.dev/api/v1/dispatch \ -H "Authorization: Bearer phk_live_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{ "namespace": "org.yourco", "capability": "llm.text.generate", "worker_id": "wkr_abc123" }'
# Verify a workercurl https://api.pyhall.dev/api/v1/verify/wkr_abc123 \ -H "Authorization: Bearer phk_live_YOUR_KEY"<?php$apiKey = 'phk_live_YOUR_KEY';$base = 'https://api.pyhall.dev';
// Route a request$ch = curl_init("$base/api/v1/dispatch");curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer $apiKey", 'Content-Type: application/json', ], CURLOPT_POSTFIELDS => json_encode([ 'namespace' => 'org.yourco', 'capability' => 'llm.text.generate', 'worker_id' => 'wkr_abc123', ]),]);$result = json_decode(curl_exec($ch), true);curl_close($ch);
echo $result['outcome']; // "allow" or "deny"require 'net/http'require 'json'require 'uri'
api_key = 'phk_live_YOUR_KEY'uri = URI('https://api.pyhall.dev/api/v1/dispatch')
req = Net::HTTP::Post.new(uri)req['Authorization'] = "Bearer #{api_key}"req['Content-Type'] = 'application/json'req.body = JSON.generate( namespace: 'org.yourco', capability: 'llm.text.generate', worker_id: 'wkr_abc123')
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }result = JSON.parse(res.body)puts result['outcome'] # "allow" or "deny"use reqwest::Client;use serde_json::json;
#[tokio::main]async fn main() -> Result<(), Box<dyn std::error::Error>> { let client = Client::new(); let api_key = "phk_live_YOUR_KEY";
let res = client .post("https://api.pyhall.dev/api/v1/dispatch") .bearer_auth(api_key) .json(&json!({ "namespace": "org.yourco", "capability": "llm.text.generate", "worker_id": "wkr_abc123" })) .send() .await?;
let body: serde_json::Value = res.json().await?; println!("{}", body["outcome"]); // "allow" or "deny" Ok(())}import java.net.URI;import java.net.http.*;import java.net.http.HttpRequest.BodyPublishers;
public class PyHallExample { public static void main(String[] args) throws Exception { var client = HttpClient.newHttpClient(); var body = """ {"namespace":"org.yourco","capability":"llm.text.generate","worker_id":"wkr_abc123"} """;
var req = HttpRequest.newBuilder() .uri(URI.create("https://api.pyhall.dev/api/v1/dispatch")) .header("Authorization", "Bearer phk_live_YOUR_KEY") .header("Content-Type", "application/json") .POST(BodyPublishers.ofString(body)) .build();
var res = client.send(req, HttpResponse.BodyHandlers.ofString()); System.out.println(res.body()); }}using System.Net.Http.Headers;using System.Text;using System.Text.Json;
var client = new HttpClient();client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "phk_live_YOUR_KEY");
var payload = JsonSerializer.Serialize(new { @namespace = "org.yourco", capability = "llm.text.generate", worker_id = "wkr_abc123"});
var res = await client.PostAsync( "https://api.pyhall.dev/api/v1/dispatch", new StringContent(payload, Encoding.UTF8, "application/json"));
var result = await res.Content.ReadAsStringAsync();Console.WriteLine(result);import Foundation
let apiKey = "phk_live_YOUR_KEY"let url = URL(string: "https://api.pyhall.dev/api/v1/dispatch")!
var req = URLRequest(url: url)req.httpMethod = "POST"req.setValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")req.setValue("application/json", forHTTPHeaderField: "Content-Type")req.httpBody = try! JSONSerialization.data(withJSONObject: [ "namespace": "org.yourco", "capability": "llm.text.generate", "worker_id": "wkr_abc123"])
URLSession.shared.dataTask(with: req) { data, _, _ in if let data, let json = try? JSONSerialization.jsonObject(with: data) { print(json) }}.resume()# PowerShell$headers = @{ Authorization = "Bearer phk_live_YOUR_KEY" "Content-Type" = "application/json"}$body = @{ namespace = "org.yourco" capability = "llm.text.generate" worker_id = "wkr_abc123"} | ConvertTo-Json
Invoke-RestMethod -Method Post ` -Uri "https://api.pyhall.dev/api/v1/dispatch" ` -Headers $headers ` -Body $bodyAuthentication
Every request needs your API key. Get one in the dashboard.
Authorization: Bearer phk_live_<your-key>API keys are scoped to a namespace. A key for org.yourco cannot route requests for x.someone-else.
Error Responses
All errors return JSON with an error field and a standard HTTP status code.
| Status | Meaning |
|---|---|
400 | Bad request — missing or invalid fields |
401 | Unauthorized — missing or invalid API key |
403 | Forbidden — key doesn’t have access to this namespace |
404 | Not found — worker or namespace doesn’t exist |
429 | Rate limited — slow down |
500 | Registry error — try again |
What the SDK Adds
The Python, TypeScript, and Go SDKs provide:
- Local Hall Server — runs in-process, maintains a session, polls the ban list
- Policy Gate — enforces dispatch rules before your worker runs
- Attestation — SHA-256 hash of your worker package, verified against the registry
- Correlation tracking — automatic
correlation_idthreading through your logs
For lightweight use cases (single scripts, cron jobs, webhooks), calling the REST API directly is completely valid. For production AI agents, the SDK gives you the full safety stack.
Have a language or framework you’d like to see covered? Open an issue in the examples repo.