Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
96 commits
Select commit Hold shift + click to select a range
ebc5be6
feat: cache invalidations
chronark Jul 21, 2025
57867a3
feat: add invalidation manager
Flo4604 Sep 26, 2025
8945cc6
Merge branch 'main' into 07-21-feat_cache_invalidations
Flo4604 Sep 26, 2025
53adcc1
Merge branch 'main' into 07-21-feat_cache_invalidations
Flo4604 Sep 26, 2025
94fd640
cleanup
Flo4604 Sep 29, 2025
66e4d92
fix: test
Flo4604 Sep 29, 2025
8e9bdef
fix: test
Flo4604 Sep 29, 2025
09c3ccb
fix: remove debugging
Flo4604 Sep 29, 2025
360e0be
Merge branch 'main' into 07-21-feat_cache_invalidations
Flo4604 Sep 30, 2025
d9f8d67
fix: go mod and unusued var
Flo4604 Sep 30, 2025
123636a
feat: basic analytics sql
Flo4604 Oct 1, 2025
adea30f
feat: openapi spec
Flo4604 Oct 1, 2025
d66e1a1
feat: docs
Flo4604 Oct 2, 2025
5257d84
fix: fmt
Flo4604 Oct 2, 2025
cf31846
feat: go mod cleanup
Flo4604 Oct 2, 2025
a8b1158
new query parser
Flo4604 Oct 2, 2025
82bffbe
remove api mdx
Flo4604 Oct 2, 2025
ff7814b
feat: too many changes
Flo4604 Oct 6, 2025
6319210
remove readme
Flo4604 Oct 6, 2025
37c298d
Merge branch 'main' into 07-21-feat_cache_invalidations
Flo4604 Oct 6, 2025
6f4d004
Merge branch '07-21-feat_cache_invalidations' into feat/analytics
Flo4604 Oct 6, 2025
272b32c
fix cache proto not generating
Flo4604 Oct 6, 2025
2969ebb
fix: dont log pw and decrpyt pw
Flo4604 Oct 6, 2025
5a6e0c4
fix: more tests and less logs
Flo4604 Oct 6, 2025
1aaf038
fix: add filters for api ids
Flo4604 Oct 6, 2025
4f5a6bb
fix: permissions for api analytics
Flo4604 Oct 6, 2025
c686139
chore: error docs
Flo4604 Oct 7, 2025
0ea4027
cleanup + cte handling
Flo4604 Oct 7, 2025
eadfb81
add tests + scoped keys
Flo4604 Oct 7, 2025
269e56f
fix: wrong cache workspace
Flo4604 Oct 7, 2025
d42d5aa
fix: use sha256
Flo4604 Oct 7, 2025
2685606
fmt and remove toUnixTimestamp64Milli
Flo4604 Oct 9, 2025
182a0aa
add dev command
Flo4604 Oct 9, 2025
d202637
add analytics dsn to tiltfile/compose
Flo4604 Oct 9, 2025
e915c1e
fix docker compose
Flo4604 Oct 9, 2025
a9d13fc
fix wording
Flo4604 Oct 9, 2025
748a478
remove virtual columns, result transformer and add externalId to clic…
Flo4604 Oct 10, 2025
9555c6a
refactor: rename keyAuth to keySpace and api_id to key_space_id
Flo4604 Oct 13, 2025
dc1fefc
cache staleness
Flo4604 Oct 13, 2025
5ecea3e
more changes
Flo4604 Oct 13, 2025
7167d2c
fix url
Flo4604 Oct 13, 2025
ac90686
some errors and doc fixes
Flo4604 Oct 13, 2025
4bd1c7a
more tests and docs
Flo4604 Oct 13, 2025
e45251e
docsss
Flo4604 Oct 14, 2025
30a9c57
docsss
Flo4604 Oct 14, 2025
97bd065
get rid of latency in docs
Flo4604 Oct 14, 2025
5e72c17
use empty array instead of null
Flo4604 Oct 14, 2025
8a24680
get rid of some examples
Flo4604 Oct 14, 2025
c27a1fc
small fix
Flo4604 Nov 3, 2025
53de9d1
[autofix.ci] apply automated fixes
autofix-ci[bot] Nov 3, 2025
db88452
Merge branch 'main' into feat/analytics
Flo4604 Nov 3, 2025
5fb3036
Merge branch 'feat/analytics' of github.com:unkeyed/unkey into feat/a…
Flo4604 Nov 3, 2025
cf4fcd0
Merge branch 'main' into 07-21-feat_cache_invalidations
Flo4604 Nov 3, 2025
e955e0a
Merge branch '07-21-feat_cache_invalidations' into feat/analytics
Flo4604 Nov 3, 2025
02fb3b9
Restore drizzle files to match main
Flo4604 Nov 3, 2025
c83b88e
chore: update pnpm-lock.yaml
Flo4604 Nov 3, 2025
e123d7b
feat: noop eventstreeam
Flo4604 Nov 3, 2025
9589bdf
feat: add debug enable/disable
Flo4604 Nov 3, 2025
3e29276
fix: tests
Flo4604 Nov 3, 2025
2fcc3fe
fix: cleanup abit
Flo4604 Nov 3, 2025
c0c5c52
fix: cleanup abit
Flo4604 Nov 3, 2025
1929ee3
fix: try to fix test maybe?
Flo4604 Nov 3, 2025
8219188
fix: try to fix test maybe?
Flo4604 Nov 3, 2025
cd406ad
fix: unique instanceIDs
Flo4604 Nov 3, 2025
a4dbfe1
fix: unique instanceIDs
Flo4604 Nov 3, 2025
00adfff
refactor: redo manager to dispatcher
Flo4604 Nov 3, 2025
76e5b00
Merge branch 'main' into 07-21-feat_cache_invalidations
Flo4604 Nov 3, 2025
616edd6
refactor: cleanup logger
Flo4604 Nov 3, 2025
e7239f9
Merge branch '07-21-feat_cache_invalidations' of github.com:unkeyed/u…
Flo4604 Nov 3, 2025
e46fa79
refactor: remove some logs
Flo4604 Nov 3, 2025
b2ee63e
refactor: try more sleep
Flo4604 Nov 3, 2025
71b07e5
refactor: remove sleep and just wait for the partition to be ready
Flo4604 Nov 3, 2025
6859704
refactor: use batch for broadcasting
Flo4604 Nov 3, 2025
68b282a
fix: tests
Flo4604 Nov 3, 2025
2b567b5
fix: downgrade aws/clickhouse pkg
Flo4604 Nov 4, 2025
0241db8
fix: downgrade aws/clickhouse pkg
Flo4604 Nov 4, 2025
58af255
chore: dont use max rows to read
Flo4604 Nov 4, 2025
7568a68
chore: cleanup code and add comment
Flo4604 Nov 4, 2025
4425186
Merge branch '07-21-feat_cache_invalidations' into feat/analytics
Flo4604 Nov 4, 2025
91954b6
Merge branch 'main' into feat/analytics
Flo4604 Nov 4, 2025
84401e9
chore: fix operationId and hide from sdk generation
Flo4604 Nov 4, 2025
95dbf04
[autofix.ci] apply automated fixes
autofix-ci[bot] Nov 4, 2025
7cf1e61
chore: fix wrong array struct
Flo4604 Nov 4, 2025
a4f6c12
Merge branch 'main' into feat/analytics
Flo4604 Nov 4, 2025
a5be3fb
chore: fix some wrong docs
Flo4604 Nov 4, 2025
f0cb0c6
fix: wait for kafka to be ready in ci
chronark Nov 4, 2025
b737958
fix: use bufstream
chronark Nov 4, 2025
b7ae657
chore: fix some nits and remove max_rows_read
Flo4604 Nov 4, 2025
ea94b68
chore: undo journal changes
Flo4604 Nov 4, 2025
77aa422
chore: add 503 response
Flo4604 Nov 4, 2025
8026fbc
fix: use host networking for some reason
chronark Nov 4, 2025
d77d3e2
Merge branch 'feat/analytics' of github.com:unkeyed/unkey into feat/a…
chronark Nov 4, 2025
0e773a7
chore: fix permission naming and some hoisting in test
Flo4604 Nov 4, 2025
972eb87
fix: some seed changes
Flo4604 Nov 4, 2025
fa2bf01
fix: hide analytics docs
Flo4604 Nov 4, 2025
1ca4e74
Merge branch 'main' into feat/analytics
chronark Nov 4, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { SettingCard } from "@unkey/ui";
import { CopyButton } from "@unkey/ui";

export const CopyKeySpaceId = ({ keySpaceId }: { keySpaceId: string }) => {
return (
<SettingCard
title={"KeySpace ID"}
description={<div className="max-w-[380px]">Identifier for the underlying keyspace.</div>}
border="bottom"
contentWidth="w-full lg:w-[420px] justify-end"
>
{/* TODO: make this a Code component in UI for CopyKeys with optional hidden button like in Code.*/}
<div className="flex flex-row justify-end items-center">
<div
className={
"flex flex-row justify-between min-w-[327px] pl-4 pr-2 py-1.5 bg-gray-2 dark:bg-black border rounded-lg border-grayA-5"
}
>
<div className="text-sm text-gray-11">{keySpaceId}</div>
<CopyButton value={keySpaceId} variant="ghost" toastMessage={keySpaceId} />
</div>
</div>
</SettingCard>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import { trpc } from "@/lib/trpc/client";
import { CopyApiId } from "./copy-api-id";
import { CopyKeySpaceId } from "./copy-key-space-id";
import { DefaultBytes } from "./default-bytes";
import { DefaultPrefix } from "./default-prefix";
import { DeleteApi } from "./delete-api";
Expand Down Expand Up @@ -62,6 +63,7 @@ export const SettingsClient = ({ apiId }: { apiId: string }) => {
<div>
<UpdateApiName api={api} />
<CopyApiId apiId={api.id} />
<CopyKeySpaceId keySpaceId={keyAuth.id} />
</div>
<div>
<DefaultBytes keyAuth={keyAuthForComponents} apiId={api.id} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ export const workspacePermissions = {
description: "Delete apis in this workspace.",
permission: "api.*.delete_api",
},
read_analytics: {
description: "Query analytics data for any API in this workspace using SQL.",
permission: "api.*.read_analytics",
},
},
Keys: {
verify_key: {
Expand Down Expand Up @@ -170,6 +174,10 @@ export function apiPermissions(apiId: string): {
description: "Update this API.",
permission: `api.${apiId}.update_api`,
},
read_analytics: {
description: "Query analytics data for this API using SQL.",
permission: `api.${apiId}.read_analytics`,
},
},
Keys: {
verify_key: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ export const workspacePermissions = {
description: "Delete apis in this workspace.",
permission: "api.*.delete_api",
},
read_analytics: {
description: "Query analytics data for all API's in this workspace using SQL.",
permission: "api.*.read_analytics",
},
},
Keys: {
verify_key: {
Expand Down Expand Up @@ -170,6 +174,10 @@ export function apiPermissions(apiId: string): {
description: "Update this API.",
permission: `api.${apiId}.update_api`,
},
read_analytics: {
description: "Query analytics data for this API using SQL.",
permission: `api.${apiId}.read_analytics`,
},
},
Keys: {
verify_key: {
Expand Down
171 changes: 171 additions & 0 deletions apps/docs/analytics/getting-started.mdx
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's worth to mention that queries supports CH functions and other utils of CH. I didn't see that so that might come in handy for users.

Copy link
Copy Markdown
Member Author

@Flo4604 Flo4604 Nov 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

analytics/schema-reference check that path

do you mean them? or where would you want to mention it instead?

Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
---
title: Getting Started
description: "Request access and run your first analytics query"
---

## Request Access

<Warning>
**Analytics is currently in private beta and available by request only.**
</Warning>

To get started:

1. **Find your workspace ID** in the Unkey dashboard settings
2. **Email us** at [support@unkey.dev](mailto:support@unkey.dev) with:
- Your workspace ID
- Your use case (billing, dashboards, reporting, etc.)
- Expected query volume

We'll enable analytics for your workspace and send you confirmation.

## Authentication

Analytics queries require a root key with analytics permissions. Create one in your dashboard:

1. Go to **Settings** → **Root Keys**
2. Click **Create New Root Key**
3. Select permissions: `api.*.read_analytics` OR `api.<api_id>.read_analytics`
4. Copy and securely store your root key

<Warning>
Root keys have powerful permissions. Store them securely and never commit them
to version control.
</Warning>

## Your First Query

Once you have access, execute your first analytics query using the `/v2/analytics.getVerifications` endpoint.

### Count Total Verifications

```sql
SELECT COUNT(*) as total
FROM key_verifications_v1
WHERE time >= now() - INTERVAL 7 DAY
```

Execute this query with curl:

```bash
curl -X POST https://api.unkey.com/v2/analytics.getVerifications \
-H "Authorization: Bearer <YOUR_ROOT_KEY>" \
-H "Content-Type: application/json" \
-d '{
"query": "SELECT COUNT(*) as total FROM key_verifications_v1 WHERE time >= now() - INTERVAL 7 DAY"
}'
```

### Break Down by Outcome

```sql
SELECT
outcome,
COUNT(*) as count
FROM key_verifications_v1
WHERE time >= now() - INTERVAL 24 HOUR
GROUP BY outcome
ORDER BY count DESC
```

Execute this query with curl:

```bash
curl -X POST https://api.unkey.com/v2/analytics.getVerifications \
-H "Authorization: Bearer <YOUR_ROOT_KEY>" \
-H "Content-Type: application/json" \
-d '{
"query": "SELECT outcome, COUNT(*) as count FROM key_verifications_v1 WHERE time >= now() - INTERVAL 24 HOUR GROUP BY outcome ORDER BY count DESC"
}'
```

### Top Users by Usage

```sql
SELECT
external_id,
SUM(count) as verifications
FROM key_verifications_per_day_v1
WHERE time >= now() - INTERVAL 30 DAY
GROUP BY external_id
ORDER BY verifications DESC
LIMIT 10
```

Execute this query with curl:

```bash
curl -X POST https://api.unkey.com/v2/analytics.getVerifications \
-H "Authorization: Bearer <YOUR_ROOT_KEY>" \
-H "Content-Type: application/json" \
-d '{
"query": "SELECT external_id, SUM(count) as verifications FROM key_verifications_per_day_v1 WHERE time >= now() - INTERVAL 30 DAY GROUP BY external_id ORDER BY verifications DESC LIMIT 10"
}'
```

<Tip>
**Performance tip:** For longer time ranges, use pre-aggregated tables instead of the raw table:
- `key_verifications_per_minute_v1` - For queries spanning hours
- `key_verifications_per_hour_v1` - For queries spanning days
- `key_verifications_per_day_v1` - For queries spanning weeks/months
- `key_verifications_per_month_v1` - For queries spanning years

Use `SUM(count)` instead of `COUNT(*)` with aggregated tables. They scan far fewer rows and are much faster.

</Tip>

<Tip>
Check out the [Query Examples](/analytics/query-examples) page for 30+
ready-to-use queries covering billing, monitoring, and analytics use cases.
</Tip>

## Understanding the Response

Analytics queries return data as an array of objects:

```json
{
"meta": {
"requestId": "req_xxx"
},
"data": [
{ "outcome": "VALID", "count": 1234 },
{ "outcome": "RATE_LIMITED", "count": 56 },
{ "outcome": "USAGE_EXCEEDED", "count": 12 }
]
}
```

Each object in the `data` array contains fields from your SELECT clause. The field names match the column names or aliases you specified in your query.

## Filtering by API or User

You can filter queries to specific APIs or users. Use `key_space_id` to filter by API (find this identifier in your API settings) and `external_id` to filter by user. These fields support standard SQL operators: `=`, `!=`, `IN`, `NOT IN`, `<`, `>`, etc.

<Note>
Queries are subject to resource limits (execution time, memory, result size,
and quota). See [Query Restrictions](/analytics/query-restrictions) for
complete details on limits and error codes.
</Note>

## Next Steps

<CardGroup cols={2}>
<Card title="Query Examples" icon="code" href="/analytics/query-examples">
Explore common SQL patterns for analytics and billing
</Card>
<Card
title="Schema Reference"
icon="table"
href="/analytics/schema-reference"
>
Browse available tables, columns, and data types
</Card>
<Card
title="Query Restrictions"
icon="shield-check"
href="/analytics/query-restrictions"
>
View limits, quotas, and permissions
</Card>
</CardGroup>
Loading
Loading