Examples
Copy-paste requests for the flows people actually run.
Every example below runs against the live host. Export a base URL first and paste as-is:
export ER_API=https://api.engineeringruntime.com
Publish a policy and watch a runtime pick it up
Publish a new version:
curl -s -X POST "$ER_API/api/public/policies" \
-H 'Content-Type: application/json' \
-d '{
"version": "v13",
"name": "enterprise-policy",
"description": "Deny production writes outside change windows",
"updated_by": "kishore",
"yaml": "version: v13\nrules:\n - id: deny-prod-write\n effect: deny\n match:\n provider: github\n operation: write\n"
}'
201 Created comes back with the stored document, and the previous active
version is deactivated in the same transaction.
Confirm what a runtime would fetch:
curl -s "$ER_API/api/public/policies?active=true" \
-H 'X-Runtime-Client: engineering-runtime'
Then sync a runtime and check it landed:
export RUNTIME_PORTAL_ENABLED=true
export RUNTIME_PORTAL_BASE_URL=$ER_API
export RUNTIME_PORTAL_PATH_PREFIX=/api/public
runtime portal sync
runtime policy show
Publishing v13 twice returns 409 — versions are immutable so that audit
records stay resolvable.
Ingest a batch of audit events
curl -s -X POST "$ER_API/api/public/audit" \
-H 'Content-Type: application/json' \
-d '{
"events": [
{
"executor": "kishore",
"consumer": "human",
"command": "github repo list",
"provider": "github",
"policy_decision": "ALLOWED",
"status": "success",
"duration_ms": 84
},
{
"executor": "claude",
"consumer": "ai",
"command": "files write /etc/hosts",
"provider": "files",
"policy_decision": "DENIED",
"reason": "path outside allowed roots",
"status": "denied",
"ai": true,
"duration_ms": 12
}
]
}'
Response: {"accepted": 2, "events": [...]} with the stored records, ids and
ingested_at filled in.
Page through audit history
# Denied commands attributed to AI consumers, newest first
curl -s "$ER_API/api/public/audit?decision=denied&consumer=ai&limit=25"
# Free-text across command, executor, resource, provider, capability
curl -s "$ER_API/api/public/audit?q=repo%20list&limit=10"
# Second page
curl -s "$ER_API/api/public/audit?limit=25&offset=25"
Read total for the count and step offset by limit; returned tells you
how many came back in this response.
Read the catalog
# Providers with live status merged from audit
curl -s "$ER_API/api/public/providers"
# Capabilities for one provider
curl -s "$ER_API/api/public/capabilities?provider=github"
# Search capabilities
curl -s "$ER_API/api/public/capabilities?q=secret"
The catalog and generated_at fields in each response tell you which
generated document answered — useful when a provider you expect is missing and
you need to know whether to redeploy.
Check the fleet
curl -s -X POST "$ER_API/api/public/fleet/heartbeat" \
-H 'Content-Type: application/json' \
-d '{
"name": "ci-runner-01",
"version": "0.9.3",
"policy_version": "v13",
"hostname": "gh-runner-7",
"os": "linux"
}'
curl -s "$ER_API/api/public/fleet"
Heartbeats upsert on (organization, name), so a runtime reporting every few
minutes updates one row rather than accumulating history. Status is derived on
read: online under 15 minutes, idle under 24 hours, offline beyond.
Enterprise: log in and manage keys (issued access)
Creating and listing API keys is an enterprise step. Credentials are issued
on request via app.engineeringruntime.com — this portal does not publish a
password or a sample key. To try the API without access, use the /api/public
examples above (or the demo UI at demo.engineeringruntime.com).
With credentials you were issued:
TOKEN=$(curl -s -X POST "$ER_API/api/v1/auth/login" \
-H 'Content-Type: application/json' \
-d '{"email":"YOUR_EMAIL","password":"YOUR_PASSWORD"}' \
| python3 -c 'import sys,json;print(json.load(sys.stdin)["token"])')
curl -s "$ER_API/api/v1/api-keys" -H "Authorization: Bearer $TOKEN"
curl -s -X POST "$ER_API/api/v1/api-keys" \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"name":"ci-runner","scopes":"runtime"}'
The plaintext key appears once, in the create response. Store it before you close the terminal — only its hash is kept.
Use a key from a runtime (after you have one)
export RUNTIME_PORTAL_ENABLED=true
export RUNTIME_PORTAL_BASE_URL=$ER_API
export RUNTIME_PORTAL_PATH_PREFIX=/api/v1
export RUNTIME_PORTAL_API_KEY=erk_… # from the create response above
runtime portal status
runtime portal sync
For Community / demo try-out without a key, set RUNTIME_PORTAL_PATH_PREFIX=/api/public
and omit RUNTIME_PORTAL_API_KEY.
Discover the API from a script
# What is this host, and where are its contracts?
curl -s "$ER_API/"
# Every documented operation, flattened
curl -s "$ER_API/metadata/apis" \
| python3 -c 'import sys,json
for op in json.load(sys.stdin)["operations"]:
print(f"{op[\"method\"]:6} {op[\"path\"]}")'
# What does this deployment actually enforce?
curl -s "$ER_API/metadata/authentication"
Generate a client from the contract
curl -sO "$ER_API/openapi.yaml"
openapi-generator-cli generate -i openapi.yaml -g typescript-fetch -o ./er-client
Smoke-test a deployment
curl -fsS "$ER_API/health"
curl -fsS "$ER_API/version"
curl -fsS "$ER_API/api/public/health"
curl -fsS "$ER_API/api/v1/health"
curl -fsS "$ER_API/openapi.yaml" > /dev/null && echo "contract ok"