SeonixSeonix
Custom API integration

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. 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. 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. 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. 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. 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.

json
{
  "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.

POST/your-endpoint

Create or update (upsert).

http
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
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.

http
# 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.

DELETE/your-endpoint/:id

Delete an article. Uses the same Bearer token.

http
# {id} in delete_url_template is substituted with external_id
DELETE https://your-site.example/api/articles/ext_article_170b63f6
Authorization: Bearer YOUR_SHARED_SECRET

Payload 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.

FieldTypeDescription
external_idstring?Your stable id. When present, used as the upsert key; preserve it across updates and deletes.
translation_keystring?Shared key that links EN and UA versions of the same article. Optional β€” same slug also links them.
slugstringLowercase, hyphen-separated. Same slug across locales keeps URLs aligned.
lang"en" | "ua"Locale of this version.
titlestringArticle title. Default value for <h1> and <title>.
excerptstringShort summary for list pages and the meta description fallback.
content_htmlstringSanitized HTML body. Either this or content_markdown is required.
content_markdownstringMarkdown 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.
tagsstring[]Freeform topic tags, up to 20.
key_takeawaysstring[]Bullet list rendered at the top of the article page.
cover_urlstringAbsolute URL of the 16/9 cover image.
authorstringByline. Defaults to β€œSeonix”.
seo_titlestring?Overrides <title> for search engines.
seo_descriptionstring?Overrides the meta description.
og_imagestring?Overrides the Open Graph image.
published_atISO stringPublish 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.

json
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.

CodeMeaning
200Success. Response carries id, url, updated_at.
400 VALIDATIONMissing or invalid field. The message names the culprit.
401 UNAUTHORIZEDBearer header missing or token revoked.
429 RATE_LIMITEDToo many requests from one IP. Respect the Retry-After header.
5xx WRITE_FAILEDTransient 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.

bash
# 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 β†’