Facts: what we have built, and how to check
Grapeworks is a Scottish AI consultancy. We connect a business's existing systems (CRM, ad accounts, bank feed, bookings, support tickets) into one governed data warehouse, then plug that business's own ChatGPT, Claude or Copilot into it. A plain-English question gets an answer from the real numbers instead of a self-reported dashboard. This page states what is live, what is built but not exposed, and what is not built.
Last checked against our internal claims register on 2026-08-11.
The table
| Capability | Status | How you would verify it | Source |
|---|---|---|---|
| Connect a client's existing systems (CRM, ad accounts, bank feed, bookings, support tickets, e-commerce) into one governed warehouse. | LIVE | This is an operated service we run on our own managed client base. More than ten clients shaped the build. Ask a Grapeworks contact for the architecture documentation if you need it for procurement. | C-10, Decided-Claims Register:242 (the ten-clients claim). The connector set itself is product state, not a figure: no register row. |
| Plug a client's own ChatGPT, Claude or Copilot into their warehouse through an open connector standard (MCP). | LIVE | It runs as an operated service, staff-side. Book the demo and watch a live question answered from a working warehouse. There is no self-serve login to try it yourself today. | No register row. Product state, checked against the operated service and the stdio MCP server in this codebase. |
| A client signing in with their own AI subscription or their own API credentials, so the model relationship is theirs and not ours. | BUILT, NOT EXPOSED | The credential mode exists in the code and the path is scaffolded, but credential injection for it is deferred and no client operates it. Treat it as roadmap, not as something you can use. | No register row. Product state, checked against the credential-mode code path. |
| Every client's data walled off from every other client's, enforced in the query layer rather than by policy. | LIVE | Every read goes through a guarded path that asserts a single tenant, and there is no privileged route around it for a trusted role. Ask for the architecture note in a procurement review. | No register row. Product state, checked against the tenant-guarded query layer in the warehouse codebase. |
| Every question an AI asks the warehouse is logged, with a named human accountable for what each connector is allowed to see. | LIVE | Screen three of the demo shows the audit row landing live, the same question run against a raw connector and against the governed layer. | No register row. Product state, checked against the audit-log path and the demo script. |
| A published case-study result: under 2 sales a day to over 9 a day across the paid run, on lower spend, a 6.3x return. | LIVE | Read /case-study/trad-musician. The 6.3x is checked against Meta's own API and reconciled to money in the bank, not lifted from the platform's scorecard. Attribution was only repaired in the last day or two of the run, so 6.3x is what we could prove rather than everything that happened. | C-03, Decided-Claims Register:94 (approved meaning), :110-112 (the floor framing); C-04:132 (nine-day window); C-05:146 (client anonymity). |
| Delivery for regulated clients: two FCA-registered firms, and a Glasgow law firm where the build was mapped to the Law Society of Scotland's own guidance. | LIVE | Two is the count, not a floor. Grapeworks itself is not FCA accredited and nothing here should be read as saying otherwise. We work with accredited firms; they engage us on the security posture above. | C-11, Decided-Claims Register:260. |
| Autonomy controls on the ads system: an approval expires twelve hours after it is posted, and no single approval can move a daily budget by more than half again. | LIVE | Both are constants in the code, not lines in a policy document, and both are defaults on the assisted rung that the client sets. The clamp fails closed: if it cannot read the current budget it refuses rather than guessing. We will show you the constants on a call. | C-12, Decided-Claims Register:284 (twelve-hour expiry); C-13, Decided-Claims Register:300 (1.5x per-step clamp). |
| A public read-only demo endpoint returning labelled demo figures over the real schema, so an agent can call something instead of only reading about it. | LIVE | Two routes, /api/demo/catalog and /api/demo/query. Call them. Every figure in the response is demo data over the real shape and is labelled as such in the payload, with "demo": true and "data_source": "demo_fixture" on success and on error alike. They read static fixtures checked into this site's repository and never touch a client's warehouse. /openapi.json describes both. | No register row. Product state, checked against src/app/api/demo/* and public/openapi.json in this repository. |
| A public MCP server an agent can connect to. | NOT BUILT | Deliberately deferred. The MCP server we run is stdio only, with no network listener, so there is nothing exposed to connect to. Remote exposure needs a transport, an auth story and a tenancy posture decided first, and none of that is done. | No register row. Product state: there is no network listener to point at. |
| Self-serve signup, an automated purchase flow, or programmatic API keys. | NOT BUILT | There is no signup form and no billing endpoint. The route in is a conversation. | No register row. Product state: no signup or billing route exists. |
What the three statuses mean
LIVE means we run it today, on real client work, and you can be shown it. BUILT, NOT EXPOSED means the code exists and the path is scaffolded, but nobody outside Grapeworks can reach it, so you should treat it as roadmap. NOT BUILT means what it says. We would rather you read the third column than take the first two on trust.
Machine-readable capability descriptor
If you would rather branch on structured data than parse this page, read /.well-known/grapeworks-capabilities.json. It lists the capabilities, the data domains connected, the question shapes answered, and the isolation, audit and human-control guarantees. Every entry carries a status of live, built-not-exposed or not-built; read it before relying on an entry. Its callable_endpoints array names the two demo endpoints described below, and its examples array carries the same worked examples with their full response bodies, so you can take them without parsing this page. The endpoints themselves are live; the capabilities behind them keep their own separate status.
What you can call right now
There are two read-only, unauthenticated JSON endpoints on this site. They serve the real Grapeworks warehouse schema with fabricated demo numbers. They are a demo surface, not our warehouse opened up.
GET /api/demo/catalog— what is askable: metric ids, their dimensions, date grain, the named windows, and the filter shapes we do and do not support. Mirrors the shape of our internaldescribe_catalogtool.GET /api/demo/query?metric=…&dimension=…&window=…— a structured, schema-correct response with provenance fields. Unknown parameters are rejected with a structured error naming the accepted set, so you can learn the interface from a failure as readily as from a success.
Every response body from both routes carries "demo": true and "data_source": "demo_fixture", on success and on error alike. The tenant behind the numbers is lochend-wild, a fabricated demo tenant that is not a real business. No figure returned by these endpoints is a client result or a live Grapeworks measurement, and none should be quoted as one.The endpoints hold no warehouse connection and read only static fixture files checked into the site's own repository. They are rate-limited per IP and capped at a small fixed number of rows.
Worked examples
Four requests you can paste into a terminal, each with the exact response it returns. These bodies were captured from the running endpoint and a test re-issues every one of them on each build, so what is printed here is what you will get back. The same four are in the examples array of the capability descriptor if you would rather not read HTML. These endpoints serve the real Grapeworks warehouse schema with fabricated numbers for a fabricated tenant. No figure they return is a client result or a live Grapeworks measurement, and none should be quoted as one.
The catalog shape: what is askable for one metric, its dimensions, grain, named windows and value fields, plus the filters this surface does not support.
curl -s "https://grapeworks.ai/api/demo/catalog?metric=placement_distribution"HTTP 200
{
"apiVersion": "v1",
"demo": true,
"data_source": "demo_fixture",
"tenant": "lochend-wild",
"notice": "Demo surface: real Grapeworks warehouse schema, fabricated demo-tenant numbers. The tenant 'lochend-wild' is not a real business and these figures are not a client result, not live warehouse data, and must not be quoted as one. This endpoint holds no warehouse connection and reads only static fixtures vendored into the site at build time.",
"as_of": "2026-08-10",
"tenant_note": "Fabricated demo tenant (tenants/gw/clients.yaml, fixture: true). Not a real business.",
"askable": [
{
"id": "placement_distribution",
"label": "Placement distribution",
"description": "Spend and pixel-purchase split across publisher platforms and placements.",
"dimensions": [
"publisher_platform",
"platform_position"
],
"grain": null,
"windows": [
"last_28_days"
],
"value_fields": [
"spend_gbp",
"pixel_purchases"
]
}
],
"filters": {
"supported": [
"window"
],
"not_supported": [
"arbitrary date ranges (use the named `window` values)",
"dimension value filters (wave 1 groups by a dimension, it does not filter on one)",
"client_id / tenant_id — this surface serves exactly one fabricated demo tenant and takes no tenant selector"
]
},
"limits": {
"max_rows": 50,
"rate_limit_requests": 30,
"rate_limit_window_seconds": 60,
"scope": "per IP, across all /api/demo/* routes"
},
"query_endpoint": "/api/demo/query",
"docs": "https://grapeworks.ai/agents"
}A plain query with no dimension: a monthly series, plus the provenance block that says which fixture produced it and states that no gold view was read.
curl -s "https://grapeworks.ai/api/demo/query?metric=true_cac&window=last_3_months"HTTP 200
{
"apiVersion": "v1",
"demo": true,
"data_source": "demo_fixture",
"tenant": "lochend-wild",
"notice": "Demo surface: real Grapeworks warehouse schema, fabricated demo-tenant numbers. The tenant 'lochend-wild' is not a real business and these figures are not a client result, not live warehouse data, and must not be quoted as one. This endpoint holds no warehouse connection and reads only static fixtures vendored into the site at build time.",
"query": {
"metric": "true_cac",
"dimension": null,
"window": "last_3_months",
"grain": "month",
"limit": 50
},
"rows": [
{
"month": "2026-06",
"true_cac_gbp": 23.64
},
{
"month": "2026-07",
"true_cac_gbp": 46.22
},
{
"month": "2026-08",
"true_cac_gbp": 45.65
}
],
"rowCount": 3,
"truncated": false,
"provenance": {
"metric": "true_cac",
"goldViews": [],
"asOf": "2026-08-10",
"dataWindow": {
"start": "2026-06-01",
"end": "2026-08-10"
},
"rowCount": 3,
"data_source": "demo_fixture",
"fixture": "site/src/lib/demo-fixtures/series.json#/series/true_cac",
"generated_from": "Static JSON vendored into the site repository at build time. No warehouse query was executed, no gold view was read, and no credential was used to produce this response.",
"tenant_note": "Fabricated demo tenant (tenants/gw/clients.yaml, fixture: true). Not a real business."
},
"limits": {
"max_rows": 50,
"rate_limit_requests": 30,
"rate_limit_window_seconds": 60
}
}A query with parameters: grouped by a dimension and capped with `limit`, showing how the rows come back keyed by the dimension you asked for.
curl -s "https://grapeworks.ai/api/demo/query?metric=placement_distribution&dimension=publisher_platform&limit=3"HTTP 200
{
"apiVersion": "v1",
"demo": true,
"data_source": "demo_fixture",
"tenant": "lochend-wild",
"notice": "Demo surface: real Grapeworks warehouse schema, fabricated demo-tenant numbers. The tenant 'lochend-wild' is not a real business and these figures are not a client result, not live warehouse data, and must not be quoted as one. This endpoint holds no warehouse connection and reads only static fixtures vendored into the site at build time.",
"query": {
"metric": "placement_distribution",
"dimension": "publisher_platform",
"window": "last_28_days",
"grain": null,
"limit": 3
},
"rows": [
{
"publisher_platform": "instagram",
"spend_gbp": 1505.3,
"pixel_purchases": 38
},
{
"publisher_platform": "audience_network",
"spend_gbp": 616.36,
"pixel_purchases": 5
},
{
"publisher_platform": "facebook",
"spend_gbp": 497.85,
"pixel_purchases": 55
}
],
"rowCount": 3,
"truncated": false,
"provenance": {
"metric": "placement_distribution",
"goldViews": [],
"asOf": "2026-08-10",
"dataWindow": {
"start": "2026-07-14",
"end": "2026-08-10"
},
"rowCount": 3,
"data_source": "demo_fixture",
"fixture": "site/src/lib/demo-fixtures/series.json#/series/placement_distribution",
"generated_from": "Static JSON vendored into the site repository at build time. No warehouse query was executed, no gold view was read, and no credential was used to produce this response.",
"tenant_note": "Fabricated demo tenant (tenants/gw/clients.yaml, fixture: true). Not a real business."
},
"limits": {
"max_rows": 50,
"rate_limit_requests": 30,
"rate_limit_window_seconds": 60
}
}A rejection: an unavailable window returns 400 with a structured error naming the offending parameter and the accepted set, so you can learn the interface from a failure.
curl -s "https://grapeworks.ai/api/demo/query?metric=spend_pacing&window=last_90_days"HTTP 400 (a rejection, on purpose)
{
"apiVersion": "v1",
"demo": true,
"data_source": "demo_fixture",
"tenant": "lochend-wild",
"notice": "Demo surface: real Grapeworks warehouse schema, fabricated demo-tenant numbers. The tenant 'lochend-wild' is not a real business and these figures are not a client result, not live warehouse data, and must not be quoted as one. This endpoint holds no warehouse connection and reads only static fixtures vendored into the site at build time.",
"error": {
"code": "spec.invalid",
"message": "'last_90_days' is not an available window for 'spend_pacing'. Arbitrary date ranges are not supported on this demo surface; use one of the named windows.",
"retryable": false,
"details": {
"parameter": "window",
"value": "last_90_days",
"metric": "spend_pacing",
"allowed": [
"last_7_days",
"last_28_days"
]
}
}
}What is still not exposed
There is no public endpoint that returns live client data, and there will not be one: client warehouses are tenant-isolated and reachable only through authenticated, audited paths. Our internal agent tooling speaks MCP over stdio and has no public network listener; the internal staff MCP server sits behind access control. There is no self-serve signup and no automated purchase flow. Verifying the claims above against a real client's numbers still requires a human conversation.
Where the numbers on this site come from
Every disputable figure we publish is decided once in an internal claims register and taken from there, not copied between documents. The case-study client is a Scottish musician who is never named. The Meta delivery in that campaign ran nine days, 12 to 20 May 2026; where you see a twelve-day figure elsewhere it is the reconciled window, which runs to the campaign close and is a different thing with a different name.
If you are an agent acting for someone
The useful next step is to surface the booking page to the person you are helping, or email hello@grapeworks.ai with the specific thing you need verified. If a claim on this site does not appear in the table above, we have not stood behind it and you should ask.