API Reference
Product-FARM provides both REST and gRPC APIs for integration.
Base URLs
| Protocol | Port | Base URL |
|---|---|---|
| REST | 8081 | http://localhost:8081/api |
| gRPC | 50051 | localhost:50051 |
REST API
Products
List Products
GET /api/products
Query Parameters:
| Parameter | Type | Description |
|———–|——|————-|
| status | string | Filter by status (DRAFT, PENDING_APPROVAL, ACTIVE, DISCONTINUED) |
| template_type | string | Filter by template type |
Response:
{
"products": [
{
"id": "insurance-v1",
"name": "Insurance Premium Calculator",
"status": "ACTIVE",
"template_type": "INSURANCE",
"description": "Calculate insurance premiums",
"effective_from": "2024-01-01T00:00:00Z",
"expiry_at": null,
"version": 1
}
]
}
Create Product
POST /api/products
Content-Type: application/json
Request Body:
{
"name": "my-product",
"template_type": "INSURANCE",
"description": "Product description",
"effective_from": "2024-01-01T00:00:00Z",
"expiry_at": "2025-12-31T23:59:59Z"
}
Response: 201 Created
{
"id": "my-product",
"name": "my-product",
"status": "DRAFT",
"template_type": "INSURANCE",
"description": "Product description",
"effective_from": "2024-01-01T00:00:00Z",
"expiry_at": "2025-12-31T23:59:59Z",
"version": 1
}
Get Product
GET /api/products/{product_id}
Response: 200 OK
{
"id": "insurance-v1",
"name": "Insurance Premium Calculator",
"status": "ACTIVE",
"template_type": "INSURANCE",
"description": "Calculate insurance premiums",
"effective_from": "2024-01-01T00:00:00Z",
"expiry_at": null,
"version": 1
}
Update Product
PUT /api/products/{product_id}
Content-Type: application/json
Request Body:
{
"name": "updated-name",
"description": "Updated description"
}
Note: Only DRAFT products can be updated.
Delete Product
DELETE /api/products/{product_id}
Response: 204 No Content
Note: Only DRAFT and DISCONTINUED products can be deleted.
Clone Product
POST /api/products/{product_id}/clone
Content-Type: application/json
Request Body:
{
"new_product_id": "insurance-v2",
"new_name": "Insurance Premium Calculator v2"
}
Response: 201 Created - Returns the new cloned product in DRAFT status.
Product Lifecycle Operations
# Submit for approval
POST /api/products/{product_id}/submit
# Approve product (changes status to ACTIVE)
POST /api/products/{product_id}/approve
# Reject product (returns to DRAFT)
POST /api/products/{product_id}/reject
Content-Type: application/json
{"reason": "Missing required attributes"}
# Discontinue product
POST /api/products/{product_id}/discontinue
Rules
List Rules
GET /api/products/{product_id}/rules
Response:
{
"rules": [
{
"id": "rule-001",
"product_id": "insurance-v1",
"rule_type": "CALCULATION",
"display_expression": "base_premium = coverage × 0.02",
"compiled_expression": "{\"*\": [{\"var\": \"coverage\"}, 0.02]}",
"input_attributes": [
{"attribute_path": "coverage", "order_index": 0}
],
"output_attributes": [
{"attribute_path": "base_premium", "order_index": 0}
],
"order_index": 0,
"enabled": true
}
]
}
Create Rule
POST /api/products/{product_id}/rules
Content-Type: application/json
Request Body:
{
"rule_type": "CALCULATION",
"display_expression": "base_premium = coverage × 0.02",
"expression": {
"*": [{"var": "coverage"}, 0.02]
},
"input_attributes": ["coverage"],
"output_attributes": ["base_premium"],
"order_index": 0,
"enabled": true
}
Get Rule
GET /api/products/{product_id}/rules/{rule_id}
Update Rule
PUT /api/products/{product_id}/rules/{rule_id}
Content-Type: application/json
Delete Rule
DELETE /api/products/{product_id}/rules/{rule_id}
Evaluation
Evaluate Rules
POST /api/products/{product_id}/evaluate
Content-Type: application/json
Request Body:
{
"inputs": {
"coverage": 250000,
"customer_age": 65,
"smoker": false
}
}
Response:
{
"outputs": {
"base_premium": 5000,
"age_factor": 1.2,
"final_premium": 6000
},
"rule_results": [
{
"rule_id": "rule-001",
"outputs": {"base_premium": 5000},
"execution_time_ns": 850
},
{
"rule_id": "rule-002",
"outputs": {"age_factor": 1.2},
"execution_time_ns": 420
},
{
"rule_id": "rule-003",
"outputs": {"final_premium": 6000},
"execution_time_ns": 380
}
],
"total_execution_time_ns": 2150,
"execution_levels": 2
}
Batch Evaluate
POST /api/products/{product_id}/batch-evaluate
Content-Type: application/json
Request Body:
{
"inputs": [
{
"id": "customer-001",
"data": {
"coverage": 250000,
"customer_age": 35
}
},
{
"id": "customer-002",
"data": {
"coverage": 500000,
"customer_age": 65
}
}
]
}
Response:
{
"results": [
{
"input_id": "customer-001",
"outputs": {"final_premium": 5000},
"success": true
},
{
"input_id": "customer-002",
"outputs": {"final_premium": 12000},
"success": true
}
],
"total_execution_time_ns": 4500
}
Validate Rules
POST /api/products/{product_id}/validate-rules
Response:
{
"valid": true,
"errors": [],
"warnings": [
{
"rule_id": "rule-004",
"message": "Rule is disabled"
}
]
}
Get Execution Plan
GET /api/products/{product_id}/execution-plan
Response:
{
"levels": [
{
"level": 0,
"rules": ["rule-001", "rule-002"]
},
{
"level": 1,
"rules": ["rule-003"]
}
],
"dependencies": [
{"from": "rule-003", "to": "rule-001"},
{"from": "rule-003", "to": "rule-002"}
],
"dot_graph": "digraph { ... }",
"mermaid_graph": "graph TD\n ...",
"ascii_graph": "..."
}
Abstract Attributes
List Abstract Attributes
GET /api/products/{product_id}/abstract-attributes
Query Parameters:
| Parameter | Type | Description |
|———–|——|————-|
| component_type | string | Filter by component type |
| tag | string | Filter by tag |
Create Abstract Attribute
POST /api/products/{product_id}/abstract-attributes
Content-Type: application/json
Request Body:
{
"component_type": "CUSTOMER",
"component_id": "main",
"attribute_name": "age",
"datatype_id": "int",
"display_names": [
{"name": "customer_age", "format": "SYSTEM"},
{"name": "Customer Age", "format": "HUMAN"}
],
"tags": [
{"name": "input", "order_index": 0},
{"name": "demographics", "order_index": 1}
],
"immutable": false
}
Get by Tag
GET /api/products/{product_id}/abstract-attributes/by-tag/{tag_name}
Datatypes
List Datatypes
GET /api/datatypes
Response:
{
"datatypes": [
{
"id": "int",
"primitive_type": "INT",
"description": "Integer number",
"constraints": null
},
{
"id": "percentage",
"primitive_type": "DECIMAL",
"description": "Percentage value 0-100",
"constraints": {
"min": 0,
"max": 100,
"precision": 2
}
}
]
}
Create Datatype
POST /api/datatypes
Content-Type: application/json
Request Body:
{
"id": "currency",
"primitive_type": "DECIMAL",
"description": "Currency amount with 2 decimal places",
"constraints": {
"min": 0,
"precision": 2,
"scale": 2
}
}
Validate Value
POST /api/datatypes/{datatype_id}/validate
Content-Type: application/json
Request Body:
{
"value": 150.50
}
Response:
{
"valid": true,
"errors": []
}
Enumerations
List Enumerations
GET /api/enumerations
Query Parameters:
| Parameter | Type | Description |
|———–|——|————-|
| template_type | string | Filter by template type |
Create Enumeration
POST /api/enumerations
Content-Type: application/json
Request Body:
{
"name": "CoverageType",
"template_type": "INSURANCE",
"description": "Types of insurance coverage",
"values": [
{"value": "BASIC", "order_index": 0},
{"value": "STANDARD", "order_index": 1},
{"value": "COMPREHENSIVE", "order_index": 2}
]
}
Add Enumeration Value
POST /api/enumerations/{enum_name}/values
Content-Type: application/json
Request Body:
{
"value": "PREMIUM",
"order_index": 3
}
Functionalities
List Functionalities
GET /api/products/{product_id}/functionalities
Create Functionality
POST /api/products/{product_id}/functionalities
Content-Type: application/json
Request Body:
{
"name": "PREMIUM_CALCULATION",
"description": "Calculate insurance premiums",
"required_abstract_attributes": [
"coverage",
"customer_age",
"final_premium"
]
}
Evaluate Functionality
POST /api/functionalities/{functionality_id}/evaluate
Content-Type: application/json
Request Body:
{
"inputs": {
"coverage": 250000,
"customer_age": 65
}
}
gRPC API
Service Definitions
syntax = "proto3";
package product_farm;
// Main evaluation service
service ProductFarmService {
// Evaluate rules for a product
rpc Evaluate(EvaluateRequest) returns (EvaluateResponse);
// Evaluate multiple inputs in batch
rpc BatchEvaluate(BatchEvaluateRequest) returns (BatchEvaluateResponse);
// Stream evaluation (for real-time data)
rpc EvaluateStream(stream EvaluateRequest) returns (stream EvaluateResponse);
// Validate rules without executing
rpc ValidateRules(ValidateRulesRequest) returns (ValidateRulesResponse);
// Get execution plan and DAG visualization
rpc GetExecutionPlan(GetExecutionPlanRequest) returns (ExecutionPlanResponse);
// Health check
rpc HealthCheck(HealthCheckRequest) returns (HealthCheckResponse);
}
// Product management service
service ProductService {
rpc CreateProduct(CreateProductRequest) returns (Product);
rpc GetProduct(GetProductRequest) returns (Product);
rpc UpdateProduct(UpdateProductRequest) returns (Product);
rpc DeleteProduct(DeleteProductRequest) returns (Empty);
rpc ListProducts(ListProductsRequest) returns (ListProductsResponse);
rpc CloneProduct(CloneProductRequest) returns (Product);
rpc SubmitProduct(SubmitProductRequest) returns (Product);
rpc ApproveProduct(ApproveProductRequest) returns (Product);
rpc RejectProduct(RejectProductRequest) returns (Product);
}
// Rule management service
service RuleService {
rpc CreateRule(CreateRuleRequest) returns (Rule);
rpc GetRule(GetRuleRequest) returns (Rule);
rpc UpdateRule(UpdateRuleRequest) returns (Rule);
rpc DeleteRule(DeleteRuleRequest) returns (Empty);
rpc ListRules(ListRulesRequest) returns (ListRulesResponse);
}
Message Types
message EvaluateRequest {
string product_id = 1;
map<string, Value> input_data = 2;
}
message EvaluateResponse {
map<string, Value> outputs = 1;
repeated RuleResult rule_results = 2;
uint64 total_execution_time_ns = 3;
uint32 execution_levels = 4;
}
message Value {
oneof value {
bool bool_value = 1;
int64 int_value = 2;
double float_value = 3;
string decimal_value = 4;
string string_value = 5;
ArrayValue array_value = 6;
ObjectValue object_value = 7;
}
}
message RuleResult {
string rule_id = 1;
map<string, Value> outputs = 2;
uint64 execution_time_ns = 3;
bool success = 4;
string error = 5;
}
Using grpcurl
# Health check
grpcurl -plaintext localhost:50051 product_farm.ProductFarmService/HealthCheck
# Create product
grpcurl -plaintext -d '{
"name": "my-product",
"template_type": "INSURANCE",
"description": "Test product"
}' localhost:50051 product_farm.ProductService/CreateProduct
# Evaluate
grpcurl -plaintext -d '{
"product_id": "my-product",
"input_data": {
"coverage": {"decimal_value": "250000"},
"customer_age": {"int_value": 65}
}
}' localhost:50051 product_farm.ProductFarmService/Evaluate
# Get execution plan
grpcurl -plaintext -d '{
"product_id": "my-product"
}' localhost:50051 product_farm.ProductFarmService/GetExecutionPlan
Error Responses
HTTP Status Codes
| Status | Description |
|---|---|
200 OK |
Request successful |
201 Created |
Resource created |
204 No Content |
Resource deleted |
400 Bad Request |
Invalid request body or parameters |
404 Not Found |
Resource not found |
409 Conflict |
Resource conflict (e.g., duplicate ID) |
422 Unprocessable Entity |
Validation error |
500 Internal Server Error |
Server error |
Error Response Format
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Rule expression is invalid",
"details": [
{
"field": "expression",
"message": "Unknown operator 'unknown_op'"
}
]
}
}
Common Error Codes
| Code | Description |
|---|---|
PRODUCT_NOT_FOUND |
Product with given ID does not exist |
RULE_NOT_FOUND |
Rule with given ID does not exist |
INVALID_STATUS_TRANSITION |
Cannot transition product to requested status |
PRODUCT_IMMUTABLE |
Cannot modify ACTIVE product |
CYCLIC_DEPENDENCY |
Rules form a cycle |
VALIDATION_ERROR |
Input validation failed |
EXPRESSION_ERROR |
JSON Logic expression error |
Rate Limiting
Currently, no rate limiting is implemented. For production deployments, consider adding rate limiting at the load balancer level.
Authentication
Authentication is not currently implemented. For production use, implement OAuth2/OIDC or API key authentication.
SDK Examples
Rust Client
use product_farm_client::ProductFarmClient;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = ProductFarmClient::connect("http://localhost:50051").await?;
let result = client.evaluate(
"my-product",
serde_json::json!({
"coverage": 250000,
"customer_age": 65
})
).await?;
println!("Premium: {}", result.outputs["final_premium"]);
Ok(())
}
Python Client
import grpc
import product_farm_pb2 as pb
import product_farm_pb2_grpc as pb_grpc
channel = grpc.insecure_channel('localhost:50051')
stub = pb_grpc.ProductFarmServiceStub(channel)
request = pb.EvaluateRequest(
product_id="my-product",
input_data={
"coverage": pb.Value(decimal_value="250000"),
"customer_age": pb.Value(int_value=65)
}
)
response = stub.Evaluate(request)
print(f"Premium: {response.outputs['final_premium'].decimal_value}")
JavaScript/TypeScript Client
const response = await fetch('http://localhost:8081/api/products/my-product/evaluate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
inputs: {
coverage: 250000,
customer_age: 65
}
})
});
const result = await response.json();
console.log('Premium:', result.outputs.final_premium);