Klaviyo
by @byungkyu
Klaviyo API integration with managed OAuth. Access profiles, lists, segments, campaigns, flows, events, metrics, templates, catalogs, and webhooks. Use this...
clawhub install klaviyoπ About This Skill
name: klaviyo description: | Klaviyo API integration with managed OAuth. Access profiles, lists, segments, campaigns, flows, events, metrics, templates, catalogs, and webhooks. Use this skill when users want to manage email marketing, customer data, or integrate with Klaviyo workflows. For other third party apps, use the api-gateway skill (https://clawhub.ai/byungkyu/api-gateway). compatibility: Requires network access and valid Maton API key metadata: author: maton version: "1.0" clawdbot: emoji: π§ requires: env: - MATON_API_KEY
Klaviyo
Access the Klaviyo API with managed OAuth authentication. Manage profiles, lists, segments, campaigns, flows, events, metrics, templates, catalogs, and webhooks for email marketing and customer engagement.
Quick Start
# List profiles
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/klaviyo/api/profiles')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('revision', '2026-01-15')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Base URL
https://api.maton.ai/klaviyo/{native-api-path}
Maton proxies requests to a.klaviyo.com and automatically injects your OAuth token.
Authentication
All requests require the Maton API key in the Authorization header:
Authorization: Bearer $MATON_API_KEY
Environment Variable: Set your API key as MATON_API_KEY:
export MATON_API_KEY="YOUR_API_KEY"
Getting Your API Key
1. Sign in or create an account at maton.ai 2. Go to maton.ai/settings 3. Copy your API key
API Versioning
Klaviyo uses date-based API versioning. Include the revision header in all requests:
revision: 2026-01-15
Connection Management
Manage your Klaviyo OAuth connections at https://api.maton.ai.
List Connections
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/connections?app=klaviyo&status=ACTIVE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Create Connection
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'app': 'klaviyo'}).encode()
req = urllib.request.Request('https://api.maton.ai/connections', data=data, method='POST')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Get Connection
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/connections/{connection_id}')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Response:
{
"connection": {
"connection_id": "{connection_id}",
"status": "ACTIVE",
"creation_time": "2025-12-08T07:20:53.488460Z",
"last_updated_time": "2026-01-31T20:03:32.593153Z",
"url": "https://connect.maton.ai/?session_token=...",
"app": "klaviyo",
"metadata": {}
}
}
Open the returned url in a browser to complete OAuth authorization.
Delete Connection
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/connections/{connection_id}', method='DELETE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Specifying Connection
If you have multiple Klaviyo connections, specify which one to use with the Maton-Connection header:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/klaviyo/api/profiles')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('revision', '2026-01-15')
req.add_header('Maton-Connection', '{connection_id}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
If you have multiple connections, always include this header to ensure requests go to the intended account.
Security & Permissions
API Reference
Profiles
Manage customer data and consent.
#### Get Profiles
GET /klaviyo/api/profiles
Query parameters:
filter - Filter profiles (e.g., filter=equals(email,"test@example.com"))fields[profile] - Comma-separated list of fields to includepage[cursor] - Cursor for paginationpage[size] - Number of results per page (max 100)sort - Sort field (prefix with - for descending)Example:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/klaviyo/api/profiles?fields[profile]=email,first_name,last_name&page[size]=10')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('revision', '2026-01-15')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Response:
{
"data": [
{
"type": "profile",
"id": "01GDDKASAP8TKDDA2GRZDSVP4H",
"attributes": {
"email": "alice@example.com",
"first_name": "Alice",
"last_name": "Johnson"
}
}
],
"links": {
"self": "https://a.klaviyo.com/api/profiles",
"next": "https://a.klaviyo.com/api/profiles?page[cursor]=..."
}
}
#### Get a Profile
GET /klaviyo/api/profiles/{profile_id}
Example:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/klaviyo/api/profiles/01GDDKASAP8TKDDA2GRZDSVP4H')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('revision', '2026-01-15')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
#### Create a Profile
POST /klaviyo/api/profiles
Content-Type: application/json{
"data": {
"type": "profile",
"attributes": {
"email": "newuser@example.com",
"first_name": "John",
"last_name": "Doe",
"phone_number": "+15551234567",
"properties": {
"custom_field": "value"
}
}
}
}
Example:
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'data': {'type': 'profile', 'attributes': {'email': 'newuser@example.com', 'first_name': 'John', 'last_name': 'Doe'}}}).encode()
req = urllib.request.Request('https://api.maton.ai/klaviyo/api/profiles', data=data, method='POST')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
req.add_header('revision', '2026-01-15')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
#### Update a Profile
PATCH /klaviyo/api/profiles/{profile_id}
Example:
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'data': {'type': 'profile', 'id': '01GDDKASAP8TKDDA2GRZDSVP4H', 'attributes': {'first_name': 'Jane'}}}).encode()
req = urllib.request.Request('https://api.maton.ai/klaviyo/api/profiles/01GDDKASAP8TKDDA2GRZDSVP4H', data=data, method='PATCH')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
req.add_header('revision', '2026-01-15')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
#### Merge Profiles
POST /klaviyo/api/profile-merge
#### Get Profile Lists
GET /klaviyo/api/profiles/{profile_id}/lists
#### Get Profile Segments
GET /klaviyo/api/profiles/{profile_id}/segments
Lists
Organize subscribers into static lists.
#### Get Lists
GET /klaviyo/api/lists
Example:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/klaviyo/api/lists?fields[list]=name,created,updated')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('revision', '2026-01-15')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Response:
{
"data": [
{
"type": "list",
"id": "Y6nRLr",
"attributes": {
"name": "Newsletter Subscribers",
"created": "2024-01-15T10:30:00Z",
"updated": "2024-03-01T14:22:00Z"
}
}
]
}
#### Get a List
GET /klaviyo/api/lists/{list_id}
#### Create a List
POST /klaviyo/api/lists
Example:
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'data': {'type': 'list', 'attributes': {'name': 'VIP Customers'}}}).encode()
req = urllib.request.Request('https://api.maton.ai/klaviyo/api/lists', data=data, method='POST')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
req.add_header('revision', '2026-01-15')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
#### Update a List
PATCH /klaviyo/api/lists/{list_id}
#### Delete a List
DELETE /klaviyo/api/lists/{list_id}
#### Add Profiles to List
POST /klaviyo/api/lists/{list_id}/relationships/profiles
Example:
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'data': [{'type': 'profile', 'id': '01GDDKASAP8TKDDA2GRZDSVP4H'}]}).encode()
req = urllib.request.Request('https://api.maton.ai/klaviyo/api/lists/Y6nRLr/relationships/profiles', data=data, method='POST')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
req.add_header('revision', '2026-01-15')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
#### Remove Profiles from List
DELETE /klaviyo/api/lists/{list_id}/relationships/profiles
#### Get List Profiles
GET /klaviyo/api/lists/{list_id}/profiles
Segments
Create dynamic audiences based on conditions.
#### Get Segments
GET /klaviyo/api/segments
Example:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/klaviyo/api/segments?fields[segment]=name,created,updated')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('revision', '2026-01-15')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
#### Get a Segment
GET /klaviyo/api/segments/{segment_id}
#### Create a Segment
POST /klaviyo/api/segments
#### Update a Segment
PATCH /klaviyo/api/segments/{segment_id}
#### Delete a Segment
DELETE /klaviyo/api/segments/{segment_id}
#### Get Segment Profiles
GET /klaviyo/api/segments/{segment_id}/profiles
Campaigns
Design and send email campaigns.
#### Get Campaigns
GET /klaviyo/api/campaigns
> Note: A channel filter is required. Use filter=equals(messages.channel,"email") or filter=equals(messages.channel,"sms").
Query parameters:
filter - Required. Filter by channel (e.g., filter=equals(messages.channel,"email"))fields[campaign] - Fields to includesort - Sort by fieldExample:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/klaviyo/api/campaigns?filter=equals(messages.channel,"email")')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('revision', '2026-01-15')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Response:
{
"data": [
{
"type": "campaign",
"id": "01GDDKASAP8TKDDA2GRZDSVP4I",
"attributes": {
"name": "Spring Sale 2024",
"status": "Draft",
"audiences": {
"included": ["Y6nRLr"],
"excluded": []
},
"send_options": {
"use_smart_sending": true
}
}
}
]
}
#### Get a Campaign
GET /klaviyo/api/campaigns/{campaign_id}
#### Create a Campaign
POST /klaviyo/api/campaigns
Example:
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'data': {'type': 'campaign', 'attributes': {'name': 'Summer Newsletter', 'audiences': {'included': ['Y6nRLr']}, 'campaign-messages': {'data': [{'type': 'campaign-message', 'attributes': {'channel': 'email'}}]}}}}).encode()
req = urllib.request.Request('https://api.maton.ai/klaviyo/api/campaigns', data=data, method='POST')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
req.add_header('revision', '2026-01-15')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
#### Update a Campaign
PATCH /klaviyo/api/campaigns/{campaign_id}
#### Delete a Campaign
DELETE /klaviyo/api/campaigns/{campaign_id}
#### Send a Campaign
POST /klaviyo/api/campaign-send-jobs
#### Get Recipient Estimation
POST /klaviyo/api/campaign-recipient-estimations
Flows
Build automated customer journeys.
#### Get Flows
GET /klaviyo/api/flows
Example:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/klaviyo/api/flows?fields[flow]=name,status,created,updated')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('revision', '2026-01-15')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Response:
{
"data": [
{
"type": "flow",
"id": "VJvBNr",
"attributes": {
"name": "Welcome Series",
"status": "live",
"created": "2024-01-10T08:00:00Z",
"updated": "2024-02-15T12:30:00Z"
}
}
]
}
#### Get a Flow
GET /klaviyo/api/flows/{flow_id}
#### Create a Flow
POST /klaviyo/api/flows
> Note: Flow creation via API may be limited. Flows are typically created through the Klaviyo UI, then managed via API. Use GET, PATCH, and DELETE operations for existing flows.
#### Update Flow Status
PATCH /klaviyo/api/flows/{flow_id}
Example:
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'data': {'type': 'flow', 'id': 'VJvBNr', 'attributes': {'status': 'draft'}}}).encode()
req = urllib.request.Request('https://api.maton.ai/klaviyo/api/flows/VJvBNr', data=data, method='PATCH')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
req.add_header('revision', '2026-01-15')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
#### Delete a Flow
DELETE /klaviyo/api/flows/{flow_id}
#### Get Flow Actions
GET /klaviyo/api/flows/{flow_id}/flow-actions
#### Get Flow Messages
GET /klaviyo/api/flows/{flow_id}/flow-messages
Events
Track customer interactions and behaviors.
#### Get Events
GET /klaviyo/api/events
Query parameters:
filter - Filter events (e.g., filter=equals(metric_id,"ABC123"))fields[event] - Fields to includesort - Sort by field (default: -datetime)Example:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/klaviyo/api/events?filter=greater-than(datetime,2024-01-01T00:00:00Z)&page[size]=50')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('revision', '2026-01-15')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Response:
{
"data": [
{
"type": "event",
"id": "4vRpBT",
"attributes": {
"metric_id": "TxVpCr",
"profile_id": "01GDDKASAP8TKDDA2GRZDSVP4H",
"datetime": "2024-03-15T14:30:00Z",
"event_properties": {
"value": 99.99,
"product_name": "Running Shoes"
}
}
}
]
}
#### Get an Event
GET /klaviyo/api/events/{event_id}
#### Create an Event
POST /klaviyo/api/events
Example:
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'data': {'type': 'event', 'attributes': {'profile': {'data': {'type': 'profile', 'attributes': {'email': 'customer@example.com'}}}, 'metric': {'data': {'type': 'metric', 'attributes': {'name': 'Viewed Product'}}}, 'properties': {'product_id': 'SKU123', 'product_name': 'Blue T-Shirt', 'price': 29.99}}}}).encode()
req = urllib.request.Request('https://api.maton.ai/klaviyo/api/events', data=data, method='POST')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
req.add_header('revision', '2026-01-15')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
#### Bulk Create Events
POST /klaviyo/api/event-bulk-create-jobs
Metrics
Access performance data and analytics.
#### Get Metrics
GET /klaviyo/api/metrics
Example:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/klaviyo/api/metrics')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('revision', '2026-01-15')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Response:
{
"data": [
{
"type": "metric",
"id": "TxVpCr",
"attributes": {
"name": "Placed Order",
"created": "2024-01-01T00:00:00Z",
"updated": "2024-03-01T00:00:00Z",
"integration": {
"object": "integration",
"id": "shopify",
"name": "Shopify"
}
}
}
]
}
#### Get a Metric
GET /klaviyo/api/metrics/{metric_id}
#### Query Metric Aggregates
POST /klaviyo/api/metric-aggregates
Example:
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'data': {'type': 'metric-aggregate', 'attributes': {'metric_id': 'TxVpCr', 'measurements': ['count', 'sum_value'], 'interval': 'day', 'filter': ['greater-or-equal(datetime,2024-01-01)', 'less-than(datetime,2024-04-01)']}}}).encode()
req = urllib.request.Request('https://api.maton.ai/klaviyo/api/metric-aggregates', data=data, method='POST')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
req.add_header('revision', '2026-01-15')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Templates
Manage email templates.
#### Get Templates
GET /klaviyo/api/templates
Example:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/klaviyo/api/templates?fields[template]=name,created,updated')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('revision', '2026-01-15')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
#### Get a Template
GET /klaviyo/api/templates/{template_id}
#### Create a Template
POST /klaviyo/api/templates
Example:
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'data': {'type': 'template', 'attributes': {'name': 'Welcome Email', 'editor_type': 'CODE', 'html': 'Welcome!
'}}}).encode()
req = urllib.request.Request('https://api.maton.ai/klaviyo/api/templates', data=data, method='POST')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
req.add_header('revision', '2026-01-15')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
#### Update a Template
PATCH /klaviyo/api/templates/{template_id}
#### Delete a Template
DELETE /klaviyo/api/templates/{template_id}
#### Render a Template
POST /klaviyo/api/template-render
#### Clone a Template
POST /klaviyo/api/template-clone
Catalogs
Manage product catalogs.
#### Get Catalog Items
GET /klaviyo/api/catalog-items
Example:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/klaviyo/api/catalog-items?fields[catalog-item]=title,price,url')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('revision', '2026-01-15')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Response:
{
"data": [
{
"type": "catalog-item",
"id": "$custom:::$default:::PROD-001",
"attributes": {
"title": "Blue Running Shoes",
"price": 129.99,
"url": "https://store.example.com/products/blue-running-shoes"
}
}
]
}
#### Get a Catalog Item
GET /klaviyo/api/catalog-items/{catalog_item_id}
#### Create Catalog Items
POST /klaviyo/api/catalog-items
#### Update Catalog Item
PATCH /klaviyo/api/catalog-items/{catalog_item_id}
#### Delete Catalog Item
DELETE /klaviyo/api/catalog-items/{catalog_item_id}
#### Get Catalog Variants
GET /klaviyo/api/catalog-variants
#### Get Catalog Categories
GET /klaviyo/api/catalog-categories
Tags
Organize resources with tags.
#### Get Tags
GET /klaviyo/api/tags
Example:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/klaviyo/api/tags')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('revision', '2026-01-15')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
#### Create a Tag
POST /klaviyo/api/tags
#### Update a Tag
PATCH /klaviyo/api/tags/{tag_id}
#### Delete a Tag
DELETE /klaviyo/api/tags/{tag_id}
#### Tag a Campaign
POST /klaviyo/api/tag-campaign-relationships
#### Tag a Flow
POST /klaviyo/api/tag-flow-relationships
#### Get Tag Groups
GET /klaviyo/api/tag-groups
Example:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/klaviyo/api/tag-groups')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('revision', '2026-01-15')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
#### Create Tag Group
POST /klaviyo/api/tag-groups
#### Update Tag Group
PATCH /klaviyo/api/tag-groups/{tag_group_id}
#### Delete Tag Group
DELETE /klaviyo/api/tag-groups/{tag_group_id}
Coupons
Manage discount codes.
#### Get Coupons
GET /klaviyo/api/coupons
#### Create a Coupon
POST /klaviyo/api/coupons
Example:
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'data': {'type': 'coupon', 'attributes': {'external_id': 'SUMMER_SALE_2024', 'description': 'Summer sale discount coupon'}}}).encode()
req = urllib.request.Request('https://api.maton.ai/klaviyo/api/coupons', data=data, method='POST')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
req.add_header('revision', '2026-01-15')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
> Note: The external_id must match regex ^[0-9_A-z]+$ (alphanumeric and underscores only, no hyphens).
#### Get Coupon Codes
GET /klaviyo/api/coupon-codes
> Note: This endpoint requires a filter parameter. You must filter by coupon ID or profile ID.
Example:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/klaviyo/api/coupon-codes?filter=equals(coupon.id,"SUMMER_SALE_2024")')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('revision', '2026-01-15')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
#### Create Coupon Codes
POST /klaviyo/api/coupon-codes
Example:
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'data': {'type': 'coupon-code', 'attributes': {'unique_code': 'SAVE20NOW', 'expires_at': '2025-12-31T23:59:59Z'}, 'relationships': {'coupon': {'data': {'type': 'coupon', 'id': 'SUMMER_SALE_2024'}}}}}).encode()
req = urllib.request.Request('https://api.maton.ai/klaviyo/api/coupon-codes', data=data, method='POST')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
req.add_header('revision', '2026-01-15')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Webhooks
Configure event notifications.
#### Get Webhooks
GET /klaviyo/api/webhooks
Example:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/klaviyo/api/webhooks')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('revision', '2026-01-15')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
#### Create Webhook
POST /klaviyo/api/webhooks
Example:
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'data': {'type': 'webhook', 'attributes': {'name': 'Order Placed Webhook', 'endpoint_url': 'https://example.com/webhooks/klaviyo', 'enabled': True}, 'relationships': {'webhook-topics': {'data': [{'type': 'webhook-topic', 'id': 'campaign:sent'}]}}}}).encode()
req = urllib.request.Request('https://api.maton.ai/klaviyo/api/webhooks', data=data, method='POST')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
req.add_header('revision', '2026-01-15')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
#### Get a Webhook
GET /klaviyo/api/webhooks/{webhook_id}
#### Update a Webhook
PATCH /klaviyo/api/webhooks/{webhook_id}
#### Delete a Webhook
DELETE /klaviyo/api/webhooks/{webhook_id}
#### Get Webhook Topics
GET /klaviyo/api/webhook-topics
Accounts
Retrieve account information.
#### Get Accounts
GET /klaviyo/api/accounts
Example:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/klaviyo/api/accounts')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('revision', '2026-01-15')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Images
Manage uploaded images.
#### Get Images
GET /klaviyo/api/images
Example:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/klaviyo/api/images')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('revision', '2026-01-15')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
#### Get an Image
GET /klaviyo/api/images/{image_id}
#### Upload Image from URL
POST /klaviyo/api/images
Example:
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'data': {'type': 'image', 'attributes': {'import_from_url': 'https://example.com/image.jpg', 'name': 'Product Image'}}}).encode()
req = urllib.request.Request('https://api.maton.ai/klaviyo/api/images', data=data, method='POST')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
req.add_header('revision', '2026-01-15')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Forms
Manage signup forms.
#### Get Forms
GET /klaviyo/api/forms
Example:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/klaviyo/api/forms')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('revision', '2026-01-15')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
#### Get a Form
GET /klaviyo/api/forms/{form_id}
#### Get Form Versions
GET /klaviyo/api/forms/{form_id}/form-versions
Reviews
Manage product reviews.
#### Get Reviews
GET /klaviyo/api/reviews
Example:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/klaviyo/api/reviews')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('revision', '2026-01-15')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
#### Get a Review
GET /klaviyo/api/reviews/{review_id}
#### Update Review
PATCH /klaviyo/api/reviews/{review_id}
Universal Content
Manage reusable email content blocks.
#### Get Universal Content
GET /klaviyo/api/template-universal-content
Example:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/klaviyo/api/template-universal-content')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('revision', '2026-01-15')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
#### Create Universal Content
POST /klaviyo/api/template-universal-content
#### Update Universal Content
PATCH /klaviyo/api/template-universal-content/{content_id}
#### Delete Universal Content
DELETE /klaviyo/api/template-universal-content/{content_id}
Bulk Profile Subscriptions
Manage email/SMS subscriptions in bulk.
#### Bulk Subscribe Profiles
POST /klaviyo/api/profile-subscription-bulk-create-jobs
Example:
python <<'EOF'
import urllib.request, os, json
data = json.dumps({
'data': {
'type': 'profile-subscription-bulk-create-job',
'attributes': {
'profiles': {
'data': [{
'type': 'profile',
'attributes': {
'email': 'newsubscriber@example.com',
'subscriptions': {
'email': {'marketing': {'consent': 'SUBSCRIBED'}}
}
}
}]
}
},
'relationships': {
'list': {'data': {'type': 'list', 'id': 'LIST_ID'}}
}
}
}).encode()
req = urllib.request.Request('https://api.maton.ai/klaviyo/api/profile-subscription-bulk-create-jobs', data=data, method='POST')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
req.add_header('revision', '2026-01-15')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
#### Bulk Unsubscribe Profiles
POST /klaviyo/api/profile-subscription-bulk-delete-jobs
#### Bulk Suppress Profiles
POST /klaviyo/api/profile-suppression-bulk-create-jobs
#### Bulk Unsuppress Profiles
POST /klaviyo/api/profile-suppression-bulk-delete-jobs
Profile Bulk Import
Import profiles in bulk.
#### Get Bulk Import Jobs
GET /klaviyo/api/profile-bulk-import-jobs
Example:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/klaviyo/api/profile-bulk-import-jobs')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('revision', '2026-01-15')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
#### Create Bulk Import Job
POST /klaviyo/api/profile-bulk-import-jobs
Filtering
Klaviyo uses JSON:API filtering syntax. Common operators:
| Operator | Example |
|----------|---------|
| equals | filter=equals(email,"test@example.com") |
| contains | filter=contains(name,"newsletter") |
| greater-than | filter=greater-than(datetime,2024-01-01T00:00:00Z) |
| less-than | filter=less-than(created,2024-03-01) |
| greater-or-equal | filter=greater-or-equal(updated,2024-01-01) |
| any | filter=any(status,["draft","scheduled"]) |
Combine filters with and:
filter=and(equals(status,"active"),greater-than(created,2024-01-01))
Pagination
Klaviyo uses cursor-based pagination:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/klaviyo/api/profiles?page[size]=50&page[cursor]=CURSOR_TOKEN')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('revision', '2026-01-15')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Response includes pagination links:
{
"data": [...],
"links": {
"self": "https://a.klaviyo.com/api/profiles",
"next": "https://a.klaviyo.com/api/profiles?page[cursor]=WzE2..."
}
}
Sparse Fieldsets
Request only specific fields to reduce response size:
# Request only email and first_name for profiles
?fields[profile]=email,first_nameRequest specific fields for included relationships
?include=lists&fields[list]=name,created
Code Examples
JavaScript
const response = await fetch(
'https://api.maton.ai/klaviyo/api/profiles?fields[profile]=email,first_name',
{
headers: {
'Authorization': Bearer ${process.env.MATON_API_KEY},
'revision': '2024-10-15'
}
}
);
const data = await response.json();
Python
import os
import requestsresponse = requests.get(
'https://api.maton.ai/klaviyo/api/profiles',
headers={
'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}',
'revision': '2024-10-15'
},
params={'fields[profile]': 'email,first_name'}
)
data = response.json()
Notes
2024-01-16T23:20:50.52Z)revision header for API versioning (recommended: 2026-01-15)200 instead of 201 for successful creationexternal_id must match regex ^[0-9_A-z]+$ (no hyphens)filter=equals(coupon.id,"..."))curl -g when URLs contain brackets (fields[], page[]) to disable glob parsingjq or other commands, environment variables like $MATON_API_KEY may not expand correctly in some shell environments. You may get "Invalid API key" errors when piping.Error Handling
| Status | Meaning | |--------|---------| | 400 | Bad request or missing Klaviyo connection | | 401 | Invalid or missing Maton API key | | 403 | Forbidden - insufficient permissions | | 404 | Resource not found | | 429 | Rate limited (fixed-window algorithm) | | 4xx/5xx | Passthrough error from Klaviyo API |
Troubleshooting: API Key Issues
1. Check that the MATON_API_KEY environment variable is set:
echo $MATON_API_KEY
2. Verify the API key is valid by listing connections:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/connections')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Troubleshooting: Invalid App Name
1. Ensure your URL path starts with klaviyo. For example:
https://api.maton.ai/klaviyo/api/profileshttps://api.maton.ai/api/profilesResources
π‘ Examples
# List profiles
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/klaviyo/api/profiles')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('revision', '2026-01-15')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
π Tips & Best Practices
2024-01-16T23:20:50.52Z)revision header for API versioning (recommended: 2026-01-15)200 instead of 201 for successful creationexternal_id must match regex ^[0-9_A-z]+$ (no hyphens)filter=equals(coupon.id,"..."))curl -g when URLs contain brackets (fields[], page[]) to disable glob parsingjq or other commands, environment variables like $MATON_API_KEY may not expand correctly in some shell environments. You may get "Invalid API key" errors when piping.