# API v1

Base path: `/profiles/:slug/api/v1`

## Related agent-friendly surfaces

These routes sit alongside the tenant-scoped public API and should be kept in sync with product behavior when onboarding or readiness semantics change:

- Public agent manifest: `GET /.well-known/agent.json`
- Human-readable agent guide: `GET /agents`
- Authenticated agent context for the current signed-in account: `GET /api/agent/context`
- Versioned inspect-first agent API: `GET /api/agent/v1/*`

Unlike the tenant-scoped public API below, the authenticated agent context is session-scoped to the current owner account and is not addressed by public profile slug.

This API mirrors the multitenant public profile app. Each request is scoped to a published profile slug.

Example base path:

```text
/profiles/bravo-studio/api/v1
```

## Authentication

- The tenant-scoped public API under `/profiles/:slug/api/v1` is public and does not require `X-API-Key`.
- Invalid or missing key responses below apply to authenticated agent endpoints, not the tenant-scoped public content/chat routes.
- Authenticated agent endpoints return `401` with:

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

## Error Contract

Error responses use:

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

Agent API v1 commonly returns these machine-readable codes:

- `api_key_missing`
- `invalid_api_key`
- `owner_context_required`
- `account_scope_required`
- `forbidden_account_scope`
- `missing_parameter`
- `account_not_found`
- `draft_not_found`
- `resource_not_found`
- `draft_archived`

Missing required agent parameters return `400` with:

```json
{
  "error": {
    "code": "missing_parameter",
    "message": "Missing required parameter",
    "details": {
      "parameter": "draft[attributes]"
    }
  }
}
```

## Endpoints

### `GET /profiles/:slug/api/v1/content/profile`

Returns the published profile summary for that account.

When an avatar is published, `avatar_url` is an Explore-served Active Storage path backed by durable storage rather than a temporary external file URL.

Response:

```json
{
  "data": {
    "name": "Bravo Studio",
    "tagline": "Design systems and shipping support",
    "bio": "Bravo Studio is another customer account fixture.",
    "location": "Remote",
    "avatar_url": "/rails/active_storage/blobs/proxy/:signed_id/bravo.png"
  }
}
```

### `GET /profiles/:slug/api/v1/content/case_studies`

Returns published case studies for that account.

Response:

```json
{
  "data": [
    {
      "title": "Platform modernization program",
      "summary": "Introduced release guardrails; improved delivery visibility; reduced ops overhead.",
      "outcome": "Reduced release risk for customer-facing changes.",
      "slug": "platform-modernization-program"
    }
  ]
}
```

### `GET /profiles/:slug/api/v1/content/posts`

Returns published writing posts for that account.

Response:

```json
{
  "data": [
    {
      "title": "Shipping practical software in small slices",
      "summary": "Notes on release discipline and pragmatic delivery.",
      "date": "2026-03-01",
      "slug": "shipping-practical-software-in-small-slices"
    }
  ]
}
```

### `GET /profiles/:slug/api/v1/content/experiences`

Returns published experience entries for that account.

Response:

```json
{
  "data": [
    {
      "company": "Bravo Studio",
      "role": "Head of Engineering",
      "location": "London",
      "start_date": "2022",
      "end_date": "",
      "current": true,
      "summary": "Scaled product delivery; led platform quality; managed release flow."
    }
  ]
}
```

### `POST /profiles/:slug/api/v1/chat/messages`

Accepts a chat question and returns a grounded answer plus sources for that profile.

Request:

```json
{
  "message": {
    "question": "What work has Bravo Studio done?"
  }
}
```

Response:

```json
{
  "data": {
    "question": "What work has Bravo Studio done?",
    "answer": "Recent work includes platform modernization and self-serve operations rollout.",
    "error_code": null,
    "sources": [
      {
        "label": "Work",
        "path": "/profiles/bravo-studio/work",
        "href": "/profiles/bravo-studio/work"
      }
    ]
  }
}
```

Unsupported questions must return:

- `answer`: `"Not in the provided material."`
- `sources`: `[]`
- optional `error_code` for provider/runtime issues such as `openai_rate_limited`, `openai_auth_failed`, `openai_timeout`, or `openai_request_failed`

## Notes

- Versioning still follows `/api/v1`.
- The API is now tenant-scoped by profile slug so it matches the public Explore experience.
- Contracts are enforced by request tests in `test/requests/api/v1`.
- Operational status remains outside this tenant surface at `GET /api/content/status`.
- `chat_status` on the ops endpoints still reports backend/readiness/key-present/model for runtime checks.
- `error_tracking_status` on the ops endpoints reports the configured provider, readiness, API-key presence, and runtime environment for internal checks.

## Explore agent API v1

Base path: `/api/agent/v1`

This is a separate inspect-first surface for humans and coding agents. It is read-heavy by default, with explicit owner-only document apply and draft operations.

The shared Explore CLI and API contract is the main integration story here. Codex, Claude Code, OpenCode, and Cursor are supported today on top of that contract, tool-specific helpers stay thin and optional, and other shell-capable tools should also work with Explore's shared CLI flow, with fuller support rolling out very soon.

### Authentication model

Public-safe endpoints:

- `GET /api/agent/v1/profile?slug=:slug`
- `GET /api/agent/v1/content?slug=:slug`

Owner-only endpoints:

- `GET /api/agent/v1/onboarding/status`
- `GET /api/agent/v1/manifest/next_actions`
- `GET /api/agent/v1/publish/preview`
- `GET /api/agent/v1/profile/document`
- `POST /api/agent/v1/profile/document/preview`
- `PUT /api/agent/v1/profile/document`
- `GET /api/agent/v1/drafts`
- `GET /api/agent/v1/drafts/:id`
- `POST /api/agent/v1/drafts/:id/apply`
- `GET /api/agent/v1/drafts/:id/apply_preview`
- `PATCH /api/agent/v1/drafts/:id`
- `POST /api/agent/v1/drafts/:id/archive`
- `POST /api/agent/v1/content/propose_update`

Owner-only endpoints use either:

- the current signed-in session account, or
- an account-scoped owner token passed as `X-API-Key`, optionally plus `account=<slug>` or `account_id=<id>` scope when the client wants to be explicit

When `slug` is provided, the public-safe inspect endpoints above do not require an API key. Owner-only endpoints still require the current signed-in session or an account token.

Account tokens are scoped to a single Explore account and should be created from that account's signed-in workspace. Most CLI users should use `explore login` and browser approval first; the token value is mainly for advanced, scripted, or non-interactive owner access.

### Response envelope

```json
{
  "status": "ok",
  "resource": "profile.inspect",
  "generated_at": "2026-03-26T11:00:00Z",
  "account_id": 1,
  "account_slug": "acme-careers",
  "context": {
    "mode": "owner"
  }
}
```

### Endpoints

#### `GET /api/agent/v1/profile`

Returns public profile data, completeness, weak spots, and next safe actions.

- Public mode: pass `slug=:slug` to inspect a published profile without `X-API-Key`.
- Owner mode: omit `slug` and use the current signed-in session, or pass an account token as `X-API-Key` plus optional `account` / `account_id`.

#### `PATCH /api/agent/v1/drafts/:id`

Updates an existing open draft without publishing it.

Request:

```json
{
  "draft": {
    "title": "Refined project draft",
    "summary": "Sharper framing for the same idea.",
    "attributes": {
      "summary": "Sharper framing for the same idea."
    }
  }
}
```

Archived drafts are read-only and return:

```json
{
  "error": {
    "code": "draft_archived",
    "message": "Archived drafts are read-only."
  }
}
```

#### `GET /api/agent/v1/content`

Returns a structured content inventory for projects, posts, experiences, testimonials, and public profile sections.

- Public mode: pass `slug=:slug` to inspect published content without `X-API-Key`.
- Owner mode: omit `slug` and use the current signed-in session, or pass an account token as `X-API-Key` plus optional `account` / `account_id`.

#### `GET /api/agent/v1/onboarding/status`

Returns the current onboarding/setup step, blockers, guidance, and useful internal/public URLs.

#### `GET /api/agent/v1/manifest/next_actions`

Returns the explicit next safe action and the currently allowed action set for the account.

#### `GET /api/agent/v1/profile/document`

Returns the canonical Explore profile document for the owner account as structured JSON. The CLI renders this into YAML for local editing. Successful responses also include `document_fingerprint`, a `sha256:` hash of the canonical document payload.

#### `POST /api/agent/v1/profile/document/preview`

Validates a proposed profile document and returns a non-mutating change preview against the current owner account state.

- invalid documents return `422` with path-specific errors under `error.details.errors` plus `field_path`, `section`, and `recommended_action`
- successful previews include `document_fingerprint`, changed sections, machine-readable diff details, compact summary counts, before/after snapshots, and allowed next actions
- preview never updates canonical Explore data; explicit apply is still required

Collection sections use the most trustworthy diff available for that section:

- `add_item`, `remove_item`, and `update_item` are used when Explore can match repeated items safely
- `replace_collection` is a deliberate fallback when Explore cannot trust a semantic item match

#### `PUT /api/agent/v1/profile/document`

Validates and applies a profile document back into the canonical Explore account records.

- invalid documents return `422` with path-specific errors under `error.details.errors`
- clients may include `expected_fingerprint` to require that the submitted document still matches the last reviewed canonical payload
- fingerprint mismatches return `409` with `error.details.expected_fingerprint`, `actual_fingerprint`, and `recommended_action`
- successful apply responses include `document_fingerprint`, `expected_fingerprint` when provided, and `fingerprint_verified`

#### `GET /api/agent/v1/publish/preview`

Returns a safe share-readiness preview and explainable publish impact. It never publishes content.

#### `POST /api/agent/v1/content/propose_update`

Records an audit-friendly content proposal event. V1 stores the proposal for review and does not mutate or publish live content.

Use this when an agent wants to suggest an edit to an existing target, or to the profile-level target, without creating a draft artifact.

Do not use this to create a brand-new post, project, or experience. For new content, prefer:

1. `POST /api/agent/v1/content/create_draft`
2. `GET /api/agent/v1/drafts/:id`
3. `GET /api/agent/v1/drafts/:id/apply_preview`
4. `POST /api/agent/v1/drafts/:id/apply`

Example: propose an update to an existing post

```json
{
  "proposal": {
    "target_type": "post",
    "target_id": 42,
    "summary": "Tighten the post summary and CTA.",
    "attributes": {
      "summary": "A sharper summary for the same published post.",
      "title": "Keeping CI Green in Agentic Workflows"
    }
  }
}
```

#### `POST /api/agent/v1/content/create_draft`

Creates a first-class draft artifact for future content. Drafts are owner-only, never published automatically, and can appear in owner-facing `content.list` / `profile.inspect` responses.

This is the preferred path for creating new content such as a new blog post, case study, project, or experience entry.

Recommended new-content workflow:

1. `POST /api/agent/v1/content/create_draft`
2. `GET /api/agent/v1/drafts/:id`
3. `GET /api/agent/v1/drafts/:id/apply_preview`
4. `POST /api/agent/v1/drafts/:id/apply`

Example: create a new post draft

```json
{
  "draft": {
    "target_type": "post",
    "title": "Shipping Safer Agent Content Workflows",
    "summary": "Draft a new post about safer authoring flows for Explore agents.",
    "attributes": {
      "title": "Shipping Safer Agent Content Workflows",
      "summary": "Draft a new post about safer authoring flows for Explore agents."
    }
  }
}
```

#### `GET /api/agent/v1/drafts`

Lists owner-only drafts with stable metadata and allowed next actions.

#### `GET /api/agent/v1/drafts/:id`

Returns one owner-only draft for review.

#### `POST /api/agent/v1/drafts/:id/apply`

Applies a reviewed draft into Explore data and archives the draft.

- Profile drafts update live account public profile fields.
- Project, post, and experience drafts create new account-backed records.
- This action is audited and does not perform broader publish orchestration.

#### `GET /api/agent/v1/drafts/:id/apply_preview`

Returns a read-only preview of what the draft would change if a future apply step were introduced.

- Profile drafts compare against the current live profile fields.
- Project, post, and experience drafts currently preview creation of a new item because V1 drafts do not yet carry a target record id.

#### `POST /api/agent/v1/drafts/:id/archive`

Marks one owner-only draft as archived so it no longer appears in open-draft pathways.
