nordabiz/docs/architecture/10-api-endpoints.md
Maciej Pienczyn fa4fb92390 docs: Add complete architecture documentation with C4 diagrams
- System Context diagram (C4 Level 1)
- Container diagram (C4 Level 2)
- Flask component diagram (C4 Level 3)
- Deployment architecture with NPM proxy
- Database schema (PostgreSQL)
- External integrations (Gemini AI, Brave Search, PageSpeed)
- Network topology (INPI infrastructure)
- Security architecture
- API endpoints reference
- Troubleshooting guide
- Data flow diagrams (auth, search, AI chat, SEO audit, news monitoring)

All diagrams use Mermaid.js and render automatically on GitHub.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 12:40:52 +01:00

51 KiB

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
  2. Authentication
  3. Rate Limiting
  4. CSRF Protection
  5. Response Formats
  6. Public Endpoints
  7. Authentication Endpoints
  8. User Endpoints
  9. API Endpoints (JSON)
  10. Admin Endpoints
  11. Audit Endpoints
  12. Error Codes
  13. 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/<slug>            # 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

# 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:

X-RateLimit-Limit: 50
X-RateLimit-Remaining: 47
X-RateLimit-Reset: 1704974400

Rate Limit Exceeded Response

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:

<form method="POST" action="/forum/nowy">
  {{ form.hidden_tag() }}  <!-- Includes CSRF token -->
  <!-- Form fields -->
</form>

CSRF Token in AJAX

For AJAX requests, include CSRF token in headers:

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:

<meta name="csrf-token" content="{{ csrf_token() }}">

JavaScript helper:

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:

GET /company/pixlab-sp-z-o-o
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8

<!DOCTYPE html>
<html>...

JSON Responses

API endpoints return JSON:

GET /api/companies
HTTP/1.1 200 OK
Content-Type: application/json

{
  "companies": [...],
  "total": 80
}

Flash Messages

User-facing messages use Flask flash messages:

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:

POST /login
HTTP/1.1 302 Found
Location: /dashboard

Public Endpoints

Homepage

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:

curl https://nordabiznes.pl/

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:

curl "https://nordabiznes.pl/search?q=strony+www&category=1"

Company Profile

GET /company/<slug>

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:

curl https://nordabiznes.pl/company/pixlab-sp-z-o-o

404 Response:

HTTP/1.1 404 Not Found
Content-Type: text/html

<h1>Firma nie została znaleziona</h1>

Health Check

GET /health

Description: System health check endpoint for monitoring

Authentication: None

Response:

{
  "status": "ok",
  "database": "connected",
  "timestamp": "2026-01-10T12:00:00Z"
}

Status Codes:

  • 200 OK - System healthy
  • 500 Internal Server Error - Database connection failed

Example:

curl https://nordabiznes.pl/health

Use Case: Zabbix monitoring, uptime checks


Company Events/News

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:

curl https://nordabiznes.pl/aktualnosci

Release Notes

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:

curl https://nordabiznes.pl/release-notes

Authentication Endpoints

User Registration

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/1.1 302 Found
Location: /login
Set-Cookie: session=...

Flash: "Konto utworzone. Sprawdź email w celu weryfikacji."

Error Response:

HTTP/1.1 200 OK
Content-Type: text/html

Flash: "Email już istnieje w systemie"

Example:

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

GET /verify-email/<token>

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/1.1 302 Found
Location: /login

Flash: "Email zweryfikowany pomyślnie!"

Error Response:

HTTP/1.1 302 Found
Location: /login

Flash: "Nieprawidłowy lub wygasły token weryfikacyjny"

Example:

curl https://nordabiznes.pl/verify-email/AbCdEf123456...

User Login

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/1.1 302 Found
Location: /dashboard
Set-Cookie: session=...; HttpOnly; Secure; SameSite=Lax

Error Response:

HTTP/1.1 200 OK
Content-Type: text/html

Flash: "Nieprawidłowy email lub hasło"

Example:

curl -X POST https://nordabiznes.pl/login \
  -d "email=jan.kowalski@example.com" \
  -d "password=SecurePass123" \
  -d "remember=on"

User Logout

GET /logout

Description: End user session

Authentication: Required

Response:

HTTP/1.1 302 Found
Location: /
Set-Cookie: session=; Expires=...

Flash: "Wylogowano pomyślnie"

Example:

curl https://nordabiznes.pl/logout \
  -H "Cookie: session=..."

Password Reset Request

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/<token>
5. Redirect to /login with flash message

Response:

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

POST /reset-password/<token>

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/1.1 302 Found
Location: /login

Flash: "Hasło zmienione pomyślnie"

Error Response:

HTTP/1.1 200 OK

Flash: "Nieprawidłowy lub wygasły token resetowania"

User Endpoints

User Dashboard

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:

curl https://nordabiznes.pl/dashboard \
  -H "Cookie: session=..."

AI Chat Interface

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:

curl https://nordabiznes.pl/chat \
  -H "Cookie: session=..."

Forum - Topic List

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

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/1.1 302 Found
Location: /forum/<topic_id>

Flash: "Temat utworzony pomyślnie"

Forum - View Topic

GET /forum/<topic_id>

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

POST /forum/<topic_id>/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/1.1 302 Found
Location: /forum/<topic_id>

Flash: "Odpowiedź dodana"

Calendar - Event List

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

POST /kalendarz/<event_id>/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/<event_id>

Response:

HTTP/1.1 302 Found
Location: /kalendarz/<event_id>

Flash: "RSVP zaktualizowane"

Messages - Inbox

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

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

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

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/1.1 302 Found
Location: /wiadomosci/wyslane

Flash: "Wiadomość wysłana"

Messages - View

GET /wiadomosci/<message_id>

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

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

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/1.1 302 Found
Location: /tablica/<classified_id>

Flash: "Ogłoszenie dodane"

Classifieds - Mark Completed

POST /tablica/<classified_id>/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/1.1 302 Found
Location: /tablica/<classified_id>

Flash: "Ogłoszenie zakończone"

API Endpoints (JSON)

Companies List (JSON Export)

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:

{
  "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:

curl https://nordabiznes.pl/api/companies
curl "https://nordabiznes.pl/api/companies?category=1&limit=10"

Chat - Start Conversation

POST /api/chat/start

Description: Initialize new AI chat conversation

Authentication: Required

Request Body:

{
  "initial_message": "Szukam firm z obszaru IT"
}

Response:

{
  "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:

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

POST /api/chat/<conversation_id>/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:

{
  "message": "Kto tworzy strony www?"
}

Response:

{
  "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:

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

GET /api/chat/<conversation_id>/history

Description: Retrieve conversation history

Authentication: Required

Path Parameters:

Parameter Type Description
conversation_id integer Conversation ID

Response:

{
  "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:

curl https://nordabiznes.pl/api/chat/123/history \
  -H "Cookie: session=..."

Chat - Submit Feedback

POST /api/chat/feedback

Description: Submit feedback on AI response

Authentication: Required

Request Body:

{
  "message_id": 456,
  "thumbs_up": true,
  "thumbs_down": false,
  "comment": "Bardzo pomocna odpowiedź!"
}

Response:

{
  "status": "success",
  "message": "Dziękujemy za feedback"
}

Verify NIP

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:

{
  "valid": true,
  "nip": "5882436505",
  "company_name": "PIXLAB SPÓŁKA Z OGRANICZONĄ ODPOWIEDZIALNOŚCIĄ",
  "status": "active",
  "source": "aleo.com"
}

Invalid NIP:

{
  "valid": false,
  "nip": "1234567890",
  "error": "NIP nie został znaleziony"
}

Example:

curl "https://nordabiznes.pl/api/verify-nip?nip=5882436505"

Check Email Availability

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:

{
  "available": true,
  "email": "jan.kowalski@example.com"
}

Email Taken:

{
  "available": false,
  "email": "existing@example.com"
}

Example:

curl "https://nordabiznes.pl/api/check-email?email=jan.kowalski@example.com"

Notifications - List

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:

{
  "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:

curl "https://nordabiznes.pl/api/notifications?unread_only=true" \
  -H "Cookie: session=..."

Notifications - Mark Read

POST /api/notifications/<notification_id>/read

Description: Mark notification as read

Authentication: Required

Path Parameters:

Parameter Type Description
notification_id integer Notification ID

Response:

{
  "status": "success",
  "notification_id": 1,
  "read": true
}

Example:

curl -X POST https://nordabiznes.pl/api/notifications/1/read \
  -H "Cookie: session=..." \
  -H "X-CSRFToken: ..."

Notifications - Mark All Read

POST /api/notifications/read-all

Description: Mark all user notifications as read

Authentication: Required

Response:

{
  "status": "success",
  "marked_read": 5
}

Example:

curl -X POST https://nordabiznes.pl/api/notifications/read-all \
  -H "Cookie: session=..." \
  -H "X-CSRFToken: ..."

Notifications - Unread Count

GET /api/notifications/unread-count

Description: Get count of unread notifications (for badge)

Authentication: Required

Response:

{
  "unread_count": 3
}

Example:

curl https://nordabiznes.pl/api/notifications/unread-count \
  -H "Cookie: session=..."

Messages - Unread Count

GET /api/messages/unread-count

Description: Get count of unread private messages

Authentication: Required

Response:

{
  "unread_count": 2
}

Example:

curl https://nordabiznes.pl/api/messages/unread-count \
  -H "Cookie: session=..."

Model Info (Gemini)

GET /api/model-info

Description: Get information about available AI models

Authentication: None

Response:

{
  "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

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

POST /admin/users/<user_id>/toggle-admin

Description: Grant/revoke admin privileges

Authentication: Admin only

Path Parameters:

Parameter Type Description
user_id integer User ID

Response:

HTTP/1.1 302 Found
Location: /admin/users

Flash: "Status administratora zaktualizowany"

Admin - Recommendations

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

POST /admin/recommendations/<id>/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/1.1 302 Found
Location: /admin/recommendations

Flash: "Rekomendacja zatwierdzona"

Admin - Reject Recommendation

POST /admin/recommendations/<id>/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/1.1 302 Found
Location: /admin/recommendations

Flash: "Rekomendacja odrzucona"

Admin - Forum Moderation

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

POST /admin/forum/topic/<id>/pin

Description: Pin topic to top of forum

Authentication: Admin only

Response:

HTTP/1.1 302 Found
Location: /admin/forum

Flash: "Temat przypięty"

Admin - Lock Topic

POST /admin/forum/topic/<id>/lock

Description: Lock topic (prevent new replies)

Authentication: Admin only

Response:

HTTP/1.1 302 Found
Location: /admin/forum

Flash: "Temat zablokowany"

Admin - Delete Topic

POST /admin/forum/topic/<id>/delete

Description: Delete forum topic and all replies

Authentication: Admin only

Warning: Permanent deletion, cascade deletes all replies

Response:

HTTP/1.1 302 Found
Location: /admin/forum

Flash: "Temat usunięty"

Admin - Delete Reply

POST /admin/forum/reply/<id>/delete

Description: Delete individual forum reply

Authentication: Admin only

Response:

HTTP/1.1 302 Found
Location: /forum/<topic_id>

Flash: "Odpowiedź usunięta"

Admin - Membership Fees

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

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/1.1 302 Found
Location: /admin/fees

Flash: "Wygenerowano składki dla 80 firm"

Admin - Mark Fee Paid

POST /admin/fees/<id>/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/1.1 302 Found
Location: /admin/fees

Flash: "Składka oznaczona jako opłacona"

Admin - Bulk Mark Paid

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/1.1 302 Found
Location: /admin/fees

Flash: "Oznaczono 15 składek jako opłacone"

Admin - Export Fees

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/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

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

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/1.1 302 Found
Location: /admin/kalendarz

Flash: "Wydarzenie utworzone"

Admin - Delete Event

POST /admin/kalendarz/<event_id>/delete

Description: Delete event

Authentication: Admin only

Warning: Cascade deletes all RSVPs

Response:

HTTP/1.1 302 Found
Location: /admin/kalendarz

Flash: "Wydarzenie usunięte"

Admin - SEO Dashboard

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

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

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

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

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

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

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)

GET /api/seo/audit

Description: Get SEO audit results

Authentication: Admin only

Query Parameters:

Parameter Type Description
slug string Filter by company slug

Response:

{
  "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)

GET /api/seo/audit/<slug>

Description: Get latest SEO audit for company

Authentication: Admin only

Path Parameters:

Parameter Type Description
slug string Company slug

Response:

{
  "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)

POST /api/seo/audit

Description: Trigger SEO audit for company

Authentication: Admin only

Rate Limit: 10 requests/hour

Request Body:

{
  "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:

{
  "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):

{
  "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)

POST /api/gbp/audit

Description: Trigger Google Business Profile audit

Authentication: Admin only

Rate Limit: 10 requests/hour

Request Body:

{
  "slug": "pixlab-sp-z-o-o"
}

Response:

{
  "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)

GET /api/gbp/audit/<slug>

Description: Get latest GBP audit for company

Authentication: Admin only

Response:

{
  "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

POST /api/social/audit

Description: Trigger social media profile audit

Authentication: Admin only

Request Body:

{
  "slug": "pixlab-sp-z-o-o"
}

Response:

{
  "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)

GET /audit/seo/<slug>

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:

curl https://nordabiznes.pl/audit/seo/pixlab-sp-z-o-o

Social Media Audit Report (Public)

GET /audit/social/<slug>

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)

GET /audit/gbp/<slug>

Description: Public Google Business Profile audit

Authentication: None

Response:

  • HTML page with GBP completeness score
  • Missing fields
  • Public recommendations

IT Audit Report (Public)

GET /audit/it/<slug>

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)

{
  "error": "Error message",
  "code": "ERROR_CODE",
  "details": {...}
}

Common Error Responses

401 Unauthorized:

{
  "error": "Authentication required",
  "code": "UNAUTHORIZED",
  "redirect": "/login"
}

403 Forbidden:

{
  "error": "Insufficient permissions",
  "code": "FORBIDDEN"
}

404 Not Found:

{
  "error": "Resource not found",
  "code": "NOT_FOUND",
  "resource": "company",
  "slug": "invalid-slug"
}

429 Rate Limit:

{
  "error": "Rate limit exceeded",
  "code": "RATE_LIMIT_EXCEEDED",
  "retry_after": 3600,
  "limit": "50/hour"
}

500 Server Error:

{
  "error": "Internal server error",
  "code": "INTERNAL_ERROR",
  "request_id": "abc123"
}

Examples

Complete AI Chat Flow

# 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

# 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

# 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

# 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


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.