Every API here is available over the APIs.io API and to AI agents over MCP.
openapi: 3.2.0
info:
title: secureFlows Management Audit API
version: 1.8.0
description: 'REST API for **workspace owners/admins** and **end-user flows** (login, invites, anonymous, self-service).
Base path: **`/api/v1`**.
### Workspace access policies
Each workspace has two independent toggles (see **`Workspace`**, **`WorkspaceCreateRequest`**, **`WorkspaceUpdateRequest`**):
| Field | Default | Controls |
|-------|---------|----------|
| **`acceptAnonimous`** | `true` | Whether anonymous Firebase users may join via **`POST /users/anonymous`** or session create without a prior invite |
| **`enableSelfService`** | `true` | Whether end users may use the self-service dashboard (**`GET /users/memberships`**, **`POST /users/login`** as `USER`/`ANONIMOUS`, **`GET /sessions/my`**, **`POST /sessions/revoke/{sessionId}`**) |
When **`enableSelfService`** is `false`, those self-service routes return **403** for end-user roles; **`ADMIN`** and **`OWNER`** console login via **`POST /users/login`** is unchanged.
### Error responses
Failures return JSON **`{"status": , "error": ""}`** unless noted otherwise.
Billing plan limit failures use **HTTP 429** with stable **`code`** values
**`BILLING_SESSION_LIMIT`** or **`BILLING_PAYLOAD_LIMIT`** (see session create / payload write paths).
### Firebase email (console, audit, integrated app UI)
Verified Firebase ID tokens may include an **`email`** claim. The server **best-effort** persists it on
the workspace user row when missing — on **`POST /users/login`**, **`GET /users`** (current page),
**`GET /users/{id}`**, session create/get-or-create/renew, and **`GET /auth/callback`** (create/renew).
This does not change authentication; it helps **`GET /users`**, **`GET /audit/events`**, and
**`GET /audit/api-access`** return emails instead of Firebase UIDs where possible. Clients do not
send email in request bodies.
**Integrated apps:** after sign-in, call **`GET /sessions/identity`** with the session token to display
the signed-in user''s email in the app UI (e.g. next to **Sign out**) and to get a stable `userId` for
your own backend to correlate external events against. Response: `{ "userId": 42, "email": "…" }`,
either field may be `null`. Session **payload** remains on **`GET /sessions`** only.
### Paging and sorting
Paged list endpoints return **`{ items, page, pageSize, totalItems, totalPages }`**. Page size is
clamped to **`[1, 200]`**; query **`pageSize=0`** means default **20** (except where noted below).
| Endpoint | Search (`q`) | Sort (`sort` / `dir`) |
|----------|----------------|------------------------|
| `GET /users` | email, name, UID, role, status, id | `email`, `name`, `role`, `status`, `id` |
| `GET /audit/events` | — | fixed newest-first |
| `GET /audit/api-access` | — | fixed newest-first |
| `GET /sessions/admin` | id, user, app | session columns (see path) |
| `GET /sessions/my` | id, app, status | session columns (see path) |
| `GET /sessions/admin/analytics/concurrent-sessions` | — | fixed date ascending |
Admin session list page size is fixed by server config (`sessions.admin.page-size`, default **10**).
Export uses `sessions.export.page-size` (default **100**).
### Tokens
| Kind | Use |
|------|-----|
| **Firebase ID token** | `POST /workspaces` (create), `GET /users/memberships`, `POST /users/login`, `POST /users/from-invite`, `POST /users/anonymous`, `POST /sessions`, `POST /sessions/get-or-create`, `POST /sessions/renew/{sessionToken}` — header `Authorization: Bearer ` |
| **Internal USER JWT** | All other routes in this document — same header; issued by backend (`tokenType=USER`) |
### Related specs
- **Hosted login & application registration**: see **`/docs/openapi/integration-auth.yaml`**
- **AI session API only**: **`/docs/openapi/session/secure-flows-session-api.yaml`**
Tags marked **`human-only`** describe console-style usage (not the session-only AI surface).'
tags:
- name: Audit
description: Workspace audit trail and HTTP API access log (ADMIN/OWNER read)
paths:
/api/v1/audit/events:
get:
tags:
- Audit
summary: List workspace audit events (paged)
description: 'Append-only workspace audit trail for the authenticated workspace (**ADMIN** or **OWNER**).
Payload contents are never stored or returned.
**User / actor columns:** Prefer **`sessionUserEmail`** and **`actorEmail`** when the linked user
row has an email (populated from Firebase on login and session flows). When email is missing,
**`sessionUserIdentifier`** / **`actorProviderUid`** fall back to the Firebase UID.
**Billing limit enforcement:** When a plan cap blocks session create or payload write, the server
returns HTTP **429** to the client and may record a throttled audit row:
**`BILLING_SESSION_LIMIT_EXCEEDED`** or **`BILLING_PAYLOAD_LIMIT_EXCEEDED`** (at most one per
workspace and limit type per hour). Metadata includes `code`, `planLimit`, `observedValue`, and
optional `appId` — see **`metadataJson`** on the event view.'
security:
- userBearerAuth: []
parameters:
- name: page
in: query
required: false
schema:
type: integer
minimum: 0
default: 0
- name: pageSize
in: query
required: false
schema:
type: integer
minimum: 0
maximum: 200
default: 20
description: Default 20 when omitted; `0` is treated as 20. Max 200. Newest-first.
responses:
'200':
description: Paged audit events (newest first).
content:
application/json:
schema:
$ref: '#/components/schemas/WorkspaceAuditEventPageResponse'
'401':
description: Missing or invalid USER token
'403':
description: Requires workspace ADMIN or OWNER
operationId: getApiV1AuditEvents
x-operation-id-source: derived
/api/v1/audit/api-access:
get:
tags:
- Audit
summary: List workspace HTTP API access log (paged)
description: 'Paged HTTP access log for the workspace (**ADMIN** or **OWNER**). Method, path,
status, and timing only — no request/response bodies.
**User column:** Prefer **`userEmail`** when the session owner has an email on file (best-effort
from Firebase on login/session flows). When email is absent, **`userFirebaseId`** is returned so
clients can show the Firebase UID instead of a blank value. Rows with **`sessionId`** also enrich
identity from the live session row when the log row omitted **`userFirebaseId`**.'
security:
- userBearerAuth: []
parameters:
- name: page
in: query
required: false
schema:
type: integer
minimum: 0
default: 0
- name: pageSize
in: query
required: false
schema:
type: integer
minimum: 0
maximum: 200
default: 20
description: Default 20 when omitted; `0` is treated as 20. Max 200. Newest-first.
responses:
'200':
description: Paged API access log entries (newest first).
content:
application/json:
schema:
$ref: '#/components/schemas/ApiAccessLogPageResponse'
'401':
description: Missing or invalid USER token
'403':
description: Requires workspace ADMIN or OWNER
operationId: getApiV1AuditApiAccess
x-operation-id-source: derived
components:
schemas:
WorkspaceAuditEventPageResponse:
type: object
required:
- items
- page
- pageSize
- totalItems
- totalPages
properties:
items:
type: array
items:
$ref: '#/components/schemas/WorkspaceAuditEventView'
page:
type: integer
pageSize:
type: integer
totalItems:
type: integer
format: int64
totalPages:
type: integer
WorkspaceAuditEventView:
type: object
required:
- id
- eventType
- occurredAt
properties:
id:
type: integer
format: int64
eventType:
type: string
description: 'High-level category, e.g. `SESSION_CREATED`, `USER_CREATED`, `USER_LOGIN`, `SESSION_KEY_WRITTEN`,
`BILLING_PLAN_CHANGED`, `BILLING_OVERRIDE_UPDATED`, `BILLING_SESSION_LIMIT_EXCEEDED`,
`BILLING_PAYLOAD_LIMIT_EXCEEDED`, `BILLING_CANCEL_REQUESTED`, `BILLING_RESUME_REQUESTED`,
`BILLING_GRACE_LOCK_ENTERED`, `BILLING_GRACE_WIPE`.
'
example: SESSION_CREATED
occurredAt:
type: string
format: date-time
sessionRowId:
type: integer
format: int64
nullable: true
description: Internal session id when the event is session-scoped
sessionUserIdentifier:
type: string
nullable: true
description: Firebase UID for the session subject user (fallback when email is absent)
sessionUserEmail:
type: string
nullable: true
description: 'Email from the workspace user row when set (best-effort from Firebase on login/session flows).
Audit UIs should prefer this over `sessionUserIdentifier`.
'
sessionAppId:
type: string
nullable: true
sessionStatus:
type: string
nullable: true
description: active, expired, or revoked when session row exists
actorProviderUid:
type: string
nullable: true
actorDisplayName:
type: string
nullable: true
actorEmail:
type: string
nullable: true
description: Actor email when known; prefer over `actorProviderUid` in UIs
metadataJson:
type: string
nullable: true
description: 'Event-specific JSON metadata (no session payload). For billing limit events, includes
`code` (`BILLING_SESSION_LIMIT` or `BILLING_PAYLOAD_LIMIT`), `planLimit`, `observedValue`,
and optional `appId`.
'
example: '{"code":"BILLING_SESSION_LIMIT","planLimit":20,"observedValue":20,"appId":"acme-web"}'
ApiAccessLogPageResponse:
type: object
required:
- items
- page
- pageSize
- totalItems
- totalPages
properties:
items:
type: array
items:
$ref: '#/components/schemas/ApiAccessLogView'
page:
type: integer
pageSize:
type: integer
totalItems:
type: integer
format: int64
totalPages:
type: integer
ApiAccessLogView:
type: object
required:
- id
- occurredAt
- httpMethod
- endpoint
- httpStatus
- durationMs
properties:
id:
type: integer
format: int64
occurredAt:
type: string
format: date-time
httpMethod:
type: string
example: POST
endpoint:
type: string
example: /sessions/set/hello
httpStatus:
type: integer
example: 200
durationMs:
type: integer
format: int64
example: 18
sessionId:
type: integer
format: int64
nullable: true
tokenRevision:
type: integer
nullable: true
userFirebaseId:
type: string
nullable: true
description: Firebase UID when email is unavailable
userEmail:
type: string
nullable: true
description: Prefer for display; enriched from user row or session owner
applicationId:
type: string
nullable: true
description: Client app id when known
clientIp:
type: string
nullable: true
userAgent:
type: string
nullable: true
securitySchemes:
firebaseBearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
description: '**Firebase ID token** from the client SDK (`Authorization: Bearer <token>`).
When the token includes `email`, session endpoints best-effort persist it on the workspace end-user row
(audit display only; no extra request fields).
'
userBearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
description: '**Internal USER JWT** issued by this API (`tokenType=USER`). Contains workspace and role claims.
'
sessionBearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
description: 'Internal session JWT from `POST /api/v1/sessions` or `POST /api/v1/sessions/get-or-create`
(`tokenType=SESSION`; subject = session id).
'