Skip to content

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" ObjectKey: record
"then into key discount" Value: { Object: { Key: discount }}
"the value at amount" Value: { Object: { Key: amount }}
"for every element of the list" ArrayValue: …
"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 APIPOST the 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.


Next: Applications & URL regex →