Merchant Risk Screening
Screen merchant risk levels and obtain pre-transaction risk assessments.
Endpoint
POST /v1/merchantRequest
The request must include merchant details for risk assessment. Risk assessment is configured via Client Config. Please contact support@orca-fraud.com to enable.
Request Schema
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Unique merchant identifier |
name | string | Yes | Merchant business name |
status | enum | No | Merchant status: active, inactive, blocked |
timestamp | number | Yes | Unix timestamp in milliseconds |
riskLevel | enum | No | Pre-assigned risk level if available: unknown, low, medium, high, very_high. Note this differs from the response risk scale, which has medium_low and n/a instead of unknown |
mcc | string | No | Merchant Category Code |
tradingName | string | No | DBA (Doing Business As) name |
organizationId | string | No | Parent organization identifier. Set the same value on every merchant the organization owns, see Organizations with Multiple Merchants |
posMode | string | No | Point of Sale mode, if applicable |
businessSegment | string | No | Business industry segment |
businessType | string | No | Type of business |
averagePurchaseSize | string | No | Average transaction size, if applicable |
monthlyVolume | string | No | Expected monthly transaction volume, if applicable |
province | string | No | Merchant location province/state |
country | string | No | Merchant location country code |
paymentMethods | array | No | Accepted payment methods, array including: cash, vas, card, bank, crypto, ewa |
registrationNumber | string | No | Business registration/tax ID |
accountNumber | string | No | Merchant account number |
bankName | string | No | Name of the merchant’s bank |
fullName | string | No | Full name of the merchant’s owner or primary contact |
phone.phone | string | No | Contact phone number in E.164 format |
phone.country | string | No | ISO 3166-1 alpha-2 country code for the phone number |
phone.isVerified | boolean | No | Whether the phone number has been verified |
email.email | string | No | Contact email address |
email.isVerified | boolean | No | Whether the email address has been verified |
document | object | No | Identification document for the merchant’s owner, same shape as a User document: country, number, type, source, fullName |
limits | object | No | Transaction limits for the merchant, see Limits |
cashbackEnabled | boolean | No | Whether cashback is enabled for this merchant |
cashbackRate | number | No | Cashback rate as a percentage, 0 to 100. Requires cashbackEnabled |
settlementEnabled | boolean | No | Whether settlement is enabled for this merchant |
settlementType | string | No | Settlement arrangement, as your platform defines it. Requires settlementEnabled |
Limits
| Field | Type | Required | Description |
|---|---|---|---|
limits.limitCurrencyCode | string | Conditional | ISO 4217 currency code the limits are denominated in. Required whenever any of the three limit amounts below is sent |
limits.transactionLimit | number | No | Maximum value of a single transaction |
limits.dailyTransactionLimit | number | No | Maximum total value per day |
limits.monthlyTransactionLimit | number | No | Maximum total value per month |
limits.limitId | string | No | Your own identifier for this set of limits |
Conditional Requirements
Three pairs of fields must be sent together. Sending the second without the first is rejected with a
VALIDATION_ERROR:
| If you send | You must also send |
|---|---|
limits.transactionLimit, limits.dailyTransactionLimit or limits.monthlyTransactionLimit | limits.limitCurrencyCode |
cashbackRate | cashbackEnabled |
settlementType | settlementEnabled |
timestamp must also not be in the future.
Example Request
{
"id": "MERCH123456",
"name": "Example Retail Store",
"status": "active",
"mcc": "5411",
"tradingName": "The Retail Store",
"businessSegment": "retail",
"businessType": "sole_proprietorship",
"averagePurchaseSize": "120.50",
"monthlyVolume": "250000",
"country": "ZA",
"province": "Western Cape",
"registrationNumber": "2021/123456/07",
"timestamp": 1734100200000
}Organizations with Multiple Merchants
An organization that trades from more than one location — branch, store, outlet, franchise, depot —
registers each location as its own merchant record, and sets organizationId to the same parent
organization identifier on every one of them.
There is no separate object for a location and no location-specific fields. Each trading location is a merchant, described with the fields in the Request Schema above, and every value is held per merchant record. A merchant’s risk level, settlement arrangement, limits, MCC and business segment are therefore independent of the other merchants in the same organization.
{
"id": "MERCH000042",
"organizationId": "ORG123456",
"name": "Example Retail Woodstock",
"tradingName": "Woodstock Superstore",
"status": "active",
"riskLevel": "medium",
"mcc": "5411",
"businessType": "franchise",
"businessSegment": "grocery",
"averagePurchaseSize": "85.00",
"monthlyVolume": "310000",
"paymentMethods": ["card", "cash"],
"accountNumber": "62001234567",
"bankName": "Example Bank",
"settlementEnabled": true,
"settlementType": "daily_net",
"limits": {
"limitCurrencyCode": "ZAR",
"transactionLimit": 5000,
"dailyTransactionLimit": 100000,
"monthlyTransactionLimit": 2000000
},
"timestamp": 1734100200000
}A transaction sets merchantId to the id of the merchant it occurred at, so screening,
aggregation and rule evaluation are scoped to that merchant rather than to the organization as a
whole. Send either merchantId or a nested merchant object, never both.
organizationId is your own identifier and is stored as sent. It is not validated against your other
merchant records, so a value that does not match the rest of the organization silently leaves that
merchant ungrouped.
Values Held Per Merchant
Every field below is recorded against the individual merchant, and may differ from the values sent for other merchants in the same organization. Types, accepted formats and validation rules are as given in the Request Schema.
| Field | Required | Why it varies within an organization |
|---|---|---|
id | Yes | Must be unique across all your merchant records and stable for the life of the merchant |
name, tradingName | name yes | Each location trades under its own display and DBA name |
status | No | A single location can be inactive or blocked while the rest keep trading |
riskLevel | No | Risk posture is assessed per location, so it need not match the organization’s other merchants |
mcc, businessSegment, businessType | No | Locations within one organization can be classified differently |
averagePurchaseSize, monthlyVolume | No | Expected activity baseline for a newly onboarded merchant with no transaction history, see Expected Activity |
paymentMethods | No | Not every location accepts every method |
accountNumber, bankName | No | A location may settle to its own bank account |
settlementEnabled, settlementType | No | A location may settle under its own arrangement |
limits | No | Limits apply to the individual merchant, see Limits |
country, province | No | Physical location |
timestamp | Yes | Onboarding date of the individual merchant, see Merchant Tenure |
Settlement details and limits are read from the merchant record the transaction points at. A location that settles to a different bank account from the rest of its organization is represented by sending that account on its own merchant record, which is what lets beneficiary-mismatch screening evaluate the arrangement the transaction actually settles under.
Merchant Tenure
timestamp is recorded as the merchant’s creation date the first time we see a given id, and
is not changed by later updates to that record. Send the true onboarding date on the first call for a
new merchant. A timestamp in the future is rejected.
Every other field can be corrected by sending the same id again with the new values; previous
values are retained as merchant history.
Expected Activity
averagePurchaseSize and monthlyVolume are strings, and give a baseline for a newly onboarded
merchant that has no transaction history yet.
Send a plain decimal number, using . as the decimal separator, with no currency symbol, no
thousands separator and no sign, e.g. "85.00", "310000". Neither field carries its own currency:
send the currency in limits.limitCurrencyCode, and use one currency per merchant record.
Response
The response includes the risk assessment details and any triggered rules.
Example Response - Low Risk
{
"id": "MERCH123456",
"riskLevel": "low",
"recommendedAction": "ALLOW",
"timestamp": 1734100200000
}Example Response - High Risk
{
"id": "MERCH123456",
"riskLevel": "high",
"recommendedAction": "REVIEW",
"timestamp": 1734100200000,
"triggered": [
{
"id": "TRIG123456",
"name": "High volume transactions",
"level": "high",
"reason": "Merchant exceeds monthly transaction volume",
"recommendedAction": "REVIEW"
}
]
}Responses
| Status | Meaning |
|---|---|
200 | Merchant processed and screened |
400 | Validation failed, see Error Reference |
401 | Unauthorized |
403 | This endpoint is not enabled for your client type. Merchant screening is only available to business clients, contact support@orca-fraud.com |
500 | Internal server error |
Error Response Format
See the Error Reference section for a full description of API errors. All API errors follow a consistent JSON structure:
{
"error": {
"type": "ERROR_TYPE",
"message": "Human-readable error description",
"details": ["Additional error details (optional)"]
},
"timestamp": 1755602195137
}In the case of a validation error, the format will be as follows:
{
"error": {
"type": "VALIDATION_ERROR",
"message": "Request validation failed",
"details": [
"/id: Expected string"
]
},
"timestamp": 1755602195137
}Response Schema
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Unique identifier for entity screened |
riskLevel | enum | Yes | Risk level: low, medium_low, medium, high, very_high, n/a |
recommendedAction | enum | Yes | Action to take: ALLOW, REVIEW, BLOCK, STEP_UP_AUTH, STEP_UP_KYC, STEP_UP_ID, FLAG_FOR_MONITORING, REPORT_SUSPICIOUS, MESSAGE_USER, MESSAGE_MERCHANT |
triggered | array | No | Array of triggered risk rules |
timestamp | number | Yes | Unix epoch milliseconds |
Triggered Rule Schema
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier for the triggered rule |
name | string | Name of the triggered rule |
level | enum | Rule severity: low, medium_low, medium, high, very_high, n/a |
reason | string | Reason for the triggered rule |
recommendedAction | enum | Suggested action: ALLOW, REVIEW, BLOCK, STEP_UP_AUTH, STEP_UP_KYC, STEP_UP_ID, FLAG_FOR_MONITORING, REPORT_SUSPICIOUS, MESSAGE_USER, MESSAGE_MERCHANT |