Integration
Strapi Cms
OpenClaw plugin for Strapi v5 CMS — 7 agent tools for full content lifecycle management
Configuration Example
{
"plugins": {
"openclaw-strapi-cms": {
"strapiUrl": "https://your-strapi-instance.com",
"strapiApiToken": "your-api-token-here",
"defaultBrand": "CRE-11TRUST" // optional
}
}
}
README
# openclaw-strapi-cms
OpenClaw plugin that gives any agent full content lifecycle management against a [Strapi v5](https://strapi.io) CMS. Install the plugin, add your Strapi URL and API token, and the agent immediately gets 7 tools for creating, editing, publishing, scheduling, and tracking content.
## Quick Start
```bash
# 1. Clone into OpenClaw's plugin directory
cd ~/.openclaw/plugins
git clone https://github.com/clenisa/openclaw-strapi-cms.git
# 2. Add config to openclaw.json
# (see Configuration section below)
# 3. Restart OpenClaw gateway
# Your agent now has cms_create, cms_update, cms_list, cms_get, cms_publish, cms_status, cms_delete
```
## Requirements
- OpenClaw with plugin support (`pi-agent-core >= 0.51.0`)
- Strapi v5 instance with REST API enabled
- Strapi API token with content read/write permissions
- **Strapi schema change**: The `agent_metadata` JSON field must be added to any content types you want agents to manage (see [Strapi Setup](#strapi-setup))
## Configuration
Add to your `openclaw.json` (global at `~/.openclaw/openclaw.json` or workspace-level):
```jsonc
{
"plugins": {
"openclaw-strapi-cms": {
"strapiUrl": "https://your-strapi-instance.com",
"strapiApiToken": "your-api-token-here",
"defaultBrand": "CRE-11TRUST" // optional
}
}
}
```
| Key | Required | Description |
|-----|----------|-------------|
| `strapiUrl` | Yes | Base URL of your Strapi instance |
| `strapiApiToken` | Yes | Strapi API token (see [Creating an API Token](#creating-an-api-token)) |
| `defaultBrand` | No | Default brand key used when creating content without specifying a brand |
## Tools
| Tool | Purpose | Key Parameters |
|------|---------|----------------|
| `cms_create` | Create a new draft | `contentType`, `title`, `brand`, `brief`, `request_id` |
| `cms_update` | Update an existing draft | `contentType`, `documentId` or `slug`, fields to change |
| `cms_list` | List/filter content | `contentType`, `brand`, `status`, `deliveryStatus`, `page` |
| `cms_get` | Get full item detail | `contentType`, `documentId` or `slug` |
| `cms_publish` | Publish or schedule | `contentType`, `documentId`, `scheduledDate` |
| `cms_status` | Check delivery status | `contentType`, `documentId` |
| `cms_delete` | Delete or unpublish | `contentType`, `documentId`, `action` (delete/unpublish) |
### Supported Content Types
- `newsletter` — Email newsletters (distributed via GoHighLevel)
- `social-post` — Social media content (Meta, Twitter/X, LinkedIn)
- `blog-post` — Blog articles (multi-site)
### Content Briefs
Every `cms_create` and `cms_update` call can include a `brief` object with research and planning data. This is stored in the `agent_metadata` JSON field on the content item:
```json
{
"brief": {
"client": "ElevenTrust",
"campaign": "Q1 2026 CRE",
"channel": "instagram",
"persona": "Commercial real estate investors, 35-55",
"goal": "Drive traffic to listing page",
"tone": "Professional, data-driven",
"length": "short",
"research_summary": "South Florida office vacancy dropped to 12.3%...",
"outline": ["Hook: vacancy stat", "Body: top 3 markets", "CTA: schedule tour"],
"sources": ["https://example.com/report"]
}
}
```
Brief fields are merged (not replaced) on update, so you can build up research incrementally.
### Idempotency
Pass `request_id` on `cms_create` to prevent duplicate content. If a create is called with the same `request_id`, the existing item is returned instead of creating a new one.
```
cms_create contentType=social-post title="My Post" request_id="opal-2026-02-16-abc"
```
## Strapi Setup
### Adding agent_metadata to content types
The plugin stores brief/research data in an `agent_metadata` JSON field. Add this field to each content type you want agents to manage.
Edit the schema file for each content type (e.g., `src/api/social-post/content-types/social-post/schema.json`):
```json
{
"attributes": {
"...existing fields...": {},
"agent_metadata": {
"type": "json"
}
}
}
```
After editing schemas, rebuild and restart Strapi:
```bash
cd /path/to/strapi-cms
pnpm build
# restart your Strapi service
```
### Creating an API Token
1. Go to Strapi Admin → **Settings** → **API Tokens**
2. Click **"Create new API Token"**
3. Configure:
- **Name**: `OPAL Agent` (or any descriptive name)
- **Description**: `OpenClaw CMS plugin - content lifecycle`
- **Token duration**: `Unlimited` (or set an expiry)
- **Token type**: `Custom`
4. Set permissions:
| Content Type | find | findOne | create | update | delete |
|-------------|------|---------|--------|--------|--------|
| Blog-post | Yes | Yes | Yes | Yes | Yes |
| Newsletter | Yes | Yes | Yes | Yes | Yes |
| Social-post | Yes | Yes | Yes | Yes | Yes |
| Brand-config | Yes | Yes | No | No | No |
5. Click **Save** and copy the token (shown only once)
### Adding a new content type
To support a new Strapi content type:
1. Add `agent_metadata` JSON field to the content type's schema
2. Rebuild Strapi (`pnpm build`)
3. In the plugin, add the mapping to `lib/types.ts`:
```typescript
export const CONTENT_TYPES = {
"newsletter": "newsletters",
"social-post": "social-posts",
"blog-post": "blog-posts",
"your-new-type": "your-new-types", // add here
} as const;
```
4. Update tool parameter enums in `tools/cms-create.ts` (and other tools) to include the new type
5. Update `CMS_INTEGRATION.md` with the new type's fields
### Adding a new brand
1. In Strapi, edit the `brand` enum in each content type's schema to include the new brand key
2. Rebuild Strapi
3. Optionally add a `brand-config` entry in Strapi Admin for the new brand (platform IDs, sender info)
4. Update `CMS_INTEGRATION.md` to document the new brand
## Architecture
```
OpenClaw Agent
└── openclaw-strapi-cms plugin
├── index.ts → register(api): creates client, registers tools + hook
├── lib/strapi-client.ts → HTTP wrapper (auth, pagination, rate limiting)
├── lib/types.ts → Shared types and content type mapping
├── tools/cms-*.ts → 7 tool factories (each receives StrapiClient)
└── CMS_INTEGRATION.md → Injected into agent context via before_agent_start hook
│
▼ HTTP (Bearer token auth)
Strapi v5 CMS
├── /api/newsletters
├── /api/social-posts
└── /api/blog-posts
```
**Key design decisions:**
- **Factory pattern**: Each tool is a function that takes `StrapiClient` and returns a tool object. The client is created once in `register()` with validated config.
- **Context injection**: `CMS_INTEGRATION.md` is read at startup and injected via the `before_agent_start` hook, so the agent knows about CMS capabilities without needing instructions in SOUL.md.
- **No new content types**: Brief/research data is stored in `agent_metadata` JSON on existing types, keeping the Strapi schema minimal.
- **Rate limiting**: Built-in 10 req/s throttle to avoid hammering the CMS.
## Testing
### Smoke tests (against live Strapi)
```bash
cd openclaw-strapi-cms
STRAPI_URL=https://your-strapi.com STRAPI_API_TOKEN=your-token ./test.sh
```
Runs 9 tests: create with metadata, get, update with brief merge, list with filter, status check, delete + confirm deletion.
### Manual verification
After installing the plugin, start an OpenClaw session and try:
```
List all social posts for CRE-11TRUST
```
The agent should use `cms_list` and return results.
## File Structure
```
openclaw-strapi-cms/
├── openclaw.plugin.json # Plugin manifest with config schema
├── package.json # Package metadata
├── index.ts # Entry point: register(api)
├── lib/
│ ├── strapi-client.ts # Strapi v5 HTTP client
│ └── types.ts # TypeScript types and constants
├── tools/
│ ├── cms-create.ts # Create draft with optional brief
│ ├── cms-update.ts # Update draft (by documentId or slug)
│ ├── cms-list.ts # List/filter with pagination
│ ├── cms-get.ts # Get single item detail
│ ├── cms-publish.ts # Publish immediately or schedule
│ ├── cms-status.ts # Check delivery/workflow status
│ └── cms-delete.ts # Delete or unpublish
├── CMS_INTEGRATION.md # Agent context doc (injected automatically)
└── test.sh # Smoke test script
```
## Troubleshooting
| Problem | Cause | Fix |
|---------|-------|-----|
| `Missing required config` in logs | `strapiUrl` or `strapiApiToken` not set | Add both to `openclaw.json` plugin config |
| `Strapi 400: Invalid key agent_metadata` | Strapi schema missing the field | Add `agent_metadata` JSON field to the content type schema, rebuild Strapi |
| `Strapi 403: Forbidden` | API token lacks permissions | Check token permissions in Strapi Admin → Settings → API Tokens |
| `Strapi 404` on get/update | Wrong `documentId` (using numeric id instead) | Strapi v5 uses `documentId` (alphanumeric), not the numeric `id` |
| Tools not appearing in agent | Plugin not discovered | Verify plugin is in `~/.openclaw/plugins/` or workspace `.openclaw/plugins/`, restart gateway |
| Duplicate content on retry | No `request_id` set | Always pass `request_id` on `cms_create` for idempotency |
| Rate limit errors from Strapi | Too many concurrent requests | Built-in 10 req/s throttle should handle this; increase `minInterval` in strapi-client.ts if needed |
## Development
### Modifying tools
Each tool in `tools/` exports a factory function. To modify a tool's behavior:
1. Edit the tool file
2. The factory receives `StrapiClient` (and optionally `defaultBrand`)
3. Return an object with `name`, `description`, `parameters` (JSON Schema), and `execute(toolCallId, params)`
4. `execute` must return `{ content: [{ type: "text", text: "..." }] }`
### Adding a new tool
1. Create `tools/cms-newtool.t
... (truncated)
integration
Comments
Sign in to leave a comment