TeamDesign.Team Social Posts

I knew as soon as I registered the teamdesign.team domain and started building the site that I wanted to automatically share new posts across several social channels. Currently, new design team stories get cross-posted to LinkedIn, Threads, Bluesky, and Mastodon.

In case anyone is interested in automating posts to one or more of those channels, I thought I’d share how I set that up. Thanks to WP RSS Aggregator, I was able to set up a custom RSS feed to use as the source. From there, I ended up creating a 10-step Zapier Zap to handle the posting. It’s not perfect, but it works. Here’s the breakdown of each step. (Note: Some of these steps require a Pro Zapier plan.)

Screenshot of the RSS step in ZapierRSS by Zapier step settings

1. New Item in Feed

RSS by Zapier

URL: https://teamdesign.team/feed/teams/
What Triggers a New Feed Item?: Different Guid/URL

2. Get current Date/Time

Formatter by Zapier

Action event: Date / Time
Transform: Add/Subtract Time
Input: Current time
Express: -2 days
To Format: MM/DD/YYYY

3. Ensure recency

Filter by Zapier

Only continue if Published Date is after the step 2 output date. I did this to prevent changes to the feed posting a bunch of older posts at once or updates to older posts on the external sites from triggering a new social post.

4. Post to Mastodon

Webhooks by Zapier

To post to Mastodon, you have to set up an app token. Go to settings > development in your account and create a new application. Be sure to check the box for write:statuses.

URL: https://mastodon.design/api/v1/statuses?access_token=APPTOKEN
Payload Type: Form
Data:
Status: “New Post from [1. Raw Source Title]: [1. Link]”

5. Get Link Metadata

Webhooks by Zapier

LinkedIn requires an image, title, and description for attached link previews, so I created a free account on urlmeta.org to fetch the opengraph data for each blog post URL.

Method: GET
URL: https://api.urlmeta.org/meta?url=[1. Link]
Headers: Authorization: Basic URLMETAKEY

6. Post to LinkedIn

LinkedIn – Create Company Update

With the attached link preview metadata, I had all the values I needed for Zapier’s LinkedIn Create Company Update action.

Update content: “New Post from [1. Raw Source Title]: [1. Link]”
Preview: [1. Link]
Preview – Thumbnail Image: [5. Article thumbnail URL]
Preview – Title: [5. Article title]
Preview – Description: [5. Article summary]

Screenshot of the bluesky settings for create post in ZapierZapier settings for creating a post on Bluesky.

7. Format Image Preview URL for Bluesky

Formatter by Zapier

Bluesky limits image uploads to 1MB, so I changed the query string value from urlmeta to get a smaller image. Even at 800px, I still occasionally get a notification from Zapier that a post didn’t go through because the image was too large.

Action Event: Text
Input: [5. Article thumbnail URL]
Find: “resize:fit:1200”
Replace: “resize:fit:800”

8. Post to Bluesky

Bluesky – Create Post

When I first added Bluesky posting, there was no native Zapier/Bluesky connection. There was a 3rd party integration from Unshape, but they charged $6/mo. To avoid that fee, I half-vibe-coded a Code by Zapier step. I’m so glad they built an integration. Now, if only Threads would do the same…

Text: “New Post from [1. Raw Source Title]: [1. Link]”
Reply to: False
Embed Type: External Link
URL: [1. Link]
Title: [5. Article title]
Description: [5. Article summary]
Thumbnail URL: [7. Output URL]

9. Create Threads Container

Code by Zapier

The code for these last two steps are a bit messy, but it could’ve been worse if I didn’t use AI. To post to Threads, you’ll have to create a Meta App that’s connected to the Threads profile you want to post to. Once you have that set up, you only need 2 strings: a Threads token and your Threads user id.

Be sure when you set up the token that you exchange it for a long-lived access token. The initial token you get back is only good for an hour.

This script creates the post, but it won’t be live yet. Threads returns a container ID for the post in the success response that we’ll use to publish in the final step.

Action Event: Run Javascript
Input Data:
message: “New Post from [1. Raw Source Title]: [1. Link]”
linkAttachment: [5. Meta Site Canonical]
accessToken: THREADSTOKEN
userID: THEADSID

Code:

const accessToken = inputData.accessToken;
const message = inputData.message; 
const linkAttachment = inputData.linkAttachment; 
const threadsUserID = inputData.userID;
const url = `https://graph.threads.net/v1.0/${threadsUserID}/threads`;

const headers = {
  'Authorization': `Bearer ${accessToken}`,
  'Content-Type': 'application/json'
};

const postBody = {
  text: message,
  link_attachment: linkAttachment,
  media_type: 'TEXT',
};

try {
  const response = await fetch(url, {
    method: 'POST',
    headers: headers,
    body: JSON.stringify(postBody)
  });

  const responseBody = await response.json();

  if (!response.ok) {
    throw new Error(`Error: ${responseBody.error.message}`);
  }

  // Success response
  return {
    success: true,
    message: 'Post created successfully',
    postId: responseBody.id,
    postDetails: responseBody
  };
} catch (error) {
  return {
    success: false,
    message: `Failed to create post: ${error.message}`,
  };
}

10. Post to Threads

Code by Zapier

Last step! With the container ID, we use the user ID and the token again to publish to Threads.

Action Event: Run Javascript
Input Data:
containerID: [9. Post Id]
userID: THEADSID
accessToken: THREADSTOKEN
Code:

const MEDIA_CONTAINER_ID = inputData.containerID;
const THREADS_USER_ID = inputData.userID;
const ACCESS_TOKEN = inputData.accessToken;

// Threads API endpoint for publishing a post
const API_ENDPOINT = `https://graph.threads.net/v1.0/${THREADS_USER_ID}/threads_publish`;

// Function to publish a post
const publishThreadPost = async (userId, creationId, accessToken) => {
  try {
    const response = await fetch(`${API_ENDPOINT}?creation_id=${creationId}&access_token=${accessToken}`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json', // Optional, can be omitted for some APIs
      },
    });

    if (!response.ok) {
      const errorDetails = await response.json();
      throw new Error(`Error: ${response.status} - ${errorDetails.message}`);
    }

    const result = await response.json();
    return result;
  } catch (error) {
    console.error('Failed to publish post on Threads:', error.message);
    return {
      error: error.message
    };
  }
};

// Execute the function
return publishThreadPost(THREADS_USER_ID, MEDIA_CONTAINER_ID, ACCESS_TOKEN).then(result => result);

Leave a Reply

Your email address will not be published. Required fields are marked *

Back to the top!