SAP S/4HANA Cloud
This guide walks you through the steps required to connect Docflo.ai to your SAP S/4HANA Public Cloud system, enabling seamless document integration and automated data posting.
- SAP S/4HANA Cloud system with appropriate authorization
- Basic authentication credentials for API access
- Access to SAP API Business Hub or system administrator
- Docflo.ai account with automation capabilities
- Understanding of SAP OData services
π Integration Stepsβ
Follow these steps to establish a connection between Docflo.ai and your SAP S/4HANA Cloud system:
Step 1: Create S/4HANA Cloud Connectionβ
- Navigate to the "Credentials" section in your Docflo.ai platform account settings
- Create a new SAP S/4HANA Cloud credentials with the following settings:
- Host: Your SAP S/4HANA Cloud API base URL (e.g.,
https://your-tenant.s4hana.cloud.sap) - Username: Your SAP S/4HANA Cloud API user
- Password: Your SAP S/4HANA Cloud API password
- Host: Your SAP S/4HANA Cloud API base URL (e.g.,
- Save the connection for use in automations

Step 2: Configure Document Fieldsβ
- Go to your document type configuration in Docflo.ai
- Create or configure fields that will be used in SAP integration, such as:
- Suppliers: Vendor information from SAP ODATA service API_BUSINESS_PARTNER (A_Supplier)
- Company Codes: SAP company code mappings from SAP ODATA service API_COMPANYCODE_SRV (A_CompanyCode)
- Cost Centers: Accounting allocation codes from SAP ODATA service api_cost_center (A_CostCenter_2)
- GL Accounts: General ledger account numbers from SAP ODATA service API_GLACCOUNTINCHARTOFACCOUNTS_SRV (A_GLAccountInChartOfAccounts)
- Map field values to corresponding SAP field names
- Set field validation rules to ensure data quality
Here is a sample configuration for company code selection list from API_COMPANYCODE_SRV.

Step 3: Create Automation for Document Postingβ
Create an automation in Docflo.ai using the following template code for supplier invoice creation:
interface InvoiceData {
invoiceId: string;
invoiceDate: string;
invoiceTotal: number;
invoiceSubTotal: number;
currency: string;
}
interface SAPInvoiceResponse {
SupplierInvoice: string;
status: string;
message?: string;
}
/**
* Handler that creates supplier invoices in SAP S/4HANA Cloud
*
* @param {object} event - Event data from the platform
* @param {object} secrets - Configuration secrets
* @param {object} manager - Document management interface
*/
import axios from 'axios';
export default async (event, secrets, manager) => {
const logs = [];
// Extract document data
const doc = event?.data?.doc;
const docId = doc?._id;
const invoiceData = doc?.docflo_results?.modelFields;
// Helper function to update document status
const updateDocumentStatus = async (status, sapInvoiceNumber = null, errorMsg = null) => {
if (!docId || !doc?.docflo_results) {
logs.push('Cannot update: Missing document data');
return false;
}
try {
const updatedDocfloResults = {
...doc.docflo_results,
modelFields: {
...doc.docflo_results.modelFields,
status: {
value: status,
type: 'string',
confidence: 1,
content: status
}
}
};
if (sapInvoiceNumber) {
// Here you will need to configure a field to save the invoice # back, like this
updatedDocfloResults.modelFields['SAP Invoice Number'] = {
value: sapInvoiceNumber,
type: 'string',
confidence: 1,
content: sapInvoiceNumber
};
}
if (errorMsg) {
updatedDocfloResults.modelFields.SAPError = {
value: errorMsg,
type: 'string',
confidence: 1,
content: errorMsg
};
}
const updateData = {
_id: docId,
status: status,
docflo_results: updatedDocfloResults
};
await manager.updateDocument(updateData);
logs.push('Document updated successfully');
return true;
} catch (updateError) {
logs.push(`Update failed: ${updateError.message}`);
return false;
}
};
try {
logs.push(`Starting SAP integration for document: ${docId}`);
if (!docId || !invoiceData) {
const errorMsg = 'Missing required document data';
logs.push(errorMsg);
return `ERROR: ${errorMsg}`;
}
// Extract invoice data with fallbacks
const invoiceId = invoiceData.InvoiceId?.value || 'INV001';
const invoiceDate = invoiceData.InvoiceDate?.value || new Date().toISOString().split('T')[0];
const invoiceTotal = invoiceData.InvoiceTotal?.value?.amount || 0;
const invoiceSubTotal = invoiceData.SubTotal?.value?.amount || 0;
const currency = invoiceData.InvoiceTotal?.value?.currencyCode || 'USD';
logs.push(`Processing invoice: ${invoiceId}, Total: ${invoiceTotal} ${currency}`);
// Format date for SAP
const formatDate = (dateStr) => {
if (dateStr && dateStr.length === 10) {
return dateStr + 'T00:00:00';
}
return new Date().toISOString().split('.')[0];
};
// Create authentication
const auth = Buffer.from(`${secrets.API_USER}:${secrets.API_PASSWORD}`).toString('base64');
// Build endpoint
const baseUrl = secrets.API_ENDPOINT;
const endpoint = baseUrl.replace(/\/$/, '') + '/sap/opu/odata/sap/API_SUPPLIERINVOICE_PROCESS_SRV/A_SupplierInvoice';
// Get CSRF Token (required by SAP)
let csrfToken = '';
let cookies = '';
logs.push('Fetching CSRF token...');
try {
const tokenResponse = await axios.get(endpoint, {
headers: {
'Authorization': `Basic ${auth}`,
'X-CSRF-Token': 'Fetch',
'Accept': 'application/json'
},
withCredentials: true
});
csrfToken = tokenResponse.headers['x-csrf-token'] || '';
cookies = tokenResponse.headers['set-cookie'] || '';
if (!csrfToken) {
throw new Error('Could not fetch CSRF token from SAP');
}
logs.push('CSRF token obtained successfully');
} catch (e) {
const errorMsg = `Failed to fetch CSRF token: ${e.message}`;
logs.push(errorMsg);
await updateDocumentStatus('ERROR', null, errorMsg);
return `ERROR: ${errorMsg}`;
}
// Build SAP request data
const sapData = {
CompanyCode: process.env.COMPANY_CODE || "1000",
DocumentDate: formatDate(invoiceDate),
PostingDate: formatDate(invoiceDate),
DocumentCurrency: currency,
InvoiceGrossAmount: invoiceTotal.toString(),
InvoicingParty: process.env.INVOICING_PARTY || "VENDOR001",
SupplierInvoiceIDByInvcgParty: invoiceId,
TaxDeterminationDate: formatDate(invoiceDate),
to_SupplierInvoiceTax: [{
TaxCode: process.env.TAX_CODE || "V1",
DocumentCurrency: currency
}],
to_SuplrInvcItemPurOrdRef: [{
PurchaseOrder: "PO001",
PurchaseOrderItem: "00010",
SupplierInvoiceItem: '1',
TaxCode: "V1",
SupplierInvoiceItemAmount: invoiceSubTotal.toString(),
DocumentCurrency: currency,
QuantityInPurchaseOrderUnit: "1",
PurchaseOrderQuantityUnit: "EA"
}]
};
// Make the request with CSRF token
const headers = {
'Authorization': `Basic ${auth}`,
'Content-Type': 'application/json',
'Accept': 'application/json',
'X-CSRF-Token': csrfToken
};
if (cookies) {
headers['Cookie'] = cookies;
}
logs.push('Sending request to SAP...');
const response = await axios.post(endpoint, sapData, {
headers,
timeout: 30000,
withCredentials: true
});
if (response.status === 201) {
const result = response.data?.d as SAPInvoiceResponse;
const sapInvoiceNumber = result?.SupplierInvoice || 'CREATED';
logs.push(`Success! SAP Invoice created: ${sapInvoiceNumber}`);
await updateDocumentStatus("SUCCESS", sapInvoiceNumber);
return {
success: true,
message: 'Invoice created successfully in SAP S/4HANA Cloud',
invoiceNumber: sapInvoiceNumber,
data: result
};
}
const unexpectedStatus = `Unexpected status ${response.status}`;
logs.push(unexpectedStatus);
await updateDocumentStatus('ERROR', null, unexpectedStatus);
return {
success: false,
message: `ERROR: ${unexpectedStatus}`
};
} catch (error) {
let errorMessage = 'Request failed: ';
if (error.response) {
errorMessage += `Status ${error.response.status}. `;
if (error.response.data?.error?.message?.value) {
errorMessage += `SAP says: ${error.response.data.error.message.value}`;
} else if (error.response.data) {
errorMessage += `Response: ${JSON.stringify(error.response.data).substring(0, 500)}`;
}
} else {
errorMessage += error.message;
}
logs.push(`Error occurred: ${errorMessage}`);
await updateDocumentStatus('ERROR', null, errorMessage);
return {
success: false,
message: `ERROR: ${errorMessage}`,
error: errorMessage
};
}
};
π§ Configuration Parametersβ
The automation requires the following environment variables and secrets:
Required Secrets (configured in Docflo.ai):β
- API_USER: Your SAP S/4HANA Cloud API username
- API_PASSWORD: Your SAP S/4HANA Cloud API password
- API_ENDPOINT: Your SAP S/4HANA Cloud base API URL
- SAP S/4HANA Cloud requires CSRF tokens for write operations
- Basic authentication credentials must have appropriate API permissions
- Test your automation thoroughly in a development environment first
- Monitor API rate limits and implement proper error handling
π You're all set! Once you've completed these steps, your Docflo.ai platform will be able to automatically post document data to your SAP S/4HANA Cloud system and track the integration status.
π Supportβ
If you encounter any issues during the integration process, please contact the Docflo.ai support team or consult your SAP S/4HANA Cloud system administrator. Common troubleshooting steps include:
- Verifying API credentials and permissions
- Checking network connectivity and firewall rules
- Reviewing SAP API documentation for field requirements
- Testing with sample data before processing live documents