3 · Custom Data Types
A Custom Data Type tells Shield what to look for inside a body. For JSON (and therefore GraphQL), you describe it with a small declarative grammar — the JSON Search DSL. This chapter covers how to build one from a HAR body; the full DSL reference documents every node and option in depth.
A Data Type also carries some metadata:
| Field | Rules |
|---|---|
| Type (ID) | Uppercase letters, digits, underscores. 1–100 chars. e.g. APPLY_DISCOUNT |
| Name | Human label, 1–100 chars. e.g. Apply Discount (Salesforce) |
| Description | Optional, ≤128 chars |
| Definition | The detector — for JSON, the Search DSL below |
A Data Type can also detect via plain regex (for text/plain), XPath (HTML/XML), or
CSV column rules — see Data Types in the admin docs — but the
workhorse for API traffic is the JSON Search DSL.
The DSL in one minute
A definition is a JSON object with one top-level key — Search or Match — wrapping a tree
of query nodes that mirrors the path to your value:
| Top key | Behavior | Use when |
|---|---|---|
Search |
Recursively descends the whole document for any node that matches. | You don't care where in the tree it is, or the path varies. Default. |
Match |
Matches starting at the document root — the query lines up from the top. | The structure is fixed and you want to anchor from the root. |
The nodes you'll chain:
| Node | Means |
|---|---|
String |
Match a scalar value (string/number/boolean) — the leaf. |
Object |
Optionally filter an object, then descend into a key. |
Array |
Apply a query to each element of an array. |
Condition |
A boolean test that pins which object you mean. |
Key |
(inside Object) the property name to descend into. |
Value |
(inside Object/Array) the next node in the path. |
Full grammar lives in the reference
Field-by-field details — Value vs Regex vs RegexSubgroup, every Condition operator
(HasKey, HasValue, IsNull, And/Or/Not), the regex flavor, the id-key gotcha, and
a complete example gallery — are in JSON Search DSL — Full Reference.
This chapter sticks to the workflow.
Translating a body into the DSL
Take the plain-English path you wrote while analyzing the HAR and map it mechanically:
| You said… | DSL node |
|---|---|
"only the ApplyDiscount request" |
Condition with HasKey/HasValue |
"go into key record" |
Object → Key: record |
"then into key discount" |
Value: { Object: { Key: discount }} |
"the value at amount" |
Value: { Object: { Key: amount }} |
| "for every element of the list" | Array → Value: … |
"where the string matches .+" |
Value: { String: { Regex: ".+" }} |
The deepest Key you leave without a Value (or whose Value is a String matcher) is the
value Shield detects and masks.
Three patterns to start from
These are the shapes that cover most policies. The reference gallery has eleven more.
A · Pin to a request action, detect a nested field
"In the ApplyDiscount request, detect record.discount.amount." (For a GraphQL API the same
shape applies — pin operationName instead of action.)
{
"Search": {
"Object": {
"Condition": {
"HasKey": { "Value": "action" },
"HasValue": { "Value": "ApplyDiscount" }
},
"Key": { "Value": "record" },
"Value": {
"Object": {
"Key": { "Value": "discount" },
"Value": { "Object": { "Key": { "Value": "amount" } } }
}
}
}
}
}
B · A value inside every element of an array (conditional)
"In the contact-list response, for each object whose type == "contact", the email is at
properties.email." Because Search recurses, it visits every object and applies the
condition — you don't even spell out the array.
{
"Search": {
"Object": {
"Condition": {
"HasKey": { "Value": "type" },
"HasValue": { "Value": "contact" }
},
"Key": { "Value": "properties" },
"Value": { "Object": { "Key": { "Value": "email" } } }
}
}
}
C · Extract free text from a deeply nested array (AI prompt)
"ChatGPT's prompt is at messages[].content.parts[]." Fixed from the root → Match plus two
explicit Array steps.
{
"Match": {
"Object": {
"Key": { "Value": "messages" },
"Value": {
"Array": {
"Value": {
"Object": {
"Key": { "Value": "content" },
"Value": {
"Object": {
"Key": { "Value": "parts" },
"Value": { "Array": { "Value": { "String": { "Regex": ".+" } } } }
}
}
}
}
}
}
}
}
}
Testing the data type
Don't guess — validate against the real body:
- Dynamic Scan API —
POSTthe captured body with your data type and check the response shows the value detected/obfuscated. - Re-fire through the proxy — replay the captured request with the rule live and confirm the activity log shows the detection.
If nothing matches, the usual culprits are: wrong Search vs Match, a missing Array step, or
a Value/Key swapped. The reference has a
full troubleshooting checklist.