# API Endpoints Reference **Document Version:** 1.0 **Last Updated:** 2026-01-10 **Application:** Norda Biznes Hub **Base URL:** https://nordabiznes.pl --- ## Table of Contents 1. [Overview](#overview) 2. [Authentication](#authentication) 3. [Rate Limiting](#rate-limiting) 4. [CSRF Protection](#csrf-protection) 5. [Response Formats](#response-formats) 6. [Public Endpoints](#public-endpoints) 7. [Authentication Endpoints](#authentication-endpoints) 8. [User Endpoints](#user-endpoints) 9. [API Endpoints (JSON)](#api-endpoints-json) 10. [Admin Endpoints](#admin-endpoints) 11. [Audit Endpoints](#audit-endpoints) 12. [Error Codes](#error-codes) 13. [Examples](#examples) --- ## Overview The Norda Biznes Hub provides **90+ HTTP endpoints** across multiple functional areas: - **Public Pages:** Company directory, search, profile viewing - **Authentication:** Registration, login, password reset, email verification - **User Features:** Dashboard, AI chat, forum, messaging, calendar, classifieds - **API (JSON):** RESTful endpoints for programmatic access - **Admin:** Content moderation, user management, audit tools, analytics - **Audit:** Public audit reports (SEO, Social Media, GBP, IT) ### Technology Stack - **Framework:** Flask 3.0 - **WSGI Server:** Gunicorn (4 workers, 120s timeout) - **Session Management:** Flask-Login (server-side sessions) - **CSRF Protection:** Flask-WTF CSRFProtect - **Rate Limiting:** Flask-Limiter (IP-based) ### URL Structure ``` https://nordabiznes.pl/ ├── / # Public pages ├── /api/ # JSON API endpoints ├── /admin/ # Admin-only pages ├── /audit/ # Public audit reports └── /company/ # Company profiles ``` --- ## Authentication ### Authentication Methods 1. **Session-based (Browser):** - Flask-Login sessions - Cookie: `session` (HttpOnly, Secure, SameSite=Lax) - Duration: 7 days with "Remember Me" checkbox - Logout: `GET /logout` 2. **No API Key Authentication:** - Public API endpoints require no authentication - Protected endpoints require active browser session ### Authorization Levels | Level | Description | Check Method | |-------|-------------|--------------| | **Public** | No authentication required | None | | **Authenticated** | Logged-in user | `@login_required` decorator | | **NORDA Member** | User with verified company association | `current_user.is_verified` | | **Admin** | Admin privileges | `current_user.is_admin == True` | ### Access Control Pattern ```python # Public endpoint (no decorator) @app.route('/search') def search(): # Anyone can access # Authenticated endpoint @app.route('/dashboard') @login_required def dashboard(): # Requires login # Admin endpoint @app.route('/admin/users') @login_required def admin_users(): if not current_user.is_admin: flash('Brak uprawnień administratora', 'danger') return redirect(url_for('index')) # Admin logic ``` --- ## Rate Limiting ### Global Rate Limits Configured via Flask-Limiter, applied by IP address: - **200 requests per day** - **50 requests per hour** ### Per-Endpoint Limits Some endpoints have additional rate limiting: | Endpoint | Limit | Reason | |----------|-------|--------| | `POST /login` | 5 requests/hour | Brute force protection | | `POST /register` | 5 requests/hour | Abuse prevention | | `POST /forgot-password` | 5 requests/hour | Email spam prevention | | `POST /api/seo/audit` | 10 requests/hour | API quota protection | | `POST /api/chat/*/message` | 50 requests/hour | AI API cost control | ### Rate Limit Headers Responses include rate limit information: ```http X-RateLimit-Limit: 50 X-RateLimit-Remaining: 47 X-RateLimit-Reset: 1704974400 ``` ### Rate Limit Exceeded Response ```http HTTP/1.1 429 Too Many Requests Content-Type: application/json { "error": "Rate limit exceeded", "retry_after": 3600 } ``` --- ## CSRF Protection ### Overview All **POST, PUT, DELETE** requests require CSRF token validation (Flask-WTF CSRFProtect). ### CSRF Token in Forms HTML forms automatically include CSRF token: ```html
{{ form.hidden_tag() }}
``` ### CSRF Token in AJAX For AJAX requests, include CSRF token in headers: ```javascript fetch('/api/chat/start', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': getCsrfToken() // Get from meta tag or cookie }, body: JSON.stringify({...}) }); ``` ### CSRF Token Retrieval Token available in HTML meta tag: ```html ``` JavaScript helper: ```javascript function getCsrfToken() { return document.querySelector('meta[name="csrf-token"]').content; } ``` ### CSRF Exemptions No endpoints are exempt from CSRF protection. --- ## Response Formats ### HTML Responses Server-side rendered Jinja2 templates: ```http GET /company/pixlab-sp-z-o-o → HTTP/1.1 200 OK Content-Type: text/html; charset=utf-8 ... ``` ### JSON Responses API endpoints return JSON: ```http GET /api/companies → HTTP/1.1 200 OK Content-Type: application/json { "companies": [...], "total": 80 } ``` ### Flash Messages User-facing messages use Flask flash messages: ```python flash('Rekomendacja dodana pomyślnie', 'success') flash('Błąd: Nieprawidłowe dane', 'danger') ``` Categories: `success`, `danger`, `warning`, `info` ### Redirects Many POST endpoints redirect after success: ```http POST /login → HTTP/1.1 302 Found Location: /dashboard ``` --- ## Public Endpoints ### Homepage ```http GET / ``` **Description:** Company directory homepage with grid of all companies **Authentication:** None **Query Parameters:** - None **Response:** - HTML page with company cards - Displays all 80 member companies - Filterable by category (client-side) **Example:** ```bash curl https://nordabiznes.pl/ ``` --- ### Company Search ```http GET /search ``` **Description:** Search companies by keyword, NIP, REGON, or category **Authentication:** None **Query Parameters:** | Parameter | Type | Required | Description | Example | |-----------|------|----------|-------------|---------| | `q` | string | No | Search query (keyword, NIP, REGON) | `strony www` | | `category` | integer | No | Category ID filter | `1` (IT) | **Search Strategies:** 1. **NIP/REGON Direct Lookup** (10 digits → NIP, 9/14 digits → REGON) 2. **Synonym Expansion** (55+ keyword mappings) 3. **PostgreSQL FTS** (full-text search with Polish stemming) 4. **Fuzzy Matching** (pg_trgm for typos) **Response:** - HTML page with search results - Results sorted by relevance score - Empty state if no matches **Example:** ```bash curl "https://nordabiznes.pl/search?q=strony+www&category=1" ``` --- ### Company Profile ```http GET /company/ ``` **Description:** Detailed company profile page **Authentication:** None **Path Parameters:** | Parameter | Type | Description | Example | |-----------|------|-------------|---------| | `slug` | string | Company URL slug (kebab-case) | `pixlab-sp-z-o-o` | **Response Sections:** 1. Header (name, category, verification badge, short description) 2. Contact bar (website, email, phone, location) 3. About (company description) 4. Services & Competencies (tags) 5. Unique Selling Points 6. Contact Details (full contact cards) 7. Legal & Business Info (NIP, REGON, KRS, year established) 8. Social Media (6 platforms) 9. Website Analysis (SEO, performance scores) **Example:** ```bash curl https://nordabiznes.pl/company/pixlab-sp-z-o-o ``` **404 Response:** ```http HTTP/1.1 404 Not Found Content-Type: text/html

Firma nie została znaleziona

``` --- ### Health Check ```http GET /health ``` **Description:** System health check endpoint for monitoring **Authentication:** None **Response:** ```json { "status": "ok", "database": "connected", "timestamp": "2026-01-10T12:00:00Z" } ``` **Status Codes:** - `200 OK` - System healthy - `500 Internal Server Error` - Database connection failed **Example:** ```bash curl https://nordabiznes.pl/health ``` **Use Case:** Zabbix monitoring, uptime checks --- ### Company Events/News ```http GET /aktualnosci ``` **Description:** Public feed of company events and news **Authentication:** None **Response:** - HTML page with list of recent company events - Sorted by date (newest first) - Pagination (20 events per page) **Example:** ```bash curl https://nordabiznes.pl/aktualnosci ``` --- ### Release Notes ```http GET /release-notes ``` **Description:** Application changelog and version history **Authentication:** None **Response:** - HTML page with release notes - Grouped by version - Change history from `app.py` **Example:** ```bash curl https://nordabiznes.pl/release-notes ``` --- ## Authentication Endpoints ### User Registration ```http POST /register ``` **Description:** Create new user account with email verification **Authentication:** None **Rate Limit:** 5 requests/hour **Request Body (Form):** | Field | Type | Required | Validation | Description | |-------|------|----------|------------|-------------| | `email` | string | Yes | Email format, unique | User email address | | `password` | string | Yes | 8+ chars, uppercase, lowercase, digit | Account password | | `first_name` | string | Yes | 1-100 chars | User first name | | `last_name` | string | Yes | 1-100 chars | User last name | | `company_id` | integer | No | Valid company ID | Associated company (optional) | **Password Requirements:** - Minimum 8 characters - At least one uppercase letter - At least one lowercase letter - At least one digit **Workflow:** ``` 1. Validate input 2. Check email uniqueness 3. Hash password (PBKDF2:SHA256) 4. Create User (is_verified=False) 5. Generate verification token (32-byte URL-safe) 6. Send verification email (Microsoft Graph API) 7. Redirect to /login with flash message ``` **Success Response:** ```http HTTP/1.1 302 Found Location: /login Set-Cookie: session=... Flash: "Konto utworzone. Sprawdź email w celu weryfikacji." ``` **Error Response:** ```http HTTP/1.1 200 OK Content-Type: text/html Flash: "Email już istnieje w systemie" ``` **Example:** ```bash curl -X POST https://nordabiznes.pl/register \ -d "email=jan.kowalski@example.com" \ -d "password=SecurePass123" \ -d "first_name=Jan" \ -d "last_name=Kowalski" ``` --- ### Email Verification ```http GET /verify-email/ ``` **Description:** Verify user email address via token from email **Authentication:** None **Path Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `token` | string | Verification token (32-byte URL-safe) | **Token Properties:** - Length: 43 characters (base64-encoded 32 bytes) - Storage: `users.verification_token` - Expiry: No expiration (should be implemented) **Workflow:** ``` 1. Query User by verification_token 2. If found: Set is_verified=True, verification_token=NULL 3. If not found: Show error 4. Redirect to /login ``` **Success Response:** ```http HTTP/1.1 302 Found Location: /login Flash: "Email zweryfikowany pomyślnie!" ``` **Error Response:** ```http HTTP/1.1 302 Found Location: /login Flash: "Nieprawidłowy lub wygasły token weryfikacyjny" ``` **Example:** ```bash curl https://nordabiznes.pl/verify-email/AbCdEf123456... ``` --- ### User Login ```http POST /login ``` **Description:** Authenticate user and create session **Authentication:** None **Rate Limit:** 5 requests/hour **Request Body (Form):** | Field | Type | Required | Description | |-------|------|----------|-------------| | `email` | string | Yes | User email | | `password` | string | Yes | User password | | `remember` | boolean | No | Remember me (7 day session) | **Workflow:** ``` 1. Query User by email 2. Verify password (werkzeug.security.check_password_hash) 3. Check is_verified = True 4. Check is_active = True 5. Call login_user(user, remember=True/False) 6. Redirect to /dashboard or ?next URL ``` **Success Response:** ```http HTTP/1.1 302 Found Location: /dashboard Set-Cookie: session=...; HttpOnly; Secure; SameSite=Lax ``` **Error Response:** ```http HTTP/1.1 200 OK Content-Type: text/html Flash: "Nieprawidłowy email lub hasło" ``` **Example:** ```bash curl -X POST https://nordabiznes.pl/login \ -d "email=jan.kowalski@example.com" \ -d "password=SecurePass123" \ -d "remember=on" ``` --- ### User Logout ```http GET /logout ``` **Description:** End user session **Authentication:** Required **Response:** ```http HTTP/1.1 302 Found Location: / Set-Cookie: session=; Expires=... Flash: "Wylogowano pomyślnie" ``` **Example:** ```bash curl https://nordabiznes.pl/logout \ -H "Cookie: session=..." ``` --- ### Password Reset Request ```http POST /forgot-password ``` **Description:** Request password reset email **Authentication:** None **Rate Limit:** 5 requests/hour **Request Body (Form):** | Field | Type | Required | Description | |-------|------|----------|-------------| | `email` | string | Yes | User email address | **Workflow:** ``` 1. Query User by email 2. Generate reset_token (32-byte URL-safe) 3. Save to users.reset_token 4. Send reset email with link to /reset-password/ 5. Redirect to /login with flash message ``` **Response:** ```http HTTP/1.1 302 Found Location: /login Flash: "Email z linkiem do resetowania hasła został wysłany" ``` **Note:** Returns success message even if email not found (security best practice) --- ### Password Reset ```http POST /reset-password/ ``` **Description:** Reset password using token from email **Authentication:** None **Path Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `token` | string | Reset token (32-byte URL-safe) | **Request Body (Form):** | Field | Type | Required | Validation | |-------|------|----------|------------| | `password` | string | Yes | 8+ chars, uppercase, lowercase, digit | | `password_confirm` | string | Yes | Must match password | **Workflow:** ``` 1. Query User by reset_token 2. Validate token exists 3. Validate password strength 4. Hash new password 5. Update user.password_hash 6. Clear reset_token 7. Redirect to /login ``` **Success Response:** ```http HTTP/1.1 302 Found Location: /login Flash: "Hasło zmienione pomyślnie" ``` **Error Response:** ```http HTTP/1.1 200 OK Flash: "Nieprawidłowy lub wygasły token resetowania" ``` --- ## User Endpoints ### User Dashboard ```http GET /dashboard ``` **Description:** Personalized user dashboard **Authentication:** Required **Features:** - User profile summary - Recent activity - Quick actions (AI chat, forum, messages) - Company profile link (if associated) **Response:** - HTML page with dashboard **Example:** ```bash curl https://nordabiznes.pl/dashboard \ -H "Cookie: session=..." ``` --- ### AI Chat Interface ```http GET /chat ``` **Description:** AI-powered company directory assistant **Authentication:** Required **Features:** - Start new conversation - View conversation history - Interactive chat interface - Cost tracking display **Response:** - HTML page with chat UI **Example:** ```bash curl https://nordabiznes.pl/chat \ -H "Cookie: session=..." ``` --- ### Forum - Topic List ```http GET /forum ``` **Description:** Community forum topic listing **Authentication:** Required **Query Parameters:** | Parameter | Type | Description | Default | |-----------|------|-------------|---------| | `page` | integer | Page number | 1 | **Response:** - HTML page with forum topics - Pagination (20 topics per page) - Pinned topics at top - Topic metadata (author, replies, last post) --- ### Forum - Create Topic ```http POST /forum/nowy ``` **Description:** Create new forum topic **Authentication:** Required **Request Body (Form):** | Field | Type | Required | Max Length | Description | |-------|------|----------|------------|-------------| | `title` | string | Yes | 200 chars | Topic title | | `content` | string | Yes | 10,000 chars | Topic content (sanitized HTML) | **Input Sanitization:** - Strips dangerous HTML tags - Allows basic formatting (b, i, u, br, p) - Max length enforcement **Success Response:** ```http HTTP/1.1 302 Found Location: /forum/ Flash: "Temat utworzony pomyślnie" ``` --- ### Forum - View Topic ```http GET /forum/ ``` **Description:** View forum topic with replies **Authentication:** Required **Path Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `topic_id` | integer | Forum topic ID | **Response:** - Topic details (title, content, author, timestamp) - All replies (nested threading) - Reply form --- ### Forum - Reply to Topic ```http POST /forum//odpowiedz ``` **Description:** Add reply to forum topic **Authentication:** Required **Path Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `topic_id` | integer | Forum topic ID | **Request Body (Form):** | Field | Type | Required | Max Length | |-------|------|----------|------------| | `content` | string | Yes | 10,000 chars | **Error Handling:** - 404 if topic not found - 403 if topic is locked - Input sanitization applied **Success Response:** ```http HTTP/1.1 302 Found Location: /forum/ Flash: "Odpowiedź dodana" ``` --- ### Calendar - Event List ```http GET /kalendarz ``` **Description:** Association event calendar **Authentication:** Required **Response:** - HTML page with upcoming events - Event details (date, time, location, description) - RSVP status for current user --- ### Calendar - RSVP ```http POST /kalendarz//rsvp ``` **Description:** RSVP to event (attend/cancel) **Authentication:** Required **Path Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `event_id` | integer | Event ID | **Request Body (Form):** | Field | Type | Values | Description | |-------|------|--------|-------------| | `status` | string | `attending`, `not_attending` | RSVP status | **Workflow:** ``` 1. Check if EventAttendee exists for user+event 2. If exists: Update status 3. If not: Create EventAttendee record 4. Redirect to /kalendarz/ ``` **Response:** ```http HTTP/1.1 302 Found Location: /kalendarz/ Flash: "RSVP zaktualizowane" ``` --- ### Messages - Inbox ```http GET /wiadomosci ``` **Description:** Private message inbox **Authentication:** Required **Response:** - HTML page with received messages - Unread count badge - Message preview (sender, subject, timestamp) - Pagination (20 messages per page) --- ### Messages - Sent ```http GET /wiadomosci/wyslane ``` **Description:** Sent messages folder **Authentication:** Required **Response:** - HTML page with sent messages - Recipient, subject, timestamp - Pagination (20 messages per page) --- ### Messages - New Message ```http GET /wiadomosci/nowa ``` **Description:** Compose new message form **Authentication:** Required **Query Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `recipient` | integer | User ID to pre-fill recipient | | `reply_to` | integer | Message ID to reply to | **Response:** - HTML form with recipient selector, subject, content --- ### Messages - Send ```http POST /wiadomosci/wyslij ``` **Description:** Send private message to another user **Authentication:** Required **Request Body (Form):** | Field | Type | Required | Max Length | |-------|------|----------|------------| | `recipient_id` | integer | Yes | - | | `subject` | string | Yes | 200 chars | | `content` | string | Yes | 10,000 chars | **Validation:** - Recipient must exist and be active - Cannot send to self - Content sanitization **Success Response:** ```http HTTP/1.1 302 Found Location: /wiadomosci/wyslane Flash: "Wiadomość wysłana" ``` --- ### Messages - View ```http GET /wiadomosci/ ``` **Description:** View message details **Authentication:** Required **Path Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `message_id` | integer | Message ID | **Authorization:** - User must be sender or recipient - Returns 403 otherwise **Side Effects:** - Marks message as read (if current user is recipient) **Response:** - HTML page with full message content - Reply button --- ### Classifieds - List ```http GET /tablica ``` **Description:** Community classifieds board **Authentication:** Required **Response:** - HTML page with active classifieds - Filter by status (active, completed) - Pagination (20 classifieds per page) --- ### Classifieds - Post New ```http POST /tablica/nowe ``` **Description:** Post new classified ad **Authentication:** Required **Request Body (Form):** | Field | Type | Required | Max Length | |-------|------|----------|------------| | `title` | string | Yes | 200 chars | | `description` | string | Yes | 10,000 chars | | `price` | decimal | No | - | | `category` | string | Yes | Enum | **Categories:** `offer`, `request`, `event`, `other` **Success Response:** ```http HTTP/1.1 302 Found Location: /tablica/ Flash: "Ogłoszenie dodane" ``` --- ### Classifieds - Mark Completed ```http POST /tablica//zakoncz ``` **Description:** Mark classified as completed/closed **Authentication:** Required (must be author) **Path Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `classified_id` | integer | Classified ID | **Authorization:** - Only author can mark as completed **Response:** ```http HTTP/1.1 302 Found Location: /tablica/ Flash: "Ogłoszenie zakończone" ``` --- ## API Endpoints (JSON) ### Companies List (JSON Export) ```http GET /api/companies ``` **Description:** Export all companies as JSON **Authentication:** None (public API) **Query Parameters:** | Parameter | Type | Description | Default | |-----------|------|-------------|---------| | `category` | integer | Filter by category ID | All | | `limit` | integer | Max companies to return | 100 | **Response:** ```json { "companies": [ { "id": 26, "name": "PIXLAB Sp. z o.o.", "slug": "pixlab-sp-z-o-o", "short_description": "Systemy informatyczne dla biznesu", "category": "IT", "nip": "5882436505", "regon": "221912907", "krs": "0000414920", "website_url": "https://pixlab.pl", "email": "kontakt@pixlab.pl", "phone": "58 621 30 00", "city": "Rumia", "services": ["Oprogramowanie", "Systemy IT", "Hosting"], "competencies": ["Python", "PostgreSQL", "DevOps"], "founded_year": 2011 }, // ... more companies ], "total": 80, "category": null, "timestamp": "2026-01-10T12:00:00Z" } ``` **Status Codes:** - `200 OK` - Success - `400 Bad Request` - Invalid parameters **Example:** ```bash curl https://nordabiznes.pl/api/companies curl "https://nordabiznes.pl/api/companies?category=1&limit=10" ``` --- ### Chat - Start Conversation ```http POST /api/chat/start ``` **Description:** Initialize new AI chat conversation **Authentication:** Required **Request Body:** ```json { "initial_message": "Szukam firm z obszaru IT" } ``` **Response:** ```json { "conversation_id": 123, "created_at": "2026-01-10T12:00:00Z", "message": "Witaj! W czym mogę pomóc?" } ``` **Status Codes:** - `201 Created` - Conversation started - `401 Unauthorized` - Not logged in - `429 Too Many Requests` - Rate limit exceeded **Example:** ```bash curl -X POST https://nordabiznes.pl/api/chat/start \ -H "Cookie: session=..." \ -H "Content-Type: application/json" \ -H "X-CSRFToken: ..." \ -d '{"initial_message": "Szukam firm IT"}' ``` --- ### Chat - Send Message ```http POST /api/chat//message ``` **Description:** Send message to AI chat **Authentication:** Required **Rate Limit:** 50 requests/hour **Path Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `conversation_id` | integer | Conversation ID | **Request Body:** ```json { "message": "Kto tworzy strony www?" } ``` **Response:** ```json { "response": "Znalazłem 5 firm zajmujących się tworzeniem stron www:\n\n1. **PIXLAB** - strony internetowe, sklepy online...", "companies_found": 5, "tokens_used": 12500, "estimated_cost_usd": 0.00094, "timestamp": "2026-01-10T12:00:05Z" } ``` **AI Chat Configuration:** - Model: gemini-2.5-flash - Max companies in context: 8 - History: Last 10 messages - Average latency: 250-350ms - Cost tracking: Automatic **Status Codes:** - `200 OK` - Success - `400 Bad Request` - Empty message - `401 Unauthorized` - Not logged in - `404 Not Found` - Conversation not found - `429 Too Many Requests` - Rate limit - `500 Internal Server Error` - AI API error **Example:** ```bash curl -X POST https://nordabiznes.pl/api/chat/123/message \ -H "Cookie: session=..." \ -H "Content-Type: application/json" \ -H "X-CSRFToken: ..." \ -d '{"message": "Kto tworzy strony www?"}' ``` --- ### Chat - Get History ```http GET /api/chat//history ``` **Description:** Retrieve conversation history **Authentication:** Required **Path Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `conversation_id` | integer | Conversation ID | **Response:** ```json { "conversation_id": 123, "created_at": "2026-01-10T12:00:00Z", "messages": [ { "id": 1, "sender": "user", "message": "Szukam firm IT", "timestamp": "2026-01-10T12:00:00Z" }, { "id": 2, "sender": "ai", "message": "Znalazłem 12 firm IT...", "timestamp": "2026-01-10T12:00:02Z", "tokens_used": 8500, "cost_usd": 0.00064 } ], "total_messages": 2, "total_cost_usd": 0.00064 } ``` **Example:** ```bash curl https://nordabiznes.pl/api/chat/123/history \ -H "Cookie: session=..." ``` --- ### Chat - Submit Feedback ```http POST /api/chat/feedback ``` **Description:** Submit feedback on AI response **Authentication:** Required **Request Body:** ```json { "message_id": 456, "thumbs_up": true, "thumbs_down": false, "comment": "Bardzo pomocna odpowiedź!" } ``` **Response:** ```json { "status": "success", "message": "Dziękujemy za feedback" } ``` --- ### Verify NIP ```http GET /api/verify-nip ``` **Description:** Verify Polish NIP (tax identification number) via ALEO.com **Authentication:** None **Query Parameters:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `nip` | string | Yes | 10-digit NIP (no hyphens) | **Response:** ```json { "valid": true, "nip": "5882436505", "company_name": "PIXLAB SPÓŁKA Z OGRANICZONĄ ODPOWIEDZIALNOŚCIĄ", "status": "active", "source": "aleo.com" } ``` **Invalid NIP:** ```json { "valid": false, "nip": "1234567890", "error": "NIP nie został znaleziony" } ``` **Example:** ```bash curl "https://nordabiznes.pl/api/verify-nip?nip=5882436505" ``` --- ### Check Email Availability ```http GET /api/check-email ``` **Description:** Check if email is available for registration **Authentication:** None **Query Parameters:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `email` | string | Yes | Email address to check | **Response:** ```json { "available": true, "email": "jan.kowalski@example.com" } ``` **Email Taken:** ```json { "available": false, "email": "existing@example.com" } ``` **Example:** ```bash curl "https://nordabiznes.pl/api/check-email?email=jan.kowalski@example.com" ``` --- ### Notifications - List ```http GET /api/notifications ``` **Description:** Get user notifications **Authentication:** Required **Query Parameters:** | Parameter | Type | Description | Default | |-----------|------|-------------|---------| | `unread_only` | boolean | Filter unread notifications | false | | `limit` | integer | Max notifications to return | 20 | **Response:** ```json { "notifications": [ { "id": 1, "type": "new_message", "message": "Nowa wiadomość od Jan Kowalski", "url": "/wiadomosci/123", "created_at": "2026-01-10T11:00:00Z", "read": false }, { "id": 2, "type": "new_recommendation", "message": "Nowa rekomendacja dla Twojej firmy", "url": "/admin/recommendations", "created_at": "2026-01-09T15:30:00Z", "read": true } ], "unread_count": 3, "total": 15 } ``` **Notification Types:** - `new_message` - New private message - `new_recommendation` - New recommendation received - `event_reminder` - Upcoming event reminder - `news_approved` - Company news approved (future) - `news_rejected` - Company news rejected (future) **Example:** ```bash curl "https://nordabiznes.pl/api/notifications?unread_only=true" \ -H "Cookie: session=..." ``` --- ### Notifications - Mark Read ```http POST /api/notifications//read ``` **Description:** Mark notification as read **Authentication:** Required **Path Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `notification_id` | integer | Notification ID | **Response:** ```json { "status": "success", "notification_id": 1, "read": true } ``` **Example:** ```bash curl -X POST https://nordabiznes.pl/api/notifications/1/read \ -H "Cookie: session=..." \ -H "X-CSRFToken: ..." ``` --- ### Notifications - Mark All Read ```http POST /api/notifications/read-all ``` **Description:** Mark all user notifications as read **Authentication:** Required **Response:** ```json { "status": "success", "marked_read": 5 } ``` **Example:** ```bash curl -X POST https://nordabiznes.pl/api/notifications/read-all \ -H "Cookie: session=..." \ -H "X-CSRFToken: ..." ``` --- ### Notifications - Unread Count ```http GET /api/notifications/unread-count ``` **Description:** Get count of unread notifications (for badge) **Authentication:** Required **Response:** ```json { "unread_count": 3 } ``` **Example:** ```bash curl https://nordabiznes.pl/api/notifications/unread-count \ -H "Cookie: session=..." ``` --- ### Messages - Unread Count ```http GET /api/messages/unread-count ``` **Description:** Get count of unread private messages **Authentication:** Required **Response:** ```json { "unread_count": 2 } ``` **Example:** ```bash curl https://nordabiznes.pl/api/messages/unread-count \ -H "Cookie: session=..." ``` --- ### Model Info (Gemini) ```http GET /api/model-info ``` **Description:** Get information about available AI models **Authentication:** None **Response:** ```json { "models": [ { "id": "flash", "name": "gemini-2.5-flash", "description": "Fast, cost-effective model for most tasks", "pricing": { "input_per_1m_tokens": 0.075, "output_per_1m_tokens": 0.30 }, "default": true }, { "id": "pro", "name": "gemini-2.5-pro", "description": "High-quality model for complex tasks", "pricing": { "input_per_1m_tokens": 1.25, "output_per_1m_tokens": 5.00 }, "default": false } ] } ``` --- ## Admin Endpoints ### Admin - Users ```http GET /admin/users ``` **Description:** User management dashboard **Authentication:** Admin only **Features:** - List all users - Search by email/name - Toggle admin status - Toggle active status - Delete users **Response:** - HTML page with user table --- ### Admin - Toggle Admin Status ```http POST /admin/users//toggle-admin ``` **Description:** Grant/revoke admin privileges **Authentication:** Admin only **Path Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `user_id` | integer | User ID | **Response:** ```http HTTP/1.1 302 Found Location: /admin/users Flash: "Status administratora zaktualizowany" ``` --- ### Admin - Recommendations ```http GET /admin/recommendations ``` **Description:** Moderation queue for company recommendations **Authentication:** Admin only **Query Parameters:** | Parameter | Type | Values | Default | |-----------|------|--------|---------| | `status` | string | `pending`, `approved`, `rejected` | `pending` | **Features:** - Filter by status - Approve/reject with reason - View recommendation details **Response:** - HTML page with recommendations table --- ### Admin - Approve Recommendation ```http POST /admin/recommendations//approve ``` **Description:** Approve pending recommendation **Authentication:** Admin only **Path Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `id` | integer | Recommendation ID | **Workflow:** ``` 1. Update status = 'approved' 2. Set moderated_by = current_user.id 3. Set moderated_at = now() 4. (Future) Send notification to recommender 5. Redirect to /admin/recommendations ``` **Response:** ```http HTTP/1.1 302 Found Location: /admin/recommendations Flash: "Rekomendacja zatwierdzona" ``` --- ### Admin - Reject Recommendation ```http POST /admin/recommendations//reject ``` **Description:** Reject pending recommendation **Authentication:** Admin only **Path Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `id` | integer | Recommendation ID | **Request Body (Form):** | Field | Type | Required | Description | |-------|------|----------|-------------| | `rejection_reason` | string | Yes | Reason for rejection | **Response:** ```http HTTP/1.1 302 Found Location: /admin/recommendations Flash: "Rekomendacja odrzucona" ``` --- ### Admin - Forum Moderation ```http GET /admin/forum ``` **Description:** Forum moderation dashboard **Authentication:** Admin only **Features:** - Pin/unpin topics - Lock/unlock topics - Delete topics - Delete replies - View all topics (including locked) --- ### Admin - Pin Topic ```http POST /admin/forum/topic//pin ``` **Description:** Pin topic to top of forum **Authentication:** Admin only **Response:** ```http HTTP/1.1 302 Found Location: /admin/forum Flash: "Temat przypięty" ``` --- ### Admin - Lock Topic ```http POST /admin/forum/topic//lock ``` **Description:** Lock topic (prevent new replies) **Authentication:** Admin only **Response:** ```http HTTP/1.1 302 Found Location: /admin/forum Flash: "Temat zablokowany" ``` --- ### Admin - Delete Topic ```http POST /admin/forum/topic//delete ``` **Description:** Delete forum topic and all replies **Authentication:** Admin only **Warning:** Permanent deletion, cascade deletes all replies **Response:** ```http HTTP/1.1 302 Found Location: /admin/forum Flash: "Temat usunięty" ``` --- ### Admin - Delete Reply ```http POST /admin/forum/reply//delete ``` **Description:** Delete individual forum reply **Authentication:** Admin only **Response:** ```http HTTP/1.1 302 Found Location: /forum/ Flash: "Odpowiedź usunięta" ``` --- ### Admin - Membership Fees ```http GET /admin/fees ``` **Description:** Membership fee management dashboard **Authentication:** Admin only **Features:** - View all fees by period - Filter by status (pending, paid, overdue) - Generate monthly fees - Mark fees as paid (single or bulk) - Export to Excel --- ### Admin - Generate Monthly Fees ```http POST /admin/fees/generate ``` **Description:** Generate membership fees for all companies for a given month **Authentication:** Admin only **Request Body (Form):** | Field | Type | Required | Description | |-------|------|----------|-------------| | `period_start` | date | Yes | Start of period (YYYY-MM-DD) | | `period_end` | date | Yes | End of period (YYYY-MM-DD) | **Workflow:** ``` 1. Get fee amount from membership_fee_config 2. Query all active companies 3. For each company: - Check if fee exists for period - If not: Create MembershipFee (status='pending') 4. Redirect to /admin/fees ``` **Response:** ```http HTTP/1.1 302 Found Location: /admin/fees Flash: "Wygenerowano składki dla 80 firm" ``` --- ### Admin - Mark Fee Paid ```http POST /admin/fees//mark-paid ``` **Description:** Mark single fee as paid **Authentication:** Admin only **Path Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `id` | integer | Fee ID | **Request Body (Form):** | Field | Type | Required | Description | |-------|------|----------|-------------| | `paid_at` | datetime | No | Payment date (default: now) | | `payment_method` | string | No | `przelew`, `gotówka`, `other` | **Response:** ```http HTTP/1.1 302 Found Location: /admin/fees Flash: "Składka oznaczona jako opłacona" ``` --- ### Admin - Bulk Mark Paid ```http POST /admin/fees/bulk-mark-paid ``` **Description:** Mark multiple fees as paid **Authentication:** Admin only **Request Body (Form):** | Field | Type | Required | Description | |-------|------|----------|-------------| | `fee_ids[]` | array[integer] | Yes | Array of fee IDs | | `payment_method` | string | No | Payment method | **Response:** ```http HTTP/1.1 302 Found Location: /admin/fees Flash: "Oznaczono 15 składek jako opłacone" ``` --- ### Admin - Export Fees ```http GET /admin/fees/export ``` **Description:** Export fees to Excel file **Authentication:** Admin only **Query Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `period_start` | date | Filter by period start | | `period_end` | date | Filter by period end | **Response:** ```http HTTP/1.1 200 OK Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet Content-Disposition: attachment; filename="skladki_2026-01.xlsx" [Excel file binary data] ``` --- ### Admin - Events ```http GET /admin/kalendarz ``` **Description:** Event management dashboard **Authentication:** Admin only **Features:** - List all events - Create new events - Edit events - Delete events - View RSVP list --- ### Admin - Create Event ```http POST /admin/kalendarz/nowy ``` **Description:** Create new association event **Authentication:** Admin only **Request Body (Form):** | Field | Type | Required | Description | |-------|------|----------|-------------| | `title` | string | Yes | Event title | | `description` | string | Yes | Event description | | `event_date` | datetime | Yes | Event date and time | | `location` | string | Yes | Event location | | `max_attendees` | integer | No | Maximum attendees (null = unlimited) | **Response:** ```http HTTP/1.1 302 Found Location: /admin/kalendarz Flash: "Wydarzenie utworzone" ``` --- ### Admin - Delete Event ```http POST /admin/kalendarz//delete ``` **Description:** Delete event **Authentication:** Admin only **Warning:** Cascade deletes all RSVPs **Response:** ```http HTTP/1.1 302 Found Location: /admin/kalendarz Flash: "Wydarzenie usunięte" ``` --- ### Admin - SEO Dashboard ```http GET /admin/seo ``` **Description:** SEO audit management dashboard **Authentication:** Admin only **Features:** - View all company SEO scores - Filter by score range - Sort by performance, SEO, accessibility - Trigger audits - View audit history **Response:** - HTML table with SEO metrics for all companies --- ### Admin - GBP Audit Dashboard ```http GET /admin/gbp-audit ``` **Description:** Google Business Profile audit dashboard **Authentication:** Admin only **Features:** - View GBP completeness scores - Identify missing profiles - Trigger audits - View AI recommendations --- ### Admin - Social Media Dashboard ```http GET /admin/social-media ``` **Description:** Social media profile audit dashboard **Authentication:** Admin only **Features:** - Platform coverage statistics - Missing profile identification - Profile verification status - Follower count tracking **Response:** - HTML dashboard with social media stats --- ### Admin - IT Audit Dashboard ```http GET /admin/it-audit ``` **Description:** IT infrastructure audit overview **Authentication:** Admin only **Features:** - IT maturity scores - Security score distribution - Collaboration readiness - Potential collaboration matches --- ### Admin - Chat Analytics ```http GET /admin/chat-analytics ``` **Description:** AI chat usage analytics **Authentication:** Admin only **Metrics:** - Total conversations - Total messages - Total API cost - Average cost per conversation - Most active users - Token usage trends **Response:** - HTML dashboard with charts and tables --- ### Admin - Debug Panel ```http GET /admin/debug ``` **Description:** Real-time application log viewer **Authentication:** Admin only **Features:** - Last 500 log entries - In-memory log buffer - Log level filtering - Auto-refresh **Response:** - HTML page with log entries --- ### Admin - Digital Maturity ```http GET /admin/digital-maturity ``` **Description:** Digital maturity overview for all companies **Authentication:** Admin only **Features:** - Maturity score distribution - Average scores by category - Companies needing improvement - Trends over time --- ### API - SEO Audit (List) ```http GET /api/seo/audit ``` **Description:** Get SEO audit results **Authentication:** Admin only **Query Parameters:** | Parameter | Type | Description | |-----------|------|----------| | `slug` | string | Filter by company slug | **Response:** ```json { "audits": [ { "company_slug": "pixlab-sp-z-o-o", "company_name": "PIXLAB Sp. z o.o.", "overall_score": 85, "seo_score": 90, "performance_score": 80, "accessibility_score": 88, "best_practices_score": 85, "audited_at": "2026-01-10T10:00:00Z" } ], "total": 45 } ``` --- ### API - SEO Audit (Get by Slug) ```http GET /api/seo/audit/ ``` **Description:** Get latest SEO audit for company **Authentication:** Admin only **Path Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `slug` | string | Company slug | **Response:** ```json { "company_slug": "pixlab-sp-z-o-o", "overall_score": 85, "seo_score": 90, "performance_score": 80, "accessibility_score": 88, "best_practices_score": 85, "pagespeed_seo_score": 92, "pagespeed_performance_score": 78, "on_page_seo_score": 88, "technical_seo_score": 90, "audited_at": "2026-01-10T10:00:00Z", "details": { "meta_tags": {...}, "headings": {...}, "images": {...}, "links": {...} } } ``` --- ### API - SEO Audit (Trigger) ```http POST /api/seo/audit ``` **Description:** Trigger SEO audit for company **Authentication:** Admin only **Rate Limit:** 10 requests/hour **Request Body:** ```json { "slug": "pixlab-sp-z-o-o" } ``` **Workflow:** ``` 1. Get company by slug 2. Extract website URL 3. Call Google PageSpeed Insights API (~20s) 4. Perform on-page SEO analysis 5. Perform technical SEO checks 6. Calculate composite score 7. Save to company_website_analysis table 8. Return results ``` **Response:** ```json { "status": "success", "company_slug": "pixlab-sp-z-o-o", "overall_score": 85, "audit_id": 123, "audited_at": "2026-01-10T10:00:22Z" } ``` **Error Response (Quota Exceeded):** ```json { "status": "error", "error": "PageSpeed API quota exceeded", "retry_after": 3600 } ``` **Status Codes:** - `200 OK` - Audit completed - `400 Bad Request` - Invalid slug or missing URL - `429 Too Many Requests` - Rate limit or API quota exceeded - `500 Internal Server Error` - API error --- ### API - GBP Audit (Trigger) ```http POST /api/gbp/audit ``` **Description:** Trigger Google Business Profile audit **Authentication:** Admin only **Rate Limit:** 10 requests/hour **Request Body:** ```json { "slug": "pixlab-sp-z-o-o" } ``` **Response:** ```json { "status": "success", "company_slug": "pixlab-sp-z-o-o", "completeness_score": 75, "recommendations": [ "Dodaj więcej zdjęć (obecnie: 5, zalecane: 10)", "Uzupełnij godziny otwarcia", "Dodaj opis działalności" ], "audited_at": "2026-01-10T10:00:15Z" } ``` --- ### API - GBP Audit (Get by Slug) ```http GET /api/gbp/audit/ ``` **Description:** Get latest GBP audit for company **Authentication:** Admin only **Response:** ```json { "company_slug": "pixlab-sp-z-o-o", "completeness_score": 75, "place_id": "ChIJ...", "has_name": true, "has_address": true, "has_phone": true, "has_website": true, "has_hours": false, "has_description": true, "photo_count": 5, "review_count": 12, "avg_rating": 4.5, "recommendations": [...], "audited_at": "2026-01-10T10:00:15Z" } ``` --- ### API - Social Media Audit ```http POST /api/social/audit ``` **Description:** Trigger social media profile audit **Authentication:** Admin only **Request Body:** ```json { "slug": "pixlab-sp-z-o-o" } ``` **Response:** ```json { "status": "success", "company_slug": "pixlab-sp-z-o-o", "platforms_found": { "facebook": true, "instagram": true, "linkedin": true, "youtube": false, "tiktok": false, "twitter": false }, "coverage": 50, "audited_at": "2026-01-10T10:00:30Z" } ``` --- ## Audit Endpoints ### SEO Audit Report (Public) ```http GET /audit/seo/ ``` **Description:** Public SEO audit report for company **Authentication:** None **Path Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `slug` | string | Company slug | **Response:** - HTML page with SEO audit results - Scores, recommendations, performance metrics - Public-facing (no admin controls) **Example:** ```bash curl https://nordabiznes.pl/audit/seo/pixlab-sp-z-o-o ``` --- ### Social Media Audit Report (Public) ```http GET /audit/social/ ``` **Description:** Public social media profile report **Authentication:** None **Response:** - HTML page showing social media presence - Platform coverage, verified profiles - Links to social media pages --- ### GBP Audit Report (Public) ```http GET /audit/gbp/ ``` **Description:** Public Google Business Profile audit **Authentication:** None **Response:** - HTML page with GBP completeness score - Missing fields - Public recommendations --- ### IT Audit Report (Public) ```http GET /audit/it/ ``` **Description:** Public IT infrastructure audit **Authentication:** None **Response:** - HTML page with IT maturity score - Security score (high-level) - Collaboration readiness - Potential collaboration matches (anonymized) --- ## Error Codes ### HTTP Status Codes | Code | Meaning | Usage | |------|---------|-------| | 200 OK | Success | Successful GET requests | | 201 Created | Resource created | POST /api/chat/start | | 302 Found | Redirect | After POST form submissions | | 400 Bad Request | Invalid input | Validation errors | | 401 Unauthorized | Not logged in | @login_required endpoints | | 403 Forbidden | Insufficient permissions | Admin-only or ownership check | | 404 Not Found | Resource not found | Invalid company slug, message ID | | 429 Too Many Requests | Rate limit exceeded | 5/hour on auth endpoints, 10/hour on audits | | 500 Internal Server Error | Server error | Unhandled exceptions, API failures | ### Error Response Format (JSON) ```json { "error": "Error message", "code": "ERROR_CODE", "details": {...} } ``` ### Common Error Responses **401 Unauthorized:** ```json { "error": "Authentication required", "code": "UNAUTHORIZED", "redirect": "/login" } ``` **403 Forbidden:** ```json { "error": "Insufficient permissions", "code": "FORBIDDEN" } ``` **404 Not Found:** ```json { "error": "Resource not found", "code": "NOT_FOUND", "resource": "company", "slug": "invalid-slug" } ``` **429 Rate Limit:** ```json { "error": "Rate limit exceeded", "code": "RATE_LIMIT_EXCEEDED", "retry_after": 3600, "limit": "50/hour" } ``` **500 Server Error:** ```json { "error": "Internal server error", "code": "INTERNAL_ERROR", "request_id": "abc123" } ``` --- ## Examples ### Complete AI Chat Flow ```bash # 1. Login curl -c cookies.txt -X POST https://nordabiznes.pl/login \ -d "email=test@nordabiznes.pl" \ -d "password=&Rc2LdbSw&jiGR0ek@Bz" # 2. Start conversation CONV_ID=$(curl -b cookies.txt -X POST https://nordabiznes.pl/api/chat/start \ -H "Content-Type: application/json" \ -H "X-CSRFToken: $(getCsrfToken)" \ -d '{"initial_message": "Szukam firm IT"}' | jq -r '.conversation_id') # 3. Send message curl -b cookies.txt -X POST https://nordabiznes.pl/api/chat/$CONV_ID/message \ -H "Content-Type: application/json" \ -H "X-CSRFToken: $(getCsrfToken)" \ -d '{"message": "Kto tworzy strony www?"}' | jq # 4. Get history curl -b cookies.txt https://nordabiznes.pl/api/chat/$CONV_ID/history | jq ``` --- ### Trigger SEO Audit ```bash # 1. Login as admin curl -c cookies.txt -X POST https://nordabiznes.pl/login \ -d "email=testadmin@nordabiznes.pl" \ -d "password=cSfQbbwegwv1v3Q2Dm0Q" # 2. Trigger audit curl -b cookies.txt -X POST https://nordabiznes.pl/api/seo/audit \ -H "Content-Type: application/json" \ -H "X-CSRFToken: $(getCsrfToken)" \ -d '{"slug": "pixlab-sp-z-o-o"}' | jq # 3. View public report curl https://nordabiznes.pl/audit/seo/pixlab-sp-z-o-o ``` --- ### Company Search with cURL ```bash # Search by keyword curl "https://nordabiznes.pl/search?q=strony+www" # Search by NIP curl "https://nordabiznes.pl/search?q=5882436505" # Search with category filter curl "https://nordabiznes.pl/search?q=budowa&category=2" ``` --- ### Export Companies JSON ```bash # All companies curl https://nordabiznes.pl/api/companies | jq # IT companies only curl "https://nordabiznes.pl/api/companies?category=1" | jq # Limit to 10 companies curl "https://nordabiznes.pl/api/companies?limit=10" | jq ``` --- ## Related Documentation - **[Flask Components](./04-flask-components.md)** - Application architecture and route organization - **[Authentication Flow](./flows/01-authentication-flow.md)** - Detailed authentication and session management - **[AI Chat Flow](./flows/03-ai-chat-flow.md)** - Complete AI chat implementation - **[SEO Audit Flow](./flows/04-seo-audit-flow.md)** - SEO audit workflow and API integration - **[Security Architecture](./09-security-architecture.md)** - Security controls and best practices --- ## Maintenance Notes ### When to Update This Document - **New endpoints added:** Add to appropriate section with full documentation - **Authentication changes:** Update authentication section and affected endpoints - **Rate limiting changes:** Update rate limiting section - **New query parameters:** Add to endpoint documentation - **Response format changes:** Update examples and schemas ### What NOT to Update - **Business logic details:** Keep this in flow documentation - **Database schema:** Keep in database schema documentation - **Infrastructure details:** Keep in deployment/network documentation ### Review Frequency - **Quarterly:** Review all endpoints for accuracy - **After major features:** Update immediately - **After API changes:** Update immediately --- ## Glossary | Term | Definition | |------|------------| | **Endpoint** | HTTP route that accepts requests and returns responses | | **Slug** | URL-friendly identifier (kebab-case, e.g., `pixlab-sp-z-o-o`) | | **CSRF** | Cross-Site Request Forgery protection | | **Rate Limiting** | Restriction on number of requests per time period | | **Flash Message** | One-time user notification message | | **Session** | Server-side user authentication state | | **JWT** | JSON Web Token (not used in this application) | | **API Key** | Authentication token for API access (not used in this application) | | **RBAC** | Role-Based Access Control | --- **Document Status:** Complete **Coverage:** 90+ endpoints documented **Completion Date:** 2026-01-10 **Next Review:** 2026-04-10 --- *This document provides a comprehensive reference for all HTTP endpoints in the Norda Biznes Hub application. It serves as the authoritative guide for developers, integrators, and system administrators working with the API.*