Skip to content

API Specification

This document is a human-readable summary of the API actually used by the dashboard/ frontend and implemented in api/ (Laravel).

For the authoritative machine spec, see api/storage/api-docs/api-docs.json (OpenAPI 3.0).

  • Base URL: the frontend is configured via dashboard/src/config and uses relative paths like /properties. In production this is typically served under /api.
  • “Resource” responses (most CRUD endpoints): Laravel API Resources return:
{ "data": <T>, "meta": { "...pagination"?: "..." }, "message"?: "..." }
  • Non-resource responses: some endpoints return a plain DTO (e.g. GET /ponto-settings, GET /matching-settings) or a bespoke shape (e.g. job status).
  • Bearer token: Authorization: Bearer <token> on all routes under auth:sanctum.
  • Obtain token: POST /auth/login with { email, password, device_name? }{ data: { user, token }, meta, message }.
    • Allowed roles: admin, agent only.
    • device_name: dashboard | mobile-pwa | default api (controls token naming and 90-day cleanup per device).
  • Current user: GET /auth/me
  • Logout: POST /auth/logout (revokes current token)
  • Profile: PUT /profile — update name, email, phone, password

Passwords are verified with bcrypt; failed login returns 422 with generic auth message.

Server-to-server (bank import / automation)

Section titled “Server-to-server (bank import / automation)”

Middleware auth.bank_import accepts:

  • Sanctum Bearer token (same as dashboard), or
  • X-API-Key header matching N8N_API_KEY in api/.env (binds to first DB user for authorization context)

Used for: bank transaction list/import, payment list/show, auto-match status, billing sync, AI batch job poll, job-status PATCH.

  • POST /auth/login: body { email, password, device_name? }
  • POST /auth/logout (Bearer)
  • GET /auth/me (Bearer)
  • PUT /profile (Bearer): update authenticated user profile

CRUD via Route::apiResource('users', ...) (Bearer). Dashboard shows Users menu for admin role only.

  • GET /users (paginated; filter search)
  • GET /users/{id}
  • POST /users — body includes role: admin | agent, password
  • PUT /users/{id}
  • DELETE /users/{id}

CRUD via Route::apiResource('properties', ...) (Bearer):

  • GET /properties (paginated)
  • GET /properties/{id}
  • POST /properties
  • PUT /properties/{id}
  • DELETE /properties/{id}

Property documents (building PDFs for RAG)

Section titled “Property documents (building PDFs for RAG)”

Nested under each property (Bearer):

  • GET /properties/{property}/property-documents
  • POST /properties/{property}/property-documents (multipart PDF)
  • GET /properties/{property}/property-documents/{id}
  • DELETE /properties/{property}/property-documents/{id}
  • GET /properties/{property}/property-documents/{id}/download
  • POST /properties/{property}/property-documents/{id}/index
  • POST /properties/{property}/property-documents/{id}/reindex

Categories: assurance | sinistre | entretien | autre. Indexed chunks are injected into AI chat when property_id is sent.

CRUD via Route::apiResource('units', ...) (Bearer):

  • GET /units (paginated; supports filtering by property_id)
  • GET /units/{id}
  • POST /units (JSON or multipart/form-data when uploading epb_certificate)
  • PUT /units/{id} (same)
  • DELETE /units/{id}

Signed download (no Bearer; URL from epb_certif_url on unit JSON):

  • GET /units/{unit}/epb-certificate

CRUD via Route::apiResource('tenants', ...) (Bearer):

  • GET /tenants (paginated; supports filtering by name)
  • GET /tenants/{id}
  • POST /tenants
  • PUT /tenants/{id}
  • DELETE /tenants/{id}

Notes:

  • Tenants store ibans: string[] (not a single iban) for matching.

CRUD via Route::apiResource('leases', ...) (Bearer):

  • GET /leases (paginated; supports filters like tenant_id, unit_id, status)
  • GET /leases/{id}
  • POST /leases (supports multipart/form-data for optional attachment)
  • PUT /leases/{id} (supports multipart/form-data for optional attachment)
  • DELETE /leases/{id}

Attachment download:

  • GET /leases/{lease}/attachment (signed URL middleware)
  • GET /leases/{lease}/rent-revisions
  • POST /leases/{lease}/rent-revisions{ data, updated_payments, new_rent_amount }
  • PUT /leases/{lease}/rent-revisions/{revision} (latest revision only)
  • DELETE /leases/{lease}/rent-revisions/{revision}{ deleted_revision_id, updated_payments, new_rent_amount }
  • GET /leases/{lease}/repayment-plan{ data: LeaseRepaymentPlan | null }
  • PUT /leases/{lease}/repayment-plan — body: { start_billing_period, extra_amount_per_month, total_debt_amount, note? }
  • DELETE /leases/{lease}/repayment-plan — cancels active plan, clears arrears_extra_amount on pending payments

CRUD via Route::apiResource('payments', ...) (Bearer) plus additional matching endpoints.

  • GET /payments (middleware auth.bank_import): paginated list; filter by lease_id
  • GET /payments/{id} (middleware auth.bank_import):
    • {id} may be a single id (12) or comma-separated ids (12,34,56)
    • response is always { data: Payment[], missing_ids: number[] }
  • POST /payments
  • PUT /payments/{id} (supports multipart/form-data via _method=PUT for cash_receipt)
  • DELETE /payments/{id}
  • POST /payments/{id}/confirm-cash — record cash received (cash_received_amount)
  • POST /payments/{id}/revert-cash — undo cash-only confirmation
  • POST /payments/sync-billing-periods-for-active-leases (bank import auth)
    • creates missing monthly payment rows for active leases
    • response: { created: number }
  • POST /payments/run-auto-match: hybrid (inline for small batches, queued for large)
    • response: { message, mode: "inline"|"queued", unmatched_count, full_matched, partial_linked }
  • GET /payments/auto-match-status (bank import auth)
    • response: { auto_match_enabled, last_run_at, last_full_matched, last_partial_linked }

Manual matching (Payment ↔ BankTransaction)

Section titled “Manual matching (Payment ↔ BankTransaction)”
  • GET /payments/{payment}/match-candidates
  • POST /payments/{payment}/match body { bank_transaction_id, match_type? }
  • POST /payments/{payment}/unmatch body { bank_transaction_id }

Bank transactions represent incoming credits only; debits/negative amounts are intentionally excluded.

  • GET /bank-transactions (bank import auth): list with filters:
    • bank_account_id, execution_date_from, execution_date_to, search, unmatched_only, exclude_ids[], include_linked_for_payment_id
  • GET /bank-transactions/unmatched: unmatched transactions whose best score is below a threshold
  • GET /bank-transactions/{id}
  • DELETE /bank-transactions/{id}
  • DELETE /bank-transactions: delete all
  • POST /bank-transactions/delete-bulk body { ids: number[] }
EndpointAuthPurpose
POST /bank-transactions/refreshSanctumPull new credits from Ponto; returns job_id for polling
POST /bank-transactions/importbank importBulk JSON array or { transactions: [...] }
POST /bank-transactionsSanctumSingle identity upsert (dedup by fingerprint / provider ids)
  • GET /bank-transactions/{id}/match-candidates: read-only scored candidates (leases/payments)
  • POST /bank-transactions/{id}/match: body { payment_id? } or { candidate_index? }
  • POST /bank-transactions/run-auto-match (bank import auth): run scoring-based auto-match across unmatched credits
  • POST /bank-transactions/ai-match: queues an AI batch match run (optional payment_ids scope)
  • GET /ai-match-batch-jobs/{job_id} (bank import auth): poll AI batch job status
  • PATCH /ai-match-batch-jobs/{job_id} (bank import auth): optional external job updates (merge)
  • POST /ai-match-batch-jobs/{job_id}/cancel (Sanctum): cancel queued/running batch
  • GET /job-status/{job_id}
  • PATCH /job-status/{job_id} (bank import auth)

Matching settings (rule-based + AI system message)

Section titled “Matching settings (rule-based + AI system message)”

Singleton config (Bearer). Managed in Settings → Matching / Assistant tabs. Global tuning — typically admin responsibility.

  • GET /matching-settings
  • PUT /matching-settings

Fields include:

  • tolerance & thresholds: rent_amount_tolerance, min_score_to_auto_match, min_score_partial_link, min_score_modal_list
  • score weights: score_iban, score_amount, score_late_window, score_billing_period, score_name_max
  • rule knobs: late_window_max_months, infer_month_from_message, tenant_name_min_similarity
  • IBAN exclusions: excluded_ibans, transaction_blacklisted_ibans
  • auto_match_enabled, plus last_auto_match_* telemetry
  • ai_chat_system_message — optional system prompt for the dashboard AI assistant (max 10 000 chars)

Singleton config (Bearer):

  • GET /ponto-settings
  • PUT /ponto-settings
    • client_secret can be cleared by sending null or "" (API returns client_secret_set: boolean)
    • selected_account_ids: string[] is the preferred multi-account selection
  • POST /ponto-settings/refresh-accounts: refresh cached accounts list

(Bearer — all authenticated users)

  • POST /ai/chat — NDJSON stream; body:
    • model (required), provider? (ollama | openai | gemini)
    • messages[]{ role: user|assistant|system, content }
    • property_id? — when set, injects top-5 RAG chunks from that building’s indexed PDFs
  • GET /ai/models{ default_provider, providers, availability }

Context assembly (server-side, in order): admin system messageportfolio entity snapshotBelgian legislation RAGproperty document RAG (if property_id).

Knowledge base documents (Belgian legislation RAG)

Section titled “Knowledge base documents (Belgian legislation RAG)”

(Bearer)

EndpointAdmin only
GET /knowledge-documentsno
GET /knowledge-documents/statsno
GET /knowledge-documents/{id}no
POST /knowledge-documents (multipart files[])yes
DELETE /knowledge-documents/{id}yes
POST /knowledge-documents/{id}/indexyes
POST /knowledge-documents/{id}/reindexyes

(Bearer — per authenticated user)

Built-in types:

  • GET /document-templates/{documentTypeId} — 404 until first save
  • PUT /document-templates/{documentTypeId} — upsert body_html + logo

Custom templates:

  • GET|POST /custom-templates
  • GET|PUT|DELETE /custom-templates/{id}

Types: indexation_letter, residential_lease_contract, notice_of_default (+ user-defined custom templates).

Singleton (Bearer): GET|PUT /communication-template-settings — plain-text email/WhatsApp bodies for rent revision sharing.