Use casesPartners and pipelinesPartner data API04 / 12
How it fits together

The key is the contract

Everything a partner may do is written on their key: one query, the columns it hashes, how often, from where and until when. Every call is held to it, and the rest of your schema stays out of reach. Their API documentation lists the one endpoint they can call.

1endpoint in their OpenAPI
16 hexfor an email, hashed
60 minof overlap when you rotate
The partner
The governed core
Your database
The key's termseverything the partner may do, on one key
partner-acme· one key, one contract
queries: acme_orders·mask email → hash·60/minute·203.0.113.0/24·expires 2027-01-01
↗

Acme's backend

key partner-acme
from 203.0.113.0/24

A leaked copy of the key

from 198.51.100.9
↗
Their API docs
/openapi.json
paths: /q/acme_orders
One endpoint, and a Postman collection if it's in one.
The governed core↗

01 · Identity

Identify the key
its secret, still active, not expired, from an allowed address
Apply its limits
60 a minute, on top of the server's own
401429 rate_limited

02 · Policy

Check the grant
this one saved query, on its own connection; no other query, no schema, no SQL
Validate parameters
status is typed and bound
403

03 · Execution

Run it
the SQL fixes Acme's partner id
Mask it
customers.email hashed, traced through aliases and expressions
Record it
history lists what was masked
↗

PostgreSQL

shop
orderscustomersemail → hash paymentspayroll staffinventory suppliersaudit_events

Behind acme_orders: two tables. The key reaches nothing else here, not even the schema.

What Acme gets backequal emails give equal codes
{"order_id": 8812, "status": "open", "qty": 3, "email": "3f9a1c0be47d2a65"} X-RateLimit-Limit: 60 X-RateLimit-Remaining: 59
Stablecompatibility promise Expmay change in a minor release A leaked secret? Rotate it: the old one keeps working for an hour by default while the partner switches.
The partnerAcme's backend, with one keyTheir API docs list one endpoint; a leaked copy of the key used from anywhere else fails like a wrong key.
The key's termsOne query · email hashed · 60/minute · their network · until 2027-01-01Everything the partner may do, written on the key.
The governed coreIdentity → policy → executionThe key is identified and limited, its grant allows one saved query and no schema, and the email column is hashed wherever the SQL puts it.
Your databaseTwo tables behind one queryThe rest of the schema isn't reachable with this key.
What Acme gets backTheir rows, email hashedEqual values give equal codes, so they can still join on it. Rate-limit headers tell them how much is left.

The same governed core as the full architecture.

For a partner or customer

Hand a partner one query, not your schema

A customer wants their orders. The usual answer is a bespoke endpoint, an auth middleware and a quarter of review time. The alternative is a scoped key that can call exactly one saved query, written for their rows.

✓A queries grant names saved queries; it reaches nothing else on the connection, not even the schema.
✓Masking rules on the key hash or hide columns like email, whatever the SQL calls them. Partners who sign in through your identity provider can share one query instead: a from_claim parameter takes customer_id from their token, never from the request.
✓Rate limit, IP allow-list and expiry on the key. Rotate its secret with a grace period when it leaks.
✓Their /openapi.json lists only the one endpoint they can call. Put it in a collection and they get a Postman collection too.
Partner data API
# the partner's key: one query, emails hashed, 60/min, from their network
curl -X POST https://data.example.com/api/v1/api-keys \
  -H 'X-API-Key: <admin>' -H 'Content-Type: application/json' -d '{
  "name": "partner-acme",
  "connections": [],
  "queries": ["acme_orders"],
  "mask": [{"column": "customers.email", "action": "hash"}],
  "rate_limit": "60/minute",
  "allowed_ips": ["203.0.113.0/24"],
  "expires_at": "2027-01-01"
}'

# what they call
GET /q/acme_orders?status=open&format=csv
X-API-Key: sk_live_…9f3a
More use casesAll twelve →

Not sure which fits?

The example APIs ship with a generated database and one scoped key per scenario, so you can try each of these in a few minutes without connecting anything of your own.

Run the examplesCompare alternatives