# 09 - API Reference > Base URL in local dev: `http://127.0.0.1:7860`. The Vite dev server at > `http://127.0.0.1:5173` proxies the same paths. --- ## Authentication Public app endpoints: ```text GET /health POST /turn POST /turn/stream POST /feedback GET /sessions/* GET /users/* DELETE /sessions/* ``` Protected operational endpoints: ```text /config* /admin/* /analytics/* ``` Protected endpoints require one of: ```http X-APE-Admin-Token: Authorization: Bearer ``` If `APE_ADMIN_TOKEN` is not configured on the server, protected endpoints return `503`. If the header is missing or wrong, they return `401`. --- ## Core Flow ### `GET /health` ```json { "status": "ok" } ``` ### `POST /turn` Generate one assistant response using the non-streaming path. Request: ```json { "user_id": "alex_retiree", "session_id": "optional-session-id", "query": "Compare Roth IRA vs Traditional IRA", "generate_response": true } ``` Response: ```json { "response_id": "resp_...", "session_id": "sess_...", "assistant_message_id": "msg_...", "classification": { "intent": "Comparison", "topic": "roth_vs_traditional_ira", "signal": "no_signal" }, "selection": { "selected_strategy": "comparison_table", "strategies_available": [ "standard_llm", "comparison_table", "pros_cons_table", "bullet_contrast" ], "ucb_at_selection": 1.522, "policy_version": "v1" }, "answer": "| Feature | Roth IRA | Traditional IRA | ...", "rendered_format": "comparison_table", "timings_ms": { "total": 2100.4 } } ``` ### `POST /turn/stream` Same request body as `/turn`, but returns Server-Sent Events with `Content-Type: text/event-stream`. Event payloads are JSON objects sent as SSE `data:` lines. `metadata` event: ```json { "event": "metadata", "response_id": "resp_...", "session_id": "sess_...", "intent": "Comparison", "topic": "roth_vs_traditional_ira", "selected_strategy": "comparison_table", "candidate_strategies": ["standard_llm", "comparison_table"], "select_timings_ms": {} } ``` `delta` event: ```json { "event": "delta", "text": "partial raw model text" } ``` `done` event: ```json { "event": "done", "response_id": "resp_...", "session_id": "sess_...", "assistant_message_id": "msg_...", "answer": "final parsed answer", "rendered_format": "comparison_table", "format_compliance": 1, "selection": {}, "classification": {}, "timings_ms": {} } ``` `error` event: ```json { "event": "error", "message": "..." } ``` Streaming and non-streaming paths both write `ape_messages`, `ape_turn_record`, and pending format compliance signals. ### `POST /feedback` Append a UI signal to one response. The response may finalize immediately or queue the signal for later finalization. Request: ```json { "response_id": "resp_...", "user_id": "alex_retiree", "signal": "copy_save" } ``` Possible response when queued: ```json { "status": "queued", "response_id": "resp_..." } ``` Possible response when applied: ```json { "status": "applied", "response_id": "resp_...", "signal": "pattern_engaged_positive", "reward_category": "weak_positive", "normalized_reward": 0.5, "strategy_row_after": { "strategy": "comparison_table", "count": 4, "avg_reward": 0.625, "cached_ucb": 1.457 } } ``` Possible response with no bandit update: ```json { "status": "applied_no_bandit_update", "response_id": "resp_...", "signal": "thumbs_up", "reward_category": null, "normalized_reward": null } ``` Possible rejected response: ```json { "status": "rejected", "response_id": "resp_...", "reason": "already_finalized", "current_status": "APPLIED" } ``` Feedback statuses: | Status | Meaning | |---|---| | `queued` | Signal buffered; turn remains PENDING | | `applied` | Turn finalized and bandit updated | | `applied_no_bandit_update` | Turn finalized but no format-relevant reward existed | | `rejected` | Missing response, wrong user, already finalized, or race | | `skipped` | Unknown signal | --- ## Sessions and History ### `GET /sessions/{session_id}/messages?user_id=X&limit=200` Returns raw chat messages for one session, scoped to the requesting user. `user_id` is required. ### `GET /sessions/{session_id}/turns?user_id=X&limit=100` Returns `ape_turn_record` rows for one session, scoped to the requesting user. `user_id` is required. ### `GET /users/{user_id}/sessions?limit=20` Returns recent session summaries for a user. ### `GET /users/{user_id}/latest-session` Returns: ```json { "session_id": "sess_..." } ``` or: ```json { "session_id": null } ``` ### `GET /users/{user_id}/responses?limit=50` Returns recent turn records for one user. ### `DELETE /sessions/{session_id}?user_id=X` Deletes one user's messages and response records for that session. Bandit state is preserved. --- ## Config Reads All config endpoints require admin token. | Endpoint | Purpose | |---|---| | `GET /config/intents` | List intents | | `GET /config/strategies` | List strategies | | `GET /config/policies` | List policies | | `GET /config/signal-rules` | List signal routing rules | | `GET /config/reward-scale` | List reward categories | | `GET /config/instructions?strategy_id=X&status=Y` | List instruction versions | | `GET /config/offers?status=Y` | List outreach policies | --- ## Config Writes ### `POST /config/intents` ```json { "intent_id": "Comparison", "description": "User wants two or more options compared side by side.", "changed_by": "admin_user" } ``` ### `POST /config/strategies` ```json { "strategy_id": "comparison_table", "format_type": "comparison_table", "changed_by": "admin_user" } ``` ### `POST /config/signal-rules` ```json { "signal_name": "format_change_request", "format_relevant": true, "content_relevant": false, "format_category": "strong_negative", "content_category": null, "source": "llm", "feature_id": 1, "expected_frequency": "rare", "evidence_quality": "high", "consumers": ["bandit", "instruction_quality"], "changed_by": "admin_user" } ``` ### `POST /config/reward-scale` ```json { "category": "strong_positive", "normalized_reward": 1.0, "changed_by": "admin_user" } ``` `raw_reward` is not part of the current request model. ### `POST /config/policies` ```json { "domain": "finance", "intent": "Comparison", "topic": "_default", "strategy_id": "comparison_table", "policy_version": "v1", "exploration_constant": 1.0, "changed_by": "admin_user" } ``` ### `POST /config/instructions` ```json { "strategy_id": "comparison_table", "version": "v2", "instruction_text": "Format as a markdown table comparing the options.", "instruction_uri": null, "activate": true, "changed_by": "admin_user" } ``` ### `POST /config/instructions/activate?strategy_id=X&version=Y&changed_by=admin_user` Activates an existing instruction version. ### `POST /config/status` ```json { "entity_type": "strategy", "entity_id": "comparison_table", "version": "v1", "status": "INACTIVE", "changed_by": "admin_user" } ``` `version` is required for instruction rows and optional for most other entity types. ### `POST /config/offers` ```json { "topic": "retirement_accounts", "offer_type": "retirement_planning_consultation", "description": "Schedule a 30-minute planning call", "min_interest_score": 0.8, "weight_frequency": 0.4, "weight_recency": 0.25, "weight_engagement": 0.25, "weight_followup": 0.1, "changed_by": "admin_user" } ``` --- ## Config Deletes | Endpoint | Purpose | |---|---| | `DELETE /config/intents/{intent_id}` | Delete an intent | | `DELETE /config/strategies/{strategy_id}` | Delete a strategy and its instructions | | `DELETE /config/signal-rules/{signal_name}` | Delete a signal rule | | `DELETE /config/reward-scale/{category}` | Delete a reward value | | `DELETE /config/policies?intent=Y&topic=Z&strategy_id=S` | Delete a policy | | `DELETE /config/instructions/{strategy_id}/{version}` | Delete an instruction version | | `DELETE /config/offers/{topic}` | Delete an outreach policy | All deletes are audited. --- ## Admin and Ops All admin endpoints require admin token. | Endpoint | Purpose | |---|---| | `DELETE /admin/clear-user/{user_id}` | Remove one user's runtime data | | `DELETE /admin/clear-all` | Clear runtime collections while preserving config/audit | | `POST /admin/seed` | Seed default config | | `GET /admin/db-snapshot?user_id=X&limit=N` | Diagnostic snapshot | | `GET /admin/bandit-state?user_id=X&only_pulled=true` | Inspect bandit rows | | `DELETE /admin/bandit-state/cell?user_id=X&domain=D&intent=I&topic=T&strategy=S` | Delete one bandit cell or one arm when `strategy` is supplied | | `GET /admin/audit?date=YYYY-MM-DD&limit=N` | Audit log | There is no `/admin/rebuild-bandit` endpoint. UCB cache refresh happens during feedback finalization, and bulk repair should be done with a script if needed. --- ## Analytics All analytics endpoints require admin token. | Endpoint | Purpose | |---|---| | `POST /analytics/recompute?days=N` | Rebuild derived collections from `ape_turn_record` | | `GET /analytics/user-interests?user_id=X&limit=K&refresh=bool` | User topic interest | | `GET /analytics/topic-users?topic=X&limit=K&min_score=Y` | Users interested in topic | | `GET /analytics/trends?days=N&limit=K&refresh=bool` | Trending topics | | `GET /analytics/topic-timeseries?topic=X&days=N` | Daily trend for one topic | | `GET /analytics/platform-timeseries?days=N` | Daily platform activity | | `GET /analytics/topics-timeseries?days=N&top_n=K` | Daily series for top topics | | `GET /analytics/user-timeseries?user_id=X&days=N` | Daily user activity | | `GET /analytics/offers/{user_id}?domain=Y` | Outreach recommendations | | `GET /analytics/customer-health?days=N&cohort_weeks=K` | Retention/satisfaction health | | `GET /analytics/rag-quality?days=N&min_turns=K&sample_limit=S` | Content-quality indicators | | `GET /analytics/instruction-quality?days=N&min_turns=K&sample_limit=S` | Format/instruction quality | | `GET /analytics/strategy-performance?user_id=X&min_pulls=N` | Strategy performance tiers | | `GET /analytics/platform-overview?days=N&top_n=K` | Dashboard overview | | `GET /analytics/active-users?days=N&min_interest=X&limit=K` | Outreach roster | | `GET /analytics/user-profile?user_id=X&domain=Y` | 12-facet user profile | | `GET /analytics/cognitive-facets?user_id=X&min_interactions=N` | Facets; omit user_id for global | --- ## Status Codes | Code | Meaning | |---|---| | `200` | Success | | `204` | Successful delete with no body | | `400` | Invalid request value | | `401` | Protected endpoint missing valid admin token | | `404` | Resource not found | | `422` | FastAPI validation error, such as missing required query param | | `500` | Store/orchestrator not initialized or unexpected server error | | `503` | Protected endpoint called while `APE_ADMIN_TOKEN` is not configured | Feedback errors are usually returned as `200` with `status: "rejected"` or `status: "skipped"` so the UI can show clean inline state without treating user feedback races as transport failures. --- ## See Also - [02 - Runtime paths](./02-runtime-paths.md) - [03 - Admin config](./03-admin-config.md) - [04 - Analytics layer](./04-analytics-layer.md)