API Documentation
Introduction
The framer.art API lets you programmatically upload, list, update, and delete images in your workspace. It's available to workspaces on a paid plan.
All endpoints are relative to the base URL:
https://www.framer.artRequest and response bodies are JSON. Send Content-Type: application/json with any request that includes a body.
Authentication
Every request must include an API key as a bearer token in the Authorization header:
Authorization: Bearer <YOUR_API_KEY>Generate an API key from your workspace's API Access page (/workspaces/:workspace_uid/api-access), available on paid plans. Keys are shown once at creation time, so save it somewhere safe.
Images
GET/api/v1/imageList the images in your workspace, most recently created first. Only images with status published or draft are returned.
Query Parameters
| Field | Type | Required | Description |
|---|---|---|---|
skip | number | No | Number of images to skip. Defaults to 0. |
limit | number | No | Maximum number of images to return. Defaults to 100. |
googlePlaceId | string | No | Filter to images whose location matches this Google Place ID. |
sku | number | No | Filter to the image with this exact sku. |
externalId | string | No | Filter to the image with this exact externalId. |
search | string | No | Free-text search, matched the same way as search on framer.art storefronts: against the title, description, SEO title and the location's name, city, state and country. Multiple words are matched as a phrase, e.g. east village. Combines with the other filters and with paging. |
Each image has a 3000pxImageSrc (the full-size file) and a thumbnailImageSrc (400px wide), which is better for grids and pickers.
Example Request
curl "https://www.framer.art/api/v1/image?limit=100&skip=0" \
-H "Authorization: Bearer <YOUR_API_KEY>"curl "https://www.framer.art/api/v1/image?search=east%20village&limit=24" \
-H "Authorization: Bearer <YOUR_API_KEY>"Example Response
{
"count": 214,
"result": [
{
"id": "a3f5c8e2-4b91-4d67-9a12-8e3f6c7d1b45",
"sku": 48213,
"createdAt": "2026-08-20T14:32:00.000Z",
"publishedAt": "2026-08-20T14:33:10.000Z",
"location": {
"name": "Golden Gate Bridge",
"city": "San Francisco",
"state": "California",
"country": "United States",
"postalCode": "94129",
"latitude": 37.8199,
"longitude": -122.4783,
"googlePlaceId": "ChIJw____96GhYARCVVwg5cT7c0"
},
"monochrome": false,
"orientation": "vertical",
"artType": "Photography",
"artist": "Ansel Adams",
"externalId": "golden-gate-001",
"googleAdsCustomLabel": "high-margin",
"title": "Golden Gate at Sunset",
"description": "A photo of the Golden Gate Bridge taken at sunset.",
"status": "published",
"sourceImages": [
{ "id": "7c2e9a41-6f38-4d5c-b2a9-1e4f8d3c6a90", "width": 2000, "height": 3000 }
],
"3000pxImageSrc": "https://cdn.framer.art/3000px/golden-gate-at-sunset-abc123.jpg",
"thumbnailImageSrc": "https://cdn.framer.art/400px/golden-gate-at-sunset-abc123.jpg",
"keywords": ["Bridge", "Sunset", "San Francisco"],
"slug": "golden-gate-at-sunset-abc123"
}
]
}GET/api/v1/image/:idGet a single image by its id.
Example Request
curl "https://www.framer.art/api/v1/image/a3f5c8e2-4b91-4d67-9a12-8e3f6c7d1b45" \
-H "Authorization: Bearer <YOUR_API_KEY>"Example Response
{
"id": "a3f5c8e2-4b91-4d67-9a12-8e3f6c7d1b45",
"sku": 48213,
"createdAt": "2026-08-20T14:32:00.000Z",
"publishedAt": "2026-08-20T14:33:10.000Z",
"location": {
"name": "Golden Gate Bridge",
"city": "San Francisco",
"state": "California",
"country": "United States",
"postalCode": "94129",
"latitude": 37.8199,
"longitude": -122.4783,
"googlePlaceId": "ChIJw____96GhYARCVVwg5cT7c0"
},
"monochrome": false,
"orientation": "vertical",
"artType": "Photography",
"artist": "Ansel Adams",
"externalId": "golden-gate-001",
"googleAdsCustomLabel": "high-margin",
"title": "Golden Gate at Sunset",
"description": "A photo of the Golden Gate Bridge taken at sunset.",
"status": "published",
"sourceImages": [
{ "id": "7c2e9a41-6f38-4d5c-b2a9-1e4f8d3c6a90", "width": 2000, "height": 3000 }
],
"3000pxImageSrc": "https://cdn.framer.art/3000px/golden-gate-at-sunset-abc123.jpg",
"thumbnailImageSrc": "https://cdn.framer.art/400px/golden-gate-at-sunset-abc123.jpg",
"keywords": ["Bridge", "Sunset", "San Francisco"],
"slug": "golden-gate-at-sunset-abc123"
}Returns 404 if no image with that ID exists in your workspace. See Errors.
POST/api/v1/imageUpload a new image.
Body
| Field | Type | Required | Description |
|---|---|---|---|
title | string | Yes | The image's title. |
description | string | Yes | The image's description. |
keywords | string[] | Yes | Keywords/subjects for the image. Can be an empty array. |
artType | string | Yes | One of: Photography, Painting, Illustration, Collage, Digital Art, Mixed Media, Drawing, Typography, Maps, Screen Print, Line Drawing, Woodblock, Linocut. |
artist | string | No | The artist associated with the image. Omitted or empty stores as null. |
externalId | string | No | Your own unique ID for the image, unique within your workspace. Omitted or empty stores as null. |
googleAdsCustomLabel | string | No | Custom label surfaced as custom_label_4 in the Google product feed. Up to 100 characters. Omitted or empty stores as null. |
status | "draft" | "published" | No | Use "draft" to save the image as a draft that you publish later. Omitted or "published" publishes the image immediately if a source image has a supported aspect ratio; otherwise it is saved as a draft. The response status shows the result. |
sourceImages | { url: string }[] | Yes | One or more source images, each given as a URL to fetch. All source images must share the same orientation. |
monochrome | boolean | No | Whether the image is black & white. |
collections | { slug: string, name: string }[] | No | Collections to add the image to. Collections that don't yet exist in your workspace are created. |
location | object | No | Where the image was taken. All fields optional: name, city, state, country, postalCode, latitude, longitude, googlePlaceId. |
Example Request
curl -X POST "https://www.framer.art/api/v1/image" \
-H "Authorization: Bearer <YOUR_API_KEY>" \
-H "Content-Type: application/json" \
-d '{
"title": "Golden Gate at Sunset",
"description": "A photo of the Golden Gate Bridge taken at sunset.",
"artType": "Photography",
"monochrome": false,
"keywords": ["Bridge", "Sunset", "San Francisco"],
"collections": [{ "slug": "bay-area", "name": "Bay Area" }],
"location": {
"name": "Golden Gate Bridge",
"city": "San Francisco",
"state": "California",
"country": "United States"
},
"sourceImages": [{ "url": "https://picsum.photos/2000/3000" }]
}'Example Response
{
"id": "a3f5c8e2-4b91-4d67-9a12-8e3f6c7d1b45",
"sku": 48213,
"createdAt": "2026-08-20T14:32:00.000Z",
"publishedAt": "2026-08-20T14:33:10.000Z",
"location": {
"name": "Golden Gate Bridge",
"city": "San Francisco",
"state": "California",
"country": "United States",
"postalCode": "94129",
"latitude": 37.8199,
"longitude": -122.4783,
"googlePlaceId": "ChIJw____96GhYARCVVwg5cT7c0"
},
"monochrome": false,
"orientation": "vertical",
"artType": "Photography",
"artist": "Ansel Adams",
"externalId": "golden-gate-001",
"googleAdsCustomLabel": "high-margin",
"title": "Golden Gate at Sunset",
"description": "A photo of the Golden Gate Bridge taken at sunset.",
"status": "published",
"sourceImages": [
{ "id": "7c2e9a41-6f38-4d5c-b2a9-1e4f8d3c6a90", "width": 2000, "height": 3000 }
],
"3000pxImageSrc": "https://cdn.framer.art/3000px/golden-gate-at-sunset-abc123.jpg",
"thumbnailImageSrc": "https://cdn.framer.art/400px/golden-gate-at-sunset-abc123.jpg",
"keywords": ["Bridge", "Sunset", "San Francisco"],
"slug": "golden-gate-at-sunset-abc123"
}New images are created with status processing while their mockups are generated, then move to published once ready (or draft if the source images can't produce a mockup yet).
PATCH/api/v1/image/:idUpdate fields on an existing image. All fields are optional: only send what you want to change. keywords, collections, and sourceImages use an add/remove shape rather than replacing the whole list.
Body
| Field | Type | Required | Description |
|---|---|---|---|
title | string | No | The image's title. |
description | string | No | The image's description. |
artType | string | No | One of: Photography, Painting, Illustration, Collage, Digital Art, Mixed Media, Drawing, Typography, Maps, Screen Print, Line Drawing, Woodblock, Linocut. |
artist | string | No | The artist associated with the image. Omit to leave unchanged; empty string clears it. |
externalId | string | No | Your own unique ID for the image. Omit to leave unchanged; empty string clears it. Must be unique within your workspace. |
googleAdsCustomLabel | string | No | Custom label surfaced as custom_label_4 in the Google product feed. Up to 100 characters. Omit to leave unchanged; empty string clears it. |
status | "published" | No | Publishes a draft image in your workspace after the other changes in the request are applied. The image needs a title, a description, and a source image with a supported aspect ratio; otherwise the request returns 400, and the other changes in the request are still saved. Images that are processing or removed cannot be published. Has no effect on an image that is already published. Images cannot be unpublished through the API. |
monochrome | boolean | No | Whether the image is black & white. |
keywords.add | string[] | No | Keywords to add. |
keywords.remove | string[] | No | Keywords to remove. |
collections.add | { slug: string, name: string }[] | No | Collections to add the image to, created if they don't yet exist. |
collections.remove | { slug: string }[] | No | Collections to remove the image from. |
sourceImages.add | { url: string }[] | No | Additional source images to add, given as URLs. Must share the same orientation as the image's existing source images. There's no way to remove a source image via the API. |
location | object | No | Replaces the image's location entirely. Same shape as on create. |
Example Request
curl -X PATCH "https://www.framer.art/api/v1/image/a3f5c8e2-4b91-4d67-9a12-8e3f6c7d1b45" \
-H "Authorization: Bearer <YOUR_API_KEY>" \
-H "Content-Type: application/json" \
-d '{
"title": "Golden Gate at Golden Hour",
"keywords": { "add": ["Golden Hour"], "remove": ["Sunset"] }
}'Example Response
{
"id": "a3f5c8e2-4b91-4d67-9a12-8e3f6c7d1b45",
"sku": 48213,
"createdAt": "2026-08-20T14:32:00.000Z",
"publishedAt": "2026-08-20T14:33:10.000Z",
"location": {
"name": "Golden Gate Bridge",
"city": "San Francisco",
"state": "California",
"country": "United States",
"postalCode": "94129",
"latitude": 37.8199,
"longitude": -122.4783,
"googlePlaceId": "ChIJw____96GhYARCVVwg5cT7c0"
},
"monochrome": false,
"orientation": "vertical",
"artType": "Photography",
"artist": "Ansel Adams",
"externalId": "golden-gate-001",
"googleAdsCustomLabel": "high-margin",
"title": "Golden Gate at Sunset",
"description": "A photo of the Golden Gate Bridge taken at sunset.",
"status": "published",
"sourceImages": [
{ "id": "7c2e9a41-6f38-4d5c-b2a9-1e4f8d3c6a90", "width": 2000, "height": 3000 }
],
"3000pxImageSrc": "https://cdn.framer.art/3000px/golden-gate-at-sunset-abc123.jpg",
"thumbnailImageSrc": "https://cdn.framer.art/400px/golden-gate-at-sunset-abc123.jpg",
"keywords": ["Bridge", "Sunset", "San Francisco"],
"slug": "golden-gate-at-sunset-abc123"
}Returns 404 if no image with that ID exists in your workspace. See Errors.
DELETE/api/v1/image/:idDelete an image from your workspace. Deleted images no longer appear in list/get requests, storefronts, or marketplaces.
Example Request
curl -X DELETE "https://www.framer.art/api/v1/image/a3f5c8e2-4b91-4d67-9a12-8e3f6c7d1b45" \
-H "Authorization: Bearer <YOUR_API_KEY>"Example Response
The response body is null.
nullReturns 404 if no image with that ID exists in your workspace. See Errors.
Storefronts
Storefront endpoints are read-only. Use the returned id as the storefrontId path segment for storefront-scoped resources such as discounts, pages, and email flows.
GET/api/v1/storefront| Field | Type | Required | Description |
|---|---|---|---|
skip | integer | No | Number of storefronts to skip. |
limit | integer | No | Maximum number to return (up to 100). |
Example Request
curl "https://www.framer.art/api/v1/storefront?limit=20" \
+ -H "Authorization: Bearer <YOUR_API_KEY>"Example Response
[{ "id": "<STOREFRONT_ID>", "uid": "gallery", "name": "My Gallery", "domain": null, "apexDomain": "example.com", "email": "hello@example.com" }]GET/api/v1/storefront/:idReturns one storefront belonging to the authenticated workspace.
Example Request
curl "https://www.framer.art/api/v1/storefront/<STOREFRONT_ID>" \
+ -H "Authorization: Bearer <YOUR_API_KEY>"Example Response
{ "id": "<STOREFRONT_ID>", "uid": "gallery", "name": "My Gallery", "domain": null, "apexDomain": "example.com", "email": "hello@example.com" }Discounts
Discounts are scoped to a storefront. Supported types are coupon_code and email_subscribe. value is a decimal percentage of price: 0.15 means 15% off. The dashboard Percentage field shows 15 for that same stored value.
GET/api/v1/storefront/:storefrontId/discountList discounts. Query fields: skip, limit.
| Field | Type | Required | Description |
|---|---|---|---|
code | string | null | No | Coupon code; required for coupon-code discounts. |
type | string | No | coupon_code or email_subscribe. |
value | number | No | Decimal percentage of the price (0.15 = 15% off). Same storage as the dashboard Percentage field, which displays 15 for 0.15. |
start | ISO date | No | Start date. |
end | ISO date | null | No | Optional end date. |
enabled | boolean | No | Whether the discount is active. |
title / subtitle | string | null | No | Optional customer-facing copy. |
includeInGoogleMerchantPromotionsFeed | boolean | No | If true, include this discount in the Google Merchant Center promotions feed. Defaults to false. |
Example Request
curl -X GET "https://www.framer.art/api/v1/storefront/<STOREFRONT_ID>/discount" \
+ -H "Authorization: Bearer <YOUR_API_KEY>" -H "Content-Type: application/json"Example Response
{ "id": "<DISCOUNT_ID>", "code": "SAVE20", "type": "coupon_code", "value": 0.2, "start": "2026-09-12T00:00:00.000Z", "end": null, "enabled": true, "title": null, "subtitle": null, "includeInGoogleMerchantPromotionsFeed": false }POST/api/v1/storefront/:storefrontId/discountCreate a discount.
| Field | Type | Required | Description |
|---|---|---|---|
code | string | null | No | Coupon code; required for coupon-code discounts. |
type | string | No | coupon_code or email_subscribe. |
value | number | No | Decimal percentage of the price (0.15 = 15% off). Same storage as the dashboard Percentage field, which displays 15 for 0.15. |
start | ISO date | No | Start date. |
end | ISO date | null | No | Optional end date. |
enabled | boolean | No | Whether the discount is active. |
title / subtitle | string | null | No | Optional customer-facing copy. |
includeInGoogleMerchantPromotionsFeed | boolean | No | If true, include this discount in the Google Merchant Center promotions feed. Defaults to false. |
Example Request
curl -X POST "https://www.framer.art/api/v1/storefront/<STOREFRONT_ID>/discount" \
+ -H "Authorization: Bearer <YOUR_API_KEY>" -H "Content-Type: application/json" \
+ -d '{ "code": "SAVE20", "type": "coupon_code", "value": 0.2, "start": "2026-09-12T00:00:00Z", "enabled": true, "includeInGoogleMerchantPromotionsFeed": false }'Example Response
{ "id": "<DISCOUNT_ID>", "code": "SAVE20", "type": "coupon_code", "value": 0.2, "start": "2026-09-12T00:00:00.000Z", "end": null, "enabled": true, "title": null, "subtitle": null, "includeInGoogleMerchantPromotionsFeed": false }GET/api/v1/storefront/:storefrontId/discount/:idGet a discount.
| Field | Type | Required | Description |
|---|---|---|---|
code | string | null | No | Coupon code; required for coupon-code discounts. |
type | string | No | coupon_code or email_subscribe. |
value | number | No | Decimal percentage of the price (0.15 = 15% off). Same storage as the dashboard Percentage field, which displays 15 for 0.15. |
start | ISO date | No | Start date. |
end | ISO date | null | No | Optional end date. |
enabled | boolean | No | Whether the discount is active. |
title / subtitle | string | null | No | Optional customer-facing copy. |
includeInGoogleMerchantPromotionsFeed | boolean | No | If true, include this discount in the Google Merchant Center promotions feed. Defaults to false. |
Example Request
curl -X GET "https://www.framer.art/api/v1/storefront/<STOREFRONT_ID>/discount/<DISCOUNT_ID>" \
+ -H "Authorization: Bearer <YOUR_API_KEY>" -H "Content-Type: application/json"Example Response
{ "id": "<DISCOUNT_ID>", "code": "SAVE20", "type": "coupon_code", "value": 0.2, "start": "2026-09-12T00:00:00.000Z", "end": null, "enabled": true, "title": null, "subtitle": null, "includeInGoogleMerchantPromotionsFeed": false }PATCH/api/v1/storefront/:storefrontId/discount/:idUpdate any supplied discount fields.
| Field | Type | Required | Description |
|---|---|---|---|
code | string | null | No | Coupon code; required for coupon-code discounts. |
type | string | No | coupon_code or email_subscribe. |
value | number | No | Decimal percentage of the price (0.15 = 15% off). Same storage as the dashboard Percentage field, which displays 15 for 0.15. |
start | ISO date | No | Start date. |
end | ISO date | null | No | Optional end date. |
enabled | boolean | No | Whether the discount is active. |
title / subtitle | string | null | No | Optional customer-facing copy. |
includeInGoogleMerchantPromotionsFeed | boolean | No | If true, include this discount in the Google Merchant Center promotions feed. Defaults to false. |
Example Request
curl -X PATCH "https://www.framer.art/api/v1/storefront/<STOREFRONT_ID>/discount/<DISCOUNT_ID>" \
+ -H "Authorization: Bearer <YOUR_API_KEY>" -H "Content-Type: application/json"Example Response
{ "id": "<DISCOUNT_ID>", "code": "SAVE20", "type": "coupon_code", "value": 0.2, "start": "2026-09-12T00:00:00.000Z", "end": null, "enabled": true, "title": null, "subtitle": null, "includeInGoogleMerchantPromotionsFeed": false }Pages
Storefront pages use ContentEditorV2 content blocks. Write image references as { "kind": "catalog", "imageId": "<IMAGE_ID>" } (must belong to this workspace) or { "kind": "upload", "url": "https://..." } (fetched server-side; JPEG/PNG/WebP, max 10MB). Stored shape for uploads is { "kind": "upload", "assetId": "<UUID>" }. uid must be unique within the storefront. Setting the storefront home page is not available via this API.
Page-safe block type values: hero, gallery, image, text, heading, embed, divider, button, columns, cardGrid, products, sellingPoints, about, reviews.
GET/api/v1/storefront/:storefrontId/pageList pages. Query fields: skip, limit.
| Field | Type | Required | Description |
|---|---|---|---|
title | string | No | Page title. |
uid | string | No | URL slug; unique within the storefront. |
content | block[] | No | Ordered ContentEditorV2 blocks (page-safe catalog). |
fullWidth | boolean | No | Full-width layout. Defaults to false. |
showBreadcrumb | boolean | No | Show breadcrumb. Defaults to true. |
showPageTitle | boolean | No | Show page title. Defaults to true. |
Example Request
curl -X GET "https://www.framer.art/api/v1/storefront/<STOREFRONT_ID>/page" \
-H "Authorization: Bearer <YOUR_API_KEY>" -H "Content-Type: application/json"Example Response
{ "id": "<PAGE_ID>", "title": "About", "uid": "about", "fullWidth": false, "showBreadcrumb": true, "showPageTitle": true, "content": [{ "id": "<BLOCK_ID>", "type": "heading", "text": "About us" }], "contentVersion": 2, "storefrontId": "<STOREFRONT_ID>" }POST/api/v1/storefront/:storefrontId/pageCreate a page.
| Field | Type | Required | Description |
|---|---|---|---|
title | string | No | Page title. |
uid | string | No | URL slug; unique within the storefront. |
content | block[] | No | Ordered ContentEditorV2 blocks (page-safe catalog). |
fullWidth | boolean | No | Full-width layout. Defaults to false. |
showBreadcrumb | boolean | No | Show breadcrumb. Defaults to true. |
showPageTitle | boolean | No | Show page title. Defaults to true. |
Example Request
curl -X POST "https://www.framer.art/api/v1/storefront/<STOREFRONT_ID>/page" \
-H "Authorization: Bearer <YOUR_API_KEY>" -H "Content-Type: application/json" \
-d '{ "title": "About", "uid": "about", "content": [{ "id": "<BLOCK_ID>", "type": "heading", "text": "About us" }] }'Example Response
{ "id": "<PAGE_ID>", "title": "About", "uid": "about", "fullWidth": false, "showBreadcrumb": true, "showPageTitle": true, "content": [{ "id": "<BLOCK_ID>", "type": "heading", "text": "About us" }], "contentVersion": 2, "storefrontId": "<STOREFRONT_ID>" }GET/api/v1/storefront/:storefrontId/page/:idGet a page.
| Field | Type | Required | Description |
|---|---|---|---|
title | string | No | Page title. |
uid | string | No | URL slug; unique within the storefront. |
content | block[] | No | Ordered ContentEditorV2 blocks (page-safe catalog). |
fullWidth | boolean | No | Full-width layout. Defaults to false. |
showBreadcrumb | boolean | No | Show breadcrumb. Defaults to true. |
showPageTitle | boolean | No | Show page title. Defaults to true. |
Example Request
curl -X GET "https://www.framer.art/api/v1/storefront/<STOREFRONT_ID>/page/<PAGE_ID>" \
-H "Authorization: Bearer <YOUR_API_KEY>" -H "Content-Type: application/json"Example Response
{ "id": "<PAGE_ID>", "title": "About", "uid": "about", "fullWidth": false, "showBreadcrumb": true, "showPageTitle": true, "content": [{ "id": "<BLOCK_ID>", "type": "heading", "text": "About us" }], "contentVersion": 2, "storefrontId": "<STOREFRONT_ID>" }PATCH/api/v1/storefront/:storefrontId/page/:idUpdate any supplied page fields.
| Field | Type | Required | Description |
|---|---|---|---|
title | string | No | Page title. |
uid | string | No | URL slug; unique within the storefront. |
content | block[] | No | Ordered ContentEditorV2 blocks (page-safe catalog). |
fullWidth | boolean | No | Full-width layout. Defaults to false. |
showBreadcrumb | boolean | No | Show breadcrumb. Defaults to true. |
showPageTitle | boolean | No | Show page title. Defaults to true. |
Example Request
curl -X PATCH "https://www.framer.art/api/v1/storefront/<STOREFRONT_ID>/page/<PAGE_ID>" \
-H "Authorization: Bearer <YOUR_API_KEY>" -H "Content-Type: application/json"Example Response
{ "id": "<PAGE_ID>", "title": "About", "uid": "about", "fullWidth": false, "showBreadcrumb": true, "showPageTitle": true, "content": [{ "id": "<BLOCK_ID>", "type": "heading", "text": "About us" }], "contentVersion": 2, "storefrontId": "<STOREFRONT_ID>" }DELETE/api/v1/storefront/:storefrontId/page/:idDelete a page.
| Field | Type | Required | Description |
|---|---|---|---|
title | string | No | Page title. |
uid | string | No | URL slug; unique within the storefront. |
content | block[] | No | Ordered ContentEditorV2 blocks (page-safe catalog). |
fullWidth | boolean | No | Full-width layout. Defaults to false. |
showBreadcrumb | boolean | No | Show breadcrumb. Defaults to true. |
showPageTitle | boolean | No | Show page title. Defaults to true. |
Example Request
curl -X DELETE "https://www.framer.art/api/v1/storefront/<STOREFRONT_ID>/page/<PAGE_ID>" \
-H "Authorization: Bearer <YOUR_API_KEY>" -H "Content-Type: application/json"Example Response
nullEmail Flows
Automated email flows are scoped to a storefront. Types: welcome, cart_abandonment, browse_abandonment, wishlist_abandonment, win_back_customer, win_back_subscriber, post_purchase. At most one flow per type per storefront. This API is data CRUD only (no send / test / preview).
GET/api/v1/storefront/:storefrontId/email-flowList email flows. Query fields: type, skip, limit.
| Field | Type | Required | Description |
|---|---|---|---|
name | string | No | Flow display name. |
type | string | No | welcome, cart_abandonment, browse_abandonment, wishlist_abandonment, win_back_customer, win_back_subscriber, or post_purchase. |
enabled | boolean | No | Whether the flow sends automatically. Defaults to false. |
intervalInHours | integer | null | No | Optional hours between automated sends. |
Example Request
curl -X GET "https://www.framer.art/api/v1/storefront/<STOREFRONT_ID>/email-flow" \
-H "Authorization: Bearer <YOUR_API_KEY>" -H "Content-Type: application/json"Example Response
{ "id": "<FLOW_ID>", "name": "Welcome", "type": "welcome", "enabled": false, "intervalInHours": null, "createdAt": "2026-09-12T00:00:00.000Z", "storefrontId": "<STOREFRONT_ID>" }POST/api/v1/storefront/:storefrontId/email-flowCreate an email flow.
| Field | Type | Required | Description |
|---|---|---|---|
name | string | No | Flow display name. |
type | string | No | welcome, cart_abandonment, browse_abandonment, wishlist_abandonment, win_back_customer, win_back_subscriber, or post_purchase. |
enabled | boolean | No | Whether the flow sends automatically. Defaults to false. |
intervalInHours | integer | null | No | Optional hours between automated sends. |
Example Request
curl -X POST "https://www.framer.art/api/v1/storefront/<STOREFRONT_ID>/email-flow" \
-H "Authorization: Bearer <YOUR_API_KEY>" -H "Content-Type: application/json" \
-d '{ "name": "Welcome", "type": "welcome", "enabled": false }'Example Response
{ "id": "<FLOW_ID>", "name": "Welcome", "type": "welcome", "enabled": false, "intervalInHours": null, "createdAt": "2026-09-12T00:00:00.000Z", "storefrontId": "<STOREFRONT_ID>" }GET/api/v1/storefront/:storefrontId/email-flow/:idGet an email flow.
| Field | Type | Required | Description |
|---|---|---|---|
name | string | No | Flow display name. |
type | string | No | welcome, cart_abandonment, browse_abandonment, wishlist_abandonment, win_back_customer, win_back_subscriber, or post_purchase. |
enabled | boolean | No | Whether the flow sends automatically. Defaults to false. |
intervalInHours | integer | null | No | Optional hours between automated sends. |
Example Request
curl -X GET "https://www.framer.art/api/v1/storefront/<STOREFRONT_ID>/email-flow/<FLOW_ID>" \
-H "Authorization: Bearer <YOUR_API_KEY>" -H "Content-Type: application/json"Example Response
{ "id": "<FLOW_ID>", "name": "Welcome", "type": "welcome", "enabled": false, "intervalInHours": null, "createdAt": "2026-09-12T00:00:00.000Z", "storefrontId": "<STOREFRONT_ID>" }PATCH/api/v1/storefront/:storefrontId/email-flow/:idUpdate any supplied flow fields.
| Field | Type | Required | Description |
|---|---|---|---|
name | string | No | Flow display name. |
type | string | No | welcome, cart_abandonment, browse_abandonment, wishlist_abandonment, win_back_customer, win_back_subscriber, or post_purchase. |
enabled | boolean | No | Whether the flow sends automatically. Defaults to false. |
intervalInHours | integer | null | No | Optional hours between automated sends. |
Example Request
curl -X PATCH "https://www.framer.art/api/v1/storefront/<STOREFRONT_ID>/email-flow/<FLOW_ID>" \
-H "Authorization: Bearer <YOUR_API_KEY>" -H "Content-Type: application/json"Example Response
{ "id": "<FLOW_ID>", "name": "Welcome", "type": "welcome", "enabled": false, "intervalInHours": null, "createdAt": "2026-09-12T00:00:00.000Z", "storefrontId": "<STOREFRONT_ID>" }DELETE/api/v1/storefront/:storefrontId/email-flow/:idDelete a flow and its nested flow emails.
| Field | Type | Required | Description |
|---|---|---|---|
name | string | No | Flow display name. |
type | string | No | welcome, cart_abandonment, browse_abandonment, wishlist_abandonment, win_back_customer, win_back_subscriber, or post_purchase. |
enabled | boolean | No | Whether the flow sends automatically. Defaults to false. |
intervalInHours | integer | null | No | Optional hours between automated sends. |
Example Request
curl -X DELETE "https://www.framer.art/api/v1/storefront/<STOREFRONT_ID>/email-flow/<FLOW_ID>" \
-H "Authorization: Bearer <YOUR_API_KEY>" -H "Content-Type: application/json"Example Response
nullFlow Emails
Flow emails are nested under an email flow. Paths use /api/v1/storefront/:storefrontId/email-flow/:flowId/flow-email (unlike other resources, the parent flow id is required in the path). content uses the email-safe block catalog plus flow-only types (flowProducts, flowDiscount, flowCta). Columns slots may only contain text, heading, image, divider, or button. Image write shapes match pages. Data CRUD only: no send, test, or preview.
GET/api/v1/storefront/:storefrontId/email-flow/:flowId/flow-emailList emails in a flow. Query fields: skip, limit.
| Field | Type | Required | Description |
|---|---|---|---|
subject | string | No | Email subject line. |
subtitle | string | null | No | Optional subtitle. |
previewText | string | null | No | Inbox preview text shown next to the subject. |
content | block[] | No | Ordered email-safe ContentEditorV2 blocks. |
published | boolean | No | Whether this email is active in the flow. |
useBaseTemplate | boolean | No | Wrap content in the base email template. |
delaySinceLastSendInHours | integer | null | No | Hours to wait after the previous flow email. |
index | integer | null | No | Position in the flow sequence. |
Example Request
curl -X GET "https://www.framer.art/api/v1/storefront/<STOREFRONT_ID>/email-flow/<FLOW_ID>/flow-email" \
-H "Authorization: Bearer <YOUR_API_KEY>" -H "Content-Type: application/json"Example Response
{ "id": "<FLOW_EMAIL_ID>", "subject": "Welcome!", "subtitle": null, "previewText": null, "content": [{ "id": "<BLOCK_ID>", "type": "text", "html": "<p>Thanks for subscribing.</p>" }], "contentVersion": 2, "published": false, "useBaseTemplate": true, "delaySinceLastSendInHours": null, "index": 0, "flowId": "<FLOW_ID>", "storefrontId": "<STOREFRONT_ID>" }POST/api/v1/storefront/:storefrontId/email-flow/:flowId/flow-emailCreate a flow email.
| Field | Type | Required | Description |
|---|---|---|---|
subject | string | No | Email subject line. |
subtitle | string | null | No | Optional subtitle. |
previewText | string | null | No | Inbox preview text shown next to the subject. |
content | block[] | No | Ordered email-safe ContentEditorV2 blocks. |
published | boolean | No | Whether this email is active in the flow. |
useBaseTemplate | boolean | No | Wrap content in the base email template. |
delaySinceLastSendInHours | integer | null | No | Hours to wait after the previous flow email. |
index | integer | null | No | Position in the flow sequence. |
Example Request
curl -X POST "https://www.framer.art/api/v1/storefront/<STOREFRONT_ID>/email-flow/<FLOW_ID>/flow-email" \
-H "Authorization: Bearer <YOUR_API_KEY>" -H "Content-Type: application/json" \
-d '{ "subject": "Welcome!", "content": [{ "id": "<BLOCK_ID>", "type": "text", "html": "<p>Thanks for subscribing.</p>" }] }'Example Response
{ "id": "<FLOW_EMAIL_ID>", "subject": "Welcome!", "subtitle": null, "previewText": null, "content": [{ "id": "<BLOCK_ID>", "type": "text", "html": "<p>Thanks for subscribing.</p>" }], "contentVersion": 2, "published": false, "useBaseTemplate": true, "delaySinceLastSendInHours": null, "index": 0, "flowId": "<FLOW_ID>", "storefrontId": "<STOREFRONT_ID>" }GET/api/v1/storefront/:storefrontId/email-flow/:flowId/flow-email/:idGet a flow email.
| Field | Type | Required | Description |
|---|---|---|---|
subject | string | No | Email subject line. |
subtitle | string | null | No | Optional subtitle. |
previewText | string | null | No | Inbox preview text shown next to the subject. |
content | block[] | No | Ordered email-safe ContentEditorV2 blocks. |
published | boolean | No | Whether this email is active in the flow. |
useBaseTemplate | boolean | No | Wrap content in the base email template. |
delaySinceLastSendInHours | integer | null | No | Hours to wait after the previous flow email. |
index | integer | null | No | Position in the flow sequence. |
Example Request
curl -X GET "https://www.framer.art/api/v1/storefront/<STOREFRONT_ID>/email-flow/<FLOW_ID>/flow-email/<FLOW_EMAIL_ID>" \
-H "Authorization: Bearer <YOUR_API_KEY>" -H "Content-Type: application/json"Example Response
{ "id": "<FLOW_EMAIL_ID>", "subject": "Welcome!", "subtitle": null, "previewText": null, "content": [{ "id": "<BLOCK_ID>", "type": "text", "html": "<p>Thanks for subscribing.</p>" }], "contentVersion": 2, "published": false, "useBaseTemplate": true, "delaySinceLastSendInHours": null, "index": 0, "flowId": "<FLOW_ID>", "storefrontId": "<STOREFRONT_ID>" }PATCH/api/v1/storefront/:storefrontId/email-flow/:flowId/flow-email/:idUpdate any supplied flow email fields.
| Field | Type | Required | Description |
|---|---|---|---|
subject | string | No | Email subject line. |
subtitle | string | null | No | Optional subtitle. |
previewText | string | null | No | Inbox preview text shown next to the subject. |
content | block[] | No | Ordered email-safe ContentEditorV2 blocks. |
published | boolean | No | Whether this email is active in the flow. |
useBaseTemplate | boolean | No | Wrap content in the base email template. |
delaySinceLastSendInHours | integer | null | No | Hours to wait after the previous flow email. |
index | integer | null | No | Position in the flow sequence. |
Example Request
curl -X PATCH "https://www.framer.art/api/v1/storefront/<STOREFRONT_ID>/email-flow/<FLOW_ID>/flow-email/<FLOW_EMAIL_ID>" \
-H "Authorization: Bearer <YOUR_API_KEY>" -H "Content-Type: application/json"Example Response
{ "id": "<FLOW_EMAIL_ID>", "subject": "Welcome!", "subtitle": null, "previewText": null, "content": [{ "id": "<BLOCK_ID>", "type": "text", "html": "<p>Thanks for subscribing.</p>" }], "contentVersion": 2, "published": false, "useBaseTemplate": true, "delaySinceLastSendInHours": null, "index": 0, "flowId": "<FLOW_ID>", "storefrontId": "<STOREFRONT_ID>" }DELETE/api/v1/storefront/:storefrontId/email-flow/:flowId/flow-email/:idDelete a flow email.
| Field | Type | Required | Description |
|---|---|---|---|
subject | string | No | Email subject line. |
subtitle | string | null | No | Optional subtitle. |
previewText | string | null | No | Inbox preview text shown next to the subject. |
content | block[] | No | Ordered email-safe ContentEditorV2 blocks. |
published | boolean | No | Whether this email is active in the flow. |
useBaseTemplate | boolean | No | Wrap content in the base email template. |
delaySinceLastSendInHours | integer | null | No | Hours to wait after the previous flow email. |
index | integer | null | No | Position in the flow sequence. |
Example Request
curl -X DELETE "https://www.framer.art/api/v1/storefront/<STOREFRONT_ID>/email-flow/<FLOW_ID>/flow-email/<FLOW_EMAIL_ID>" \
-H "Authorization: Bearer <YOUR_API_KEY>" -H "Content-Type: application/json"Example Response
nullTransactions
Transactions may be workspace-level or associated with an optional storefrontId.
GET/api/v1/transactionList transactions; accepts skip, limit, and optional storefrontId query fields.
| Field | Type | Required | Description |
|---|---|---|---|
category | string | No | Financial transaction category. |
amount | number | No | Signed transaction amount. |
name | string | No | Transaction name. |
date | ISO date | No | Transaction date. |
transactionId | string | null | No | Optional external reference. |
storefrontId | UUID | null | No | Optional storefront in this workspace. |
Example Request
curl -X GET "https://www.framer.art/api/v1/transaction" \
+ -H "Authorization: Bearer <YOUR_API_KEY>" -H "Content-Type: application/json"Example Response
{ "id": "<TRANSACTION_ID>", "category": "software", "amount": -29, "name": "Editing software", "date": "2026-09-12T00:00:00.000Z", "transactionId": null, "storefrontId": null }POST/api/v1/transactionCreate a transaction.
| Field | Type | Required | Description |
|---|---|---|---|
category | string | No | Financial transaction category. |
amount | number | No | Signed transaction amount. |
name | string | No | Transaction name. |
date | ISO date | No | Transaction date. |
transactionId | string | null | No | Optional external reference. |
storefrontId | UUID | null | No | Optional storefront in this workspace. |
Example Request
curl -X POST "https://www.framer.art/api/v1/transaction" \
+ -H "Authorization: Bearer <YOUR_API_KEY>" -H "Content-Type: application/json" \
+ -d '{ "category": "software", "amount": -29, "name": "Editing software", "date": "2026-09-12T00:00:00Z" }'Example Response
{ "id": "<TRANSACTION_ID>", "category": "software", "amount": -29, "name": "Editing software", "date": "2026-09-12T00:00:00.000Z", "transactionId": null, "storefrontId": null }GET/api/v1/transaction/:idGet a transaction.
| Field | Type | Required | Description |
|---|---|---|---|
category | string | No | Financial transaction category. |
amount | number | No | Signed transaction amount. |
name | string | No | Transaction name. |
date | ISO date | No | Transaction date. |
transactionId | string | null | No | Optional external reference. |
storefrontId | UUID | null | No | Optional storefront in this workspace. |
Example Request
curl -X GET "https://www.framer.art/api/v1/transaction/<TRANSACTION_ID>" \
+ -H "Authorization: Bearer <YOUR_API_KEY>" -H "Content-Type: application/json"Example Response
{ "id": "<TRANSACTION_ID>", "category": "software", "amount": -29, "name": "Editing software", "date": "2026-09-12T00:00:00.000Z", "transactionId": null, "storefrontId": null }PATCH/api/v1/transaction/:idUpdate supplied transaction fields.
| Field | Type | Required | Description |
|---|---|---|---|
category | string | No | Financial transaction category. |
amount | number | No | Signed transaction amount. |
name | string | No | Transaction name. |
date | ISO date | No | Transaction date. |
transactionId | string | null | No | Optional external reference. |
storefrontId | UUID | null | No | Optional storefront in this workspace. |
Example Request
curl -X PATCH "https://www.framer.art/api/v1/transaction/<TRANSACTION_ID>" \
+ -H "Authorization: Bearer <YOUR_API_KEY>" -H "Content-Type: application/json"Example Response
{ "id": "<TRANSACTION_ID>", "category": "software", "amount": -29, "name": "Editing software", "date": "2026-09-12T00:00:00.000Z", "transactionId": null, "storefrontId": null }DELETE/api/v1/transaction/:idDelete a transaction.
| Field | Type | Required | Description |
|---|---|---|---|
category | string | No | Financial transaction category. |
amount | number | No | Signed transaction amount. |
name | string | No | Transaction name. |
date | ISO date | No | Transaction date. |
transactionId | string | null | No | Optional external reference. |
storefrontId | UUID | null | No | Optional storefront in this workspace. |
Example Request
curl -X DELETE "https://www.framer.art/api/v1/transaction/<TRANSACTION_ID>" \
+ -H "Authorization: Bearer <YOUR_API_KEY>" -H "Content-Type: application/json"Example Response
nullPlanner Locations
Planner location endpoints are read-only. They return the places saved in your workspace's Location Planner, with their notes, tags, and links to the original reference photos you uploaded.
GET/api/v1/planner-locationReturns saved locations, newest first.
| Field | Type | Required | Description |
|---|---|---|---|
skip | integer | No | Number of locations to skip. |
limit | integer | No | Maximum number to return (up to 100, default 100). Use skip to page through larger workspaces. |
Example Request
curl "https://www.framer.art/api/v1/planner-location?limit=20" \
-H "Authorization: Bearer <YOUR_API_KEY>"Example Response
[{
"id": "5d1e7b20-8c3a-4f6e-9b42-0a7c3e9f1d58",
"createdAt": "2026-09-01T18:04:00.000Z",
"notes": "Best light about 30 minutes before sunset.",
"tags": ["bridge", "sunset"],
"location": {
"name": "Golden Gate Bridge",
"city": "San Francisco",
"state": "California",
"country": "United States",
"postalCode": "94129",
"latitude": 37.8199,
"longitude": -122.4783,
"googlePlaceId": "ChIJw____96GhYARCVVwg5cT7c0"
},
"savedImages": [
{
"id": "9e4b2c71-3a5d-4f80-b6e1-2c8d7a0f4e13",
"url": "https://<SOURCE_CDN_HOST>/location-planner-images/<FILE>.jpg",
"createdAt": "2026-09-02T09:15:00.000Z"
}
]
}]GET/api/v1/planner-location/:idReturns one saved location belonging to the authenticated workspace.
Example Request
curl "https://www.framer.art/api/v1/planner-location/<PLANNER_LOCATION_ID>" \
-H "Authorization: Bearer <YOUR_API_KEY>"Example Response
{
"id": "5d1e7b20-8c3a-4f6e-9b42-0a7c3e9f1d58",
"createdAt": "2026-09-01T18:04:00.000Z",
"notes": "Best light about 30 minutes before sunset.",
"tags": ["bridge", "sunset"],
"location": {
"name": "Golden Gate Bridge",
"city": "San Francisco",
"state": "California",
"country": "United States",
"postalCode": "94129",
"latitude": 37.8199,
"longitude": -122.4783,
"googlePlaceId": "ChIJw____96GhYARCVVwg5cT7c0"
},
"savedImages": [
{
"id": "9e4b2c71-3a5d-4f80-b6e1-2c8d7a0f4e13",
"url": "https://<SOURCE_CDN_HOST>/location-planner-images/<FILE>.jpg",
"createdAt": "2026-09-02T09:15:00.000Z"
}
]
}MCP Server
The same API is also available as an MCP (Model Context Protocol) server, so MCP-compatible AI tools (Claude Desktop, custom agents, etc.) can call it directly instead of you writing REST requests by hand. It supports the Streamable HTTP transport at:
https://www.framer.art/api/v1/mcpMCP authenticates via OAuth. Compatible clients prompt you to sign in when connecting to this server.
Tools
Each tool mirrors one of the REST endpoints above 1:1, including the same validation and error behavior.
| Tool | Equivalent REST endpoint |
|---|---|
list_storefronts | GET /api/v1/storefront |
get_storefront | GET /api/v1/storefront/:id |
list_discounts | GET /api/v1/storefront/:storefrontId/discount |
get_discount | GET /api/v1/storefront/:storefrontId/discount/:id |
create_discount | POST /api/v1/storefront/:storefrontId/discount |
update_discount | PATCH /api/v1/storefront/:storefrontId/discount/:id |
delete_discount | DELETE /api/v1/storefront/:storefrontId/discount/:id |
list_pages | GET /api/v1/storefront/:storefrontId/page |
get_page | GET /api/v1/storefront/:storefrontId/page/:id |
create_page | POST /api/v1/storefront/:storefrontId/page |
update_page | PATCH /api/v1/storefront/:storefrontId/page/:id |
delete_page | DELETE /api/v1/storefront/:storefrontId/page/:id |
list_email_flows | GET /api/v1/storefront/:storefrontId/email-flow |
get_email_flow | GET /api/v1/storefront/:storefrontId/email-flow/:id |
create_email_flow | POST /api/v1/storefront/:storefrontId/email-flow |
update_email_flow | PATCH /api/v1/storefront/:storefrontId/email-flow/:id |
delete_email_flow | DELETE /api/v1/storefront/:storefrontId/email-flow/:id |
list_flow_emails | GET /api/v1/storefront/:storefrontId/email-flow/:flowId/flow-email |
get_flow_email | GET /api/v1/storefront/:storefrontId/email-flow/:flowId/flow-email/:id |
create_flow_email | POST /api/v1/storefront/:storefrontId/email-flow/:flowId/flow-email |
update_flow_email | PATCH /api/v1/storefront/:storefrontId/email-flow/:flowId/flow-email/:id |
delete_flow_email | DELETE /api/v1/storefront/:storefrontId/email-flow/:flowId/flow-email/:id |
list_transactions | GET /api/v1/transaction |
get_transaction | GET /api/v1/transaction/:id |
create_transaction | POST /api/v1/transaction |
update_transaction | PATCH /api/v1/transaction/:id |
delete_transaction | DELETE /api/v1/transaction/:id |
list_planner_locations | GET /api/v1/planner-location |
get_planner_location | GET /api/v1/planner-location/:id |
list_images | GET /api/v1/image |
get_image | GET /api/v1/image/:id |
create_image | POST /api/v1/image |
update_image | PATCH /api/v1/image/:id |
delete_image | DELETE /api/v1/image/:id |
Errors
Errors are returned as JSON in the shape { "error": string }, with one exception noted below.
400 Bad Request: a request failed validation or a business rule (e.g. you've hit your plan's image limit, source images don't share an orientation, or a referenced collection doesn't exist). This includes a body that isn't valid JSON or doesn't match the expected shape (a required field is missing, a value is the wrong type, or an enum value isn't one of the documented options): theerrornames each offending field, e.g."artType: Invalid enum value. Expected 'Photography' | ..., received 'Nope'".404 Not Found: no image exists with the givenidin your workspace, forGET/PATCH/DELETEon/api/v1/image/:id.409 Conflict: another image in your workspace already uses thisexternalId.405 Method Not Allowed: the HTTP method isn't supported on that path.
A few cases currently fall through as an unstructured 500 Internal Server Error rather than a clean error response; this is existing behavior, not a documented contract, so don't rely on the response body in these cases:
- The Authorization header is missing.
- The API key in the Authorization header doesn't exist.
If your workspace's plan doesn't include API access, you'll get a clean 400 with { "error": "You must be a paid user to use the API." }.
Have a question or a use case that isn't yet supported? Contact us and we'll help you out.
