# Klaviyo Enterprise Rbac

> | Configure Klaviyo enterprise access control with API key scopes and OAuth. Use when implementing per-key scoping, configuring OAuth app authorization, or setting up organization-level access controls for Klaviyo. Trigger with phrases like "klaviyo scopes", "klaviyo RBAC", "klaviyo enterprise", "klaviyo permissions", "klaviyo OAuth", "klaviyo access control".

- Skill: `thedixitjain/klaviyo-enterprise-rbac` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds add thedixitjain/klaviyo-enterprise-rbac`
- Raw SKILL.md: https://api.skillmd.com/api/skills/thedixitjain/klaviyo-enterprise-rbac/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Security
- Author: thedixitjain (https://skillmd.com/u/thedixitjain)
- Updated: 2026-09-08
- Page: https://skillmd.com/skills/thedixitjain/klaviyo-enterprise-rbac

---


# Klaviyo Enterprise RBAC

## Overview

Enterprise access control for Klaviyo: API key scoping with granular read/write permissions, OAuth app authorization flows, and application-level RBAC built on top of Klaviyo's scope system. Klaviyo has no built-in roles, so you compose least-privilege access from scoped keys plus an application permission layer.

## Prerequisites

- Klaviyo account with API key management access
- Understanding of OAuth 2.0 (for OAuth apps)
- Application requiring per-user or per-role Klaviyo access

## Klaviyo Access Control Model

Klaviyo uses **scoped API keys** and **OAuth** for access control. There are no built-in "roles" in Klaviyo's API -- you implement RBAC by creating multiple API keys with different scopes.

### API Key Scopes

| Scope | Read | Write | What It Controls |
|-------|------|-------|-----------------|
| `accounts` | Account info | N/A | Organization name, timezone |
| `campaigns` | List campaigns | Create/send campaigns | Email, SMS, push campaigns |
| `catalogs` | Browse items | CRUD catalog items | Product catalog management |
| `coupons` | List coupons | Create coupons | Coupon/discount codes |
| `data-privacy` | N/A | Delete profiles | GDPR/CCPA deletion requests |
| `events` | Query events | Track events | Server-side event tracking |
| `flows` | List flows | Create/update flows | Flow automation |
| `images` | List images | Upload images | Email template images |
| `lists` | List lists | CRUD lists/members | List management |
| `metrics` | Query metrics | N/A | Metric aggregations |
| `profiles` | Read profiles | Create/update profiles | Profile management |
| `segments` | Read segments | N/A | Segment queries |
| `tags` | Read tags | CRUD tags | Resource tagging |
| `templates` | Read templates | Create/update templates | Email templates |
| `webhooks` | List webhooks | CRUD webhooks | Webhook subscriptions |

## Instructions

The full, copy-ready code for every step lives in
[the implementation walkthrough](references/implementation.md). At a high level
the workflow is five steps:

1. **Create scoped API keys** — one key per service/role in the Klaviyo
   dashboard (**Settings > API Keys**), each granted only the scopes it needs.
   A profile-sync service gets `profiles:*` + `lists:*`; a reporting dashboard
   gets `*:read` only; the admin key gets everything and is used sparingly.
2. **Model application-level RBAC** — map each app role (admin, marketer,
   developer, viewer, service) to a permission set and to its scoped key, so the
   app enforces intent and the key enforces the hard boundary.
3. **Add permission middleware** — reject requests whose role lacks the required
   permission before the handler runs (401 for no role, 403 for wrong role).
4. **Wire the OAuth app flow** — for third-party/marketplace integrations,
   request only the scopes the app uses, then exchange the code for tokens.
5. **Record an audit trail** — log every access with role/action/resource and
   alert the security team on destructive actions like profile deletion.

The essential skeleton — a least-privilege key plus the permission check — looks
like this:

```typescript
// One scoped key per service — reporting is read-only
const reportingSession = new ApiKeySession(process.env.KLAVIYO_KEY_REPORTING!);

// App-level guard: refuse before the handler runs
app.post('/api/klaviyo/campaigns/send',
  requireKlaviyoPermission('canSendCampaigns'),
  campaignSendHandler
);
```

See [the implementation walkthrough](references/implementation.md) for the role
permission matrix, `getSessionForRole`, the middleware, the OAuth exchange, the
audit logger, and the full environment variable layout in
[the implementation walkthrough](references/implementation.md).

## Output

Applying this skill produces:

- **Multiple scoped API keys** in Klaviyo, one per service/role, each holding
  only the scopes that service needs (defence in depth beyond app-level checks).
- **An RBAC module** (`src/klaviyo/rbac.ts`) exporting `checkPermission` and
  `getSessionForRole`, mapping the five app roles to permissions and keys.
- **Permission middleware** (`src/middleware/klaviyo-auth.ts`) that returns 401
  when no role is assigned and 403 when a role lacks the requested permission.
- **An OAuth flow** for marketplace apps requesting minimized scopes.
- **An audit log** (`src/klaviyo/audit.ts`) that records access and alerts on
  destructive actions.

A blocked request returns a structured 403, e.g.:

```json
{ "error": "Forbidden", "message": "Role 'marketer' does not have permission: canDeleteProfiles" }
```

## Examples

Grounded, end-to-end scenarios — least-privilege keys, route gating by
permission, resolving a session from a role, OAuth scope minimization, and
alerting on a destructive action — are collected in
[the examples reference](references/examples.md).

Quick illustration — a marketer is allowed to send campaigns but blocked from
deleting profiles, purely from the role permission set:

```typescript
// canSendCampaigns: true  -> allowed
app.post('/api/klaviyo/campaigns/send', requireKlaviyoPermission('canSendCampaigns'), campaignSendHandler);

// canDeleteProfiles: false -> 403 Forbidden
app.delete('/api/klaviyo/profiles/:id', requireKlaviyoPermission('canDeleteProfiles'), profileDeleteHandler);
```

## Error Handling

| Error | Status | Cause | Solution |
|-------|--------|-------|----------|
| `permission_denied` | 403 | API key missing required scope | Create new key with correct scopes |
| OAuth code expired | 400 | User took too long to authorize | Retry authorization flow |
| Token refresh failed | 401 | Refresh token revoked | Re-authorize the app |
| Role not assigned | 401 | User missing `klaviyoRole` | Assign role in your user management |

## Resources

- [Full implementation walkthrough](references/implementation.md) — all five steps, verbatim code
- [Worked examples](references/examples.md) — least-privilege, route gating, OAuth minimization
- [Klaviyo API Scopes](https://developers.klaviyo.com/en/docs/authenticate_)
- [OAuth Setup Guide](https://developers.klaviyo.com/en/docs/set_up_oauth)
- [Update OAuth Scopes](https://developers.klaviyo.com/en/docs/update_your_oauth_scopes)
- For major migrations, see the `klaviyo-migration-deep-dive` skill.

---

**Source:** [`jeremylongshore/claude-code-plugins-plus-skills`](https://github.com/jeremylongshore/claude-code-plugins-plus-skills) → `plugins/saas-packs/klaviyo-pack/skills/klaviyo-enterprise-rbac/SKILL.md`

