How the integration works
The user enters or changes a record in an SAP application (Fiori, SAPUI5, SAP GUI or a workflow step).
The SAP backend or your integration layer sends the record to the DQC Validation API.
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.
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.