Rule Tags
ruleTags lets you attach short labels to a charge that your payment provider's pre-configured rules can match on — so the provider can route the charge to a specific processor, require or skip 3-D Secure, decline early, or change which payment options are offered, based on rules you set up once in your provider dashboard.
You send the labels; your provider's rules read them and act. Nothing about the routing logic lives in your code — you describe what this charge is (a premium-funnel order, a $199.99 basket, a first-time buyer) and let the rules you already configured decide what to do.
Field: ruleTags in the SDK, rule_tags on the wire. Optional. Available in @vonpay/checkout-node 0.13.0. Supported today on the direct-charge paymentIntents.create call.
ruleTags vs metadata
Both are string maps you attach at charge time, and both keep your keys exactly as you write them — but they go to different places:
metadata | ruleTags | |
|---|---|---|
| Where it goes | Stored on the payment record as your own data. | Delivered to your payment provider for rule matching. |
| Who reads it | You (dashboards, reconciliation, your own records). | Your provider's pre-configured rules. |
| Effect on the charge | None — it's a label for your records. | Can change routing, 3-D Secure, decline, or payment options if a rule matches it. |
Use metadata for data you want to keep. Use ruleTags for labels a provider rule needs to see. Putting an order ID in metadata records it; putting funnel: "premium" in ruleTags lets a rule act on it. They don't overlap — sending a value in metadata will not reach the provider's rules, and sending one in ruleTags is not a substitute for storing it.
ruleTags, not metadataYour provider's rule editor labels the matching condition "Metadata" — but it matches the values you send in ruleTags, not the values in the metadata field. Von Payments deliberately does not forward metadata to your provider (it's stored as your private record), so a value like metadata.funnel can never trigger a "Metadata → funnel" rule no matter what you name it. Routing labels must go in ruleTags.
You don't send the same value twice. A routing label lives only in ruleTags. You'd also put it in metadata only if you separately wanted it stored on your payment record for your own reconciliation — uncommon for a pure routing label, so most of the time it's ruleTags and nothing else.
Configure the rule first
A rule tag does nothing on its own. It only has an effect when a rule that matches it already exists in your provider dashboard. The order matters:
- Create the rule in your provider dashboard. Set up a rule that matches on a key + value — for example, "if
funnelequalspremium, require 3-D Secure" or "iforder_valueis greater than100, route to your high-value processor." The key and value you match on are exactly the ones you'll send. - Send the matching tag on the charge. Pass the same key and value in
ruleTagsonpaymentIntents.create. - The provider evaluates its rules against the tags on the charge and acts — routing, a 3-D Secure decision, an early decline, or a change to payment options.
A tag with no matching rule is a harmless no-op — it's forwarded to the provider but nothing acts on it, and the charge is unaffected. So there's no harm in sending a tag early, but it won't do anything until the matching rule is in place. Create the rule first, then send the tag.
Example
import { VonPayCheckout } from "@vonpay/checkout-node";
const vonpay = new VonPayCheckout(process.env.VON_PAY_SECRET_KEY);
const intent = await vonpay.paymentIntents.create({
amount: 19999, // $199.99
currency: "USD",
paymentMethod: { id: "vp_pmt_test_QAqnXEJF_TCum1jg" },
ruleTags: {
funnel: "premium",
order_value: "199.99",
},
});
The equivalent raw request — the wire field is rule_tags:
curl -X POST https://checkout.vonpay.com/v1/payment_intents \
-H "Authorization: Bearer vp_sk_test_xxx" \
-H "Content-Type: application/json" \
-d '{
"amount": 19999,
"currency": "USD",
"payment_method": { "id": "vp_pmt_test_QAqnXEJF_TCum1jg" },
"rule_tags": { "funnel": "premium", "order_value": "199.99" }
}'
Your keys travel verbatim. The SDK converts the field name to rule_tags on the wire, but it does not touch the keys inside the map — order_value stays order_value, and orderValue would stay orderValue. So the key you send must be spelled exactly like the key your rule matches on (see Gotchas).
String vs numeric matching
Every value is a string on the wire — even numbers. How you match depends on the kind of rule you set up:
Text match (equality). Send the literal string and match it with an equals condition.
ruleTags: { funnel: "premium" }
// Dashboard rule: if `funnel` equals "premium" → require 3-D Secure
Numeric match (comparison). Send the number as a numeric string and match it with your provider's numeric condition (greater-than, less-than, between). The provider parses the string as a number, so the comparison is numeric, not alphabetical.
ruleTags: { order_value: "199.99" }
// Dashboard rule: if `order_value` is greater than 100 → route to your high-value processor
The distinction matters: if you send "199.99" but write the rule as a text equals, it will only match the exact string "199.99" — not "greater than 100." For thresholds, use a numeric condition in the rule and send the value as a numeric string.
Limits
| Rule | Limit |
|---|---|
| Number of keys | Up to 5 |
| Value length | ≤ 80 characters |
| Key-name length | ≤ 64 characters |
| Key and value types | Strings, non-empty (both must have at least one character) |
| Reserved keys | A small set of reserved internal key names is rejected (see below) |
| Secrets / PII | Values that look like an email, API key, or secret are rejected |
A request that breaks any of these is rejected with 400 validation_error — see Error codes. A few things worth calling out:
- Reserved keys. A small set of key names is reserved for internal reconciliation and can't be used as tag keys; using one returns a
400. Everyday routing keys (funnel,order_value,region,plan, and the like) are all fine. - No secrets, no PII. Values are delivered to your payment provider, so they must never contain sensitive data. Values shaped like an email address, API key, or secret are rejected outright; don't put card data, personal data, or credentials in a tag.
Provider support
ruleTags is only honored when your merchant account runs on a payment provider that supports orchestration rules. If your provider doesn't support them, the create call is rejected before the charge runs with capability_not_supported (HTTP 422) — rather than silently ignoring the tags. That's deliberate: a silently-dropped tag could leave you believing a control (say, a 3-D Secure or decline rule) is active when it isn't. If you hit this and need rule tags on that merchant, contact support.
Rule tags are supported today on the direct-charge paymentIntents.create call.
Gotchas
- No matching rule = no effect. A tag doesn't do anything by itself — the matching rule must already exist in your provider dashboard. If a control isn't firing, confirm a rule matches the exact key and value you sent.
- Keys are verbatim and case-sensitive.
orderValue,order_value, andordervalueare three different keys. The key and value you send must match your rule's condition exactly, including case. The SDK does not snake_case your keys (it only snake_cases the field name torule_tags). - Values are provider-visible — never put secrets or PII in them. Anything you send is delivered to the provider. Keep tags to non-sensitive routing labels; secret- or email-shaped values are rejected.
- It's not a replacement for
metadata. Data you want stored on the payment record goes inmetadata; labels a provider rule needs to see go inruleTags. A value in one does not appear in the other. - An unsupported provider is a hard error, not a no-op. On a provider without orchestration-rule support, the whole create call fails with
capability_not_supported(422) — handle that case rather than assuming tags are always ignored safely.
Related
- Payment Intents — the server-driven charge lifecycle
ruleTagsattaches to - Payment Intent Object — the response shape
- 3D Secure & SCA — one of the controls a rule can drive
- Error codes —
validation_errorandcapability_not_supported