403 when attempting to post via API

I’ve written a Node application to create posts (see below), but every time I run it, I get a 403. However, if I use the same exact token, URL, and payload and send the request via the VS Code Rest Client, it works great, so clearly the authentication is correct.

Same issue if using fetch vs axios. I even told ChatGPT to convert it to Python and tried that with the same exact result.

Any thoughts as to why this could happen?

const GHOST_API_URL="http://localhost/ghost/api/admin"
const GHOST_API_KEY=1111111111:111111111

async function publishToGhost() {
    const [id, secret] = GHOST_API_KEY.split(':');
    const token = jwt.sign({}, Buffer.from(secret, 'hex'), {
        keyid: id,
        algorithm: 'HS256',
        expiresIn: '5m',
        audience: `/admin/`
    });

    const response = await axios.post(
        `${GHOST_API_URL}/posts`,
        { posts: [ { title: "Welcome!" }] },
        { 
             headers: { 
                Authorization: `Ghost ${token}`,
                "Content-Type": "application/json" 
             }
        }
    )

    if (response.status === 201) {
        console.log(`Published ${response.data.posts.length} posts: \r\n${response.data.posts.map(post => post.title).join('\r\n')}`);
    } else {
        throw new Error(`Failed to publish posts: ${response.status} ${response.statusText}`);
    }
}

Try sticking a trailing slash on that URL…

Do you mean

`${GHOST_API_URL}/posts/`

instead of

`${GHOST_API_URL}/posts`

That didn’t work either…

Yeah, that is what I meant. I’ve got a cloud function that makes posts. Let me find the code and I’ll post it.

Oh, and if you haven’t found the SDK yet, it tends to remove these headaches, IF it supports what you’re trying to do…

Here’s an API-based example - slightly messy because I cut out some logic you didn’t need.

export  async function pushPostToGhost(HTML, date, dateFull) {

    const api = new GhostAdminAPI({
      url: process.env.GHOST_API_URL,
      version: "v5.0",
      key: process.env.GHOST_ADMIN_API_KEY
      });

      let title = `What's Happening - ${date}, ${year}`
            // create a new post
            let post = {
                title: title,
                html: HTML,
                status: 'draft',
                tags: ['#daily-newsletter'],
                email_only: true,
                custom_excerpt: `News and events ${date}, ${year}` 
            }
            let response = await api.posts.add(post, {source: 'html'});
}
      ```

Thank you! Sorry, I also forgot to mention that I also got the same 403 error even with the JS admin API!

Anything informative in your Ghost error logs?

Thank you that helped a lot.
The logs were showing a 301 error and long story short the URL I was trying to call was not the one configured in the Ghost config. Thank you!

(Though I am still flabbergasted as to why it worked calling it from VS Code… never got to the root of that one.)

1 Like

Ahha! Glad you found it.

I suspect your vs code extension follows redirects by default.

Posting API. Has someone already tried to code the posts going to 20 or so publications via RSS? Syndication -ish?

That’s what I’m working on once I can get the trained GPT to send my email via this schema through Render’s endpoint.