Skip to main content
SocialCutter

CMS and websites

Upload images to Webflow and use SocialCutter

Guide to uploading a master image to Webflow Assets with the v2 API, calling SocialCutter and using the correct outputs in a CMS Collection or on page images.

  • Webflow
  • API v2
  • Assets
  • CMS Collection
  • SocialCutter
  • feature image
  • images

The problem: one master, many sizes

Webflow lets you upload an image and place it in a CMS Collection or in a page’s image field. What it does not do for you is produce that same image at the sizes each network expects: 1200x630 for a Facebook card, 1080x1920 for TikTok, 1200x675 for X. If you upload a single JPG and reuse it everywhere, you end up with forced crops or a cut-off subject.

This guide’s flow uploads one master to Webflow Assets, processes it with SocialCutter and uses each output where it belongs. One source, every size correct.

Requirements and site token

You need the Webflow Data API v2 (base https://api.webflow.com/v2) and a site token. Create it under Site settings → Apps & integrations → API access and enable the scopes we use:

ScopePurpose
assets:read / assets:writeCreate and read Assets
cms:read / cms:writeRead and write collection items
sites:read / sites:writeResolve the site_id and publish the site

The exact scope names are shown on the token creation screen and may vary between versions. The official reference is at https://developers.webflow.com/data/reference. API v2 replaces the old v1: if you find examples with /sites/{site_id}/assets without the /v2 prefix, they belong to the retired version.

Store the token and the site_id in variables:

export WEBFLOW_TOKEN="your_site_token"
export SITE_ID="your_site_id"
export API_URL="https://api.socialcutter.theboomer.dev"
export API_KEY="sc_your_key"

1. Upload the master to Assets

Uploading in the v2 API takes two steps, exactly as the official Upload Asset reference describes: first you create the asset record and the API returns an upload URL with the form details; then you send the file as multipart to that URL.

POST https://api.webflow.com/v2/sites/{site_id}/assets · scope assets:write

FieldRequiredWhat it is
fileNameYesFile name including the extension; under 100 characters
fileHashYesMD5 hash of the file contents
parentFolderNoID of the Asset folder the file lands in

Create the asset

curl -s -X POST "https://api.webflow.com/v2/sites/$SITE_ID/assets" \
  -H "Authorization: Bearer $WEBFLOW_TOKEN" \
  -H "accept-version: 2.0.0" \
  -H "Content-Type: application/json" \
  -d '{
    "fileName": "master.jpg",
    "fileHash": "md5_hash_of_the_file",
    "parentFolder": "asset_folder_id"
  }' > asset.json

jq '{id, contentType, uploadUrl, assetUrl, hostedUrl, parentFolder}' asset.json

The 200 response carries, among others, these fields:

FieldWhat it is
idAsset identifier; you use it later to read the asset or change its alt text
uploadUrlTemporary presigned Amazon S3 URL the binary is sent to
uploadDetailsMetadata for uploading the asset binary: the form fields to send with the file
assetUrlS3 link to the asset
hostedUrlLink to the asset, the one you reference
parentFolderParent folder for the asset
contentType, originalFileName, createdOn, lastUpdatedType, original file name and dates

The documentation is explicit: you must use uploadUrl and uploadDetails in the POST request to S3 to complete the upload. That URL is issued by Webflow; SocialCutter does not host your file.

The fileHash is the MD5 hash of the file contents: generate it with md5sum master.jpg (on macOS, md5 -q master.jpg). Webflow uses it to avoid duplicates: if the hash matches a file that already exists, it does not store it again. If it does not match, the upload fails with 400. parentFolder is the ID of the Assets folder and is optional.

Create the target folder (optional)

If you do not want assets to land at the site root, create the folder first:

curl -s -X POST "https://api.webflow.com/v2/sites/$SITE_ID/asset_folders" \
  -H "Authorization: Bearer $WEBFLOW_TOKEN" \
  -H "accept-version: 2.0.0" \
  -H "Content-Type: application/json" \
  -d '{ "displayName": "SocialCutter" }' > folder.json

jq '{id, displayName, parentFolder}' folder.json

The endpoint is POST /v2/sites/{site_id}/asset_folders (scope assets:write) and it accepts displayName (required) and parentFolder (optional, to nest folders). Keep the folder id and pass it as parentFolder when creating the asset.

Send the file

The uploadDetails fields must be sent exactly as given, together with the file, to the uploadUrl:

UPLOAD_URL=$(jq -r '.uploadUrl' asset.json)
jq -r '.uploadDetails | to_entries[] | "\(.key)=\(.value)"' asset.json > fields.txt

curl -s -X POST "$UPLOAD_URL" \
  $(while IFS= read -r line; do printf -- "-F %s " "$line"; done < fields.txt) \
  -F "file=@./master.jpg" > upload.json

Do not invent the field names: they come from uploadDetails and change with the asset type. Send exactly what the API returns.

Size limit: Webflow images must not exceed 4 MB (documents are capped at 10 MB), per the Working with Assets guide. SocialCutter accepts masters up to 5 MB, so a large master may not go straight into Assets: generate it with SocialCutter first, whose outputs are much lighter, or shrink it.

Verify the asset landed correctly

GET https://api.webflow.com/v2/assets/{asset_id} (scope assets:read) returns the detail of the uploaded asset:

curl -s "https://api.webflow.com/v2/assets/$ASSET_ID" \
  -H "Authorization: Bearer $WEBFLOW_TOKEN" \
  -H "accept-version: 2.0.0" \
  | jq '{id, hostedUrl, contentType, size, originalFileName, altText}'

The fields that matter for verification: hostedUrl (the real link, the one you pass to SocialCutter), contentType (file format), size (size in bytes), originalFileName, altText and variants (the responsive variants Webflow creates to serve your site responsively). If hostedUrl does not load when opened, the upload to uploadUrl did not complete: repeat the POST with the uploadDetails fields and the file.

The same detail can be listed per folder. Note that folderId only appears in list responses, not when querying a single asset. The list endpoint takes folderId (a 24-character hex ObjectId) and pagination with limit (max 100) and offset:

curl -s "https://api.webflow.com/v2/sites/$SITE_ID/assets?folderId=$FOLDER_ID&limit=100" \
  -H "Authorization: Bearer $WEBFLOW_TOKEN" \
  -H "accept-version: 2.0.0" \
  | jq '.assets[] | {id, hostedUrl, size, folderId, altText}'

Alt text and the display name are changed with PATCH https://api.webflow.com/v2/assets/{asset_id} (scope assets:write), sending altText and/or displayName.

2. Call SocialCutter with the asset URL

Once the master is in Assets, its hostedUrl is the source for SocialCutter:

curl -s -X POST "$API_URL/api/v1/images/process" \
  -H "X-API-Key: *** \
  -H "Content-Type: application/json" \
  -d "{
    \"source\": { \"type\": \"url\", \"value\": \"$(jq -r '.hostedUrl' asset.json)\" },
    \"destinations\": [
      { \"platform\": \"facebook\", \"format\": \"link\" },
      { \"platform\": \"instagram\", \"format\": \"post\" },
      { \"platform\": \"twitter\", \"format\": \"summary_large_image\" }
    ]
  }" > sc.json

jq '.image_id, (.outputs[] | {url, platform, format, width, height})' sc.json

Each element of outputs carries the output URL, its platform, its format and its dimensions. The cover crop (the default) is centered.

3. Publish the site

New Assets and created items do not show on the published site until you publish it:

curl -s -X POST "https://api.webflow.com/v2/sites/$SITE_ID/publish" \
  -H "Authorization: Bearer $WEBFLOW_TOKEN" \
  -H "accept-version: 2.0.0" \
  -H "Content-Type: application/json" \
  -d '{ "publishToWebflowSubdomain": true, "customDomains": ["your-domain.com"] }'

Adjust customDomains to the project’s real domains.

4. Use the outputs in the CMS Collection

If the CMS Collection has an image field, there are two routes: upload each output as an Asset (repeating step 1) and reference its id, or pass the URL directly if your field accepts it. Check the collection schema before building the item:

# List collections
curl -s "https://api.webflow.com/v2/sites/$SITE_ID/collections" \
  -H "Authorization: Bearer $WEBFLOW_TOKEN" \
  -H "accept-version: 2.0.0" | jq '.collections[] | {id, slug}'

# Schema of one collection (fields and types)
curl -s "https://api.webflow.com/v2/collections/$COLLECTION_ID" \
  -H "Authorization: Bearer $WEBFLOW_TOKEN" \
  -H "accept-version: 2.0.0" | jq '.fields[] | {slug, type}'

Create the live item with the image field value taken from outputs[0].url:

curl -s -X POST "https://api.webflow.com/v2/collections/$COLLECTION_ID/items/live" \
  -H "Authorization: Bearer $WEBFLOW_TOKEN" \
  -H "accept-version: 2.0.0" \
  -H "Content-Type: application/json" \
  -d "{
    \"fieldData\": {
      \"name\": \"Sample post\",
      \"slug\": \"sample-post\",
      \"image\": \"$(jq -r '.outputs[0].url' sc.json)\"
    }
  }"

The real name of the image field is the slug the schema returns; it is not necessarily called image.

5. Use the outputs as page images

For a standalone image on a page, upload the output to Assets and use its URL in the HTML. In practice the cleanest pattern is: upload the master, process with SocialCutter and upload each output once, keeping its hostedUrl to reference from the CMS or from pages.

That order is the point: the file you upload to Assets is already produced by SocialCutter at the destination’s proportion (1080x1080 for instagram post, 1200x627 for linkedin post), as a centred crop. Webflow only hosts it and creates its responsive variants, so the CDN serves files that are already at the correct size instead of re-cropped versions of the original master.

Cost

  • 1 use per destination (platform and format combination) per request.
  • Repeated destinations in the same request are not charged twice.
  • Failed processing runs are refunded.

Common errors

SituationLikely cause
401 from WebflowToken missing, malformed or lacking the required scope (assets:write to create the asset)
400 creating the assetfileHash does not match the file’s MD5, or fileName is over 100 characters
Asset stays empty or hostedUrl does not loadThe POST to uploadUrl was not completed with the uploadDetails fields
Image not showingSite not published or item still a draft
401 from SocialCuttersc_ key malformed or revoked
413 from SocialCutterMaster file exceeds 5 MB
Master will not upload to WebflowIt exceeds Webflow’s 4 MB per-image limit
429 from SocialCutterWallet quota exhausted
Empty image fieldThe field slug is not what you assumed: check the schema
An asset is duplicated or missingWebflow uses the fileHash to avoid storing files with the same MD5 twice

Next steps

Frequently asked questions

What permissions does a Webflow site token need?

A site token created under Site settings → Apps & integrations → API access. For this flow enable the assets scopes (read and write), the cms scopes (read and write) and the sites scopes (read and write, so the site can be published). Check the exact scope names on the token screen, as they change between versions.

What does the create-asset endpoint return?

The 200 response carries id, uploadUrl (a presigned Amazon S3 URL for the binary), uploadDetails (the upload form fields), assetUrl (S3 link to the asset), hostedUrl (link to the asset), parentFolder and fields such as contentType, originalFileName and createdOn.

What is parentFolder for when creating the asset?

It is optional and it is the ID of the Assets folder the file lands in. Folders are created with POST /v2/sites/{site_id}/asset_folders, which takes displayName and an optional parentFolder and returns its id.

How do I check that the asset was uploaded correctly?

With GET /v2/assets/{asset_id}, which returns hostedUrl, contentType, size in bytes, originalFileName and altText. If hostedUrl does not load, the POST to uploadUrl was not completed with the uploadDetails fields.

Is there a size limit for Webflow asset uploads?

Yes: Webflow images must not exceed 4 MB and documents are capped at 10 MB. The SocialCutter API accepts masters up to 5 MB, so a large master may not go straight into Assets: run it through SocialCutter first, whose outputs are much smaller, or shrink it.

Can I use the Webflow asset URL as the source in SocialCutter?

Yes. After the master is uploaded, the API returns its hostedUrl; pass that URL as the source in POST /api/v1/images/process and SocialCutter will download the file from there.

Do I have to publish the site for CMS Collection images to show?

Items created through the live endpoint and a site published with POST /sites/{site_id}/publish become visible. If you only create draft items, they stay hidden until published.

How much does processing cost?

1 use per destination, meaning per platform and format combination you request. Repeated destinations in the same request are not charged twice and failures are refunded.