Write events to your table. Send an array of objects; only name is required. Any field you send that is not a column is kept under attrs.
One endpoint, one key, one table. An agent can record what it did and ask questions about it in SQL, and the key it holds only ever sees its own rows. There is nothing to provision — write something, then ask about it.
Sign in at dogfood.olivercloud.ai. Your key is shown once, begins ohv_, and goes on every call:
Authorization: Bearer <your key>
Each key opens exactly one table and sees only the rows written with that key. Service is hosted in AWS us-west-2. Free and Pro plans; Pro is $19 a month, billed on our site.
A call without a key is refused rather than emptied:
HTTP 401
{"error":"missing_key",
"message":"send your key as Authorization: Bearer ohv_…"}
https://dogfood.olivercloud.ai
POST /mcp | Model Context Protocol |
POST /v1/events | write events |
POST /v1/query | SQL over your events |
GET /v1/schema | the table’s columns |
GET /v1/manifest | docs for your key |
GET /v1/me | plan and usage today |
Model Context Protocol over streamable HTTP, at https://dogfood.olivercloud.ai/mcp. Stateless — there is no session to open, every call carries the key. Three tools, each the same audited path as the HTTP doors, so an agent gets identical answers whichever it uses.
Write events to your table. Send an array of objects; only name is required. Any field you send that is not a column is kept under attrs.
Run one SQL statement over your rows in the events table. Scoped to you automatically — never filter on user_id.
Returns the manifest for this key: the table, how to write, how to ask, the plan and today’s usage. Read it once at the start.
{
"mcpServers": {
"oliver": {
"url": "https://dogfood.olivercloud.ai/mcp",
"headers": {
"Authorization": "Bearer <your key>"
}
}
}
}
claude mcp add --transport http oliver \
https://dogfood.olivercloud.ai/mcp \
--header "Authorization: Bearer <your key>"
codex mcp add oliver \
--url https://dogfood.olivercloud.ai/mcp \
--bearer-token-env-var OLIVER_KEY
Use the key-in-path form and select no authentication:
https://dogfood.olivercloud.ai/mcp/<your key>
One object or an array, up to 10,000 events per request. Only name is required. ts is optional — send seconds, milliseconds or RFC 3339, or leave it out and the service stamps the current time.
curl -X POST https://dogfood.olivercloud.ai/v1/events \
-H "Authorization: Bearer <your key>" \
-H "content-type: application/json" \
-d '[
{"name":"signup","app":"myapp","plan":"pro"},
{"kind":"metric","name":"latency_ms","value":182.5,
"app":"myapp","attrs":{"route":"/checkout"}},
{"kind":"note","name":"decision",
"text":"We chose Postgres for billing.",
"session_id":"chat-42"}
]'
Answer: {"ingested": 3, "dropped": []}. Rows land within a few seconds.
Send {"sql": "..."}. The table is events. No time filter means your plan’s whole window.
-- what happened in the last 24 hours, by name
SELECT name, count(*) FROM events
WHERE ts >= now() - interval '24 hours'
GROUP BY name ORDER BY count(*) DESC LIMIT 20
-- a value summed by one of your own properties, per day
SELECT time_bucket('1d', ts), attrs.region, sum(value)
FROM events WHERE kind = 'metric' AND name = 'order_total'
GROUP BY time_bucket('1d', ts), attrs.region
-- range of a metric over a window
SELECT min(value), max(value), avg(value)
FROM events
WHERE name = 'latency_ms' AND ts >= '2026-09-01'
Aggregates count(*), sum(value), avg, min, max, and percentile_cont(0.95) WITHIN GROUP (ORDER BY value) for p50/p95/p99. Time buckets with time_bucket('1h', ts) — '5m', '1h', '1d', or a number of seconds. Full text with text_match(text, 'word'). Your own fields with attrs.key = 'value'. Date literals like ts >= '2026-09-01'. Rows are immutable: there is no update or delete, you write a new row.
Everything lands in events. Anything you send that is not a column below is kept under attrs and stays queryable as attrs.key, so you never migrate a schema to record something new.
| column | meaning |
|---|---|
name | what happened, or the metric or note name — the only required field |
ts | when; send it or let the service stamp now |
kind | event · metric · log · note · trace |
app | your project or app slug |
session_id | thread, request or conversation id, for replay |
level | debug · info · warn · error |
value | a number: metric value, duration, score |
text | message, note body, memory — full-text searchable |
attrs | any other field you send, queryable as attrs.key |
| Free | Pro — $19/month | |
|---|---|---|
| rows written per day | 10,000 | 1,000,000 |
| queries per day | 1,000 | 20,000 |
| results read per day | 1 GB | 50 GB |
| concurrent queries | 2 | 4 |
| query window | 7 days | 90 days |
| groups per answer | 1,000 | 5,000 |
| seconds per query | 10 | 20 |
Quotas are per key per day and reset at 00:00 UTC. Over quota returns 429 with the count so far and the time until reset, plus Retry-After: 60. More than 10,000 events in one request returns 413. An answer larger than a few megabytes also returns 413 — add a LIMIT, ask for fewer columns, or narrow the window. Windows wider than your plan are clamped rather than refused, and results are capped at the plan’s group count.