API

Web docs for the current API contract.

Admin Sync

Content Status

Mode: cache_db

Sync source hint: notion

Writing source hint: hey_world (hey_world_live)

Cached records: profiles=1, case_studies=1, posts=23, experiences=10

Last sync: ok=true, source=notion, code=ok, at=2026-02-28T19:18:20Z

Content Debug (Safe)

CONTENT_SYNC_SOURCE: notion

WRITING_SOURCE: hey_world

HEY_WORLD_URL: https://world.hey.com/johnnybutler/

NOTION_TOKEN present: true

NOTION_PROFILE_DB_ID present: true

NOTION_CASE_STUDIES_DB_ID present: true

NOTION_PROFILE_PAGE_ID present: true

NOTION_CV_DATABASE_ID present: true

Chat Runtime Status

Backend: openai

Ready: true

OPENAI_API_KEY present: true

OPENAI_MODEL: gpt-4.1-mini

Recent Sync Runs

Sync Configuration

CHAT_BACKEND=openai OPENAI_API_KEY=sk-... CONTENT_SYNC_SOURCE=notion WRITING_SOURCE=hey_world bin/rails content:sync

API v1

Base path: /api/v1

Authentication

{
  "error": {
    "code": "unauthorized",
    "message": "Unauthorized"
  }
}

Error Contract

Error responses use:

{
  "error": {
    "code": "<machine_code>",
    "message": "<human_message>"
  }
}

Malformed chat payloads return 400 with:

{
  "error": {
    "code": "invalid_request",
    "message": "Invalid request payload"
  }
}

Endpoints

GET /api/v1/content/profile

Returns profile content from the cache/fixture source.

Response:

{
  "data": {
    "name": "Johnny Butler",
    "tagline": "Building useful software with fast feedback loops",
    "bio": "Engineer focused on pragmatic delivery, clear process, and grounded AI systems.",
    "location": "London"
  }
}

GET /api/v1/content/case_studies

Returns case studies from the cache/fixture source.

Response:

{
  "data": [
    {
      "title": "Customer Onboarding Refresh",
      "summary": "Reduced signup drop-off by simplifying first-run setup.",
      "outcome": "Activation up 18% in six weeks.",
      "slug": "customer-onboarding-refresh"
    }
  ]
}

GET /api/v1/content/posts

Returns writing posts from the cache/fixture source.

Response:

{
  "data": [
    {
      "title": "Keeping CI Green",
      "summary": "Treat CI red as stop-the-line, and fix in the same branch.",
      "date": "2026-01-01",
      "slug": "keeping-ci-green"
    }
  ]
}

GET /api/v1/content/experiences

Returns CV experiences from cache source.

Response:

{
  "data": [
    {
      "company": "Materials Market",
      "role": "Head of Engineering",
      "location": "Remote",
      "start_date": "2021-11-01",
      "end_date": "",
      "current": true,
      "summary": "Owns platform and operations."
    }
  ]
}

POST /api/v1/chat/messages

Accepts a chat question and returns grounded answer + sources.

Request:

{
  "message": {
    "question": "What work has Johnny done?"
  }
}

Response:

{
  "data": {
    "question": "What work has Johnny done?",
    "answer": "Recent work includes...",
    "error_code": null,
    "sources": [
      {
        "label": "Content cache",
        "path": "app/services/content/cache_store.rb",
        "href": "/app/services/content/cache_store.rb"
      }
    ]
  }
}

Unsupported questions must return:

Notes

Source Files