2 · Analyzing the HAR in Postman
You've captured a HAR and imported it into Postman as a collection. Now find the one request that carries the customer's data and read its body. Everything you build next — the Custom Data Type and the Application — comes from this request.
Rather not leave the browser? You can import the HAR back into Chrome/Firefox DevTools and do the same thing — see the next page. Postman is the recommended path and what this chapter uses.
HAR structure in 30 seconds
Each HAR entry is one request/response pair. The fields that matter:
entry
├── request
│ ├── method → Application method (GET/POST/…)
│ ├── url → Application URL regex
│ └── postData
│ └── text → THE REQUEST BODY (what you send)
└── response
└── content
└── text → THE RESPONSE BODY (what you receive)
Postman maps this cleanly: each HAR entry becomes a request in the collection; its
Body tab shows request.postData, and sending it shows the response.
The two bodies you'll live in:
- Request body (
request.postData.text) — what the browser sends. Source for request-side policies (blocking or detecting data on its way out). - Response body (
response.content.text) — what the browser receives. Source for response-side policies (masking data on its way in to the user).
The direction the data lives in decides the Rule's Apply To later — note it now.
Step 1 — Find the request
In the imported collection:
- Scan the request list. Postman shows method + name/URL per request. The one you want is
almost always Fetch/XHR: a
POST/PUT/PATCH(for sends) or aGETreturning JSON (for lists you want to mask). - Search (the collection search box) for a literal you know is in the data — the email you typed, the amount, an ID. That jumps you straight to the carrying request.
- For GraphQL, open the Body tab — the
operationName(e.g.ApplyDiscount) names the operation directly. All GraphQL goes to the same/graphqlURL, so the body is how you tell operations apart. - Ignore noise — analytics/telemetry hosts (segment, datadog, sentry, google-analytics), images, fonts, CSS. A clean single-action capture leaves only a handful of real candidates.
Confirm by re-firing
Click Send on the candidate request. If it reproduces the action (or returns the data you expected), you've got the right one. You can tweak a body value and re-send to confirm which field drives the behavior.
Step 2 — Read and pretty-print the body
Open the request's Body tab (for sends) or Send it and read the Response body (for receives). Use Postman's Pretty view to format the JSON. You'll see something like:
{
"action": "ApplyDiscount",
"record": {
"opportunityId": "0065g00000ABCDE",
"discount": { "amount": 17, "currency": "USD" }
}
}
Response sometimes arrives encoded/compressed
If a response body looks like gibberish or base64, it was compressed/encoded on the wire. Re-firing in Postman decodes it for you (Postman handles gzip), which is one more reason to use it for response-side analysis.
Step 3 — Read the shape (this is the data type)
Look at the body as a tree and answer three questions:
- What uniquely identifies this request among others to the same URL?
For REST it might be an
actionfield, atypefield, or simply the URL path. For GraphQL it'soperationName. → becomes the condition / anchor of your Data Type. - Where exactly does the value live? Walk the key path:
record.discount.amount,results[].properties.email,messages[].content.parts[]. → becomes the traversal. - Is it a single value, a value inside a repeated object, or an array of values?
Single → plain
Objecttraversal. Repeated (a list of users, a list of messages) → you'll need anArraystep. → decides whether your DSL needs anArraynode.
Write the path in plain English first:
"In the
ApplyDiscountrequest, the discount is atrecord.discount.amount.""In the contact-list response, every element of
results[]withtype == "contact"has the email atproperties.email."
That sentence translates almost mechanically into the Search DSL in Custom Data Types.
Step 4 — Note the URL and method
From the request's URL bar, record:
- the full URL (
https://acme.my.salesforce.com/services/apexrest/discount) — for the Application match, and - the method (
POST) — to constrain the Application.
That's everything the Applications chapter needs.
What Shield can (and can't) match
Shield scans bodies, by content type. It does not scan request/response headers for data values (headers are only used for routing/identity filters). Supported body types:
| Content type | Matched? | Notes |
|---|---|---|
application/json (++json) |
✅ | Includes GraphQL — it's just JSON. Use the JSON DSL. |
text/plain |
✅ | Regex matchers |
text/html |
✅ | XPath + regex |
application/xml, text/xml (++xml) |
✅ | XPath + regex |
text/csv |
✅ | Column-name + cell regex |
application/x-www-form-urlencoded |
✅ | Parsed into key/value pairs |
multipart/form-data |
✅ | Each part scanned by its own content type |
text/event-stream (SSE) |
✅ | Streamed AI responses — scanned line by line |
| Office Open XML / PDF | ✅ | Text extracted, then regex |
| Request/response headers | ❌ | Used for routing/identity only, not value detection |
Most custom policies are JSON (REST APIs and GraphQL), which is why the
Custom Data Types chapter focuses on the JSON Search DSL. Check the
Content-Type (Postman shows it on the request/response Headers tab) to know which matcher
family you're in.
Next: Analyzing the HAR in the browser → (alternative), or skip straight to Custom Data Types →.