Why?
API requests that create or modify a user’s account or posts can only be made by the user. These API requests require a “bearer token” to authenticate that the request is coming from the user. The function below obtains a token.
How?
Add this function to the Apps Script Editor and call it prior to the API request. It returns the token that will need to be included in the subsequent API request.
function getToken(userDid, userPassword) {
const API_URL = "https://bsky.social/xrpc/com.atproto.server.createSession";
// The domain must be wherever the user's account is registered; bsky.social is most common (ie the app/website)
const url = API_URL;
const params = {
"contentType": "application/json",
"method" : "post",
"payload" : JSON.stringify({"identifier": userDid, "password": userPassword})
};
const response = UrlFetchApp.fetch(url, params);
return JSON.parse(response.getContentText()).accessJwt;
}Notes
See Resolve a handle into a DID to obtain the user’s DID.
An account password for use with the script can be generated via the Bluesky app.
API requests that merely retrieve records are handled by public.api.bsky.app. However, API requests associated with authenticated requests to create or modify records must be handled by the “personal data server” (“PDS”) the user’s account is registered with. This is bsky.social for accounts created via the Bluesky app. Importantly, the token is valid only on the domain that created it, so any subsequent API requests utilising the token must be made to the same server.
Tokens will expire after a short while (the duration of the authenticated “session”). If being obtained for a single subsequent API request this will be fine. If being used for multiple requests in the same session it is conceivable that it might need to be refreshed; see the reference below if required.

