WordPress
Already have a WordPress site? Point your blog at it and every post you publish here is pushed there automatically — as a real WordPress post, in your theme, at your permalinks. No plugin to install. WordPress core has shipped everything needed since 5.6: the REST API and Application Passwords.
Connect in three steps
Create an application password in WordPress In WordPress, go to Users → Profile, scroll to Application Passwords, type a name (
baas), and press Add New Application Password. WordPress shows a 24-character password once — copy it now.Connect as an administrator or editor. Those roles hold the
unfiltered_htmlcapability, which is what lets your rich content arrive intact — see What survives the push below.Paste it into the blog's delivery card On your blog page in baas, find Where posts go, switch delivery to Push to WordPress, and fill in:
Field Value WordPress site URL Your site root, e.g. https://example.com— not the/wp-adminURLWordPress username The login name of that user, not their email Application password The 24-character string, spaces and all Posts arrive as Published, orDraftif you want a final look inside WordPressWe store the password encrypted (AES-256-GCM) and never display it again — the field stays blank on every later visit, and saving with it blank keeps the stored one.
Test the connection Press Test connection. A green result names the WordPress user we authenticated as. Anything else tells you exactly what WordPress said — see Troubleshooting below.
What gets pushed, and when
A post is pushed when it becomes published — the moment you approve it in the review queue, or the moment auto-publish publishes it for you. The push carries:
| WordPress field | From |
|---|---|
title | The post title |
slug | The post slug — your permalink stays predictable |
content | The rendered HTML, wrapped and styled (below) |
excerpt | The generated excerpt |
status | Whatever you chose in Posts arrive as |
date_gmt | The publish time recorded here |
Pushing again updates the same WordPress post rather than creating a second one: we remember the WordPress post id. If you delete the post inside WordPress, the next push recreates it.
Taking a post down
Take down on a post pulls it here and on WordPress: the article stops being served by us, and its WordPress copy is set back to draft, so it answers 404 on your site while the content stays in your CMS. We never delete or trash a post on your site — the draft keeps the same WordPress post id, so Publish again pushes over the same post instead of creating a second one.
If WordPress cannot be reached, the withdrawal retries with backoff and the card shows the error; the post is already down here either way.
What survives the push
Our posts are richer than plain HTML — callouts, step guides, comparison
panels, charts, annotated code. WordPress decides how much of that it
accepts based on one capability of the user you connected as:
unfiltered_html.
Holds unfiltered_html on a single-site install.
- Full component styling — we send a scoped
<style>block - Charts, flow diagrams, sparklines (SVG) all render
- Your blog theme palette travels with the content
- The styles are scoped to our wrapper and cannot repaint your theme
Lacks unfiltered_html, so WordPress runs your content through its KSES
filter.
- We deliberately send no
<style>block — WordPress strips the tags but leaves the CSS behind as visible text, which would dump a stylesheet into your article - SVG is removed entirely, so charts and diagrams disappear
- Text, headings, lists, tables, code, callout structure still arrive
The dashboard warns you about this after a connection test, so you find
out before your readers do. On WordPress multisite, only a Super Admin
holds unfiltered_html — that is a WordPress rule, not ours.
SEO fields (Yoast and Rank Math)
We send the focus keyphrase, SEO title, and meta description for both Yoast SEO and Rank Math on every push. Whether they stick is up to your site:
To make them stick, drop this must-use plugin into
wp-content/mu-plugins/baas-seo-bridge.php — byte-identical to the file
this project's own compose stack mounts into its WordPress dogfood target:
<?php
/**
* Plugin Name: baas SEO bridge
* Description: Exposes the Yoast SEO and Rank Math post-meta fields over the
* WordPress REST API so an external publisher can set the focus
* keyphrase, SEO title, and meta description when it pushes a
* post. Without this, WordPress silently drops those keys. Also
* registers the _baas_post_id ownership marker so a retried push
* recognises a post it already created and updates it in place
* instead of publishing a duplicate — and never touches a post it
* did not create.
* Version: 1.1.0
* License: MIT
*
* Drop this file in wp-content/mu-plugins/ — must-use plugins load
* automatically and cannot be deactivated by accident.
*/
defined('ABSPATH') || exit;
add_action('init', function () {
$keys = [
// Yoast SEO. Leading underscore = protected meta, which the REST API
// refuses to touch unless it is registered with an auth_callback.
'_yoast_wpseo_focuskw',
'_yoast_wpseo_metadesc',
'_yoast_wpseo_title',
// Rank Math. Not underscore-prefixed, but still unregistered for REST
// on a stock install, so it needs the same treatment.
'rank_math_focus_keyword',
'rank_math_description',
'rank_math_title',
// baas ownership marker: our own post uuid, written on create and read
// back (context=edit) so an id-less retry adopts only the post WE made.
'_baas_post_id',
];
foreach ($keys as $key) {
register_post_meta('post', $key, [
'type' => 'string',
'single' => true,
'show_in_rest' => true,
'auth_callback' => function ($allowed, $meta_key, $post_id) {
// Same gate the editor screen uses: whoever may edit the post
// may set its SEO fields.
return current_user_can('edit_post', $post_id);
},
]);
}
});
// Keep the ownership marker out of PUBLIC (view-context) REST responses. We
// only ever read it back in edit context (authenticated) to recognise a post
// we created; exposing it to anonymous callers would leak our internal post
// id and baas provenance on the customer's own public site.
add_filter('rest_prepare_post', function ($response, $post, $request) {
if (($request['context'] ?? 'view') !== 'edit') {
$data = $response->get_data();
if (isset($data['meta']['_baas_post_id'])) {
unset($data['meta']['_baas_post_id']);
$response->set_data($data);
}
}
return $response;
}, 10, 3);
Everything else — title, slug, content, excerpt, publish date, status — works on stock WordPress with no plugin at all.
Pull with the plugin instead
If you would rather not hand us an application password, install the DailySmith Sync plugin and let WordPress pull. It polls the sync feed hourly with your site's public key, creates or updates posts with the same content, SEO fields, featured image and styling the push sends, and never overwrites a post it did not create. It also registers the SEO fields and the ownership marker above, so with it installed you do not need the bridge snippet, and a site running both channels ends up with one WordPress post per article.
The plugin is open source (MIT) and lives on GitHub:
TechnicalInsideTI/dailysmith-wordpress-plugin.
Download the zip from the latest release, upload it under Plugins → Add
New, then open Settings → DailySmith Sync and paste your pk_… key.
Troubleshooting
| What you see | What it means |
|---|---|
401 (incorrect_password) | The application password is wrong or was revoked. Generate a new one — the old value is unrecoverable. |
401 with a correct password | Your server is stripping the Authorization header. On Apache, add RewriteRule .* - [E=REMOTE_USER:%{HTTP:Authorization}] right after RewriteEngine On in .htaccess. |
403 (rest_cannot_create) | The user authenticated but is not allowed to create posts. Use an editor or administrator. |
404 on the REST route | Pretty permalinks are off, or a security plugin has disabled the REST API. Set Settings → Permalinks to anything other than "Plain". |
| "returned something that is not JSON" | The URL is not the site root — use https://example.com, not https://example.com/wp-admin. |
| "Application passwords are not available" | WordPress only offers them over HTTPS. Put the site behind TLS. |
| Styling missing on WordPress | The connected user lacks unfiltered_html — see What survives the push above. |
Security notes
- The application password is stored encrypted at rest (AES-256-GCM, with a fresh nonce per value and the ciphertext bound to the blog it belongs to). It is never returned by any API response, never logged, and never rendered in the dashboard.
- An application password is not your WordPress login. Revoking it in Users → Profile cuts us off immediately and changes nothing else.
- Give it the least role that can publish. Editor is usually right; administrator is only needed if you also want plugin-level SEO fields set by a user who can install the bridge snippet.
- We require
https://for the site URL. An application password sent over plain HTTP is readable by anything on the path.
Self-hosting note
Operators running their own baas deployment must set
BAAS_ENCRYPTION_KEY to 32 random bytes, base64 encoded
(openssl rand -base64 32); without it the API refuses to store WordPress
credentials at all. To rotate: move the current value into
BAAS_ENCRYPTION_KEYS_OLD (comma-separated, decrypt-only), put a fresh key
in BAAS_ENCRYPTION_KEY, and restart. Stored credentials keep working
because the retired key still decrypts them — but they are not
re-encrypted automatically. To finish retiring the old key, re-save each
WordPress connection (that rewrites it under the new key), then drop the
retired key from BAAS_ENCRYPTION_KEYS_OLD.