Consent Record Validator
governance.consent_record_validate
Overview
You send one consent record and the policy it should satisfy, plus the instant to evaluate it at, and we report what the record says: which of the six completeness elements are present, whether the lawful basis is one your policy permits, whether the consent had expired or been withdrawn at that instant, and the window it covered. Findings are closed codes with a field path and nothing else — the subject reference never appears anywhere in the response, not in a message, not in a count key. We validate the record you sent against the policy you sent; we certify no regulation and grant no permission to process anything.
A consent store is only as good as the records in it, and the ways a record goes wrong are boring and repetitive: no lawful basis, no withdrawal state, an expiry that disagrees with the duration next to it, a withdrawal dated before the grant. Checking that by hand across a store does not scale, and writing the checklist a fourth time in a fourth service is how two services disagree about the same record.
Reliability
Fully deterministic and independent of the machine: the same record, policy and asOf give the same verdict whatever the default locale, zone or charset. Two design choices are worth knowing before you rely on the output. First, a record stating both expiresAt and grantedAt plus duration that disagree by even one second is refused rather than resolved, because picking a winner would let a broken record validate cleanly under whichever precedence you did not expect. Second, consent is valid over the half-open window from grantedAt up to but not including the effective expiry, so an asOf exactly equal to the expiry is expired, and the same rule makes an asOf equal to withdrawnAt withdrawn. Severity is fixed per code rather than varied by policy, and the require flags decide whether an issue is raised at all, not how badly. Refusals, and what to do about each. GOV_CONSENT_INPUT_TOO_LARGE means the declared request is past the 65,536-byte inline limit, so send fewer purposes. GOV_CONSENT_RECORD_INVALID means a record member is the wrong type, empty where a value is needed, or outside its charset, and the message names the path. GOV_CONSENT_FIELD_UNKNOWN means a property we do not declare turned up, so delete it rather than assuming it was read. GOV_CONSENT_POLICY_INVALID means the policy block is malformed — a non-string purpose id or an out-of-range maxDurationDays. GOV_CONSENT_PURPOSE_LIMIT_EXCEEDED means more than 64 purposes arrived in the record or the policy. GOV_CONSENT_PURPOSE_ID_DUPLICATE means one purpose id appears twice, which makes its granted flag ambiguous, so merge them. GOV_CONSENT_LAWFUL_BASIS_UNSUPPORTED means a basis outside the six Article 6(1) members arrived in the record or in allowedLawfulBases. GOV_CONSENT_WITHDRAWAL_STATE_UNSUPPORTED means withdrawalState is not ACTIVE, WITHDRAWN or SUPERSEDED. GOV_CONSENT_EVIDENCE_METHOD_UNSUPPORTED means evidence.method is outside the five members; send UNKNOWN rather than inventing a label. GOV_CONSENT_WITHDRAWN_AT_MISSING means the state says WITHDRAWN with no instant, which cannot be placed on the timeline, so supply withdrawnAt. GOV_CONSENT_TIMESTAMP_INVALID means a record instant carries no explicit offset or does not parse, so add the offset. GOV_CONSENT_DURATION_INVALID means the duration is not an ISO-8601 duration between P1D and the P36500D equivalent. GOV_CONSENT_EXPIRY_CONFLICT means expiresAt and grantedAt plus duration state two different instants, so correct the record rather than asking us to choose. GOV_CONSENT_ASOF_INVALID means asOf carries no explicit offset or does not parse. GOV_CONSENT_TIMEZONE_REQUIRED means the duration carries a calendar component, where a day is 23, 24 or 25 hours, so send policy.timeZone. GOV_CONSENT_TIMEZONE_UNKNOWN means the zone is not an IANA id such as Europe/Amsterdam. GOV_CONSENT_WORK_REFUSED means the projected checks went past the budget of 1,024, and the refusal reports both numbers.
Context footprint
A verdict, a state, a bounded issue list of codes and paths, and small counts. No record value comes back. A successful call costs 1 execution credit.
When to use it
When you have a consent record and a written policy, and you want the record's completeness and its state at a particular instant, with the field paths to fix.
When you need a runtime permission decision, a compliance certificate, or an evaluation against a clock we do not have.
Scope
Non-goals
- Does not certify compliance with GDPR or any other regulation — it checks the record against the policy you sent
- Does not decide whether processing may proceed; nothing here is an authorization decision the platform acts on
- Does not store, index or forward the record
- Does not read a clock — expiry and withdrawal are evaluated at your asOf
- Does not echo the subject reference, a purpose id or a timestamp you sent
Properties
Capabilities
Cost & billing
Flat rate per invocation
File handling
How we handle your data
What this means
This skill may receive sensitive content, but it does not target sensitive data and returns only a structured non-sensitive result.
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 |
|---|---|---|---|
| record | object | Yes | Closed consent record whose members are all optional so incompleteness is reported rather than refused. |
| policy | object | No | Optional completeness policy for required purposes, permitted bases, max duration, and which fields must be present; omit it for the published completeness defaults. |
| asOf | string | Yes | Instant expiry and withdrawal are evaluated at, as ISO-8601 with an explicit offset. |
Output schema
| Field | Type | Required | Description |
|---|---|---|---|
| valid | boolean | Yes | True when no ERROR-severity issue is present; a warning does not fail the record. |
| state | string | Yes | Overall record state after expiry and withdrawal ranking. |
| issues | array | Yes | Up to 200 findings of code, severity, and fieldPath; empty on a complete in-date record. |
| effectiveFrom | string | No | Grant instant in UTC; omitted when the record declares none. |
| effectiveUntil | string | No | Earlier of withdrawal and expiry in UTC; omitted when neither is declared. |
| summary | object | Yes | Closed counters for purposes, lawful basis, asOf, optional time zone, issue counts, checks applied, 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.consent_record_validate","input":{"record":{"subjectRef":"subj_9d41","purposes":[{"id":"marketing_email","granted":true}],"lawfulBasis":"CONSENT","grantedAt":"2025-03-01T08:00:00Z","expiresAt":"2027-03-01T08:00:00Z","withdrawalState":"ACTIVE"},"asOf":"2026-07-29T12:00:00Z"}}'import { Squillify } from "@squillify/sdk";
const sq = new Squillify({ apiKey: process.env.SQUILLIFY_API_KEY });
const { output } = await sq.execute("governance.consent_record_validate", {"asOf":"2026-07-29T12:00:00Z","policy":{"allowedLawfulBases":["CONSENT"],"requiredPurposes":["marketing_email","product_analytics"],"maxDurationDays":730,"requireExplicitExpiry":true,"requireWithdrawalMechanism":true,"requireNoticeVersion":true},"record":{"subjectRef":"subj_9d41","purposes":[{"id":"marketing_email","granted":true},{"id":"product_analytics","granted":true}],"lawfulBasis":"CONSENT","grantedAt":"2025-03-01T08:00:00Z","expiresAt":"2027-03-01T08:00:00Z","withdrawalState":"ACTIVE","noticeVersion":"notice-2025.03","evidence":{"method":"EXPLICIT_OPT_IN"},"withdrawalMechanism":"SELF_SERVICE"}});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_CONSENT_INPUT_TOO_LARGE | 429 | No | A size, quota, rate, or time limit was exceeded. |
| GOV_CONSENT_RECORD_INVALID | 400 | No | The request payload failed this skill's input validation. |
| GOV_CONSENT_FIELD_UNKNOWN | 400 | No | The request payload failed this skill's input validation. |
| GOV_CONSENT_POLICY_INVALID | 400 | No | The request payload failed this skill's input validation. |
| GOV_CONSENT_PURPOSE_LIMIT_EXCEEDED | 429 | No | A size, quota, rate, or time limit was exceeded. |
| GOV_CONSENT_PURPOSE_ID_DUPLICATE | 400 | No | The request payload failed this skill's input validation. |
| GOV_CONSENT_LAWFUL_BASIS_UNSUPPORTED | 400 | No | The skill does not support the requested variant. |
| GOV_CONSENT_WITHDRAWAL_STATE_UNSUPPORTED | 400 | No | The skill does not support the requested variant. |
| GOV_CONSENT_EVIDENCE_METHOD_UNSUPPORTED | 400 | No | The skill does not support the requested variant. |
| GOV_CONSENT_WITHDRAWN_AT_MISSING | 400 | No | The request payload failed this skill's input validation. |
| GOV_CONSENT_TIMESTAMP_INVALID | 400 | No | The request payload failed this skill's input validation. |
| GOV_CONSENT_DURATION_INVALID | 400 | No | The request payload failed this skill's input validation. |
| GOV_CONSENT_EXPIRY_CONFLICT | 400 | No | The request payload failed this skill's input validation. |
| GOV_CONSENT_ASOF_INVALID | 400 | No | The request payload failed this skill's input validation. |
| GOV_CONSENT_TIMEZONE_REQUIRED | 400 | No | The request payload failed this skill's input validation. |
| GOV_CONSENT_TIMEZONE_UNKNOWN | 400 | No | The request payload failed this skill's input validation. |
| GOV_CONSENT_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 certify compliance with GDPR or any other regulation — it checks the record against the policy you sent
- Does not decide whether processing may proceed; nothing here is an authorization decision the platform acts on
- Does not store, index or forward the record
- Does not read a clock — expiry and withdrawal are evaluated at your asOf
- Does not echo the subject reference, a purpose id or a timestamp you sent
- Gating a processing decision at runtime
- Proving a regulator's requirements are met
- Validating a consent record whose expiry you want us to guess from today's date
How to call
POST /v1/execute (requires authentication)
{
"skillKey": "governance.consent_record_validate",
"input": {
"asOf": "2026-07-29T12:00:00Z",
"policy": {
"allowedLawfulBases": [
"CONSENT"
],
"requiredPurposes": [
"marketing_email",
"product_analytics"
],
"maxDurationDays": 730,
"requireExplicitExpiry": true,
"requireWithdrawalMechanism": true,
"requireNoticeVersion": true
},
"record": {
"subjectRef": "subj_9d41",
"purposes": [
{
"id": "marketing_email",
"granted": true
},
{
"id": "product_analytics",
"granted": true
}
],
"lawfulBasis": "CONSENT",
"grantedAt": "2025-03-01T08:00:00Z",
"expiresAt": "2027-03-01T08:00:00Z",
"withdrawalState": "ACTIVE",
"noticeVersion": "notice-2025.03",
"evidence": {
"method": "EXPLICIT_OPT_IN"
},
"withdrawalMechanism": "SELF_SERVICE"
}
}
}Examples
A complete, in-date, active record under a two-purpose policy
{
"asOf": "2026-07-29T12:00:00Z",
"policy": {
"allowedLawfulBases": [
"CONSENT"
],
"requiredPurposes": [
"marketing_email",
"product_analytics"
],
"maxDurationDays": 730,
"requireExplicitExpiry": true,
"requireWithdrawalMechanism": true,
"requireNoticeVersion": true
},
"record": {
"subjectRef": "subj_9d41",
"purposes": [
{
"id": "marketing_email",
"granted": true
},
{
"id": "product_analytics",
"granted": true
}
],
"lawfulBasis": "CONSENT",
"grantedAt": "2025-03-01T08:00:00Z",
"expiresAt": "2027-03-01T08:00:00Z",
"withdrawalState": "ACTIVE",
"noticeVersion": "notice-2025.03",
"evidence": {
"method": "EXPLICIT_OPT_IN"
},
"withdrawalMechanism": "SELF_SERVICE"
}
}{
"effectiveUntil": "2027-03-01T08:00:00Z",
"effectiveFrom": "2025-03-01T08:00:00Z",
"issues": [],
"summary": {
"purposeCount": 2,
"grantedPurposeCount": 2,
"lawfulBasis": "CONSENT",
"asOf": "2026-07-29T12:00:00Z",
"errorCount": 0,
"warningCount": 0,
"infoCount": 0,
"checksApplied": 40,
"truncated": false
},
"valid": true,
"state": "VALID"
}Withdrawn and expired at the same instant, reporting both
{
"asOf": "2026-07-29T12:00:00Z",
"record": {
"subjectRef": "subj_9d41",
"grantedAt": "2024-01-15T09:00:00Z",
"expiresAt": "2026-01-15T09:00:00Z",
"withdrawalState": "WITHDRAWN",
"purposes": [
{
"id": "marketing_email",
"granted": true
}
],
"withdrawnAt": "2025-06-01T10:30:00Z",
"lawfulBasis": "CONSENT"
}
}{
"effectiveUntil": "2025-06-01T10:30:00Z",
"effectiveFrom": "2024-01-15T09:00:00Z",
"issues": [
{
"code": "CONSENT_WITHDRAWN",
"severity": "INFO",
"fieldPath": "record.withdrawnAt"
},
{
"code": "CONSENT_EXPIRED",
"severity": "INFO",
"fieldPath": "record.expiresAt"
}
],
"summary": {
"purposeCount": 1,
"grantedPurposeCount": 1,
"lawfulBasis": "CONSENT",
"asOf": "2026-07-29T12:00:00Z",
"errorCount": 0,
"warningCount": 0,
"infoCount": 2,
"checksApplied": 28,
"truncated": false
},
"valid": true,
"state": "WITHDRAWN"
}Recommended use cases
- Screen a consent store for records missing an element your own policy requires
- Reconstruct the lawful-processing window of one record, and see whether withdrawal or expiry ended it
Avoid for
- Gating a processing decision at runtime
- Proving a regulator's requirements are met
- Validating a consent record whose expiry you want us to guess from today's date
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.