Universal Field Validator

@UVALIDATE

@UVALIDATE is a REDCap action tag that validates a field against a check character (ISO/IEC 7064, Damm, Verhoeff, Luhn) or a regex format pattern, live as the value is typed.

@UVALIDATE is the module's flagship rule. It recomputes a field's check character, tests its format against a regex pattern, or both — and reports the two kinds of failure separately, so the person entering data knows which mistake they made.

Syntax

The tag has three forms, from shortest to most explicit. All three go in a field's Field Annotation box in the Online Designer.

@UVALIDATE                                        bare — all defaults (ISO 7064 Mod 37,36)
@UVALIDATE=verhoeff                               short — pick the algorithm
@UVALIDATE={"algorithm":"verhoeff","blockSave":"hard"}    JSON — every option

Algorithm shorthands

@UVALIDATE=verhoeff
@UVALIDATE=9710                 ISO 7064 Mod 97,10
@UVALIDATE=3736                 ISO 7064 Mod 37,36 (the default)
@UVALIDATE=gs1                  GS1 / GTIN / EAN / UPC barcodes
@UVALIDATE=isbn                 ISBN-10 weighted Mod-11

Format patterns instead of a check character

Set algorithm to none (or regex) and give a pattern. In stock REDCap a custom regex validation type has to be added server-wide by an administrator; here a project designer sets it per rule.

@UVALIDATE={"algorithm":"none","pattern":"FC[0-9]{4}"}
@UVALIDATE={"algorithm":"regex","pattern":"TB-[0-9]{6}","blockSave":"hard"}

Both at once

@UVALIDATE={"algorithm":"iso7064_mod37_36","pattern":"TB[A-Z]{3}-[0-9]{5}[0-9A-Z]"}
@UVALIDATE={"algorithm":"mod11_10","source":"digits_only"}
@UVALIDATE={"algorithm":"3736","strip":"-/ _|\\"}

Pooled fields

One box holding several IDs — space-separated, comma-separated, or run together with no separator — is split at the boundaries where the check character verifies, then shown as one chip per member.

Several ID formats in one field

Give the rule a list of alternates instead of one pattern/algorithm. A value — or a token inside a pooled box — is valid if any one entry accepts it, so IDs from several studies can share a field without the ones that carry a check character losing it. Each entry needs its own pattern; a pooled rule also needs lengths on every entry.

@UVALIDATE={"type":"pooled","strip":"-","blockSave":"hard","alternates":[
  {"label":"GHIT",      "pattern":"FC[1-9]-[0-9]{4}",        "algorithm":"none","lengths":[8]},
  {"label":"START4KIDS","pattern":"SK[1-9]-[0-9]{4}[0-9A-Z]","algorithm":"3736","lengths":[9]},
  {"label":"DARETB",    "pattern":"DT[1-9]-[0-9]{5}[0-9A-Z]","algorithm":"3736","lengths":[10]},
  {"label":"SCREENTB",  "pattern":"ST[1-9]-[0-9]{5}[0-9A-Z]","algorithm":"3736","lengths":[10]}]}

Entry keys: pattern (required), algorithm, source, strip, lengths, label; anything omitted falls back to the rule-level value. Up to 8 entries per rule. When alternates is used the rule-level pattern, idLengths, idMinLen and idMaxLen must be left unset.

A value that matches the pattern of a check-bearing entry must pass that entry's check character; format-only entries are tried only when no check-bearing pattern matches. Combinations that could report a mis-scan as a clean ID are refused when the rule is saved, each with a message naming the entry and the fix: lengths where one is the sum of two or more others, a format-only pattern that accepts a value a check-bearing pattern also accepts (in a pooled rule, between entries sharing a length), and entries that disagree about which characters survive cleaning. The overlap test is exact for ordinary patterns; a backreference, or lookaround it cannot settle, makes the rule refused rather than assumed safe. Plain character classes avoid it.

Patterns run as JavaScript in the browser and as PCRE on the server, so syntax the two read differently is refused when the rule is saved: \pL and \p{L}, \K \G \h \v \R \N \X \C \Q…\E \g \z, POSIX classes such as [[:digit:]], inline flags (?i) (?x), comments (?#…), atomic groups (?>…) and an empty class []. Explicit classes, (?:…), lookahead, lookbehind and (?<name>…) all work.

Pooled fields

@UVALIDATE={"type":"pooled","idLengths":[9],"expectedIds":3}
@UVALIDATE={"type":"pooled","algorithm":"none","pattern":"FC[0-9]{4}","idLengths":[6]}
@UVALIDATE={"type":"pooled","idMinLen":9,"idMaxLen":12,"blockSave":"confirm"}

Methods supported

FamilyMembersNotes
ISO/IEC 7064Mod 37,36 (default), Mod 11,10, Mod 97,10, Mod 11,2, Mod 37,2, two letters-only variantsAlphanumeric payloads
Classic decimalDamm, Verhoeff, LuhnDigit payloads, one check digit
Weighted modulusGS1 Mod-10, ABA Mod-10, ICAO MRZ Mod-10, ISBN-10 weighted Mod-11weighted_mod11 may emit X
Nonenone / regexFormat pattern only

The three Mod-10 schemes catch every single-digit error at any length but miss adjacent swaps of digits differing by 5. weighted_mod11 catches every single-digit error and every adjacent swap only up to 9 digits — the ISBN-10 domain. At 10 or more digits the position carrying weight 11 goes blind to substitutions, so prefer Mod 11,2 or Mod 97,10 for longer numbers.

The method must match how the IDs were minted. A check character is only meaningful against the scheme that produced it — pointing @UVALIDATE at the wrong algorithm will reject valid IDs.

Conditional validation with when

Any rule may carry a condition and validate only while that condition is true.

@UVALIDATE={"algorithm":"verhoeff","when":"[specimen_type]='2'"}
@UVALIDATE={"algorithm":"none","pattern":"FC[0-9]{4}","when":"[consent(1)]='1' and [site]<>'9'"}

The condition language is a REDCap-style subset, not byte-for-byte REDCap logic:

SupportedRejected when the rule is saved
[field] and [checkbox(code)] referencesFunctions such as datediff(...)
'text', "text" and number literalsSmart variables such as [record-name]
= <> != > < >= <=[event][field] cross-event prefixes
and / or / not, parenthesesArithmetic and piping

Branched validation

Several @UVALIDATE tags with different when conditions on one field branch, plus at most one tag without a condition as the fallback.

@UVALIDATE={"algorithm":"verhoeff","when":"[specimen_type]='2'"}
@UVALIDATE={"algorithm":"none","pattern":"FC[0-9]{4}"}          ← the "otherwise"

The "should end in X" hint

A hint naming the expected check character is available per rule via suggestFix, but it is off by default. A visible expected character can entice staff to force-fit a mistyped ID instead of re-scanning it.

@UVALIDATE={"algorithm":"3736","suggestFix":true,"note":"Blood specimen barcode"}

Field types

Check-character and regex validation apply to Text and Notes fields. The other rule kinds cover a wider set — see each tag's page.