Shield
Send transactions
Send every transaction, including small ones: velocity and AML rules only see what they are sent.
Send a transaction
POST /api/Transaction/ExecuteTransaction takes SanctionScanner's transaction model and answers with the decision in the same call.
curl -X POST https://api.verifex.dev/api/Transaction/ExecuteTransaction \
-u "key_id:vfx_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"transactionID": "TXN-1001",
"transactionDate": "2026-10-01T09:30:00Z",
"amount": "6000.00",
"amountCurrencyCode": 944,
"direction": 2,
"source": 1,
"channel": 1,
"triggeredRuleSetKey": "your-ruleset-key",
"originatorNumber": "CUST-1001",
"originator": "Aysel Mammadova",
"originatorAccountCurrencyCode": 944,
"beneficiary": "Kamran Aliyev",
"beneficiaryAccountNumber": "AZ21NABZ00000000137010001944",
"beneficiaryAccountCurrencyCode": 944
}'Required fields
transactionID(your id; also the idempotency key),transactionDate(UTC, ending inZ),amount,amountCurrencyCode,originatorAccountCurrencyCode,beneficiaryAccountCurrencyCode,source,channel,direction,triggeredRuleSetKey, andoriginatorNumber(without it no per-customer rule can work).- Currencies are ISO 4217 numeric codes (
944AZN,840USD); the letter code is accepted too. direction: 1 Inbound, 2 Outbound, 3 both. It decides which side is your customer: inbound → the beneficiary, otherwise the originator. The other side is screened for sanctions and PEP.amountmay be a number or a decimal string and is parsed exactly; more decimals than the currency has is a 400, never rounded. Send numbers over 15 significant digits as strings.- A non-AZN amount needs
baseMultiply(the rate to AZN). Without it no amount rule can run, so the transaction is held forreviewwith reasonBASE_AMOUNT_UNKNOWN. - Unknown top-level fields are refused (
UNKNOWN_FIELDS). Shield-only inputs (device signals, SIMA profile, behaviour) go in an optionalshieldobject.
Fields specific checks need
- Counterparty name:
beneficiaryon an outbound transfer,originatoron an inbound one. It is what Shield screens for sanctions and PEP. Without it the transaction is allowed and marked not screened (COUNTERPARTY_NOT_SCREENED). AddbeneficiaryTypeId/originatorTypeId(1 individual, 2 entity) when you know it. customerIdentity: the sender's identity document number. The monthly AML checks (S2 activity, S3 beneficiaries) group a sender's transfers by it; without it they fall back tooriginatorNumber.optionalParameters→dailyLimit: the sender's daily transfer limit in AZN, as a plain decimal ({ "key": "dailyLimit", "value": "5000.00" }). S1 counts the days the sender reached it; Shield works out the limit hits itself.beneficiaryAccountNumber(card hash or IBAN): S3 and S4 count distinct accounts by it. Never a full card number.
Reading the answer
Every response uses the SanctionScanner envelope, with the HTTP status set to the same code. Act on result.shield.outcome; totalScore and triggeredAlarm keep SanctionScanner's meaning. The outcome is Shield's recommendation; your system carries it out:
clear: let it through.review: let it through; a case is opened for a person to look at afterwards.delay: hold it briefly, then review.escalate: hold it until a senior reviewer decides.block: stop it.
If the sanctions check could not complete, compliance.degraded is true and the outcome is never clear.
// Abridged: the fields to act on. revision / supersedesCapsuleId appear after a ChangeTransaction.
{
"httpStatusCode": 200, "isSuccess": true, "errorCode": null, "errorMessage": null, "extraInfo": null,
"result": {
"transactionId": "TXN-1001",
"totalScore": 70,
"triggeredAlarm": "Low",
"triggeredRulesList": [{ "ruleKey": "…", "score": 70 }],
"shield": {
"outcome": "review",
"riskScore": 70,
"humanReviewRequired": true,
"rulesTriggered": [{ "ruleId": "TXN_01", "name": "High-Value Transaction", "mode": "active",
"riskContribution": 70, "reason": "Amount … at or above …" }],
"compliance": { "screened": true, "sanctionsMatch": false, "pepMatch": false,
"degraded": false, "incomplete": false },
"capsuleId": "…",
"caseId": "…"
}
}
}Retries and idempotency
transactionID is the idempotency key per account. Re-sending the same body returns the stored answer unchanged (header X-Idempotent-Replay: true) and changes no counter, so retrying after a timeout is safe. The same id with a different body is 409 IDEMPOTENCY_KEY_REUSED — to change a transaction, use ChangeTransaction.
Card numbers are refused
A full card number anywhere in a request — any field, key, note, metadata, the path or the query string, with any separator — is refused with PAN_NOT_ALLOWED, on every Shield endpoint. Send masked numbers (416973******1234). A real IBAN in an IBAN or account-number field is accepted however it is spaced.
Late transactions
AML months follow transactionDate in your account's time zone (default Asia/Baku). A transaction may be dated in the current month, or in the previous month until day 5 of the current one; anything older is 400 VALIDATION_FAILED. A late transaction counts in its own month and is flagged late.
Changing a transaction
POST /api/Transaction/ChangeTransaction/{transactionId} (also Resend/{id} with a body) replaces the transaction and decides it again. The earlier decision is kept as evidence and marked superseded; the AML month figures drop the old version before counting the new one, so a transfer changed to declined, refunded or another amount leaves exact S1–S4 figures. A rule that already fired is not retracted. The transaction keeps its case — unless that case was closed, in which case a change that needs review opens a new one. A late retry of an earlier change gets the current answer and does not undo a newer change.
Warning
409 TRANSACTION_PERIOD_CLOSED).