Skip to main content

Circle to FluentCommunity migration skill for Claude Code

If you're like me — having a FluentCommunity lifetime deal license sitting idle and not yet having moved away from your Circle community — then this one is for you.

We are an adult academy, running a large course catalog. We currently have 3 Circle community-driven courses with three full-year courses containing hundreds of items, and I faced the challenge of migrating all of these to FluentCommunity. I didn't want to do it manually, so I used Claude Code to handle every single part of the migration — without touching FluentCommunity manually even once (after the initial setup).

Here I want to share the skill I created out of this completed migration. You can use it if you're on the fence about migrating your Circle community to FluentCommunity. I asked AI to generate a summary of everything this migration skill (and all the included python scripts) can do, and you can simply download it, , unzip it, install it (tell Claude to also use the python scripts), and get started.

Note: To use the Circle API you need their Business plan (USD 199.- /month), but you can wait until the last 7 days before your next billing cycle, and upgrade then and only pay the difference for those 7 days (and then quit).

Here is that summary:

Circle.so → FluentCommunity Migration Guide

A practical guide for applying this skill to migrate eLearning content from a Circle.so community to a FluentCommunity (WordPress) installation.

What this skill can and can't migrate -> check the attachments


Important Caveats & Workarounds

1. Circle Admin API v2 Returns Wrong Lesson Positions ⚠️

The position field in Circle's Admin API v2 responses does NOT reflect the actual display order in the Circle UI.

Example: In Modul 7 of a Numerologie course, the API reported all Quizzes at positions 19–30 and Meditations at 31–42 — but the Circle UI actually shows them interleaved directly after each Lebenszahl lesson.

Solution: Always scrape the correct lesson order directly from the Circle browser UI using Playwright.


2. Circle CDN URLs Must Never Be Embedded in FC

Circle media is hosted at https://assets-v2.circle.so/.... These URLs:

  • Require Circle authentication to access
  • Will break when a Circle subscription ends
  • Must not be stored in FC content

Solution: Always download Circle assets locally first, then re-upload to FC via the WP media library (POST /feeds/media-upload) or document endpoint (POST /documents/upload).


3. Lesson Videos: Circle "Featured Video" Requires a Workaround

In Circle, lesson videos are typically stored as "featured video" (Active Storage blob), not as a URL in the lesson API response. The lesson API returns no video URL for these.

Solution: Since most Circle videos are hosted on Vimeo and then embedded in Circle, retrieve them directly from your Vimeo account:

  1. Fetch all videos from the relevant Vimeo folder via Vimeo API

  2. Match each Circle lesson title to a Vimeo video title (fuzzy word-overlap matching)

  3. Embed the Vimeo URL in FC using oEmbed format:

    { "type": "oembed", "url": "https://vimeo.com/{video_id}", "content_type": "video" }
  4. For webinar recordings not yet on Vimeo: upload using PyVimeo (vimeo.upload(filepath))



4. XProfile Username ≠ WordPress user_nicename

FluentCommunity creates XProfile usernames by removing hyphens from the WP user_nicename. For example, clemens-mazza becomes clemensmazza as the XProfile username.

Impact: REST API badge/profile endpoints use the XProfile username, not the WP nicename. Always look up the actual XProfile username via WP-CLI before making API calls:

wp eval '$x = \FluentCommunity\App\Models\XProfile::where("user_id", 123)->first(); echo $x->username;'

5. XProfile Must Be Created Before Badge Assignment

New WP users created via the REST API do NOT automatically get an FC XProfile. XProfiles are only created on first portal login — or explicitly:

wp eval '

foreach ([101, 102, 103] as $uid) {

` $u = \FluentCommunity\App\Models\User::find($uid);`

` if ($u) $u->syncXProfile(true);`

}

echo "Done\n";

'

wp fluent_community sync_x_profile --force is insufficient — it only runs for already-active portal users.


7. Wordfence Rate-Limits Bulk Media Uploads

Bulk file uploads (MP3s, PDFs, images) trigger Wordfence security rules at high frequency.

Solution: Add 0.5s delay between uploads. If still blocked, use Playwright browser upload as fallback (drag-and-drop via admin UI).


8. Badge Assignment: WP-CLI Is More Reliable Than REST API

Writing badges via the REST API requires the correct XProfile username (see caveat 5). Writing directly to the database via WP-CLI bypasses all these issues:

wp eval '

$x = \FluentCommunity\App\Models\XProfile::where("user_id", 123)->first();

$meta = is_string($x->meta) ? json_decode($x->meta, true) : ($x->meta ?? []);

$meta["badge_slug"] = ["your-badge-slug"];

$x->meta = $meta;

$x->save();

'


9. Circle API Must Be Called From Local Machine, Not Cloud Sandbox

Circle's API blocks requests from cloud sandbox IPs (e.g. ctx_execute sandboxes get HTTP 403). Always run Circle API scripts locally:

python3 /tmp/fetch_circle_lessons.py

Step-by-Step Migration Order

  1. Course structure + lessons — text, status, section hierarchy
  2. Lesson order correction — scrape Circle UI via Playwright, apply PATCH indexes
  3. Videos — fetch from Vimeo, match by title, embed in lessons
  4. Audio files (MP3s) — download from Circle CDN, upload to FC
  5. Documents (PDFs) — download and re-upload as FC lesson documents
  6. Course + space icons — fetch and set as featured images
  7. Members — create WP users, sync XProfiles, add to spaces
  8. Badges — define badge slugs in FC, assign via WP-CLI
  9. FluentCRM — create tags, import subscribers
  10. Lesson order final check — re-verify via Playwright UI scrape vs FC order

Required Inputs From the User

Before starting the migration, collect the following credentials and IDs:

Circle.so

Input Where to find it Example Circle API Token Circle Admin → Settings → Developers → API Tokens BF2J1jST... Circle Community ID URL of any API response, or Admin → Settings → General 107415 Circle Community Slug URL of your Circle space: circle.so/c/{slug} numerologie-persoenlichkeit Circle Admin Login Your Circle admin email + password (for Playwright UI scraping) —

WordPress / FluentCommunity

Input Where to find it Example WP Domain Your WordPress site URL https://www.your-domain.com WP Admin Username WP Admin → Users admin WP Application Password WP Admin → Users → your user → Application Passwords → Add New XXXX XXXX XXXX XXXX XXXX XXXX WP-CLI SSH access Server access to run wp eval '...' commands —

Vimeo (only if migrating videos)

Input Where to find it Example Vimeo Access Token developer.vimeo.com → My Apps → your app → Authentication → Generate Token abc123... Vimeo Folder/Showcase ID URL of your Vimeo folder: vimeo.com/manage/folders/{id} 12345678

Technical Prerequisites

  • Python 3.10+ with requestsPyVimeo installed
  • Playwright MCP active in Claude Code
  • WordPress MCP configured with your domain + application password
  • WP-CLI access to the server (SSH or local) - can be done manually
  • Claude Code running locally (not in cloud) for Circle API calls
Circle to FluentCommunity migration skill for Claude Code Circle to FluentCommunity migration skill for Claude Code Circle to FluentCommunity migration skill for Claude Code

Nicolas D

Nice job !

Margaret Thomas

Wow, this couldn't have come at a better time. Thanks for sharing this!

Clemens Mazza

Margaret Thomas Let me know how it works for you.