Skip to content

Connect real data (resources)

Volly apps get a lot more useful once they can show real company data: a dashboard over your production database, a report built on PostHog analytics, a tool that reads an Airtable base. Resources are how you connect that data. An admin sets one up once, then decides which apps are allowed to use it, and app code never sees a password or API key.

Head to Settings → Resources and add one. What you need depends on the type:

  • Postgres. Volly never asks for your admin database credentials. Setup generates a SQL script for you to run yourself, in Supabase’s SQL Editor, psql, wherever you manage the database. That script creates a sandboxed, read-only role scoped to whichever schemas you pick, and Volly only ever stores that role’s connection string. Your database needs to be reachable from the public internet for any of this to work.
  • PostHog. Paste a personal API key with read-only scopes, or click Connect with PostHog and approve the requested scopes. No key to create or rotate that way.
  • Airtable and other REST APIs. Paste an API key (a personal access token for Airtable, or whatever the service calls it) plus the base URL apps should be confined to.
  • Google Sheets. Connects through Google OAuth, no key at all. By default each teammate links their own Google account the first time an app touches the resource, so every read or write is attributed to them and limited to sheets they can already open. Flip Share credentials between users if one connected account should serve everyone instead.

Once it’s connected, open the resource and choose which apps can use it, and whether they get read-only or read-and-write access.

Building an app that needs one of these? Open the project page, pick a resource from the org’s list, add a short note on why, and submit. An admin reviews it in Settings → Resources: approve grants the access, deny sends a reason back to your project page. That page always shows what’s granted, pending, and denied for your app.

An app that hasn’t been granted a resource simply can’t see it. There’s nothing to guess at.

Once access is granted, calling a resource is just fetch, no keys to manage. Every call needs the app running in a signed-in session and the X-Volly-Resource: 1 header attached (that’s a CSRF guard, not a secret; just include it).

const res = await fetch('/__volly/resources/crm-data/query', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'X-Volly-Resource': '1' },
  body: JSON.stringify({
    sql: 'select name, plan from customers where plan = $1 limit 50',
    params: ['enterprise'],
  }),
})
const { rows, rowCount } = await res.json()

Errors come back as JSON with the real database message, so an agent writing the query can debug it directly: { "error": "column \"nope\" does not exist (42703)" }, for instance. Results cap at 2 MB, so add a LIMIT if you hit that.

Take any GET endpoint from the vendor’s own docs, drop their base URL, and prepend /__volly/resources/<alias>/proxy:

// PostHog docs: GET /api/projects/<id>/insights
const insights = await fetch('/__volly/resources/posthog/proxy/insights?limit=10', {
  headers: { 'X-Volly-Resource': '1' },
}).then((r) => r.json())

// Airtable
const tasks = await fetch('/__volly/resources/airtable/proxy/Tasks?maxRecords=50', {
  headers: { 'X-Volly-Resource': '1' },
}).then((r) => r.json())

GET and HEAD always work; other methods return 405 unless the app was granted read-and-write. Most vendor SDKs won’t work here since they tend to hardcode their own host or attach their own key, so plain fetch is the supported path.

Google Sheets and other per-viewer resources

Section titled “Google Sheets and other per-viewer resources”

If a teammate hasn’t linked their Google account yet, a call comes back 403 with a link to send them to:

{
  "error": "connect your google account to use this resource",
  "code": "oauth_not_connected",
  "connectUrl": "/__volly/resources/team-sheet/connect"
}

Render connectUrl as a real link the viewer clicks, not a fetch or an iframe (Google won’t run consent inside one). Add ?return_to=<path> so they land back where they were, and once connected, the same call just works.

Status Meaning
401 Session expired, reload to sign in again
403 Missing the X-Volly-Resource header, or an OAuth connect prompt (see above)
404 Unknown resource, or this app hasn’t been granted it
405 Tried to write without a read-and-write grant
429 Too many requests, retry shortly
502 / 504 The upstream database or API failed or timed out

Agents connected through Volly’s MCP server can call list_resources to see what’s available, what a given app can already reach, and copy-ready code for using it. Ask an agent to “build a dashboard on our CRM data” and it knows exactly what to call; granting access itself stays a human, admin action in the dashboard.

Curious how this stays safe with credentials an app never sees? See how resources stay secure.