managinator/backend/api_summary.json
2025-10-15 01:41:25 +02:00

518 lines
26 KiB
JSON

{
"api_meta": {
"name": "Managinator Backend API",
"overview": "Bearer-protected Flask service for managing investment profiles, users, and scrapers.",
"base_url_example": "http://localhost:8000",
"default_headers": {
"Content-Type": "application/json"
},
"cors": {
"allow_origin": "*",
"allow_credentials": true,
"notes": "Server responds to OPTIONS preflight without authentication and accepts credentialed requests from any origin."
},
"environment": {
"API_TOKEN": "Bearer token expected on every request.",
"FLUXIMMO_API_KEY": "Credential forwarded to Fluximmo analytics count endpoint.",
"FLUXIMMO_COUNT_URL": "Optional override for Fluximmo count API base URL (defaults to https://analytics.fluximmo.io/properties/count)."
}
},
"authentication": {
"strategy": "bearer_token",
"header": "Authorization",
"format": "Bearer <API_TOKEN>",
"env_variable": "API_TOKEN",
"notes": [
"Every request (except OPTIONS and Flask static assets) must carry the bearer token in the Authorization header.",
"There is no login endpoint; the expected token value is configured server-side via the API_TOKEN environment variable.",
"Missing, malformed, or incorrect bearer tokens return HTTP 401."
]
},
"conventions": {
"datetime_format": "Use ISO 8601 strings with timezone. Responses normalize to UTC with trailing Z.",
"boolean_input": "Send JSON booleans true/false. The backend also accepts 1/0 and common string equivalents, but clients should prefer booleans.",
"integer_input": "Integer fields accept JSON numbers. Numeric strings will be coerced server-side but should be avoided.",
"null_handling": "Fields documented as nullable accept JSON null and will persist as SQL NULL.",
"error_responses": {
"typical_status_codes": [400, 401, 404, 409, 503],
"body_format": "Flask default HTTP error responses; body may be plain text or HTML containing the description. Rely on HTTP status and message."
}
},
"schemas": {
"Profile": {
"description": "Investment profile descriptor persisted in users_investmentprofile.",
"fields": [
{"name": "profile_id", "type": "uuid", "nullable": false, "read_only": true, "description": "Primary key generated server-side."},
{"name": "name", "type": "string", "nullable": false, "read_only": false, "description": "Human-readable profile label."},
{"name": "description", "type": "string", "nullable": true, "read_only": false, "description": "Optional free-form description. Empty string preserved."},
{"name": "criteria", "type": "json", "nullable": false, "read_only": false, "description": "Arbitrary JSON object/array describing selection rules."},
{"name": "created_at", "type": "datetime", "nullable": false, "read_only": false, "description": "Creation timestamp. Defaults to current UTC when omitted."},
{"name": "is_active", "type": "boolean", "nullable": false, "read_only": false, "description": "Activation flag. Defaults to true on create."}
]
},
"User": {
"description": "Auth user record persisted in auth_user.",
"fields": [
{"name": "id", "type": "integer", "nullable": false, "read_only": true, "description": "Database primary key."},
{"name": "username", "type": "string", "nullable": false, "read_only": false, "description": "Unique login name."},
{"name": "password", "type": "string", "nullable": false, "read_only": false, "description": "Raw password value inserted as-is. Not returned in responses."},
{"name": "first_name", "type": "string", "nullable": false, "read_only": false, "description": "User first name."},
{"name": "last_name", "type": "string", "nullable": false, "read_only": false, "description": "User last name."},
{"name": "email", "type": "string", "nullable": false, "read_only": false, "description": "User email address."},
{"name": "is_superuser", "type": "boolean", "nullable": false, "read_only": false, "description": "Administrative superuser flag."},
{"name": "is_staff", "type": "boolean", "nullable": false, "read_only": false, "description": "Staff membership flag."},
{"name": "is_active", "type": "boolean", "nullable": false, "read_only": false, "description": "Account enabled flag."},
{"name": "date_joined", "type": "datetime", "nullable": false, "read_only": false, "description": "Account creation timestamp. Defaults to current UTC when omitted."},
{"name": "last_login", "type": "datetime", "nullable": true, "read_only": false, "description": "Most recent login timestamp."}
]
},
"Scraper": {
"description": "Scraper configuration persisted in scraper table.",
"fields": [
{"name": "id", "type": "uuid", "nullable": false, "read_only": true, "description": "Primary key generated server-side."},
{"name": "params", "type": "string", "nullable": true, "read_only": false, "description": "Serialized scraper parameters."},
{"name": "last_seen_days", "type": "integer", "nullable": true, "read_only": false, "description": "Number of days since listings were last seen."},
{"name": "first_seen_days", "type": "integer", "nullable": true, "read_only": false, "description": "Number of days since listings were first seen."},
{"name": "frequency", "type": "string", "nullable": true, "read_only": false, "description": "Cron or human-readable frequency descriptor."},
{"name": "task_name", "type": "string", "nullable": true, "read_only": false, "description": "Associated task identifier."},
{"name": "enabled", "type": "integer", "nullable": true, "read_only": false, "description": "1/0 flag for activation. Stored as integer."},
{"name": "property_types", "type": "string", "nullable": true, "read_only": false, "description": "Comma-separated property type filters."},
{"name": "page_size", "type": "integer", "nullable": true, "read_only": false, "description": "Listings fetched per page."},
{"name": "max_pages", "type": "integer", "nullable": true, "read_only": false, "description": "Maximum number of pages to crawl."},
{"name": "enrich_llm", "type": "integer", "nullable": true, "read_only": false, "description": "Toggle for LLM enrichment (1 enabled, 0 disabled)."},
{"name": "only_match", "type": "integer", "nullable": true, "read_only": false, "description": "Toggle for strict matching (1 enabled, 0 disabled)."}
]
}
},
"endpoints": [
{
"operation_id": "listProfiles",
"method": "GET",
"path": "/profiles",
"authenticated": true,
"description": "List all investment profiles ordered by most recent creation.",
"request": {
"headers": {
"Authorization": {"type": "string", "required": true, "description": "Bearer <API_TOKEN>"}
}
},
"responses": {
"200": {"description": "Array of profiles.", "body": {"type": "array", "items": {"$ref": "#/schemas/Profile"}}},
"401": {"description": "Missing or invalid bearer token."},
"503": {"description": "Database unavailable."}
}
},
{
"operation_id": "getProfile",
"method": "GET",
"path": "/profiles/<profile_id>",
"authenticated": true,
"description": "Retrieve a single profile by UUID.",
"request": {
"headers": {
"Authorization": {"type": "string", "required": true, "description": "Bearer <API_TOKEN>"}
},
"path_params": [
{"name": "profile_id", "type": "uuid", "required": true, "description": "Profile identifier."}
]
},
"responses": {
"200": {"description": "Profile payload.", "body": {"$ref": "#/schemas/Profile"}},
"401": {"description": "Missing or invalid bearer token."},
"404": {"description": "Profile not found."},
"503": {"description": "Database unavailable."}
}
},
{
"operation_id": "createProfile",
"method": "POST",
"path": "/profiles",
"authenticated": true,
"description": "Create a new investment profile.",
"request": {
"headers": {
"Content-Type": {"type": "string", "required": true, "example": "application/json"},
"Authorization": {"type": "string", "required": true, "description": "Bearer <API_TOKEN>"}
},
"body": {
"type": "object",
"required_fields": ["name", "criteria"],
"fields": {
"name": {"type": "string", "nullable": false, "description": "Profile name."},
"description": {"type": "string", "nullable": true, "description": "Optional description. Use null to clear."},
"criteria": {"type": "json", "nullable": false, "description": "Required JSON criteria payload."},
"created_at": {"type": "datetime", "nullable": false, "description": "Optional creation timestamp. Defaults to now (UTC)."},
"is_active": {"type": "boolean", "nullable": false, "description": "Defaults to true."}
}
}
},
"responses": {
"201": {"description": "Profile created.", "body": {"$ref": "#/schemas/Profile"}},
"400": {"description": "Validation failure (missing criteria, invalid field formats, or empty request)."},
"401": {"description": "Missing or invalid bearer token."},
"409": {"description": "Constraint violation (e.g., duplicate profile_id)."},
"503": {"description": "Database unavailable."}
}
},
{
"operation_id": "updateProfile",
"method": "PUT",
"path": "/profiles/<profile_id>",
"authenticated": true,
"description": "Update mutable fields on an existing profile.",
"request": {
"headers": {
"Content-Type": {"type": "string", "required": true, "example": "application/json"},
"Authorization": {"type": "string", "required": true, "description": "Bearer <API_TOKEN>"}
},
"path_params": [
{"name": "profile_id", "type": "uuid", "required": true, "description": "Profile identifier."}
],
"body": {
"type": "object",
"allowed_fields": ["name", "description", "criteria", "created_at", "is_active"],
"min_fields": 1,
"fields": {
"name": {"type": "string", "nullable": false, "description": "New profile name."},
"description": {"type": "string", "nullable": true, "description": "Use null to clear description."},
"criteria": {"type": "json", "nullable": false, "description": "Updated criteria JSON."},
"created_at": {"type": "datetime", "nullable": false, "description": "New creation timestamp."},
"is_active": {"type": "boolean", "nullable": false, "description": "Activation flag."}
}
}
},
"responses": {
"200": {"description": "Profile updated.", "body": {"$ref": "#/schemas/Profile"}},
"400": {"description": "No updatable fields provided or invalid data."},
"401": {"description": "Missing or invalid bearer token."},
"404": {"description": "Profile not found."},
"409": {"description": "Constraint violation."},
"503": {"description": "Database unavailable."}
}
},
{
"operation_id": "deleteProfile",
"method": "DELETE",
"path": "/profiles/<profile_id>",
"authenticated": true,
"description": "Remove a profile by UUID.",
"request": {
"headers": {
"Authorization": {"type": "string", "required": true, "description": "Bearer <API_TOKEN>"}
},
"path_params": [
{"name": "profile_id", "type": "uuid", "required": true, "description": "Profile identifier."}
]
},
"responses": {
"204": {"description": "Profile deleted. Empty body."},
"401": {"description": "Missing or invalid bearer token."},
"404": {"description": "Profile not found."},
"503": {"description": "Database unavailable."}
}
},
{
"operation_id": "listUsers",
"method": "GET",
"path": "/users",
"authenticated": true,
"description": "List all users ordered by ascending id.",
"request": {
"headers": {
"Authorization": {"type": "string", "required": true, "description": "Bearer <API_TOKEN>"}
}
},
"responses": {
"200": {"description": "Array of users.", "body": {"type": "array", "items": {"$ref": "#/schemas/User"}}},
"401": {"description": "Missing or invalid bearer token."},
"503": {"description": "Database unavailable."}
}
},
{
"operation_id": "getUser",
"method": "GET",
"path": "/users/<user_id>",
"authenticated": true,
"description": "Retrieve a user by numeric id.",
"request": {
"headers": {
"Authorization": {"type": "string", "required": true, "description": "Bearer <API_TOKEN>"}
},
"path_params": [
{"name": "user_id", "type": "integer", "required": true, "description": "User primary key."}
]
},
"responses": {
"200": {"description": "User payload.", "body": {"$ref": "#/schemas/User"}},
"401": {"description": "Missing or invalid bearer token."},
"404": {"description": "User not found."},
"503": {"description": "Database unavailable."}
}
},
{
"operation_id": "createUser",
"method": "POST",
"path": "/users",
"authenticated": true,
"description": "Create a new user record.",
"request": {
"headers": {
"Content-Type": {"type": "string", "required": true, "example": "application/json"},
"Authorization": {"type": "string", "required": true, "description": "Bearer <API_TOKEN>"}
},
"body": {
"type": "object",
"required_fields": ["password", "username", "first_name", "last_name", "email"],
"fields": {
"password": {"type": "string", "nullable": false, "description": "Password to store. No hashing performed."},
"username": {"type": "string", "nullable": false, "description": "Unique username."},
"first_name": {"type": "string", "nullable": false, "description": "First name."},
"last_name": {"type": "string", "nullable": false, "description": "Last name."},
"email": {"type": "string", "nullable": false, "description": "Email address."},
"is_superuser": {"type": "boolean", "nullable": false, "description": "Defaults to false when omitted."},
"is_staff": {"type": "boolean", "nullable": false, "description": "Defaults to false when omitted."},
"is_active": {"type": "boolean", "nullable": false, "description": "Defaults to true when omitted."},
"date_joined": {"type": "datetime", "nullable": true, "description": "Optional. Defaults to current UTC when omitted or null."},
"last_login": {"type": "datetime", "nullable": true, "description": "Optional last login timestamp."}
}
}
},
"responses": {
"201": {"description": "User created.", "body": {"$ref": "#/schemas/User"}},
"400": {"description": "Validation failure."},
"401": {"description": "Missing or invalid bearer token."},
"409": {"description": "Constraint violation (e.g., duplicate username or email)."},
"503": {"description": "Database unavailable."}
}
},
{
"operation_id": "updateUser",
"method": "PUT",
"path": "/users/<user_id>",
"authenticated": true,
"description": "Update mutable fields on a user.",
"request": {
"headers": {
"Content-Type": {"type": "string", "required": true, "example": "application/json"},
"Authorization": {"type": "string", "required": true, "description": "Bearer <API_TOKEN>"}
},
"path_params": [
{"name": "user_id", "type": "integer", "required": true, "description": "User primary key."}
],
"body": {
"type": "object",
"allowed_fields": ["password", "username", "first_name", "last_name", "email", "is_superuser", "is_staff", "is_active", "date_joined", "last_login"],
"min_fields": 1,
"fields": {
"password": {"type": "string", "nullable": false, "description": "New raw password."},
"username": {"type": "string", "nullable": false, "description": "New username."},
"first_name": {"type": "string", "nullable": false, "description": "New first name."},
"last_name": {"type": "string", "nullable": false, "description": "New last name."},
"email": {"type": "string", "nullable": false, "description": "New email."},
"is_superuser": {"type": "boolean", "nullable": false, "description": "Updated admin flag."},
"is_staff": {"type": "boolean", "nullable": false, "description": "Updated staff flag."},
"is_active": {"type": "boolean", "nullable": false, "description": "Updated active flag."},
"date_joined": {"type": "datetime", "nullable": false, "description": "Updated join timestamp."},
"last_login": {"type": "datetime", "nullable": true, "description": "Updated last login timestamp."}
}
}
},
"responses": {
"200": {"description": "User updated.", "body": {"$ref": "#/schemas/User"}},
"400": {"description": "No updatable fields provided or invalid data."},
"401": {"description": "Missing or invalid bearer token."},
"404": {"description": "User not found."},
"409": {"description": "Constraint violation."},
"503": {"description": "Database unavailable."}
}
},
{
"operation_id": "deleteUser",
"method": "DELETE",
"path": "/users/<user_id>",
"authenticated": true,
"description": "Delete a user by id.",
"request": {
"headers": {
"Authorization": {"type": "string", "required": true, "description": "Bearer <API_TOKEN>"}
},
"path_params": [
{"name": "user_id", "type": "integer", "required": true, "description": "User primary key."}
]
},
"responses": {
"204": {"description": "User deleted. Empty body."},
"401": {"description": "Missing or invalid bearer token."},
"404": {"description": "User not found."},
"503": {"description": "Database unavailable."}
}
},
{
"operation_id": "listScrapers",
"method": "GET",
"path": "/scrapers",
"authenticated": true,
"description": "List all scraper configurations ordered by id.",
"request": {
"headers": {
"Authorization": {"type": "string", "required": true, "description": "Bearer <API_TOKEN>"}
}
},
"responses": {
"200": {"description": "Array of scrapers.", "body": {"type": "array", "items": {"$ref": "#/schemas/Scraper"}}},
"401": {"description": "Missing or invalid bearer token."},
"503": {"description": "Database unavailable."}
}
},
{
"operation_id": "getScraper",
"method": "GET",
"path": "/scrapers/<scraper_id>",
"authenticated": true,
"description": "Retrieve a single scraper by text id.",
"request": {
"headers": {
"Authorization": {"type": "string", "required": true, "description": "Bearer <API_TOKEN>"}
},
"path_params": [
{"name": "scraper_id", "type": "string", "required": true, "description": "Scraper identifier."}
]
},
"responses": {
"200": {"description": "Scraper payload.", "body": {"$ref": "#/schemas/Scraper"}},
"401": {"description": "Missing or invalid bearer token."},
"404": {"description": "Scraper not found."},
"503": {"description": "Database unavailable."}
}
},
{
"operation_id": "createScraper",
"method": "POST",
"path": "/scrapers",
"authenticated": true,
"description": "Create a scraper configuration.",
"request": {
"headers": {
"Content-Type": {"type": "string", "required": true, "example": "application/json"},
"Authorization": {"type": "string", "required": true, "description": "Bearer <API_TOKEN>"}
},
"body": {
"type": "object",
"required_fields": [],
"fields": {
"params": {"type": "string", "nullable": true, "description": "Serialized parameter payload."},
"frequency": {"type": "string", "nullable": true, "description": "Execution cadence descriptor."},
"task_name": {"type": "string", "nullable": true, "description": "Linked task name."},
"property_types": {"type": "string", "nullable": true, "description": "Comma-separated property types."},
"last_seen_days": {"type": "integer", "nullable": true, "description": "Days since listing last seen."},
"first_seen_days": {"type": "integer", "nullable": true, "description": "Days since listing first seen."},
"page_size": {"type": "integer", "nullable": true, "description": "Fetch size per page."},
"max_pages": {"type": "integer", "nullable": true, "description": "Maximum crawl pages."},
"enabled": {"type": "integer", "nullable": true, "description": "Activation flag (1 or 0)."},
"enrich_llm": {"type": "integer", "nullable": true, "description": "LLM enrichment toggle (1 or 0)."},
"only_match": {"type": "integer", "nullable": true, "description": "Strict matching toggle (1 or 0)."}
}
}
},
"responses": {
"201": {"description": "Scraper created.", "body": {"$ref": "#/schemas/Scraper"}},
"400": {"description": "Validation failure."},
"401": {"description": "Missing or invalid bearer token."},
"409": {"description": "Constraint violation (e.g., duplicate id)."},
"503": {"description": "Database unavailable."}
}
},
{
"operation_id": "updateScraper",
"method": "PUT",
"path": "/scrapers/<scraper_id>",
"authenticated": true,
"description": "Update mutable fields on a scraper configuration.",
"request": {
"headers": {
"Content-Type": {"type": "string", "required": true, "example": "application/json"},
"Authorization": {"type": "string", "required": true, "description": "Bearer <API_TOKEN>"}
},
"path_params": [
{"name": "scraper_id", "type": "string", "required": true, "description": "Scraper identifier."}
],
"body": {
"type": "object",
"allowed_fields": ["params", "frequency", "task_name", "property_types", "last_seen_days", "first_seen_days", "page_size", "max_pages", "enabled", "enrich_llm", "only_match"],
"min_fields": 1,
"fields": {
"params": {"type": "string", "nullable": true, "description": "Updated serialized parameters."},
"frequency": {"type": "string", "nullable": true, "description": "Updated cadence descriptor."},
"task_name": {"type": "string", "nullable": true, "description": "Updated task name."},
"property_types": {"type": "string", "nullable": true, "description": "Updated property type list."},
"last_seen_days": {"type": "integer", "nullable": true, "description": "Updated last seen days."},
"first_seen_days": {"type": "integer", "nullable": true, "description": "Updated first seen days."},
"page_size": {"type": "integer", "nullable": true, "description": "Updated page size."},
"max_pages": {"type": "integer", "nullable": true, "description": "Updated max pages."},
"enabled": {"type": "integer", "nullable": true, "description": "Updated enabled flag."},
"enrich_llm": {"type": "integer", "nullable": true, "description": "Updated enrichment flag."},
"only_match": {"type": "integer", "nullable": true, "description": "Updated only match flag."}
}
}
},
"responses": {
"200": {"description": "Scraper updated.", "body": {"$ref": "#/schemas/Scraper"}},
"400": {"description": "No updatable fields provided or invalid data."},
"401": {"description": "Missing or invalid bearer token."},
"404": {"description": "Scraper not found."},
"409": {"description": "Constraint violation."},
"503": {"description": "Database unavailable."}
}
},
{
"operation_id": "deleteScraper",
"method": "DELETE",
"path": "/scrapers/<scraper_id>",
"authenticated": true,
"description": "Delete a scraper configuration.",
"request": {
"headers": {
"Authorization": {"type": "string", "required": true, "description": "Bearer <API_TOKEN>"}
},
"path_params": [
{"name": "scraper_id", "type": "string", "required": true, "description": "Scraper identifier."}
]
},
"responses": {
"204": {"description": "Scraper deleted. Empty body."},
"401": {"description": "Missing or invalid bearer token."},
"404": {"description": "Scraper not found."},
"503": {"description": "Database unavailable."}
}
},
{
"operation_id": "countScraperProperties",
"method": "POST",
"path": "/scrapers/count",
"authenticated": true,
"description": "Return the Fluximmo property count derived from a scraper configuration.",
"request": {
"headers": {
"Content-Type": {"type": "string", "required": true, "example": "application/json"},
"Authorization": {"type": "string", "required": true, "description": "Bearer <API_TOKEN>"}
},
"body": {
"type": "object",
"required_fields": ["params"],
"fields": {
"params": {"type": "object", "nullable": false, "description": "Base Fluximmo search filters. Accepts a JSON object or string-encoded JSON."},
"first_seen_days": {"type": "integer", "nullable": true, "description": "Days before now for firstSeenAt.min."},
"last_seen_days": {"type": "integer", "nullable": true, "description": "Days before now for lastSeenAt.max."}
}
}
},
"responses": {
"200": {"description": "Count retrieved.", "body": {"type": "object", "fields": {"count": {"type": "integer", "nullable": false}}}},
"400": {"description": "Validation failure."},
"401": {"description": "Missing or invalid bearer token."},
"502": {"description": "Fluximmo upstream error."}
}
}
]
}