draft → publish
Accept once and fetch the public post by ID.
Turn one public WordPress post into distinct social drafts with the title, excerpt, link, categories, and proof intact—without firing on revisions or private content.

WordPress fires status hooks when a post is saved, even when the status did not truly change. The gate checks old and new status, post type, visibility, and password before Emelyn fetches anything.
add_action(
'transition_post_status',
function ($new_status, $old_status, $post) {
if ($old_status === 'publish' || $new_status !== 'publish') return;
if ($post->post_type !== 'post') return;
if (!empty($post->post_password)) return;
wp_remote_post(EMELYN_WEBHOOK_URL, [
'headers' => [
'Content-Type' => 'application/json',
'X-Emelyn-Signature' => sign_post_id($post->ID),
],
'body' => wp_json_encode(['post_id' => $post->ID]),
]);
},
10,
3
);Webhook recipe revision 1.0 · WordPress 6.9 REST schema · Last tested 29 August 2026.
Accept once and fetch the public post by ID.
Treat as an edit. Do not create a new campaign automatically.
Ignore. These are editing artifacts, not public moments.
Reject before content leaves WordPress.
Create a WordPress user for the integration, give it only the role needed to read eligible posts and media, then generate an Application Password. Use HTTPS for every REST request.
Application Passwords are per-application credentials. They can be revoked without changing the person’s main login password.
Do not reuse an administrator account. Confirm it can read the exact post types you selected.
Copy it once into Emelyn’s encrypted credential store. Never put it in the webhook body or URL.
Set the Emelyn webhook endpoint and signature secret outside the public theme repository.
Confirm the post ID, REST fetch, field map, visibility gate, distinct channel drafts, and review queue.
The webhook carries a signed post ID. Emelyn then reads the current public REST representation, maps the source, strips unsafe markup, and creates channel-native drafts in review.
GET /wp-json/wp/v2/posts/87234?context=view
{
"id": 87234,
"date_gmt": "2026-08-29T09:30:00",
"link": "https://example.com/the-small-release",
"status": "publish",
"title": { "rendered": "The small release" },
"excerpt": { "rendered": "What changed and why." },
"content": { "rendered": "<p>Full article…</p>" },
"featured_media": 441,
"categories": [12],
"tags": [28, 31]
}| WordPress | Emelyn | Purpose |
|---|---|---|
| id | wordpress.post_id | Duplicate and retry key |
| link | source_url | Original article destination |
| title.rendered | source_title | Angle context |
| excerpt.rendered | source_summary | Short source explanation |
| content.rendered | source_text | Claims and examples to preserve |
| featured_media | source_media_id | Resolve separately; do not guess |
| categories and tags | source_topics | Optional filters and context |
Every attempt records the post ID, HTTP result, and next step. A retry updates the same intake record; it does not manufacture a second social campaign.
The signed post ID is queued. WordPress can finish the publish request.
Retry with backoff up to four times using the same post ID.
Stop. Rotate the Application Password or correct the user role.
Hold for review. A plugin, cache, or custom post type may hide the REST route.
Limits: custom post types must expose REST fields; page-builder content may need a source adapter; protected or private content stays out; media resolution is bounded; and a published article can still require a person to choose the right promotional angle.
People want reliable first-publish sharing, control over title, summary, link, tags, and channel variations, and a price-safe alternative to stacking plugins. The implementation uses core status hooks, REST fields, and revocable Application Passwords.