Skip to content

Entity Resolution Recipe API

The Entity Resolution recipe has no API resource of its own. It is a guided flow over the same objects you can create by hand. Reproducing it programmatically means calling the standard endpoints in the order the recipe does: create the check, dry-run it, activate it, scan, materialize the golden set, and acknowledge the anomaly. The recipe's AI suggestions come from a small set of assist endpoints that you can call too.

Tip

For complete API documentation, including request/response schemas, visit the API docs.

All endpoints use the base URL of your Qualytics deployment (e.g., https://your-instance.qualytics.io/api) and require a Personal API Token. Every call needs the Member role. The team permission on the source datastore depends on the step, from Reporter for the AI assists to Editor for the scan and the materialize. See Permissions for the full matrix.

Note

The full payload reference for an Entity Resolution check, including every option of target_fields, is on the rule type's API page. This page shows only what the recipe sends.

The Recipe Sequence

1. Create the Check as a Draft

The Review step creates the check in Draft status. type takes String, Numeric, or DateTime; role is compare for weighted evidence and block for a hard boundary, and block fields always use exact comparison. weight is any non-negative number, and at least one compare field must have a positive weight. The recipe normalizes its suggested weights to sum to 1, but the API does not require that.

The recipe starts the threshold at 0.75, which is why the example sends it explicitly. A check created by hand without the field uses the platform default of 0.7.

Endpoint: POST /quality-checks

Permission: Drafter team permission on the datastore for a Draft, Author to create it Active directly

Example request
curl -X POST "https://your-instance.qualytics.io/api/quality-checks" \
  -H "Authorization: Bearer YOUR_QUALYTICS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "description": "Entity Resolution check on customers",
    "container_id": 145,
    "rule": "entityResolution",
    "status": "Draft",
    "properties": {
      "distinct_field_name": "customer_id",
      "composite_match_threshold": 0.75,
      "target_fields": [
        {
          "type": "String",
          "field_name": "company_name",
          "role": "compare",
          "comparison_type": "fuzzy",
          "pair_substrings": true,
          "pair_homophones": true,
          "consider_term_frequency": false,
          "weight": 0.5
        },
        {
          "type": "String",
          "field_name": "billing_email",
          "role": "compare",
          "comparison_type": "fuzzy",
          "weight": 0.3
        },
        {
          "type": "String",
          "field_name": "country",
          "role": "compare",
          "comparison_type": "exact",
          "weight": 0.2
        }
      ]
    }
  }'

Response (200 OK): the created check, with id and status: "Draft".

Before creating, the Review step also asks whether an active Entity Resolution check already exists for the asset. The same probe is available as POST /quality-checks/find-conflicting. It takes {"quality_check": <the create payload>}, optionally with exclude_check_id to ignore a check you are editing, needs the Drafter team permission, and responds with conflicting_check set to the overlapping check or to null.

2. Dry-Run the Check

The Validate step runs the check on a sample of up to 10,000 records per partition.

Endpoint: POST /quality-checks/{id}/dry-run

Permission: Drafter team permission on the datastore

Example request
curl -X POST "https://your-instance.qualytics.io/api/quality-checks/812/dry-run" \
  -H "Authorization: Bearer YOUR_QUALYTICS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "max_records_analyzed_per_partition": 10000 }'

Response (200 OK): a list with one result for the table, carrying the records processed and a per-partition breakdown. When duplicates exist, each partition result carries a single anomaly whose record count is the number of duplicate records in that sample.

3. Activate the Check

Validate finishes by setting the check to Active. The bulk update endpoint takes a list, so several checks can be activated at once.

Endpoint: PATCH /quality-checks

Permission: Author team permission on the datastore

Example request
curl -X PATCH "https://your-instance.qualytics.io/api/quality-checks" \
  -H "Authorization: Bearer YOUR_QUALYTICS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '[{ "id": 812, "status": "Active" }]'

Response (200 OK): the updated checks.

PATCH /quality-checks/activate with [{ "id": 812 }] is a more forgiving alternative: it activates every check it can and reports the ones it could not, with the reason for each, instead of failing the whole request.

4. Run the Full Scan

The Scan step runs a full scan of every record in the selected table, not only the records changed since the last scan.

Endpoint: POST /operations/run

Permission: Editor team permission on the datastore

Example request
curl -X POST "https://your-instance.qualytics.io/api/operations/run" \
  -H "Authorization: Bearer YOUR_QUALYTICS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "scan",
    "datastore_id": 12,
    "container_names": ["customers"],
    "incremental": false
  }'

Response (200 OK): the queued scan operation, with its id. Poll GET /operations/{id} until result is no longer queued or running.

The recipe also raises enrichment_source_record_limit so that up to 100,000 source records of the duplicates are stored for remediation, and sets archive_duplicate_anomalies to false so the new anomaly does not archive the previous run's. Once the scan completes, list the check's anomalies with GET /anomalies?quality_check={check_id} and read the duplicates with GET /anomalies/{id}/source-record. Each source record carries the entity it was grouped into in the _qualytics_entity_id column.

5. Materialize the Golden Set

The Materialize step writes the records you kept to the enrichment destination. The records to leave out travel as the list of their distinction-field values in exclusion_values, and materialize_inverse asks for the companion output of excluded records.

Endpoint: POST /operations/run

Permission: Editor team permission on the datastore

Field Required Description
type Yes "materialize".
datastore_id Yes The source datastore. The outputs are written to its linked enrichment destination.
container_names Yes, for this use The source table, as a one-element list. The endpoint also accepts container_tags instead.
exclusion_field With exclusion_values The field whose values identify the records to exclude. The recipe uses the distinction field.
exclusion_values With exclusion_field The values of the excluded records, as strings. At most 25,000 values, and 1,000,000 characters in total. Duplicates are ignored.
materialize_inverse No true also writes the excluded records to an output named like the golden set with an _inverse suffix. Default false.
exclusion_filter No Alternative to the two fields above: a SQL condition over the table (a WHERE clause without the keyword), up to 4,000 characters, where rows that satisfy it are kept. Only comparison and logical operators are accepted. Cannot be combined with exclusion_field and exclusion_values.
Example request
curl -X POST "https://your-instance.qualytics.io/api/operations/run" \
  -H "Authorization: Bearer YOUR_QUALYTICS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "materialize",
    "datastore_id": 12,
    "container_names": ["customers"],
    "exclusion_field": "customer_id",
    "exclusion_values": ["CUST-2417", "CUST-3090", "CUST-5561"],
    "materialize_inverse": true
  }'

Response (200 OK): the queued materialize operation. When it completes, the enrichment destination holds <prefix>_mat_er_customers and, with materialize_inverse, <prefix>_mat_er_customers_inverse.

Requests that break the exclusion rules are rejected with 422 Unprocessable Entity. The message arrives inside the standard validation error list, prefixed with Value error,, and reads one of:

  • exclusion_filter and exclusion_field/exclusion_values are mutually exclusive
  • exclusion_field is required when exclusion_values is set
  • exclusion_values is required when exclusion_field is set
  • exclusion_values exceeds the maximum of 25000 values
  • exclusion_values exceeds the maximum total length of 1000000 characters
  • exclusion_filter exceeds the maximum length of 4000 characters
  • exclusion_filter may not call the '<name>' function; only comparison and logical predicates are permitted

A materialize on a datastore without a linked enrichment destination, or with a disconnected one, is rejected with 409 Conflict.

6. Acknowledge the Anomaly

After the golden set is written, the recipe acknowledges the anomaly whose duplicates it remediated.

Endpoint: PUT /anomalies/{id}

Permission: Author team permission on the datastore

Example request
curl -X PUT "https://your-instance.qualytics.io/api/anomalies/4021" \
  -H "Authorization: Bearer YOUR_QUALYTICS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "status": "Acknowledged" }'

Response (200 OK): the updated anomaly.

AI Assist Endpoints

The recipe's AgentQ suggestions come from six endpoints under /recipes/entity-resolution/. Each call stands alone: it takes the identifiers of the objects involved, asks the configured AI provider, and returns the suggestion with its reasoning. They all need the Member role, the Reporter team permission or above on the datastore of the container, check, or operation named in the request, and an AI integration configured for AgentQ. The AgentQ data sharing level must allow the step: Metadata Shared for all of them except golden-record recommendations, which need Source Data Shared.

Endpoint Request Response
POST /recipes/entity-resolution/suggest-entity-key container_id suggested_field_name, suggested_field_id, reasoning
POST /recipes/entity-resolution/suggest-block-fields container_id, entity_key_field_id, optional selected_field_ids to leave out suggested_fields (each with field_name, field_id, type, role, comparison_type, weight, the three string options, and reasoning), composite_match_threshold, threshold_reasoning. Every block field comes back with exact comparison and weight 0.
POST /recipes/entity-resolution/suggest-compare-fields Same as above; the recipe passes the blocking fields as selected_field_ids Same shape. The string options are pair_substrings, pair_homophones, and consider_term_frequency, and the weights are normalized to sum to 1.
POST /recipes/entity-resolution/analyze-dry-run quality_check_id, plus records_processed, duplicate_entity_count, duplicate_record_count from a dry run; when any of the three counts is omitted, the endpoint runs the dry run itself assessment (good, adjust_threshold, adjust_fields, or no_matches), reasoning, suggested_threshold, and the three counts
POST /recipes/entity-resolution/interpret-scan-results operation_id, quality_check_id headline, findings, recommended_next_steps
POST /recipes/entity-resolution/recommend-golden-records container_id, quality_check_id, entity_clusters (each with entity_id and its records) recommendations, each with entity_id, preferred_record_index, reasoning
Example: recommend golden records
curl -X POST "https://your-instance.qualytics.io/api/recipes/entity-resolution/recommend-golden-records" \
  -H "Authorization: Bearer YOUR_QUALYTICS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "container_id": 145,
    "quality_check_id": 812,
    "entity_clusters": [
      {
        "entity_id": 7,
        "records": [
          { "customer_id": "CUST-0182", "company_name": "Acme Corp", "billing_email": "billing@acme.com", "country": "BR" },
          { "customer_id": "CUST-2417", "company_name": "Acme Corp Ltda", "billing_email": "billing@acmecorp.com", "country": "BR" }
        ]
      }
    ]
  }'

Response (200 OK):

{
  "recommendations": [
    { "entity_id": 7, "preferred_record_index": 0, "reasoning": "Both records describe the same company; the first has the canonical name and the primary billing address." }
  ]
}

The records in one request may total at most 50 across all clusters. Larger sets are sent in several requests, which is how the recipe scores entities a few at a time. Entities the model could not score within the time allowed are left out of the response rather than failing the request, so a partial response is normal. Only when no entity could be scored does the request fail with 503. This endpoint sends the record values themselves to the AI provider; with no AI integration configured it fails and nothing leaves Qualytics.

Error Responses

Status Code Description
400 Bad Request No AI integration is configured for the assist endpoints, or the anomaly cannot be changed, for example because it is archived.
401 Unauthorized Missing or invalid API token.
403 Forbidden The user lacks the role or the team permission the step needs, or the AgentQ data sharing level does not allow the assist. The message reads, for example, Golden-record recommendations needs Source Data Shared, and your Qualytics Administrator has not enabled it.
404 Not Found No container, check, operation, or anomaly exists with the specified ID.
409 Conflict The materialize cannot run because the datastore has no linked enrichment destination or that destination is disconnected. A scan returns it only when the datastore has no destination and its Remediation Strategy setting is anything other than None.
422 Unprocessable Entity Invalid request body, such as a target_fields entry with a wrong type or role, an exclusion payload that breaks the rules above, a container with no profiled fields, or more than 50 records in one golden-record request. For scan interpretation, also an operation that is not a scan or that did not assert the given check.
429 Too Many Requests Too many concurrent operations. Please try again later
503 Service Unavailable The AI provider could not produce a usable response: AI assist could not produce a usable response. Please try again.
Error response examples

403 Forbidden (data sharing level too low):

{ "detail": "Golden-record recommendations needs Source Data Shared, and your Qualytics Administrator has not enabled it." }

422 Unprocessable Entity (no profiled fields):

{ "detail": "No profiled fields found on this container. Run a profile first." }

422 Unprocessable Entity (too many records in one request):

{ "detail": "Too many records for one request (120); send fewer clusters so their records sum to at most 50." }