Replace the entire merchant / MCC list (either an allowlist or a blocklist)
x-on-behalf-of required
Use this endpoint to block gambling / adult / crypto MCCs on travel cards, restrict procurement cards to selected suppliers, or restrict subscription cards to one merchant.
⚠ PUT replaces the whole rule; it does not append. The upstream supports one rule per card; an allowlist and blocklist cannot coexist. Send the complete list every time. Treating it as an append operation would replace all existing entries when you thought you were adding one.
⚠ merchant_names and mcc_list cannot both be empty. An empty allowlist would prohibit every merchant, which is almost certainly not intended. Use DELETE to remove restrictions.
⚠ 200 means the upstream confirmed receipt; the response has synced: true. With 502 upstream_error, the rule may already be active. We have stored your requested list as the baseline for subsequent changes, but have no confirmation. Retry with the same idempotency key.
merchant_names matches merchant names in clearing messages, whose formats vary by acquirer (AMAZON / AMZN Mktp US / AMAZON.COM*2K4TY). Use merchant_name from GET /v1/cards/{id}/transactions, not the name on the brand's website.
Prerequisites
- The card is not
closed/expired/replaced. - The issuer supports card-level lists (the
merchantRulescapability flag).
Path Parameters
| Field | Type | Required | Description |
|---|---|---|---|
id |
string | Required | Card ID |
Request Headers
| Field | Type | Required | Description |
|---|---|---|---|
x-idempotency-key |
string | Required | |
x-on-behalf-of |
string | Required | The member on whose behalf to call. |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
rule_type |
"white" | "black" | Required | white permits spending only at listed merchants; black rejects all listed merchants.
A card can have only one rule type. |
merchant_names |
string[] | Optional | Merchant names, maximum 200 entries. An empty array means merchant-name matching is not used. |
mcc_list |
string[] | Optional | MCC list: four-digit codes under ISO 18245, maximum 200 entries. |
Response
{
"id": "crd_9f2c1b7a-3d51-4a2e-9c08-6b1f0d4e77aa",
"rule_type": "black",
"merchant_names": [],
"mcc_list": [
"7995",
"5967",
"6051"
],
"synced": true,
"synced_at": "2026-08-13T02:11:47.000Z"
}invalid_fields: an MCC is not four digits, both lists are empty, or there are more than 200 entries
(includes a fields array identifying each invalid item).
state_invalid: card closed or expired.
product_not_available: the issuer does not support card-level lists or credentials are incomplete.
invalid_request: the upstream rejected the list.not_found: the card does not exist or does not belong to this member.upstream_error: outcome unknown; the rule may already have been created.
Retry with the same idempotency key. A new key does not create a second upstream rule
(there is only one per card), but adds an unassociated key to your reconciliation records.curl -X PUT 'https://api.zinfra.vip/v1/cards/{id}/merchant-rules' \
-H 'x-auth-token: Bearer $TOKEN' \
-H 'x-on-behalf-of: $MEMBER_ID' \
-H 'content-type: application/json' \
-d '{
"rule_type": "black",
"merchant_names": [],
"mcc_list": [
"7995",
"5967",
"6051"
]
}'const res = await fetch("https://api.zinfra.vip/v1/cards/{id}/merchant-rules", {
method: "PUT",
headers: {
"x-auth-token": "Bearer $TOKEN",
"x-on-behalf-of": "$MEMBER_ID",
"content-type": "application/json",
},
body: JSON.stringify({
"rule_type": "black",
"merchant_names": [],
"mcc_list": [
"7995",
"5967",
"6051"
]
}),
});
// Keep monetary amounts as strings, never numbers.
const data = await res.json();import requests
res = requests.put(
"https://api.zinfra.vip/v1/cards/{id}/merchant-rules",
headers={
"x-auth-token": "Bearer $TOKEN",
"x-on-behalf-of": "$MEMBER_ID",
"content-type": "application/json",
},
json={
"rule_type": "black",
"merchant_names": [],
"mcc_list": [
"7995",
"5967",
"6051"
]
},
)
# Use Decimal(str(...)) for amounts, not float.
data = res.json()req, _ := http.NewRequest("PUT", "https://api.zinfra.vip/v1/cards/{id}/merchant-rules",
strings.NewReader(`{
"rule_type": "black",
"merchant_names": [],
"mcc_list": [
"7995",
"5967",
"6051"
]
}`))
req.Header.Set("x-auth-token", "Bearer $TOKEN")
req.Header.Set("x-on-behalf-of", "$MEMBER_ID")
req.Header.Set("content-type", "application/json")
res, err := http.DefaultClient.Do(req)
// Decode amount fields as string, not float64.HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create("https://api.zinfra.vip/v1/cards/{id}/merchant-rules"))
.header("x-auth-token", "Bearer $TOKEN")
.header("x-on-behalf-of", "$MEMBER_ID")
.header("content-type", "application/json")
.method("PUT", HttpRequest.BodyPublishers.ofString("""
{
"rule_type": "black",
"merchant_names": [],
"mcc_list": [
"7995",
"5967",
"6051"
]
}
"""))
.build();
// Use String / BigDecimal for amounts, not double.$ch = curl_init('https://api.zinfra.vip/v1/cards/{id}/merchant-rules');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'x-auth-token: Bearer $TOKEN',
'x-on-behalf-of: $MEMBER_ID',
'content-type: application/json',
],
CURLOPT_POSTFIELDS => <<<'JSON'
{
"rule_type": "black",
"merchant_names": [],
"mcc_list": [
"7995",
"5967",
"6051"
]
}
JSON,
]);
$res = curl_exec($ch);
// Use bcmath / strings for amounts, not floatval.
{
"id": "crd_9f2c1b7a-3d51-4a2e-9c08-6b1f0d4e77aa",
"rule_type": "black",
"merchant_names": [],
"mcc_list": [
"7995",
"5967",
"6051"
],
"synced": true,
"synced_at": "2026-08-13T02:11:47.000Z"
}