Data Classifier
governance.data_classify
Overview
No content is read to decide what your data is. You send metadata — an object id, a system of record, a domain, field names, tags, small scalar attributes — plus the content assertions you are willing to make, and your own table of labels and rules. We evaluate the table and return one label, the rule that decided, and the rules that matched but lost. An unasserted hint is a third state, not a false: by default a rule that asked about a hint nobody asserted does not fire, because collapsing nobody said into no is how a document full of credentials ends up labelled public. There is no built-in dictionary of suspicious field names — matching is against your literals only.
A classification standard is a table, and applying it by hand across a catalogue produces a different answer every time somebody new reads the document. This applies your table mechanically and tells you which line decided, so a reviewer can check the decision against the standard rather than trusting a model's opinion about your data.
Reliability
Fully deterministic and independent of the machine: the same metadata, hints and policy give the same label whatever the default locale, zone or charset. The one property worth checking yourself, because it is the whole boundary of this skill: change every attribute string to something completely different while leaving hints and policy alone, and the label does not move. Matching is exact equality after NFC normalisation and surrounding-whitespace removal, with no case folding and no pattern of any kind, so Confidential and confidential are different literals. Precedence is a published total order — highest priority, then the higher label rank so the more restrictive wins, then the lexicographically smallest rule id — which means reordering your policy file cannot change the answer. Refusals, and what to do about each. GOV_CLASSIFY_INPUT_TOO_LARGE means the declared request is past the 65,536-byte inline limit, so trim the metadata or split the policy. GOV_CLASSIFY_METADATA_INVALID means a metadata member is the wrong type, past its length, outside the identifier charset, or over its element cap. GOV_CLASSIFY_FIELD_UNKNOWN means a property we do not declare turned up, so delete it rather than assuming it was read. GOV_CLASSIFY_FREE_TEXT_REJECTED means an attribute string went past 256 characters, which is free text, and reading it to decide a label is exactly what this skill does not do. GOV_CLASSIFY_HINTS_INVALID means a hint value is not a boolean; omit the key rather than sending null, because absent is a distinct third state. GOV_CLASSIFY_HINT_KEY_UNSUPPORTED means a hint key outside the nine arrived, and a misspelling is refused rather than dropped, because a dropped hint returns a more permissive label than you asked for. GOV_CLASSIFY_HINT_SOURCE_MISSING means hints arrived without source, and a stored classification has to record whose claim it rested on. GOV_CLASSIFY_HINT_SOURCE_UNSUPPORTED means source is outside the four members. GOV_CLASSIFY_POLICY_INVALID means the policy is malformed — fewer than two labels, a rank out of range, an unknown metadata path, or a non-string literal. GOV_CLASSIFY_LABEL_LIMIT_EXCEEDED means more than 32 labels arrived. GOV_CLASSIFY_LABEL_ID_DUPLICATE means one label id is declared twice. GOV_CLASSIFY_LABEL_RANK_DUPLICATE means two labels share a rank, which leaves the more-restrictive tie-break undefined. GOV_CLASSIFY_RULE_LIMIT_EXCEEDED means more than 64 rules arrived. GOV_CLASSIFY_RULE_ID_DUPLICATE means one rule id is declared twice, and the id is the final tie-break, so precedence would be undefined. GOV_CLASSIFY_PREDICATE_UNSUPPORTED means a predicate outside the closed set arrived; there is no pattern, expression or nested condition in this grammar, and a disjunction is a second rule. GOV_CLASSIFY_PREDICATE_LIMIT_EXCEEDED means a rule declared more than 8 predicates or a predicate more than 32 literals. GOV_CLASSIFY_LABEL_UNDECLARED means a rule classifies to a label that is not in labels. GOV_CLASSIFY_FALLBACK_UNDECLARED means fallbackClassification names a label that is not in labels. GOV_CLASSIFY_WORK_REFUSED means the projected predicate evaluations went past the budget of 1,024, and the refusal reports both numbers.
Context footprint
One label, a bounded reason list of rule ids and codes, and small counts. No metadata value, tag or literal comes back. A successful call costs 1 execution credit.
When to use it
When you already know what an object contains — or are willing to assert it — and you want your written standard applied to it consistently and auditably.
When you do not yet know what the data contains. Discovery is a different job with a different error profile, and this skill will not guess.
Scope
Non-goals
- Does not read, scan or sample content to decide what the data is
- Does not carry a built-in dictionary of suspicious field names — recognising ssn or iban would be inference about content by proxy
- Does not detect personal data; data.detect_pii_columns is the heuristic skill for that, with the limitations that come with it
- Does not enforce a label, apply a control, or make an access decision the platform acts on
- Does not default the fallback label, because a classifier with an implicit default silently labels everything it does not understand
- Does not store the metadata, the assertions, the policy or the label it returned — nothing is retained once the response is written
Properties
Capabilities
Cost & billing
Flat rate per invocation
File handling
How we handle your data
What this means
This skill only accepts structured technical values such as numbers, booleans, enums, timestamps, or identifiers. It does not process free-form text, files, or documents.
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 does not contain submitted 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.
Input schema
| Field | Type | Required | Description |
|---|---|---|---|
| metadata | object | No | Optional object metadata used only as literals: ids, domain, field names, tags, and bounded scalar attributes. Omit it to leave metadata predicates unmatched. |
| contentHints | object | No | Optional closed content assertions from nine boolean keys plus a required source once any hint is present; omit the whole object when no hint is asserted. |
| policy | object | Yes | Closed decision table with 2 to 32 labels, 1 to 64 rules, a required fallback label, and unasserted-hint behaviour. |
Output schema
| Field | Type | Required | Description |
|---|---|---|---|
| classification | string | Yes | Resolved label id, always one you declared. |
| reasons | array | Yes | Up to 64 reason entries naming matched, superseded, failed, unasserted, and fallback outcomes. |
| decidingRuleId | string | No | Rule that won precedence; omitted when the fallback applied. |
| matchedRuleIds | array | Yes | Every rule whose conjunction held, in declaration order, from 0 through 64 ids. |
| summary | object | Yes | Closed classification counters including optional objectId, label and rule counts, hint source, fallback flag, and truncation. |
API contract
POST /v1/executecurl -sS -X POST "$BASE/v1/execute" -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' -d '{"skillKey":"governance.data_classify","input":{"metadata":{"objectId":"tbl_patient_visits","dataDomain":"clinical"},"contentHints":{"containsHealthData":true,"source":"HUMAN_REVIEW"},"policy":{"labels":[{"id":"public","rank":0},{"id":"restricted","rank":3}],"rules":[{"id":"r_health","priority":10,"when":[{"op":"hintIsTrue","key":"containsHealthData"}],"classify":"restricted"}],"fallbackClassification":"public"}}}'import { Squillify } from "@squillify/sdk";
const sq = new Squillify({ apiKey: process.env.SQUILLIFY_API_KEY });
const { output } = await sq.execute("governance.data_classify", {"policy":{"labels":[{"id":"public","rank":0},{"id":"internal","rank":1},{"id":"confidential","rank":2},{"id":"restricted","rank":3}],"rules":[{"priority":20,"classify":"restricted","when":[{"key":"containsHealthData","op":"hintIsTrue"}],"id":"r_health"},{"priority":10,"classify":"confidential","when":[{"key":"containsPersonalData","op":"hintIsTrue"},{"literals":["clinical","hr"],"op":"domainIn"}],"id":"r_personal_clinical"},{"priority":0,"classify":"internal","when":[{"literals":["production"],"op":"tagIn"}],"id":"r_production_tag"}],"fallbackClassification":"internal","unassertedBehaviour":"RULE_DOES_NOT_FIRE"},"metadata":{"systemOfRecord":"ehr_primary","fieldNames":["patient_ref","visit_date","diagnosis_code"],"tags":["production"],"objectId":"tbl_patient_visits","dataDomain":"clinical"},"contentHints":{"containsPersonalData":true,"source":"HUMAN_REVIEW","containsHealthData":true}});Errors
| Code | HTTP | Retryable | Description |
|---|---|---|---|
| VALIDATION_ERROR | 400 | No | The request payload failed schema validation. |
| UNAUTHORIZED | 401 | No | Missing or invalid API key. |
| SKILL_NOT_ACCESSIBLE | 403 | No | The API key is not granted access to this skill. |
| SKILL_NOT_AVAILABLE_FOR_PLAN | 403 | No | This skill is not included in your current plan. |
| PLAN_ELIGIBILITY_UNAVAILABLE | 503 | Yes | Plan eligibility could not be decided. Retry after backoff. |
| SKILL_NOT_FOUND | 404 | No | The requested skill or version does not exist. |
| TENANT_RATE_LIMITED | 429 | Yes | Request rate limit exceeded. Retry after backoff. |
| QUOTA_EXCEEDED | 429 | Yes | Plan quota exhausted. Retryable once the quota period resets. |
| EXECUTION_TIMEOUT | 504 | Yes | Execution exceeded the allowed time budget. |
| GOV_CLASSIFY_INPUT_TOO_LARGE | 429 | No | A size, quota, rate, or time limit was exceeded. |
| GOV_CLASSIFY_METADATA_INVALID | 400 | No | The request payload failed this skill's input validation. |
| GOV_CLASSIFY_FIELD_UNKNOWN | 400 | No | The request payload failed this skill's input validation. |
| GOV_CLASSIFY_FREE_TEXT_REJECTED | 422 | No | The input was rejected as unsafe. |
| GOV_CLASSIFY_HINTS_INVALID | 400 | No | The request payload failed this skill's input validation. |
| GOV_CLASSIFY_HINT_KEY_UNSUPPORTED | 400 | No | The skill does not support the requested variant. |
| GOV_CLASSIFY_HINT_SOURCE_MISSING | 400 | No | The request payload failed this skill's input validation. |
| GOV_CLASSIFY_HINT_SOURCE_UNSUPPORTED | 400 | No | The skill does not support the requested variant. |
| GOV_CLASSIFY_POLICY_INVALID | 400 | No | The request payload failed this skill's input validation. |
| GOV_CLASSIFY_LABEL_LIMIT_EXCEEDED | 429 | No | A size, quota, rate, or time limit was exceeded. |
| GOV_CLASSIFY_LABEL_ID_DUPLICATE | 400 | No | The request payload failed this skill's input validation. |
| GOV_CLASSIFY_LABEL_RANK_DUPLICATE | 400 | No | The request payload failed this skill's input validation. |
| GOV_CLASSIFY_RULE_LIMIT_EXCEEDED | 429 | No | A size, quota, rate, or time limit was exceeded. |
| GOV_CLASSIFY_RULE_ID_DUPLICATE | 400 | No | The request payload failed this skill's input validation. |
| GOV_CLASSIFY_PREDICATE_UNSUPPORTED | 400 | No | The skill does not support the requested variant. |
| GOV_CLASSIFY_PREDICATE_LIMIT_EXCEEDED | 429 | No | A size, quota, rate, or time limit was exceeded. |
| GOV_CLASSIFY_LABEL_UNDECLARED | 400 | No | The request payload failed this skill's input validation. |
| GOV_CLASSIFY_FALLBACK_UNDECLARED | 400 | No | The request payload failed this skill's input validation. |
| GOV_CLASSIFY_WORK_REFUSED | 429 | No | A size, quota, rate, or time limit was exceeded. |
Other limits
| Limit | Value |
|---|---|
| Recommended inline bytes | 32 KiB |
Limitations
- Does not read, scan or sample content to decide what the data is
- Does not carry a built-in dictionary of suspicious field names — recognising ssn or iban would be inference about content by proxy
- Does not detect personal data; data.detect_pii_columns is the heuristic skill for that, with the limitations that come with it
- Does not enforce a label, apply a control, or make an access decision the platform acts on
- Does not default the fallback label, because a classifier with an implicit default silently labels everything it does not understand
- Does not store the metadata, the assertions, the policy or the label it returned — nothing is retained once the response is written
- Discovering sensitive data you have not already described
- Classifying a document by its text
- Enforcing the label you get back
How to call
POST /v1/execute (requires authentication)
{
"skillKey": "governance.data_classify",
"input": {
"policy": {
"labels": [
{
"id": "public",
"rank": 0
},
{
"id": "internal",
"rank": 1
},
{
"id": "confidential",
"rank": 2
},
{
"id": "restricted",
"rank": 3
}
],
"rules": [
{
"priority": 20,
"classify": "restricted",
"when": [
{
"key": "containsHealthData",
"op": "hintIsTrue"
}
],
"id": "r_health"
},
{
"priority": 10,
"classify": "confidential",
"when": [
{
"key": "containsPersonalData",
"op": "hintIsTrue"
},
{
"literals": [
"clinical",
"hr"
],
"op": "domainIn"
}
],
"id": "r_personal_clinical"
},
{
"priority": 0,
"classify": "internal",
"when": [
{
"literals": [
"production"
],
"op": "tagIn"
}
],
"id": "r_production_tag"
}
],
"fallbackClassification": "internal",
"unassertedBehaviour": "RULE_DOES_NOT_FIRE"
},
"metadata": {
"systemOfRecord": "ehr_primary",
"fieldNames": [
"patient_ref",
"visit_date",
"diagnosis_code"
],
"tags": [
"production"
],
"objectId": "tbl_patient_visits",
"dataDomain": "clinical"
},
"contentHints": {
"containsPersonalData": true,
"source": "HUMAN_REVIEW",
"containsHealthData": true
}
}
}Examples
Health data with an asserted hint reaching the restricted label
{
"policy": {
"labels": [
{
"id": "public",
"rank": 0
},
{
"id": "internal",
"rank": 1
},
{
"id": "confidential",
"rank": 2
},
{
"id": "restricted",
"rank": 3
}
],
"rules": [
{
"priority": 20,
"classify": "restricted",
"when": [
{
"key": "containsHealthData",
"op": "hintIsTrue"
}
],
"id": "r_health"
},
{
"priority": 10,
"classify": "confidential",
"when": [
{
"key": "containsPersonalData",
"op": "hintIsTrue"
},
{
"literals": [
"clinical",
"hr"
],
"op": "domainIn"
}
],
"id": "r_personal_clinical"
},
{
"priority": 0,
"classify": "internal",
"when": [
{
"literals": [
"production"
],
"op": "tagIn"
}
],
"id": "r_production_tag"
}
],
"fallbackClassification": "internal",
"unassertedBehaviour": "RULE_DOES_NOT_FIRE"
},
"metadata": {
"systemOfRecord": "ehr_primary",
"fieldNames": [
"patient_ref",
"visit_date",
"diagnosis_code"
],
"tags": [
"production"
],
"objectId": "tbl_patient_visits",
"dataDomain": "clinical"
},
"contentHints": {
"containsPersonalData": true,
"source": "HUMAN_REVIEW",
"containsHealthData": true
}
}{
"matchedRuleIds": [
"r_health",
"r_personal_clinical",
"r_production_tag"
],
"reasons": [
{
"ruleId": "r_health",
"code": "CLASSIFY_RULE_MATCHED"
},
{
"ruleId": "r_personal_clinical",
"code": "CLASSIFY_RULE_SUPERSEDED"
},
{
"ruleId": "r_production_tag",
"code": "CLASSIFY_RULE_SUPERSEDED"
}
],
"classification": "restricted",
"decidingRuleId": "r_health",
"summary": {
"objectId": "tbl_patient_visits",
"labelCount": 4,
"ruleCount": 3,
"matchedRuleCount": 3,
"assertionSource": "HUMAN_REVIEW",
"unassertedHintCount": 0,
"fallbackApplied": false,
"unassertedBehaviour": "RULE_DOES_NOT_FIRE",
"truncated": false
}
}No rule fires, so the required fallback applies
{
"policy": {
"labels": [
{
"id": "public",
"rank": 0
},
{
"id": "internal",
"rank": 1
},
{
"id": "confidential",
"rank": 2
},
{
"id": "restricted",
"rank": 3
}
],
"rules": [
{
"priority": 20,
"classify": "restricted",
"when": [
{
"key": "containsHealthData",
"op": "hintIsTrue"
}
],
"id": "r_health"
},
{
"priority": 10,
"classify": "confidential",
"when": [
{
"key": "containsPersonalData",
"op": "hintIsTrue"
},
{
"literals": [
"clinical",
"hr"
],
"op": "domainIn"
}
],
"id": "r_personal_clinical"
},
{
"priority": 0,
"classify": "internal",
"when": [
{
"literals": [
"production"
],
"op": "tagIn"
}
],
"id": "r_production_tag"
}
],
"fallbackClassification": "internal",
"unassertedBehaviour": "RULE_DOES_NOT_FIRE"
},
"metadata": {
"objectId": "tbl_exchange_rates",
"dataDomain": "reference"
}
}{
"classification": "internal",
"reasons": [
{
"code": "CLASSIFY_NO_RULE_MATCHED"
},
{
"code": "CLASSIFY_FALLBACK_APPLIED"
},
{
"ruleId": "r_health",
"code": "CLASSIFY_HINT_UNASSERTED",
"predicateIndex": 0
},
{
"ruleId": "r_personal_clinical",
"code": "CLASSIFY_HINT_UNASSERTED",
"predicateIndex": 0
},
{
"ruleId": "r_production_tag",
"code": "CLASSIFY_PREDICATE_FAILED",
"predicateIndex": 0
}
],
"matchedRuleIds": [],
"summary": {
"objectId": "tbl_exchange_rates",
"labelCount": 4,
"ruleCount": 3,
"matchedRuleCount": 0,
"unassertedHintCount": 2,
"fallbackApplied": true,
"unassertedBehaviour": "RULE_DOES_NOT_FIRE",
"truncated": false
}
}Recommended use cases
- Apply one written classification standard consistently across a catalogue of objects, from the metadata your catalogue already holds
- Record which claim a stored classification rested on, by way of the mandatory assertion source
Avoid for
- Discovering sensitive data you have not already described
- Classifying a document by its text
- Enforcing the label you get back
Related skills
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.