Attribution Data API
A read-only HTTP API over the curated attribution warehouse. Authenticate with a scoped bearer key and pull JSON — no database connection string required. Every dataset is a curated, allowlisted view; customer/staff PII lives only behind dedicated *_pii scopes.
Curated datasets
Cleanly-shaped read-only views — leads, events, activities, attribution, LTV, staff and reference. Filter by any column, paginate, done.
Scoped & PII-gated
Each key is granted only the datasets it needs. Team-performance data is non-PII; names / emails / phones require a separate *_pii scope.
Using the API
All routes are read-only GETs returning JSON over HTTPS. Base URL /v1.
Authentication
Send a bearer key on every data route (this catalog and /v1/health need none). Keys are scoped per dataset — the scope name equals the dataset name (e.g. attr_activity, attr_leads).
Authorization: Bearer <your-api-key>
| Situation | Status | Meaning |
|---|---|---|
| valid key, in scope | 200 | Data returned |
| missing / bad key | 401 | No or invalid Authorization header |
| wrong scope | 403 | Key not scoped for that dataset |
| unknown id / dataset | 404 | No matching row, or dataset not in the allowlist |
| bad filter column | 400 | Filter column not on that dataset |
Scopes & PII
Scoping is the only access mechanism, and PII-gating is just what it achieves for sensitive fields:
- Scoped — every dataset is its own scope. A key reads only the datasets it was granted; anything else is a
403. - PII-gated — customer/staff PII (names, emails, phones, raw notes) lives only in the separate
*_piidatasets. A key without a*_piiscope never sees PII — and the non-PII view doesn't even contain those columns.
So an app that doesn't need PII is granted only the plain scopes and cannot retrieve it. Example — a sales-performance dashboard:
| App | Granted scopes | Can read |
|---|---|---|
| sales dashboard (no PII) | attr_activity attr_leads attr_events attr_staff | bookings, advisors, funnel, revenue — no names/emails/phones |
| app that needs contacts | above + crm_leads_pii | adds contact/identity fields, for that key only |
Pagination & filtering
| Param | Type | Description |
|---|---|---|
| limit | integer | Rows to return. Default 100, max 1000 |
| offset | integer | Rows to skip. Default 0 |
| {column}={value} | any | Any real column becomes an equality filter (else 400) |
List responses are enveloped: { items, limit, offset, total }. Single-record routes (/v1/{dataset}/{id}) return the row object directly.
Health & docs
curl -s /v1/health → {"status":"ok"} curl -s /v1/health/db → {"status":"ok","db":"ok"}
Interactive OpenAPI docs: /docs · /redoc · /openapi.json.