SAP S/4HANA Cloud – Gotchas

Service ID: sap-s4hana-cloud

SAP S/4HANA Cloud is SAP's intelligent cloud ERP suite covering finance, procurement, sales, and more, exposed via OData v2/v4 APIs through the SAP API Business Hub.

⚠️

20 gotchas across 10 resources

These are connector-specific behaviors and limitations to be aware of when integrating.

addbillCreditNotesAdd

reference is required on create (SAP rejects a supplier credit note without a document reference) and note is limited to 25 characters. number is assigned by SAP and read-only — it is the SAP document number, not the caller's reference. subsidiary.id is required and selects the company the credit note posts to — it cannot be supplied any other way. A posted credit note reads back as status: authorised and is immutable: update is unsupported, and delete posts a reversal rather than removing the document.

deletebillCreditNotesDelete

Deleting cancels the posted credit note: SAP posts a reversal document and the credit note reads back as voided. Only a bill credit note can be cancelled here: the id of a bill returns 404 and nothing is cancelled (use DELETE /accounting/bills/{id}). If the document type can't be verified (for example SAP is unavailable), the request fails and nothing is cancelled; a 429 or 5xx can be retried.

addbillPaymentsAdd

SAP S/4HANA Cloud creates the bill payment as a standalone financial accounting posting. It does not run SAP's automatic payment program. company_id, supplier.id and account.id are required. If SAP rejects the posting (for example an unknown bank account or a supplier not set up in the company), the request fails with a 400 and nothing is posted.

Bills3 gotchas

allbillsAll

Supplier credit notes are excluded from this list and returned under the bill-credit-notes resource instead.

addbillsAdd

Required fields on create: company_id, bill_date, supplier.id, currency, total and line_items. The posting date is set to bill_date.

Each line item is either a purchase-order line or a G/L line:

  • Lines with line_items[].purchase_order.id are posted against that purchase order. Optionally provide line_items[].purchase_order.line_number for the purchase order item.
  • All other lines are G/L lines and require line_items[].ledger_account.id (or .code). A line with neither a purchase order nor a ledger account is rejected with a 400.

line_items[].tax_rate.code takes the SAP tax code (e.g. V1). SAP S/4HANA Cloud does not expose tax rates as readable data, so the tax code must be supplied directly.

deletebillsDelete

Cancellation is supported and reverses the posted supplier invoice. By default, reversal reason 01 is used and the posting date is set to today (UTC). Override via pass_through is supported.

Only a bill can be cancelled here: the id of a bill credit note returns 404 and nothing is cancelled (use DELETE /accounting/bill-credit-notes/{id}). If the document type can't be verified (for example SAP is unavailable), the request fails and nothing is cancelled; a 429 or 5xx can be retried.

Credit Notes2 gotchas

allcreditNotesAll

Credit notes are SAP FI customer credit memos read from the accounting-document line items, grouped into one record per document. type is always accounts_receivable_credit; status is authorised, or paid once the customer item has been cleared. Every posting line of the document other than the customer line is returned as a line_items entry — including tax lines, which appear as a line on the tax account rather than as total_tax. sub_total, total_tax, balance and allocations are not returned. created_at is the date the document was entered and date_issued its document date; both carry a date only (midnight UTC).

Supported filters are updated_since, created_since and number; customer_id is not supported. The date filters compare on whole days (the time part is ignored) and are inclusive of the day given. number is the SAP document number and is only unique within a company and fiscal year, so filter[number] can return several records — use id (company:fiscal_year:document) to address a single one.

limit applies to SAP posting lines, not to credit notes: a page can hold fewer credit notes than limit, the last credit note on a page can be returned with only some of its line_items, and no next cursor is returned. Results are newest first. Use the maximum limit (200) and narrow the set with created_since or updated_since; a document with more lines than fit on a page cannot be read completely from the list — fetch it by id.

addcreditNotesAdd

SAP S/4HANA Cloud creates a pure FI customer credit memo (the accounts-receivable mirror of an invoice on this connector). This is not an SD credit memo request or billing document — no pricing, no sales-order link, and it will not appear in SD reporting. company_id, customer.id, currency, a positive total_amount and at least one line_items[] entry with ledger_account.id or ledger_account.code (the revenue account to debit) are required; a zero or negative total_amount is rejected, because the connector always posts the credit direction and SAP would otherwise accept a reversed document silently. The line_items[].total_amount values must add up to total_amount, each as a positive number — SAP rejects an unbalanced document, an unknown ledger account, a customer not set up in the company, and an account that requires a tax code or a line description; the request then fails with a 400 and nothing is posted.

note is written as the 25-character document header text and reference as the 16-character document reference; both are returned on reads. line_items[].description is written as the line text (50 characters) and is required by SAP for some accounts. line_items[].department_id posts the cost center and line_items[].tracking_categories[0].id the profit center; the header-level department_id and tracking_categories are ignored. Tax is not posted: tax_code, total_tax and line_items[].tax_rate are ignored, so a revenue account that requires a tax code cannot be used — post tax as its own line on the tax account. allocations and terms are ignored; the credit is not applied to any invoice. number is assigned by SAP and cannot be set. Posted credit notes are immutable: update and delete are unsupported, and SAP's correction path is a separate reversal document.

Customers3 gotchas

addcustomersAdd

Creating a customer requires a correspondence language and a business partner grouping. They default to EN and BP01. Override either one with pass_through (extend_object), for example:

"pass_through": [{"service_id": "sap-s4hana-cloud", "extend_object": {"BusinessPartnerGrouping": "BP02", "CorrespondenceLanguage": "DE"}}]

Which grouping to use depends on the SAP tenant. Each tenant configures whether a grouping assigns numbers automatically. If the grouping requires a manually assigned number, SAP returns 400 Grouping <code> has external number assignment. Please enter a valid number. Either use a grouping that assigns numbers automatically, or supply the number in the same extend_object as "BusinessPartner": "<8–10 digit number, unique in the tenant>".

An address with a country is required. Provide addresses[0] with at least country (e.g. US, DE). Without it, SAP returns 400 Country/Region Key ... is a required entry field.

updatecustomersUpdate

Only first_name, last_name, company_name, display_name and status can be updated.

Updates that include addresses, bank_accounts, emails, phone_numbers or websites are rejected with a 400. Set those values when creating the record, or change them directly in SAP.

deletecustomersDelete

SAP does not allow deleting a customer that has been used in transactions. Delete blocks the customer instead: the record stays in SAP and subsequent reads return status: inactive. Updating status to active unblocks it.

allgoodsReceiptsAll

Only goods receipts against a purchase order are returned; other inbound and outbound goods movements recorded in the same document series are excluded from the list. Fetching by id is not restricted this way — see the get-one endpoint.

id is a composite of the document number and its fiscal year in the form <document>:<year>, for example 5000000123:2026. Pass it back unchanged to GET /accounting/goods-receipts/{id}. receipt_number, display_id and downstream_id carry the bare document number.

No filters are supported: the accounting system records no last-changed timestamp on a goods receipt, so filter[updated_since] cannot be honoured, and filter[supplier_id] and filter[purchase_order_id] are not available either. Any filter is rejected with a 400 UnsupportedFiltersError. Only sort[by]=created_at is supported, and updated_at is null. created_at carries the creation date at midnight UTC — the accounting system records the creation time in a separate field this release does not read, so the time component is always 00:00:00Z and receipts created on the same day have no guaranteed order under sort[by]=created_at.

Supplier, purchase order and location are recorded per line. The header supplier, purchase_order_id, po_number and location_id are populated only when every line agrees; when a receipt spans several purchase orders or suppliers they are null and line_items[].purchase_order carries the link. location_id is the plant and storage location joined as <plant>/<storage location>. quantity_ordered, unit_price and line_items[].purchase_order.line_id are not available; line_items[].purchase_order.line_number is populated instead. line_items[].total_amount is populated only when an amount was entered on the goods movement; the value of a receipt priced from its purchase order is not exposed.

Goods receipts are immutable. A cancellation is posted as a separate reversal document: the reversal is returned as its own receipt with status: cancelled, and the original document is also reported as cancelled. meta.total_count is populated.

onegoodsReceiptsOne

id must be the composite <document>:<year> value returned by the list endpoint, for example 5000000123:2026. A bare document number, or any other shape, is rejected with a 400. Fetching by id returns the document whether or not it is a purchase-order receipt; the purchase-order scope applies to the list endpoint only.

created_at carries the document's creation date at midnight UTC: the accounting system records the creation time in a separate field that this release does not read, so the time component is always 00:00:00Z.

Invoices2 gotchas

allinvoicesAll

number is the SAP document number, which is only unique within a company code, so filter[number] can return invoices from more than one company code. Use the full id (<company code>:<fiscal year>:<document number>, e.g. 1710:2026:1800000001) to identify an invoice.

addinvoicesAdd

SAP S/4HANA Cloud creates the invoice as a financial accounting posting, not a billing document: no pricing, output or billing workflow is triggered.

Required: company_id, customer.id, currency, total, and at least one line item with ledger_account.id.

  • total must equal the sum of line_items[].total_amount.
  • The G/L account is taken from each line's ledger_account. The top-level ledger_account is not used.
  • The customer and each line's G/L account must already be set up in the company code given in company_id. GET /accounting/subsidiaries lists the company codes.
  • Optional per line: department_id (cost center) and tracking_categories[0].id (profit center).

If SAP rejects the posting (for example an unknown ledger account, a customer not set up in the company, or an unbalanced document), the request fails with a 400 and nothing is posted.

addjournalEntriesAdd

company_id (the SAP company code) is required. Journal entries are posted as standard G/L postings; the SAP document type cannot be changed. If SAP rejects the posting (for example an unknown company code or ledger account, or an unbalanced entry), the request fails with a 400 and nothing is posted.

Payments1 gotcha

addpaymentsAdd

SAP S/4HANA Cloud creates the payment as a financial accounting posting. It is not a bank statement import or a payment-program run. company_id, customer.id and account.id are required. If SAP rejects the posting (for example an unknown bank account or a customer not set up in the company), the request fails with a 400 and nothing is posted.

Suppliers3 gotchas

addsuppliersAdd

Creating a supplier requires a correspondence language and a business partner grouping. They default to EN and BP01. Override either one with pass_through (extend_object), for example:

"pass_through": [{"service_id": "sap-s4hana-cloud", "extend_object": {"BusinessPartnerGrouping": "BP02", "CorrespondenceLanguage": "DE"}}]

Which grouping to use depends on the SAP tenant. Each tenant configures whether a grouping assigns numbers automatically. If the grouping requires a manually assigned number, SAP returns 400 Grouping <code> has external number assignment. Please enter a valid number. Either use a grouping that assigns numbers automatically, or supply the number in the same extend_object as "BusinessPartner": "<8–10 digit number, unique in the tenant>".

An address with a country is required. Provide addresses[0] with at least country (e.g. US, DE). Without it, SAP returns 400 Country/Region Key ... is a required entry field.

updatesuppliersUpdate

Only first_name, last_name, company_name, display_name and status can be updated.

Updates that include addresses, bank_accounts, emails, phone_numbers or websites are rejected with a 400. Set those values when creating the record, or change them directly in SAP.

deletesuppliersDelete

SAP does not allow deleting a supplier that has been used in transactions. Delete blocks the supplier instead: the record stays in SAP and subsequent reads return status: inactive. Updating status to active unblocks it.