How to post to Bluesky using an Axiom.ai automation
Automating the creation of new posts to Bluesky can offer your brand an opportunity to get their voice out into a new environment quickly. This allows you to build automations that can use data to create your posts. Combine this with bots that scrape data from websites, or read data from Google Sheets and you have a powerful automation. Even better? Combine it with a schedule.
We recommend having a bit of experience with JavaScript before attempting this guide - our support team will be unable to assist with issues related to this code, or any modifications of it.
# Getting started
A Bluesky account is required to post on Bluesky, head to bsky.app (opens new window) to get started.
- Username - Your username can be found on your Bluesky profile.
- App password - To generate an app password head into Bluesky → Settings → Privacy and security → App passwords → "Add App Password". Store this for later - you won't be able to access it again.
To learn more about managing login credentials within your automations, see Logins.
# Creating a new Bluesky post using an Axiom.ai automation
To get started, create your automation as normal. Once you are ready to create your Bluesky post, continue with this guide. We'll show you how to use your automation data to create dynamic content. All of the code below is part of a single script which has been broken down to make it easier to follow and understand, these parts should all be combined into a single script within a single Write JavaScript step, see full script for the whole script.
# Step 1: Creating an authorisation session
Authorising your account is the first step to automating posting on Bluesky. To do this, you will need your username and app password to get started. To begin, create an object containing your credentials:
const authorisationData = {
"identifier": "<USERNAME>.bsky.social",
"password": "<APP_PASSWORD>"
}
Now that you have this, we can create a method to send a POST request to Bluesky to authorise your account. We'll also be creating the accessJwt and did variables to hold the two pieces of data we need from the response. We will be sending this request to the https://bsky.social/xrpc/com.atproto.server.createSession endpoint.
var accessJwt;
var did;
const authorise = async () => {
try {
// Create a POST request and send along your credentials from the `authorisationData` variable
const response = await fetch("https://bsky.social/xrpc/com.atproto.server.createSession", {
method: "POST,
headers: { "Content-Type": "application/json" },
body: JSON.stringify(authorisationData)
})
// Something went wrong with the request, double check your credentials and the URL used above
if (!response.ok) throw new Error(`Something went wrong: ${response.status}`);
// Parse the response
const result = await response.json();
// Access the information in the `results` variable for later use
accessJwt = result.accessJwt;
did = result.did;
} catch (error) {
throw error;
}
}
If all goes well, you'll now be authorised to continue with your script and create new posts on Bluesky.
# Step 2: Create a new post
To create a new post, ensure that you have completed the step above - without completing this step you will not be authorised to post to your Bluesky account. To create a new post, we first need to construct the body of the request that is to be sent to their API. This will use the data that you stored in the accessJwt and did variables previously.
const data = {
"repo": did,
"collection": "app.bsky.feed.post",
"record": {
"$type": "app.bsky.feed.post",
"text": "[custom-data]",
"createdAt": `${new Date().toISOString()}`
}
}
You'll notice in the line "text": "[custom-data]" we used an Axiom.ai data token - use the "Insert data" option to insert data from your automation into your "text" field to create dynamic posts that make use of your automation data.
Next, we will want to create the post itself. We will be using the https://bsky.social/xrpc/com.atproto.repo.createRecord endpoint. We'll also be creating a variable called status to store the status of the response.
var status;
const createPost = async () => {
// Throw an error if there is no access token
if (!accessJwt) throw new Error("Access token missing");
try {
// Create a POST request, using the `accessJwt` variable for authorisation, and the `createPostObject()` function to create your data
const response = await fetch("https://bsky.social/xrpc/com.atproto.repo.createRecord", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${accessJwt}`
},
body: JSON.stringify(data)
})
// Parse the response
const result = await response.json();
// Access the information in the `results` variable for later use
status = result.validationStatus;
} catch (error) {
throw error;
}
}
Finally, we are going to want to call these functions and return a response to be handled in step 3.
await authorise();
await createPost();
return status;
# Step 3: Handling the response
Now that your post has been posted, we can make use of your data. If all has gone well, the status variable that has been returned will contain a string valid. This will be stored in the code-data data token that is output from the 'Write Javascript' step. You can use this in various steps to make a dynamic automation, such as:
- If/else step.
- Continue only if a condition is met step.
- Conditionally jump to another step step.
# Full script
As previously mentioned, this script should be contained inside a single "Write Javascript" step. Find the full script below:
const authorisationData = {
"identifier": "<USERNAME>",
"password": "<APP_PASSWORD>"
}
var accessJwt;
var did;
const authorise = async () => {
try {
// Create a POST request and send along your credentials from the `authorisationData` variable
const response = await fetch("https://bsky.social/xrpc/com.atproto.server.createSession", {
method: "POST,
headers: { "Content-Type": "application/json" },
body: JSON.stringify(authorisationData)
})
// Something went wrong with the request, double check your credentials and the URL used above
if (!response.ok) throw new Error(`Something went wrong: ${response.status}`);
// Parse the response
const result = await response.json();
// Access the information in the `results` variable for later use
accessJwt = result.accessJwt;
did = result.did;
} catch (error) {
throw error;
}
}
const data = {
"repo": did,
"collection": "app.bsky.feed.post",
"record": {
"$type": "app.bsky.feed.post",
"text": "[custom-data]",
"createdAt": `${new Date().toISOString()}`
}
}
var status;
const createPost = async () => {
// Throw an error if there is no access token
if (!accessJwt) throw new Error("Access token missing");
try {
// Create a POST request, using the `accessJwt` variable for authorisation, and the `createPostObject()` function to create your data
const response = await fetch("https://bsky.social/xrpc/com.atproto.repo.createRecord", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${accessJwt}`
},
body: JSON.stringify(data)
})
// Parse the response
const result = await response.json();
// Access the information in the `results` variable for later use
status = result.validationStatus;
} catch (error) {
throw new Error(`Something wrong wrong: ${error}`)
}
}
await authorise();
await createPost();
return status;
# Testing your workflow
Once you have the script in place, hit "Run". If there are any errors, follow the instructions or see common errors for more details. If successful, you will see your post appear on Bluesky - congrats!
# Wrapping up
As Bluesky continues to grow in popularity, we are likely to see more and more brands move over to the social network site. Automating posting to this site has been made easy due to the protocols that they offer and when combined with Axiom.ai allows you to create an automated posting bot that can do most of the heavy lifting. You can also combine this bot with a bot that posts to Facebook, X and LinkedIn using our templates.