SAP integration guide

DQC works with SAP in three ways:

  1. Read SAP data and check it against the rules your business team maintains in DQC, through the SAP HANA, SAP RFC or OData connector.

  2. Check data before it is saved: SAP applications send new or changed records to the DQC Validation API and show the findings before the record is saved.

  3. Write corrections back: an SAP application on your side loads the results of DQC improvement flows and posts the approved changes to SAP. DQC itself only reads from SAP.


Part 1: Read SAP data

Choose the connector that fits your SAP landscape. Connecting to SAP systems compares the options in detail.

Connector

Reads from

Setup guide

SAP HANA

The HANA database, through SQL (SAP HANA Cloud, on-premise, SAP Datasphere)

Connection to SAP HANA

SAP RFC

The SAP application server, through RFC (SAP_BASIS 7.54 or later; on request)

Connection to SAP RFC

OData

OData v2 or v4 services of SAP Gateway, S/4HANA, SAP BW or SAP Datasphere

Connection to OData

Third-party tools

A SQL database that an extraction tool loads SAP data into

Connecting to SAP systems

SAP HANA in brief

  • The connection is always TLS-encrypted

  • One connector lists the tables and views of one schema. Without a schema, DQC lists the objects in the user's own schema; for S/4HANA tables, enter the ABAP schema (for example SAPHANADB)

  • SQL checks on HANA tables and views run inside HANA; ask your HANA administrator to map the DQC database user to a workload class

  • Troubleshooting is covered in the setup guide

OData in brief

Authentication:

  • Basic authentication with a technical user and password

  • OAuth 2.0 client credentials with client ID, client secret, token URL and an optional scope. DQC sends the client ID and secret in the request body of the token call, and requests a new access token automatically when the old one expires

  • Optional custom headers as a JSON object, for example {"x-api-key": "<key>"}

What DQC handles automatically:

  • Entity discovery: DQC reads the service metadata and lists the entity sets. Properties whose names start with to_ (SAP's naming convention for navigation properties) are not loaded as columns

  • SAP Datasphere catalog: with OData version v4, if the service URL points to the Datasphere consumption catalog, DQC lists the exposed assets and reads each one through its relational data URL (or the analytical one if there is none)

  • Character encoding: DQC reads responses as UTF-8 and attempts to repair text that a server sends double-encoded, in the metadata and in the data. Correct encoding on the service is still required

  • Empty entity sets: for OData v2 services, DQC lists only entity sets that contain data and answer a $count request

Enter the service root URL without query parameters such as ?sap-client=100; DQC appends $metadata and the entity set names to it.

Troubleshooting:

Symptom

What to check

"Unable to connect to OData service"

The service URL (service root, https://, no query parameters) and the OData version. For v2, the message mentions incorrect credentials for every HTTP error on $metadata, also for a wrong URL (404) or a missing authorization (403): retrieve <service URL>/$metadata with the same user and headers as the connector

Authentication failed

User and password, or client ID, client secret, token URL and scope for OAuth; for SAP Gateway, the technical user's PFCG roles

Entity not found

The exact entity set name (case-sensitive) in $metadata

No entities listed

Required custom headers, the service's authorizations, and for v2 whether the entity sets contain data and support $count


Part 2: Check data before it is saved

SAP applications call the DQC Validation API with the record that is being created or changed. DQC applies the rules of a ruleset and returns the findings, so the user can correct the record before it is saved. The business team maintains the rules in DQC. A ruleset in standard mode also monitors your existing data; a ruleset in Validation API only mode is used only through the API.

How the integration works

  1. The user enters or changes a record in an SAP application (Fiori, SAPUI5, SAP GUI or a workflow step).

  2. The SAP backend or your integration layer sends the record to the DQC Validation API.

  3. DQC checks the record against the active rules of the ruleset. Each call connects to the data source of the ruleset's table, so the call fails if that source is not reachable. Rules that compare against existing data, for example "material number is not yet used", query this source with the values you send. Send values in the format the table stores: tables read directly from SAP contain the internal format, for example 000000000000004711 for material number 4711. When you check a changed record, such a uniqueness rule also finds the record itself; use rulesToApply to run it only for new records.

  4. The application shows the findings next to the fields and lets the user correct the record, or blocks the save.

Call the API from the SAP backend or your integration layer, for example through an HTTP destination in SM59 or a destination on SAP BTP. Do not call it from the browser: the API token would reach every user's device, and the DQC Platform does not accept cross-origin requests from SAP hosts.

Prerequisites

  • A ruleset in DQC whose rules check the fields you send. The field names in the request must match the column names of the ruleset's table (for example MATNR), including the columns that the ruleset's filters use. The ruleset ID is the UUID after /ruleset/ in the address bar when you open the ruleset in DQC

  • A DQC API token with the permission Read & write. The dialog preselects Read-only, which cannot call the Validation API. The token needs view access to the ruleset and its table; rules that read other tables also need view access to those tables, otherwise DQC reports them as not executed. A token that is scoped to a group only reaches that group's resources

  • Tokens expire one year after creation, and a token stops working when the user who created it is no longer a member of the tenant. Create integration tokens with a user who stays in the tenant

  • An outbound HTTPS route from the SAP system or integration layer to the DQC Platform

Endpoint

POST https://<your-dqc-host>/apiV2/validation/ruleset/{ruleset_id}/

For DQC SaaS, <your-dqc-host> is shared.app.dqc.ai.

Header

Value

Content-Type

application/json

Authorization

Bearer <API token>

The API token identifies your DQC tenant; you do not need to send a tenant header.

Query parameter

Values

Description

response_format

powerapps (default), standard, rows, human

Shape of the response, see below

message_format

text (default), html

Only for rows: renders ERROR_MESSAGE as plain text or as an HTML fragment

output_as_string

false (default), true

Wraps the response as {"output_as_string": "<JSON>"} for clients that can only handle strings. Inside the string, field names are in snake_case (for example request_id, issues_by_field)

The parameters detailed and powerapps_format from earlier versions of this page still work; response_format takes precedence over both.

Request

Each entry in data is one record. Each field has a type and a value:

{
  "requestId": "material-create-HX-M8-40",
  "data": [
    {
      "MATNR": { "type": "text", "value": "HX-M8-40" },
      "MAKTX_DE": { "type": "text", "value": "Sechskantschraube M8x40" },
      "MAKTX_EN": { "type": "text", "value": "Hexagon screw M8x40" },
      "NTGEW": { "type": "numeric", "value": 1.2 },
      "BRGEW": { "type": "numeric", "value": 1.5 },
      "ERSDA": { "type": "date", "value": "20261029", "dateFormat": "%Y%m%d" }
    }
  ],
  "rulesToApply": []
}

Field

Description

requestId

Your own ID for the call; DQC returns it in the response

data

List of records. A record is an object of field names and typed values

rulesToApply

Optional list of rule IDs (ruleId in the standard response). Empty or missing: all active rules of the ruleset. IDs of inactive rules or of rules that do not belong to the ruleset are ignored without an error; if no rule is left, the response reports success with an empty ruleResults list

Type

Value

text

Any value, checked as text. Send identifiers as strings. null becomes the text None; use empty for missing values

numeric

A number, or a string with a dot as decimal separator ("1.5"). Any other value, including "" and null, makes the whole request fail with HTTP 400

date

A date string. dateFormat uses Python strptime notation, for example %Y-%m-%d, %d.%m.%Y or %Y%m%d for ABAP DATS values. A value that does not match dateFormat makes the whole request fail with HTTP 400; send an initial ABAP date (00000000) as empty. Without dateFormat, the value is checked as text

bool

true, 1, "true", "yes" and "1" (any letter case) count as true; everything else counts as false, including ABAP's "X"

empty

No value. Send { "type": "empty", "value": null }; the value key is required

You can also send plain values instead of typed objects. DQC then treats strings as text, numbers as numeric, booleans as bool and null as an empty value:

{
  "requestId": "material-create-HX-M8-40",
  "data": [ { "MATNR": "HX-M8-40", "MAKTX_DE": "Sechskantschraube M8x40", "NTGEW": 1.2, "BRGEW": 1.5 } ]
}

Response

Every finding message is built from the rule name and the rule description, which the business team maintains in DQC: Rule '<rule name>' failed: <rule description>.

powerapps (default): compact, one entry per record in issuesByField (null if the record has no field-level findings). globalIssues holds findings that do not belong to one field and rules that could not be executed. Use it for calls with one record.

{
  "requestId": "material-create-HX-M8-40",
  "success": false,
  "totalIssues": 2,
  "issuesByField": [
    {
      "MATNR": ["Rule 'Material number is not yet used' failed: MATNR must be unique in the material master"],
      "MAKTX_EN": ["Rule 'English short text is maintained' failed: MAKTX_EN must not be empty"]
    }
  ],
  "globalIssues": []
}

standard: the full result per rule, including rules that failed to run (status is executed, error or skipped).

{
  "requestId": "material-create-HX-M8-40",
  "totalRowsValidated": 1,
  "totalIssues": 1,
  "passed": false,
  "ruleResults": [
    {
      "ruleId": "c4ca4238-a0b9-3382-8dcc-509a6f75849b",
      "ruleName": "Material number is not yet used",
      "ruleDescription": "MATNR must be unique in the material master",
      "totalIssues": 1,
      "issues": [
        {
          "rowIndex": 0,
          "columnName": "MATNR",
          "value": "HX-M8-40",
          "expectedPattern": "Value should satisfy 'duplicates' constraint",
          "errorMessage": "Rule 'Material number is not yet used' failed: MATNR must be unique in the material master"
        }
      ],
      "passed": false,
      "status": "executed",
      "errorMessage": null,
      "matchedRowIndices": null
    }
  ],
  "hasExecutionErrors": false
}

rows: returns one entry in rows per record you sent, in request order, with your values plus ERROR_MESSAGE and ERRORS_DETAILS (both null for records without findings). Findings that DQC cannot attribute to a record, for example from rules that compare against existing data, and rules that could not be executed are listed in globalIssues. Always check passed and globalIssues as well. ERROR_MESSAGE and ERRORS_DETAILS are reserved field names.

{
  "requestId": "material-batch-17",
  "passed": false,
  "totalRowsValidated": 2,
  "totalIssues": 1,
  "rows": [
    {
      "MATNR": "HX-M8-40",
      "BRGEW": 0,
      "ERROR_MESSAGE": "Rule 1: Gross weight is positive - BRGEW must be greater than 0",
      "ERRORS_DETAILS": {
        "c4ca4238-a0b9-3382-8dcc-509a6f75849b": {
          "ruleName": "Gross weight is positive",
          "message": "BRGEW must be greater than 0",
          "columns": ["BRGEW"]
        }
      }
    },
    { "MATNR": "HX-M8-50", "BRGEW": 1.5, "ERROR_MESSAGE": null, "ERRORS_DETAILS": null }
  ],
  "globalIssues": []
}

human: a plain-text report in the field report, for logs or e-mails.

When you send several records, use rows. Only rows and human attribute each finding to the record that caused it. In powerapps and standard, the position in issuesByField and the rowIndex are estimated and can point to the wrong record if you send more than one record.

Example: ABAP backend

This example is a method template for SAP_BASIS 7.54 or later. It needs the data elements MATNR and MAKTX and the class /UI2/CL_JSON on the system. zcl_dqc_settings stands for your own class that reads the API token from secure storage; never hard-code the token.

Set up the HTTP destination DQC_API in transaction SM59: type G, target host of your DQC Platform, service number 443, empty path prefix, SSL active with the SSL client PSE DFAULT or ANONYM, no logon data. Import the root CA certificate of the DQC host into this PSE in transaction STRUST, and maintain the HTTP proxy in the destination if outbound traffic uses one.

METHODS check_material
  IMPORTING
    iv_matnr    TYPE matnr
    iv_maktx_de TYPE maktx
    iv_maktx_en TYPE maktx
  RETURNING
    VALUE(rv_result) TYPE string.   " PASSED, FAILED or NOT_CHECKED
METHOD check_material.
  TYPES: BEGIN OF ty_rule_result,
           rule_name     TYPE string,
           status        TYPE string,
           passed        TYPE abap_bool,
           total_issues  TYPE i,
           error_message TYPE string,
         END OF ty_rule_result,
         ty_rule_results TYPE STANDARD TABLE OF ty_rule_result WITH EMPTY KEY,
         BEGIN OF ty_result,
           passed               TYPE abap_bool,
           total_issues         TYPE i,
           has_execution_errors TYPE abap_bool,
           rule_results         TYPE ty_rule_results,
         END OF ty_result.
  DATA lo_client TYPE REF TO if_http_client.
  DATA ls_result TYPE ty_result.

  rv_result = `NOT_CHECKED`.
  DATA(lv_ruleset) = `<ruleset-id>`.
  DATA(lv_token)   = zcl_dqc_settings=>api_token( ).   " your own class, reads the token from secure storage

  cl_http_client=>create_by_destination(
    EXPORTING  destination = 'DQC_API'
    IMPORTING  client      = lo_client
    EXCEPTIONS OTHERS      = 1 ).
  IF sy-subrc <> 0.
    RETURN.   " destination DQC_API missing or not permitted
  ENDIF.
  lo_client->propertytype_logon_popup = if_http_client=>co_disabled.

  DATA(lv_body) =
    |\{"requestId":"material-create-{ escape( val = iv_matnr format = cl_abap_format=>e_json_string ) }",| &&
    |"data":[\{| &&
    |"MATNR":"{ escape( val = iv_matnr format = cl_abap_format=>e_json_string ) }",| &&
    |"MAKTX_DE":"{ escape( val = iv_maktx_de format = cl_abap_format=>e_json_string ) }",| &&
    |"MAKTX_EN":"{ escape( val = iv_maktx_en format = cl_abap_format=>e_json_string ) }"| &&
    |\}]\}|.

  cl_http_utility=>set_request_uri(
    request = lo_client->request
    uri     = |/apiV2/validation/ruleset/{ lv_ruleset }/?response_format=standard| ).
  lo_client->request->set_method( if_http_request=>co_request_method_post ).
  lo_client->request->set_content_type( 'application/json; charset=utf-8' ).
  lo_client->request->set_header_field( name = 'Authorization' value = |Bearer { lv_token }| ).
  lo_client->request->set_cdata( lv_body ).

  lo_client->send( EXPORTING timeout = 30 EXCEPTIONS OTHERS = 1 ).
  IF sy-subrc = 0.
    lo_client->receive( EXCEPTIONS OTHERS = 2 ).
  ENDIF.
  IF sy-subrc <> 0.
    lo_client->close( EXCEPTIONS OTHERS = 1 ).
    RETURN.   " DQC not reachable or no answer within 30 seconds
  ENDIF.

  lo_client->response->get_status( IMPORTING code = DATA(lv_status) ).
  DATA(lv_json) = lo_client->response->get_cdata( ).
  lo_client->close( EXCEPTIONS OTHERS = 1 ).
  IF lv_status <> 200.
    RETURN.   " 400 request or data source, 401/403 token, 404 ruleset ID
  ENDIF.

  /ui2/cl_json=>deserialize(
    EXPORTING json        = lv_json
              pretty_name = /ui2/cl_json=>pretty_mode-camel_case
    CHANGING  data        = ls_result ).

  IF ls_result-has_execution_errors = abap_true OR ls_result-rule_results IS INITIAL.
    RETURN.   " a rule did not run, or no rule ran at all
  ENDIF.
  rv_result = COND #( WHEN ls_result-passed = abap_true THEN `PASSED` ELSE `FAILED` ).
ENDMETHOD.

The method returns NOT_CHECKED if DQC could not check the record or a rule did not run; apply your fallback rule in that case. To show the individual findings, extend ty_rule_result with the issues table, or request response_format=powerapps and read issuesByField. If your material numbers are numeric, pass them in the format your ruleset's table stores (see step 3 above).

Example: SAP BTP

On SAP BTP, create a destination for the DQC Platform: URL https://<your-dqc-host>, proxy type Internet, authentication NoAuthentication. Your service or integration flow adds the header Authorization: Bearer <API token> to each call and reads the token from a secret store: in SAP Integration Suite from a secure parameter in the security material, in your own application from the SAP Credential Store service. Do not expose this destination to the browser through an application router, because every app user could then call the DQC API with your token. Your Fiori app calls your own service, which forwards the record to DQC and returns the findings.

Best practices

  • Validate before database changes. Place the HTTP call in a validation step before the record is written, outside update task processing and open database cursor loops: classic HTTP client calls can trigger an implicit database commit. SAP still enforces its own authorizations, locks and checks when saving

  • Decide the fallback. Set a timeout (for example 10 to 30 seconds) and decide what happens if DQC cannot check the record: block the save, or save and check the record later

  • Send every field the rules use, with the column names of the ruleset's table, including the columns that the ruleset's filters use. If such a field is missing, the affected rules can be left out of the result without an error message, and the response still reports success. During integration testing, call the API once with response_format=standard and check that ruleResults lists every rule you expect

  • Treat execution errors as "not checked". hasExecutionErrors: true in standard, or messages containing "could not be executed" in globalIssues, mean that a rule did not run

  • Keep the token on the server, use a group-scoped token with access only to the needed rulesets, and replace it before it expires (DQC shows the remaining days): create a new token, switch the integration, then revoke the old one

  • Show findings next to the fields. The messages come from the rule names and descriptions that the business team maintains in DQC

Troubleshooting

Response

Cause

What to check

HTTP 400 "Request/response validation error"

The request does not match the format: requestId or data is missing, a field is unknown, or the ruleset ID is not a UUID

Request body against the examples on this page; field_errors in the response names the field

HTTP 400 "We encountered an error while processing your request"

A value cannot be converted to its type (for example a date that does not match dateFormat), or DQC cannot reach the data source of the ruleset's table

Types, values and dateFormat; the data source in DQC

HTTP 401

The API token is missing, invalid, expired or revoked

Authorization header; token status and expiry date in DQC

HTTP 403

The token is read-only, has no access to the ruleset or its table, or the user who created the token is no longer a member of the tenant

Token permission Read & write, group scope, access to ruleset and table, token creator

HTTP 404

The ruleset ID does not exist

Ruleset ID

HTTP 500

A check against the data source failed

Retry later; if it persists, contact DQC support

"Rule '...' could not be executed: ..." in globalIssues

The rule type is not supported by the Validation API, or a custom SQL, cross-table or cross-connector rule failed

The reason after the colon

success or passed is true, but ruleResults lacks rules you expect

A field that a rule or a ruleset filter uses is missing or named differently, or the rule is inactive

Field names in the request against the column names of the ruleset's table


Part 3: Write corrections back

The output of a DQC improvement flow depends on its steps. Enrichment steps provide proposed values together with confidence scores and source information. DQC does not write to SAP. An SAP application on your integration layer loads the results, lets users set a confidence threshold and review the selection, and posts the approved changes through SAP's own interfaces and authorizations. Before applying a proposal, it should check whether the SAP record has changed since DQC read it. In the video, this application runs on SAP BTP.

The application is set up together with your SAP team for each process: contact us. For the validation basics, see also Introduction to API validations.