{"openapi":"3.1.0","info":{"title":"Madie API","description":"Rule engine API for financial transaction monitoring","license":{"name":""},"version":"2.0.0"},"paths":{"/fields/{orgId}":{"get":{"tags":["fields"],"summary":"GET /fields/:orgId — List all custom fields","operationId":"list_fields","parameters":[{"name":"orgId","in":"path","description":"Organization ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"List of custom fields","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiResponse_Vec_CustomFieldResponse"}}}}}},"post":{"tags":["fields"],"summary":"POST /fields/:orgId — Register a custom field","operationId":"register_field","parameters":[{"name":"orgId","in":"path","description":"Organization ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomFieldDto"}}},"required":true},"responses":{"201":{"description":"Field registered"},"400":{"description":"Invalid field"},"409":{"description":"Field already exists"}}}},"/fields/{orgId}/{fieldName}":{"get":{"tags":["fields"],"summary":"GET /fields/:orgId/:fieldName — Get a specific custom field","operationId":"get_field","parameters":[{"name":"orgId","in":"path","description":"Organization ID","required":true,"schema":{"type":"string"}},{"name":"fieldName","in":"path","description":"Field name","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Field details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiResponse_CustomFieldResponse"}}}},"404":{"description":"Field not found"}}}},"/rules/{orgId}":{"get":{"tags":["rules"],"summary":"GET /rules/:orgId — List all rules for an organization","operationId":"get_rules","parameters":[{"name":"orgId","in":"path","description":"Organization ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"List of rules","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiResponse_Vec_RuleResponse"}}}}}},"post":{"tags":["rules"],"summary":"POST /rules/:orgId — Register a new rule","operationId":"register_rule","parameters":[{"name":"orgId","in":"path","description":"Organization ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RuleDto"}}},"required":true},"responses":{"201":{"description":"Rule created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiResponse_RuleIdResponse"}}}},"400":{"description":"Invalid rule"}}}},"/rules/{orgId}/{ruleId}":{"get":{"tags":["rules"],"summary":"GET /rules/:orgId/:ruleId — Get a rule by ID","operationId":"get_rule_by_id","parameters":[{"name":"orgId","in":"path","description":"Organization ID","required":true,"schema":{"type":"string"}},{"name":"ruleId","in":"path","description":"Rule ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Rule details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiResponse_RuleResponse"}}}},"404":{"description":"Rule not found"}}},"put":{"tags":["rules"],"summary":"PUT /rules/:orgId/:ruleId — Update a rule","operationId":"update_rule","parameters":[{"name":"orgId","in":"path","description":"Organization ID","required":true,"schema":{"type":"string"}},{"name":"ruleId","in":"path","description":"Rule ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RuleDto"}}},"required":true},"responses":{"200":{"description":"Rule updated"},"404":{"description":"Rule not found"}}},"delete":{"tags":["rules"],"summary":"DELETE /rules/:orgId/:ruleId — Delete a rule","operationId":"delete_rule","parameters":[{"name":"orgId","in":"path","description":"Organization ID","required":true,"schema":{"type":"string"}},{"name":"ruleId","in":"path","description":"Rule ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Rule deleted"},"404":{"description":"Rule not found"}}}},"/rules/{orgId}/{ruleId}/sql-preview":{"post":{"tags":["rules"],"summary":"POST /rules/:orgId/:ruleId/sql-preview — Preview evaluation SQL","operationId":"preview_evaluation_sql","parameters":[{"name":"orgId","in":"path","description":"Organization ID","required":true,"schema":{"type":"string"}},{"name":"ruleId","in":"path","description":"Rule ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EvaluationSqlPreviewRequest"}}},"required":true},"responses":{"200":{"description":"SQL preview generated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiResponse_Vec_EvaluationSqlPreviewItem"}}}},"404":{"description":"Rule not found"}}}},"/tables/{orgId}":{"get":{"tags":["tables"],"summary":"GET /tables/:orgId — List all tables","operationId":"list_tables","parameters":[{"name":"orgId","in":"path","description":"Organization ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"List of table names","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiResponse_Vec_String"}}}}}},"post":{"tags":["tables"],"summary":"POST /tables/:orgId — Create a custom table","operationId":"create_table","parameters":[{"name":"orgId","in":"path","description":"Organization ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTableDto"}}},"required":true},"responses":{"201":{"description":"Table created"},"400":{"description":"Invalid table definition"},"409":{"description":"Table already exists"}}}},"/tables/{orgId}/{tableName}":{"delete":{"tags":["tables"],"summary":"DELETE /tables/:orgId/:tableName — Drop a custom table","operationId":"drop_table","parameters":[{"name":"orgId","in":"path","description":"Organization ID","required":true,"schema":{"type":"string"}},{"name":"tableName","in":"path","description":"Table name","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Table dropped"},"404":{"description":"Table not found"}}}},"/tables/{orgId}/{tableName}/data":{"get":{"tags":["tables"],"summary":"GET /tables/:orgId/:tableName/data — Get paginated table data","operationId":"get_table_data","parameters":[{"name":"orgId","in":"path","description":"Organization ID","required":true,"schema":{"type":"string"}},{"name":"tableName","in":"path","description":"Table name","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","description":"Row limit (max 500)","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"offset","in":"query","description":"Row offset","required":false,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"Paginated table data"},"400":{"description":"Invalid pagination params"}}},"put":{"tags":["tables"],"summary":"PUT /tables/:orgId/:tableName/data — Bulk insert data","operationId":"bulk_insert_data","parameters":[{"name":"orgId","in":"path","description":"Organization ID","required":true,"schema":{"type":"string"}},{"name":"tableName","in":"path","description":"Table name","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"array","items":{}}}},"required":true},"responses":{"200":{"description":"Rows inserted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiResponse_BulkInsertResult"}}}},"400":{"description":"Invalid data"}}}},"/tables/{orgId}/{tableName}/data/{id}":{"patch":{"tags":["tables"],"summary":"PATCH /tables/:orgId/:tableName/data/:id — Update a single row","operationId":"update_table_data","parameters":[{"name":"orgId","in":"path","description":"Organization ID","required":true,"schema":{"type":"string"}},{"name":"tableName","in":"path","description":"Table name","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","description":"Row ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{}}},"required":true},"responses":{"200":{"description":"Row updated"},"404":{"description":"Row not found"}}}},"/tables/{orgId}/{tableName}/schema":{"get":{"tags":["tables"],"summary":"GET /tables/:orgId/:tableName/schema — Get table schema","operationId":"get_table_schema","parameters":[{"name":"orgId","in":"path","description":"Organization ID","required":true,"schema":{"type":"string"}},{"name":"tableName","in":"path","description":"Table name","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Table schema"},"404":{"description":"Table not found"}}}},"/transaction/{orgId}":{"post":{"tags":["transactions"],"summary":"POST /transaction/:orgId — Process a transaction","operationId":"process_transaction","parameters":[{"name":"orgId","in":"path","description":"Organization ID","required":true,"schema":{"type":"string"}},{"name":"ruleIds","in":"query","description":"Comma-separated rule IDs to evaluate","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TransactionRequestDto"},"examples":{"invalid_time_format":{"summary":"Invalid transaction_time format","value":{"transaction_id":"tx-1002","entity_id":"entity-123","amount":1250.75,"currency":"USD","transaction_date":"2026-02-13","transaction_time":"25:99:99","transaction_type":"TRANSFER","source_account_number":"100000001","source_account_name":"John Doe","source_bank_code":"001","source_account_type":"SAVINGS","beneficiary_account_number":"200000002","beneficiary_account_name":"Jane Roe","beneficiary_bank_code":"002","beneficiary_account_type":"CHECKING","beneficiary_is_cross_border":false,"pep":false}},"valid_transaction":{"summary":"Valid transaction payload","value":{"transaction_id":"tx-1001","entity_id":"entity-123","amount":1250.75,"currency":"USD","transaction_date":"2026-02-13","transaction_time":"14:35:59","transaction_type":"TRANSFER","source_account_number":"100000001","source_account_name":"John Doe","source_bank_code":"001","source_account_type":"SAVINGS","beneficiary_account_number":"200000002","beneficiary_account_name":"Jane Roe","beneficiary_bank_code":"002","beneficiary_account_type":"CHECKING","beneficiary_is_cross_border":false,"pep":false,"device_fingerprint":"abc123"}}}}},"required":true},"responses":{"200":{"description":"Transaction processed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiResponse_TransactionResponseDto"}}}},"400":{"description":"Invalid transaction","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"statusCode":400,"message":"transaction_time must be a valid time","code":0,"timestamp":"2026-02-13T14:36:00Z"}}}}}}}},"components":{"schemas":{"ActionDto":{"type":"object","description":"Incoming action definition.","required":["type","description"],"properties":{"description":{"type":"string"},"type":{"type":"string"}}},"ActionResponseDto":{"type":"object","description":"Action in a rule result.","required":["type","description"],"properties":{"description":{"type":"string"},"type":{"type":"string"}}},"ApiResponse_BulkInsertResult":{"type":"object","description":"Standard API response envelope matching the NestJS format.","required":["success","data"],"properties":{"data":{"type":"object","description":"Result of a bulk insert operation, including success stats and warnings","required":["inserted_count","skipped_count","total_count","warnings"],"properties":{"inserted_count":{"type":"integer","description":"Count of records successfully inserted","minimum":0},"skipped_count":{"type":"integer","description":"Count of records that were not inserted due to conflicts","minimum":0},"total_count":{"type":"integer","description":"Total number of records processed","minimum":0},"warnings":{"type":"array","items":{"$ref":"#/components/schemas/BulkInsertWarning"},"description":"Warnings for records that were not inserted"}}},"message":{"type":["string","null"]},"success":{"type":"boolean"}}},"ApiResponse_CustomFieldResponse":{"type":"object","description":"Standard API response envelope matching the NestJS format.","required":["success","data"],"properties":{"data":{"type":"object","description":"Outbound custom field representation.","required":["name","fieldType","orgId"],"properties":{"fieldType":{"type":"string"},"name":{"type":"string"},"orgId":{"type":"string"}}},"message":{"type":["string","null"]},"success":{"type":"boolean"}}},"ApiResponse_RuleIdResponse":{"type":"object","description":"Standard API response envelope matching the NestJS format.","required":["success","data"],"properties":{"data":{"type":"object","description":"Response for a registered rule ID.","required":["ruleId"],"properties":{"ruleId":{"type":"string"}}},"message":{"type":["string","null"]},"success":{"type":"boolean"}}},"ApiResponse_RuleResponse":{"type":"object","description":"Standard API response envelope matching the NestJS format.","required":["success","data"],"properties":{"data":{"type":"object","description":"Outbound rule representation.","required":["id","name","description","threshold","useAi","active","evaluations","actions"],"properties":{"actions":{"type":"array","items":{"$ref":"#/components/schemas/ActionDto"}},"active":{"type":"boolean"},"description":{"type":"string"},"evaluations":{"type":"array","items":{"$ref":"#/components/schemas/EvaluationResponse"}},"id":{"type":"string"},"name":{"type":"string"},"threshold":{"type":"number","format":"double"},"useAi":{"type":"boolean"}}},"message":{"type":["string","null"]},"success":{"type":"boolean"}}},"ApiResponse_TransactionResponseDto":{"type":"object","description":"Standard API response envelope matching the NestJS format.","required":["success","data"],"properties":{"data":{"type":"object","description":"Outbound transaction processing response.","required":["message","processedRules","triggeredRulesCount"],"properties":{"message":{"type":"string"},"processedRules":{"type":"array","items":{"$ref":"#/components/schemas/RuleResultDto"}},"trace":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/RequestTrace","description":"Per-request timing breakdown (only present when `BENCH_TRACE=1`)."}]},"triggeredRulesCount":{"type":"integer","minimum":0}}},"message":{"type":["string","null"]},"success":{"type":"boolean"}}},"ApiResponse_Vec_CustomFieldResponse":{"type":"object","description":"Standard API response envelope matching the NestJS format.","required":["success","data"],"properties":{"data":{"type":"array","items":{"type":"object","description":"Outbound custom field representation.","required":["name","fieldType","orgId"],"properties":{"fieldType":{"type":"string"},"name":{"type":"string"},"orgId":{"type":"string"}}}},"message":{"type":["string","null"]},"success":{"type":"boolean"}}},"ApiResponse_Vec_EvaluationSqlPreviewItem":{"type":"object","description":"Standard API response envelope matching the NestJS format.","required":["success","data"],"properties":{"data":{"type":"array","items":{"type":"object","description":"SQL preview response item.","required":["name","sql"],"properties":{"name":{"type":"string"},"sql":{"type":"string"}}}},"message":{"type":["string","null"]},"success":{"type":"boolean"}}},"ApiResponse_Vec_RuleResponse":{"type":"object","description":"Standard API response envelope matching the NestJS format.","required":["success","data"],"properties":{"data":{"type":"array","items":{"type":"object","description":"Outbound rule representation.","required":["id","name","description","threshold","useAi","active","evaluations","actions"],"properties":{"actions":{"type":"array","items":{"$ref":"#/components/schemas/ActionDto"}},"active":{"type":"boolean"},"description":{"type":"string"},"evaluations":{"type":"array","items":{"$ref":"#/components/schemas/EvaluationResponse"}},"id":{"type":"string"},"name":{"type":"string"},"threshold":{"type":"number","format":"double"},"useAi":{"type":"boolean"}}}},"message":{"type":["string","null"]},"success":{"type":"boolean"}}},"ApiResponse_Vec_String":{"type":"object","description":"Standard API response envelope matching the NestJS format.","required":["success","data"],"properties":{"data":{"type":"array","items":{"type":"string"}},"message":{"type":["string","null"]},"success":{"type":"boolean"}}},"BulkInsertResult":{"type":"object","description":"Result of a bulk insert operation, including success stats and warnings","required":["inserted_count","skipped_count","total_count","warnings"],"properties":{"inserted_count":{"type":"integer","description":"Count of records successfully inserted","minimum":0},"skipped_count":{"type":"integer","description":"Count of records that were not inserted due to conflicts","minimum":0},"total_count":{"type":"integer","description":"Total number of records processed","minimum":0},"warnings":{"type":"array","items":{"$ref":"#/components/schemas/BulkInsertWarning"},"description":"Warnings for records that were not inserted"}}},"BulkInsertWarning":{"type":"object","description":"Warning details for a conflicting record in bulk insert","required":["row_index","id_value","id_column","message"],"properties":{"id_column":{"type":"string","description":"ID column that had the conflict"},"id_value":{"type":"string","description":"ID value that caused the conflict"},"message":{"type":"string","description":"Additional context information"},"row_index":{"type":"integer","description":"Position of the record in the original input array (0-based)","minimum":0}}},"ColumnDefinitionDto":{"type":"object","description":"Column definition in a create-table request.","required":["name","columnType"],"properties":{"columnType":{"type":"string"},"name":{"type":"string"}}},"CreateTableDto":{"type":"object","description":"Request body for creating a custom table.","required":["tableName","tableType","columns"],"properties":{"columns":{"type":"array","items":{"$ref":"#/components/schemas/ColumnDefinitionDto"}},"tableName":{"type":"string"},"tableType":{"type":"string"}}},"CustomFieldDto":{"type":"object","description":"Request body for registering a custom field.","required":["name","fieldType"],"properties":{"fieldType":{"type":"string"},"name":{"type":"string"}}},"CustomFieldResponse":{"type":"object","description":"Outbound custom field representation.","required":["name","fieldType","orgId"],"properties":{"fieldType":{"type":"string"},"name":{"type":"string"},"orgId":{"type":"string"}}},"ErrorResponse":{"type":"object","description":"Structured error response matching the NestJS API format.","required":["statusCode","message","code","timestamp"],"properties":{"code":{"type":"integer","format":"int32"},"details":{"type":["string","null"]},"message":{"type":"string"},"statusCode":{"type":"integer","format":"int32","minimum":0},"timestamp":{"type":"string"}}},"EvaluationDto":{"type":"object","description":"Incoming evaluation definition. `operations` is a JSON-encoded string or raw array.","required":["name","operations","weight","description"],"properties":{"description":{"type":"string"},"name":{"type":"string"},"operations":{"description":"Operations can arrive as a JSON string (from NestJS-compatible clients)\nor as a raw JSON array."},"weight":{"type":"number","format":"double"}}},"EvaluationResponse":{"type":"object","description":"Outbound evaluation representation (operations serialized back to string).","required":["name","operations","weight","description"],"properties":{"description":{"type":"string"},"name":{"type":"string"},"operations":{"type":"string"},"weight":{"type":"number","format":"double"}}},"EvaluationResultDto":{"type":"object","description":"Evaluation result in a rule result.","required":["name","passed","weight"],"properties":{"error":{"type":["string","null"]},"name":{"type":"string"},"passed":{"type":"boolean"},"weight":{"type":"number","format":"double"}}},"EvaluationSqlPreviewItem":{"type":"object","description":"SQL preview response item.","required":["name","sql"],"properties":{"name":{"type":"string"},"sql":{"type":"string"}}},"EvaluationSqlPreviewRequest":{"type":"object","description":"SQL preview request body.","required":["entityId","transaction"],"properties":{"entityId":{"type":"string"},"evaluationName":{"type":["string","null"]},"transaction":{}}},"PaginationParams":{"type":"object","description":"Pagination query parameters.","properties":{"limit":{"type":["integer","null"],"format":"int64"},"offset":{"type":["integer","null"],"format":"int64"}}},"RequestTrace":{"type":"object","description":"Timing breakdown for a single transaction request.\n\nAll values are in **milliseconds** (f64).","required":["total_ms","validate_ms","fetch_rules_ms","insert_ms","eval_rules_ms","cleanup_ms"],"properties":{"cleanup_ms":{"type":"number","format":"double"},"eval_rules_ms":{"type":"number","format":"double"},"fetch_rules_ms":{"type":"number","format":"double"},"insert_ms":{"type":"number","format":"double"},"total_ms":{"type":"number","format":"double"},"validate_ms":{"type":"number","format":"double"}}},"RuleDto":{"type":"object","description":"Request body for creating or updating a rule.","required":["name","description","threshold","useAi","active","evaluations","actions"],"properties":{"actions":{"type":"array","items":{"$ref":"#/components/schemas/ActionDto"}},"active":{"type":"boolean"},"description":{"type":"string"},"evaluations":{"type":"array","items":{"$ref":"#/components/schemas/EvaluationDto"}},"name":{"type":"string"},"threshold":{"type":"number","format":"double"},"useAi":{"type":"boolean"}}},"RuleIdResponse":{"type":"object","description":"Response for a registered rule ID.","required":["ruleId"],"properties":{"ruleId":{"type":"string"}}},"RuleResponse":{"type":"object","description":"Outbound rule representation.","required":["id","name","description","threshold","useAi","active","evaluations","actions"],"properties":{"actions":{"type":"array","items":{"$ref":"#/components/schemas/ActionDto"}},"active":{"type":"boolean"},"description":{"type":"string"},"evaluations":{"type":"array","items":{"$ref":"#/components/schemas/EvaluationResponse"}},"id":{"type":"string"},"name":{"type":"string"},"threshold":{"type":"number","format":"double"},"useAi":{"type":"boolean"}}},"RuleResultDto":{"type":"object","description":"Rule result in a transaction response.","required":["id","name","triggered","score","action","evaluations"],"properties":{"action":{"type":"array","items":{"$ref":"#/components/schemas/ActionResponseDto"}},"evaluations":{"type":"array","items":{"$ref":"#/components/schemas/EvaluationResultDto"}},"id":{"type":"string"},"name":{"type":"string"},"score":{"type":"number","format":"double"},"triggered":{"type":"boolean"}}},"TransactionRequestDto":{"type":"object","description":"Inbound transaction payload with required typed fields.\n\nUnknown fields are preserved in `extra` and persisted into `custom_data`.","required":["transaction_id","entity_id","amount","currency","transaction_date","transaction_time","transaction_type","source_account_number","source_account_name","source_bank_code","source_account_type","beneficiary_account_number","beneficiary_account_name","beneficiary_bank_code","beneficiary_account_type","beneficiary_is_cross_border","pep"],"properties":{"amount":{"type":"number","format":"double"},"beneficiary_account_name":{"type":"string"},"beneficiary_account_number":{"type":"string"},"beneficiary_account_type":{"type":"string"},"beneficiary_bank_code":{"type":"string"},"beneficiary_is_cross_border":{"type":"boolean"},"currency":{"type":"string"},"entity_id":{"type":"string"},"pep":{"type":"boolean"},"source_account_name":{"type":"string"},"source_account_number":{"type":"string"},"source_account_type":{"type":"string"},"source_bank_code":{"type":"string"},"transaction_date":{"type":"string","format":"date","example":"2026-02-13"},"transaction_id":{"type":"string"},"transaction_time":{"type":"string","example":"14:35:59"},"transaction_type":{"type":"string"}},"additionalProperties":{}},"TransactionResponseDto":{"type":"object","description":"Outbound transaction processing response.","required":["message","processedRules","triggeredRulesCount"],"properties":{"message":{"type":"string"},"processedRules":{"type":"array","items":{"$ref":"#/components/schemas/RuleResultDto"}},"trace":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/RequestTrace","description":"Per-request timing breakdown (only present when `BENCH_TRACE=1`)."}]},"triggeredRulesCount":{"type":"integer","minimum":0}}}}},"tags":[{"name":"rules","description":"Rule management endpoints"},{"name":"tables","description":"Custom table management endpoints"},{"name":"fields","description":"Custom field management endpoints"},{"name":"transactions","description":"Transaction processing endpoints"}]}