condition_sets[].conditions[] are combined with logical AND. For OR semantics, split into multiple condition sets ordered by sort_number.
condition_type catalog
conditional operators
LESS_OR_EQUAL_THAN is the operator of VOLUME_PAYMENTS_LIMIT and is not meant for the other
types in the table: the limit condition carries its own threshold semantics (the account total over a
period, see below) rather than a generic comparison of one attribute.
CONTAINS is a substring test for METADATA. For ADDITIONAL_FIELD it compares for equality against
the values the field resolves to: on a multi-valued field, such as order_fee_item_sku_code, it matches
when any item equals values[0], which is how you test “the order contains this SKU”.
The Rejected at save time column is the value count only. A yes means
POST/PUT returns 400 when the count does not match, naming the condition type and operator.
A no means the count is not checked at save time, and what a wrong count does at evaluation
depends on the operator — do not rely on it being harmless. TIME_PERIOD (one or two values) and
VOLUME_PAYMENTS_LIMIT (exactly one) are always checked, whatever the operator.
Where enforcement applies it is exact, not a minimum: a surplus value is rejected just as a
missing one is. BETWEEN with ["0", "60", "EUR"] is a 400, not a silently truncated range.
A value count already stored on a routing version is kept. If a version holds a condition whose
count predates this check, saving that version again does not fail on it — only a newly introduced
wrong count is rejected. This also holds for a version duplicated from such a source.
Card brands
CARD_BRAND accepts the values below. A value outside the list is rejected at save time with
400 INVALID_CARD_BRAND, naming the offending value. The list is validated against two internal
sources at save time, so a newly supported scheme may be accepted before it appears here.
Schedules: TIME_PERIOD
TIME_PERIOD matches when the payment is created inside a schedule. values[0] is when the schedule starts, the optional values[1] is when it ends, and the recurrence fields narrow it down:
time_period_repeat_amount, its weekday is in time_period_repetition_days (when set) and its time is inside the window (when set). YEARLY additionally requires the same month and day as the start.
Rules checked at save time, each a 400 naming the field: a value that is not an ISO-8601 date-time with offset, three or more values, a frequency without an amount or the reverse, a zero amount, a start time after the end time, a start date after the end date, one end of the time window without the other, repetition days or a time window without a frequency, a timezone that is not an offset, and limit_by or payments_count on a schedule. A value outside an enum or a zero amount is caught at the API edge as INVALID_PARAMETERS; the rest is INVALID_ROUTE_CONFIGURATION.
Volume and count limits: VOLUME_PAYMENTS_LIMIT
VOLUME_PAYMENTS_LIMIT matches while the account stays under a limit over a period, counting the payment being routed. Use it to send the first N payments, or the first X of volume, of a day or a month through one route and the rest elsewhere:
time_period_repetition_days, time_period_start_time or time_period_end_time; a valid currency when limit_by is VOLUME (400 INVALID_VOLUME_LIMIT_CURRENCY); a supported period (400 INVALID_VOLUME_LIMIT_PERIOD); a whole-hour timezone (400 UNSUPPORTED_VOLUME_LIMIT_TIMEZONE).
Place the limit set before the route that should take the overflow. Once the limit is reached the set stops matching, so the following sets and the
default_route receive the remaining payments of the period.Additional fields: ADDITIONAL_FIELD
ADDITIONAL_FIELD matches a field of the payment request that has no condition type of its own. key names the field and is validated at save time; an unknown key is rejected with 400 INVALID_ROUTE_CONFIGURATION.
email is compared case-insensitively. On a multi-valued field (order items, airline legs, passengers) every item must satisfy the operator, except CONTAINS, which matches when any item equals values[0]. A field absent from the payment never matches, with one exception: the numeric keys (installments, the amounts and quantities) resolve to the string "null" when absent, so a NOT_EQUAL or NOT_ONE_OF on them matches payments that do not carry the field.
Special cases
AMOUNTrequirescurrency. Comparisons are FX-naive; only payments whose currency matches the rule’scurrencyare evaluated.AMOUNTwith an emptyvalueslist is a currency-only condition:{ "condition_type": "AMOUNT", "conditional": "EQUAL", "values": [], "currency": "BRL" }matches every payment in BRL, whatever the amount. OnlyEQUALis accepted in that form; another operator is rejected with400 INVALID_ROUTE_CONFIGURATION. Responses return it in this same shape.METADATArequireskey. Comparespayment.metadata[key]. If the key is absent on the payment (or the payment has nometadata), the condition does not match, whatever the operator. This includes the negative operators: a payment without the key does not matchNOT_EQUALorNOT_ONE_OFeither. See the note below for how to route payments that don’t carry the key.ADDITIONAL_FIELDrequireskeyfrom the supported list;METADATAkeys are free-form,ADDITIONAL_FIELDkeys are not.- Card-only condition types (
CARD_ISSUER_COUNTRY,CARD_TYPE,CARD_BRAND,CARD_BIN) are only valid whenpayment_method = "CARD". COUNTRYvsCARD_ISSUER_COUNTRY. The two are sourced independently:COUNTRYmatches the transaction country your backend submits in the payment request, whileCARD_ISSUER_COUNTRYmatches the issuer country Yuno’s own BIN lookup derives from the card number. The same payment can beCOUNTRY = USandCARD_ISSUER_COUNTRY = GBat once. The BIN lookup runs before a provider is selected, is identical in sandbox and production, and reflects real-world BIN-registry data — never the processor’s view of the card.- Not to be confused with Communications Campaigns. That product has its own rule engine, where
AMOUNT_AND_CURRENCYwithBETWEENtakes three values["min", "max", "currency"]. Routing’sAMOUNTtakes two values plus a siblingcurrency, and a third value is rejected. CARD_BINwithBETWEENexpresses an issuer range and is validated more strictly than the generic two-value rule: exactly two values, both the same length, that length either 6 or 8, all digits, and the first less than or equal to the second. Anything else is400 INVALID_ROUTE_CONFIGURATION— so["411111", "41111111"](mismatched lengths) and["500000", "400000"](descending) are both rejected. Unlike every other condition type, a storedCARD_BIN BETWEENis not grandfathered: a version holding a malformed one is rejected on every subsequent save until it is corrected.- Schedule and limit fields belong to
TIME_PERIODandVOLUME_PAYMENTS_LIMITonly. Any of them on anothercondition_typeis rejected with400 INVALID_ROUTE_CONFIGURATION. - Numeric values are submitted as strings to match the
values: string[]schema. Yuno parses them based on thecondition_type.
Routing payments that don’t carry a metadata keyBecause a missing key never matches, a rule such as
METADATA QA NOT_EQUAL "true" skips every payment that has no QA key. To split traffic by the presence of a key, match the payments that do carry it, and let everything else fall through:- Put a condition set with
METADATAQAEQUAL"true"first (lowersort_number). - Put the condition set for all other payments after it, without a
METADATAcondition onQA.
QA = "true" match the first set; payments without the key, or with any other value, reach the second.Testing CARD_ISSUER_COUNTRY rules in sandbox
Two examples with Stripe’s test cards:
To verify a
CARD_ISSUER_COUNTRY rule end-to-end today, work with resolved values rather than documented ones — the lookup is deterministic:
- Run one sandbox payment with a candidate test card.
- Read
card_data.country_codefrom the payment (payment details in the dashboard, or the Retrieve Payment response). - Build the rule against that value. For example,
4242 4242 4242 4242is a dependableGBcard end-to-end.
CARD_TYPE, CARD_BRAND), though country is where provider test-card documentation diverges most often.