get/approvals
List Approvals
List approvals for the caller's tenant.
Runs a lazy expiry sweep before the query so stale rows are not
returned as ``pending``.
Parameters
status (query)session_id (query)limit (query)offset (query)authorization (header)
Responses
200Successful Response422Validation Error
get/approvals/{approval_id}
Get Approval
Return a single approval. 404 for cross-tenant or unknown ids.
Parameters
approval_id (path)requiredauthorization (header)
Responses
200Successful Response422Validation Error
post/approvals/{approval_id}/resolve
Resolve Approval Endpoint
Grant or reject a pending approval.
Errors:
* 404 ``approval_not_found`` — unknown id OR cross-tenant.
Deliberately 404 (not 403) to avoid leaking existence across
tenant boundaries.
* 409 ``approval_already_resolved`` — the row's status is no longer
``pending`` (already granted / rejected / expired).
Parameters
approval_id (path)requiredauthorization (header)
Request body
JSON body (application/json)
Responses
200Successful Response422Validation Error
get/audit
List Audit Events
List audit events for the caller's tenant, most-recent-first.
Gated behind :func:`require_agency_admin` — only users with role
``agency_admin`` may read their tenant's audit log. Tenant isolation
is enforced on every query by filtering on the principal's
``tenant_id`` — callers never pass a tenant id.
Parameters
actor_id (query)kind (query)from (query)to (query)limit (query)offset (query)authorization (header)
Responses
200Successful Response422Validation Error
get/audit/export.csv
Export Audit Csv
Stream an audit-log CSV for the caller's tenant.
Same filters as :func:`list_audit_events` (actor, kind, from, to) —
no pagination. Admin-only (``require_agency_admin``). Refuses to
export more than :data:`AUDIT_CSV_EXPORT_MAX_ROWS` rows; narrow the
filter range for bigger exports. Columns:
* ``created_at, kind, actor_kind, actor_id, actor_email, status,
summary, payload_json``
``payload_json`` is the same ``payload`` dict the JSON endpoint
returns, serialized with ``json.dumps(default=str)`` so Decimals
and datetimes survive the round-trip. Uses
:func:`csv.writer` — never hand-roll quoting.
Parameters
actor_id (query)kind (query)from (query)to (query)authorization (header)
Responses
200Successful Response422Validation Error
post/auth/accept-invite
Accept Invite
Consume an invite token, create the user, and sign them in.
Request body
JSON body (application/json)
Responses
201Successful Response422Validation Error
get/auth/api-keys
List Api Keys Route
List every API key in the caller's tenant.
Responses
200Successful Response422Validation Error
post/auth/api-keys
Create Api Key Route
Mint a new API key; the plaintext is returned exactly once.
Request body
JSON body (application/json)
Responses
201Successful Response422Validation Error
post/auth/api-keys/{key_id}/revoke
Revoke Api Key Route
Soft-revoke an API key. Tenant-scoped.
Parameters
key_id (path)requiredauthorization (header)
Responses
204Successful Response422Validation Error
get/auth/invites
List Invites
Parameters
status (query)authorization (header)
Responses
200Successful Response422Validation Error
post/auth/invites
Create Invite
Create a pending invite into the caller's tenant (admin-only).
Request body
JSON body (application/json)
Responses
201Successful Response422Validation Error
post/auth/invites/{invite_id}/revoke
Revoke Invite
Parameters
invite_id (path)requiredauthorization (header)
Responses
200Successful Response422Validation Error
get/auth/invites/lookup
Lookup Invite
Public — resolve an invite token to its safe public metadata.
Responses
200Successful Response422Validation Error
get/auth/me
Me
Return the authenticated user as :class:`PublicUser`.
Responses
200Successful Response422Validation Error
patch/auth/profile
Update Profile
Patch ``full_name`` / ``email`` on the authenticated user.
Request body
JSON body (application/json)
Responses
200Successful Response422Validation Error
post/auth/refresh
Refresh
Exchange a refresh token for a new (access, refresh) pair.
Request body
JSON body (application/json)
Responses
200Successful Response422Validation Error
post/auth/request-password-reset
Request Password Reset
Always 200 — do not leak whether the email is registered.
Request body
JSON body (application/json)
Responses
200Successful Response422Validation Error
post/auth/reset-password
Reset Password
Request body
JSON body (application/json)
Responses
200Successful Response422Validation Error
post/auth/send-verification-email
Send Verification Email
Issue a one-shot verification link for the authenticated user.
Responses
200Successful Response422Validation Error
post/auth/sign-in
Sign In
Verify credentials and issue a fresh token pair.
Request body
JSON body (application/json)
Responses
200Successful Response422Validation Error
post/auth/sign-in-totp
Sign In Totp
Second-step sign-in for users with 2FA enabled.
Request body
JSON body (application/json)
Responses
200Successful Response422Validation Error
post/auth/sign-out
Sign Out
Best-effort revoke the refresh token and the access ``jti``.
Request body
JSON body (application/json)
Responses
204Successful Response422Validation Error
post/auth/sign-up
Sign Up
Create a new tenant and its first owner user.
Request body
JSON body (application/json)
Responses
201Successful Response422Validation Error
post/auth/totp/disable
Totp Disable
Disable 2FA. Requires BOTH the user's password AND a TOTP code.
Request body
JSON body (application/json)
Responses
200Successful Response422Validation Error
post/auth/totp/setup
Totp Setup
Mint a fresh TOTP secret and return ``{secret, otpauth_url}``.
Responses
200Successful Response422Validation Error
post/auth/totp/verify
Totp Verify
Verify the first-time 6-digit code and flip ``totp_enabled=True``.
Request body
JSON body (application/json)
Responses
200Successful Response422Validation Error
post/auth/verify-email
Verify Email
Consume a verification token and mark the user verified.
Request body
JSON body (application/json)
Responses
200Successful Response422Validation Error
get/chat/sessions
List Sessions
List chat sessions for the current tenant, newest first.
Reads from the ``sessions`` SQL table rather than the in-memory
session_store so the sidebar stays populated across restarts.
Returns ``{id, title, created_at, message_count}`` for each row.
Responses
200Successful Response422Validation Error
post/chat/sessions
Create Session
Create a brand-new chat session owned by the authenticated tenant.
Request body
JSON body (application/json)
Responses
201Successful Response422Validation Error
delete/chat/sessions/{session_id}
Delete Session
Delete a chat session and cascade to messages + pending approvals.
Tenant-scoped: a session belonging to another tenant returns 404
(never 403 — we refuse to confirm existence across tenants). The
SQL FKs on ``messages.session_id`` and ``pending_approvals.session_id``
are both ``ON DELETE CASCADE``, so a single DELETE on ``sessions``
wipes the child rows in the same transaction.
We also best-effort-drop the row from the in-memory session_store
so subsequent ``/chat/sessions/{id}`` reads return 404 immediately
without waiting for the store's TTL.
Parameters
session_id (path)requiredauthorization (header)
Responses
204Successful Response422Validation Error
get/chat/sessions/{session_id}
Get Session
Return session metadata without message content (history can get big).
Returns ``404`` both when the session doesn't exist and when it
exists but belongs to a different tenant — we don't want to confirm
session existence across tenants.
Parameters
session_id (path)requiredauthorization (header)
Responses
200Successful Response422Validation Error
patch/chat/sessions/{session_id}
Rename Session
Update a session's title. Only ``title`` is mutable today.
Returns the updated :class:`SessionSummaryResponse` on success,
404 if the session doesn't exist or belongs to another tenant.
Parameters
session_id (path)requiredauthorization (header)
Request body
JSON body (application/json)
Responses
200Successful Response422Validation Error
post/chat/sessions/{session_id}/messages
Send Message
Stream an agent turn as SSE.
One ``event: agent_event`` frame per :class:`AgentEvent`; ``event:
heartbeat`` every 15 s of silence; a synthetic ``error``-kind frame on
any unexpected exception before the stream closes.
Each ``agent_event`` carries a monotonically-increasing ``id:``
field scoped to the session. On reconnect, clients send the last
seen id in the ``Last-Event-ID`` HTTP header; the endpoint replays
any buffered events with a higher id without re-running the
orchestrator. Heartbeats carry no ``id:``.
The replay buffer is in-memory per session and capped at
``SSE_REPLAY_BUFFER_CAP`` (200 by default).
Parameters
session_id (path)requiredauthorization (header)
Request body
JSON body (application/json)
Responses
200Successful Response422Validation Error
get/enquiries
List Enquiries
List enquiries for the caller's tenant, most-recent-first.
Advanced filters (all optional, all ANDed together):
* ``customer_email`` / ``destination`` / ``origin`` — case-insensitive
substring (ILIKE ``%term%``)
* ``depart_from`` / ``depart_to`` — inclusive date range on
``depart_date``
* ``created_from`` / ``created_to`` — inclusive date range on
``created_at``. ``created_to`` is widened to 23:59:59 UTC so a
single-day bracket matches rows created at any time that day.
422 is returned when either date range has ``from > to``.
Parameters
status (query)limit (query)offset (query)q (query)customer_email (query)destination (query)origin (query)depart_from (query)depart_to (query)created_from (query)created_to (query)authorization (header)
Responses
200Successful Response422Validation Error
post/enquiries
Create Enquiry
Log a new customer enquiry against the caller's tenant.
Request body
JSON body (application/json)
Responses
201Successful Response422Validation Error
get/enquiries/{enquiry_id}
Get Enquiry
Fetch a single enquiry. 404 on cross-tenant.
Parameters
enquiry_id (path)requiredauthorization (header)
Responses
200Successful Response422Validation Error
patch/enquiries/{enquiry_id}
Patch Enquiry
Partial update. Omitted fields are left alone; explicit nulls clear them.
Blocks re-opening a terminal enquiry (``cancelled`` / ``booked`` ->
``new`` / ``quoted``).
Parameters
enquiry_id (path)requiredauthorization (header)
Request body
JSON body (application/json)
Responses
200Successful Response422Validation Error
post/enquiries/{enquiry_id}/promote-to-session
Promote To Session
Attach a chat session to the enquiry. Idempotent.
If the enquiry already has a ``session_id``, the existing id is
returned without touching it — so retries / duplicate clicks never
mint extra sessions.
Parameters
enquiry_id (path)requiredauthorization (header)
Responses
200Successful Response422Validation Error
get/health
Health
get/health/db
Health Db
Probe the database with a ``SELECT 1``.
Returns 503 when no ``VOYAGENT_DB_URL`` is configured so probes
against a misconfigured deployment fail loudly.
get/health/redis
Health Redis
PING the Redis instance used by the offer cache.
get/internal/metrics
Metrics
get/reports/itinerary
Itinerary Report
Return the structured itinerary built so far for a single chat session.
404 is returned when the session doesn't exist OR belongs to a
different tenant — the two are deliberately indistinguishable to
avoid leaking existence across tenant boundaries.
Parameters
session_id (query)requiredauthorization (header)
Responses
200Successful Response422Validation Error
get/reports/payables
Payables Report
Aging of open vendor bills (airlines via BSP, hotels, visa agents).
Open = ``received`` or ``scheduled``. Same aging buckets as
receivables.
Parameters
from (query)requiredto (query)requiredauthorization (header)
Responses
200Successful Response422Validation Error
get/reports/receivables
Receivables Report
Aging of open customer invoices within ``[from, to]``.
Open = ``issued`` or ``partially_paid``. Void / draft / paid rows
are excluded. Amounts outstanding are bucketed by days past
``due_date`` relative to today.
Parameters
from (query)requiredto (query)requiredauthorization (header)
Responses
200Successful Response422Validation Error
get/reports/trial-balance
Trial Balance Report
GL trial balance for the caller's tenant.
Aggregates ``journal_entries`` grouped by ledger account, summing
debits and credits posted on or before ``as_of`` (UTC). Accounts
with no activity are omitted unless ``include_zero=1``.
Parameters
as_of (query)include_zero (query)authorization (header)
Responses
200Successful Response422Validation Error
get/schemas/money
Money Schema
Return the JSON Schema for the canonical Money type.
Used as a smoke test that the Pydantic -> OpenAPI -> TS contract pipeline
can see the canonical models.
get/tenant-settings
Get Tenant Settings
Return the current tenant's settings, creating defaults if absent.
Responses
200Successful Response422Validation Error
patch/tenant-settings
Patch Tenant Settings
Admin-only partial update. Omitted fields are left alone.
Request body
JSON body (application/json)
Responses
200Successful Response422Validation Error