Review and approve pending WordPress posts with Gemini and Slack
When a writer submits a post for review, WordPress sends its ID to one n8n webhook. In n8n, the post is checked against your rules and reviewed by Gemini. An editor then gets it in Slack with one Review button: publish it, or send it back with a note that goes to the author by email.
Why it goes through n8n
WordPress has a Pending status but nothing that acts on it: someone has to remember to look. This build sends the moment of submission out, so the review starts right away and the editor never has to check the posts list.
n8n holds the WordPress, Gemini, Slack and email connections, so WordPress only ever talks to one URL. Change the house style, the word count or the review channel in n8n, and nothing changes in WordPress.
What you need first
- In n8n, import the template Review and approve pending WordPress posts with Gemini and Slack. In its Settings node, set the review channel, your house style, the minimum word count, whether a featured image or excerpt is required, how long the editor has to answer and the sender address.
- Connect WordPress (an administrator or editor account with an application password, because a pending post is not public), Gemini (an API key from Google AI Studio), Slack (a bot token with
chat:write, invited to the review channel) and SMTP inside n8n. - Open the Review Request Webhook node, add a Header Auth credential with the name
X-Webhook-Tokenand a long random token as the value, and copy the node's production URL. - Import this build. The webhook arrives disabled: replace its endpoint URL with that production URL, give the import the same token when it asks for the credential, then enable it. The token lives in the Credentials Vault and is never part of a shared build.
What gets sent
{
"post_id": 123
}Only the ID travels. wp_after_insert_post hands over the post ID, the post, an update flag and the post as it was before the save; args.0 becomes post_id and nothing else is sent. n8n reads the post and its author back through the WordPress API, so it always sees the saved version.
Three conditions decide when it fires: args.1.post_status equals pending, args.3.post_status does not equal pending, and args.1.post_type equals post. A post fires once, when it moves to Pending. Saving it again while it waits, and revisions, send nothing.
Why this hook
The block editor saves the post first and its featured image, categories and custom fields after. transition_post_status and save_post fire in between, so n8n would read a post with no featured image yet. wp_after_insert_post fires last, when everything is stored.
If a delivery fails
Delivery is asynchronous, so the writer never waits on n8n. A failed POST is retried 5 times (1m, 2m, 4m, 8m) and every attempt is kept in the delivery log. If n8n was down for the whole ladder, hit Replay once it's back and the post goes to review as if it had just been submitted.
A 200 from the webhook node only means n8n accepted the post. Check n8n's own execution list to see what happened next. If Gemini is down, the editor still gets the post, marked for review by hand. Before publishing, n8n checks the post is still pending, so a post someone already published by hand is left alone.
/ The conversation
This build was not assembled by hand. Its author described what they wanted in Build with AI, and the agent read the site, planned the work and applied it. This is that conversation.
When a writer submits a post for review, send its ID to my n8n webhook at https://n8n.flowsystems.pl/webhook/cd59e65c-51ed-4f71-af9a-b1045957df7c, using the "n8n post review webhook token" from the vault as the X-Webhook-Token header. Only for posts, only at the moment the post moves to Pending (not when it's saved again while it's already pending), and send just the post ID as post_id.
Checking the trigger payload schema for post submissions and locating the vault credential...
I'll create the webhook for the wp_after_insert_post trigger to capture the moment a post is submitted for review.
Here is how this will be configured:
- Trigger & Filter: Using
wp_after_insert_post, filtering withargs.1.post_type equals post,args.1.post_status equals pending, andargs.3.post_status not_equals pending. This triggers strictly when a post first enters pending review (ignoring subsequent saves while already pending, autosaves, and other post types). - Payload Mapping: Mapping
args.0topost_idand dropping unmapped fields so only{"post_id": ...}is sent. - Authentication: Attaching stored vault credential n8n post review webhook token (
X-Webhook-Tokenheader). - Delivery Mode: Background (asynchronous) mode is selected so post saves in WordPress are not delayed.
Since the trigger schema was read from the reference library, we'll verify the outgoing payload with a test dispatch before enabling the webhook.
Published by the author on purpose. Tool output and raw payloads never travel with a shared build, and secrets never leave the site they were created on.
/ Abilities used (6)
- create_webhook Create disabled webhook for post review submissions
- assign_credential Attach the n8n post review token vault credential
- set_conditions Filter for posts transitioning into pending status
- set_mapping Map post ID to post_id and drop unmapped fields
- test_dispatch Send test delivery to verify the payload and authentication with n8n
- enable_webhook Enable the webhook to go live
Each of these is a real ability the agent can call — see the docs for what each one does.
/ What runs
Every attempt is retried on its own schedule and written to the delivery log.
WordPress fires the action wp_after_insert_post, if the conditions below match, the payload is mapped and sent as POST to n8n.flowsystems.pl.
The delivery is queued and sent in the background, so the visitor never waits. Along the way authentication comes from a credential you supply on import.
/ How it is configured
/ Only when
- args.1.post_type equals post
- and args.1.post_status equals pending
- and args.3.post_status does not equal pending
- ↳ otherwise the delivery is skipped, and logged as skipped
/ Field mapping
/ Authentication
This build authenticates with a credential you supply on import. The shared file carries the name and type only — never the secret.
- n8n post review webhook token — api_key
/ Use this build
Download the file, then in WordPress open Webhook Actions → Webhooks → Import and drop it in. Anything needing authentication asks you to pick or create a credential as it imports — see Import & Export for what travels with a build and what does not.
/ Questions