Ship logs to a generic HTTP endpoint
Use this guide to stream your Audit Logs to any destination that accepts JSON over HTTPS. Use this sink type when your SIEM or log management platform isn't covered by one of the other Log Sink providers.
Step 1: Prepare your endpoint
Expose an HTTPS endpoint that accepts POST requests with a JSON array body
and responds with a 2xx status code once the batch is durably accepted.
Respond 2xx only after persisting, since Firezone treats it as permission
to advance past those events. Optionally require a bearer token.
Your endpoint's behavior maps directly onto Firezone's retry semantics, shown in
the status code table below: return 5xx or 429 for transient conditions
you want retried, and reserve other 4xx responses for genuine
misconfiguration, since they disable the sink.
Step 2: Create the Log Sink in Firezone
In the admin portal, go to SettingsLog Sinks, click Add, and select HTTP. Configure the sink:
| Field | Description |
|---|---|
| Name | A name to identify this log sink. |
| Endpoint URL | The HTTPS URL to POST log batches to, for example https://logs.example.com/ingest. |
| Bearer Token | Optional. Sent as an Authorization: Bearer header on every request. Leave blank for none. |
| Batch Size | The maximum number of events per POST, between 1 and 1000. Requests stay under 512 KB regardless. Defaults to 100. |
The endpoint URL must be HTTPS, and hostnames that resolve to private or reserved IP addresses are rejected. Redirects are not followed.
Then choose which Log streams to deliver, all four of which are enabled by default, and optionally check Deliver existing logs to backfill logs recorded before the sink was created.
Deliver existing logs can only be selected when you create the sink. It can't be turned on later for an existing sink.
Click Create. Deliveries begin within about a minute.
Delivery format
Each request body is a JSON array of raw event objects, with no envelope.
Every event includes a type field and a unique log_id. A flow's start and
end events are never split across two requests, even at Batch Size 1.
[
{
"type": "change",
"log_id": "c0654d20aa1b4a0000000002",
"timestamp": "2026-07-14T09:26:31.114213Z",
"object": "policies",
"operation": "update",
"before": { "id": "0a83588e-…", "description": "Engineering → GitLab" },
"after": {
"id": "0a83588e-…",
"description": "Engineering → GitLab (MFA required)"
},
"subject": {
"actor_id": "e2f6a9c4-…",
"actor_name": "Jamie Admin",
"actor_email": "jamie@company.com",
"actor_type": "account_admin_user",
"auth_provider_id": "9d0c2f4e-…",
"ip": "203.0.113.44",
"ip_region": "US",
"ip_city": "San Francisco",
"ip_lat": 37.7749,
"ip_lon": -122.4194,
"user_agent": "Mozilla/5.0 …"
}
}
]
[
{
"type": "session",
"log_id": "53f2c8a91b7e4d20a6c19e04",
"timestamp": "2026-07-14T09:26:31.114213Z",
"context": "client",
"subject": {
"actor_id": "7a1d9e3c-…",
"actor_name": "Riley Engineer",
"actor_email": "riley@company.com",
"actor_type": "account_user",
"auth_provider_id": "9d0c2f4e-…",
"device_id": "2b4d6f8a-…",
"token_id": "c4e6a8b0-…",
"ip": "203.0.113.44",
"ip_region": "US",
"ip_city": "San Francisco",
"ip_lat": 37.7749,
"ip_lon": -122.4194,
"user_agent": "Firezone/1.4.0 (macOS 15.5; arm64)"
}
}
]
[
{
"type": "api_request",
"log_id": "a7b3e91c4d208f5a6e1b2c3d",
"timestamp": "2026-07-14T09:26:31.114213Z",
"actor_id": "b8e0c2a4-…",
"api_token_id": "d0f2a4c6-…",
"method": "GET",
"path": "/policies",
"content_length": 0,
"request_id": "F8nMlbf6MUyJZUUABBzB9-yT",
"user_agent": "terraform-provider-firezone/0.4.1",
"ip": "203.0.113.44",
"ip_region": "US",
"ip_city": "San Francisco",
"ip_lat": 37.7749,
"ip_lon": -122.4194
}
]
Each flow is delivered as two events sharing its log_id. The start event
is suffixed with -s and carries no counters, while the end event is
suffixed with -e and carries the final totals:
[
{
"type": "flow",
"log_id": "f4e8a2c61b09d735e2a48b17-e",
"timestamp": "2026-07-14T09:26:01.000000Z",
"flow_start": "2026-07-14T09:25:31.000000Z",
"flow_end": "2026-07-14T09:26:01.000000Z",
"last_packet": "2026-07-14T09:26:01.000000Z",
"device_id": "2b4d6f8a-…",
"role": "initiator",
"policy_authorization_id": "6c8e0a2b-…",
"policy_id": "0a83588e-…",
"resource_id": "5c8f6f6e-…",
"resource_name": "GitLab",
"resource_address": "gitlab.company.com",
"actor_id": "7a1d9e3c-…",
"actor_email": "riley@company.com",
"actor_name": "Riley Engineer",
"client_version": "1.4.0",
"device_os_name": "iOS",
"device_os_version": "17.4",
"protocol": "tcp",
"inner_src_ip": "100.64.0.1",
"inner_src_port": 54321,
"inner_dst_ip": "10.0.0.5",
"inner_dst_port": 443,
"outer_src_ip": "203.0.113.10",
"outer_src_port": 51820,
"outer_dst_ip": "198.51.100.5",
"outer_dst_port": 51820,
"domain": "gitlab.company.com",
"rx_packets": 100,
"tx_packets": 80,
"rx_bytes": 102400,
"tx_bytes": 20480
}
]
How Firezone responds to HTTP status codes
| Response | Behavior |
|---|---|
2xx | The batch is accepted and delivery advances. |
429, 5xx | Retried every minute. If failures persist for 24 hours, the sink is disabled and account admins are emailed. |
413 | The batch is split in half and retried to isolate an oversized event. |
400 | The batch is bisected to isolate the rejected event. Delivery for that stream pauses at it and Firezone engineers are notified automatically. No entries are skipped. |
Other 4xx | Treated as a configuration error, for example 401 for a wrong bearer token: the sink is disabled immediately. |
Duplicates and redelivery
Firezone guarantees at-least-once delivery, retrying any batch that isn't
acknowledged rather than risk losing data, so your endpoint can occasionally
receive the same event twice. When it does, deduplicate on log_id: flow
start and end events are already suffixed with -s and -e, which makes the
log_id a unique key across all streams. If your endpoint stores events keyed by
log_id, redelivered batches have no effect on your data.
Recovering a failed sink
A sink showing Warning is retrying automatically, so unless the failures continue there's nothing you need to do. A sink showing Error has stopped delivering and needs attention: fix the cause on your endpoint , commonly an expired TLS certificate or a rotated bearer token, then open the sink, click Edit, and click Save to re-enable it. Delivery picks up right where it left off, from the last acknowledged entry, and Deliver Now triggers a delivery immediately if you don't want to wait for the next cycle.
Deleting the sink deletes its delivery state along with it, so re-creating it starts fresh: the new sink delivers logs recorded from its creation onward, plus a full backfill if Deliver existing logs is selected, which re-sends events your endpoint may already have.
Need help? See all support options.