Skip to content

Routing Rules

Overview

A routing rule is a directive that tells the platform which integration to use when a specific capability is invoked. Without routing rules, the platform cannot determine which of your configured providers should handle a payment initiation or message send - routing rules are the bridge between a requested operation and a provider integration.

Routing rules become essential as soon as you operate multiple integrations for the same capability. A business with Stripe and Cashfree both active for payments might route domestic transactions through Cashfree and international transactions through Stripe. A business with Twilio and Plivo for SMS might route high-volume batches differently from one-off transactional messages. Rules let you encode that logic in configuration rather than application code, and the POST /evaluate endpoint lets you dry-run any context before putting rules into production.

How Routing Works

When an operation is triggered, the platform evaluates routing rules for the relevant capability using the following algorithm:

  1. Load all rules for the capability that have an active integration reference, sorted by priority ascending (lower numbers are evaluated first).
  2. For each rule, evaluate all conditions in the conditions array against the operation context. All conditions in a rule must be satisfied for the rule to match.
  3. Select the first rule whose conditions all match. If the matched integration is unavailable, use the rule's fallback_integration_id if one is set.
  4. If no rule with conditions matches but a rule with is_default: true exists, use the default rule.
  5. If nothing matches, the operation fails with a NoActiveProviderException.

Priority Order

Priority values are arbitrary integers - only their relative order matters. Leaving gaps between values (10, 20, 30) makes inserting new rules easier later. Use POST /api/v1/routing-rules/reorder to bulk-update priorities without deleting and recreating rules.

Condition Types

Each condition in a routing rule targets one attribute of the operation context. Multiple conditions on the same rule are evaluated with AND logic - all must match for the rule to fire.

TypeOperatorsDescriptionExample
regionequals, not_equals, inGeographic region code of the operationRoute India traffic to Cashfree
currencyequals, not_equalsISO 4217 currency codeRoute USD payments to Stripe
amount_thresholdgt, gte, lt, ltePayment amount in the operation's currencyApply premium routing above ₹100,000
recipient_countgt, gte, lt, lteNumber of message recipients in a batchUse bulk SMS provider for large sends
message_typeequals, not_equals, inMessage channel typeRoute WhatsApp messages to Twilio only

Supported Operators:

OperatorDescription
equalsExact match
not_equalsExcludes a specific value
inMatches any value in a provided array
gtGreater than
gteGreater than or equal to
ltLess than
lteLess than or equal to

API Endpoints

List Routing Rules

Returns all routing rules for your tenant. Filter by capability to retrieve rules relevant to a specific operation type.

Endpoint: GET /api/v1/routing-rules

Authentication: Required (Bearer token)

Query Parameters:

  • capability (string) - Filter by capability name: initiate_payment, process_refund, verify_payment, send_sms, send_whatsapp
  • per_page (integer) - Results per page (default: 25, max: 100)
  • page (integer) - Page number

Example Request:

bash
GET /api/v1/routing-rules?capability=send_sms

Response (200 OK):

json
{
  "success": true,
  "data": [
    {
      "id": "01hwrule1abc456def789ghi00",
      "capability": "send_sms",
      "integration_id": "01hwxyz456abc789def012ghi3",
      "integration": {
        "id": "01hwxyz456abc789def012ghi3",
        "provider": "twilio",
        "display_name": "Twilio",
        "status": "active"
      },
      "conditions": [
        { "type": "region", "operator": "equals", "value": "IN" }
      ],
      "fallback_integration_id": "01hwxyz789abc012def345ghi6",
      "priority": 10,
      "is_default": false,
      "created_at": "2026-01-15T09:00:00Z",
      "updated_at": "2026-01-15T09:00:00Z"
    },
    {
      "id": "01hwrule2abc456def789ghi00",
      "capability": "send_sms",
      "integration_id": "01hwxyz789abc012def345ghi6",
      "integration": {
        "id": "01hwxyz789abc012def345ghi6",
        "provider": "plivo",
        "display_name": "Plivo",
        "status": "active"
      },
      "conditions": [],
      "fallback_integration_id": null,
      "priority": 100,
      "is_default": true,
      "created_at": "2026-01-15T09:10:00Z",
      "updated_at": "2026-01-15T09:10:00Z"
    }
  ],
  "meta": {
    "current_page": 1,
    "per_page": 25,
    "total": 2,
    "last_page": 1
  },
  "links": {
    "first": "/api/v1/routing-rules?page=1",
    "last": "/api/v1/routing-rules?page=1",
    "prev": null,
    "next": null
  }
}

Create Routing Rule

Creates a new routing rule. Conditions are optional - a rule with no conditions acts as an unconditional match and is typically used as the default fallback rule for a capability.

Endpoint: POST /api/v1/routing-rules

Authentication: Required (Bearer token)

Request Body:

json
{
  "capability": "initiate_payment",
  "integration_id": "01hwxyz123abc456def789ghi0",
  "conditions": [
    { "type": "region", "operator": "equals", "value": "IN" },
    { "type": "currency", "operator": "equals", "value": "INR" }
  ],
  "fallback_integration_id": "01hwxyz456abc789def012ghi3",
  "priority": 10,
  "is_default": false
}

Required Fields:

  • capability - Capability name this rule applies to
  • integration_id - ULID of the target integration (must be active)
  • priority - Integer priority; lower values are evaluated first
  • is_default - If true, this rule is used when no other conditions match

Optional Fields:

  • conditions - Array of condition objects; each requires type, operator, and value
  • fallback_integration_id - Integration to use if the primary is unavailable

Response (201 Created):

json
{
  "success": true,
  "message": "Routing rule created successfully",
  "data": {
    "id": "01hwrule3abc456def789ghi00",
    "capability": "initiate_payment",
    "integration_id": "01hwxyz123abc456def789ghi0",
    "conditions": [
      { "type": "region", "operator": "equals", "value": "IN" },
      { "type": "currency", "operator": "equals", "value": "INR" }
    ],
    "fallback_integration_id": "01hwxyz456abc789def012ghi3",
    "priority": 10,
    "is_default": false,
    "created_at": "2026-01-20T10:00:00Z",
    "updated_at": "2026-01-20T10:00:00Z"
  },
  "meta": {}
}

Update Routing Rule

Updates an existing routing rule. You can change the target integration, conditions, fallback, priority, or default flag independently.

Endpoint: PATCH /api/v1/routing-rules/{id}

Authentication: Required (Bearer token)

Request Body (partial update allowed):

json
{
  "conditions": [
    { "type": "region", "operator": "in", "value": ["IN", "LK", "NP"] },
    { "type": "currency", "operator": "equals", "value": "INR" }
  ],
  "priority": 5
}

Response (200 OK):

json
{
  "success": true,
  "message": "Routing rule updated successfully",
  "data": {
    "id": "01hwrule3abc456def789ghi00",
    "capability": "initiate_payment",
    "conditions": [
      { "type": "region", "operator": "in", "value": ["IN", "LK", "NP"] },
      { "type": "currency", "operator": "equals", "value": "INR" }
    ],
    "priority": 5,
    "updated_at": "2026-01-25T14:30:00Z"
  },
  "meta": {}
}

Delete Routing Rule

Permanently deletes a routing rule. If the deleted rule was the only match for certain operation contexts, those contexts will fall through to the default rule or raise a NoActiveProviderException.

Endpoint: DELETE /api/v1/routing-rules/{id}

Authentication: Required (Bearer token)

Response (200 OK):

json
{
  "success": true,
  "message": "Routing rule deleted successfully"
}

Reorder Rules

Updates the priority of multiple rules in a single request. Use this to restructure evaluation order without deleting and recreating rules.

Endpoint: POST /api/v1/routing-rules/reorder

Authentication: Required (Bearer token)

Request Body:

json
{
  "rules": [
    { "id": "01hwrule1abc456def789ghi00", "priority": 10 },
    { "id": "01hwrule3abc456def789ghi00", "priority": 20 },
    { "id": "01hwrule2abc456def789ghi00", "priority": 100 }
  ]
}

Response (200 OK):

json
{
  "success": true,
  "message": "Routing rules reordered successfully",
  "data": {
    "updated": 3
  },
  "meta": {}
}

Evaluate Routing Context

Performs a dry-run evaluation against your current routing rules without triggering any actual operation. Use this to verify rule logic before deploying changes to production.

Endpoint: POST /api/v1/routing-rules/evaluate

Authentication: Required (Bearer token)

Request Body:

json
{
  "capability": "initiate_payment",
  "region": "IN",
  "currency": "INR",
  "amount": 250000,
  "recipient_count": null,
  "message_type": null
}

Request Fields:

  • capability (required) - Capability to evaluate
  • region (optional) - Region code for region conditions
  • currency (optional) - Currency code for currency conditions
  • amount (optional) - Numeric amount for amount_threshold conditions
  • recipient_count (optional) - Integer for recipient_count conditions
  • message_type (optional) - String for message_type conditions

Response (200 OK):

json
{
  "success": true,
  "message": "Routing rule evaluated successfully",
  "data": {
    "matched_rule": {
      "id": "01hwrule3abc456def789ghi00",
      "capability": "initiate_payment",
      "priority": 5,
      "is_default": false,
      "conditions": [
        { "type": "region", "operator": "in", "value": ["IN", "LK", "NP"] },
        { "type": "currency", "operator": "equals", "value": "INR" }
      ]
    },
    "selected_integration": {
      "id": "01hwxyz123abc456def789ghi0",
      "provider": "cashfree",
      "display_name": "Cashfree",
      "status": "active"
    },
    "fallback_integration": {
      "id": "01hwxyz456abc789def012ghi3",
      "provider": "stripe",
      "display_name": "Stripe",
      "status": "active"
    }
  },
  "meta": {}
}

Response when no rule matches:

json
{
  "success": false,
  "message": "No matching routing rule found for the given context.",
  "data": null,
  "meta": {}
}

Business Scenarios

Scenario 1: Regional SMS Routing

Context: You have Twilio for South Asian traffic and Plivo as the global default. Regional pricing and deliverability differ significantly.

Workflow:

  1. Create a Twilio rule for send_sms with a region condition matching South Asian country codes
  2. Set Plivo as the default catch-all rule with a higher priority number
  3. Use POST /evaluate to confirm the logic before going live

API Calls:

bash
# 1. Create regional rule for South Asia
POST /api/v1/routing-rules
{
  "capability": "send_sms",
  "integration_id": "01hwxyz456abc789def012ghi3",
  "conditions": [
    { "type": "region", "operator": "in", "value": ["IN", "LK", "NP", "BD", "PK"] }
  ],
  "fallback_integration_id": "01hwxyz789abc012def345ghi6",
  "priority": 10,
  "is_default": false
}

# 2. Create global default rule
POST /api/v1/routing-rules
{
  "capability": "send_sms",
  "integration_id": "01hwxyz789abc012def345ghi6",
  "conditions": [],
  "priority": 100,
  "is_default": true
}

# 3. Verify with evaluate
POST /api/v1/routing-rules/evaluate
{
  "capability": "send_sms",
  "region": "IN"
}

Scenario 2: High-Value Payment Routing

Context: Transactions above ₹500,000 should route through Stripe for its advanced fraud tooling; lower-value domestic payments go to Cashfree.

Workflow:

  1. Create a Stripe rule for initiate_payment with an amount_threshold condition
  2. Create a Cashfree rule with a lower priority number as the domestic default
  3. Verify with two evaluate calls - one above the threshold, one below

API Calls:

bash
# 1. High-value rule → Stripe
POST /api/v1/routing-rules
{
  "capability": "initiate_payment",
  "integration_id": "01hwxyz123abc456def789ghi0",
  "conditions": [
    { "type": "currency", "operator": "equals", "value": "INR" },
    { "type": "amount_threshold", "operator": "gte", "value": 500000 }
  ],
  "priority": 10,
  "is_default": false
}

# 2. Default domestic rule → Cashfree
POST /api/v1/routing-rules
{
  "capability": "initiate_payment",
  "integration_id": "01hwcashfree23abc456def78",
  "conditions": [],
  "priority": 100,
  "is_default": true
}

# 3. Verify above threshold
POST /api/v1/routing-rules/evaluate
{
  "capability": "initiate_payment",
  "currency": "INR",
  "amount": 750000
}

# 4. Verify below threshold
POST /api/v1/routing-rules/evaluate
{
  "capability": "initiate_payment",
  "currency": "INR",
  "amount": 25000
}

Scenario 3: Configuring a Fallback

Context: Your primary SMS provider occasionally has regional outages. You want automatic failover to a secondary provider without manual intervention.

Workflow:

  1. Identify the secondary provider's integration ID
  2. Update your primary routing rule to include fallback_integration_id
  3. Verify via evaluate that the fallback is shown in the response

API Calls:

bash
# Update existing rule to add fallback
PATCH /api/v1/routing-rules/01hwrule2abc456def789ghi00
{
  "fallback_integration_id": "01hwxyz789abc012def345ghi6"
}

# Verify fallback appears in evaluation
POST /api/v1/routing-rules/evaluate
{
  "capability": "send_sms",
  "region": "US"
}

Best Practices

1. Always Define One Default Rule Per Capability

Every capability that your application uses should have exactly one rule with is_default: true. This rule acts as the catch-all and ensures operations never fail simply because no condition-based rule matched. Without a default, any context that does not match a condition rule raises an exception.

2. Use Sparse Priority Values

Assign priorities in increments of 10 or 20 rather than 1, 2, 3. Sparse priority values make it straightforward to insert new rules between existing ones without a full reorder. Use POST /reorder when you need to restructure a large set of rules.

3. Test with Evaluate Before Deploying

Run POST /api/v1/routing-rules/evaluate with representative contexts after any change to your routing rules. Cover edge cases - the threshold boundary, regions on either side of a condition, and empty/null context values. This prevents misrouted operations that are difficult to diagnose after the fact.

4. Document Your Routing Logic

Use the metadata field on integrations to annotate which routing decisions they serve. Keep a record of why each rule exists and which business requirement it satisfies. Routing rules tend to accumulate over time; undocumented rules become obstacles during incident response and provider migrations.


Troubleshooting

No Provider Found for Operation

Symptom: Operations fail with a NoActiveProviderException or 404-style error indicating no provider is available for the capability.

Possible Causes:

  • No routing rule exists for the capability
  • All matching rules reference integrations that are not in active status
  • No is_default: true rule exists and no condition-based rule matched the context

Resolution:

  1. List rules for the capability: GET /api/v1/routing-rules?capability={capability}
  2. Check the status of each referenced integration: GET /api/v1/integrations/{id}
  3. Run evaluate with the failing context to confirm which rule (if any) matches: POST /api/v1/routing-rules/evaluate
  4. Create a default rule or activate the referenced integration as appropriate

Wrong Integration Selected

Symptom: Operations are routed to the correct provider in testing but to the wrong one in production, or vice versa.

Possible Causes:

  • Condition values are incorrect (region codes, currency casing, threshold units)
  • Priority order is not as expected
  • A higher-priority rule matches before the intended rule

Resolution:

bash
# 1. Inspect all rules for the capability in priority order
GET /api/v1/routing-rules?capability=initiate_payment

# 2. Run evaluate with the exact production context
POST /api/v1/routing-rules/evaluate
{
  "capability": "initiate_payment",
  "region": "US",
  "currency": "USD",
  "amount": 1500
}

# 3. If the matched rule is wrong, adjust conditions or reorder
PATCH /api/v1/routing-rules/01hwrule3abc456def789ghi00
{
  "priority": 5
}

Evaluate Returns an Error

Symptom: POST /evaluate returns success: false with "No matching routing rule found."

Possible Causes:

  • No default rule configured for the capability
  • The context fields provided do not satisfy any rule's conditions
  • All matching integrations are inactive

Resolution:

  1. Confirm a default rule exists: GET /api/v1/routing-rules?capability={capability} and look for is_default: true
  2. Check that the integration referenced by the default rule is active
  3. If no default exists, create one:
bash
POST /api/v1/routing-rules
{
  "capability": "send_sms",
  "integration_id": "01hwxyz789abc012def345ghi6",
  "conditions": [],
  "priority": 100,
  "is_default": true
}

Documentation for SynthesQ CRM/ERP Platform