AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified Apache-2.0 Self-run

B2c Custom Job Steps

skill-salesforcecommercecloud-b2c-developer-tooling-b2c-custom-job-steps · by SalesforceCommerceCloud

Create custom job steps for B2C Commerce batch processing. Use this skill whenever the user needs to write a batch job, data export script, scheduled cleanup task, or any server-side processing that runs on a schedule. Also use when they ask about steptypes.json, chunk-oriented vs task-oriented job steps, read/process/write patterns, how to get a custom job to appear in Business Manager, or how t…

No reviews yet
0 installs
16 views
0.0% view→install

Install

$ agentstack add skill-salesforcecommercecloud-b2c-developer-tooling-b2c-custom-job-steps

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-salesforcecommercecloud-b2c-developer-tooling-b2c-custom-job-steps)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
1mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of B2c Custom Job Steps? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Custom Job Steps Skill

This skill guides you through creating new custom job steps for Salesforce B2C Commerce batch processing.

> Running an existing job? If you need to execute jobs or import site archives via CLI, use the b2c-cli:b2c-job skill instead.

When to Use

  • Creating a new scheduled job for batch processing
  • Building a data import job (customers, products, orders)
  • Building a data export job (reports, feeds, sync)
  • Implementing data sync between systems
  • Creating cleanup or maintenance tasks

Overview

Custom job steps allow you to execute custom business logic as part of B2C Commerce jobs. There are two execution models:

| Model | Use Case | Progress Tracking | |-------|----------|-------------------| | Task-oriented | Single operations (FTP, import/export) | Limited | | Chunk-oriented | Bulk data processing | Fine-grained |

File Structure

my_cartridge/
├── cartridge/
│   ├── scripts/
│   │   └── steps/
│   │       ├── myTaskStep.js       # Task-oriented script
│   │       └── myChunkStep.js      # Chunk-oriented script
│   └── my_cartridge.properties
└── steptypes.json                  # Step type definitions (at cartridge ROOT)

Important: The steptypes.json file must be placed in the root folder of the cartridge, not inside the cartridge/ directory. Only one steptypes.json file per cartridge.

Step Type Definition (steptypes.json)

{
    "step-types": {
        "script-module-step": [
            {
                "@type-id": "custom.MyTaskStep",
                "@supports-parallel-execution": "false",
                "@supports-site-context": "true",
                "@supports-organization-context": "false",
                "description": "My custom task step",
                "module": "my_cartridge/cartridge/scripts/steps/myTaskStep.js",
                "function": "execute",
                "timeout-in-seconds": 900,
                "parameters": {
                    "parameter": [
                        {
                            "@name": "InputFile",
                            "@type": "string",
                            "@required": "true",
                            "description": "Path to input file"
                        },
                        {
                            "@name": "Enabled",
                            "@type": "boolean",
                            "@required": "false",
                            "default-value": "true",
                            "description": "Enable processing"
                        }
                    ]
                },
                "status-codes": {
                    "status": [
                        {
                            "@code": "OK",
                            "description": "Step completed successfully"
                        },
                        {
                            "@code": "ERROR",
                            "description": "Step failed"
                        },
                        {
                            "@code": "NO_DATA",
                            "description": "No data to process"
                        }
                    ]
                }
            }
        ],
        "chunk-script-module-step": [
            {
                "@type-id": "custom.MyChunkStep",
                "@supports-parallel-execution": "true",
                "@supports-site-context": "true",
                "@supports-organization-context": "false",
                "description": "Bulk data processing step",
                "module": "my_cartridge/cartridge/scripts/steps/myChunkStep.js",
                "before-step-function": "beforeStep",
                "read-function": "read",
                "process-function": "process",
                "write-function": "write",
                "after-step-function": "afterStep",
                "total-count-function": "getTotalCount",
                "chunk-size": 100,
                "transactional": "false",
                "timeout-in-seconds": 1800,
                "parameters": {
                    "parameter": [
                        {
                            "@name": "CategoryId",
                            "@type": "string",
                            "@required": "true"
                        }
                    ]
                }
            }
        ]
    }
}

From Step Type to Runnable Job (jobs.xml)

steptypes.json only declares a step type — it does not create a job. To get a job that b2c job run can execute and Business Manager can schedule, author a job definition (jobs.xml) that references your step type, then import it:

b2c job import ./my-job-archive   # jobs.xml at the archive root

A minimal valid definition wires one step into a flow and includes the required `` element:


    
        
            
            
            
                
                    /export/products.csv
                
            
        
        
            
                2025-01-01
                00:00:00.000Z
            
        
    

Key rules (full details in the [jobs.xml Reference](references/JOBS-XML.md)):

  • **` is required** by the schema — a jobs.xml without it fails import validation. Use for an on-demand/manually-run job, or ` to schedule it.
  • The ` **type** attribute references the step type; its value must match the **@type-id** you declared in steptypes.json (don't confuse the two — steptypes.json uses @type-id, jobs.xml uses type`).
  • ` children must appear in order: description → parameters → flow/split → rules → triggers`.
  • The cartridge carrying the step's steptypes.json + module must be deployed and on the cartridge path before the job can resolve the step type.

After import, run it with b2c job run MyNightlyExport --wait (see the b2c-cli:b2c-job skill).

Task-Oriented Steps

Use for single operations like FTP transfers, file generation, or import/export.

Script (scripts/steps/myTaskStep.js)

'use strict';

var Status = require('dw/system/Status');
var Logger = require('dw/system/Logger');

/**
 * Execute the task step
 * @param {Object} parameters - Job step parameters
 * @param {dw.job.JobStepExecution} stepExecution - Step execution context
 * @returns {dw.system.Status} Execution status
 */
exports.execute = function (parameters, stepExecution) {
    var log = Logger.getLogger('job', 'MyTaskStep');

    try {
        var inputFile = parameters.InputFile;
        var enabled = parameters.Enabled;

        if (!enabled) {
            log.info('Step disabled, skipping');
            return new Status(Status.OK, 'SKIP', 'Step disabled');
        }

        // Your business logic here
        log.info('Processing file: ' + inputFile);

        // Return success
        return new Status(Status.OK);

    } catch (e) {
        log.error('Step failed: ' + e.message);
        return new Status(Status.ERROR, 'ERROR', e.message);
    }
};

Status Codes

// Success
return new Status(Status.OK);
return new Status(Status.OK, 'CUSTOM_CODE', 'Custom message');

// Error
return new Status(Status.ERROR);
return new Status(Status.ERROR, null, 'Error message');

Important: Custom status codes work only with OK status. If you use a custom code with ERROR status, it is replaced with ERROR. Custom status codes cannot contain commas, wildcards, leading/trailing whitespace, or exceed 100 characters.

Chunk-Oriented Steps

Use for bulk processing of countable data (products, orders, customers).

Important: You cannot define custom exit status for chunk-oriented steps. Chunk modules always finish with either OK or ERROR.

Required Functions

| Function | Purpose | Returns | |----------|---------|---------| | read() | Get next item | Item or nothing | | process(item) | Transform item | Processed item or nothing (filters) | | write(items) | Save chunk of items | Nothing |

Optional Functions

| Function | Purpose | Returns | |----------|---------|---------| | beforeStep() | Initialize (open files, queries) | Nothing | | afterStep(success) | Cleanup (close files) | Nothing | | getTotalCount() | Return total items for progress | Number | | beforeChunk() | Before each chunk | Nothing | | afterChunk() | After each chunk | Nothing |

Script (scripts/steps/myChunkStep.js)

'use strict';

var ProductMgr = require('dw/catalog/ProductMgr');
var Transaction = require('dw/system/Transaction');
var Logger = require('dw/system/Logger');
var File = require('dw/io/File');
var FileWriter = require('dw/io/FileWriter');

var log = Logger.getLogger('job', 'MyChunkStep');
var products;
var fileWriter;

/**
 * Initialize before processing
 */
exports.beforeStep = function (parameters, stepExecution) {
    log.info('Starting chunk processing');

    // Open resources
    var outputFile = new File(File.IMPEX + '/export/products.csv');
    fileWriter = new FileWriter(outputFile);
    fileWriter.writeLine('ID,Name,Price');

    // Query products
    products = ProductMgr.queryAllSiteProducts();
};

/**
 * Get total count for progress tracking
 */
exports.getTotalCount = function (parameters, stepExecution) {
    return products.count;
};

/**
 * Read next item
 * Return nothing to signal end of data
 */
exports.read = function (parameters, stepExecution) {
    if (products.hasNext()) {
        return products.next();
    }
    // Return nothing = end of data
};

/**
 * Process single item
 * Return nothing to filter out item
 */
exports.process = function (product, parameters, stepExecution) {
    // Filter: skip offline products
    if (!product.online) {
        return;  // Filtered out
    }

    // Transform
    return {
        id: product.ID,
        name: product.name,
        price: product.priceModel.price.value
    };
};

/**
 * Write chunk of processed items
 */
exports.write = function (items, parameters, stepExecution) {
    for (var i = 0; i ` for any step's full parameters and defaults. **Scope** is the execution scope (Organization, Site, or both).

| Type ID | Scope | Key required params |
| --- | --- | --- |
| `ImportCatalog` | Organization | `NoFilesFoundHandling`, `ImportMode`, `ImportFailedHandling` |
| `ImportInventoryLists` | Organization | `NoFilesFoundHandling`, `ImportMode`, `ImportFailedHandling` |
| `ImportPriceBook` | Organization | `NoFilesFoundHandling`, `ImportMode`, `ImportFailedHandling` |
| `ImportContent` | Site | `NoFilesFoundHandling`, `ImportMode`, `ImportFailedHandling` |
| `ImportCustomObjects` | Organization & Sites | `NoFilesFoundHandling`, `ImportFailedHandling` |
| `ExportCatalog` | Organization | `CatalogID` |
| `ExportInventoryLists` | Site | (none) |
| `ExportPriceBook` | Organization | `PriceBookID` |
| `ExportContent` | Organization & Sites | `LibraryID` |
| `ExportOrders` | Site | `Confirmation Status`, `Shipment Status`, `Payment Status` |
| `ExecutePreconfiguredDataReplicationProcess` | Organization | `ReplicationProcessID` |
| `SearchReindex` | Site | `Indexer Type` |
| `ExecuteScriptModule` | Organization & Sites | `ExecuteScriptModule.Module` |

Import steps share a common set of file-handling parameters (`WorkingFolder`, `FileNamePattern`, `ImportMode`, `NoFilesFoundHandling`, `ImportFailedHandling`, `AfterImportFileHandling`, `ArchiveFolder`); export steps share `ExportFile` / `FileNamePrefix` / `OverwriteExportFile`. Processing steps (replication, reindex, cache invalidation, `ExecutePipeline`/`ExecuteScriptModule`/`IncludeStepsFromJob`) have their own parameters. Read any step's doc for the exact list.

### Referencing an IMPEX-staged file from a prior step

Standard import steps read from the instance **IMPEX** area. The `WorkingFolder` parameter is resolved relative to `IMPEX/src/` (and defaults to `IMPEX/src/`); `FileNamePattern` is a regex that selects which file(s) in that folder to import. This is the hand-off contract: **a step that writes a file under `IMPEX/src/...` can be followed by a standard import step that reads it** — no download/upload round-trip.

In a custom step, write to that location with `dw.io.File` using the `IMPEX` constant:

```javascript
var File = require('dw/io/File');

// Custom step writes a catalog import file into IMPEX/src/jobdata/
exports.beforeStep = function () {
    var dir = new File(File.IMPEX + '/src/jobdata');
    dir.mkdirs();
    outputFile = new File(dir, 'catalog-' + Date.now() + '.xml');
    fileWriter = new dw.io.FileWriter(outputFile);
    // ... write valid catalog XML (validate against the `catalog` XSD:
    //     b2c docs schema catalog) ...
};

Then the standard ImportCatalog step in the next stage of the flow reads it by pointing WorkingFolder at src/jobdata (relative to IMPEX/src/ → use jobdata) with a FileNamePattern of catalog-.*\.xml.

Chaining custom + standard steps in one flow

A flow can interleave your custom steps with standard ones. Example: a custom step pulls data from an external system and generates a catalog XML in IMPEX; a standard ImportCatalog step then applies it; finally a standard replication step publishes the change to production.


  
    
    
      
        jobdata
      
    

    
    
      
        jobdata
        catalog-.*\.xml
        Merge
        ERROR
        ERROR
        Archive
      
    

    
    
      
        nightly-catalog-publish
      
    
  

The custom step's OutputFolder and the standard step's WorkingFolder agree on jobdata (i.e. IMPEX/src/jobdata/), so the file written in step 1 is exactly what step 2 imports; step 3 then replicates the result. (In Business Manager you build the same flow visually: add your custom step, then the standard ImportCatalog step after it, then the replication step, setting each step's parameters in its form.)

In-flow standard step vs. the CLI equivalent

Some standard steps overlap with b2c CLI commands (the CLI's b2c job import/b2c job export are themselves the sfcc-site-archive-import/-export system jobs). Choose based on where the data lives:

  • Use an in-flow standard step when the file is produced or already staged on the instance — especially when an earlier step in the same flow generated it (no round-trip), when it should run on a Business Manager schedule, or when it must follow custom processing server-side. Example: the standard ImportCatalog step consuming a catalog XML that a prior custom step wrote to IMPEX.
  • Use the CLI (b2c job import, b2c job export) when you are moving data between your machine and the instance — uploading a local archive, downloading an export, or scripting a one-off from CI.

Rule of thumb: data already on (or generated on) the instance → in-flow standard step; data crossing the machine/instance boundary → CLI. See the b2c-cli:b2c-job skill for the CLI side.

Best Practices

  1. Use chunk-oriented for bulk data - better progress tracking and resumability
  2. Close resources in afterStep() - queries, files, connections
  3. Set explicit timeouts - default may be too short
  4. Log progress - helps debugging
  5. Handle errors gracefully - return proper Status objects
  6. Don't rely on transactional=true - use Transaction.wrap() for control

Related Skills

  • b2c-cli:b2c-job - For running existing jobs and importing site archives via CLI
  • b2c-cli:b2c-docs - To look up standard job step type IDs and their parameters (b2c docs read job-steps, b2c docs read )
  • b2c:b2c-webservices - When job steps need to call external HTTP services or APIs, use the webservices skill for service configuration and HTTP client patterns

Detailed Reference

  • [Task-Oriented Steps](references/TASK-ORIENTED.md) - Full task step patterns
  • [Chunk-Oriented Steps](references/CHUNK-ORIENTED.md) - Full chunk step pattern

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.