Connect any site to Seonix
Expose a single REST endpoint on your site and Seonix will publish generated articles straight to it β with updates and optional deletes out of the box. Works with any stack: Node, Python, PHP, Go, Rails, WordPress, or a static site builder.
How it works
Seonix is the client. Your site exposes the endpoint. You generate, edit, and score articles inside Seonix, then click Publish β Seonix POSTs the article to your endpoint with a Bearer token. Re-publishes reuse the same external_id, so you never end up with duplicates.
1. Implement the endpoint
Accept a JSON POST, validate the Bearer token against your own secret, and upsert the article keyed on external_id. A few lines in any framework is enough β see the client samples below.
2. Connect the channel in Seonix
In your project's Channels, add a Custom API channel with the endpoint URL and the token. Verify and publish β Seonix pushes each article, stores the id you return, and keeps updates idempotent.
3. Updates and deletes reuse the id
Subsequent edits hit the same endpoint with the same external_id β upsert on your side. If you configure a delete URL template, deleting an article in Seonix triggers a DELETE on your endpoint.
Set up the connection
Three fields are required; two are optional. You can paste and save them in the Seonix Channels tab without leaving the app.
- 1
Pick a shared secret
Generate a long random string (32+ characters). It's the Bearer token Seonix sends in every request. Store it on your server; never hardcode it in client code.
- 2
Implement POST /your-endpoint
Validate the Authorization header against your secret, upsert the article keyed on external_id, and return { "id": "β¦", "url": "β¦" }. 4xx = permanent, 5xx = retry.
- 3
(Optional) Implement DELETE /your-endpoint/:id
If you want articles removed from your site when deleted in Seonix, handle DELETE with the same Bearer token. Return 200 on success; 404 is treated as already-gone.
- 4
Add the channel in Seonix
Open the project's Channels tab β Custom Webhook β Connect. Paste the endpoint URL, the token, and (optionally) the delete URL template, default locale, and author. Seonix verifies the connection on save.
- 5
Publish from the editor
Open any article, click Publish, pick your Custom API channel in the picker, and hit Publish now. Seonix stores the returned id so every subsequent update goes to the same record.
Channel config
The Custom API channel stores your endpoint URL, the bearer token, an optional delete URL template, and any extra headers you need. Secrets stay inside your Seonix project and are only read to build outbound requests.
{
"api_url": "https://your-site.example/api/articles",
"api_token": "YOUR_SHARED_SECRET",
"delete_url_template": "https://your-site.example/api/articles/{id}",
"headers": { "X-Custom": "optional" },
"lang": "en",
"author": "Your team"
}All config fields are stored inside your Seonix project, readable only by the Seonix backend, and never leave the engine. Edit the config any time through the same Channels β Custom Webhook β Manage modal.
Authentication
Pick any bearer-style token β it's a shared secret between your site and Seonix. Seonix sends it in the Authorization header of every request. Rotate it by updating the channel config; no server restart required.
- Authorization:Bearer <API token>
The full token is shown only once at creation. Copy it immediately; only a hash is stored. Lost a token? Create a fresh one and revoke the old key in the same admin panel.
Publish an article
Seonix sends a JSON POST to your configured URL for both creates and updates β the endpoint is an upsert keyed on external_id, so re-submitting the same id should update the existing record rather than creating a duplicate.
/your-endpointCreate or update (upsert).
POST https://your-site.example/api/articles
Authorization: Bearer YOUR_SHARED_SECRET
Content-Type: application/json
{
"external_id": "ext_article_170b63f6",
"slug": "how-to-automate-seo-content",
"lang": "en",
"title": "How to automate SEO content in 2026",
"excerpt": "Short summary shown on list pages and meta tags.",
"content_html": "<h2 id=\"intro\">Intro</h2><p>Body copyβ¦</p>",
"category": "automation",
"tags": ["seo", "ai"],
"cover_url": "https://cdn.example/covers/abc.jpg",
"author": "Your team",
"seo_title": "Automate SEO content in 2026 β a field guide",
"seo_description": "Short meta description.",
"og_image": "https://cdn.example/og/abc.jpg",
"published_at": "2026-04-16T09:00:00.000Z"
}HTTP/1.1 200 OK
Content-Type: application/json
{
"id": "42",
"url": "https://your-site.example/blog/how-to-automate-seo-content"
}Updates are the same POST with the same external_id. Fields you omit keep their previous value.
# Updates reuse the same POST.
# Send the same external_id β your endpoint should upsert on it.
# Omit a field to keep the stored value.
POST https://your-site.example/api/articles
Authorization: Bearer YOUR_SHARED_SECRET
{
"external_id": "ext_article_170b63f6",
"slug": "how-to-automate-seo-content",
"lang": "en",
"title": "How to automate SEO content in 2026 (refined)",
"excerpt": "Updated summary.",
"content_html": "<p>Updated bodyβ¦</p>"
}Delete an article
Configure a delete_url_template in the channel β Seonix will substitute {id} with the external_id and issue a DELETE with the same Bearer token. Skip the template to ignore deletes; 404 responses are treated as already-gone.
/your-endpoint/:idDelete an article. Uses the same Bearer token.
# {id} in delete_url_template is substituted with external_id
DELETE https://your-site.example/api/articles/ext_article_170b63f6
Authorization: Bearer YOUR_SHARED_SECRETPayload fields
This is the exact shape Seonix sends. Map the fields to your own schema server-side. Ignore anything you don't need β Seonix doesn't care which fields your CMS stores.
| Field | Type | Description |
|---|---|---|
| external_id | string? | Your stable id. When present, used as the upsert key; preserve it across updates and deletes. |
| translation_key | string? | Shared key that links EN and UA versions of the same article. Optional β same slug also links them. |
| slug | string | Lowercase, hyphen-separated. Same slug across locales keeps URLs aligned. |
| lang | "en" | "ua" | Locale of this version. |
| title | string | Article title. Default value for <h1> and <title>. |
| excerpt | string | Short summary for list pages and the meta description fallback. |
| content_html | string | Sanitized HTML body. Either this or content_markdown is required. |
| content_markdown | string | Markdown body, rendered to HTML on save via remark + GFM + syntax highlighting. |
| category | "seo" | "ai" | "automation" | "content-strategy" | "case-studies" | "product" | Taxonomy bucket shown on list pages. Must match one of the fixed values. |
| tags | string[] | Freeform topic tags, up to 20. |
| key_takeaways | string[] | Bullet list rendered at the top of the article page. |
| cover_url | string | Absolute URL of the 16/9 cover image. |
| author | string | Byline. Defaults to βSeonixβ. |
| seo_title | string? | Overrides <title> for search engines. |
| seo_description | string? | Overrides the meta description. |
| og_image | string? | Overrides the Open Graph image. |
| published_at | ISO string | Publish date. Use it as the canonical timestamp. |
Expected response
Reply with JSON carrying the record id and the public URL of the published page. Seonix persists the id as external_id and reuses it for the next update or delete. Anything else in the response is ignored.
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": "42",
"url": "https://your-site.example/blog/how-to-automate-seo-content"
}Errors and retries
Return 4xx for permanent errors (they surface to the operator as-is) and 5xx for transient failures β Seonix retries with exponential backoff. A short human-readable message in the body helps debugging.
| Code | Meaning |
|---|---|
| 200 | Success. Response carries id, url, updated_at. |
| 400 VALIDATION | Missing or invalid field. The message names the culprit. |
| 401 UNAUTHORIZED | Bearer header missing or token revoked. |
| 429 RATE_LIMITED | Too many requests from one IP. Respect the Retry-After header. |
| 5xx WRITE_FAILED | Transient blog-side failure. The engine retries with exponential backoff. |
Client samples
Minimal receivers you can drop into any backend. They validate the bearer token, upsert the article, and return the response Seonix expects.
# Minimal receiver test β simulate what Seonix sends you
curl -X POST "$YOUR_ENDPOINT" \
-H "Authorization: Bearer $YOUR_SHARED_SECRET" \
-H "Content-Type: application/json" \
-d '{
"external_id": "ext_abc123",
"slug": "how-to-automate-seo-content",
"lang": "en",
"title": "How to automate SEO content in 2026",
"excerpt": "Short summary.",
"content_html": "<p>Bodyβ¦</p>",
"category": "automation",
"tags": ["seo", "ai"],
"published_at": "2026-04-16T09:00:00.000Z"
}'Ready to ship?
Create a Seonix project, add your blog channel, and publish the first article in under ten minutes. The same token unlocks both the write and the delete paths.
Start free β