The webhook output validation system extends the existing input validation
framework to validate responses being returned to webhook clients. This
ensures data quality and integrity at the API boundary before responses
are sent.
Output validation consists of three layers executed in sequence:
def_webhook_output_validationsIf any validation layer fails, the webhook returns an error response
with HTTP 400 status code.
RenderResult() called
|
v
Build results dictionary from:
- hooklookupsql query
- workflow results
- factory outputs
|
v
_validate_output_required()
- Check required RETURN params exist
- Check values are non-empty
|
v [PASS]
_validate_output_types()
- Validate INT/FLOAT/JSON types
- Skip STRING/TEXT types
|
v [PASS]
_validate_output(package, archetype)
- Execute custom SQL validations
- Check Buildoutputvalidation = 'Y'
|
v [PASS]
Persist results to database
|
v
Return (results, statuscode)
Added column:
`Buildoutputvalidation` char(1) DEFAULT 'N'
Set to 'Y' to enable custom SQL output validation for a webhook.
CREATE TABLE `def_webhook_output_validations` (
`WebhookCode` varchar(255) NOT NULL,
`Package` varchar(100) NOT NULL DEFAULT '',
`Description` varchar(255) NOT NULL,
`Block` varchar(255) NOT NULL DEFAULT '',
`Rank` int(11) NOT NULL DEFAULT 1,
`ValidationSql` mediumtext DEFAULT NULL,
`ValidationNote` varchar(255) DEFAULT NULL,
`RemoteConnection` varchar(255) DEFAULT '',
`Active` char(1) DEFAULT 'Y',
PRIMARY KEY (`WebhookCode`, `Package`, `Description`, `Block`),
KEY `idx_package` (`Package`),
KEY `idx_rank` (`Rank`),
KEY `idx_webhookcode` (`WebhookCode`),
KEY `idx_active` (`Active`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
Fields:
Added Active column for consistency:
`Active` char(1) DEFAULT 'Y'
Set Buildoutputvalidation = 'Y' in def_webhook table:
UPDATE def_webhook
SET Buildoutputvalidation = 'Y'
WHERE WebhookCode = 'CUSTOMER_CREATE'
AND Package = 'homechoice';
Mark output parameters as required in def_webhook_parameters:
UPDATE def_webhook_parameters
SET Required = 'Y'
WHERE WebhookCode = 'CUSTOMER_CREATE'
AND Parameter = 'customer_id'
AND PostType = 'RETURN';
Insert validation rules into def_webhook_output_validations:
INSERT INTO def_webhook_output_validations (
WebhookCode,
Package,
Description,
Rank,
ValidationSql,
ValidationNote,
Active
) VALUES (
'CUSTOMER_CREATE',
'homechoice',
'Verify customer_id is valid',
1,
'SELECT IF(EXISTS(
SELECT 1 FROM customers
WHERE customer_id = $customer_id$
), "Y", "N")',
'Customer ID not found in database',
'Y'
);
Validation SQL supports parameter substitution using $parameter$
syntax:
The _patch_output_param() method replaces placeholders with actual
values before execution.
Validation SQL must return a single value:
Check numeric range:
SELECT IF($total_amount$ BETWEEN 0 AND 999999, 'Y', 'N')
Verify referential integrity:
SELECT IF(EXISTS(
SELECT 1 FROM accounts
WHERE account_id = $account_id$
AND status = 'ACTIVE'
), 'Y', 'N')
Validate calculated fields:
SELECT IF(
$total_amount$ = $subtotal$ + $tax$ + $shipping$,
'Y',
'N'
)
Check against business rules:
SELECT IF(
$credit_score$ >= 600
OR $down_payment$ >= 0.20 * $loan_amount$,
'Y',
'N'
)
When output validation fails, the webhook returns:
{
"result": "Error",
"result_note": "Output validation failure: Customer ID not found",
"Statuscode": 400
}
If multiple validations fail, notes are joined with semicolons:
{
"result": "Error",
"result_note": "Output validation failure: Customer ID not found;
Total amount exceeds limit",
"Statuscode": 400
}
Enable DO_DEBUG = True to see detailed validation execution:
WEBHOOK: Process output validation
Output validation SQL: SELECT IF(...)
Output validation note: Customer ID not found
Output validation error: Exception...
Output validation failed: ...
Type validation is performed based on ValueType in
def_webhook_parameters:
| ValueType | Validation |
|---|---|
| INT | Must be valid integer |
| FLOAT | Must be valid numeric |
| JSON | Must be valid JSON object or parseable JSON |
| STRING | No validation (default) |
| TEXT | No validation |
INT validation:
# PASS
{"customer_id": "12345"}
# FAIL
{"customer_id": "abc123"}
# Error: "customer_id must be integer"
FLOAT validation:
# PASS
{"total_amount": "123.45"}
# FAIL
{"total_amount": "not-a-number"}
# Error: "total_amount must be numeric"
JSON validation:
# PASS
{"metadata": {"key": "value"}}
{"metadata": '{"key": "value"}'}
# FAIL
{"metadata": "invalid-json{"}
# Error: "metadata must be valid JSON"
If you currently have input validation but want to add output
validation:
Enable output validation:
UPDATE def_webhook
SET Buildoutputvalidation = 'Y'
WHERE WebhookCode = 'YOUR_WEBHOOK';
Mark required outputs:
UPDATE def_webhook_parameters
SET Required = 'Y'
WHERE WebhookCode = 'YOUR_WEBHOOK'
AND PostType = 'RETURN'
AND Parameter IN ('critical_field1', 'critical_field2');
Add custom validations:
Insert rows into def_webhook_output_validations for business
rule checks.
If starting fresh:
Buildoutputvalidation = 'Y'ValueType andRequired flagsdef_webhook_output_validationsfrom factory.web.WebHooks import WebHooks
# Initialize webhook
hook = WebHooks()
hook.Read('CUSTOMER_CREATE')
# Set test payload
payload = '{"customer_name": "John Doe", "email": "john@example.com"}'
hook.set_payload(payload, 'JSON')
hook.token = 'test-token'
# Execute
hook.commit()
# Get results (validates outputs)
results, status_code = hook.RenderResult()
# Check validation
if status_code == 400:
print(f"Validation failed: {results['result_note']}")
else:
print(f"Success: {results}")
UPDATE def_webhook
SET Buildoutputvalidation = 'N'
WHERE WebhookCode = 'CUSTOMER_CREATE';
Or disable specific validation rules:
UPDATE def_webhook_output_validations
SET Active = 'N'
WHERE WebhookCode = 'CUSTOMER_CREATE'
AND Description = 'Verify customer_id is valid';
Validation SQL Efficiency
EXISTS() instead of COUNT(*) when checking existenceExecution Order
Remote Connections
Validation Granularity
Error Messages
Security
Maintainability
_validate_output_required() -> boolValidates required RETURN parameters are present and non-empty.
Returns: True if validation passes, False otherwise
Sets on failure:
self.success = "Error"self.success_value = 400self.success_note = "Required output fields missing: ..."_validate_output_types() -> boolValidates output parameter types match expected ValueType.
Returns: True if validation passes, False otherwise
Sets on failure:
self.success = "Error"self.success_value = 400self.success_note = "Output type validation failed: ..."_validate_output(package: str, archetype: str) -> boolExecutes custom SQL validations from def_webhook_output_validations.
Parameters:
Returns: True if validation passes, False otherwise
Sets on failure:
self.success = "Error"self.success_value = 400self.success_note = "Output validation failure: ..."self.OutputValidationNote - Validation error detailsself.OutputValidationDescription - Validation descriptions_get_output_validation_list(package: str, archetype: str) -> listRetrieves active output validation rules from database.
Parameters:
Returns: List of tuples:
(ValidationSql, ValidationNote, RemoteConnection, Description)
_patch_output_param(query: str) -> strSubstitutes parameters in query using output results.
Parameters:
Returns: Query with substituted values
Substitutes:
$guid$ - Execution GUID$param$ - Values from self.params (input)$output$ - Values from self.results (output)Issue: Validation always passes even with invalid data
Issue: Validation SQL never executes
Issue: Parameter substitution not working
$parameter$ (lowercase, with $)Issue: Type validation fails unexpectedly
Buildoutputvalidation = 'Y' in def_webhookActive = 'Y' on validation rulesdef_webhook_validations usagefactory.web/WebHooks.mdfactory.core/ObjHook.yamlfactory.core/ObjWorkflow.mddef_webhook_output_validations tableBuildoutputvalidation flag to def_webhookActive column to def_webhook_validations for consistency