auto_awesome

SEOAgent.TEAM

Custom Website Integration

Not using WordPress or Shopify? Connect SEO Agent to any website that has a REST API.

api Two Integration Methods

A Custom API Recommended

Already have a REST API? Connect directly with configurable endpoints and field mapping.

  • ✓ Use your existing API as-is
  • ✓ Flexible auth: Bearer, Basic, API Key
  • ✓ Map field names to your API format
  • ✓ JSON or multipart/form-data

B WordPress API Bridge

Implement WordPress REST API format on your server. More work, but full feature parity.

  • ✓ Full media upload support
  • ✓ Categories & tags management
  • ✓ Scheduling support
  • ✓ Uses same flow as WordPress sites

A Custom API Integration

settings How It Works

SEO Agent connects to your existing API and maps its own fields to your API's field names. You configure everything in the dashboard β€” no code changes needed on your server.

link

1. API Base URL

Your API endpoint

lock

2. Authentication

Bearer, Basic, API Key

swap_horiz

3. Field Mapping

title → tieude, etc.

output

4. Response Mapping

data.id, data.url, etc.

1 Setup in Dashboard

  1. Go to Dashboard → Publishing Sites
  2. Click "Add Website"
  3. Select platform: "Custom API"
  4. Fill in your API details (see configuration below)
  5. Click "Save" — SEO Agent will test the connection automatically

2 Configuration Reference

Authentication Types

Type Header Sent Use Case
Bearer Token Authorization: Bearer <token> Direct token or SHA-256(contract_id + secret)
Basic Auth Authorization: Basic base64(user:pass) Username + password
API Key X-API-Key: <key> (configurable header name) API key in custom header
None (no auth header) Public API or IP-whitelisted

Field Mapping

Map SEO Agent's fields to your API's field names. Leave empty to skip a field.

SEO Agent Field Default Example Custom Description
titletitletieudeArticle title
contentcontentnoidungHTML content body
excerptexcerptmotaShort description
slugslugslugURL-friendly slug
featured_imagefeatured_imagephotoImage URL or file field
seo_titleseo_titletitleSEO <title> tag
seo_keywordsseo_keywordskeywordMeta keywords
seo_descriptionseo_descriptiondescriptionMeta description
tagstagstagsComma-separated tags
categorycategorycategoryCategory name

Response Mapping (dot notation)

Tell SEO Agent where to find the post ID and URL in your API's response. Use dot notation for nested fields.

Example β€” Your API returns
{
  "status": 200,
  "message": "Success",
  "data": {
    "id": 123,
    "namevi": "Article Title",
    "slugvi": "article-slug"
  }
}
Response Mapping Configuration
Post ID:    data.id       # extracts 123
Post URL:   data.slugvi   # extracts "article-slug"
Post Title: data.namevi   # extracts "Article Title"
Post Slug:  data.slugvi   # extracts "article-slug"

3 Real-World Example

Here's how to configure SEO Agent for a custom Vietnamese CMS API:

API Spec
Base URL:        https://yoursite.com/api/v1
Auth:            Bearer Token (SHA-256 of contract_id + secret)
Create Post:     POST /post/create     (multipart/form-data)
Update Post:     POST /post/edit/{id}
Delete Post:     DELETE /post/{id}
List Posts:      GET /posts
SEO Agent Configuration
# Connection
API Base URL:      https://yoursite.com/api/v1
Authentication:    Bearer Token
  Contract ID:     25916725
  Secret:          YOUR_SECRET_KEY
  (token = SHA-256 of "25916725" + "YOUR_SECRET_KEY")
Content-Type:      multipart/form-data

# Endpoints
Create:            /post/create
Update:            /post/edit/{id}
Delete:            /post/{id}
List (for test):   /posts

# Field Mapping
title       →  tieude
content     →  noidung
excerpt     →  mota
slug        →  slug
image       →  photo
seo_title   →  title
seo_keywords → keyword
seo_desc    →  description

# Response Mapping
Post ID:     data.id
Post URL:    data.slugvi
Post Title:  data.namevi
Post Slug:   data.slugvi

What SEO Agent sends: When publishing an article, SEO Agent sends a POST request to https://yoursite.com/api/v1/post/create with your Bearer token and fields mapped to your API format.

checklist Minimum API Requirements

Your API needs at minimum:

1.

A "create post" endpoint

Accepts title + HTML content, returns a post ID in the response.

2.

A "list/test" endpoint

Any GET endpoint that returns HTTP 200 when authenticated. Used to test the connection.

3.

JSON response with post ID (recommended)

SEO Agent needs to know the created post's ID for tracking. Return it somewhere in the JSON response.

Note: Categories, tags, media upload, and page creation are handled automatically when available in your API. If not, SEO Agent will skip those features gracefully.

bug_report Testing Your API

Test your API with cURL before adding to SEO Agent:

Terminal
# Bearer Token auth
curl -H "Authorization: Bearer YOUR_TOKEN" \
     https://yoursite.com/api/v1/posts

# Basic Auth
curl -u "username:password" \
     https://yoursite.com/api/v1/posts

# API Key auth
curl -H "X-API-Key: YOUR_KEY" \
     https://yoursite.com/api/v1/posts

# Create a test post (JSON)
curl -X POST https://yoursite.com/api/v1/post/create \
     -H "Authorization: Bearer YOUR_TOKEN" \
     -H "Content-Type: application/json" \
     -d '{"tieude":"Test","noidung":"<p>Hello</p>"}'

# Create a test post (multipart)
curl -X POST https://yoursite.com/api/v1/post/create \
     -H "Authorization: Bearer YOUR_TOKEN" \
     -F "tieude=Test" \
     -F "noidung=<p>Hello</p>"

B WordPress API Bridge

Alternative: implement the WordPress REST API format on your custom server. This gives full feature parity (media uploads, categories, tags, scheduling) but requires more development work.

lock Base URL & Authentication

All endpoints must be under the /wp-json/wp/v2/ path. Authentication uses HTTP Basic Auth.

Configuration
# Base URL
https://yoursite.com/wp-json/wp/v2/

# Authentication Header
Authorization: Basic base64("username:app_password")

Tip: When adding your site in SEO Agent, select "WordPress" as platform and enter the same username and password. SEO Agent will send them as Basic Auth on every request.

route Required Endpoints

Implement these WordPress-compatible endpoints:

Method Endpoint Purpose
GET/wp-json/wp/v2/users/meAuth test
POST/wp-json/wp/v2/postsCreate post
POST/wp-json/wp/v2/posts/{id}Update post
GET/wp-json/wp/v2/postsList posts
POST/wp-json/wp/v2/mediaUpload image
POST/wp-json/wp/v2/categoriesCreate category
POST/wp-json/wp/v2/tagsCreate tag

For detailed request/response format for each endpoint, refer to the WordPress REST API documentation.

code Code Examples (WordPress API Bridge)

Choose your framework below. Each example shows how to set up the WordPress-compatible API routes on your server.

server.js β€” Express
const express = require('express');
const multer = require('multer');
const app = express();
const upload = multer({ dest: 'uploads/' });

app.use(express.json());

// Middleware: Basic Auth
function auth(req, res, next) {
  const header = req.headers.authorization || '';
  if (!header.startsWith('Basic ')) return res.status(401).json({
    code: 'rest_not_logged_in', message: 'Unauthorized', data: {status: 401}
  });
  const [user, pass] = Buffer.from(header.slice(6), 'base64')
    .toString().split(':');
  // TODO: validate user:pass from your database
  if (!isValidUser(user, pass)) return res.status(401).json({
    code: 'rest_not_logged_in', message: 'Invalid credentials', data: {status: 401}
  });
  req.userId = getUserId(user);
  next();
}

// 1. Auth test
app.get('/wp-json/wp/v2/users/me', auth, (req, res) => {
  res.json({ id: req.userId, name: 'Admin', slug: 'admin', roles: ['administrator'] });
});

// 2. Create post
app.post('/wp-json/wp/v2/posts', auth, (req, res) => {
  const { title, content, status, excerpt, slug, categories, tags, featured_media } = req.body;
  // TODO: insert into your database
  const post = createPost({ title, content, status, excerpt, slug, categories, tags, featured_media });
  res.status(201).json({
    id: post.id,
    title:  { raw: post.title, rendered: post.title },
    slug:   post.slug,
    link:   `https://yoursite.com/${post.slug}/`,
    status: post.status
  });
});

// 3. Update post
app.post('/wp-json/wp/v2/posts/:id', auth, (req, res) => {
  const post = updatePost(req.params.id, req.body);
  res.json({ id: post.id, title: { raw: post.title, rendered: post.title }, slug: post.slug });
});

// 4. List posts
app.get('/wp-json/wp/v2/posts', auth, (req, res) => {
  const { per_page = 10, page = 1, status } = req.query;
  const { posts, total } = listPosts({ per_page, page, status });
  res.set('X-WP-Total', total);
  res.set('X-WP-TotalPages', Math.ceil(total / per_page));
  res.json(posts);
});

// 5. Upload media
app.post('/wp-json/wp/v2/media', auth, upload.single('file'), (req, res) => {
  const { title, alt_text, post } = req.body;
  // TODO: move file to public folder, save to DB
  const media = saveMedia(req.file, { title, alt_text, post_id: post });
  res.status(201).json({ id: media.id, source_url: media.public_url });
});

// 6. Categories
app.get('/wp-json/wp/v2/categories', auth, (req, res) => {
  const cats = listCategories(req.query);
  res.json(cats); // [{id, name, slug, count}, ...]
});
app.post('/wp-json/wp/v2/categories', auth, (req, res) => {
  const existing = findCategoryBySlug(req.body.slug || slugify(req.body.name));
  if (existing) return res.status(400).json({
    code: 'term_exists', message: 'Category exists', data: { term_id: existing.id }
  });
  const cat = createCategory(req.body);
  res.status(201).json(cat);
});

// 7. Tags
app.get('/wp-json/wp/v2/tags', auth, (req, res) => {
  const tags = searchTags(req.query); // supports ?search= and ?slug=
  res.json(tags);
});
app.post('/wp-json/wp/v2/tags', auth, (req, res) => {
  const existing = findTagByName(req.body.name);
  if (existing) return res.status(400).json({
    code: 'term_exists', message: 'Tag exists', data: { term_id: existing.id }
  });
  const tag = createTag(req.body);
  res.status(201).json(tag);
});

// 8. Delete post
app.delete('/wp-json/wp/v2/posts/:id', auth, (req, res) => {
  deletePost(req.params.id);
  res.json({ deleted: true, id: parseInt(req.params.id) });
});

app.listen(3000);

bug_report Testing WordPress API Bridge

Use cURL to verify each endpoint before connecting to SEO Agent:

Terminal
# 1. Test auth
curl -u "admin:yourpassword" https://yoursite.com/wp-json/wp/v2/users/me

# 2. Create a test post
curl -u "admin:yourpassword" -X POST https://yoursite.com/wp-json/wp/v2/posts \
  -H "Content-Type: application/json" \
  -d '{"title":"Test Post","content":"<p>Hello</p>","status":"draft"}'

# 3. Upload an image
curl -u "admin:yourpassword" -X POST https://yoursite.com/wp-json/wp/v2/media \
  -F "[email protected]" -F "title=Test Image" -F "alt_text=Test"

# 4. Create a category
curl -u "admin:yourpassword" -X POST https://yoursite.com/wp-json/wp/v2/categories \
  -H "Content-Type: application/json" -d '{"name":"Tech","slug":"tech"}'

Tip: When adding the site in SEO Agent, select "WordPress" as platform (not Custom API). SEO Agent will communicate using the standard WordPress REST API format.

check_circle Which Method Should I Choose?

Choose Custom API (Option A) if:

  • ✓ You already have a REST API
  • ✓ You want minimal development effort
  • ✓ You only need article publishing
  • ✓ Your API uses Bearer/Basic/API Key auth

Choose WP Bridge (Option B) if:

  • ✓ You need full media upload (images to your server)
  • ✓ You need category/tag auto-creation
  • ✓ You want scheduled post support
  • ✓ You're building a new API from scratch

Need help? Contact us at [email protected]