Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .changeset/scapi-migration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@salesforce/b2c-cli': minor
'@salesforce/b2c-tooling-sdk': minor
'b2c-vs-extension': minor
'@salesforce/b2c-dx-docs': minor
---

Migrate `job`, `code`, `bm users`, and `bm roles` commands to support SCAPI alongside OCAPI. In auto mode (the default), the CLI prefers SCAPI when `shortCode` and `tenantId` are configured and silently falls back to OCAPI if the SCAPI scopes aren't granted. Use `--api-backend ocapi|scapi|auto` or `apiBackend` in dw.json to control explicitly. SCAPI scopes: `sfcc.jobs(.rw)`, `sfcc.scripts(.rw)`, `sfcc.users(.rw)`, `sfcc.roles(.rw)` — read-only scopes are honored for list/get operations, falling back to read-only when the `*.rw` scope is not granted. New `job execution delete` command (SCAPI only). `code deploy` and all VS Code extension code-version actions (list/activate/delete/reload/create plus active-version discovery) also honor `apiBackend`. `bm users update --disabled` transparently falls back to OCAPI in auto mode (SCAPI Users PATCH does not support the `disabled` flag). Auto mode only selects SCAPI when authentication can request the required scopes — stateless OAuth (client-credentials, JWT bearer); stateful and implicit flows fall back to OCAPI unless `--api-backend scapi` is set explicitly.
30 changes: 29 additions & 1 deletion docs/cli/bm.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,35 @@ description: Commands for administering Business Manager resources on a B2C Comm

# Business Manager Commands

Commands for administering instance-level Business Manager resources via the OCAPI Data API. These are distinct from [Account Manager commands](/cli/account-manager) which manage cross-instance identity.
Commands for administering instance-level Business Manager resources. These are distinct from [Account Manager commands](/cli/account-manager) which manage cross-instance identity.

## API Backend

Most `bm users` and `bm roles` commands support both the OCAPI Data API and the SCAPI Merchant Users / Merchant Roles APIs. By default (`auto` mode), SCAPI is preferred when `shortCode` and `tenantId` are configured. If the SCAPI scopes aren't granted on your API client, the CLI silently falls back to OCAPI.

```bash
# Force SCAPI backend
b2c bm users list --api-backend scapi

# Force OCAPI backend
b2c bm roles get Administrator --api-backend ocapi
```

Or set in `dw.json`: `"api-backend": "scapi"`. Or `SFCC_API_BACKEND=scapi` env var.

| Command | SCAPI | OCAPI |
|---|---|---|
| `bm users list/get/update/delete` | ✓ (`sfcc.users.rw`) | ✓ |
| `bm users search` | ✗ — OCAPI only | ✓ |
| `bm whoami` | ✗ — OCAPI only | ✓ |
| `bm access-key *` | ✗ — OCAPI only | ✓ |
| `bm roles list/get/create/delete` | ✓ (`sfcc.roles.rw`) | ✓ |
| `bm roles grant/revoke` | ✓ (`sfcc.roles.rw`) | ✓ |
| `bm roles permissions get/set` | ✓ (`sfcc.roles.rw`) | ✓ |

::: warning
The SCAPI Users PATCH endpoint does not support changing the `disabled` flag. `bm users update --disabled` falls back to OCAPI in auto mode; with `--api-backend scapi` it errors with a clear message.
:::

## Authentication

Expand Down
31 changes: 27 additions & 4 deletions docs/cli/code.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,37 @@ description: Commands for deploying, downloading, activating code versions, and

Commands for managing cartridge code on B2C Commerce instances.

## API Backend

The `code list`, `code activate`, and `code delete` commands support both OCAPI and SCAPI backends. By default (`auto` mode), SCAPI is preferred when `shortCode` and `tenantId` are configured. If SCAPI scopes are unavailable, the CLI falls back to OCAPI transparently.

```bash
# Force SCAPI
b2c code list --api-backend scapi

# Force OCAPI
b2c code list --api-backend ocapi
```

Or set in `dw.json`: `"api-backend": "scapi"`. Or `SFCC_API_BACKEND=scapi` env var.

::: tip
The `code activate --reload` flag forces an OCAPI call regardless of `--api-backend`, since SCAPI does not expose the cache-rebuild operation.
:::

::: tip
The `code deploy`, `code download`, and `code watch` commands always use WebDAV (no SCAPI equivalent for cartridge file transfer).
:::

## Authentication

Code commands use different authentication depending on the operation:

| Operation | Auth Required |
|-----------|--------------|
| `code deploy`, `code download`, `code watch` | WebDAV (Basic Auth or OAuth) |
| `code list`, `code activate`, `code delete` | OAuth + OCAPI |
| `code list`, `code activate`, `code delete` (SCAPI) | OAuth + `sfcc.scripts` (read) or `sfcc.scripts.rw` (write) + tenant scope |
| `code list`, `code activate`, `code delete` (OCAPI) | OAuth + OCAPI permissions for `/code_versions` |

### WebDAV Operations (deploy, download, watch)

Expand All @@ -24,16 +47,16 @@ export SFCC_USERNAME=your-bm-username
export SFCC_PASSWORD=your-webdav-access-key
```

### OCAPI Operations (list, activate, delete)
### SCAPI / OCAPI Operations (list, activate, delete)

These commands require OAuth authentication with OCAPI permissions for the `/code_versions` resource configured in Business Manager.
These commands require OAuth authentication. For SCAPI, configure the `sfcc.scripts.rw` scope on your API client in Account Manager. For OCAPI, configure permissions for the `/code_versions` resource in Business Manager.

```bash
export SFCC_CLIENT_ID=your-client-id
export SFCC_CLIENT_SECRET=your-client-secret
```

For complete setup instructions including OCAPI configuration, see the [Authentication Guide](/guide/authentication).
For complete setup instructions, see the [Authentication Guide](/guide/authentication).

---

Expand Down
75 changes: 73 additions & 2 deletions docs/cli/jobs.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,51 @@ description: Commands for executing jobs, importing and exporting site archives,

Commands for executing and monitoring jobs on B2C Commerce instances.

## API Backend

Job commands support both OCAPI and SCAPI backends. By default (`auto` mode), SCAPI is preferred when `shortCode` and `tenantId` are configured. If SCAPI scopes are unavailable, the CLI falls back to OCAPI transparently.

Use `--api-backend` to control explicitly:

```bash
# Force SCAPI
b2c job run my-job --api-backend scapi

# Force OCAPI
b2c job run my-job --api-backend ocapi

# Auto-detect (default)
b2c job run my-job --api-backend auto
```

Or set in `dw.json`:

```json
{
"api-backend": "scapi"
}
```

Or via environment variable: `SFCC_API_BACKEND=scapi`.

::: tip
The `job import` and `job export` commands currently use OCAPI only, regardless of the `--api-backend` setting.
:::

## Authentication

Job commands require OAuth authentication with OCAPI permissions.
### SCAPI (recommended)

When using SCAPI, your API client needs the appropriate scopes in Account Manager:

| Scope | Operations |
|-------|------------|
| `sfcc.jobs.rw` | Execute, delete, search, and get job executions (recommended) |
| `sfcc.jobs` | Search and get job executions (read-only) |

### Required OCAPI Permissions
You also need `shortCode` and `tenantId` configured (in `dw.json` or via flags).

### OCAPI

Configure these resources in Business Manager under **Administration** > **Site Development** > **Open Commerce API Settings**:

Expand Down Expand Up @@ -255,6 +295,37 @@ b2c job log my-custom-job > job.log

---

## b2c job execution delete

Delete a job execution record. This command requires the SCAPI backend (`sfcc.jobs.rw` scope).

### Usage

```bash
b2c job execution delete JOBID EXECUTIONID
```

### Arguments

| Argument | Description | Required |
|----------|-------------|----------|
| `JOBID` | Job ID | Yes |
| `EXECUTIONID` | Execution ID to delete | Yes |

### Examples

```bash
# Delete a specific execution
b2c job execution delete my-job abc123-def456
```

### Notes

- Requires SCAPI backend — not available via OCAPI.
- Requires the `sfcc.jobs.rw` scope on your API client.

---

## b2c job import

Import a site archive to a B2C Commerce instance using the `sfcc-site-archive-import` system job.
Expand Down
38 changes: 26 additions & 12 deletions docs/guide/authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ The CLI uses different authentication mechanisms depending on the operation:
| Operation | Auth Method | Setup Required |
| -------------------------------------------------------------------------------------------------- | ---------------------------- | ---------------------------------------------------------------------------------------- |
| [Code](/cli/code) deploy, watch (file upload) | WebDAV (Basic Auth or OAuth) | [WebDAV Access](#webdav-access) |
| [Code](/cli/code) list, activate, delete | OAuth + OCAPI | [API Client](#account-manager-api-client) + [OCAPI](#ocapi-configuration) |
| [Jobs](/cli/jobs), [Sites](/cli/sites) | OAuth + OCAPI | [API Client](#account-manager-api-client) + [OCAPI](#ocapi-configuration) |
| [Code](/cli/code) list, activate, delete | OAuth + OCAPI **or** SCAPI (`sfcc.scripts` / `sfcc.scripts.rw`) | [API Client](#account-manager-api-client) + [OCAPI](#ocapi-configuration) or [SCAPI Scopes](#scapi-authentication) |
| [Jobs](/cli/jobs) | OAuth + OCAPI **or** SCAPI (`sfcc.jobs` / `sfcc.jobs.rw`) | [API Client](#account-manager-api-client) + [OCAPI](#ocapi-configuration) or [SCAPI Scopes](#scapi-authentication) |
| [BM users / roles](/cli/bm) | OAuth + OCAPI **or** SCAPI (`sfcc.users(.rw)` / `sfcc.roles(.rw)`) | [API Client](#account-manager-api-client) + [OCAPI](#ocapi-configuration) or [SCAPI Scopes](#scapi-authentication) |
| [Sites](/cli/sites) | OAuth + OCAPI | [API Client](#account-manager-api-client) + [OCAPI](#ocapi-configuration) |
| SCAPI commands ([schemas](/cli/scapi-schemas), [custom-apis](/cli/custom-apis), [eCDN](/cli/ecdn)) | OAuth + SCAPI scopes | [API Client](#account-manager-api-client) + [SCAPI Scopes](#scapi-authentication) |
| [CIP analytics](/cli/cip) (`cip query`, `cip report`) | OAuth + Client Credentials | [API Client](#account-manager-api-client) + Salesforce Commerce API role + tenant filter |
| [SLAS](/cli/slas) client management | OAuth | None (uses built-in client) or [API Client](#account-manager-api-client) |
Expand Down Expand Up @@ -478,15 +480,25 @@ SCAPI commands (eCDN, SCAPI schemas, custom APIs) require OAuth authentication w

### Scopes by Command

| Command | Required Scope | Reference |
| ----------------------------- | -------------------- | ----------------------------------- |
| `b2c scapi schemas list/get` | `sfcc.scapi-schemas` | [SCAPI Schemas](/cli/scapi-schemas) |
| `b2c scapi custom status` | `sfcc.custom-apis` | [Custom APIs](/cli/custom-apis) |
| `b2c ecdn` (read operations) | `sfcc.cdn-zones` | [eCDN](/cli/ecdn) |
| `b2c ecdn` (write operations) | `sfcc.cdn-zones.rw` | [eCDN](/cli/ecdn) |
| Command | Required Scope | Reference |
| ------------------------------------------------------ | ------------------------------------ | ----------------------------------- |
| `b2c scapi schemas list/get` | `sfcc.scapi-schemas` | [SCAPI Schemas](/cli/scapi-schemas) |
| `b2c scapi custom status` | `sfcc.custom-apis` | [Custom APIs](/cli/custom-apis) |
| `b2c ecdn` (read operations) | `sfcc.cdn-zones` | [eCDN](/cli/ecdn) |
| `b2c ecdn` (write operations) | `sfcc.cdn-zones.rw` | [eCDN](/cli/ecdn) |
| `b2c jobs` (read; e.g. `list`, `get`, `wait`) | `sfcc.jobs` or `sfcc.jobs.rw` | [Jobs](/cli/jobs) |
| `b2c jobs` (write; e.g. `run`, `delete`) | `sfcc.jobs.rw` | [Jobs](/cli/jobs) |
| `b2c code list` | `sfcc.scripts` or `sfcc.scripts.rw` | [Code](/cli/code) |
| `b2c code activate`, `code delete` | `sfcc.scripts.rw` | [Code](/cli/code) |
| `b2c bm users list/get` | `sfcc.users` or `sfcc.users.rw` | [BM](/cli/bm) |
| `b2c bm users create/update/delete` | `sfcc.users.rw` | [BM](/cli/bm) |
| `b2c bm roles list/get` | `sfcc.roles` or `sfcc.roles.rw` | [BM](/cli/bm) |
| `b2c bm roles create/delete/grant/revoke/permissions` | `sfcc.roles.rw` | [BM](/cli/bm) |

The CLI automatically requests these scopes. Your API client must have them in the Default Scopes list.

For commands that have both an OCAPI and a SCAPI implementation (`code`, `jobs`, `bm users`, `bm roles`), the CLI defaults to `--api-backend auto`: it tries SCAPI when shortCode + tenantId are configured and the API client has the required `sfcc.*` scope, otherwise it falls back to OCAPI. Use `--api-backend ocapi` or `--api-backend scapi` to force a backend explicitly.

::: tip
For detailed authentication requirements including specific scopes for each command, see the individual [CLI command reference pages](/cli/).
:::
Expand Down Expand Up @@ -594,11 +606,12 @@ Here's a complete example for setting up CLI access:
- `Salesforce Commerce API` - add tenant filter with your tenant IDs
- `Sandbox API User` - if using ODS (add tenant filter)
- **Default Scopes**: `mail roles tenantFilter openid sfcc.cdn-zones`
- For SCAPI-backed dual commands, also add the relevant `sfcc.jobs(.rw)`, `sfcc.scripts(.rw)`, `sfcc.users(.rw)`, `sfcc.roles(.rw)` scopes — see [Scopes by Command](#scopes-by-command).
- **Redirect URLs**: `http://localhost:8080` (for user authentication)

### 2. Configure OCAPI (for code list/activate/delete, jobs, sites)
### 2. Configure OCAPI (for `sites` and as the auto-mode fallback for code/jobs/bm)

Add the JSON configuration shown in [OCAPI Configuration](#ocapi-configuration) to enable code version and job APIs.
Add the JSON configuration shown in [OCAPI Configuration](#ocapi-configuration). With the SCAPI scopes above also configured on your client, `code list/activate/delete`, `code deploy --activate/--reload`, `jobs`, and `bm users/roles` will prefer SCAPI in `auto` mode and fall back to OCAPI if a scope is missing.

### 3. Configure WebDAV Access (for code deploy/watch, webdav commands)

Expand All @@ -614,10 +627,11 @@ Either:
export SFCC_CLIENT_ID=your-client-id
export SFCC_CLIENT_SECRET=your-client-secret

# Instance (for OCAPI commands)
# Instance hostname (used by WebDAV and OCAPI)
export SFCC_SERVER=your-instance.demandware.net

# SCAPI (for eCDN, schemas, custom-apis)
# SCAPI — required for SCAPI-only commands (eCDN, schemas, custom-apis) and
# enables `auto` mode to prefer SCAPI for code/jobs/bm commands.
export SFCC_TENANT_ID=zzxy_prd
export SFCC_SHORTCODE=kv7kzm78

Expand Down
1 change: 1 addition & 0 deletions docs/guide/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ For the full command reference with all flags, see [Setup Commands](/cli/setup).
| `certificate` | Path to PKCS12 certificate for two-factor auth (mTLS) |
| `certificate-passphrase` | Passphrase for the certificate. Also accepts `passphrase`. |
| `self-signed` | Allow self-signed server certificates. Also accepts `selfsigned`. |
| `api-backend` | API backend for `job`, `code`, `bm users`, and `bm roles` commands: `ocapi`, `scapi`, or `auto` (default). Auto prefers SCAPI when `shortCode` and `tenant-id` are set, falling back to OCAPI on missing scopes. |

### Two-Factor Authentication (mTLS)

Expand Down
13 changes: 8 additions & 5 deletions packages/b2c-cli/src/commands/bm/roles/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
* For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0
*/
import {Args, Flags} from '@oclif/core';
import {InstanceCommand} from '@salesforce/b2c-tooling-sdk/cli';
import {createBmRole, type BmRole} from '@salesforce/b2c-tooling-sdk/operations/bm-roles';
import {BmCommand} from '@salesforce/b2c-tooling-sdk/cli';
import {type RoleInfo} from '@salesforce/b2c-tooling-sdk/operations/bm-roles';
import {t} from '../../../i18n/index.js';

export default class BmRolesCreate extends InstanceCommand<typeof BmRolesCreate> {
export default class BmRolesCreate extends BmCommand<typeof BmRolesCreate> {
static args = {
role: Args.string({
description: 'Role ID to create',
Expand All @@ -33,16 +33,19 @@ export default class BmRolesCreate extends InstanceCommand<typeof BmRolesCreate>
}),
};

async run(): Promise<BmRole> {
async run(): Promise<RoleInfo> {
this.requireOAuthCredentials();

const {role: roleId} = this.args;
const {description} = this.flags;
const hostname = this.resolvedConfig.values.hostname!;

const backend = this.createRolesBackend();
this.logger.debug(`Using ${backend.name} backend for roles create`);

this.log(t('commands.bm.roles.create.creating', 'Creating role {{roleId}} on {{hostname}}...', {roleId, hostname}));

const role = await createBmRole(this.instance, roleId, {description});
const role = await backend.createRole(roleId, {description});

if (this.jsonEnabled()) {
return role;
Expand Down
10 changes: 6 additions & 4 deletions packages/b2c-cli/src/commands/bm/roles/delete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
* For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0
*/
import {Args} from '@oclif/core';
import {InstanceCommand} from '@salesforce/b2c-tooling-sdk/cli';
import {deleteBmRole} from '@salesforce/b2c-tooling-sdk/operations/bm-roles';
import {BmCommand} from '@salesforce/b2c-tooling-sdk/cli';
import {t} from '../../../i18n/index.js';

interface DeleteResult {
Expand All @@ -14,7 +13,7 @@ interface DeleteResult {
hostname: string;
}

export default class BmRolesDelete extends InstanceCommand<typeof BmRolesDelete> {
export default class BmRolesDelete extends BmCommand<typeof BmRolesDelete> {
static args = {
role: Args.string({
description: 'Role ID to delete',
Expand All @@ -37,11 +36,14 @@ export default class BmRolesDelete extends InstanceCommand<typeof BmRolesDelete>
const {role: roleId} = this.args;
const hostname = this.resolvedConfig.values.hostname!;

const backend = this.createRolesBackend();
this.logger.debug(`Using ${backend.name} backend for roles delete`);

this.log(
t('commands.bm.roles.delete.deleting', 'Deleting role {{roleId}} from {{hostname}}...', {roleId, hostname}),
);

await deleteBmRole(this.instance, roleId);
await backend.deleteRole(roleId);

const result = {success: true, role: roleId, hostname};

Expand Down
Loading
Loading