Automatically crossposts your WordPress blog posts to your Inkwell.social journal. Requires an Inkwell Plus subscription ($5/month) for API write access.
- Automatically crossposts when a post is published
- Sends the full post content as HTML to Inkwell
- Uploads your featured image as the Inkwell cover image
- Maps WordPress tags to Inkwell tags
- Extracts
#hashtagsfrom post content and sends them as Inkwell tags - Per-post privacy control (public, friends only, private)
- Per-post category override
- Disable crossposting on individual posts
- Manually trigger a crosspost from the post editor
- Crosspost log stored per post so you can track what was sent
- API key verification with persistent connected account display
- Show/hide toggle for the API key field
- Debug log page for diagnosing API issues
- Supports Jetpack Social Notes (
jetpack-social-notepost type)
| Requirement | Version |
|---|---|
| WordPress | 5.8 or higher |
| PHP | 7.4 or higher |
| Inkwell.social account | Required |
| Inkwell Plus subscription | Required for API write access |
- In your WordPress dashboard go to Plugins → Add New
- Search for Crosspost to Inkwell
- Click Install Now, then Activate
- Download the latest release zip from this repository
- In your WordPress dashboard go to Plugins → Add New → Upload Plugin
- Upload the zip file and click Install Now, then Activate
cd wp-content/plugins
git clone https://github.com/evecodesx/crosspost-to-inkwell.gitThen activate the plugin from your WordPress Plugins menu.
- Go to Settings → Crosspost to Inkwell
- Enter your Inkwell API key and click Verify Key
- The connected account name will be saved and displayed persistently
- Configure your default privacy setting and category
- Click Save Settings
- Log into inkwell.social
- Go to Settings → API
- Click Create API Key
- Copy the key immediately — it is only shown once
- Paste it into the plugin settings page
Once configured, the plugin will automatically crosspost every new post you publish. No further action needed.
Every post has a Crosspost to Inkwell panel in the editor sidebar where you can:
- Disable crosspost for that specific post
- Override the default privacy setting (public, friends only, private)
- Override the default category
- Manually send the post to Inkwell using the "Send to Inkwell Now" button
- View the crosspost log showing when the post was sent and its Inkwell entry URL
| Setting | Description |
|---|---|
| API Key | Your Inkwell API key (starts with ink_) |
| Default Privacy | Public, friends only, or private |
| Default Category | The Inkwell category to file entries under |
| Auto Crosspost | Automatically crosspost on publish |
| Crosspost on Update | Also crosspost when an existing post is updated (creates a new Inkwell entry each time) |
| Post Types | Which post types to crosspost (posts, pages, custom types, Jetpack Social Notes) |
| Send Featured Image | Upload the featured image as the Inkwell entry cover |
| Send Excerpt | Include the post excerpt in the Inkwell entry |
| Hashtag Tags | Extract #hashtags from post body and send as Inkwell tags |
Do I need an Inkwell Plus subscription? Yes. The Inkwell API requires a Plus subscription to create entries. You can verify your API key without Plus, but posts will not crosspost until you upgrade.
Will my full post content be sent to Inkwell? Yes. The plugin sends your complete post content as HTML. All standard WordPress content (blocks, shortcodes, embeds) is processed before sending.
Will updating a post create a duplicate on Inkwell? Not by default. The plugin only posts when a post is first published. You can optionally enable "Crosspost on Update" in settings, but this creates a new Inkwell entry on every update.
What happens if the API request fails? The error is recorded in the post's crosspost log, visible in the editor sidebar. The post is not marked as crossposted, so you can retry manually using the "Send to Inkwell Now" button.
Is there a character limit? No. Unlike short-form social platforms, Inkwell supports full journal entries with no character limit.
Can I stop a specific post from being crossposted? Yes. Check "Disable crosspost for this post" in the editor sidebar before publishing and it will be skipped.
Does it work with Jetpack Social Notes?
Yes. Enable the jetpack-social-note post type in Settings → Post Types. Hashtag extraction works on Social Notes too. Since Social Notes have no real title, the plugin uses a date/time string (e.g. March 16, 2026 1:24 AM) as the Inkwell entry title instead of Jetpack's placeholder.
How does hashtag extraction work?
When the Hashtag Tags setting is enabled, the plugin scans the post body for #hashtag patterns, sanitizes them, and merges them with your existing WordPress post tags before sending to Inkwell. Duplicates are removed automatically.
What is the Debug Log? The Debug Log page (Settings → Crosspost to Inkwell → 🪲 Debug Log) records every API request and response when enabled. Useful for diagnosing connection issues. Disable it when not needed.
This plugin connects to the Inkwell.social API (api.inkwell.social) to create journal entries on your behalf. By using this plugin you agree to the Inkwell Terms of Service.
Data sent to Inkwell.social:
- Post title
- Full post content (as HTML)
- Post excerpt (optional)
- Post tags +
#hashtagsextracted from content (optional) - Category
- Featured image (optional, uploaded as base64)
No data is sent to any service other than your own Inkwell.social account. No user tracking occurs. Full API documentation is available at inkwell.social/developers.
v1.0.0 — Submitted to WordPress.org 03/10/26
02/27/26, 1:02 AM EST — Initial build
- Plugin created with auto-crosspost on publish via
transition_post_statushook - Manual crosspost button in the post editor sidebar (AJAX, no page reload)
- Featured image upload via
POST /api/imagesbefore entry creation - Post tags synced and sanitized to Inkwell's alphanumeric+hyphen format
- Per-post overrides: disable crosspost, custom category, custom privacy
- Crosspost status and Inkwell entry URL stored in post meta and displayed in sidebar
- Configurable default privacy (
public,friends_only,private) - Optional featured image and excerpt sending
- Support for custom post types in addition to standard posts
03/01/26, 12:55 AM EST — API layer & double-post fix
- Rebuilt API integration against the official Inkwell API docs
- Added live API key verification on settings page
- Fixed double-posting bug: WordPress fires
transition_post_statusmultiple times per save; resolved with static$fired[]array + transient lock +_inkwell_crosspostedmeta guard - Plugin renamed to "WP Inkwell Crosspost"
03/05/26, 9:12 AM EST — Stability
- Rolled back an unstable version of the double-post fix; restored stable build
03/06/26, 12:39 AM EST — Submission prep begins
- Mapped full WordPress.org plugin directory submission checklist
03/09/26, 4:02 PM EST — readme & validator
readme.txtwritten to WordPress.org spec- Donate link added
Tested up toupdated to 6.9
03/09/26, 6:15 PM EST — Naming compliance
- Plugin renamed from "WP Inkwell Crosspost" → "Crosspost to Inkwell" per WP.org Guideline 17
03/09/26, 7:04 PM EST — Security audit & PCP compliance
- Full output escaping audit:
esc_html(),esc_attr(),esc_url(),esc_textarea()applied throughout - All
$_POSTreads wrapped withwp_unslash()+ appropriate sanitize function sanitize_key()for post type slugs;sanitize_text_field()for all other string inputs- All forms and AJAX actions protected by nonces via
check_ajax_referer() register_setting()modernized to array format withsanitize_callbackin_array()uses strict comparison (true) throughoutmb_substr()explicitly specifiesUTF-8encodingfile_get_contents()replaced with WP Filesystem API
03/09/26, 8:39 PM EST — i18n, JS & form cleanup
- Full i18n across all ~99 user-facing strings with
/* translators: */comments load_plugin_textdomain()removed (auto-loaded since WP 4.6)- Nested
<form>removed — Verify Key button converted to pure AJAX - All JavaScript moved to properly registered handles via
wp_add_inline_script(); no raw<script>blocks - Version set to 1.0.0; settings stored in
wp_options; no custom database tables
03/10/26, 10:02 PM EST — Final pre-submission fixes
- All
wp_enqueue_script()calls moved toadmin_enqueue_scriptshook current_time('mysql')replaced withwp_date('Y-m-d H:i:s')(deprecated since WP 5.3)wp_register_script()srcchanged from''tofalsefor inline-only handlesRequires at least: 5.8andRequires PHP: 7.4added to PHP file header- License URI unified to
gpl-2.0.htmlacross PHP header and readme - External service disclosure and privacy notice added to readme.txt
- Plugin slug renamed from
inkwell-crosspost→crosspost-to-inkwellthroughout - Text domain:
crosspost-to-inkwell - Submitted to WordPress.org plugin directory ✅
03/15/26 — "Publishing failed" fix (WordPress 6.9.4)
- Crosspost deferred via
shutdownaction hook when publishing via block editor (REST_REQUEST) — prevents blocking Gutenberg's publish response ignore_user_abort(true)added to keep DB connections alive after response is sentfastcgi_finish_request()called when available for immediate response flush on PHP-FPM hosts- Shutdown execution wrapped in
try/catch (\Throwable)to surface silent fatal errors - Fixed
add_transient()unavailable in shutdown context — replaced with directget_post_meta()check +force=true
03/15/26 — Debug Log page
- New admin page at Settings → Crosspost to Inkwell → 🪲 Debug Log
- Enable/disable toggle — off by default
- Dark terminal-style log viewer, color-coded by entry type
- Refresh (live AJAX) and Clear Log buttons
- Log stored in
wp_options, capped at 200 entries - 26 log points throughout the full crosspost flow
- Debug Log link added to settings page header and plugin action links
03/15/26 — Verify Key reads live field value
- Fixed "No API key entered" error when clicking Verify Key before saving
- JS now reads the current field value and passes it in the AJAX request
03/15/26 — API key show/hide toggle
- 👁 / 🔒 button added inline with the API key field
- Typing in the field clears the connected account status
03/16/26 — Persistent connected account display
- Connected account name and handle saved to
wp_optionson successful verify - Displayed persistently on page load — no longer disappears on refresh
- Fixed sanitize callback silently overwriting newly saved account info on every
update_optioncall wp_kses()used instead ofsanitize_text_field()to preserve emoji and Unicode in display names- Account info cleared when API key is changed
03/16/26 — Hashtag-to-tag extraction
- New Hashtag Tags setting (enabled by default) under Settings → Entry Settings
- Scans post body for
#hashtagpatterns after stripping HTML - Sanitized and merged with WP post tags; duplicates removed
- Debug log records extracted hashtags by name
03/16/26 — Jetpack Social Notes support
jetpack-social-notepost type explicitly added to Post Types checklist viapost_type_exists()fallback- Hashtag extraction works on Social Notes
- Social Notes with Jetpack placeholder titles (
#NuMb3rs) replaced with readable date/time string - Fixed timezone offset in date title — uses
post_date_gmtwithwp_date()for correct local time
03/16/26 — Plugin Check (PCP) fixes
/* translators: */comment moved directly above__()call insidewp_kses()INKWELL_DEBUG_LOG_MAXwrapped inabsint()for output escaping compliancephpcs:ignoreupdated to suppressInputNotSanitizedon already-sanitized$_POST['api_key']error_log()removed — same data captured byinkwell_debug_log()