The Formance Platform ships with an Audit Log feature that streams every administrative action and every API request against your stacks to a destination of your choice — typically a SIEM such as Splunk, Datadog, or Elastic, or any HTTPS endpoint that accepts JSON POSTs. This page describes what's captured, how forwarding works, and what the events look like on the wire.
To enable Audit Log forwarding, contact your account team.
What's captured#
Two independent streams are available, and you can subscribe to either or both.
Lifecycle events#
Administrative activity at the organisation and stack level: invitations, user and permission changes, stack create / update / delete / upgrade, region changes, module enable / disable, and similar.
Volume is proportional to administrative activity, not to your API traffic, so it tends to be light.
The following lifecycle event types are emitted:
| Scope | Event |
|---|---|
| organizations | organizations.created |
| organizations | organizations.updated |
| organizations | organizations.deleted |
| organizations | organizations.join |
| organizations | organizations.user.updated |
| organizations | organizations.user.deleted |
| organizations | organizations.application.enabled |
| organizations | organizations.application.disabled |
| organizations | organizations.auth-provider.configured |
| organizations | organizations.auth-provider.deleted |
| organizations | organizations.client.created |
| organizations | organizations.client.updated |
| organizations | organizations.client.deleted |
| organizations | organizations.feature.updated |
| organizations | organizations.feature.deleted |
| invitations | invitations.created |
| invitations | invitations.accepted |
| invitations | invitations.accepted-from-link |
| invitations | invitations.rejected |
| invitations | invitations.cancelled |
| regions | regions.created |
| regions | regions.deleted |
| regions | regions.allowed-access |
| regions | regions.denied-access |
| regions | regions.set_connected |
| regions | regions.set_disconnected |
| regions | regions.ping |
| users | users.created |
| users | users.deleted |
| users | users.join-domain |
| users | users.onboarded |
| stacks | stacks.created |
| stacks | stacks.updated |
| stacks | stacks.deleted |
| stacks | stacks.restored |
| stacks | stacks.enabled |
| stacks | stacks.disabled |
| stacks | stacks.upgraded |
| stacks | stacks.warned |
| stacks | stacks.pruned |
| stacks | stacks.disposal |
| stacks | stacks.disposal-reset |
| stacks | stacks.status.updated |
| stacks | stacks.status.deleted |
| stacks | stacks.reachness.updated |
| stacks | stacks.module.enabled |
| stacks | stacks.module.disabled |
| stacks | stacks.module.status.updated |
| stacks | stacks.module.status.deleted |
| stacks | stacks.stargate.enabled |
| stacks | stacks.stargate.disabled |
| stacks | stacks.user.updated |
| stacks | stacks.user.deleted |
| stacks | stacks.versions.upserted |
| stacks | stacks.versions.deleted |
Module HTTP audit#
Every non-streaming API call against your module services (ledger, payments, wallets, reconciliation, orchestration, and so on). Each event captures:
| Field | Description | Path in payload |
|---|---|---|
Event ID | A unique identifier for the event | id |
Trace ID | The distributed trace ID for the request | trace_id |
Actor claims | OIDC claims resolved from the JWT used for the call | actor.claims |
Token validation error | Set when the JWT could not be validated | actor.token_validation_error |
Organisation | The organisation the call was made against | actor.organization_id |
Stack | The stack the call was made against | actor.stack_id |
IP address | The originating IP address | actor.ip_address |
HTTP method | The request method (GET, POST, etc.) | http.request.method |
Path | The API endpoint accessed | http.request.path |
Host | The request host | http.request.host |
Request headers | Headers included in the request | http.request.header |
Request body | The request body | http.request.body |
Status code | The HTTP response status code | http.response.status_code |
Response headers | Headers included in the response | http.response.headers |
Response body | The response body | http.response.body |
Volume scales one-to-one with your API call rate.
How forwarding works#
The Formance Platform pushes events to an HTTPS endpoint you provide. There is no queue for you to subscribe to and no agent to deploy — events arrive at your endpoint as POST requests as they happen, with the headers you've configured.
To set up forwarding, share with your account team:
- The HTTPS URL where events should be delivered.
- Any authentication headers to include on the POST. For Splunk HEC this is typically
Authorization: Splunk <hec-token>. - Which stream(s) you want — lifecycle, module, or both.
- The stacks the integration should cover, if you have multiple.
- Whether you want events batched. By default each event is delivered as its own POST; we can batch into bulk POSTs on request.
Provisioning is operated by Formance.
Event format#
Both streams share a common envelope:
{
"date": "<ISO 8601 UTC timestamp>",
"app": "gateway | membership",
"version": "v2",
"type": "<event type>",
"payload": { ... }
}The shape of payload depends on the stream and event type.
Module audit event#
{
"date": "2026-04-30T09:23:41.123Z",
"app": "gateway",
"version": "v2",
"type": "AUDIT",
"payload": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
"actor": {
"claims": { "sub": "...", "email": "...", "iss": "..." },
"organization_id": "...",
"stack_id": "...",
"ip_address": "..."
},
"http": {
"request": {
"method": "POST",
"path": "/api/ledger/v2/main/transactions",
"host": "...",
"header": { "Content-Type": ["application/json"] },
"body": "..."
},
"response": {
"status_code": 200,
"headers": { "Content-Type": ["application/json"] },
"body": "..."
}
}
}
}When the request's JWT can't be validated, actor.claims is omitted and actor.token_validation_error carries the reason instead.
Lifecycle event#
Example — a stack creation:
{
"date": "2026-04-30T09:15:00Z",
"app": "membership",
"version": "v2",
"type": "stacks.created",
"payload": {
"ownerId": "...",
"organizationId": "...",
"subject": "...",
"data": {
"id": "...",
"name": "prod-stack",
"regionId": "..."
}
}
}The structure of payload.data varies by event type. The full list of lifecycle payload shapes is available on request from your account team.
What's stripped before delivery#
Module audit events are intentionally complete. Auditors and security teams need to be able to reconstruct exactly what happened, so headers and bodies are captured verbatim — with three exceptions, applied at the source:
- The
Authorizationrequest header is removed. - The response body for
/api/auth/oauth/tokenis cleared. - Request and response bodies for streaming endpoints (Content-Type
application/vnd.formance*-streamorapplication/octet-stream) are not captured.
Everything else is preserved as it appeared on the wire.
Module audit events will contain whatever your API calls contain. For ledger that means transaction amounts, account references, and any metadata you've attached. For payments, counterparty details and amounts. Once events are delivered to your SIEM, they live under your access controls, retention rules, and downstream pipelines — treat the audit stream as an extension of the data plane it observes, and apply the same data governance you'd apply to any system handling financial or personal information.
Delivery semantics#
Events are delivered at least once. If your endpoint returns a non-2xx response or is unreachable, the Formance Platform retries. During extended outages on your side, events may be queued internally up to a bounded retention window; sustained outages can cause queued events to be lost. Treat the destination receiver as durable storage rather than relying on the Formance Platform as a long-term buffer.
There is no API to replay historical events from before forwarding was set up. If you need historical data, request it during onboarding.
Inspection from the command line#
For ad-hoc inspection of lifecycle events, fctl provides direct access:
fctl cloud org history <organization-id>
fctl stack history <stack-id>This is intended for occasional review rather than continuous ingestion. For SIEM integration, use forwarding as described above.
FAQ#
Which destinations are supported? Any HTTPS endpoint that accepts JSON POSTs. Splunk HEC, Datadog, Elastic, and custom endpoints are all in production with existing customers.
Can we filter events at the source? Not today — the configured stream is forwarded in full. Filter at your destination.
Are the two streams delivered on the same connection? They share an envelope but come from independent forwarders. Distinguish on the app field (gateway for module audit, membership for lifecycle) and on type.
Is the schema stable? Yes. The current schema version is v2. Changes will be coordinated with your account team.
What's the latency from event to delivery? Module audit events are forwarded as the request completes. Lifecycle events are forwarded as they occur. End-to-end latency to your endpoint is typically sub-second under normal conditions.