Skills sap

install
source · Clone the upstream repo
git clone https://github.com/TerminalSkills/skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/TerminalSkills/skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/sap" ~/.claude/skills/terminalskills-skills-sap && rm -rf "$T"
manifest: skills/sap/SKILL.md
safety · automated scan (medium risk)
This is a pattern-based risk scan, not a security review. Our crawler flagged:
  • global npm install
  • references .env files
Always read a skill's source content before installing. Patterns alone don't mean the skill is malicious — but they warrant attention.
source content

SAP

Overview

Integrate with SAP — the enterprise ERP backbone. This skill covers SAP S/4HANA Cloud and on-premise APIs (OData V2/V4, BAPI/RFC), SAP Business One (Service Layer), SAP BTP application development, SAP CAP (Cloud Application Programming model), SAP Cloud SDK for JavaScript/TypeScript, master data management, procurement and sales document automation, and integration patterns for connecting SAP with external systems.

Instructions

Step 1: Authentication & Connectivity

S/4HANA Cloud — OAuth2 Client Credentials:

export SAP_TOKEN_URL="https://your-tenant.authentication.eu10.hana.ondemand.com/oauth/token"
export SAP_CLIENT_ID="client-id"
export SAP_CLIENT_SECRET="client-secret"
export SAP_BASE_URL="https://my-s4hana.com/sap/opu/odata/sap"
async function getSAPToken() {
  const res = await fetch(process.env.SAP_TOKEN_URL!, {
    method: "POST",
    headers: {
      "Content-Type": "application/x-www-form-urlencoded",
      Authorization: "Basic " + Buffer.from(`${SAP_CLIENT_ID}:${SAP_CLIENT_SECRET}`).toString("base64"),
    },
    body: "grant_type=client_credentials",
  });
  return (await res.json()).access_token;
}

async function sapApi(method: string, path: string, token: string, body?: any) {
  const res = await fetch(`${process.env.SAP_BASE_URL}${path}`, {
    method,
    headers: {
      Authorization: `Bearer ${token}`, "Content-Type": "application/json", Accept: "application/json",
      ...(method !== "GET" ? { "X-CSRF-Token": await getCSRFToken(token) } : {}),
    },
    body: body ? JSON.stringify(body) : undefined,
  });
  if (!res.ok) throw new Error(`SAP ${res.status}: ${await res.text()}`);
  return res.json();
}

SAP Business One — Service Layer:

const B1_URL = "https://your-b1-server:50000/b1s/v1";

async function b1Login() {
  const res = await fetch(`${B1_URL}/Login`, {
    method: "POST", headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ CompanyDB: "SBODemoUS", UserName: "manager", Password: "password" }),
  });
  return res.headers.get("set-cookie"); // Session cookie
}

async function b1Api(method: string, path: string, session: string, body?: any) {
  const res = await fetch(`${B1_URL}${path}`, {
    method, headers: { "Content-Type": "application/json", Cookie: session },
    body: body ? JSON.stringify(body) : undefined,
  });
  return res.json();
}

Step 2: S/4HANA OData APIs

Business Partners:

const customers = await sapApi("GET",
  "/API_BUSINESS_PARTNER/A_BusinessPartner?$filter=BusinessPartnerCategory eq '1'&$select=BusinessPartner,BusinessPartnerFullName,Industry&$top=50", token);

await sapApi("POST", "/API_BUSINESS_PARTNER/A_BusinessPartner", token, {
  BusinessPartnerCategory: "1", BusinessPartnerFullName: "TechStart GmbH",
  SearchTerm1: "TECHSTART", Industry: "IT",
  to_BusinessPartnerAddress: [{ Country: "DE", CityName: "Berlin", StreetName: "Friedrichstraße", HouseNumber: "123", PostalCode: "10117" }],
});

Sales Orders:

await sapApi("POST", "/API_SALES_ORDER_SRV/A_SalesOrder", token, {
  SalesOrderType: "OR", SalesOrganization: "1010", DistributionChannel: "10",
  SoldToParty: "CUSTOMER_BP_ID", PurchaseOrderByCustomer: "PO-2026-001",
  to_Item: [
    { Material: "TG11", RequestedQuantity: "10", RequestedQuantityUnit: "EA", NetPriceAmount: "100.00", NetPriceCurrency: "EUR" },
    { Material: "TG12", RequestedQuantity: "5", RequestedQuantityUnit: "EA", NetPriceAmount: "250.00", NetPriceCurrency: "EUR" },
  ],
});

Purchase Orders & Material Master:

await sapApi("POST", "/API_PURCHASEORDER_PROCESS_SRV/A_PurchaseOrder", token, {
  PurchaseOrderType: "NB", CompanyCode: "1010", Supplier: "VENDOR_BP_ID",
  to_PurchaseOrderItem: [{ Material: "RAW-001", OrderQuantity: "1000", PurchaseOrderQuantityUnit: "KG",
    NetPriceAmount: "5.50", DocumentCurrency: "EUR", Plant: "1010" }],
});

const material = await sapApi("GET", "/API_PRODUCT_SRV/A_Product('TG11')?$expand=to_Description,to_Plant", token);

Step 3: SAP Business One Operations

// Sales order → invoice → payment flow
const order = await b1Api("POST", "/Orders", session, {
  CardCode: "C20000", DocDate: "2026-02-18",
  DocumentLines: [
    { ItemCode: "A00001", Quantity: 10, UnitPrice: 100 },
    { ItemCode: "A00002", Quantity: 5, UnitPrice: 250 },
  ],
});

const invoice = await b1Api("POST", "/Invoices", session, {
  CardCode: "C20000",
  DocumentLines: [
    { BaseType: 17, BaseEntry: order.DocEntry, BaseLine: 0 },
    { BaseType: 17, BaseEntry: order.DocEntry, BaseLine: 1 },
  ],
});

await b1Api("POST", "/IncomingPayments", session, {
  CardCode: "C20000", DocDate: "2026-02-18", CashSum: 1750,
  PaymentInvoices: [{ DocEntry: invoice.DocEntry, SumApplied: 1750, InvoiceType: "it_Invoice" }],
});

Step 4: SAP CAP (Cloud Application Programming)

npm install -g @sap/cds-dk
cds init my-project && cd my-project && npm install

Data model (

db/schema.cds
):

namespace my.project;
entity Products { key ID: UUID; name: String(100); price: Decimal(10,2); stock: Integer; category: Association to Categories; }
entity Categories { key ID: UUID; name: String(50); products: Association to many Products on products.category = $self; }
entity Orders { key ID: UUID; orderDate: Date; customer: String(100); status: String(20) default 'New';
  items: Composition of many OrderItems on items.order = $self; totalAmount: Decimal(12,2); }
entity OrderItems { key ID: UUID; order: Association to Orders; product: Association to Products; quantity: Integer; unitPrice: Decimal(10,2); }

Service logic (

srv/catalog-service.js
):

const cds = require("@sap/cds");
module.exports = class CatalogService extends cds.ApplicationService {
  init() {
    const { Products, Orders } = this.entities;
    this.before("CREATE", Orders, async (req) => {
      for (const item of req.data.items) {
        const product = await SELECT.one.from(Products).where({ ID: item.product_ID });
        if (!product) throw req.reject(404, `Product ${item.product_ID} not found`);
        if (product.stock < item.quantity) throw req.reject(409, `Insufficient stock for ${product.name}`);
        item.unitPrice = product.price;
      }
      req.data.totalAmount = req.data.items.reduce((s, i) => s + i.unitPrice * i.quantity, 0);
    });
    this.after("CREATE", Orders, async (order) => {
      for (const item of order.items)
        await UPDATE(Products).set({ stock: { "-=": item.quantity } }).where({ ID: item.product_ID });
    });
    return super.init();
  }
};

Run locally:

cds watch
→ API at
http://localhost:4004/catalog
.

Step 5: Integration Patterns

SAP → External (polling):

import cron from "node-cron";
let lastCheck = new Date().toISOString();
cron.schedule("*/5 * * * *", async () => {
  const token = await getSAPToken();
  const orders = await sapApi("GET",
    `/API_SALES_ORDER_SRV/A_SalesOrder?$filter=CreationDate gt datetime'${lastCheck}'&$expand=to_Item`, token);
  for (const order of orders.d.results) {
    await externalApi.createOrder({ sapOrderId: order.SalesOrder, customer: order.SoldToParty,
      items: order.to_Item.results.map((i: any) => ({ material: i.Material, quantity: parseFloat(i.OrderQuantity) })),
    });
  }
  lastCheck = new Date().toISOString();
});

External → SAP (Shopify webhook):

app.post("/webhook/shopify/order", async (req, res) => {
  const token = await getSAPToken();
  const items = req.body.line_items.map((item: any) => ({
    Material: item.sku, RequestedQuantity: String(item.quantity),
    NetPriceAmount: String(item.price), NetPriceCurrency: req.body.currency,
  }));
  await sapApi("POST", "/API_SALES_ORDER_SRV/A_SalesOrder", token, {
    SalesOrderType: "OR", SalesOrganization: "1010", DistributionChannel: "10",
    SoldToParty: "CUSTOMER_BP_ID", to_Item: items,
  });
  res.sendStatus(200);
});

Examples

Example 1: Automated procurement workflow with inventory checks

User prompt: "When our warehouse stock for material RAW-001 drops below 500 KG, automatically create a purchase order to our primary vendor for 2000 KG. Check stock daily and log every PO created."

The agent will set up a cron job that runs daily, querying the SAP Product API for material RAW-001's current stock level. If stock is below 500, it will create a purchase order via the Purchase Order API with the vendor BP ID, 2000 KG quantity, the standard plant and storage location, and log the PO number and creation date. It will also check for any existing open POs for the same material to avoid duplicate orders.

Example 2: E-commerce to SAP sales order sync

User prompt: "Build a webhook endpoint that receives Shopify orders and creates matching sales orders in S/4HANA with all line items mapped by SKU to SAP material numbers, including a customer lookup by email."

The agent will create an Express POST endpoint that receives Shopify order webhooks, extracts customer email and line items, queries the Business Partner API to find an existing customer by email (or creates a new one), maps each Shopify line item's SKU to an SAP material number, and creates a sales order via the Sales Order API with all items, quantities, prices, and the customer reference. It will return a 200 status and log the SAP sales order number for reconciliation.

Guidelines

  • Always fetch a CSRF token before write operations — S/4HANA on-premise requires an
    X-CSRF-Token: Fetch
    GET request before any POST/PATCH/DELETE; cache the token and refresh on 403 responses.
  • Use
    $select
    and
    $top
    on all OData queries
    — SAP OData endpoints return all fields by default, which is slow for entities with hundreds of columns; always limit fields and page size.
  • Handle SAP date formats carefully — OData V2 uses
    /Date(timestamp)/
    format while V4 uses ISO 8601; parse dates explicitly rather than relying on automatic conversion.
  • Use SAP Cloud SDK for type safety — the generated VDM (Virtual Data Model) clients provide compile-time checking of field names, filters, and navigation properties.
  • Rate-limit polling integrations — SAP systems have finite OData throughput; poll no more frequently than every 5 minutes and use
    $filter
    on timestamps to minimize payload size.
  • Test against the SAP API Business Hub sandbox — use
    api.sap.com
    sandbox endpoints during development to avoid impacting production or QA systems.