Available on hosted gateways only.
☁️ Self-hosted deployments configure their own OTLP usage-export target directly.
See Kubernetes & Helm.
Supported protocols
Setup
From Org settings → Integrations (Infrastructure group), add one or more destinations. Pick a destination type, then fill in its fields.OTLP
S3
ClickHouse
The table must already exist before you add the destination: Edgee never creates or migrates
schema on your instance. Every row is sent via
INSERT ... FORMAT JSONEachRow, so the table needs a
column for each field Edgee sends, with matching names and compatible types.
Full table definition
Full table definition
ENGINE/PARTITION BY/ORDER BY clauses to your own retention and query patterns as
long as every column above stays present.
Usage logs are batched in memory per destination and flushed every 60 seconds, or sooner if the
buffer grows past 8 MB. Each flush is one write, not one per log record. For S3, that write is one
NDJSON object with one usage-log row per line; for ClickHouse, one INSERT ... FORMAT JSONEachRow
into your configured table.
S3 object keys are laid out as <prefix>/org=<your org id>/date=<yyyy-mm-dd>/hour=<HH>/<timestamp>.jsonl,
the partitioning layout most S3-based query engines (Athena, BigQuery external tables, DuckDB)
expect out of the box.
BigQuery
Usage rows are buffered in memory per destination and written through BigQuery streaming inserts
in batches, every 60 seconds or sooner once 5,000 rows accumulate. Each row carries the same
fields Edgee’s own ClickHouse pipeline stores. The destination table is created automatically on
first delivery if it does not exist yet, provided the service account has the BigQuery Table Data
Editor role; a pre-created table works too.
Snowflake
Before saving, generate a Programmatic Access Token for the user this destination writes as
(
ALTER USER <user> ADD PROGRAMMATIC ACCESS TOKEN <name>; the secret is shown once, so capture it
immediately), and attach a network policy to that user. Snowflake refuses PAT authentication
without one. A “Test Connection” that fails with Network policy is required is failing on this
check. An allow-all policy satisfies the requirement:
ALLOWED_IP_LIST once you know which source IPs the traffic should come from.
ALLOWED_IP_LIST takes IPv4 addresses only. If your account enforces IPv6, add the IPv6
counterpart with SET ALLOWED_IP6_LIST = ('::/0'). Both statements need a role holding the
network-policy privileges, typically ACCOUNTADMIN.
Rows are delivered over Snowflake’s SQL REST API rather than a driver connection, so there is no
username field: the token resolves its own user. If the target table doesn’t already exist, Edgee
creates it automatically the first time it flushes, with this shape: one typed column per
usage-log field, the same fields the OTLP and S3 destinations carry.
Snowflake DDL: the table Edgee auto-creates
Snowflake DDL: the table Edgee auto-creates
tags, compression_tool_stats, mcp_surface_stats, optimization_stats) are
VARIANT rather than a scalar type, since each holds a list or a nested object, the standard
Snowflake choice for that shape. Query them with
Snowflake’s dot/bracket notation
or FLATTEN, e.g. SELECT tags[0]::string FROM EDGEE_USAGE_LOGS_V1. Every other column is a plain
scalar you can SELECT directly, e.g. SELECT model, total_cost FROM EDGEE_USAGE_LOGS_V1.
The _V1 suffix is a schema version. It applies only to the auto-created default table, not to
the name you pass in the Table field. If a future change needs a different column shape, it
ships as EDGEE_USAGE_LOGS_V2 rather than altering _V1 in place, so existing rows and any views
or pipes built on top of this table keep working unchanged. If you’d rather pre-create the table
yourself (e.g. under a role that doesn’t grant CREATE TABLE), run the statement above against
your own table name and pass it in the Table field.
Usage logs are batched in memory per destination and flushed as one multi-row INSERT every 60
seconds, or sooner once 1,000 rows are buffered, delivered in chunks of 50 rows per statement.
Larger chunks would put too many bound parameters in a single statement, since every usage-log
field is its own column.
You can configure more than one destination. Each active one gets its own copy of the logs.
Test before saving
Run two checks against the form before saving:- Test Connection: checks reachability only (a TCP/TLS handshake for OTLP, a bucket
HEADfor S3, a ping for ClickHouse, a dataset lookup for BigQuery, aSELECT 1probe for Snowflake). - Send Log: sends one real log record through the full path, in the destination’s protocol, so you can confirm your endpoint or bucket actually receives it.
Security
Secret values (OTLP header values, an S3 destination’s secret access key, a ClickHouse destination’s password, a BigQuery destination’s service account key, a Snowflake destination’s Programmatic Access Token) are encrypted at rest and masked (********) on every read, the same
handling Edgee uses for BYOK provider keys. Plaintext values are never returned
once saved. Editing a destination requires re-entering its secret, since saving fully replaces the
stored config.
Delivery semantics
- Usage logs keep reaching Edgee’s internal pipeline unchanged, regardless of what you configure here.
- Forwarding is additive and asynchronous: a slow or failing destination never blocks or delays internal delivery, and never affects your other destinations.
- The feature is entirely opt-in: no destination configured means no behavior change and no extra calls.