assets/report-template.md
# Portal Creation Report — template
The skill's final action (Step 4) is always to write `report.md`. It must start with the heading
`# Portal Creation Report`, cover the portal name, the API used and why, the framework/template, the
dispatched request + response, the remaining Step 3 work, and end with the exact sentinel line
`Task completed: portal creation dispatched — see report.md`.
Skeleton:
```markdown
# Portal Creation Report
- **Portal name:** IT Support Portal
- **API:** POST /services/data/v67.0/connect/communities — chosen because no guest Embedded Service
Deployment exists (self-service/site requires one).
- **Framework / template:** Aura — Agentforce Employee Center
## Dispatched
POST /services/data/v67.0/connect/communities
{ "name": "IT Support Portal", "urlPathPrefix": "itsupport", "templateName": "Agentforce Employee Center" }
## Response
{ "jobId": "08P...", "status": "Queued" }
## Remaining
- Step 3: Activate (Network status Live) → Add Members (profiles) → Publish (`sf community publish`).
- Embedded Agentforce agent is a separate follow-up step (not created by this call).
Task completed: portal creation dispatched — see report.md
```
references/mcp-invocation.md
# MCP Invocation Reference
Every operation in this skill dispatches through the **headless-360** MCP server. Do **not**
use the Salesforce CLI (its `api request`, `data query`, or `org open` subcommands), the
project-codey MCP server, raw `curl`, or any other HTTP client. headless-360 is the only
execution path.
## Tools
- `mcp__headless-360__discover` — search the indexed API/SOR corpus by natural language query (preflight/discovery). Optional `domain` and `resultType` (`endpoint` | `sor`) filters.
- `mcp__headless-360__describe` — pull full detail for a specific operation or Setup Operation Recipe (SOR) by id. The returned `path` (or a SOR step's `api`) is dispatch-ready — copy it verbatim.
- `mcp__headless-360__dispatch_readonly` — GET-only dispatcher. **Prefer this for all reads.**
- `mcp__headless-360__dispatch` — dispatcher for all methods (GET/POST/PUT/PATCH/DELETE). Use for writes.
`dispatch` / `dispatch_readonly` accept:
| Field | Notes |
|-------|-------|
| `method` | `"GET"`, `"POST"`, `"PATCH"`, … (`dispatch_readonly` only accepts `"GET"`) |
| `url` | The relative API path, **including** the `/services/data/vXX.0` prefix — e.g. `"/services/data/v67.0/connect/communities"`. Unlike some other dispatchers, headless-360 does **not** resolve or add the version — passing a bare `"/connect/communities"` returns `400 ROUTE_NOT_FOUND`. |
| `body` | JSON object for POST/PATCH |
| `queryParams` | map of key-value pairs for GET query strings |
| `headers` | optional HTTP headers |
Send the `url` exactly as returned by `discover`/`describe` when available — do not edit it or
prepend a host. When `discover`/`describe` don't surface the operation (common for standard
Connect API create/list calls on Experience Cloud — see "Discovery" below), dispatch the
well-known, versioned path documented in this skill directly.
Credentials are handled by whichever org the headless-360 MCP session is bound to at connection
time — **the skill never handles credentials, tokens, or login URLs.** If a call returns an auth
error, tell the user to re-authenticate the org the MCP session is pointed at — do not attempt to
fetch a token yourself.
## Response envelope
`dispatch` / `dispatch_readonly` return the org's raw Connect API response merged with a wrapper:
```json
{ "status_code": 202, "body": { "jobId": "08P...", "message": "...", "name": "..." }, "url": "https://<org-host>/services/data/v67.0/connect/communities" }
```
`body` is the controller payload. Status codes: `200`/`202` = success, `400` = bad body/route (re-check
the `url` version prefix and body shape), `404` = endpoint not in this org, `500` = downstream
dependency missing. Errors from the underlying org come back as
`{"error_code": "...", "message": "...", "status_code": 400/...}` (e.g. `ROUTE_NOT_FOUND`,
`INVALID_TYPE`, `INVALID_INPUT`) — read `error_code` and `message` to diagnose.
---
## Discovery — run first each session (optional but recommended)
```text
mcp__headless-360__discover(query="create self-service Experience Cloud site")
mcp__headless-360__discover(query="create Experience Cloud community site from template")
mcp__headless-360__discover(query="create partner PRM portal site")
```
**Known limitation (verified):** the headless-360 corpus indexes many capabilities as multi-step
SORs (Setup Operation Recipes) covering *management* of an already-existing community (topics,
navigation menus, branding, sharing sets, etc.), not the base `POST /connect/communities` /
`GET /connect/communities/templates` create/list calls themselves. Those two operations are
**not** individually resolvable via `discover`/`describe` in this corpus at time of writing — a
`describe` call for ids like `chatter.connect.api.postCommunityCollection` or
`post-community-collection` returns `"No operation or SOR found for id: ..."`, even though the
*guidance text* inside the `chatter-connect-api` SOR describe output mentions
`post-community-collection` in a documentation string (not as an actual indexed step).
**Do not conclude the capability is missing from the org.** These are standard, long-lived
Connect API endpoints (available since API v46.0+/v48.0+ per Salesforce docs) — dispatch them
directly using the verified paths below rather than falling back to the `sf` CLI or declaring "no
tool connects to a live org."
The stable, **verified-working** paths at time of writing (confirmed live against a test org):
| Purpose | Method | Path (verified) |
|---------|--------|------------------|
| List valid community templates | GET | `/services/data/v67.0/connect/communities/templates` |
| List existing Experience Cloud sites | GET | `/services/data/v67.0/connect/communities` |
| Create a general community from a template | POST | `/services/data/v67.0/connect/communities` |
| Employee/self-service Aura (or LWR) site + MIAW | POST | `/services/data/v67.0/connect/self-service/site` |
| Poll self-service site job | GET | `/services/data/v67.0/connect/self-service/site/status/{jobId}` |
| Partner (PRM) portal — synchronous | POST | `/services/data/v67.0/connect/prm/setup/sites` |
| Poll a community-create `BackgroundOperation` job | GET | `/services/data/v67.0/query?q=SELECT+Id,+Status+FROM+BackgroundOperation+WHERE+Id='<jobId>'` |
| Create the Agentforce employee agent (optional) | PATCH | `/services/data/v67.0/headless/invoke/einstein/genai-agentbuilder/create-copilot-from-template` |
| List service-agent templates for the AI-portal editor | GET | `/services/data/v67.0/headless/invoke/platform/communities/unified-aiportal-setup/get-service-agent-templates` |
If `discover` surfaces a different/newer operation id for one of these, prefer the `path` it
returns (already dispatch-ready) over the hardcoded table above — the table is a fallback, not a
canonical override.
---
## Routes
### Employee Service / Self-Service — `POST /connect/self-service/site`
Creates an **Aura** (or LWR) Experience Builder site and wires MIAW in at creation time. Requires a
**guest** Embedded Service Deployment config id.
```text
method: "POST"
url: "/services/data/v67.0/connect/self-service/site"
body:
{
"siteName": "<portal name>",
"siteType": "AURA", // AURA (default) or LWR — never Visualforce
"guestEmbeddedServiceConfigId": "<guest ESD id>",// REQUIRED
"embeddedServiceConfigId": "<auth ESD id>", // optional
"enableForGuest": true
}
```
Response: `{success, siteName, urlPathPrefix, siteUrl, error, jobId, status}`. Poll with the status
route below.
> No guest ESD yet? The self-service API **hard-requires** `guestEmbeddedServiceConfigId`. Either
> create an Embedded Service Deployment first (Setup → Embedded Service Deployments, or the
> MIAW/embedded-service setup skill), **or** use the general-community route with an Aura template
> (`Employee Portal`, `Customer Service`, or `Help Center`) — which needs no ESD.
Poll job:
```text
method: "GET"
url: "/services/data/v67.0/connect/self-service/site/status/{jobId}" // substitute the jobId into the path
```
### General Community — `POST /connect/communities`
No ESD dependency. Use an **Experience Builder** `templateName` (Aura or LWR), validated against the
live template list.
First, list templates:
```text
method: "GET"
url: "/services/data/v67.0/connect/communities/templates"
```
Verified response shape (from a live test org):
```json
{
"templates": [
{ "publisher": "Salesforce", "templateName": "Build Your Own" },
{ "publisher": "Salesforce", "templateName": "Help Center" },
{ "publisher": "Salesforce", "templateName": "Customer Account Portal" },
{ "publisher": "Salesforce", "templateName": "Customer Service" },
{ "publisher": "Salesforce", "templateName": "Agentforce Employee Center" },
{ "publisher": "Salesforce", "templateName": "Build Your Own (LWR)" },
{ "publisher": "Salesforce", "templateName": "Salesforce Tabs + Visualforce" }
],
"total": 11
}
```
Use a returned `templateName` **verbatim**. For an employee service portal prefer `Agentforce Employee Center`
(if present) — the richest employee experience (ticketing, catalog, knowledge, Agentforce-ready) —
else `Employee Portal`, else `Customer Service`; for a knowledge/help desk prefer `Help Center`. Never
use `Salesforce Tabs + Visualforce` — it is the legacy Visualforce site with no Builder.
Create (the body accepts only `name`, `description`, `templateName`, `templateParams`, `urlPathPrefix`):
```text
method: "POST"
url: "/services/data/v67.0/connect/communities"
body:
{
"name": "<portal name>",
"urlPathPrefix": "<alphanumeric only, no spaces or hyphens>",
"templateName": "Agentforce Employee Center",
"description": "<optional>"
}
```
Response (verified, `202`): `{jobId, message, name}`, e.g.:
```json
{
"jobId": "08PSB000023cP5t2AE",
"message": "Your site is almost ready. To track the site creation status, query the BackgroundOperation object and enter the jobId as the Id.",
"name": "GK Portal v2"
}
```
Poll `BackgroundOperation` (see below).
> Duplicate names return `400 INVALID_INPUT "Enter a different name. That one already exists."` —
> list existing sites with `GET /services/data/v67.0/connect/communities` and pick a non-colliding name/prefix.
#### Agentforce assistant for `Agentforce Employee Center` (optional, separate step)
The community-create call provisions the **site** only. To add the embedded **Agentforce**
conversational assistant, create the internal employee agent from its shipped template — this is a
distinct call and is not required to stand up the portal:
```text
method: "PATCH"
url: "/services/data/v67.0/headless/invoke/einstein/genai-agentbuilder/create-copilot-from-template"
body:
{
"templateNameOrId": "EmployeeCopilot__AgentforceEmployeeAgent",
"copilotContext": { "name": "<agent label>", "company": "<company name>", "newAgentUser": true }
}
```
- `copilotContext.company` is **required** — omitting it returns `500 CONTROLLER_ERROR "Company is Mandatory in Agents"` (`companyName` is a separate, non-substitute field).
- The agent is created **`Inactive`** (v1 version) and must be activated and wired to the site's channel afterward. `newAgentUser: true` auto-provisions a runtime user.
- Full Agentforce configuration (activation, channel wiring, permissions) is broad and out of scope for this skill — this skill provisions the site and points to the agent template above.
### Partner (PRM) — `POST /connect/prm/setup/sites`
Synchronous. Requires the `CommonPrmEnabled` feature.
```text
method: "POST"
url: "/services/data/v67.0/connect/prm/setup/sites"
body:
{
"siteName": "<name>",
"siteUrlPrefix": "<url prefix>",
"siteDesc": "<optional>",
"prmTemplate": "<org-specific PRM template>"
}
```
Response: `{networkId}`. No job polling (synchronous).
---
## Background job monitoring (communities path)
The communities response says "query the BackgroundOperation object." Do this through
`mcp__headless-360__dispatch_readonly` against the **regular REST query endpoint** —
**not** `/tooling/query`, and **not** the Salesforce CLI `data query` subcommand.
```text
method: "GET"
url: "/services/data/v67.0/query"
queryParams: { "q": "SELECT Id, Status FROM BackgroundOperation WHERE Id = '<jobId>'" }
```
> **Verified gotcha — Tooling query fails on this object.** Calling
> `GET /services/data/vXX.0/tooling/query` with this SOQL returns:
> ```json
> { "error_code": "API_ERROR", "status_code": 400, "body": "[{\"message\":\"sObject type 'BackgroundOperation' is not supported.\",\"errorCode\":\"INVALID_TYPE\"}]" }
> ```
> Switching to the plain `/services/data/vXX.0/query` endpoint with the identical SOQL succeeds
> (`200`, `done: true`, `records: [{Id, Status}]`). Always use the non-Tooling query path for
> `BackgroundOperation`.
> **Column discipline:** on `BackgroundOperation`, only a few columns are queryable. `SELECT Id, Status`
> works. **Do NOT** select `JobType`, `CompletedDate`, or `NumErrors` — those columns do not exist on
> `BackgroundOperation` and return `INVALID_FIELD`. Use the SOQL string exactly as above; add fields
> only after confirming them via a describe.
`Status` values observed: `Scheduled` → `Queued` → `InProgress` (or `Running`) → `Complete` → `Error`.
For the self-service path, prefer the dedicated `GET /services/data/vXX.0/connect/self-service/site/status/{jobId}`
route instead of a `BackgroundOperation` query — it returns a typed `{status, siteUrl, error}` payload.
---
## Verification
List all Experience Cloud sites and confirm the new one is present and Builder-based:
```text
method: "GET"
url: "/services/data/v67.0/connect/communities"
```
Each entry includes `siteAsContainerEnabled` — `true` means an **Experience Builder** (Aura/LWR)
site, `false` means Salesforce Tabs + Visualforce. Confirm the new site shows `true` and a non-null
`builderUrl`, and that `templateName` is not `Salesforce Tabs + Visualforce`.
---
## Gotchas
| Issue | Detail |
|-------|--------|
| Using `sf` CLI or project-codey | Not allowed. Every read/write goes through `mcp__headless-360__dispatch` / `dispatch_readonly`. |
| **Version prefix is required** | Pass `"/services/data/v67.0/connect/communities"`, **not** `"/connect/communities"`. Unlike project-codey, headless-360 does not resolve or add the version — a bare path returns `400 ROUTE_NOT_FOUND`. |
| `discover`/`describe` may not resolve create/list Connect API ops | `POST /connect/communities` and `GET /connect/communities/templates` are not individually indexed as describable operations in this corpus at time of writing. Dispatch the documented path directly instead of concluding the capability is missing. |
| `BackgroundOperation` via Tooling query | `GET /services/data/vXX.0/tooling/query` rejects `BackgroundOperation` with `400 INVALID_TYPE`. Use the plain `/services/data/vXX.0/query` endpoint instead. |
| `BackgroundOperation` columns | Only `Id`, `Status` are safe. `JobType`/`CompletedDate`/`NumErrors` → `INVALID_FIELD`. |
| Duplicate site name/prefix | `400 INVALID_INPUT "…already exists."` List `GET /services/data/vXX.0/connect/communities` and choose another. |
| Missing guest ESD | Self-service `POST` requires `guestEmbeddedServiceConfigId`; without one use the communities route with an Aura template. |
| URL prefix format | Alphanumeric only — no spaces or hyphens (`demosupportportal123`, not `demo-support-portal 123`). |
| `dispatch_readonly` is GET-only | Use `dispatch` for POST/PATCH/PUT/DELETE; `dispatch_readonly` rejects non-GET methods. |
| Credentials | Never fetch or print tokens / login URLs. The MCP session's bound org handles auth. |
references/post-creation-activate-publish.md
# Post-Creation: Activate, Add Members, Publish (make the site reachable)
`POST /connect/communities` (and the self-service site API) only **provisions** the site — it comes
back `UnderConstruction` with **no members** and **unpublished pages**. In that state the site URL
is **not reachable** (a bare `https://<domain>.my.site.com/<prefix>` returns an error / "under
construction" page). Three post-creation steps make it actually work, in this order:
1. **Activate** the Network (`status` → `Live`)
2. **Add members** (profiles/permission sets that can access the site)
3. **Publish** the Experience Builder pages
> **Tooling note — this is the one place the skill steps outside headless-360.** There is **no
> Connect API** for activating or publishing an Experience Builder site, and
> `PATCH /connect/communities/<id>` returns **405 METHOD_NOT_ALLOWED** (the endpoint is GET/HEAD
> only). Activation and membership go through the **Metadata API** (deploy the `Network`), and
> publish goes through the **`sf community publish` CLI**. Use these here even though the rest of
> the skill is headless-360-only — they are the verified working paths (confirmed live against
> `orgfarm-825d2dfcc4`).
---
## Why the site looked "broken"
A freshly created Experience site:
- has `status: UnderConstruction` on its `Network` — not addressable until set `Live`;
- has only the admin profile as a member — no employee/customer profiles can reach it;
- has **unpublished** Builder pages — even once Live, the pages 404 / show "under construction"
until published.
Also note the **URL path**: Aura Employee/Customer sites serve at the `/s`-style path and login at
`.../<prefix>/login` (or `.../<prefix>/s/login`), **not** the bare `.../<prefix>`. Confirm the
exact `loginUrl` / `siteUrl` from `GET /connect/communities/<networkId>` before telling the user
the site is broken.
---
## Step 1 — Activate the Network (status → Live)
Retrieve the Network metadata, flip `status`, redeploy. (Site name = the community `name`.)
```bash
sf project retrieve start --metadata "Network:<Site Name>" -o <org-alias>
```
Edit `force-app/main/default/networks/<Site Name>.network-meta.xml`:
```xml
<status>UnderConstruction</status> <!-- change to -->
<status>Live</status>
```
```bash
sf project deploy start --metadata "Network:<Site Name>" -o <org-alias>
```
Verify via headless-360:
```text
mcp__headless-360__dispatch_readonly(
method: "GET",
url: "/services/data/v67.0/connect/communities/<networkId>"
)
```
`status` must now read `Live`.
---
## Step 2 — Add members (profiles / permission sets)
Community membership is **group-based** — you add **profiles** (and/or permission sets), not
individual users. Users holding a member profile become members automatically. `networkMemberGroups`
in the same `Network` metadata holds these; add a `<profile>` (or `<permissionSet>`) child and
redeploy.
> **"Unified Employee" is a Profile, not a UserRole.** Verify the exact name first — a `UserRole`
> query for it returns 0 rows; it is a **Standard Profile** (`SELECT Id, Name, UserType FROM Profile
> WHERE Name = 'Unified Employee'`). Metadata uses the profile **Name** (`Unified Employee`), not
> the Id.
Edit the same `<Site Name>.network-meta.xml`:
```xml
<networkMemberGroups>
<profile>admin</profile>
<profile>Unified Employee</profile> <!-- added -->
</networkMemberGroups>
```
```bash
sf project deploy start --metadata "Network:<Site Name>" -o <org-alias>
```
Verify via headless-360 (`ParentId` = the added profile's Id):
```text
mcp__headless-360__dispatch_readonly(
method: "GET",
url: "/services/data/v67.0/query",
queryParams: { "q": "SELECT Id, NetworkId, ParentId FROM NetworkMemberGroup WHERE NetworkId = '<networkId>'" }
)
```
> **`NetworkMemberGroup` column discipline:** it exposes `Id`, `NetworkId`, `ParentId` — **no**
> `MemberType`, and **no** `Parent.Name` relationship. Selecting those returns `INVALID_FIELD` /
> "Didn't understand relationship 'Parent'". Query `ParentId` and match it to a `Profile.Id` you
> looked up separately.
Steps 1 and 2 can be combined into a **single** `Network` deploy (flip `status` **and** add the
member group in one edit) to save a round-trip.
---
## Step 3 — Publish the Experience Builder pages
No Connect API exists for this; use the CLI. It publishes all Builder pages and kicks off a
background job.
```bash
sf community publish --name "<Site Name>" -o <org-alias>
```
Returns `{ id, jobId, status, url, message }` — "We're publishing your changes now."
Poll the job to completion via headless-360 (regular `/query`, only `Id, Status`):
```text
mcp__headless-360__dispatch_readonly(
method: "GET",
url: "/services/data/v67.0/query",
queryParams: { "q": "SELECT Id, Status FROM BackgroundOperation WHERE Id = '<jobId>'" }
)
```
Wait for `Status: Complete`.
---
## Final verification
```text
mcp__headless-360__dispatch_readonly(
method: "GET",
url: "/services/data/v67.0/connect/communities/<networkId>"
)
```
Confirm `status: Live`. Give the user the **login URL** (`.../<prefix>/login`), not the bare
prefix. Members on the added profile(s) can now sign in.
---
## Gotchas index
| Issue | Fix |
|-------|-----|
| Bare site URL `.../<prefix>` not reachable | The site serves at `.../<prefix>/login` (and the app at `/s`). Read `loginUrl` from `GET /connect/communities/<id>` — don't assume the bare prefix works. |
| `PATCH /connect/communities/<id>` → 405 METHOD_NOT_ALLOWED | The communities endpoint is GET/HEAD only. Activate via `Network` **Metadata** deploy (`status: Live`), not a Connect PATCH. |
| Site stays `UnderConstruction` after create | Expected — creation only provisions. Deploy `Network` with `status: Live` to activate. |
| Members can't reach the site | Only admin is a member on create. Add the target profile(s) to `networkMemberGroups` and redeploy. |
| "Unified Employee" not found as a role | It's a **Profile**, not a `UserRole`. Use the profile Name in `networkMemberGroups`. |
| `NetworkMemberGroup` `MemberType` / `Parent.Name` → INVALID_FIELD | Select only `Id, NetworkId, ParentId`; resolve `ParentId` against `Profile.Id`. |
| Published pages still 404 after activation | Activation (Network → Live) and **publish** are separate. Run `sf community publish` and wait for the `BackgroundOperation` to reach `Complete`. |
| No Connect API for activate/publish | By design — use Metadata API (`Network`) for activate + members, and `sf community publish` for publish. These are the verified paths. |
references/templates.md
# Portal Templates Reference
This document describes how to choose the framework and template for a Digital Experience portal.
## Framework: always Experience Builder (Aura or LWR), never Visualforce
Experience Cloud sites come in two families:
- **Experience Builder sites** — built on **Aura** or **LWR** (Lightning Web Runtime). These have the drag-and-drop **Builder**, modern components, branding, and responsive layouts. This is what you want in almost all cases.
- **Salesforce Tabs + Visualforce sites** (a.k.a. the legacy "VF Template") — a standard-nav + Visualforce site with **no Experience Builder**. This is legacy. Do **not** create these for new portals.
**How to tell them apart:** the Metadata API `Network.enableSiteAsContainer` field is `true` for an Experience Builder site and `false` for a Salesforce Tabs + Visualforce site. In Setup → Digital Experiences → All Sites, the **Framework** column shows `Aura`, `LWR`, or `Visualforce`, and only Builder-based sites show a **Builder** workspace link.
> The bug this reference exists to prevent: creating a portal with `templateName: "VF Template"` (Salesforce Tabs + Visualforce). It produces a Visualforce-framework site with no Builder. Use the self-service site API with `siteType: AURA`, or a `communities` API Experience Builder `templateName`.
---
## Recommended path for employee service / ITSM / help desk portals
**Preferred (richest employee experience): the `Agentforce Employee Center` Aura template via the communities API.** This is a purpose-built employee-service portal — IT and HR ticket management, a self-service catalog, a knowledge base, enablement programs, and (optionally) an embedded **Agentforce** conversational assistant. Prefer it over the plainer `Employee Portal` / `Customer Service` templates whenever the org's live template list (`GET /connect/communities/templates`) includes `Agentforce Employee Center`.
```text
mcp__headless-360__dispatch(
method: "POST",
url: "/services/data/v67.0/connect/communities",
body: { "name": "<portal name>", "urlPathPrefix": "<alphanumeric>", "templateName": "Agentforce Employee Center" }
)
```
The communities API takes only `{name, description, templateName, templateParams, urlPathPrefix}` — pass `Agentforce Employee Center` verbatim as `templateName`. This path needs **no** MIAW Embedded Service Deployment.
> **Two layers.** `POST /connect/communities` with `templateName: "Agentforce Employee Center"` provisions the **site** (pages, ticketing, catalog, knowledge). The **conversational assistant** is a *separate* Agentforce agent — the site alone does not create one. To add it, create the internal employee agent from its shipped template (see "Agentforce assistant" below); this skill provisions the site and points the user to that step. It does **not** activate an agent automatically.
### Alternative paths (when Agentforce Employee Center is not desired or not present)
- **MIAW at creation time + a guest ESD exists →** `POST /connect/self-service/site` with `siteType: "AURA"`. This deploys an Aura Experience Builder site and wires **MIAW** in at creation time via Embedded Service Deployment (ESD) config IDs. Key inputs: `siteName` (required); `guestEmbeddedServiceConfigId` (required) — guest MIAW ESD; `embeddedServiceConfigId` (optional) — authenticated MIAW ESD; `siteType` — `AURA` (default) or `LWR`; plus `enableForGuest`, `contentDocumentId` (logo), `brandColors` (optional).
- **A plainer employee portal, no Agentforce, no MIAW →** communities API with `Employee Portal` (if present), else `Customer Service` or `Help Center`. Needs no ESD.
> Note: an internal "ITSM Employee Service" seed template exists as an org/feature template for the ITSM product (associated with CMDB and the `ItSrvcDscvrMgrPsl` permission set license). That is **not** a `templateName` value for `POST /connect/communities` — do not pass it as `templateName`.
---
## Agentforce assistant for the Employee Center (optional, separate step)
The `Agentforce Employee Center` site can host an **Agentforce** conversational assistant, but the site-create call does not build one. The agent is created from a template that ships with the org:
```text
mcp__headless-360__dispatch(
method: "PATCH",
url: "/services/data/v67.0/headless/invoke/einstein/genai-agentbuilder/create-copilot-from-template",
body: {
"templateNameOrId": "EmployeeCopilot__AgentforceEmployeeAgent",
"copilotContext": { "name": "<agent label>", "company": "<company name>", "newAgentUser": true }
}
)
```
- `templateNameOrId` — `EmployeeCopilot__AgentforceEmployeeAgent` is the shipped **internal employee** agent template (the service-agent equivalent is `SvcCopilotTmpl__EinsteinServiceAgent`).
- `copilotContext.company` is **required** — omitting it returns `500 CONTROLLER_ERROR "Company is Mandatory in Agents"`. `companyName` is a distinct, non-substitute field.
- The agent is created **`Inactive`** with a v1 version; it must be activated and connected to the site's channel afterward. `newAgentUser: true` auto-provisions a runtime user.
- The Experience Builder AI-portal editor lists attachable agents via `GET /services/data/v67.0/headless/invoke/platform/communities/unified-aiportal-setup/get-service-agent-templates`.
> Agentforce setup (agent creation, activation, channel wiring, permissions) is broad and largely out of scope for this portal-create skill. This skill provisions the **site** and, when the user wants the conversational assistant, points them to the agent template above rather than fully configuring Agentforce.
---
## Community Templates (`POST /connect/communities`)
The communities API accepts an Experience Builder **display name** as `templateName`. Accepted strings can vary by org edition/version — **always validate against the live list** via headless-360:
```text
mcp__headless-360__dispatch_readonly(method: "GET", url: "/services/data/v67.0/connect/communities/templates")
```
Use a returned `templateName` verbatim. The most common Experience Builder templates:
### Agentforce Employee Center (preferred for employee service)
**Template name:** `Agentforce Employee Center` · **Framework:** Aura · **Publisher:** Salesforce
The richest employee-service template: **IT and HR ticket management**, a **self-service catalog**, a **knowledge base**, **enablement programs**, and an optional embedded **Agentforce** conversational assistant. This is the modern successor to the plainer `Employee Portal` / `Customer Service` experiences for internal help desks. Not Visualforce (`siteAsContainerEnabled = true`).
**Use when:** Building an employee service / IT help desk / HR service portal and the org's live template list (`GET /connect/communities/templates`) includes `Agentforce Employee Center`. Prefer it over `Employee Portal` and `Customer Service` for this use case.
**Note:** The `POST /connect/communities` call provisions the site only. The Agentforce conversational assistant is a **separate** step — create it from the `EmployeeCopilot__AgentforceEmployeeAgent` template (see "Agentforce assistant for the Employee Center" above). Do not assume the site-create call activates an agent.
---
### Employee Portal
**Template name:** `Employee Portal` · **Framework:** Aura
Purpose-built Aura Experience Builder template for **employee service / internal self-service** portals (IT help desk, HR service, ITSM). A plainer alternative to `Agentforce Employee Center` — use it when the richer Agentforce template is not present or not wanted. Not Visualforce (`siteAsContainerEnabled = true`).
**Use when:** Building an employee-facing service/self-service portal via the communities API without a pre-existing MIAW Embedded Service Deployment (the self-service site API requires a guest ESD; this template does not), and `Agentforce Employee Center` is unavailable or undesired.
---
### Customer Service (formerly "Napili")
**Template name:** `Customer Service` · **Framework:** Aura · internal developerName: `Service Community Template`
The main Aura self-service template: Knowledge articles, case deflection, case management, community/Q&A. Mobile-responsive.
**Use when:** Building a self-service support community for customers or employees.
> Do not pass the legacy brand name `Napili` or a snake_case guess like `customer_service`. The current API display value is `Customer Service`.
---
### Help Center
**Template name:** `Help Center` · **Framework:** Aura · internal developerName: `Help Center Template`
Public / self-service knowledge + case deflection help center. More focused on knowledge and deflection than the fuller Customer Service community.
**Use when:** Building a knowledge-first help center or an IT help desk self-service experience.
---
### Customer Account Portal
**Template name:** `Customer Account Portal` · **Framework:** Aura · internal developerName: `CPT Community Template`
Authenticated account self-service — view/pay invoices, manage account, access records.
**Use when:** Building an authenticated portal for customers to manage their account.
---
### Partner Central
**Template name:** `Partner Central` · **Framework:** Aura · internal developerName: `PRM Community Template`
Partner Relationship Management — channel sales, deal registration, lead distribution, partner account management.
**Use when:** Building a partner portal via the communities API (as opposed to the dedicated PRM API).
---
### Build Your Own
**Template name:** `Build Your Own` · **Framework:** Aura · internal developerName: `Starter Template`
Blank Aura starting point — full control over pages and components.
**Use when:** You want a custom Aura site without pre-built self-service pages.
---
### Build Your Own (LWR)
**Template name:** `Build Your Own (LWR)` · **Framework:** LWR · internal developerName: `talon-template-byo`
Blank modern Lightning Web Runtime site — fast, headless-friendly.
**Use when:** You want the most modern, performant framework and are comfortable building from a blank canvas.
---
### Microsite (LWR)
**Template name:** `Microsite (LWR)` · **Framework:** LWR · internal developerName: `microsite-template-marketing`
Lightweight LWR marketing microsite.
**Use when:** Building a small, fast marketing site.
---
### Legacy: Salesforce Tabs + Visualforce ("VF Template") — DO NOT USE
**Template name:** `Salesforce Tabs + Visualforce`
A standard-nav + Visualforce site with **no Experience Builder**. This is the source of the "created as Visualforce" bug. Not an Experience Builder site (`enableSiteAsContainer = false`). Avoid for all new portals.
---
## PRM Templates (`POST /connect/prm/setup/sites`)
PRM templates are org-specific and vary by:
- PRM package version installed
- Org configuration
- Industry-specific templates
- Custom templates added by admins
### How to Find Available PRM Templates
1. **Via headless-360:** the Aura PRM templates (`Partner Central`, `Partner Central (Enhanced)`) also appear in `mcp__headless-360__dispatch_readonly(method: "GET", url: "/services/data/v67.0/connect/communities/templates")`. Dedicated PRM-only templates are org-specific.
2. **Via Setup UI:** Setup → Digital Experiences → Settings → Partner Templates. Note the exact names (case-sensitive).
3. **Common names:** `Partner Central`, `Channel Management`, plus custom templates (varies by org).
**Important:** Template names are case-sensitive and must match exactly. If unsure, check the Setup UI or ask the user.
Alternatively, if the org does not have PRM enabled, build a partner portal via the communities API with the `Partner Central` template.
---
## MIAW (Messaging for In-App and Web)
- The **self-service site API** wires MIAW in at creation time via `guestEmbeddedServiceConfigId` / `embeddedServiceConfigId`. This is the recommended path when the portal needs messaging.
- For **general communities** (`POST /connect/communities`), MIAW is **not** part of the create call. Configure it separately — create a Messaging Channel + Embedded Service Deployment (Setup → Embedded Service Deployments), then add the Embedded Service (Messaging) component to the site's pages in Experience Builder.
- Embedded Service Deployments must **pre-exist**; this skill does not create them.
---
## Template Parameters (`templateParams`)
The `templateParams` object on the communities API accepts template-specific configuration. Parameters vary by template and are not well-documented.
**Best practice:** For standard Experience Builder templates (`Customer Service`, `Help Center`, `Partner Central`), omit `templateParams` unless you need specific customization — the defaults work well. To discover parameters, create a test site via the UI and inspect the resulting Network/ExperienceBundle metadata.
---
## Template Selection Guide
All rows dispatch through `mcp__headless-360__dispatch` / `dispatch_readonly`.
| Requirement | API | Framework | Template / `siteType` |
|-------------|-----|-----------|-----------------------|
| Employee service / ITSM / HR / help desk (richest; Agentforce-ready) | `communities` | Aura | `Agentforce Employee Center` |
| Employee service / ITSM / help desk (with MIAW, guest ESD exists) | `self-service/site` | Aura | `siteType: AURA` |
| Employee service / ITSM / help desk (plainer, no Agentforce / no MIAW) | `communities` | Aura | `Employee Portal` (fallback `Customer Service`) |
| Customer support / self-service community | `communities` | Aura | `Customer Service` |
| Knowledge base / case deflection | `communities` | Aura | `Help Center` |
| Authenticated account self-service | `communities` | Aura | `Customer Account Portal` |
| Partner portal (with PRM) | `prm/setup/sites` | Aura | Org-specific PRM template |
| Partner portal / channel (no PRM) | `communities` | Aura | `Partner Central` |
| Custom Aura site | `communities` | Aura | `Build Your Own` |
| Modern blank / headless-friendly site | `communities` | LWR | `Build Your Own (LWR)` |
| Marketing microsite | `communities` | LWR | `Microsite (LWR)` |
---
## Template Customization Post-Creation
After portal creation, customize via:
1. **Experience Builder** — visual page builder. Setup → Digital Experiences → [Your Site] → **Builder**. Drag-and-drop components, layouts, styling.
2. **Branding Editor** — colors, fonts, logo. Builder → Settings → Theme/Branding.
3. **Pages** — create or override pages in Builder.
4. **Custom components** — develop LWC (or Aura) components, deploy, and add them via Builder.
**Note:** Initial framework/template selection sets the foundation, but extensive customization is possible post-creation.
---
## Version Compatibility
### API Version Notes
- All calls go through `mcp__headless-360__dispatch` / `dispatch_readonly`, which do **not** resolve the API version — pass the full `/services/data/vXX.0` prefix in every `url` (e.g. `url: "/services/data/v67.0/connect/communities"`).
- **v67.0 (Summer '26)** is the current/latest API version verified against a live test org at time of writing.
- `POST /connect/self-service/site` is available in recent versions (min-version 262).
- `GET /connect/communities/templates` is available v46.0+.
- `POST /connect/communities` is available v48.0+.
### Framework guidance
- **Aura Experience Builder templates** (Customer Service, Help Center, Customer Account Portal, Partner Central, Build Your Own) — current best practice for most portals.
- **LWR templates** (Build Your Own (LWR), Microsite (LWR)) — the most modern framework; prefer for greenfield custom or headless-friendly sites.
- **Salesforce Tabs + Visualforce** ("VF Template") — legacy; **do not use** for new portals.
- **Deprecated templates** (e.g. Kokua, Koa) — avoid.
**Best practice:** For employee service / ITSM / HR / help-desk portals, prefer the **`Agentforce Employee Center`** Aura template via the communities API — it ships the fullest employee experience (ticketing, catalog, knowledge, Agentforce-ready). Fall back to the self-service site API with `siteType: AURA` when the portal needs MIAW wired in at creation time and a guest ESD exists, or to the `Employee Portal` template (then `Customer Service`) when a plainer, non-Agentforce site is wanted. Use `Customer Service` (or `Help Center`) for customer self-service communities. Always validate exact `templateName` strings via `mcp__headless-360__dispatch_readonly(method: "GET", url: "/services/data/v67.0/connect/communities/templates")` in the target org — template availability varies by edition/version. The Agentforce conversational assistant is a separate step (`EmployeeCopilot__AgentforceEmployeeAgent`).
SKILL.md
---
name: experience-portal-create
description: "Create / provision / set up a NEW Digital Experience (Communities) / Experience Cloud site — employee service, IT support, help desk, HR, customer, and partner portals — via the headless-360 MCP site-creation APIs. Use whenever a user asks to create/provision/set up a portal, site, or community, e.g.: 'create an employee service portal', 'create an IT Support Portal', 'create an Agentforce Employee Center', including wiring MIAW (Messaging for In-App/Web) at create. This is the site-CREATION skill and OWNS provisioning a new Aura OR LWR Experience Builder site from scratch — trigger it even when the user says 'Experience Builder site' or 'LWR site', as long as a NEW site is wanted. It always writes a portal-creation report as its final step (never ad hoc via CLI) and never makes a legacy Tabs+Visualforce site. DO NOT TRIGGER only for modifying an EXISTING site's pages/routes/theme/branding/guest-access metadata (experience-lwr-site-generate), existing-site customization (experience-ui-bundle), CMS, Commerce."
metadata:
version: "1.0"
domains: ["Experience"]
minApiVersion: "67.0"
relatedSkills:
- "experience-lwr-site-generate"
mcpTools:
headless-360:
tools: ["describe", "discover", "dispatch", "dispatch_readonly"]
semver: ">=1.0.0"
cliTools:
- tool: ["sf"]
semver: ">=2.0.0"
allowed-tools: |
Read AskUserQuestion Bash
mcp__headless-360__discover
mcp__headless-360__describe
mcp__headless-360__dispatch
mcp__headless-360__dispatch_readonly
---
# Create Digital Experience Portal
Create a new Digital Experience (formerly Communities) portal/site in Salesforce. Supports employee service portals, partner portals (PRM), and general customer communities.
**Every operation runs through the headless-360 MCP server** (`mcp__headless-360__discover` →
`mcp__headless-360__describe` → `mcp__headless-360__dispatch` / `mcp__headless-360__dispatch_readonly`).
Do **not** use the Salesforce CLI (its `api request`, `data query`, or `org open` subcommands), the project-codey MCP
server, raw `curl`, or any other HTTP client — `dispatch`/`dispatch_readonly` is the only way this
skill talks to the org. See `references/mcp-invocation.md` for the exact call shapes.
## Scope
- **In scope**: Creating Digital Experience sites via the headless-360 Connect API dispatcher. Portal type selection. Basic configuration (name, URL, templates). Self-service portals with embedded service configs. **Making the site reachable end to end** — activating the Network (`status: Live`), adding member profiles, and publishing the Experience Builder pages (see `references/post-creation-activate-publish.md`).
- **Out of scope**: Deep post-creation customization (page layout/component authoring in Builder). Content authoring. Branding beyond initial setup. Individual per-user record management (membership is added at the profile/permission-set level, not per user).
---
## Execution model (read first)
Every org call is a **dispatch**: `mcp__headless-360__dispatch_readonly(url, method: "GET", queryParams)`
for reads, `mcp__headless-360__dispatch(url, method, body)` for writes; read `status_code` + `body` from
the response. To resolve the endpoint for a portal type, use `mcp__headless-360__discover(query=...)` and
`mcp__headless-360__describe(id=...)` as needed. Connect API create/list operations for Experience Cloud
are not always indexed by `discover`/`describe` — when a lookup returns nothing, dispatch the well-known
versioned Connect API path directly (see `references/mcp-invocation.md`) rather than concluding the
capability is missing.
**Critical: paths must include the full `/services/data/vXX.0/...` prefix** (e.g.
`"/services/data/v67.0/connect/communities"`) — unlike some other dispatchers, headless-360 does
**not** resolve or inject the API version for you. A path without the version prefix returns
`400 ROUTE_NOT_FOUND`. Copy the path verbatim from a `discover`/`describe` result when available;
otherwise use the version shown in this skill's examples (`v67.0` at time of writing) and adjust if
the org runs a different version. Full details, response envelope, job-monitoring, and gotchas live
in `references/mcp-invocation.md`.
---
## Clarifying Questions
Before proceeding, determine:
1. **Portal type?**
- Employee Service / ITSM / HR / help desk → **prefer the `Agentforce Employee Center` Aura template** via the communities API (richest employee experience; Agentforce-ready). Use the self-service API instead when MIAW must be wired in at creation time and a guest ESD exists.
- Partner Portal (PRM) → requires PRM feature enabled
- Customer Community → general community creation (Aura or LWR Experience Builder template)
2. **Basic settings (required for all types):**
- Portal name?
- URL prefix? (must be alphanumeric only, no hyphens or spaces)
- Description (optional)
3. **For Employee Service / Self-Service portals:**
- `siteType`? → default `AURA` (Aura Experience Builder + Builder). Use `LWR` only if the user explicitly wants a Lightning Web Runtime site. **Never** create a Salesforce Tabs + Visualforce ("VF Template") site — those are legacy and have no Builder.
- MIAW / Embedded Service Deployment ID(s)? These wire Messaging for In-App and Web into the portal at creation time. A **guest** ESD config is required by the self-service API; an **authenticated-user** ESD config is optional. If the user hasn't created an Embedded Service Deployment yet, point them to Setup → Embedded Service Deployments first.
4. **For Partner portals only:**
- PRM template name? (check org-specific templates)
---
## Required Inputs
### Employee Service / Self-Service Portals (`POST /connect/self-service/site`):
- `siteName` (required) - portal name
- `guestEmbeddedServiceConfigId` (required) - Embedded Service Deployment (MIAW) config ID for **guest** users
- `embeddedServiceConfigId` (optional) - Embedded Service Deployment (MIAW) config ID for **authenticated** users
- `siteType` (optional) - `AURA` (default) or `LWR`. Produces an Experience Builder site. Do **not** use Visualforce.
- `enableForGuest` (optional) - whether guest (unauthenticated) users can access the site
- `contentDocumentId` (optional) - ContentDocument ID of a logo image to wire into the site's branding set
- `brandColors` (optional) - array of RGBA colors targeting `action`, `link`, `border`, `text`, `pageBackground`
> This API sets the URL path prefix automatically from the site name. There is no `templateName` — the framework is chosen with `siteType` (Aura/LWR), never Visualforce.
### Partner Portals (PRM):
- `siteName` (required)
- `siteUrlPrefix` (required)
- `prmTemplate` (required)
- `siteDesc` (optional)
### General Communities (`POST /connect/communities`):
- `name` (required)
- `urlPathPrefix` (required) - alphanumeric only, no hyphens
- `templateName` (required) - an **Experience Builder** template. Aura: `Agentforce Employee Center` (preferred for employee service), `Employee Portal`, `Customer Service`, `Help Center`, `Customer Account Portal`, `Partner Central`, `Build Your Own`. LWR: `Build Your Own (LWR)`, `Microsite (LWR)`. Validate the exact string via `GET /connect/communities/templates` (see below). Do **not** use `Salesforce Tabs + Visualforce` ("VF Template") — it is a legacy Visualforce site with no Builder.
- `description` (optional)
---
## Workflow
### Step 1: Determine API Based on Portal Type
1. **Employee Service / Self-Service** → `POST /connect/self-service/site`
- Creates an **Aura** (or LWR) Experience Builder site — never Visualforce
- Wires MIAW (Embedded Service Deployment) into the site at creation time
- Prerequisites: `CustomizeApplication` permission; org has self-service site-creation API access; a guest Embedded Service Deployment exists
2. **Partner (PRM)** → `POST /connect/prm/setup/sites`
- Prerequisites: `CommonPrmEnabled` feature
3. **General Community** → `POST /connect/communities`
- Uses an Experience Builder `templateName` (Aura or LWR) — never `Salesforce Tabs + Visualforce`
- Prerequisites: Manage Communities permission (`ManageNetworks`)
---
### Step 2: Create Portal (By Type)
#### Option A: Employee Service / Self-Service Portal
Use the self-service site API. It creates an **Aura Experience Builder** site (with the Builder option) by deploying CustomSite, Network, and ExperienceBundle metadata, then wires MIAW into the site via the given Embedded Service Deployment (ESD) config IDs. This is the correct path for ITSM / IT help desk / employee self-service portals.
**API Call** (via `mcp__headless-360__dispatch`):
```text
method: "POST"
url: "/services/data/v67.0/connect/self-service/site"
body:
{
"siteName": "<portal-name>",
"siteType": "AURA",
"guestEmbeddedServiceConfigId": "<guest-ESD-config-id>",
"embeddedServiceConfigId": "<auth-ESD-config-id>",
"enableForGuest": true
}
```
Poll with `GET /services/data/v67.0/connect/self-service/site/status/{jobId}` via `mcp__headless-360__dispatch_readonly`.
- `siteType` defaults to `AURA` (Aura Experience Builder + Builder). Pass `LWR` only if the user explicitly asks for a Lightning Web Runtime site. Never create a Visualforce site.
- `guestEmbeddedServiceConfigId` is **required** — it is the MIAW Embedded Service Deployment config ID for guest users. `embeddedServiceConfigId` (authenticated users) is optional. If the user has no Embedded Service Deployment yet, have them create one first (Setup → Embedded Service Deployments), or use the MIAW/embedded-service setup skill.
- Optional branding: `contentDocumentId` (logo) and `brandColors` (array of `{ "type": "action|link|border|text|pageBackground", "color": { "r": 0-255, "g": 0-255, "b": 0-255, "a": 0-1 } }`).
**Response:**
```json
{
"success": true,
"siteName": "IT Support Portal",
"urlPathPrefix": "itsupport",
"siteUrl": "https://domain.my.site.com/itsupport",
"jobId": "708...",
"status": "Queued"
}
```
**On success**, report `Success:` — the portal creation started (Aura + Experience Builder); give the
name, framework (Aura), `jobId`, and `status`, and note it is provisioning in the background
(Network, CustomSite, ExperienceBundle metadata + the Embedded Service/MIAW deployment). Next: monitor
the job (see 'Background Job Monitoring'), then complete Step 3 (Activate → Add Members → Publish).
**On failure**, report `Failure:` with the `{error}` — see the "Common Errors" section for the causes
(missing/invalid `guestEmbeddedServiceConfigId`, duplicate name/URL prefix, org lacks self-service
site-creation API access, missing `CustomizeApplication`) and their resolutions.
---
#### Option B: Partner Portal (PRM)
**API Call** (via `mcp__headless-360__dispatch`):
```text
method: "POST"
url: "/services/data/v67.0/connect/prm/setup/sites"
body:
{
"siteName": "<name>",
"siteUrlPrefix": "<url-prefix>",
"siteDesc": "<description>",
"prmTemplate": "<template-name>"
}
```
Synchronous — no job polling.
**Response:**
```json
{
"networkId": "0DB..."
}
```
**On success**, report `Success:` — the partner portal was created; give the name, `networkId`,
`siteUrlPrefix`, and `prmTemplate`. Next: find it at Setup → Digital Experiences → All Sites (by
Network ID), then complete Step 3 (Activate → Add Members → Publish).
**On failure**, report `Failure:` — see "Common Errors" (org lacks PRM/`CommonPrmEnabled`, invalid PRM
template name, duplicate name/URL prefix). PRM templates: Setup → Digital Experiences → Settings →
Partner Templates.
---
#### Option C: General Community
**First, discover valid templates** (required — accepted `templateName` strings vary by org edition/version), via `mcp__headless-360__dispatch_readonly`:
```text
method: "GET"
url: "/services/data/v67.0/connect/communities/templates"
```
Response: `{ "templates": [ { "publisher": "Salesforce", "templateName": "Employee Portal" }, … ], "total": N }`. Use a returned `templateName` verbatim. Prefer an **Experience Builder** template (Aura or LWR). Never use `Salesforce Tabs + Visualforce`.
**API Call** (via `mcp__headless-360__dispatch`):
```text
method: "POST"
url: "/services/data/v67.0/connect/communities"
body:
{
"name": "<name>",
"urlPathPrefix": "<url-prefix>",
"description": "<description>",
"templateName": "Agentforce Employee Center"
}
```
The body accepts only `{name, description, templateName, templateParams, urlPathPrefix}` — omit `templateParams` unless you need template-specific config.
For an **employee service / ITSM / HR portal**, prefer the **`Agentforce Employee Center`** template when the org's live template list includes it — it ships IT/HR ticketing, a self-service catalog, a knowledge base, and an Agentforce-ready experience. Fall back to `Employee Portal` (then `Customer Service`) for a plainer, non-Agentforce site. Other options by use case: `Help Center` (Aura knowledge/deflection), `Customer Account Portal` (Aura authenticated account self-service), `Partner Central` (Aura PRM), or `Build Your Own (LWR)` for a modern blank LWR site.
> **Agentforce Employee Center is two layers.** This `POST /connect/communities` call provisions the **site** only. The embedded **Agentforce conversational assistant** is a separate step — create the internal employee agent from its shipped template (`EmployeeCopilot__AgentforceEmployeeAgent`) via `PATCH /services/data/v67.0/headless/invoke/einstein/genai-agentbuilder/create-copilot-from-template` (`copilotContext.company` is **required**), then activate it and wire it to the site. This skill provisions the site and points the user to that step; full Agentforce setup is out of scope. See `references/templates.md`.
**Response:**
```json
{
"jobId": "08P...",
"message": "Your site is almost ready. To track the site creation status, query the BackgroundOperation object and enter the jobId as the Id.",
"name": "Customer Community"
}
```
**On success**, report `Success:` — community creation started; give the name, `jobId`, and
`message`. Next: monitor the job (see 'Background Job Monitoring'), then complete Step 3 (Activate →
Add Members → Publish).
**On failure**, report `Failure:` — see "Common Errors" (invalid `templateName` — run
`GET /services/data/v67.0/connect/communities/templates` and use a returned value verbatim; duplicate
name/URL prefix; missing Manage Communities permission).
---
### Step 3: Make the Site Reachable — Activate, Add Members, Publish
**Creation only provisions the site** — it comes back `UnderConstruction`, admin-only, with
unpublished pages, so its URL is **not reachable** yet (the #1 "my portal doesn't work" cause).
Complete three steps, in order: (1) **Activate** — deploy the `Network` metadata with
`<status>Live</status>`; (2) **Add members** — add the target **profile(s)** to
`networkMemberGroups` (membership is profile-based, not per user; e.g. **`Unified Employee`** — a
Profile, not a UserRole) and redeploy (combinable with step 1); (3) **Publish** —
`sf community publish --name "<Site Name>"`, then poll the returned `jobId` on `BackgroundOperation`
until `Complete`. Then confirm `status: Live` and give the user the **login URL**
(`.../<prefix>/login`), not the bare prefix.
> **Tooling exception:** activate/members use the **Metadata API** (`Network` deploy) and publish
> uses **`sf community publish`** — there is no Connect API for these (`PATCH /connect/communities`
> returns 405). This is the one place the skill uses tools other than headless-360; Step 3 reads
> still go through headless-360.
**Exact commands, XML, verification queries, and gotchas: `references/post-creation-activate-publish.md`.**
---
### Step 4: Write the Portal Creation Report (always — final step)
**Always finish by writing a `report.md` summarizing what was done** — this is the skill's final,
non-optional action, whether the create call succeeded, is still provisioning, or failed. Write it to
the working/output directory as `report.md`.
The report must:
- Start with the heading `# Portal Creation Report`.
- State the **portal name**, the **API used** (`self-service/site`, `communities`, or `prm`) and
**why** (e.g. "no guest ESD present → communities API"), the **framework** (Aura / LWR), and the
**template** or `siteType` chosen.
- Give the **dispatched request** (path + key body fields) and the **response** (`jobId` /
`networkId` / `siteUrl` / `status`, or the error).
- List the **remaining Step 3 work** (Activate → Add Members → Publish) and, for employee-service
sites, note that the **embedded Agentforce agent is a separate follow-up step**.
- End with the sentinel line, exactly:
`Task completed: portal creation dispatched — see report.md`
**Copy the template at `assets/report-template.md`** and fill in the portal-specific values.
---
## Template Recommendations
All recommendations produce **Experience Builder** sites (Aura or LWR). Never recommend `Salesforce Tabs + Visualforce` ("VF Template") — it is legacy and has no Builder.
| Use Case | API | Framework | Template / `siteType` |
|----------|-----|-----------|-----------------------|
| Employee service / ITSM / HR / help desk (richest; Agentforce-ready) | `communities` | Aura | `Agentforce Employee Center` |
| Employee service / help desk (MIAW at creation, guest ESD exists) | `self-service/site` | Aura | `siteType: AURA` (+ MIAW ESD config) |
| Employee service / help desk (plainer, no Agentforce) | `communities` | Aura | `Employee Portal` (fallback `Customer Service`) |
| Customer support / self-service community | `communities` | Aura | `Customer Service` |
| Knowledge base / case deflection | `communities` | Aura | `Help Center` |
| Authenticated account self-service | `communities` | Aura | `Customer Account Portal` |
| Partner portal (with PRM) | `prm/setup/sites` | Aura | Org-specific PRM template |
| Partner portal / channel (no PRM) | `communities` | Aura | `Partner Central` |
| Modern blank / headless-friendly site | `communities` | LWR | `Build Your Own (LWR)` |
**Modern recommendations:**
- For **employee service / ITSM / HR** portals, prefer the **`Agentforce Employee Center`** Aura template via the communities API — it ships the fullest employee experience (ticketing, catalog, knowledge, Agentforce-ready). The conversational assistant is a separate agent step (`EmployeeCopilot__AgentforceEmployeeAgent`). Use the **self-service site API** (`siteType: AURA`) instead when the portal needs MIAW wired in at creation time and a guest Embedded Service Deployment exists; use `Employee Portal` for a plainer, non-Agentforce site.
- For **customer communities**, use the **`Customer Service`** template (Aura, mobile-responsive) via the communities API.
See `references/templates.md` for complete template documentation.
---
## Background Job Monitoring
Portal creation is asynchronous (except PRM which is synchronous). Poll through
`mcp__headless-360__dispatch_readonly`.
**Self-service site path** — use the dedicated typed status route (preferred):
```text
method: "GET"
url: "/services/data/v67.0/connect/self-service/site/status/{jobId}"
```
Returns `{success, siteName, urlPathPrefix, siteUrl, error, jobId, status}`.
**Communities path** — query `BackgroundOperation` via the **regular** REST query endpoint,
**not** `/tooling/query`:
```text
method: "GET"
url: "/services/data/v67.0/query"
queryParams: { "q": "SELECT Id, Status FROM BackgroundOperation WHERE Id = '<jobId>'" }
```
> **Tooling vs. regular query (verified gotcha):** `BackgroundOperation` is **not** a valid Tooling
> API sObject through this dispatcher — `GET /services/data/vXX.0/tooling/query` with that SOQL
> returns `400 INVALID_TYPE "sObject type 'BackgroundOperation' is not supported."`. Use the plain
> `/services/data/vXX.0/query` endpoint instead; it succeeds with the same SOQL string.
> **Column discipline:** on `BackgroundOperation`, select only `Id` and `Status`. `JobType`,
> `CompletedDate`, and `NumErrors` are **not** columns on this object and return `INVALID_FIELD`.
> Use the SOQL string above exactly.
**Job statuses:**
- `Queued` / `Scheduled` — waiting to start
- `InProgress` / `Running` — executing
- `Complete` — finished successfully
- `Error` — failed (check the `error` field on the status route)
---
## Verification
After creation completes:
1. **API (primary):** `mcp__headless-360__dispatch_readonly(url: "/services/data/v67.0/connect/communities", method: "GET")` lists all Experience Cloud sites. Find the new one and confirm `siteAsContainerEnabled: true` (Experience Builder — Aura/LWR) and a non-null `builderUrl`, and that `templateName` is **not** `Salesforce Tabs + Visualforce`. `siteAsContainerEnabled: false` means a legacy Visualforce site — the bug this skill exists to avoid.
2. **Setup UI (optional):** Setup → Digital Experiences → All Sites. The **Framework** column should show **Aura** (or LWR) — not Visualforce — with a **Builder** workspace link.
3. **Test URL:** Use `siteUrl` from the response (portal will be inactive initially).
**Note:** Portal must be activated and published before external users can access it.
---
## Rules / Constraints
| Constraint | Rationale |
|---|---|
| Portal creation is asynchronous | Deploys metadata and provisions resources in background |
| Site names must be unique | Each portal needs distinct name within org |
| URL prefixes must be unique | URL paths cannot conflict |
| URL prefixes must be alphanumeric | No hyphens, spaces, or special characters allowed |
| PRM requires PRM feature | Gated by licensing and org config |
| Created portals start inactive | Must manually activate/publish after creation |
| Paths must include the API version prefix | `dispatch`/`dispatch_readonly` do not inject `/services/data/vXX.0` — omitting it returns `400 ROUTE_NOT_FOUND` |
---
## Prerequisites by Type
### Employee Service / Self-Service:
- `CustomizeApplication` permission
- Communities/Digital Experience enabled
- Org has self-service site-creation API access enabled
- A **guest** Embedded Service Deployment (MIAW) config exists (its ID is required); optionally an authenticated-user ESD config
### Partner (PRM):
- Org has `CommonPrmEnabled`
- Portal creation permissions
- Valid PRM template name
### General Community:
- Manage Communities permission (`ManageNetworks`)
- Communities/Digital Experience enabled
- Valid template name
---
## Common Errors
### Invalid URL prefix:
"The URL can only contain alphanumeric characters. Remove hyphens, spaces, or special characters (e.g., 'employeeservice' not 'employee-service') and try a different prefix."
### Invalid template name (general community path):
"The specified template does not exist.
**Resolution:**
- Run `GET /services/data/v67.0/connect/communities/templates` and use a returned `templateName` verbatim
- Prefer an Experience Builder template: `Agentforce Employee Center` (employee service), `Employee Portal`, `Customer Service`, `Help Center`, `Customer Account Portal`, `Partner Central`, `Build Your Own`, `Build Your Own (LWR)`
- Template names are case-sensitive — match exactly
- Do NOT use `Salesforce Tabs + Visualforce` ("VF Template") — it is a legacy Visualforce site with no Builder"
### Missing guest Embedded Service config (self-service path):
"The self-service site API requires a guest Embedded Service Deployment config ID.
**Resolution:**
- Create an Embedded Service Deployment (MIAW) at Setup → Embedded Service Deployments, or use the MIAW/embedded-service setup skill
- Pass its config ID as `guestEmbeddedServiceConfigId` (and optionally `embeddedServiceConfigId` for authenticated users)"
### PRM not enabled:
"This org doesn't have Partner Relationship Management (PRM) enabled.
**Options:**
1. Contact Salesforce to enable PRM feature
2. Create a general community with the `Partner Central` template instead (via Communities API)"
### Duplicate name/URL:
"A portal with this name or URL prefix already exists (the communities API returns `400 INVALID_INPUT` — `Enter a different name. That one already exists.`).
**Check existing portals:** `mcp__headless-360__dispatch_readonly(url: "/services/data/v67.0/connect/communities", method: "GET")` and scan the `name` / `urlPathPrefix` fields.
Choose a different name or URL prefix."
### Missing permissions:
"You don't have permission to create portals.
**Required permissions:**
- Self-service portals: `CustomizeApplication`
- General communities: `ManageNetworks` (Manage Communities)
Contact your Salesforce admin to request these permissions."
### Route not found (missing version prefix):
"`400 ROUTE_NOT_FOUND` on a path that otherwise matches this skill's documentation. Confirm the `url` includes the full `/services/data/vXX.0/...` prefix — `dispatch`/`dispatch_readonly` require it verbatim and won't add it for you. If a specific version 404s, try the version shown in a recent `discover`/`describe` result for that org."
---
## Related Operations
**After creation:**
- Activate portal — Setup → Digital Experiences → All Sites → Activate
- Configure branding — Customize colors, logo, theme
- Add pages/components — Use Experience Builder
- Set up user access — Profiles, permission sets, sharing rules
- Publish portal — Make accessible to external users
**Manage existing:**
- List portals — `mcp__headless-360__dispatch_readonly(url: "/services/data/v67.0/connect/communities", method: "GET")`
- Update settings — Network Tooling API or Metadata API
- Deactivate — Via Setup UI
---
## Important Notes
1. **Creation is asynchronous** (except PRM) and only provisions — APIs return a job ID and the portal comes back `UnderConstruction`, member-less, and unpublished, so its URL is **not reachable** until you complete Step 3 (Activate → Add Members → Publish; see `references/post-creation-activate-publish.md`).
2. **URL prefix becomes the site path** — `https://<domain>.my.site.com/<prefix>` (serve/login at `.../<prefix>/login`, not the bare prefix).
3. **Embedded Service / MIAW configs must pre-exist** — This skill does not create Embedded Service Deployments. Create them separately at Setup → Embedded Service Deployments (or via the MIAW/embedded-service setup skill), then pass the config IDs to the self-service site API. For general communities, MIAW is added post-creation via an Embedded Service component in Experience Builder.
4. **Template names are case-sensitive** — Verify available templates in your org before attempting creation.
7. **discover/describe may not resolve a specific Connect API operation by id** — the headless-360 corpus indexes many operations as multi-step SORs rather than single endpoints, and some standard Connect API writes (e.g. `POST /connect/communities`) are not individually indexed. Do not conclude the capability is missing; dispatch the well-known, versioned Connect API path directly (documented in this skill).
---
## Reference Documentation
- `references/mcp-invocation.md` — **Read every session.** Exact `mcp__headless-360__*` call shapes, the version-prefix requirement, response envelope, job monitoring, `BackgroundOperation` column/endpoint discipline, and gotchas.
- `references/templates.md` — Available templates, template parameters, selection guide.
- `references/post-creation-activate-publish.md` — Step 3 (activate, add members, publish) to make the site reachable: the Metadata-API + `sf community publish` paths, the 405-on-PATCH gotcha, `NetworkMemberGroup` column discipline, and the login-URL note.
- `assets/report-template.md` — Step 4 `report.md` template to copy (heading, required fields, sentinel).
---
## API Type Classification
**Site creation** is dispatched through `mcp__headless-360__dispatch` / `mcp__headless-360__dispatch_readonly` (never project-codey or raw HTTP). All paths include the `/services/data/vXX.0` prefix. **Post-creation (Step 3) is the one exception:** activating the Network and adding members use the **Metadata API** (`sf project deploy start --metadata Network:...`), and publishing uses **`sf community publish`** — there is no Connect API for these (a `PATCH /connect/communities/<id>` returns 405). Reads/verification for Step 3 still go through headless-360. Creation paths:
- **Self-service portal:** Connect API `POST /services/data/vXX.0/connect/self-service/site` — asynchronous (poll `GET /services/data/vXX.0/connect/self-service/site/status/{jobId}`)
- **PRM portal:** Connect API `POST /services/data/vXX.0/connect/prm/setup/sites` — synchronous
- **General community:** Connect API `POST /services/data/vXX.0/connect/communities` — asynchronous (poll `BackgroundOperation` via `GET /services/data/vXX.0/query`, not `/tooling/query`)