Skip to content

Services

Services define validation rules for entities in your system. Assigning a service to an entity enables supplemental validation of messages sent by that entity, ensuring data quality, enforcing business rules, and enabling automated workflows.

Overview

  • Services add extra validation on top of basic message permissions (policies).
  • They help you:
  • Validate message structure and content
  • Enforce business rules
  • Ensure data quality and consistency
  • Trigger automated flows on validation failures

Quick Example:

Service Creation UI Creating a new service in the UI.

You can also define services using JSON, as shown below:

{
  "name": "temp_sensor_service",
  "description": "Validates temperature sensor messages",
  "dynamicActions": [
    {
      "name": "temp-publish",
      "requiredFields": ["temperature"],
      "rules": [
        {
          "field": "temperature",
          "operator": "between",
          "value": [-50, 150],
          "message": "Temperature must be between -50°C and 150°C"
        }
      ]
    }
  ]
}

Core Concepts

Service Structure

A service consists of:

  • Name (required): Unique identifier
  • Description (optional): What the service validates
  • Dynamic Actions (optional): Validation rules for specific message types
  • Resource Path (auto-generated): accountNumber:region:service:name

Validation Operators

Operator Description Example
eq Equals "status" eq "active"
ne Not equals "type" ne "error"
gt Greater than "temperature" gt 20
lt Less than "count" lt 100
ge Greater than or equal "version" ge "1.0"
le Less than or equal "priority" le 5
startsWith String starts with "name" startsWith "device_"
endsWith String ends with "filename" endsWith ".json"
contains String contains "message" contains "error"
in Value in list "status" in ["active", "pending"]
notIn Value not in list "type" notIn ["error", "critical"]
hasAll Array contains all values "tags" hasAll ["urgent", "monitoring"]
hasAny Array contains any value "categories" hasAny ["sensor", "actuator"]
between Value in range "temperature" between [0, 100]
isTimestamp Valid timestamp "created_at" isTimestamp

How Validation Works

When an entity sends a message, the system validates it in several steps:

  1. Check Permissions
    The system first checks if the message is allowed by your policies.
  2. Apply Service Validation
    If the entity has a service assigned:
  3. The system looks for a dynamic action matching the message type.
  4. It checks that all required fields are present.
  5. It applies each validation rule for that action.
  6. Handle Validation Failures
    If any rule fails:
  7. The system returns a validation error to the sender, including details about what failed.
  8. Tags from the failed dynamic action are added to the message context.
  9. Trigger Flows
    The message, now containing validation failure tags, is processed by the flows system.
  10. Any connector (webhook, HTTP endpoint, etc.) with matching tags will be triggered automatically.

Dynamic Actions

Dynamic actions define validation rules for specific message types (such as operations or event types). Each action can have its own required fields, rules, and tags.

Dynamic Action UI Adding a dynamic action in the UI.

Example:

You can also define dynamic actions using JSON:

{
  "name": "temp-publish",
  "requiredFields": ["temperature"],
  "tags": ["monitoring", "temperature"],
  "rules": [
    {
      "field": "temperature",
      "operator": "between",
      "value": [-50, 150],
      "message": "Temperature must be between -50°C and 150°C"
    }
  ]
}
  • Name: Message type or operation
  • Required Fields: Must be present in the message
  • Rules: Validate field values and formats
  • Tags: Added to the message context if validation fails (used for automation)

Tags and Flow Triggering

Tags from failed dynamic actions are automatically added to the message context. The flows system uses these tags to trigger connectors (webhooks, HTTP endpoints, etc.).

Example Connector:

{
  "name": "critical_alert_webhook",
  "type": "http",
  "tags": ["critical", "emergency"],
  "config": {
    "url": "https://your-alert-system.com/webhook",
    "method": "POST"
  }
}

If a message fails validation with the tags ["critical", "emergency"], this webhook will be triggered.

Usage

Creating Services

  1. Go to the Services page in your dashboard
  2. Click "Create Service"
  3. Use the visual builder or JSON mode to define your service
  4. Add dynamic actions for the message types you want to validate
  5. Configure validation rules for each action
  6. Save the service

Assigning Services to Entities

When creating an entity, you can optionally specify a service:

{
  "name": "temperature_sensor_01",
  "service": "sensor_service",  // Optional - no validation if omitted
  "description": "Temperature sensor in building A"
}

Or create an entity without any service:

{
  "name": "simple_device",
  "description": "Device without validation rules"
}

Validating Service Configurations

Use the /api/services/validate endpoint to test your service configuration before saving it. This helps catch errors early.

Best Practices

  • Start Simple: Begin with basic validation rules and add complexity as needed
  • Use Clear Names: Choose descriptive names for services and actions
  • Test Validation: Use the validation endpoint to test your service configurations
  • Document Rules: Add clear error messages to help debug validation failures
  • Consider Performance: Keep validation rules efficient for high-volume message processing
  • Plan Your Tags: Design a tag strategy that enables meaningful automation:
  • Use consistent tag naming (e.g., critical, monitoring, data_quality)
  • Group related validations with similar tags
  • Consider severity levels (info, warning, critical, emergency)
  • Use domain-specific tags for different types of equipment or systems
  • Configure Flows: Set up connectors with appropriate tags to respond to validation failures
  • Monitor Automation: Track which validation failures trigger which automated responses

Examples

1. Smart Home Sensor Service

Validates IoT sensor data with nested fields

{
  "name": "smart_home_sensors",
  "description": "Validates smart home sensor data",
  "dynamicActions": [
    {
      "name": "publish",
      "requiredFields": ["sensor_id", "readings"],
      "tags": ["environmental", "data_quality"],
      "rules": [
        {
          "field": "sensor_id",
          "operator": "startsWith",
          "value": "ENV_",
          "message": "Sensor ID must start with ENV_"
        },
        {
          "field": "readings.temperature",
          "operator": "between",
          "value": [-40, 60],
          "message": "Temperature must be between -40°C and 60°C"
        },
        {
          "field": "readings.humidity",
          "operator": "between",
          "value": [0, 100],
          "message": "Humidity must be between 0% and 100%"
        }
      ]
    }
  ]
}

2. Healthcare Vitals Service

Validates patient vital signs with nested data

{
  "name": "healthcare_vitals",
  "description": "Validates patient vital signs data",
  "dynamicActions": [
    {
      "name": "publish",
      "requiredFields": ["patient_id", "vitals"],
      "tags": ["healthcare", "vitals"],
      "rules": [
        {
          "field": "patient_id",
          "operator": "startsWith",
          "value": "PAT_",
          "message": "Patient ID must start with PAT_"
        },
        {
          "field": "vitals.heart_rate",
          "operator": "between",
          "value": [30, 200],
          "message": "Heart rate must be between 30 and 200 BPM"
        },
        {
          "field": "vitals.blood_pressure.systolic",
          "operator": "between",
          "value": [70, 200],
          "message": "Systolic blood pressure must be between 70 and 200 mmHg"
        }
      ]
    }
  ]
}

Message Flow Example

Here's how a message flows through validation and webhook triggering:

Step 1: Entity Sends Message

{
  "msgType": "publish",
  "data": {
    "sensor_id": "ENV_001",
    "readings": {
      "temperature": 150,
      "humidity": 45.2
    }
  }
}

Step 2: Validation Applied

The system applies the smart_home_sensors service validation:

  • sensor_id starts with "ENV_" ✓
  • readings.temperature (150) is not between [-40, 60] ✗
  • readings.humidity (45.2) is between [0, 100] ✓

Step 3: Validation Failure - Message Continues Processing

Important: When validation fails, the message is NOT rejected. Instead:

  • The message continues to be processed normally
  • Tags from the failed dynamic action (["environmental", "data_quality"]) are added to the message context
  • The message is stored with validation failure information
  • Any webhooks with matching tags are triggered

What Gets Stored:

{
  "message": {
    "data": { "sensor_id": "ENV_001", "readings": { "temperature": 150, "humidity": 45.2 } },
    "context": { "tags": ["environmental", "data_quality"] },
    "msgType": "publish",
    "source": "123:us-1:entity:temperature_sensor_01",
    "timestamp": "2024-01-15T10:30:00Z"
  },
  "metadata": {
    "actionResult": {
      "name": "temp-publish",
      "valid": false,
      "message": "Temperature must be between -40°C and 60°C",
      "ruleName": "readings.temperature_validation",
      "tags": ["environmental", "data_quality"],
      "field": "readings.temperature",
      "actual": 150,
      "expected": { "between": [-40, 60] }
    }
  }
}

Step 4: Webhook Triggered

Since the message has tags ["environmental", "data_quality"], any webhook with matching tags is triggered:

Webhook Configuration:

{
  "name": "data_quality_alert",
  "type": "http",
  "tags": ["data_quality"],
  "config": {
    "url": "https://your-monitoring-system.com/alerts",
    "method": "POST"
  }
}

What the Webhook Receives:

{
  "data": { "sensor_id": "ENV_001", "readings": { "temperature": 150, "humidity": 45.2 } },
  "context": { "tags": ["environmental", "data_quality"] },
  "msgType": "publish",
  "source": "123:us-1:entity:temperature_sensor_01",
  "timestamp": "2024-01-15T10:30:00Z"
}

Key Points

  1. Validation Failures Don't Block Messages: Failed validation doesn't prevent message processing
  2. Tags Enable Automation: Failed validations add tags that trigger webhooks and flows
  3. Original Message Preserved: The complete original message is always stored and forwarded
  4. Validation Context Added: Failed validations include detailed error information in the message context
  5. Webhook Payload: Webhooks receive the original message data plus any tags from failed validations

This flow ensures your webhook recipients get the complete message data and can respond appropriately to both successful and failed validations.

Validation Failure Output Format

When a validation rule fails, the actionResult includes:

  • name: The action name (e.g., "temp-publish")
  • valid: Whether the validation passed (true/false)
  • message: The error message for the failed rule
  • ruleName: The name of the rule that failed
  • tags: Tags associated with the failed validation
  • field: The field that failed
  • actual: The value that was provided
  • expected: A JSON object describing the rule, e.g. { "between": [0, 100] }

Example:

{
  "actionResult": {
    "name": "temp-publish",
    "valid": false,
    "message": "Temperature must be between -40°C and 60°C",
    "ruleName": "readings.temperature_validation",
    "tags": ["environmental", "data_quality"],
    "field": "readings.temperature",
    "actual": 150,
    "expected": { "between": [-40, 60] }
  }
}

Operator to Expected Output Mapping

Operator Example Rule Value Example expected Output (JSON)
eq "active" { "eq": "active" }
ne "error" { "ne": "error" }
gt 20 { "gt": 20 }
lt 100 { "lt": 100 }
ge 1.0 { "ge": 1.0 }
le 5 { "le": 5 }
between [0, 100] { "between": [0, 100] }
in ["A", "B", "C"] { "in": ["A", "B", "C"] }
notIn ["error", "critical"] { "notIn": ["error", "critical"] }
contains "error" { "contains": "error" }
hasAll ["urgent", "monitoring"] { "hasAll": ["urgent", "monitoring"] }
hasAny ["sensor", "actuator"] { "hasAny": ["sensor", "actuator"] }
startsWith "ENV_" { "startsWith": "ENV_" }
endsWith ".json" { "endsWith": ".json" }
isTimestamp (no value) { "isTimestamp": null }