Skip to main content
Many Memberships endpoints reference images by an image resource ID rather than accepting a raw file. For example, Create Tier takes an imageResourceId, Create Video Post takes an imageResourceId, and Create Post Series takes a coverImageResourceId and a thumbnailResourceId. Before you can set one of those fields, you must upload the image and obtain its resourceId. Uploading is a three-step flow: create the image, upload the bytes directly to storage, then confirm the upload.
All image endpoints require the memberships_write OAuth scope. API keys have full access.

Overview

  1. Ask Fourthwall to create an image, sending the file’s byteSize and checksum.
  2. Fourthwall responds with a signed, pre-authorized uploadData.url, the headers you must send with the upload, a signedId, and the resourceId you’ll ultimately reference.
  3. Upload the raw image bytes directly to cloud storage using the returned URL and headers.
  4. Confirm the upload with Fourthwall, passing back the signedId.
  5. Once confirmed, the resourceId is ready to be used on any endpoint that accepts an image resource.

Step 1 — Create the image

Compute the image’s byte size and a base64-encoded MD5 checksum, then call Create Image.
curl -X POST "https://api.fourthwall.com/open-api/v1.1/memberships/images" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "byteSize": 20481,
    "checksum": "7vJP+O8buKShZwMbdl7wJQ=="
  }'
import { readFile } from "node:fs/promises";
import { createHash } from "node:crypto";

const bytes = await readFile("./tier-image.png");
const checksum = createHash("md5").update(bytes).digest("base64");

const res = await fetch(
  "https://api.fourthwall.com/open-api/v1.1/memberships/images",
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${accessToken}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ byteSize: bytes.length, checksum }),
  },
);

const { uploadData, resourceId } = await res.json();
The response contains everything you need for the next two steps:
{
  "uploadData": {
    "url": "https://storage.googleapis.com/...",
    "headers": {
      "content-MD5": "7vJP+O8buKShZwMbdl7wJQ==",
      "content-Disposition": "inline; filename=\"7c8f5486-7841-4890-841f-53a3ebe2b445\"; filename*=UTF-8''7c8f5486-7841-4890-841f-53a3ebe2b445",
      "cache-Control": "public, max-age=2592000"
    },
    "signedId": "eyJfcmFpbHMiOnsib..."
  },
  "resourceId": 123456
}
FieldDescription
uploadData.urlPre-authorized URL to PUT the raw image bytes to.
uploadData.headersHeaders you must send verbatim with the direct upload.
uploadData.signedIdOpaque token identifying this upload. Pass it back in Step 3.
resourceIdThe ID you’ll use as imageResourceId, thumbnailResourceId, etc.
The checksum you send here must be the base64-encoded MD5 of the exact bytes you upload in Step 2. Storage rejects the upload if the content-MD5 header does not match the uploaded content.

Step 2 — Upload the bytes directly

PUT the raw image bytes to uploadData.url, sending every header from uploadData.headers. This request goes to cloud storage directly — do not send your Fourthwall Authorization header to it.
curl -X PUT "$UPLOAD_URL" \
  -H "Content-MD5: 7vJP+O8buKShZwMbdl7wJQ==" \
  -H "Content-Disposition: inline; filename=\"...\"" \
  -H "Cache-Control: public, max-age=2592000" \
  --data-binary "@tier-image.png"
await fetch(uploadData.url, {
  method: "PUT",
  headers: uploadData.headers,
  body: bytes,
});
A successful upload returns a 200 OK from storage with no body.

Step 3 — Confirm the upload

Tell Fourthwall the bytes are in place by calling Confirm Image Upload with the resourceId in the path and the signedId from Step 1 in the body.
curl -X PATCH "https://api.fourthwall.com/open-api/v1.1/memberships/images/123456/confirm" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "signedId": "eyJfcmFpbHMiOnsib..."
  }'
await fetch(
  `https://api.fourthwall.com/open-api/v1.1/memberships/images/${resourceId}/confirm`,
  {
    method: "PATCH",
    headers: {
      Authorization: `Bearer ${accessToken}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ signedId: uploadData.signedId }),
  },
);
A successful confirmation returns 204 No Content. The image resourceId is now ready to use.

Step 4 — Reference the image

Pass the resourceId into whichever field the target endpoint expects. For example, when creating a paid tier:
cURL
curl -X POST "https://api.fourthwall.com/open-api/v1.1/memberships/tiers" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Gold",
    "imageResourceId": 123456
  }'
The same resourceId can be used for any image field across the Memberships API:
FieldUsed by
imageResourceIdCreate Tier, Create Audio Post, Create Video Post
thumbnailResourceIdCreate Post Tag, Create Post Series
coverImageResourceIdCreate Post Series
Use https://api.fourthwall.com for production. For testing against staging, swap in https://api.staging.fourthwall.com.