# API Specification All endpoints are prefixed with `/api`. In production the full path is: `https://optical-dev.oliver.solutions/barclays-banner-builder/api/…` Authentication is JWT Bearer token unless marked **public**. Interactive docs available at `/docs` in development only (`APP_ENV=development`). --- ## Auth — `/api/auth` | Method | Path | Auth | Description | |--------|------|------|-------------| | POST | `/api/auth/token` | Public | Login — returns JWT access token | | GET | `/api/auth/me` | Bearer | Return current user's id, email, role | ### POST /api/auth/token Request body: `application/x-www-form-urlencoded` | Field | Type | Required | Notes | |-------|------|---------|-------| | `username` | string | Yes | User email address | | `password` | string | Yes | Plain-text password | Response `200`: ```json { "access_token": "eyJ…", "token_type": "bearer" } ``` Error `401`: Invalid credentials. --- ## Health — `/api/health` | Method | Path | Auth | Description | |--------|------|------|-------------| | GET | `/api/health` | Public | Liveness check — returns `{"status": "ok"}` | --- ## Conversations — `/api/conversations` | Method | Path | Auth | Description | |--------|------|------|-------------| | POST | `/api/conversations` | Bearer | Create conversation (and optionally send first message) | | GET | `/api/conversations` | Bearer | List user's conversations (last 50, newest first) | | GET | `/api/conversations/{id}` | Bearer | Get conversation with full message history | | POST | `/api/conversations/{id}/messages` | Bearer | Send a message — triggers intent classification + async job | ### POST /api/conversations ```json { "first_message": "string (optional)" } ``` Response `201`: ```json { "conversation_id": "uuid", "brief_id": "uuid", "message_id": "uuid|null", "job_id": "uuid|null" } ``` If `first_message` is non-empty, an RQ `chat_turn` job is enqueued. Poll `GET /api/jobs/{job_id}`. ### POST /api/conversations/{id}/messages ```json { "text": "string" } ``` Response `202`: ```json { "message_id": "uuid", "job_id": "uuid|null", "reply": "string|null" } ``` If intent is `clarify`, `reply` contains the inline response and `job_id` is null. If intent is `generate` or `refine`, poll the job for the resulting `banner_set_id`. --- ## Briefs — `/api/briefs` | Method | Path | Auth | Description | |--------|------|------|-------------| | GET | `/api/briefs` | Bearer | List user's briefs (last 50) with associated banner sets | | POST | `/api/briefs` | Bearer | Create brief and enqueue copy generation job | | GET | `/api/briefs/{brief_id}` | Bearer | Get single brief | ### POST /api/briefs ```json { "text": "string", "n_variants": 4, "aspect_ratios": ["Medium", "Large"] } ``` Response `202`: ```json { "job_id": "uuid", "brief_id": "uuid" } ``` --- ## Jobs — `/api/jobs` | Method | Path | Auth | Description | |--------|------|------|-------------| | GET | `/api/jobs/{job_id}` | Public | Poll job status | Response: ```json { "id": "uuid", "type": "generate_copy|chat_turn|render_pdf|index_icons|ingest_rag", "status": "pending|running|done|failed", "result": { "banner_set_id": "uuid", "variant_count": 8 }, "error": "string|null", "created_at": "ISO 8601", "finished_at": "ISO 8601|null" } ``` --- ## Banner Sets — `/api/banner-sets` | Method | Path | Auth | Description | |--------|------|------|-------------| | GET | `/api/banner-sets/{id}` | Bearer | Get banner set with all variants (includes resolved icon URLs) | | PATCH | `/api/banner-sets/{id}/status` | Bearer | Update status: `draft`, `approved`, `exported` | | POST | `/api/banner-sets/{id}/custom-size` | Bearer | Add Custom-format variants at given dimensions | | POST | `/api/banner-sets/{id}/contact-sheet` | Bearer | Enqueue PDF rendering job | | GET | `/api/banner-sets/{id}/contact-sheet.pdf` | Bearer | Download rendered PDF (404 if not yet rendered) | | GET | `/api/banner-sets/{id}/export.csv` | Bearer | Download Workfront CSV (optional `?variant_ids=uuid,uuid`) | ### GET /api/banner-sets/{id} Response includes: ```json { "id": "uuid", "brief_id": "uuid", "status": "draft", "variants": [{ "id": "uuid", "aspect_ratio": "SM2|MD1|MD2|MD3|LG1|LG2|Custom", "pair_id": "uuid", "theme": "navy|sky-blue|yellow|lime|teal", "short_title": "string", "long_body": "string", "cta": "string", "cta_secondary": "string|null", "dam_asset_ref": "string|null", "dam_asset_url": "string|null", "icon_id": "uuid|null", "icon_url": "string|null", "edited_by_user": false, "custom_width": null, "custom_height": null }] } ``` ### POST /api/banner-sets/{id}/custom-size ```json { "width": 640, "height": 200 } ``` Response `201`: ```json { "created": 4, "variant_ids": ["uuid", "uuid", "uuid", "uuid"] } ``` --- ## Banner Variants — `/api/banner-variants` | Method | Path | Auth | Description | |--------|------|------|-------------| | PUT | `/api/banner-variants/{id}` | Bearer | Update variant copy, DAM asset, icon, or theme | | POST | `/api/banner-variants/{id}/refine` | Bearer | AI-refine this variant's copy based on feedback text | ### PUT /api/banner-variants/{id} All fields optional: ```json { "short_title": "string", "long_body": "string", "cta": "string", "cta_secondary": "string", "dam_asset_ref": "string", "dam_asset_url": "string", "icon_id": "uuid", "theme": "navy|sky-blue|yellow|lime|teal" } ``` Sets `edited_by_user = true`. Returns updated variant object. ### POST /api/banner-variants/{id}/refine ```json { "feedback": "Make the title more urgent and mention 0% balance transfer" } ``` Returns updated variant object with AI-rewritten copy. --- ## Icons — `/api/icons` | Method | Path | Auth | Description | |--------|------|------|-------------| | GET | `/api/icons/categories` | Public | List available illustration categories | | GET | `/api/icons` | Public | Search icons by `?category=Banking&q=card&limit=50` | Response: ```json { "icons": [{ "id": "uuid", "name": "string", "category": "string", "url": "/api/illustrations/…" }] } ``` Categories: Abstract, Architecture, Banking, Cards, Devices, Documents, Infrastructure, Nature, Objects, World --- ## DAM — `/api/dam` | Method | Path | Auth | Description | |--------|------|------|-------------| | GET | `/api/dam/search` | Bearer | Search DAM assets (`?q=…&page=1&page_size=20`) | | GET | `/api/dam/assets/{asset_id}` | Bearer | Get single DAM asset by ID | When `ADOBE_DAM_API_BASE_URL` is empty, the mock client is used — returns canned Picsum placeholder images. --- ## Admin — `/api/admin` All admin endpoints require `role = "admin"`. ### System Prompts | Method | Path | Description | |--------|------|-------------| | GET | `/api/admin/system-prompts` | List all versions (newest first) | | GET | `/api/admin/system-prompts/active` | Get currently active prompt | | POST | `/api/admin/system-prompts` | Create new version (not yet active) | | PUT | `/api/admin/system-prompts/{id}` | Update — creates new version, deactivates old (immutable audit trail) | | POST | `/api/admin/system-prompts/{id}/activate` | Activate a specific version | ### System Prompt body ```json { "label": "string", "system_text": "string", "tov_text": "string|null", "tov_banned_phrases": "say hello to,give you peace of mind", "short_title_max": 32, "long_body_max": 128, "cta_max": 50 } ``` ### Users | Method | Path | Description | |--------|------|-------------| | GET | `/api/admin/users` | List all users | | POST | `/api/admin/users` | Create user (`email`, `password`, `role`) | | PATCH | `/api/admin/users/{id}` | Update email, password, or role | | DELETE | `/api/admin/users/{id}` | Delete user (cannot delete self or last admin) | --- ## Static Assets | Path | Description | |------|-------------| | `/api/illustrations/{Category}/{filename}.png` | Served by FastAPI StaticFiles mount — Barclays illustration library |