5 · End-to-End Example
This walks the entire pipeline using an example request from a Salesforce "Apply Discount" action. The goal: detect and record whenever an agent grants a discount / account credit. We reconstruct the finished policy from scratch.
Step 1 — Capture
In Salesforce, with DevTools → Network open and the log cleared, the agent clicks Apply discount. One Fetch/XHR request appears in the Network list.
Step 2 — Find the request
Click the single Fetch/XHR POST to acme.my.salesforce.com/services/apexrest/discount and open
its Payload (request body) tab. The body carries an action of ApplyDiscount, which is
exactly the action we want.
{
"action": "ApplyDiscount",
"record": {
"opportunityId": "0065g00000ABCDE",
"discount": { "amount": 17, "currency": "USD" },
"reason": "LOYALTY_CREDIT",
"notes": "Discount for testing"
}
}
Key facts to record:
| Fact | Value | Feeds into |
|---|---|---|
| URL | https://acme.my.salesforce.com/services/apexrest/discount |
Application |
| Method | POST |
Application |
mimeType |
application/json |
→ JSON Search DSL |
| Direction | data is in request.postData |
Rule Apply To: Request |
| Identifying field | action == "ApplyDiscount" |
Data Type condition |
| Value of interest | record.discount.amount (the discount) |
Data Type traversal |
Step 3 — Read the shape (plain English)
"In the request whose
actionisApplyDiscount, the discount amount is atrecord.discount.amount."
Single value, no arrays → a straight Object traversal with a Condition to pin the action.
Step 4 — Custom Data Type
Translate the sentence node-by-node:
- "only
ApplyDiscount" →ConditionwithHasKey: actionandHasValue: ApplyDiscount - "go into
record" →Object→Key: record - "then
discount" →Value: { Object: { Key: discount }} - "the value at
amount" →Value: { Object: { Key: amount }}
{
"Search": {
"Object": {
"Condition": {
"HasKey": { "Value": "action" },
"HasValue": { "Value": "ApplyDiscount" }
},
"Key": { "Value": "record" },
"Value": {
"Object": {
"Key": { "Value": "discount" },
"Value": { "Object": { "Key": { "Value": "amount" } } }
}
}
}
}
}
| Field | Value |
|---|---|
| Type (ID) | APPLY_DISCOUNT |
| Name | Apply Discount (Salesforce) |
Step 5 — Application
A single endpoint handles the action, so the Application is broad and the Data Type does the
discrimination. Reuse a shared Salesforce Discount Application
(Full URL: acme.my.salesforce.com/services/apexrest/discount, method POST). If creating fresh:
| Field | Value |
|---|---|
| Name | Salesforce Discount |
| Definition | Full URL → acme.my.salesforce.com/services/apexrest/discount |
| Method | POST |
Step 6 — Wire it up (standard config, not HAR-derived)
The capture work is done — you have the Application and the Custom Data Type. The rest is normal Shield configuration (see Mask Formats · Obfuscations · Rules in the admin docs). For this use case — detect & record, not hide:
| Object | Setting |
|---|---|
| Mask Format | Detect Only |
| Obfuscation | Data Type Apply Discount (Salesforce) + Detect Only, Store Value: YES (so the granted amount lands in the activity log) |
| Rule | Action Detect; Apply To: Request (data was in request.postData); Application Salesforce Discount; the Obfuscation above |
Step 7 — Verify
Re-fire the request (repeat the action in Salesforce with the rule live) and confirm an
activity-log entry appears showing APPLY_DISCOUNT detected with the stored amount. If it doesn't
fire, check, in order: Application URL/method match → Search finds the action → the
record.discount.amount path is exact. (See the full
troubleshooting checklist.)
The same pipeline, response side
For contrast, a HubSpot "hide contact list" policy runs the opposite direction — and the only structural differences come straight from the captured message:
| Step | Apply Discount (this page) | Hide Contact List |
|---|---|---|
| Data lives in | request.postData |
response.content |
| Apply To | Request | Response |
| URL match | acme.my.salesforce.com/services/apexrest/discount |
Regex api\.hubapi\.com\/crm\/v3\/objects\/contacts\?.* |
| DSL needs | object traversal | object traversal with a Condition per array element (type == contact) |
| Mask Format | Detect Only | Fully Obfuscate |
| Action | Detect | Block |
Same five objects, same procedure — the message dictates every difference. That's the whole method: read the message, describe the location, choose the action.
← Back to the overview.