Skip to main content

Squillify development is paused indefinitely. Read why, and discover what this experiment achieved. Read the story

MicroSkills Reference

Reconcile Records

data.reconcile_records

ProductionBounded custom (Class B)Deterministicstrictmoderatev1.0.0
No network

Overview

You send two tables, the columns that identify a row, and the rules that decide whether two rows agree. We index both sides on the key, compare only the pairs that share one, and hand back three lists: pairs where every rule passed, pairs where at least one rule failed with both disagreeing values so you can act without re-fetching the rows, and rows with no counterpart. Seven comparison rules are offered — exact, case-folded, normalised text, absolute and relative numeric tolerance, calendar-day equality and null equivalence — and none of them guesses: there is no similarity score here, because a match you cannot explain is not a reconciliation. Keys compare as text with no type coercion, so 1, 1.0 and "1" are three different keys, the same rule data.join_tables publishes.

Knowing two rows met is not the same as knowing they agree, and the difference is where the money is.

Reliability

The same two tables, keys and rules always give the same three lists, on any machine and under any locale, time zone or default charset. Normalised text lower-cases with the root locale and calendar-day equality reads the offset as written, so neither moves with the host. Numeric comparison is exact decimal arithmetic rounded half-up at decimalScale, the same semantics data.validate_totals publishes, so no answer here depends on binary floating point. Conflicts are the one place values come back, because 1234.50 against 1234.05 is a typo and 1234.50 against 9910.00 is a different invoice, and a caller told only that a field differs has to re-fetch both rows; each echoed value stops at 128 code points and says so. Matched and unmatched rows carry indexes only. Work is projected from the bucket histogram, never from left times right: the implementation indexes on the key and never forms a Cartesian product, so refusing on work that will not happen would push you into batching around a limit that does not exist. Fifteen refusals, none retryable unchanged: RECONCILE_ROWS_INVALID and RECONCILE_ROW_LIMIT_EXCEEDED for a malformed table or one above 50000 rows; RECONCILE_KEYS_INVALID for a malformed key list and RECONCILE_KEY_LIMIT_EXCEEDED above eight key mappings, so remove extra blocking columns before retrying; RECONCILE_KEY_NOT_UNIQUE when duplicateKeyPolicy is REFUSE and a bucket holds several rows on one side; RECONCILE_RULES_INVALID, RECONCILE_RULE_UNSUPPORTED for an unknown comparison rule and RECONCILE_RULE_LIMIT_EXCEEDED above 64 rules, so split the comparison or remove unnecessary rules; RECONCILE_VALUE_LENGTH_INVALID when maxValueLength is outside 1 to 512 or a value is wider than the rule declared; RECONCILE_VALUE_NOT_NUMERIC and RECONCILE_VALUE_NOT_DATE when a typed rule meets a value it cannot read, which is a refusal rather than a silent conflict because a parse failure and a real disagreement are different findings; RECONCILE_TOLERANCE_INVALID for a negative tolerance or one sent to a rule that does not read it; RECONCILE_POLICY_UNSUPPORTED for a policy outside its members; RECONCILE_WORK_REFUSED above 200000000 comparison bytes, with both numbers; and RECONCILE_OUTPUT_TOO_LARGE when the report would not fit the inline response ceiling.

Context footprint

Returns indexes for matches and unmatched rows, and for conflicts the failing rule with two values capped at 128 code points each. A successful call costs 3 execution credits.

When to use it

When two systems hold what should be the same records and you need the disagreements, row by row and field by field.

When the two sides share no reliable identifier — there is nothing here to block on.

Scope

Note
Matches two tables on keys and reports field-level disagreements.

Non-goals

  • Does not fuzzy-match keys or values
  • Does not coerce types when comparing keys
  • Does not merge, correct or choose between two disagreeing rows
  • Does not score how similar two rows are

Properties

IdempotentRetry safeStreaming supportedFile input supportedRequires external service

Capabilities

data-transformationvalidation

Cost & billing

Flat rate per invocation

MetercallsBillable onsuccessCost per call3 credits

File handling

Accepts file_refProduces file_refMax input2 MB

How we handle your data

Sensitive output possible
What this means

This skill may receive sensitive content and may return submitted, transformed, extracted, or reformatted content.

Squillify guarantees
  • Runs inside Squillify
  • This skill runs inside Squillify.
  • Payloads are not sent to an external service by this skill.
  • Squillify does not permanently store submitted payloads.
  • Telemetry records metadata only, not submitted content.
  • Submitted payloads are not used for training.
  • Submitted payloads are not reviewed by humans.
  • This skill does not target sensitive data.
  • The response may contain submitted or transformed content.
Your responsibility

Squillify does not determine whether submitted content is sensitive. Your application is responsible for deciding what data it is allowed to submit. Your application is responsible for securing the response.

Read the Microskill Data Handling Policy

Input schema

FieldTypeRequiredDescription
leftarrayYesBounded array of 1 through 50000 left-side row objects keyed by column name.
rightarrayYesBounded array of 1 through 50000 right-side row objects keyed by column name.
keysarrayYesBounded array of 1 through 8 closed blocking-key pairs compared as text with no type coercion.
rulesarrayYesBounded array of 1 through 64 closed comparison rules with left, right, kind and optional tolerance.
duplicateKeyPolicystringNoHow crowded key buckets are handled; omit it for PAIR_BY_INPUT_ORDER.
unmatchedPolicystringNoWhich unmatched rows are listed; omit it for REPORT.

Output schema

FieldTypeRequiredDescription
matchesarrayYesBounded array of 0 through 50000 closed matching pairs with leftIndex, rightIndex and the blocking key.
conflictsarrayYesBounded array of 0 through 50000 closed conflicting pairs with leftIndex, rightIndex and the failing rule.
unmatchedarrayYesBounded array of 0 through 50000 closed unmatched rows naming the side and the original index.
summaryobjectYesClosed complete counts even when unmatchedPolicy narrows the listed rows.
workobjectYesClosed admission decision in comparison bytes with projected size and the budget ceiling.

API contract

POST /v1/execute
curl
curl -sS -X POST "$BASE/v1/execute" -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' -d '{"skillKey":"data.reconcile_records","input":{"left":[{"id":"A1","amount":"1234.50"}],"right":[{"id":"A1","amount":"1234.05"}],"keys":[{"left":"id"}],"rules":[{"left":"amount","rule":"NUMERIC_TOLERANCE","tolerance":0.01}]}}'
Integrations
TypeScript SDK
import { Squillify } from "@squillify/sdk";

const sq = new Squillify({ apiKey: process.env.SQUILLIFY_API_KEY });

const { output } = await sq.execute("data.reconcile_records", {"right":[{"id":"A1","amount":"1234.05"}],"keys":[{"left":"id"}],"left":[{"id":"A1","amount":"1234.50"}],"rules":[{"tolerance":0.01,"left":"amount","rule":"NUMERIC_TOLERANCE"}]});

Errors

CodeHTTPRetryableDescription
VALIDATION_ERROR400NoThe request payload failed schema validation.
UNAUTHORIZED401NoMissing or invalid API key.
SKILL_NOT_ACCESSIBLE403NoThe API key is not granted access to this skill.
SKILL_NOT_AVAILABLE_FOR_PLAN403NoThis skill is not included in your current plan.
PLAN_ELIGIBILITY_UNAVAILABLE503YesPlan eligibility could not be decided. Retry after backoff.
SKILL_NOT_FOUND404NoThe requested skill or version does not exist.
TENANT_RATE_LIMITED429YesRequest rate limit exceeded. Retry after backoff.
QUOTA_EXCEEDED429YesPlan quota exhausted. Retryable once the quota period resets.
EXECUTION_TIMEOUT504YesExecution exceeded the allowed time budget.
RECONCILE_ROWS_INVALID400NoThe request payload failed this skill's input validation.
RECONCILE_ROW_LIMIT_EXCEEDED429NoA size, quota, rate, or time limit was exceeded.
RECONCILE_KEYS_INVALID400NoThe request payload failed this skill's input validation.
RECONCILE_KEY_LIMIT_EXCEEDED429NoA size, quota, rate, or time limit was exceeded.
RECONCILE_KEY_NOT_UNIQUE412NoThe input is valid but cannot be processed as-is.
RECONCILE_RULES_INVALID400NoThe request payload failed this skill's input validation.
RECONCILE_RULE_UNSUPPORTED400NoThe skill does not support the requested variant.
RECONCILE_RULE_LIMIT_EXCEEDED429NoA size, quota, rate, or time limit was exceeded.
RECONCILE_VALUE_LENGTH_INVALID400NoThe request payload failed this skill's input validation.
RECONCILE_VALUE_NOT_NUMERIC400NoThe request payload failed this skill's input validation.
RECONCILE_VALUE_NOT_DATE400NoThe request payload failed this skill's input validation.
RECONCILE_TOLERANCE_INVALID400NoThe request payload failed this skill's input validation.
RECONCILE_POLICY_UNSUPPORTED400NoThe skill does not support the requested variant.
RECONCILE_WORK_REFUSED429NoA size, quota, rate, or time limit was exceeded.
RECONCILE_OUTPUT_TOO_LARGE429NoA size, quota, rate, or time limit was exceeded.

Other limits

LimitValue
Recommended inline bytes32 KiB

Limitations

  • Does not fuzzy-match keys or values
  • Does not coerce types when comparing keys
  • Does not merge, correct or choose between two disagreeing rows
  • Does not score how similar two rows are
  • Matching records that have no shared identifier
  • Deduplicating one table — data.detect_duplicates does that

How to call

POST /v1/execute (requires authentication)

Request bodyjson
{
  "skillKey": "data.reconcile_records",
  "input": {
    "right": [
      {
        "id": "A1",
        "amount": "1234.05"
      }
    ],
    "keys": [
      {
        "left": "id"
      }
    ],
    "left": [
      {
        "id": "A1",
        "amount": "1234.50"
      }
    ],
    "rules": [
      {
        "tolerance": 0.01,
        "left": "amount",
        "rule": "NUMERIC_TOLERANCE"
      }
    ]
  }
}

Examples

Two ledgers where one amount disagrees

Inputjson
{
  "right": [
    {
      "id": "A1",
      "amount": "1234.05"
    }
  ],
  "keys": [
    {
      "left": "id"
    }
  ],
  "left": [
    {
      "id": "A1",
      "amount": "1234.50"
    }
  ],
  "rules": [
    {
      "tolerance": 0.01,
      "left": "amount",
      "rule": "NUMERIC_TOLERANCE"
    }
  ]
}
Expected outputjson
{
  "work": {
    "projected": 1026,
    "budget": 200000000
  },
  "conflicts": [
    {
      "rightIndex": 0,
      "key": [
        "A1"
      ],
      "failures": [
        {
          "rightField": "amount",
          "leftValue": "1234.50",
          "delta": "0.45",
          "truncated": false,
          "leftField": "amount",
          "rightValue": "1234.05",
          "rule": "NUMERIC_TOLERANCE"
        }
      ],
      "leftIndex": 0
    }
  ],
  "unmatched": [],
  "matches": [],
  "summary": {
    "ambiguousKeys": 0,
    "truncated": false,
    "conflictCount": 1,
    "unmatchedLeft": 0,
    "unmatchedRight": 0,
    "leftCount": 1,
    "matchCount": 0,
    "rightCount": 1,
    "candidatePairs": 1
  }
}

A matched status on both systems

Inputjson
{
  "right": [
    {
      "status": "paid",
      "id": "A1"
    }
  ],
  "keys": [
    {
      "left": "id"
    }
  ],
  "left": [
    {
      "status": "paid",
      "id": "A1"
    }
  ],
  "rules": [
    {
      "left": "status",
      "rule": "EXACT"
    }
  ]
}
Expected outputjson
{
  "work": {
    "projected": 1026,
    "budget": 200000000
  },
  "conflicts": [],
  "unmatched": [],
  "matches": [
    {
      "leftIndex": 0,
      "key": [
        "A1"
      ],
      "rightIndex": 0
    }
  ],
  "summary": {
    "ambiguousKeys": 0,
    "truncated": false,
    "conflictCount": 0,
    "unmatchedLeft": 0,
    "unmatchedRight": 0,
    "leftCount": 1,
    "matchCount": 1,
    "rightCount": 1,
    "candidatePairs": 1
  }
}
  • Comparing a bank statement against a ledger export
  • Checking a migrated table against its source
  • Finding which invoice amounts disagree between two systems

Avoid for

  • Matching records that have no shared identifier
  • Deduplicating one table — data.detect_duplicates does that

Composable with

Safety & privacy

  • Runs without calling any external service.
  • Your payloads are never stored permanently.
  • Telemetry captures metadata only — never your payload contents.
  • We never train models on your payloads.
  • No human reviews your payloads.

Tags

validationreconciliationdata