Skip to content

API Reference

The Ekklesia API is a FastAPI application (src/ekklesia/api/app.py) serving five endpoints. All requests are async; the database session is managed per-request via the get_session dependency.

Interactive docs are available at http://localhost:8000/docs (Swagger UI) and http://localhost:8000/redoc.

Endpoints

GET /health

Health check. Returns immediately with no database access.

Response 200 OK:

{"status": "ok"}


POST /sermons

Starts a sermon preparation pipeline and streams progress as Server-Sent Events.

Request body:

{"topic": "The power of faith in difficult times"}

Field Type Constraints
topic string 1–2000 characters, required

Response: text/event-stream

The response body is a stream of SSE events. Each event is a JSON-encoded StageEvent on a data: line, followed by a blank line:

data: {"stage":"planner","status":"started","elapsed_ms":0,"payload":null}

data: {"stage":"planner","status":"completed","elapsed_ms":4312,"payload":{...}}

data: {"stage":"research","status":"started","elapsed_ms":0,"payload":null}

...

data: {"stage":"brief","status":"completed","elapsed_ms":892,"payload":{"draft":{...},"markdown":"..."}}

The stream closes after brief (success) or the first failed event.

StageEvent schema:

Field Type Values
stage string planner research exegesis structure critique brief error
status string started completed failed
elapsed_ms integer milliseconds since stage start
payload object or null stage output dict, or {"error":"...", "type":"..."} on failure
sources array or null RetrievalSource[] — present on research.completed; stable IDs for future inline citations

Example — failed event:

{
  "stage": "research",
  "status": "failed",
  "elapsed_ms": 1204,
  "payload": {"error": "504 Gateway Timeout", "type": "HTTPStatusError"}
}


POST /sermons/revise

Applies critique recommendations to an existing sermon draft. Uses the Revision agent to produce an improved markdown document without re-running the full pipeline.

Request body:

{
  "current_markdown": "# Sermon Title\n\n...",
  "recommended_revisions": [
    "Strengthen the application section with more concrete examples",
    "Add a cross-reference to Hebrews 11 in the faith section"
  ]
}

Field Type Constraints
current_markdown string min length 1, required
recommended_revisions string[] min 1 item, required

Response 200 OKRevisionResult:

{
  "revised_markdown": "# Sermon Title\n\n...(improved)...",
  "changes_summary": "Expanded the application section with three concrete daily examples. Added Hebrews 11:1-6 citation to reinforce the definition of faith."
}

This endpoint powers the "Apply Critique" button in the frontend canvas. The recommended_revisions list comes directly from the Critique agent's CritiqueReport.recommended_revisions field.


GET /lookup/strongs/{strongs_number}

Returns a single lexicon entry for a Strong's number.

Path parameter:

Parameter Pattern Example
strongs_number ^[GH]\d{1,5}$ G4102, H539

Response 200 OKLexiconEntry:

{
  "strongs_number": "G4102",
  "language": "greek",
  "lemma": "πίστις",
  "transliteration": "pistis",
  "pronunciation": "pis'-tis",
  "definition": "persuasion, credence; moral conviction of religious truth...",
  "kjv_usage": "assurance, belief, faith, fidelity",
  "derivation": "from G3982",
  "occurrences": 243
}

Errors:

Code Condition
404 Not Found Strong's number not in database
422 Unprocessable Entity Pattern validation failure (e.g. X999)

GET /passage/{reference:path}

Returns a Bible passage by reference string. The :path suffix allows slashes in the reference (e.g. multi-chapter ranges).

Path parameter:

Parameter Examples
reference Romans 8:28, John 3:16-17, Rom 8:28, Genesis 1:1-2:3

Reference parsing is lenient — common abbreviations (Rom, Gen, Mt) are resolved to canonical book names.

Response 200 OK:

{
  "passage": {
    "id": "esv:45:8:28:8:28",
    "translation": "esv",
    "book": "Romans",
    "reference": "Romans 8:28",
    "pericope_title": "Future Glory",
    "text": "And we know that for those who love God all things work together for good..."
  },
  "verses": [
    {
      "book": "Romans",
      "chapter": 8,
      "verse": 28,
      "text": "And we know that for those who love God..."
    }
  ]
}

Errors:

Code Condition
422 Unprocessable Entity Reference string cannot be parsed
404 Not Found Passage not found in database

SSE client notes

The /sermons endpoint uses sse-starlette's EventSourceResponse. A few implementation details matter for clients:

EventSource limitation. The browser's native EventSource API does not support POST requests. The frontend uses fetch + ReadableStream instead (web/src/api/sermonStream.ts).

Heartbeat pings. sse-starlette emits : ping comment lines periodically. These keep the connection alive through proxies. Clients should silently ignore lines starting with :.

nginx buffering. In the Docker/Railway deployment nginx must have proxy_buffering off and proxy_cache off on the /sermons location or events will batch up and not stream through. See web/nginx.conf.template.

Railway edge proxy timeout. Railway's edge cuts idle SSE connections at 30s. The X-Accel-Buffering: no response header on /sermons prevents this.

CORS

Allowed origins are configured via the CORS_ORIGINS environment variable (comma-separated). The default covers local development:

CORS_ORIGINS=http://localhost:5173,http://localhost:3000

For production, set this to the Railway frontend URL:

CORS_ORIGINS=https://ekklesia-web.up.railway.app

Logfire instrumentation

logfire.instrument_fastapi(app) traces every request: method, path, status code, and duration. The Logfire bootstrap in app.py must run before create_app() imports local routers so that SQLAlchemy instrumentation is active when deps.py creates the engine.