faz
Integrations

REST API

Use the faz REST API for non-MCP clients, scripts, and CI. Same safety pipeline, plain HTTP.

faz serve exposes faz's full surface as a FastAPI app on 127.0.0.1:8787. Use this when the client isn't an MCP-compatible AI assistant — scripts, custom agents, dashboards, CI checks, or anything that just wants to send HTTP requests.

For the per-endpoint reference (every path, every field, every status code), see REST endpoints.

Prerequisites

  • faz installed (pip install faz-core — see Install).
  • A faz.yaml with at least one database connection and a permissions block.

Start the server

faz serve
uv run faz serve
python -m faz serve

Output:

INFO:     Uvicorn running on http://127.0.0.1:8787 (Press CTRL+C to quit)
INFO:     Application startup complete.

Default bind is 127.0.0.1:8787. To bind elsewhere:

faz serve --host 127.0.0.1 --port 9000
uv run faz serve --host 127.0.0.1 --port 9000
python -m faz serve --host 127.0.0.1 --port 9000

Anything off-loopback prints a warning at startup:

warning: binding '0.0.0.0' exposes faz off-loopback. faz has
         no built-in remote auth — put a reverse proxy with
         your own auth (nginx / Cloudflare Tunnel / Tailscale)
         in front before going to prod.

This is not optional. faz has no auth surface of its own.

Quick example

The smallest useful request:

curl -X POST http://localhost:8787/v1/query/simple \
  -H 'Content-Type: application/json' \
  -d '{
    "database": "<database>",
    "table": "<table>",
    "language": "sql",
    "query": "SELECT COUNT(*) FROM <table>"
  }'

Response:

{
  "request_id": "req_a1b2c3d4e5f6",
  "status": "ok",
  "data": {
    "columns": ["count"],
    "rows": [{ "count": 8421 }],
    "row_count": 1
  },
  "safety": {
    "stages_passed": ["PROMPT_GUARD", "RBAC_GATE", "AST_CHECKER", "INJECTION_ANALYSER", "GUARDRAILS"],
    "warnings": []
  },
  "metadata": {
    "database": "<database>",
    "execution_time_ms": 12.4,
    "transport": "rest/local",
    "steps": [],
    "merge_latency_ms": null
  }
}

A blocked query returns 403 with a structured envelope:

{
  "request_id": "req_a1b2c3d4e5f6",
  "status": "blocked",
  "error": {
    "stage": "RBAC_GATE",
    "reason": "Access denied: <database>.<sensitive-table> requires permission for SELECT; policy grants none",
    "suggestion": null
  }
}

The full request and response shapes for every endpoint are on REST endpoints.

Authentication

There isn't any.

faz has no built-in concept of users, tokens, or sessions. The model is: bind to loopback by default, and trust whatever auth your reverse proxy enforces if you expose faz remotely.

For development, this is fine — faz serve only listens on 127.0.0.1 so only processes on the same host can hit it.

For production, put faz behind something:

  • nginx / Caddy / Traefik with HTTP basic auth, JWT validation, or mTLS.
  • Cloudflare Tunnel with Cloudflare Access for SSO.
  • Tailscale to scope access to a private network.
  • A small auth-aware proxy you write that validates an Authorization header and forwards.

The reverse proxy is responsible for identifying who's calling. faz doesn't read auth headers; it doesn't know who you are. Per-agent or per-user permissions aren't supported (there is no agent identity model — see Permissions).

Never expose faz serve directly to the public internet. Always front it with auth. faz's audit log is per-instance, not per-user — if you let multiple users hit the same instance, you can't tell which user ran which query.

Verify

curl http://localhost:8787/v1/health
{ "status": "ok", "version": "0.1.0", "databases_connected": 1 }

If databases_connected is 0, faz couldn't open any of the connections in faz.yaml. Check the server logs for connection errors and see Connection failed.

For a deeper check:

curl http://localhost:8787/v1/databases

Returns each configured database with reachable: true | false and discovered tables. Failed connections appear in the list with reachable: false and an error describing why.

Common patterns

Scripted ad-hoc queries:

#!/bin/bash
QUERY="SELECT COUNT(*) FROM <table> WHERE <condition>"
curl -sS -X POST http://localhost:8787/v1/query/simple \
  -H 'Content-Type: application/json' \
  -d "{\"database\":\"<database>\",\"table\":\"<table>\",\"language\":\"sql\",\"query\":$(jq -Rsa . <<<"$QUERY")}" \
  | jq '.data.rows[0]'

CI assertions:

COUNT=$(curl -sS http://faz.internal/v1/query/simple ... | jq -r '.data.rows[0].count')
[ "$COUNT" -gt 0 ] || exit 1

Custom client SDKs: any HTTP client library works. The Pydantic schemas (SimpleQueryRequest, FederatedQueryRequest) are stable; generate types from /openapi.json if your language has codegen tooling.

Dashboards: point Grafana, Metabase, or any dashboard that accepts a custom JSON HTTP source at faz endpoints. The audit log captures the dashboard's queries the same way it captures an LLM's.

Operational notes

  • faz reads faz.yaml once at startup. To pick up config changes, restart faz serve.
  • Audit logging is identical across transports. REST requests log as transport: "rest/local".
  • Long-running queries are bounded by the safety.query_timeout_seconds from faz.yaml (default 30s) and HTTP keepalive timeouts at your proxy layer.
  • The OpenAPI schema is at http://localhost:8787/docs (Swagger UI) and http://localhost:8787/openapi.json (raw JSON) for tooling.

On this page