Install
$ agentstack add skill-ilia-inovaflow-s4hana-create-record-skills-s4hana-create-product ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo 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 Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ 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.
About
s4hana-create-product
Create product master (material) records on SAP S/4HANA Cloud Public. Verified production-ready against SAP S/4HANA Cloud Public Edition 2026-05-13 (10 products created via API: HAWA, FERT, SERV mix on my438741).
When to trigger
Verbs: create / post / add / generate / seed / onboard Objects: product(s), material(s), SKU(s), HAWA / FERT / SERV / ROH / HALB record(s) Counts: 1 to ~200 records.
Hard rules (never violate)
- Use OData V4, not the SOAP
productmdmbulkreplicaterequestservice. SOAP returns HTTP 202 but silently drops on Cloud Public tenants without MDG / sender-business-system registration. V4 is the supported create path. - Always GET an existing product first to discover the tenant's internal unit codes (e.g.
STfor Stück,LEfor Leistungseinheit). V2 ISO-translates these (ST→PC); V4 does not. Mismatched UoM →MG/142 "value not allowed for MARM-MEINH". - Deep-insert child must repeat parent's Product key in each
_ProductDescriptionentry. SAP's SADL framework rejects inheritance — without explicitProduct:in the child you getCMD_PROD_RAP/003 "Property PRODUCT is a key and cannot be initial". - One-record probe before bulk (≥3 records).
- Never invent unit codes or product groups — verify against existing tenant data via GET first.
- Scripts go in
/.s4hana-tmp/create-products-/. Never commit, never modify.env. - POSTs are sequential with 200ms delay; halt on 3 consecutive failures.
Endpoint
- Service path:
/sap/opu/odata4/sap/api_product/srvd_a2x/sap/product/0002 - Entity sets:
Product,ProductDescription,ProductPlant,ProductSales, plus 20+ extension entities - Method: POST (deep-insert with
_ProductDescriptioncollection) - Communication scenario:
SAP_COM_0009— "Product Integration"
Phase 0 — Setup check (MANDATORY, no exceptions)
Before any API call, before any tool use, do this check. Even if you already know credentials from earlier in the conversation — IGNORE that knowledge and re-check from scratch each invocation.
- Announce: tell the user "Checking for SAP credentials in ``..."
- Check ONLY these two sources:
- A
.envfile at./.env(in the current working directory — NOT parent dirs, NOT~/.env, NOT any memory file) - Shell-exported env vars:
SAP_HOSTANDSAP_AUTH_MODEboth present
- If neither: auto-create
./.envfrom the bundled template (curl fromhttps://raw.githubusercontent.com/ilia-inovaflow/s4hana-create-record-skills/main/.env.example), append.envto.gitignoreif not already there, tell the user clearly what to fill in (auth mode + host + creds), and wait for them to say "ready" before making any API call. Never overwrite an existing.env. - If credentials are present: report back to the user: "✓ Loaded credentials for `
in` mode. Proceeding with [task]."
Never use credentials from conversation memory, from another project's .env, or from any tenant the user previously worked with in a different session. Each project gets its own .env. See [shared/setup-check.md](../../shared/setup-check.md) for the full rationale and edge cases.
Phase 1 — Parse user intent
Determine: count, ProductType mix (HAWA goods / FERT finished / SERV services / ROH raw / HALB semi-finished), whether external IDs should be supplied or auto-generated (the V4 service requires explicit Product field — there is no auto-numbering via API).
Ask one consolidated question if ambiguous:
- How many products and what ProductType mix?
- Specific IDs / naming convention, or auto-generated (default: `
likeH123456`)? - Specific Base Unit pool, or pull from existing products of the same ProductType?
- Specific Product Group, or auto-pick from tenant defaults?
Phase 2 — Discovery (cached per-tenant)
Always do this before the first POST in any session, even if the user supplied all fields explicitly — needed to confirm internal codes.
GET /sap/opu/odata4/sap/api_product/srvd_a2x/sap/product/0002/Product
?$top=10
&$select=Product,ProductType,BaseUnit,ProductGroup,IndustrySector
&sap-client=
From the result, build per-ProductType pools:
- BaseUnit pool — e.g. HAWA →
[ST, ...], SERV →[LE, ...]. V4 returns internal codes (NOTPC/EA). - ProductGroup pool — common values per ProductType.
- IndustrySector — usually a single value tenant-wide (typically
Mfor demo tenants).
Cache for the session. If empty (fresh tenant), use these conservative defaults: HAWA/FERT/ROH/HALB → BaseUnit ST, ProductGroup A001, IndustrySector M. SERV → BaseUnit LE, ProductGroup A001, IndustrySector M.
Phase 3 — Build payload
Per record:
{
"Product": "",
"ProductType": "",
"BaseUnit": "",
"ProductGroup": "",
"IndustrySector": "",
"_ProductDescription": [
{ "Product": "", "Language": "EN", "ProductDescription": "" }
]
}
ID generation: ` (e.g. H for HAWA, F for FERT, S` for SERV) keeps batches scannable.
Phase 4 — CSRF + POST
GET /?sap-client=withX-CSRF-Token: Fetch— capture token + cookies.POST /Product?sap-client=with the deep-insert payload above. Headers:Authorization,Content-Type: application/json,Accept: application/json,X-CSRF-Token:,Cookie:.- Reuse token across batch; refresh on
403 CSRF token validation failed.
Expected success: HTTP 201 with full product entity in response body (including auto-populated CreationDate, CreatedByUser, BaseISOUnit, etc.).
Phase 5 — Verify
After each POST: extract Product from the 201 response body and append {ok, productID, productType, ts, error?} to create-log.jsonl. After bulk completes, do a spot-check with $expand=_ProductDescription:
GET /Product('')?$expand=_ProductDescription&sap-client=
Confirm IsMarkedForDeletion=false and the description was committed.
Known error catalog
| Code | Cause | Fix | |---|---|---| | CMD_PROD_RAP/003 "Property PRODUCT is a key and cannot be initial" | Deep-insert child missing parent key | Add "Product": "" to each _ProductDescription entry | | MG/142 "Value X not allowed for MARM-MEINH" | Wrong unit code (used V2/ISO PC instead of V4 internal ST) | GET existing product to discover the tenant's actual code | | PMD_MSG/032 "Description of product X not maintained" | Tried to POST Product without _ProductDescription | Description is mandatory at create — include it in deep-insert | | API_PRD_MSG/009 "Create operation not allowed on entity" | Using OData V2 API_PRODUCT_SRV instead of V4 | Switch to V4 path — V2 is read-only for Product header | | M3/098 "Enter a material type" | Missing ProductType | Required field, supply HAWA/FERT/etc. | | (none) "Enter an industry sector" | Missing IndustrySector | Required even though Fiori's manual dialog doesn't ask for it | | HTTP 202 + silent drop (SOAP path) | Used productmdmbulkreplicaterequest SOAP service | Switch to V4 — SOAP needs sender business system registration |
Output structure
/.s4hana-tmp/create-products-/
├── tenant-discovery.json # per-ProductType pools (BaseUnit, ProductGroup, IndustrySector)
├── payloads.json
├── create-log.jsonl
├── results.json
└── verify-sample.json
Cross-tenant migration notes
- Source product IDs (MATNR) can be preserved on the target if they're ≤18 chars and the tenant allows external numbering. No internal numbering option exists via API.
- Unit codes do NOT carry across tenants — translate to the target tenant's internal codes via the Phase 2 discovery.
- Plant / sales / valuation / procurement views are SEPARATE entities (
ProductPlant,ProductSales, etc.) on the same V4 service. The header create handled by this skill is sufficient for basic-data lookup but extending to a plant requires a second POST toProductPlant.
Reference files
references/v4-vs-soap.md— why OData V4 and not the SOAP MDM Replicate service (decision matrix + the silent-drop trap)references/field-reference.md— complete Product entity field list with required vs optional, max lengths, deep-insert nav propertiesscripts/bulk-product-poster.mjs— reference implementation
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: ilia-inovaflow
- Source: ilia-inovaflow/s4hana-create-record-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.