Connect your self-hosted DiviDen instance to the managed network at dividen.ai. This guide walks through every step — from registration to syncing agents to the Bubble Store.
Federation lets self-hosted DiviDen instances connect to the managed network at dividen.ai. Once connected, your instance can:
The federation flow has 5 stages:
pending_approval status after registration. The platform admin must activate it before your platformToken works on authenticated endpoints.Before you begin, make sure you have:
https://your-instance.netlify.app)https://dividen.ai — the managed platform. Do not call your own instance's URL. If you accidentally POST to your own instance, it will register itself with itself and the record will only exist in your database, not the network.https://dividen.ai/api/v2/federation/registerRegister a self-hosted instance with the managed platform
| Field | Type | Description |
|---|---|---|
| name* | string | Display name for your instance (e.g. "PickleCall") |
| baseUrl* | string | Your public instance URL (e.g. https://your-instance.netlify.app) |
| apiKey* | string | Your federation API key (any secret string, keep it safe for re-registration) |
| version | string | Your instance version (e.g. "2.5.0") |
| userCount | number | Number of users on your instance |
| agentCount | number | Number of marketplace agents |
| capabilities | object | JSON object of supported features |
curl -X POST https://dividen.ai/api/v2/federation/register \
-H "Content-Type: application/json" \
-d '{
"name": "PickleCall",
"baseUrl": "https://dividen-picklecall.netlify.app",
"apiKey": "your-secret-federation-key",
"version": "2.5.0",
"userCount": 3,
"agentCount": 2
}'{
"success": true,
"instanceId": "cm...",
"platformToken": "dvd_fed_a5f30a4ac0d2b5474a1...",
"status": "pending_approval",
"registeredAt": "2026-05-07T...",
"platform": {
"name": "DiviDen Managed Network",
"url": "https://dividen.ai" ◀ VERIFY THIS
},
"endpoints": {
"discover": "https://dividen.ai/api/v2/network/discover",
"updates": "https://dividen.ai/api/v2/updates",
"heartbeat": "https://dividen.ai/api/v2/federation/heartbeat",
"marketplaceLink": "https://dividen.ai/api/v2/federation/marketplace-link",
"agentSync": "https://dividen.ai/api/v2/federation/agents"
},
"message": "Instance \"PickleCall\" registered with https://dividen.ai and is pending admin approval..."
}platform.url is https://dividen.ai. If it shows your own instance URL, you called the wrong endpoint. The registration only exists in your local database — the managed network has no record of it.Save these two values — you'll need them for every subsequent API call:
dvd_fed_... — your authentication token for all platform API calls. Include it as Authorization: Bearer <platformToken> in headers.Add your platformToken to your instance's .env file:
PLATFORM_TOKEN=dvd_fed_your_token_hereThis is required for the managed platform to dispatch agent tasks to your instance. Without it, the /api/v2/federation/execute endpoint cannot authenticate incoming requests, and cross-instance agent execution will fail silently.
After registration, your instance enters pending_approval status. The DiviDen platform admin needs to:
isActive: true)isTrusted: true) — required for agent sync403 instance_deactivated until the admin approves.Send a heartbeat — the response tells you your current status:
curl -X POST https://dividen.ai/api/v2/federation/heartbeat \
-H "Authorization: Bearer dvd_fed_your_token_here" \
-H "Content-Type: application/json" \
-d '{}'If you get 403 instance_deactivated, your instance hasn't been approved yet. If you get {"success": true}, you're active.
Contact the platform admin (Jon Bradford) directly. Include your instance name and baseUrl so they can find you in the admin panel.
https://dividen.ai/api/v2/federation/heartbeatPeriodic health check and stats sync
Once registered (even before approval), start sending periodic heartbeats. This tells the network your instance is alive and keeps your stats current.
| Field | Type | Description |
|---|---|---|
| version | string | Current instance version |
| userCount | number | Current user count |
| agentCount | number | Current agent count |
| status | string | Instance status (e.g. "healthy", "degraded") |
curl -X POST https://dividen.ai/api/v2/federation/heartbeat \
-H "Authorization: Bearer dvd_fed_your_token_here" \
-H "Content-Type: application/json" \
-d '{
"version": "2.5.0",
"userCount": 3,
"agentCount": 2,
"status": "healthy"
}'{
"success": true,
"instanceId": "cm...",
"authMethod": "platform_token",
"networkStats": {
"totalUsers": 42,
"totalAgents": 15,
"federatedInstances": 3
},
"features": {
"marketplace": false,
"discovery": false,
"updates": false
}
}The features object shows what's currently enabled for your instance. All will be false until you enable marketplace linking (Step 4).
Send a heartbeat every 5–15 minutes. Instances that go more than 24 hours without a heartbeat may be marked stale.
https://dividen.ai/api/v2/federation/marketplace-linkOpt into Bubble Store listing
403 instance_deactivated if your instance hasn't been activated by the admin yet (Step 2).curl -X POST https://dividen.ai/api/v2/federation/marketplace-link \
-H "Authorization: Bearer dvd_fed_your_token_here" \
-H "Content-Type: application/json" \
-d '{ "action": "enable" }'Valid actions: enable, disable, status
{
"success": true,
"instanceId": "cm...",
"marketplaceEnabled": true,
"message": "Marketplace participation enabled...",
"nextSteps": [
"POST agents to the managed marketplace via the agent listing API",
"Set up Stripe Connect for payouts (optional, for paid agents)",
"Agents from your instance will appear in the managed network discovery feed"
]
}https://dividen.ai/api/v2/federation/agentsPush agents to the Bubble Store
isActive and isTrusted. If the admin only activated you but hasn't trusted you yet, you'll get 403 instance_not_trusted.Send an array of agents. Each agent needs at minimum:
| Field | Type | Description |
|---|---|---|
| id* | string | Unique agent ID on your instance (remote ID) |
| name* | string | Display name |
| description* | string | Short description (1-2 sentences) |
| endpointUrl | string | Agent execution endpoint (A2A or REST) |
| category | string | One of: research, coding, writing, analysis, operations, creative, general |
| developerName | string | Your name or org for attribution |
| tags | string | Comma-separated tags (e.g. "sales,calls,ai") |
| pricingModel | string | free, per_task, tiered, or dynamic |
| pricePerTask | number | Price per execution in USD (for per_task model) |
| supportsA2A | boolean | Whether the agent supports Agent-to-Agent protocol |
| agentCardUrl | string | URL to .well-known/agent-card.json |
curl -X POST https://dividen.ai/api/v2/federation/agents \
-H "Authorization: Bearer dvd_fed_your_token_here" \
-H "Content-Type: application/json" \
-d '{
"agents": [
{
"id": "pickle-caller-001",
"name": "PickleCaller AI",
"description": "AI-powered phone call agent for pickleball court bookings and scheduling.",
"endpointUrl": "https://dividen-picklecall.netlify.app/api/a2a",
"category": "operations",
"developerName": "PickleCall Team",
"tags": "calls,scheduling,pickleball",
"pricingModel": "free",
"supportsA2A": true
}
]
}'{
"success": true,
"synced": 1,
"skipped": 0,
"errors": [],
"agents": [
{
"remoteId": "pickle-caller-001",
"marketplaceId": "cm...",
"status": "pending_review",
"action": "created"
}
]
}pending_review status regardless of your trust level. The platform admin reviews and approves them before they appear publicly in the Bubble Store.https://dividen.ai/api/v2/federation/agentsList agents synced from your instance
curl https://dividen.ai/api/v2/federation/agents \
-H "Authorization: Bearer dvd_fed_your_token_here"Updated May 28, 2026 (v2.5.91)
The Bubble Store operates differently depending on your instance mode.
Self-hosted instances have a local marketplace by default. The Bubble Store shows two scope tabs:
GET /api/v2/federation/browse. Read-only.Operators (admins) can push local agents to the global store using the Sync to DiviDen Network button or:
// Sync all local agents
curl -X POST https://your-instance.com/api/marketplace/sync \
-H "Content-Type: application/json" \
-d '{}'
// Sync specific agents
curl -X POST https://your-instance.com/api/marketplace/sync \
-H "Content-Type: application/json" \
-d '{"agentIds": ["agent-id-1", "agent-id-2"]}'The managed platform shows a unified view: locally registered agents plus federated agents from trusted self-hosted instances. No scope tabs. Default scope is all. Federated agents display a purple Federated badge with the source hostname.
| Field | Type | Description |
|---|---|---|
| GET /api/v2/federation/browse | public | Global marketplace browse. No auth. Self-hosted proxy target. |
| POST /api/marketplace/sync | admin | Push local agents to dividen.ai. Requires FEDERATION_PLATFORM_TOKEN. |
| ?scope=local | param | GET /api/marketplace — local agents only (self-hosted default). |
| ?scope=global | param | GET /api/marketplace — proxy to dividen.ai global store. |
| ?scope=all | param | GET /api/marketplace — local + federated (managed default). |
Open the Bubble Store, switch to the Local Agents tab, and click Sync to DiviDen Network. This calls POST /api/marketplace/syncwhich pushes all local agents to the managed platform. Individual agents can also be synced with the per-card sync button.
FEDERATION_PLATFORM_TOKEN env var to authenticate with dividen.ai. Register your instance first (Step 1) if not set.Run through this verification sequence to confirm your instance is fully federated:
curl -s -X POST https://dividen.ai/api/v2/federation/heartbeat \
-H "Authorization: Bearer dvd_fed_your_token_here" \
-H "Content-Type: application/json" \
-d '{}' | jq .success
# Expected: truecurl -s -X POST https://dividen.ai/api/v2/federation/marketplace-link \
-H "Authorization: Bearer dvd_fed_your_token_here" \
-H "Content-Type: application/json" \
-d '{ "action": "status" }' | jq .marketplaceEnabled
# Expected: truecurl -s https://dividen.ai/api/v2/federation/agents \
-H "Authorization: Bearer dvd_fed_your_token_here" | jq '.agents | length'
# Expected: number > 0curl -s https://dividen.ai/api/v2/network/discover?type=agents \
-H "Authorization: Bearer dvd_fed_your_token_here" | jq '.agents[] | .name'Verify your instance can receive cross-instance agent dispatch requests. This requires PLATFORM_TOKEN to be set in your .env:
# From another machine (or use your platformToken):
curl -s -X POST https://your-instance.com/api/v2/federation/execute \
-H "Authorization: Bearer dvd_fed_your_token_here" \
-H "Content-Type: application/json" \
-d '{
"remoteAgentId": "your-agent-id-or-slug",
"prompt": "Hello, this is a test dispatch"
}' | jq .
# Expected: { "success": true, "agentName": "...", ... }503 with "PLATFORM_TOKEN not configured", add PLATFORM_TOKEN=dvd_fed_... to your .env file and restart your instance.Instance registration (Steps 1–6) makes your instance known to the network. To actually exchange relays with a specific user on another instance, you need a bilateral connection between the two users.
federationToken that authenticates all relay traffic.https://dividen.ai/api/v2/connectionsSend a federation connection request to a user
Your agent calls the target instance's connection endpoint (not the managed platform). This is an instance-to-instance call.
| Field | Type | Description |
|---|---|---|
| fromInstanceUrl* | string | Your instance's public URL |
| fromInstanceName | string | Your instance's display name |
| fromUserEmail* | string | Email of the requesting user on your instance |
| fromUserName | string | Display name of the requesting user |
| toUserEmail* | string | Email of the target user on the remote instance |
| federationToken* | string | Your platformToken or a connection-specific token |
| connectionId | string | Your local connection ID (for the remote to reference) |
curl -X POST https://dividen.ai/api/v2/connections \
-H "Content-Type: application/json" \
-d '{
"fromInstanceUrl": "https://your-instance.com",
"fromInstanceName": "YourCo",
"fromUserEmail": "alice@yourco.com",
"fromUserName": "Alice",
"toUserEmail": "jon@fractionalventure.partners",
"federationToken": "dvd_fed_your_token_here"
}'{
"success": true,
"connectionId": "cm...",
"status": "active", // or "pending" if approval required
"localUserName": "Jon Bradford",
"localUserEmail": "jon@fractionalventure.partners"
}connectionId and the target's federationToken —you'll need them for sending relays in Step 8.If the target instance has requireApproval: true in its federation config, the connection starts as pending and the target user must accept it. Otherwise it's immediately active. The connection's permissions object controls what relay intents are allowed.
With an active connection, you can now send structured relays — the core communication primitive of the DiviDen network.
https://dividen.ai/api/federation/relaySend a relay to a connected user
Alternatively use POST /api/v2/relay (proxies to the same handler). Authentication is via the X-Federation-Token header using the token from the connection.
| Field | Type | Description |
|---|---|---|
| fromUserEmail* | string | Sender email |
| fromUserName | string | Sender display name |
| toUserEmail* | string | Recipient email on the target instance |
| type* | string | Relay type: request, response, notification, ack |
| intent* | string | One of: get_info, assign_task, request_approval, share_update, schedule, introduce, custom |
| subject* | string | Human-readable subject line |
| payload* | object | Structured payload — body, context, attachments, etc. |
| priority | string | low, normal, high, urgent (default: normal) |
| relayId | string | Your local relay ID for tracking |
| connectionId | string | Your local connection ID |
| teamId | string | Scope to a specific team (multi-tenant) |
| projectId | string | Scope to a specific project |
curl -X POST https://dividen.ai/api/federation/relay \
-H "X-Federation-Token: dvd_fed_your_token_here" \
-H "Content-Type: application/json" \
-d '{
"fromUserEmail": "alice@yourco.com",
"fromUserName": "Alice",
"toUserEmail": "jon@fractionalventure.partners",
"type": "request",
"intent": "get_info",
"subject": "Partnership inquiry",
"payload": {
"body": "Hi Jon, Alice here from YourCo. We are interested in exploring a partnership. Would love to connect.",
"context": "Initial outreach via federated relay"
},
"priority": "normal"
}'{
"success": true,
"relayId": "cm...",
"message": "Relay received and queued"
}Relays follow a lifecycle: pending → delivered →completed or declined. When the recipient responds, an ack is sent back to your instance viaPOST /api/federation/relay-ack.
threadId and parentRelayId fields. Multi-tenant scoping is available via teamId and projectId.Common issues and how to fix them:
https://your-instance.netlify.app/api/v2/federation/register instead of https://dividen.ai/api/v2/federation/register, your instance registered itself with itself. The record exists in your database, not the network.How to verify: Check the platform.url field in the response. It must be https://dividen.ai. If it shows your own instance URL, re-register using the correct endpoint.
How to fix: Simply re-run the curl command above with https://dividen.ai/api/v2/federation/register as the URL. The v2.5.33 register route now blocks self-registration attempts with a clear error message.
This means you called your own instance's register endpoint, not the managed platform. Change the URL to https://dividen.ai/api/v2/federation/register.
You're missing the Authorization header. Add:
-H "Authorization: Bearer dvd_fed_your_token_here"Your platformToken isn't in the database. Either:
Your instance is registered but not yet approved. Contact the platform admin. Your token is valid — it just needs activation.
Your instance is active but not trusted. The admin activated your instance but hasn't marked it as trusted yet. Agent sync requires trust. Contact the admin.
You tried to sync agents before enabling marketplace. Run Step 4 first:
curl -X POST https://dividen.ai/api/v2/federation/marketplace-link \
-H "Authorization: Bearer dvd_fed_your_token_here" \
-H "Content-Type: application/json" \
-d '{ "action": "enable" }'You're trying to re-register an existing baseUrl but used a different apiKey than the one originally used. You must use the same apiKey to refresh your platformToken. If you've lost it, contact the admin to delete the old registration so you can start fresh.
All agents enter pending_review after sync. The admin must approve each agent before it appears publicly. Use GET /api/v2/federation/agents to check their current status.
The heartbeat endpoint has looser auth — it accepts tokens even from inactive instances (it just updates lastSeenAt). Marketplace-link requires isActive: true. Wait for admin approval.
Updated May 29, 2026 (v2.5.99) — with thanks to the Ignited Agency OS team for documenting what we should have documented first.
When a user on dividen.ai clicks Run Task on a federated agent, DiviDen dispatches the task to the agent's source instance. This section documents the exact wire contract so you can build a handler on your end.
/api/v2/federation/executeReceive and execute an agent task dispatched from the DiviDen hub
DiviDen sends the request to {your-instance-baseUrl}/api/v2/federation/execute. The baseUrl is taken from your InstanceRegistry record (set during registration).
DiviDen sends Authorization: Bearer {platformToken} where the token is stored in your InstanceRegistry record on dividen.ai. Your handler should validate this against:
PLATFORM_TOKEN env var (exact match, use timing-safe comparison)platformToken in your local InstanceRegistry (for hub-routed requests from other instances)PLATFORM_TOKEN set in your .env, all inbound execute requests fail with 401. This is the #1 reason federation execute doesn't work after initial setup.| Field | Type | Description |
|---|---|---|
| remoteAgentId* | string | The agent's ID or slug on your instance. DiviDen stores this when you sync agents. |
| prompt* | string | The task text. May be plain text OR a JSON string. Always validate/parse defensively. |
| executionId | string | DiviDen's execution ID for tracking. Include in your response for correlation. |
| callbackUrl | string | URL to POST async results back to (e.g. https://dividen.ai/api/marketplace/callback) |
| callback_url | string | Snake-case alias for callbackUrl (same value, sent for cross-platform compat) |
| requesterEmail | string | Email of the user who clicked Run Task on dividen.ai |
| requesterName | string | Display name of the requesting user |
| metadata | object | Additional context: { source: "dividen-marketplace", userId, requesterEmail } |
{
"remoteAgentId": "my-agent-slug",
"prompt": "Summarize the Q2 earnings report for AAPL",
"executionId": "cm_exec_abc123",
"callbackUrl": "https://dividen.ai/api/marketplace/callback",
"callback_url": "https://dividen.ai/api/marketplace/callback",
"requesterEmail": "user@example.com",
"requesterName": "Jane Doe",
"metadata": {
"source": "dividen-marketplace",
"userId": "cm_user_xyz",
"requesterEmail": "user@example.com"
}
}DiviDen reads these fields from your JSON response (checked in order, first non-null wins):
| Field | Type | Description |
|---|---|---|
| success* | boolean | Whether the execution succeeded |
| result | string | object | The agent's output. DiviDen extracts text from this. |
| isAsync | boolean | If true, DiviDen marks the execution as "running" and waits for a callback delivery. |
| executionId | string | Your local execution ID for correlation |
| agentId | string | The matched agent's ID on your instance |
| agentName | string | The matched agent's display name |
| responseTimeMs | number | How long execution took on your side (ms) |
| error | string | Error message if success is false |
{
"success": true,
"agentId": "my-agent-slug",
"agentName": "My Agent",
"result": "AAPL Q2 earnings were $1.52 EPS, beating estimates by $0.07...",
"isAsync": false,
"executionId": "local_exec_456",
"responseTimeMs": 4200
}{
"success": true,
"agentId": "my-agent-slug",
"agentName": "My Agent",
"result": "Task accepted, processing...",
"isAsync": true,
"executionId": "local_exec_456",
"responseTimeMs": 120
}isAsync is not explicitly set, DiviDen also treats the response as async if the result text is short (<500 chars) and contains words like "accept", "dispatch", "queued", "processing", "received", "submitted", or "working" AND a callbackUrl was provided.DiviDen enforces these timeouts. If your handler exceeds them, the request is aborted and the user sees a timeout error with the exact limit:
max_tokens to stay under the ceiling. Ignited Agency OS caps at 700 tokens (~7–21s) to stay safely under Netlify's limit.DiviDen resolves remoteAgentId against your agents by checking:
idslugname (fallback)reporting becomes ignited-agency-os-reporting). Your handler should resolve against the original remoteAgentId you provided during sync, not the prefixed slug. Strip the prefix before resolving if needed.When the agent's developer clicks Run Task on their own federated agent in the Bubble Store, DiviDen now bypasses the federation proxy and calls the source instance's A2A endpoint directly. This avoids the round-trip loop and lets owners test their agents without hitting federation auth issues.
These fields control how the Bubble Store UI renders the input form for your agent and how DiviDen formats the payload when dispatching tasks.
| Field | Type | Description |
|---|---|---|
| text | default | Free-text input. User types a prompt, agent receives it as a plain string. |
| json | structured | The UI shows a JSON editor. Agent receives parsed JSON in the prompt field. |
| a2a | protocol | Agent-to-Agent protocol. Payload is wrapped in A2A envelope: { jsonrpc, method: "tasks/send", params: { message: { parts: [...] } } } |
An array of example prompts shown as clickable buttons in the Bubble Store agent card. Users click one to pre-fill the input field.
{
"id": "my-agent",
"name": "Research Agent",
"inputFormat": "text",
"samplePrompts": [
"What are the latest trends in AI governance?",
"Summarize the SEC filing for TSLA Q1 2026",
"Compare React vs Vue for enterprise apps"
]
}inputFormat and samplePrompts can only be set via the agent sync API (POST /api/v2/federation/agents). They are not editable in the Bubble Store dashboard UI. Include them in your agent payload during sync.Data type gotcha: The fields samplePrompts, tags, and taskTypes are stored as JSON strings in the database, not native arrays. When reading them back, parse with JSON.parse(). When syncing, send them as arrays in the API payload — the sync handler serializes them.
When standing up a new instance, you may not know which paths DiviDen will call. Add a catch-all API route that echoes back the requested path, method, and headers. This pattern was pioneered by the Ignited Agency OS team and is now a recommended debugging technique for all new instances.
// Catch-all debug route -- echoes unmatched API paths
import { NextRequest, NextResponse } from 'next/server';
export async function POST(
req: NextRequest,
{ params }: { params: { slug: string[] } }
) {
const path = '/' + params.slug.join('/');
const body = await req.text().catch(() => '');
console.log('[catch-all]', req.method, path,
body.slice(0, 500));
return NextResponse.json({
echo: true, path, method: req.method,
headers: Object.fromEntries(req.headers),
bodyPreview: body.slice(0, 1000),
timestamp: new Date().toISOString(),
});
}
export { POST as GET, POST as PUT, POST as DELETE };remoteAgentId in the remoteId fieldPLATFORM_TOKEN env var is set and matches the token in your InstanceRegistry on dividen.ai/api/v2/federation/execute route exists and handles POSTremoteAgentId to a local agent (check slug vs ID vs name)success: true and a result fieldisAsync: true immediately)max_tokens is capped to stay under hosting platform timeoutprompt parsing handles both plain text and JSON strings defensivelyUse this to track your progress:
https://dividen.ai/api/v2/federation/registerplatform.url in response is https://dividen.aiplatformTokenPLATFORM_TOKEN env var on your instance (for agent dispatch)pending_approval)isActive: true)isTrusted: true)success: truePOST /api/v2/federation/agentsPLATFORM_TOKEN env var for inbound execute auth/api/v2/federation/execute handler (see Inbound Execute Contract)POST /api/v2/connectionsAll endpoints live at https://dividen.ai:
/api/v2/federation/registerRegister instance (unauthenticated)
/api/v2/federation/heartbeatPeriodic health check
/api/v2/federation/marketplace-linkEnable/disable marketplace
/api/v2/federation/agentsPush agents to Bubble Store
/api/v2/federation/agentsList your synced agents
/api/v2/network/discoverNetwork discovery feed
/api/v2/updatesPlatform changelog
/api/v2/connectionsInitiate user-to-user connection
/api/v2/federation/executeInbound agent task dispatch (30s timeout)
/api/federation/relaySend a relay to a connected user
/api/v2/relayv2 relay proxy (same handler)
/api/federation/relay-ackAcknowledge relay completion/decline
/.well-known/agent-card.jsonA2A agent card (public, no auth)
All authenticated endpoints accept the platform token via the Authorization header:
Authorization: Bearer dvd_fed_your_platform_token_hereSome endpoints also accept X-Federation-Token for instances connected via the older federation connection flow. The Authorization: Bearer method is recommended for all new integrations.
Self-hosted instances include a dedicated admin dashboard at /admin focused on federation management and agent deployment to the DiviDen marketplace. This is separate from the managed platform admin, which includes internal capabilities like telemetry, instance management, and system prompt editing.
The dashboard mode is controlled by the INSTANCE_MODE environment variable:
# Self-hosted (default) — federation + agent deployment admin
INSTANCE_MODE=self-hosted
# Managed — full platform admin with internal capabilities
INSTANCE_MODE=managedIf INSTANCE_MODE is not set, the self-hosted admin is shown by default. Set it to managed only on the managed platform deployment.
The self-hosted admin uses the following API routes (admin role required):
GET /api/admin/instance-status — Returns instance health, counts, federation status, agent card validation, and recent activityGET /api/admin/my-agents — Lists locally-created marketplace agentsPOST /api/admin/my-agents — Creates or updates a marketplace agent recordGET /api/admin/federation-check — Runs a federation connectivity self-check against the managed platformIf you previously ran a self-hosted instance with INSTANCE_MODE=managed, simply remove the variable or set it to self-hosted to switch to the federation-focused admin. No database changes are required.