Skip to main content

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.art

Request 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/image

List the images in your workspace, most recently created first. Only images with status published or draft are returned.

Query Parameters

FieldTypeRequiredDescription
skipnumberNoNumber of images to skip. Defaults to 0.
limitnumberNoMaximum number of images to return. Defaults to 100.
googlePlaceIdstringNoFilter to images whose location matches this Google Place ID.
skunumberNoFilter to the image with this exact sku.
externalIdstringNoFilter to the image with this exact externalId.
searchstringNoFree-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/:id

Get 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/image

Upload a new image.

Body

FieldTypeRequiredDescription
titlestringYesThe image's title.
descriptionstringYesThe image's description.
keywordsstring[]YesKeywords/subjects for the image. Can be an empty array.
artTypestringYesOne of: Photography, Painting, Illustration, Collage, Digital Art, Mixed Media, Drawing, Typography, Maps, Screen Print, Line Drawing, Woodblock, Linocut.
artiststringNoThe artist associated with the image. Omitted or empty stores as null.
externalIdstringNoYour own unique ID for the image, unique within your workspace. Omitted or empty stores as null.
googleAdsCustomLabelstringNoCustom label surfaced as custom_label_4 in the Google product feed. Up to 100 characters. Omitted or empty stores as null.
status"draft" | "published"NoUse "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 }[]YesOne or more source images, each given as a URL to fetch. All source images must share the same orientation.
monochromebooleanNoWhether the image is black & white.
collections{ slug: string, name: string }[]NoCollections to add the image to. Collections that don't yet exist in your workspace are created.
locationobjectNoWhere 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/:id

Update 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

FieldTypeRequiredDescription
titlestringNoThe image's title.
descriptionstringNoThe image's description.
artTypestringNoOne of: Photography, Painting, Illustration, Collage, Digital Art, Mixed Media, Drawing, Typography, Maps, Screen Print, Line Drawing, Woodblock, Linocut.
artiststringNoThe artist associated with the image. Omit to leave unchanged; empty string clears it.
externalIdstringNoYour own unique ID for the image. Omit to leave unchanged; empty string clears it. Must be unique within your workspace.
googleAdsCustomLabelstringNoCustom 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"NoPublishes 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.
monochromebooleanNoWhether the image is black & white.
keywords.addstring[]NoKeywords to add.
keywords.removestring[]NoKeywords to remove.
collections.add{ slug: string, name: string }[]NoCollections to add the image to, created if they don't yet exist.
collections.remove{ slug: string }[]NoCollections to remove the image from.
sourceImages.add{ url: string }[]NoAdditional 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.
locationobjectNoReplaces 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/:id

Delete 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.

null

Returns 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
FieldTypeRequiredDescription
skipintegerNoNumber of storefronts to skip.
limitintegerNoMaximum 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/:id

Returns 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/discount

List discounts. Query fields: skip, limit.

FieldTypeRequiredDescription
codestring | nullNoCoupon code; required for coupon-code discounts.
typestringNocoupon_code or email_subscribe.
valuenumberNoDecimal percentage of the price (0.15 = 15% off). Same storage as the dashboard Percentage field, which displays 15 for 0.15.
startISO dateNoStart date.
endISO date | nullNoOptional end date.
enabledbooleanNoWhether the discount is active.
title / subtitlestring | nullNoOptional customer-facing copy.
includeInGoogleMerchantPromotionsFeedbooleanNoIf 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/discount

Create a discount.

FieldTypeRequiredDescription
codestring | nullNoCoupon code; required for coupon-code discounts.
typestringNocoupon_code or email_subscribe.
valuenumberNoDecimal percentage of the price (0.15 = 15% off). Same storage as the dashboard Percentage field, which displays 15 for 0.15.
startISO dateNoStart date.
endISO date | nullNoOptional end date.
enabledbooleanNoWhether the discount is active.
title / subtitlestring | nullNoOptional customer-facing copy.
includeInGoogleMerchantPromotionsFeedbooleanNoIf 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/:id

Get a discount.

FieldTypeRequiredDescription
codestring | nullNoCoupon code; required for coupon-code discounts.
typestringNocoupon_code or email_subscribe.
valuenumberNoDecimal percentage of the price (0.15 = 15% off). Same storage as the dashboard Percentage field, which displays 15 for 0.15.
startISO dateNoStart date.
endISO date | nullNoOptional end date.
enabledbooleanNoWhether the discount is active.
title / subtitlestring | nullNoOptional customer-facing copy.
includeInGoogleMerchantPromotionsFeedbooleanNoIf 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/:id

Update any supplied discount fields.

FieldTypeRequiredDescription
codestring | nullNoCoupon code; required for coupon-code discounts.
typestringNocoupon_code or email_subscribe.
valuenumberNoDecimal percentage of the price (0.15 = 15% off). Same storage as the dashboard Percentage field, which displays 15 for 0.15.
startISO dateNoStart date.
endISO date | nullNoOptional end date.
enabledbooleanNoWhether the discount is active.
title / subtitlestring | nullNoOptional customer-facing copy.
includeInGoogleMerchantPromotionsFeedbooleanNoIf 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/page

List pages. Query fields: skip, limit.

FieldTypeRequiredDescription
titlestringNoPage title.
uidstringNoURL slug; unique within the storefront.
contentblock[]NoOrdered ContentEditorV2 blocks (page-safe catalog).
fullWidthbooleanNoFull-width layout. Defaults to false.
showBreadcrumbbooleanNoShow breadcrumb. Defaults to true.
showPageTitlebooleanNoShow 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/page

Create a page.

FieldTypeRequiredDescription
titlestringNoPage title.
uidstringNoURL slug; unique within the storefront.
contentblock[]NoOrdered ContentEditorV2 blocks (page-safe catalog).
fullWidthbooleanNoFull-width layout. Defaults to false.
showBreadcrumbbooleanNoShow breadcrumb. Defaults to true.
showPageTitlebooleanNoShow 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/:id

Get a page.

FieldTypeRequiredDescription
titlestringNoPage title.
uidstringNoURL slug; unique within the storefront.
contentblock[]NoOrdered ContentEditorV2 blocks (page-safe catalog).
fullWidthbooleanNoFull-width layout. Defaults to false.
showBreadcrumbbooleanNoShow breadcrumb. Defaults to true.
showPageTitlebooleanNoShow 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/:id

Update any supplied page fields.

FieldTypeRequiredDescription
titlestringNoPage title.
uidstringNoURL slug; unique within the storefront.
contentblock[]NoOrdered ContentEditorV2 blocks (page-safe catalog).
fullWidthbooleanNoFull-width layout. Defaults to false.
showBreadcrumbbooleanNoShow breadcrumb. Defaults to true.
showPageTitlebooleanNoShow 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/:id

Delete a page.

FieldTypeRequiredDescription
titlestringNoPage title.
uidstringNoURL slug; unique within the storefront.
contentblock[]NoOrdered ContentEditorV2 blocks (page-safe catalog).
fullWidthbooleanNoFull-width layout. Defaults to false.
showBreadcrumbbooleanNoShow breadcrumb. Defaults to true.
showPageTitlebooleanNoShow 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

null

Email 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-flow

List email flows. Query fields: type, skip, limit.

FieldTypeRequiredDescription
namestringNoFlow display name.
typestringNowelcome, cart_abandonment, browse_abandonment, wishlist_abandonment, win_back_customer, win_back_subscriber, or post_purchase.
enabledbooleanNoWhether the flow sends automatically. Defaults to false.
intervalInHoursinteger | nullNoOptional 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-flow

Create an email flow.

FieldTypeRequiredDescription
namestringNoFlow display name.
typestringNowelcome, cart_abandonment, browse_abandonment, wishlist_abandonment, win_back_customer, win_back_subscriber, or post_purchase.
enabledbooleanNoWhether the flow sends automatically. Defaults to false.
intervalInHoursinteger | nullNoOptional 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/:id

Get an email flow.

FieldTypeRequiredDescription
namestringNoFlow display name.
typestringNowelcome, cart_abandonment, browse_abandonment, wishlist_abandonment, win_back_customer, win_back_subscriber, or post_purchase.
enabledbooleanNoWhether the flow sends automatically. Defaults to false.
intervalInHoursinteger | nullNoOptional 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/:id

Update any supplied flow fields.

FieldTypeRequiredDescription
namestringNoFlow display name.
typestringNowelcome, cart_abandonment, browse_abandonment, wishlist_abandonment, win_back_customer, win_back_subscriber, or post_purchase.
enabledbooleanNoWhether the flow sends automatically. Defaults to false.
intervalInHoursinteger | nullNoOptional 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/:id

Delete a flow and its nested flow emails.

FieldTypeRequiredDescription
namestringNoFlow display name.
typestringNowelcome, cart_abandonment, browse_abandonment, wishlist_abandonment, win_back_customer, win_back_subscriber, or post_purchase.
enabledbooleanNoWhether the flow sends automatically. Defaults to false.
intervalInHoursinteger | nullNoOptional 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

null

Flow 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-email

List emails in a flow. Query fields: skip, limit.

FieldTypeRequiredDescription
subjectstringNoEmail subject line.
subtitlestring | nullNoOptional subtitle.
previewTextstring | nullNoInbox preview text shown next to the subject.
contentblock[]NoOrdered email-safe ContentEditorV2 blocks.
publishedbooleanNoWhether this email is active in the flow.
useBaseTemplatebooleanNoWrap content in the base email template.
delaySinceLastSendInHoursinteger | nullNoHours to wait after the previous flow email.
indexinteger | nullNoPosition 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-email

Create a flow email.

FieldTypeRequiredDescription
subjectstringNoEmail subject line.
subtitlestring | nullNoOptional subtitle.
previewTextstring | nullNoInbox preview text shown next to the subject.
contentblock[]NoOrdered email-safe ContentEditorV2 blocks.
publishedbooleanNoWhether this email is active in the flow.
useBaseTemplatebooleanNoWrap content in the base email template.
delaySinceLastSendInHoursinteger | nullNoHours to wait after the previous flow email.
indexinteger | nullNoPosition 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/:id

Get a flow email.

FieldTypeRequiredDescription
subjectstringNoEmail subject line.
subtitlestring | nullNoOptional subtitle.
previewTextstring | nullNoInbox preview text shown next to the subject.
contentblock[]NoOrdered email-safe ContentEditorV2 blocks.
publishedbooleanNoWhether this email is active in the flow.
useBaseTemplatebooleanNoWrap content in the base email template.
delaySinceLastSendInHoursinteger | nullNoHours to wait after the previous flow email.
indexinteger | nullNoPosition 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/:id

Update any supplied flow email fields.

FieldTypeRequiredDescription
subjectstringNoEmail subject line.
subtitlestring | nullNoOptional subtitle.
previewTextstring | nullNoInbox preview text shown next to the subject.
contentblock[]NoOrdered email-safe ContentEditorV2 blocks.
publishedbooleanNoWhether this email is active in the flow.
useBaseTemplatebooleanNoWrap content in the base email template.
delaySinceLastSendInHoursinteger | nullNoHours to wait after the previous flow email.
indexinteger | nullNoPosition 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/:id

Delete a flow email.

FieldTypeRequiredDescription
subjectstringNoEmail subject line.
subtitlestring | nullNoOptional subtitle.
previewTextstring | nullNoInbox preview text shown next to the subject.
contentblock[]NoOrdered email-safe ContentEditorV2 blocks.
publishedbooleanNoWhether this email is active in the flow.
useBaseTemplatebooleanNoWrap content in the base email template.
delaySinceLastSendInHoursinteger | nullNoHours to wait after the previous flow email.
indexinteger | nullNoPosition 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

null

Transactions

Transactions may be workspace-level or associated with an optional storefrontId.

GET/api/v1/transaction

List transactions; accepts skip, limit, and optional storefrontId query fields.

FieldTypeRequiredDescription
categorystringNoFinancial transaction category.
amountnumberNoSigned transaction amount.
namestringNoTransaction name.
dateISO dateNoTransaction date.
transactionIdstring | nullNoOptional external reference.
storefrontIdUUID | nullNoOptional 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/transaction

Create a transaction.

FieldTypeRequiredDescription
categorystringNoFinancial transaction category.
amountnumberNoSigned transaction amount.
namestringNoTransaction name.
dateISO dateNoTransaction date.
transactionIdstring | nullNoOptional external reference.
storefrontIdUUID | nullNoOptional 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/:id

Get a transaction.

FieldTypeRequiredDescription
categorystringNoFinancial transaction category.
amountnumberNoSigned transaction amount.
namestringNoTransaction name.
dateISO dateNoTransaction date.
transactionIdstring | nullNoOptional external reference.
storefrontIdUUID | nullNoOptional 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/:id

Update supplied transaction fields.

FieldTypeRequiredDescription
categorystringNoFinancial transaction category.
amountnumberNoSigned transaction amount.
namestringNoTransaction name.
dateISO dateNoTransaction date.
transactionIdstring | nullNoOptional external reference.
storefrontIdUUID | nullNoOptional 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/:id

Delete a transaction.

FieldTypeRequiredDescription
categorystringNoFinancial transaction category.
amountnumberNoSigned transaction amount.
namestringNoTransaction name.
dateISO dateNoTransaction date.
transactionIdstring | nullNoOptional external reference.
storefrontIdUUID | nullNoOptional 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

null

Planner 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-location

Returns saved locations, newest first.

FieldTypeRequiredDescription
skipintegerNoNumber of locations to skip.
limitintegerNoMaximum 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/:id

Returns 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/mcp

MCP 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.

ToolEquivalent REST endpoint
list_storefrontsGET /api/v1/storefront
get_storefrontGET /api/v1/storefront/:id
list_discountsGET /api/v1/storefront/:storefrontId/discount
get_discountGET /api/v1/storefront/:storefrontId/discount/:id
create_discountPOST /api/v1/storefront/:storefrontId/discount
update_discountPATCH /api/v1/storefront/:storefrontId/discount/:id
delete_discountDELETE /api/v1/storefront/:storefrontId/discount/:id
list_pagesGET /api/v1/storefront/:storefrontId/page
get_pageGET /api/v1/storefront/:storefrontId/page/:id
create_pagePOST /api/v1/storefront/:storefrontId/page
update_pagePATCH /api/v1/storefront/:storefrontId/page/:id
delete_pageDELETE /api/v1/storefront/:storefrontId/page/:id
list_email_flowsGET /api/v1/storefront/:storefrontId/email-flow
get_email_flowGET /api/v1/storefront/:storefrontId/email-flow/:id
create_email_flowPOST /api/v1/storefront/:storefrontId/email-flow
update_email_flowPATCH /api/v1/storefront/:storefrontId/email-flow/:id
delete_email_flowDELETE /api/v1/storefront/:storefrontId/email-flow/:id
list_flow_emailsGET /api/v1/storefront/:storefrontId/email-flow/:flowId/flow-email
get_flow_emailGET /api/v1/storefront/:storefrontId/email-flow/:flowId/flow-email/:id
create_flow_emailPOST /api/v1/storefront/:storefrontId/email-flow/:flowId/flow-email
update_flow_emailPATCH /api/v1/storefront/:storefrontId/email-flow/:flowId/flow-email/:id
delete_flow_emailDELETE /api/v1/storefront/:storefrontId/email-flow/:flowId/flow-email/:id
list_transactionsGET /api/v1/transaction
get_transactionGET /api/v1/transaction/:id
create_transactionPOST /api/v1/transaction
update_transactionPATCH /api/v1/transaction/:id
delete_transactionDELETE /api/v1/transaction/:id
list_planner_locationsGET /api/v1/planner-location
get_planner_locationGET /api/v1/planner-location/:id
list_imagesGET /api/v1/image
get_imageGET /api/v1/image/:id
create_imagePOST /api/v1/image
update_imagePATCH /api/v1/image/:id
delete_imageDELETE /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): the error names each offending field, e.g. "artType: Invalid enum value. Expected 'Photography' | ..., received 'Nope'".
  • 404 Not Found: no image exists with the given id in your workspace, for GET/PATCH/DELETE on /api/v1/image/:id.
  • 409 Conflict: another image in your workspace already uses this externalId.
  • 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.