Salv AML API (1.0.4)

Download OpenAPI specification:Download

Introduction

Welcome to the Salv AML API documentation.

The Salv API is built on HTTP. Our API is RESTful. It has predictable resource URLs. It returns HTTP response codes to indicate errors. It also accepts and returns JSON in the HTTP body. You can use your favorite HTTP/REST library for your programming language to use Salv API.

API definition to import into Postman can be downloaded at https://docs.salv.com/api/public.yaml

Recent API changes

  • 22.07.2024 - 1.0.8
    • Added information about rate limiting to Error handling section
  • 10.07.2024 - 1.0.7
    • Updated monitoring-related docs with new scenario type names
  • 28.06.2024 - 1.0.6
    • Removed fetch all monitoring alerts endpoint
  • 28.05.2024 - 1.0.5
    • Added removal notice to fetch all monitoring alerts endpoint
  • 03.11.2023 - 1.0.4
    • Added hits info to ALERT_CREATED webhook payload
  • 22.09.2023 - 1.0.3
    • Updated authentication documentation
  • 12.05.2023 - 1.0.2
    • Added case management section and endpoint to update alert status
  • 09.01.2023 - 1.0.1
    • Specified that IDs of persons and transactions are stored as case-sensitive text values
    • Improved language in overview of three endpoints to create or modify person
  • 28.12.2022 - 1.0.0
    • Refactored the API documentation structure and introduced changelog

Getting started

Signing up

Contact our sales team at sales@salv.com to sign up for service.

Terms used

  • Client — organisation that is using or intends to use Salv service.
  • Client application — application owned by client. Will probably integrate with this API.
  • Person — natural person or legal entity, one of the customers of the client.
  • Transaction — monetary transaction performed by one of the persons.

General workflow

While the integration process may wary in each case, on average the steps to use the service look as follows:

  • Upload historical persons and transactions data using CSV data upload endpoint.
  • Configure client application to upload new persons and transactions automatically using addPerson endpoint and addTransaction endpoint.
  • Configure monitoring, screening and risks. That would include monitoring scenarios, risk rules and a number of settings for screening. Currently, most of those things can only be configured through Salv UI. However, some of the configuration is available in open API as well:
  • Salv will produce monitoring alerts, screening alerts and risk levels for provided persons and transactions based on configuration provided:
  • In order to receive the data about new screening and monitoring alerts in real time, client would need to configure webhooks. Configuring a webhook will cause Salv to notify client application about a particular event, for example about a creation of a new alert or change of a risk level for any person.

Alternatively, all the monitoring alerts, screening alerts and risk levels are available in Salv UI, along with a lot of features to help manage and organize the process of examining the alerts and risks.

Authentication

OAuth2

In order to use the API, you need to generate client credentials using Salv UI. Never share your secret keys. Keep them guarded and secure.

We use OAuth2 client credentials flow to issue our API tokens. By default our API tokens have expiration time of 50 years, so effectively they never expire. Please do not make any assumptions about the content of the access_token. At the moment we use a JWT token, but it can change to any other string with the future updates.

An API token can be invalidated using Salv UI by deleting the client credentials that were used to generate the token.

Please make sure you only request it once per reasonable amount of time, as oauth/token endpoint has a rate limit of 10 requests per minute per IP address.

Token URL for sandbox environment is https://demo.salv.com/oauth/token

Security Scheme Type: OAuth2
Flow type: clientCredentials
Token URL: https://app.salv.com/oauth/token
Scopes:
  • aml -

    Can use AML API

Authentication flow examples

With cURL

$ curl -X POST -u "{clientId}:{clientSecret}" -d "grant_type=client_credentials" "https://app.salv.com/oauth/token"
{
  "access_token": "{yourBearerToken}",
  "expires_in": 1576799999,
  "token_type": "Bearer"
}

With Python Requests

import requests

resp = requests.post('https://app.salv.com/oauth/token', auth=({clientId}, {clientSecret}), data={'grant_type': 'client_credentials'})
print(resp.json())

{
  "access_token": "{yourBearerToken}",
  "expires_in": 1576799999,
  "token_type": "Bearer"
}

Compatibility

  • New properties may be added to a representation at any time. Make sure that your API client will ignore any unknown properties.
  • Removal or alteration of an existing property will be handled through versioning.

Error handling

Errors 5XX

All requests which return a response with status code 500-599 should be retried after some interval.

If a retry of a failed request fails again, it should be retried again until it succeeds. Retrying a request that repeatedly fails should be done at increasingly longer interval.

Errors 429

If a request returns a response with status code 429, it means that the client has exceeded the rate limit specified in contract.

The request should be retried after some interval.

Data upload from CSV

Steps to upload data using CSV

  1. Format the csv file and add all the mandatory data fields indicated in the User Manual

  2. Upload the csv file via API. Note: the upload API response contains the id that will to be used in status check API call (step 3). When a data file has been successfully uploaded, an HTTP response with 202 status code is returned. A response with 202 status code indicates that the file has been uploaded successfully and the system will try to process the data. It does not guarantee that data will be successfully processed.

    What will happen next?

    1. First comes the validation phase:

      1. If the Schemas are added in the UI, then our system will validate the file against the schemas (e.g. if there is a field added with DATE type, then system will validate if the date format is correct)
      2. If no Schemas are added in the UI, then our system will consider all fields as STRING

      Note: during this validation phase we do not check if the transaction has been added to the system before.

      1. If there are any errors, then no data will be uploaded. In this case calling check status API call will return FAILED status and a error message for the the first error encountered. The error message will be in the reason field in a human readable text.
      2. If there are no errors, then the upload phase starts.
    2. Second (if no errors occurred) comes the upload phase: our system will start uploading and saving data in batches of 1000, and in case of transaction file will check for duplicates and if the person is present in the system.

      1. If no errors occur, then the file is uploaded successfully.

      2. If an error occurs, then when step no 3 (check status API call) is done, then:

        1. system will show the first error and give the id of the person or transaction that caused the error.
        2. system will show the id of the last uploaded person or transaction and the id of the first person or transaction that was not uploaded.

        and,

        1. the data following the last uploaded person or transaction will not be uploaded.
    • Example:

      Csv file has 200 000 rows. There is a duplicate id on row 87 456. In validation phase duplicate errors are not checked thus the file will move on to the upload phase. First 87 000 rows are uploaded without any errors. Rows starting from 87 001 will not get uploaded because there is a duplicate error in the batch (the patch of 1000 that gets checked). System will show that there is a duplicate, will show the id of the duplicate Person or Transaction and will show the last uploaded Person or Transaction id and the last id that was not added.

      Example of an upload phase error message:

      {
          "status": "PARTIALLY_COMPLETED",
          "reason": "The upload appears to contain transactions (id ameio-1234567) that have already been added to Salv. Please make sure that your upload does not contain any duplicates or transactions that have already been added to Salv. The first transaction not added to Salv with this upload has id fmeio-1234567. Last processed and saved row was with id: dseio-1234567"
      }
      
  3. In order to check the status of the file upload either use get status of data upload API call or check Data upload page on app.salv.com. This will provide the status and any further information about the errors.

Upload data from a CSV file

Upload a list of person, person relation, or transaction data in a CSV format. Files up to 100M are supported. File processing happens asynchronously.

Authorizations:
OAuth2
query Parameters
type
required
string (ApiDataUploadType)
Enum: "PERSON" "PERSON_RELATION" "TRANSACTION" "TRANSACTION_UPDATE"
Example: type=TRANSACTION

Which type of data to upload

Request Body schema: multipart/form-data
required
file
required
string <binary>

Responses

Response samples

Content type
application/json
{
  • "id": 123,
  • "fileName": "transactions-2021-11-29.csv",
  • "type": "TRANSACTION"
}

Get status of data upload

Authorizations:
OAuth2
path Parameters
uploadId
required
integer <int64> >= 1

ID of data upload

Responses

Response samples

Content type
application/json
{
  • "status": "COMPLETED",
  • "reason": "string"
}

Persons & Transactions

This section contains all operations managing the state of persons and transactions.

There are three endpoints for creating or modifying a person, make sure to choose the right one for your task:

  • Create new person will create a new person if a person with the same ID does not already exist in our system.
  • Patch person partially updates an existing person if this person exists in the system. A request to this endpoint will only update or add attributes which are provided in the request. Use this endpoint to update selected attributes of a person or to add new attributes without changing attributes not present in the request payload. This endpoint may also be used to update person type.
  • Update person updates an existing person if this person exists in the system. A request to this endpoint must contain both person type and attributes. A request to this endpoint overwrites both person type and attributes with the values given in the request.

ID of a person or a transaction is a case-sensitive text field in our system. Salv does not transform the ID in any way. Only the following characters are allowed: Alphanumerics a-zA-Z0-9, hyphens -, underscores _, colons :, dots ..

All requests must use ISO-8601 formatted timestamps. That includes defined fields (e.g. timestamp property in "Create new transaction" request) and custom key-value pairs (e.g. under attributes property in "Create new person" request).

For any data passed in person or transaction objects, the keys must adhere to the following rules:

  • It must not contain a "."
  • It must not consist of whitespaces

Example: Uploading person and transaction

This example shows how to upload data to use it later for monitoring or screening.

1. Add a person if not already added

Add a new person, according to the documentation at addPerson(POST).

If the person has already been added to Salv, then skip this step.

With cURL

curl --request POST \
  --url https://app.salv.com/api/v2/persons \
  --header 'Authorization: Bearer {yourBearerToken}' \
  --header 'Content-Type: application/json' \
  --data '{
  "id": "bbt12345",
  "type": "INDIVIDUAL",
  "attributes": {
    "first_name": "Sheldon",
    "last_name": "Cooper",
    "dob": "1983-05-12",
    "city": "Pasadena",
    "street_address": "2311 North Los Robles Avenue",
    "country": "US",
    "onboarding_date": "2019-04-16",
    "email": "[email protected]",
    "phone_number": "+1582693582",
    "id_document": "passport",
    "id_country": "US",
    "gender": "F"
  }
}'

With Python Requests

import requests

url = "https://app.salv.com/api/v2/persons"

payload = {
    "id": "bbt12345",
    "type": "INDIVIDUAL",
    "attributes": {
        "first_name": "Sheldon",
        "last_name": "Cooper",
        "dob": "1983-05-12",
        "city": "Pasadena",
        "street_address": "2311 North Los Robles Avenue",
        "country": "US",
        "onboarding_date": "2019-04-16",
        "email": "[email protected]",
        "phone_number": "+1582693582",
        "id_document": "passport",
        "id_country": "US",
        "gender": "F"
    }
}
headers = {
    "Content-Type": "application/json",
    "Authorization": "{yourBearerToken}"
}

response = requests.request("POST", url, json=payload, headers=headers)

print(response.text)

2. Add a transaction

Add a new transaction, according to the documentation at addTransaction(POST).

With cURL

curl --request POST \
  --url https://app.salv.com/api/v1/persons/bbt12345/transactions \
  --header 'Authorization: Bearer {yourBearerToken}' \
  --header 'Content-Type: application/json' \
  --data '{
  "id": "aeio-1234567",
  "attributes": {
    "sender_name": "Sheldon Cooper",
    "sender_account": "GB123456789",
    "receiver_name": "Leonard Hofstadter",
    "receiver_account": "DE987654321",
    "amount": "15678.34",
    "type": "wire_transfer",
    "direction": "I",
    "timestamp": "2022-01-20T08:48:39.000Z"
  }
}'

With Python Requests

import requests

url = "https://app.salv.com/api/v1/persons/bbt12345/transactions"

payload = {
    "id": "aeio-1234567",
    "attributes": {
        "sender_name": "Sheldon Cooper",
        "sender_account": "GB123456789",
        "receiver_name": "Leonard Hofstadter",
        "receiver_account": "DE987654321",
        "amount": "15678.34",
        "type": "wire_transfer",
        "direction": "I",
        "timestamp": "2022-01-20T08:48:39.000Z"
    }
}
headers = {
    "Content-Type": "application/json",
    "Authorization": "{yourBearerToken}"
}

response = requests.request("POST", url, json=payload, headers=headers)

print(response.text)

Create new person

Attributes supported maximum nesting depth is 2.

Authorizations:
OAuth2
Request Body schema: application/json
required
id
required
string non-empty

External unique person ID

type
required
string (ApiPersonType)
Enum: "INDIVIDUAL" "PRIVATE" "BUSINESS" "LEGAL" "UNDEFINED"

Use INDIVIDUAL LEGAL UNDEFINED.

PRIVATE and BUSINESS are deprecated.

required
object

Key value pair where key is string and value one of following: array, object, string. Keys can be in snake case (e.g. first_name) or camel case (e.g. firstName)

For array objects, an id key is strongly recommended to be present. An id is needed to support updates. If an id is missing, then this item in an array cannot be updated.

For individual persons attributes first_name last_name dob street_address country onboarding_date email phone_number id_document id_country gender are expected and used in typical monitoring scenarios. All attributes are optional.

For legal persons attributes company_name onboarding_date country city street_address phone_number email industry business_reg_number expected_monthly_turnover are expected and used in typical monitoring scenarios. All attributes are optional.

A typical attribute for legal persons is shareholders which can be used for screening shareholders of a legal person.

In addition to the typical attributes listed here, custom attributes may also be used.

Timestamps may contain up to 6 digits after seconds.

Responses

Request samples

Content type
application/json
Example
{
  • "id": "bbt12345",
  • "type": "INDIVIDUAL",
  • "attributes": {
    }
}

Patch person

Partial person update. Only attributes present in request will be added/updated. Attributes supported maximum nesting depth is 2.

This endpoint also supports optional person type update.

In case the operation is successful there will be an empty response.

Examples

cURL

$ curl --request PATCH \
  --url https://app.salv.com/api/v2/persons/{personId} \
  --header 'Authorization: Bearer {yourBearerToken}' \
  --header 'Content-Type: application/json' \
  --data '{
    "type": "LEGAL",
    "attributes": {
      "company_name": "Best Vacuum Cleaning Ltd."
    }
  }'

With Python Requests

import requests

payload = {
  "attributes": {
    "phone_number": "(198)747-8241x9209",
    "shareholders": [{
      "id": 1,
      "name": "Rose Doe",
      "dob": "1970-01-01",
      "email": "[email protected]"
    }]
  }
}

response = requests.post(
   'https://app.salv.com/api/v2/persons/{personId}',
   auth = ({clientId}, {clientSecret}),
   json = payload)

response.raise_for_status()
Authorizations:
OAuth2
path Parameters
personId
required
string non-empty

External unique person ID

Request Body schema: application/json
required
type
string (ApiPersonType)
Enum: "INDIVIDUAL" "PRIVATE" "BUSINESS" "LEGAL" "UNDEFINED"

Use INDIVIDUAL LEGAL UNDEFINED.

PRIVATE and BUSINESS are deprecated.

required
object

Key value pair where key is string and value one of following: array, object, string. Keys can be in snake case (e.g. first_name) or camel case (e.g. firstName)

For array objects, an id key is strongly recommended to be present. An id is needed to support updates. If an id is missing, then this item in an array cannot be updated.

Responses

Request samples

Content type
application/json
Example
{
  • "attributes": {
    }
}

Update person

Person update. Attributes in the request will be used to replace existing ones and type will be updated. Attributes supported maximum nesting depth is 2.

In case the operation is successful there will be an empty response.

Examples

cURL

$ curl --request PUT \
  --url https://app.salv.com/api/v2/persons/{personId} \
  --header 'Authorization: Bearer {yourBearerToken}' \
  --header 'Content-Type: application/json' \
  --data '{
    "type": "LEGAL",
    "attributes": {
      "company_name": "Best Vacuum Cleaning Ltd."
    }
  }'

With Python Requests

import requests

payload = {
  "type": "INDIVIDUAL",
  "attributes": {
    "phone_number": "(198)747-8241x9209",
    "shareholders": [{
      "id": 1,
      "name": "Rose Doe",
      "dob": "1970-01-01",
      "email": "[email protected]"
    }]
  }
}

response = requests.post(
   'https://app.salv.com/api/v2/persons/{personId}',
   auth = ({clientId}, {clientSecret}),
   json = payload)

response.raise_for_status()
Authorizations:
OAuth2
path Parameters
personId
required
string non-empty

External unique person ID

Request Body schema: application/json
required
type
required
string (ApiPersonType)
Enum: "INDIVIDUAL" "PRIVATE" "BUSINESS" "LEGAL" "UNDEFINED"

Use INDIVIDUAL LEGAL UNDEFINED.

PRIVATE and BUSINESS are deprecated.

required
object

Key value pair where key is string and value one of following: array, object, string. Keys can be in snake case (e.g. first_name) or camel case (e.g. firstName)

For array objects, an id key is strongly recommended to be present. An id is needed to support partial updates through PATCH API call.

Responses

Request samples

Content type
application/json
Example
{
  • "type": "INDIVIDUAL",
  • "attributes": {
    }
}

Fetch all persons

NB! If there are more than 10000 results then the API shows the number of total results as 10000 and you can't paginate past the 10000th result.

Authorizations:
OAuth2
query Parameters
page
required
integer >= 0
Default: 0

Number of the page

size
required
integer [ 1 .. 50 ]
Default: 20

Size of the page

query
string [ 1 .. 200 ] characters

Search query

attribute
string non-empty

Search query attribute

category
string
Enum: "COUNTERPARTY" "CUSTOMER"

Person category

orderBy
string [ 1 .. 200 ] characters

Order attribute

direction
string (ApiSortDirection)
Enum: "ASC" "DESC"

Order direction

operator
string (ApiSearchOperator)
Default: "IS_EXACTLY"
Enum: "IS_EXACTLY" "CONTAINS" "STARTS_WITH"
Example: operator=IS_EXACTLY

Search query operator which defines whether the search is using the exact term or matches only part of the string

Responses

Response samples

Content type
application/json
{
  • "number": 0,
  • "size": 20,
  • "total": 1,
  • "content": [
    ]
}

Create new person (v1) Deprecated

Use Create new person v2 instead.

Authorizations:
OAuth2
Request Body schema: application/json
required
id
required
string non-empty

External unique person ID

type
required
string (ApiPersonType)
Enum: "INDIVIDUAL" "PRIVATE" "BUSINESS" "LEGAL" "UNDEFINED"

Use INDIVIDUAL LEGAL UNDEFINED.

PRIVATE and BUSINESS are deprecated.

object

Person attributes key value pair. Keys first_name, last_name, company_name, dob, email, phone_number, street_address, city and country are expected and can be used for online monitoring, but are optional. Expected keys that include multiple phrases can be in snake case (e.g. first_name) or camel case (e.g. firstName). Custom key value pairs are allowed, but can only be used for offline monitoring.

Responses

Request samples

Content type
application/json
Example
{
  • "id": "bbt12345",
  • "type": "INDIVIDUAL",
  • "attributes": {
    }
}

Response samples

Content type
application/json
Example
{
  • "id": "bbt12345",
  • "type": "INDIVIDUAL",
  • "attributes": {
    }
}

Fetch person by id

Authorizations:
OAuth2
path Parameters
personId
required
string non-empty

ID of person to return

Responses

Response samples

Content type
application/json
Example
{
  • "id": "bbt12345",
  • "type": "INDIVIDUAL",
  • "attributes": {
    },
  • "status": "NEW",
  • "createdTime": "2019-08-24T14:15:22Z",
  • "snoozeConfiguration": {
    },
  • "category": "CUSTOMER"
}

Patch person (v1) Deprecated

Use Patch person v2 instead.

Authorizations:
OAuth2
path Parameters
personId
required
string non-empty

ID of person to return

Request Body schema: application/json
required
id
required
string non-empty

External unique person ID

type
string (ApiPersonType)
Enum: "INDIVIDUAL" "PRIVATE" "BUSINESS" "LEGAL" "UNDEFINED"

Use INDIVIDUAL LEGAL UNDEFINED.

PRIVATE and BUSINESS are deprecated.

object

Person attributes key value pair

status
string
Enum: "NEW" "REVIEWED" "MONITORING" "SUSPENDED" "TERMINATED" "TERMINATED_AND_REPORTED"

Person current status

createdTime
string <date-time>
object (ApiSnoozeConfiguration)
Array of objects (ApiSnoozeConfiguration)

Configurations for scenario based snooze configurations

category
string (ApiPersonCategory)
Value: "COUNTERPARTY"

Responses

Request samples

Content type
application/json
{
  • "id": "user-00000009",
  • "type": "INDIVIDUAL",
  • "attributes": {
    },
  • "status": "NEW",
  • "createdTime": "2019-08-24T14:15:22Z",
  • "snoozeConfiguration": {
    },
  • "scenarioSnoozeConfigurations": [
    ],
  • "category": "COUNTERPARTY"
}

Response samples

Content type
application/json
{
  • "id": "user-00000009",
  • "type": "INDIVIDUAL",
  • "attributes": {
    },
  • "status": "NEW",
  • "createdTime": "2019-08-24T14:15:22Z",
  • "snoozeConfiguration": {
    },
  • "scenarioSnoozeConfigurations": [
    ],
  • "category": "COUNTERPARTY"
}

Fetch all person transactions

NB! If there are more than 10000 results then the API shows the number of total results as 10000 and you can't paginate past the 10000th result.

Authorizations:
OAuth2
path Parameters
personId
required
string non-empty

ID of person

query Parameters
page
required
integer >= 0
Default: 0

Number of the page

size
required
integer [ 1 .. 50 ]
Default: 20

Size of the page

query
string [ 1 .. 200 ] characters

Search query

orderBy
string [ 1 .. 200 ] characters

Order attribute

direction
string (ApiSortDirection)
Enum: "ASC" "DESC"

Order direction

Responses

Response samples

Content type
application/json
{
  • "number": 0,
  • "size": 0,
  • "total": 0,
  • "content": [
    ]
}

Create new transaction

Authorizations:
OAuth2
path Parameters
personId
required
string non-empty
Example: bbt12345

ID of person

Request Body schema: application/json
required
id
required
string non-empty

External unique transaction ID

object

Transaction attributes key value pair.

amount is the only required attribute.

Attributes type direction timestamp are expected for all transactions.

Other attributes vary depending on the kind of transaction. Please see the examples on the right hand side for inspiration.

Value for timestamp MUST use ISO-8601 format.

Responses

Request samples

Content type
application/json
Example
{
  • "id": "aeio-1234567",
  • "attributes": {
    }
}

Response samples

Content type
application/json
Example
{
  • "id": "aeio-1234567",
  • "personId": "bbt12345",
  • "attributes": {
    }
}

Fetch transaction by id

Authorizations:
OAuth2
path Parameters
personId
required
string non-empty

ID of person

transactionId
required
string non-empty

ID of transaction to return

Responses

Response samples

Content type
application/json
Example
{
  • "id": "aeio-1234567",
  • "personId": "bbt12345",
  • "attributes": {
    }
}

Update transaction

Updates transaction. Currently status is the only attribute that can be updated.

Authorizations:
OAuth2
path Parameters
personId
required
string non-empty
Example: user-00000009

ID of person

transactionId
required
string non-empty
Example: transaction-000000000009

ID of transaction

Request Body schema: application/json
required
id
required
string non-empty

External unique transaction ID

object

Transaction attributes key value pairs. Adding completely new keys or updating status key, is supported. All others attributes in request are ignored.

Responses

Request samples

Content type
application/json
{
  • "id": "transaction-000000000009",
  • "attributes": {
    }
}

Response samples

Content type
application/json
{
  • "id": "transaction-000000000009",
  • "attributes": {
    },
  • "createdTime": "2019-08-24T14:15:22Z",
  • "personId": "string"
}

Fetch history of status changes for the person

Authorizations:
OAuth2
path Parameters
personId
required
string non-empty

ID of person

Responses

Response samples

Content type
application/json
{
  • "statuses": [
    ],
  • "snoozeHistory": [
    ],
  • "latestSanctionCheck": {
    }
}

Webhooks

Setting up

You can set up webhook configuration on the webhooks page.

Please note that you must have an administrator role.

Webhook retry mechanism

If sending the webhook fails, we'll retry sending it until it is accepted. Retry attempts happen regularly for up to 7 days, at increasing time intervals:

  • 2 minutes
  • 5 minutes
  • 10 minutes
  • 15 minutes
  • 30 minutes
  • 1 hour
  • 2 hours
  • 4 hours

After that, retries happen every 8 hours for the following 7 days. For tackling transient errors (like a momentary network glitch) we retry every request 3 times with 1 second interval.

The timeout limit for webhooks is 1 second for connect and 9 seconds for read timeout.

Webhook triggers

Salv supports sending out certain webhooks based on the following event triggers:

  • Trigger -> Supported Actions
  • ALERT -> ALERT_CREATED
  • ALERT_STATUS -> ALERT_STATUS_UPDATED
  • SCREENING_ALERT -> SCREENING_ALERT_CREATED
  • SCREENING_ALERT_STATUS -> SCREENING_ALERT_STATUS_UPDATED
  • FINAL_RISK -> FINAL_RISK_UPDATED
  • PERSON_STATUS -> PERSON_STATUS_UPDATED
  • PERSON -> The name of the succeeding decision rule. Example: PERSON_CLEARED
  • TRANSACTION -> The name of the succeeding decision rule. Example: TRANSACTION_CLEARED
  • CUSTOM_LIST_RECORD -> CUSTOM_LIST_RECORD_CREATED, CUSTOM_LIST_RECORD_UPDATED, CUSTOM_LIST_RECORD_ARCHIVED

Decision webhook

When configuring decision webhook rules, every time an alert related to a person or a transaction is changed, this set of rules will be run on all the related alerts for that person/transaction.
If any of the rules succeeds, it will send out a webhook with the name of that rule in action parameter. For more information, check Salv user manual.

action
string
createdTime
string <date-time>

Event created time

required
object (AlertCreatedEventMeta)
Example
{
  • "action": "ALERT_CREATED",
  • "createdTime": "2019-08-24T14:15:22Z",
  • "metadata": {
    }
}

Custom Lists

Endpoints to add, update, delete and get custom list records.

Custom Lists can be used in two ways:

  • Custom Lists can be used to screen persons and transactions against them in the same way it works for Dow Jones lists. For example, if a client has some in-house black list, it can be added to screening as custom list.
  • Custom Lists can be referenced in monitoring scenarios. For example, a scenario may trigger when the transaction amount is higher than some threshold and the recipient country is in custom list of high risk countries.

Get batch records

Authorizations:
OAuth2
path Parameters
customListId
required
string <uuid>

ID of the custom list

query Parameters
page
required
integer >= 0
Default: 0

Number of the page

size
required
integer [ 1 .. 50 ]
Default: 20

Size of the page

Responses

Response samples

Content type
application/json
{
  • "number": "0,",
  • "size": "20,",
  • "total": 1223,
  • "content": [
    ]
}

Create custom list record

Authorizations:
OAuth2
path Parameters
customListId
required
string <uuid>

ID of the custom list

Request Body schema: application/json
required
id
required
string non-empty

External unique record ID

required
object

Key value pair where key is string and value one of following: array, object, string. Keys can be in snake case (e.g. first_name) or camel case (e.g. firstName)

Responses

Request samples

Content type
application/json
Example
{
  • "id": "sdf12345",
  • "attributes": {
    }
}

Response samples

Content type
application/json
{
  • "id": "record-00000009",
  • "attributes": {
    }
}

Get single Record

Authorizations:
OAuth2
path Parameters
customListId
required
string <uuid>

ID of the custom list

id
required
string non-empty

ID of the record to be fetched

Responses

Response samples

Content type
application/json
{
  • "id": "record-00000009",
  • "attributes": {
    }
}

Update custom list record

Authorizations:
OAuth2
path Parameters
customListId
required
string <uuid>

ID of the custom list

id
required
string non-empty

ID of the record to be updated

Request Body schema: application/json
required
id
required
string non-empty

External unique record ID

required
object

Key value pair where key is string and value one of following: array, object, string. Keys can be in snake case (e.g. first_name) or camel case (e.g. firstName)

Responses

Request samples

Content type
application/json
Example
{
  • "id": "sdf12345",
  • "attributes": {
    }
}

Response samples

Content type
application/json
{
  • "id": "record-00000009",
  • "attributes": {
    }
}

Archive custom list record

Authorizations:
OAuth2
path Parameters
customListId
required
string <uuid>

ID of the custom list

id
required
string non-empty

ID of the record to be deleted

Responses

Monitoring overview

Monitoring allows clients to run various checks (called scenarios) against their persons and transactions. There are three scenario types:

  • Real-time (ONLINE) scenarios are run by client by calling real-time checks endpoints. Those are intended to be used for the real time checks, for example to decide whether or not to suspend a transaction.
  • Post-event (OFFLINE) scenarios are run by Salv. Person post-event scenarios are run when person is created or updated, transaction post-event scenarios are only run on transaction creation.
  • Periodic scenarios are run by Salv according to specified schedule.

All those scenarios are configured in Salv UI. For each pair of triggered scenario and person/transaction one monitoring alert is created. Those alerts are stored by Salv and can later be accessed in UI or by API.

Example: Real-time (pre-processing) transaction monitoring

Transaction monitoring runs all real-time (ONLINE) monitoring scenarios and returns response in realtime. This flow can be used to block a transaction based on transaction monitoring checks.

1. Add a person (if not already added) and a transaction

Add a person and a transaction as shown in Example: Uploading person and transaction.

2. Perform monitoring check

Perform transaction monitoring according to the documentation at runTransactionMonitoringChecks(POST).

With cURL

curl --request POST \
  --url https://app.salv.com/api/v1/transactions/transactionId/monitoring-checks \
  --header 'Authorization: Bearer {yourBearerToken}' \
  --header 'Content-Type: application/json'

With Python Requests

import requests

url = "https://app.salv.com/api/v1/transactions/transactionId/monitoring-checks"

headers = {
    "Content-Type": "application/json",
    "Authorization": "{yourBearerToken}"
}

response = requests.request("POST", url, headers=headers)

print(response.text)

API response when an alert was created

In this case you should block the transaction using your internal logic.

{
  "results": [
    {
      "reason": "High incoming sum",
      "details": "Sum >= 5 000€ (Sum: 19 694.05) in last 30 days",
      "score": 100,
      "weight": 1,
      "scenarioType": "ONLINE",
      "alertId": 321
    },
    {
      "reason": "Outgoing transfer is 80% of the total balance",
      "details": "Amount: 7785.0, balance: 9693.43",
      "score": 100,
      "weight": 1,
      "scenarioType": "ONLINE",
      "alertId": 543
    }
  ]
}

API response when no alert was created

In this case you should process the transaction as no alert was created.

{
  "results": []
}

Decision lifecycle

Data flow diagram

Example: Real-time (pre-processing) transaction monitoring

Transaction monitoring runs all real-time (ONLINE) monitoring scenarios and returns response in realtime. This flow can be used to block a transaction based on transaction monitoring checks.

1. Add a person (if not already added) and a transaction

Add a person and a transaction as shown in Example: Uploading person and transaction.

2. Perform monitoring check

Perform transaction monitoring according to the documentation at runTransactionMonitoringChecks(POST).

With cURL

curl --request POST \
  --url https://app.salv.com/api/v1/transactions/transactionId/monitoring-checks \
  --header 'Authorization: Bearer {yourBearerToken}' \
  --header 'Content-Type: application/json'

With Python Requests

import requests

url = "https://app.salv.com/api/v1/transactions/transactionId/monitoring-checks"

headers = {
    "Content-Type": "application/json",
    "Authorization": "{yourBearerToken}"
}

response = requests.request("POST", url, headers=headers)

print(response.text)

API response when an alert was created

In this case you should block the transaction using your internal logic.

{
  "results": [
    {
      "reason": "High incoming sum",
      "details": "Sum >= 5 000€ (Sum: 19 694.05) in last 30 days",
      "score": 100,
      "weight": 1,
      "scenarioType": "ONLINE",
      "alertId": 321
    },
    {
      "reason": "Outgoing transfer is 80% of the total balance",
      "details": "Amount: 7785.0, balance: 9693.43",
      "score": 100,
      "weight": 1,
      "scenarioType": "ONLINE",
      "alertId": 543
    }
  ]
}

API response when no alert was created

In this case you should process the transaction as no alert was created.

{
  "results": []
}

Decision lifecycle

Data flow diagram

Monitoring checks

Person & Transaction monitoring.

Real-time (ONLINE) scenarios should be used when an alert created by the scenario should block the transaction. Example: Real-time (pre-processing) transaction monitoring

Post-event (OFFLINE) scenarios should be used when an alert created should NOT block the transaction. Example: Simple integration

In the rare occasion that a monitoring check fails with error code 500, the transaction should be blocked. In that case Salv has not been able to verify that the transaction is safe to process.

Run real-time (ONLINE) person monitoring checks

Authorizations:
OAuth2
path Parameters
personId
required
string non-empty

ID of a person

query Parameters
all-results
boolean

Return all results regardless of scenario score

Responses

Response samples

Content type
application/json
{
  • "results": [
    ]
}

Run real-time (ONLINE) transaction monitoring checks

Authorizations:
OAuth2
path Parameters
transactionId
required
string non-empty

ID of transaction

Responses

Response samples

Content type
application/json
{
  • "results": [
    ]
}

Monitoring alerts

Get transaction monitoring alert

Authorizations:
OAuth2
path Parameters
alertId
required
integer <int64> >= 1

ID of alert to get

Responses

Response samples

Content type
application/json
{
  • "id": 1,
  • "priority": "LOW",
  • "status": "string",
  • "statusId": 0,
  • "person": {
    },
  • "score": {
    },
  • "assignee": {
    },
  • "createdTime": "2019-08-24T14:15:22Z",
  • "assignments": [
    ],
  • "statuses": [
    ],
  • "notes": [
    ],
  • "transaction": {
    },
  • "entityType": "PERSON"
}

Screening overview

Screening allows clients to check their persons and transactions against screening lists, such as sanctions lists, PEPs (politically exposed persons) lists and adverse media lists. It is also possible to configure custom lists to check persons and transactions against them as well.

There are two ways to use screening.

  • To quickly check some name against the screening lists without uploading any data or creating alerts use screening search endpoint.

  • To configure more intricate checks first upload person and transaction data to Salv. That will enable automatic person re-screening and allow you to configure a lot of screening details. Then it will be possible to run screening with all that configuration for any particular person or transaction using the API.

Screening settings that can be configured in Salv IU

  • Clearance rules
  • Fields to screen
  • Significant Update Indicators
  • Transaction Goodlist Identifiers
  • Person and Transaction Segments
  • Automatic Re-screening
  • Dow Jones lists to screen against
  • Custom Lists to screen against
  • Fuzzy matching threshold
  • BIC Length Threshold

Example: Transaction screening

Transaction screening checks transaction sender and recipient names against screening lists

1. Add a transaction

Add a new transaction, according to the documentation at addTransaction(POST).

2. Perform screening check

Perform transaction screening according to the documentation at checkTransaction(POST).

With cURL

curl -X POST \
  https://app.salv.com/api/v1/transactions/000000000000009/screening-checks \
  -H 'Authorization: Bearer {yourBearerToken}' \
  -H 'Content-Type: application/json'

With Python Requests

import requests

url = "https://app.salv.com/api/v1/transactions/id/screening-checks"

headers = {
    "Content-Type": "application/json",
    "Authorization": "{yourBearerToken}"
}

response = requests.request("POST", url, headers=headers)

print(response.text)

API response when an alert was created

In this case you should block the transaction using your internal logic.

{
  "matches": [
    {
      "inputData": "Hassan Bahadlri",
      "matchData": "{"id": "67c79e6f14245df0791a307154512e8a097d6a57", "schema": "Person", "properties": {"name": ["Hassan Bahadori"], "lastName": ["Bahadori"], "firstName": ["Hassan"]}}",
      "sanctionList": "US OFAC sanctions",
      "score": 99,
      "checkType": "SANCTION",
      "alertId": 321
      "referenceGroupCodes": ["EUCON", "OFACAIS"]
    },
    {
      "inputData": "Gazprom Neft",
      "matchData": "{"id": "303a2ec82f2c5c48e051bfd7e3822baba5b9ca06", "schema": "LegalEntity", "properties": {"name": ["Gazprom Neft"]}}",
      "sanctionList": "CH SECO sanctions",
      "score": 100,
      "checkType": "SANCTION"
      "alertId": 123,
      "referenceGroupCodes": null
    }
  ]
}

API response when no alert was created

In this case you should process the transaction as no alert was created.

{
  "matches": []
}

Example: Person screening

Person screening checks person name against screening lists

1. Add a person if not already added

Add a new person, according to the documentation at addPerson(POST).

If the person has already been added to Salv, then skip this step.

2. Perform screening check

Perform person screening according to the documentation at checkPerson(POST).

With cURL

curl -X POST \
  https://app.salv.com/api/v1/persons/000007/screening-checks \
  -H 'Authorization: Bearer {yourBearerToken}' \
  -H 'Content-Type: application/json'

With Python Requests

import requests

url = "https://app.salv.com/api/v1/persons/id/screening-checks"

headers = {
    "Content-Type": "application/json",
    "Authorization": "{yourBearerToken}"
}

response = requests.request("POST", url, headers=headers)

print(response.text)

API response when an alert was created

{
  "matches": [
    {
      "inputData": "Hassan Bahadlri",
      "matchData": "{"id": "67c79e6f14245df0791a307154512e8a097d6a57", "schema": "Person", "properties": {"name": ["Hassan Bahadori"], "lastName": ["Bahadori"], "firstName": ["Hassan"]}}",
      "sanctionList": "US OFAC sanctions",
      "score": 99,
      "checkType": "SANCTION",
      "alertId": 321
      "referenceGroupCodes": ["EUCON", "OFACAIS"]
    },
    {
      "inputData": "Hassan Bahadlri",
      "matchData": "{"id": "303a2ec82f2c5c48e051bfd7e3822baba5b9ca06", "schema": "Person", "properties": {"name": ["Hassan Bahadlri"], "wikidataId": ["Q85387153231"]}, "organizations": []}",
      "sanctionList": "PEP every politician",
      "score": 100,
      "checkType": "PEP",
      "alertId": 123,
      "referenceGroupCodes": null
    }
  ]
}

API response when no alert was created

{
  "matches": []
}

Screening checks

Screening checks for transaction and person. If your field names do not match the example, make sure to configure this under mappings.

Screen transaction

Transaction is screened by one or all attributes - reference, receiver_bank_bic, sender_bank_bic, sender_bank_name, receiver_bank_name, from_name and to_name. Selection of screened attributes come from configuration.

Authorizations:
OAuth2
path Parameters
id
required
string

external ID of the transaction to screen

Responses

Response samples

Content type
application/json
{
  • "matches": [
    ]
}

Screen person

Person is screened by first_name + last_name and company_name attribute.

Authorizations:
OAuth2
path Parameters
id
required
string

external ID of the person to screen

Responses

Response samples

Content type
application/json
{
  • "matches": [
    ]
}

Screening search

Screening search can be used to check any name against screening lists without first uploading a person or a transaction.

Run screening search

Checks if provided name matches a sanctioned person or entity.

Authorizations:
OAuth2
Request Body schema: application/json
required
name
required
string >= 3 characters

Screening name

matchEntityType
string
Enum: "PERSON" "ENTITY" "ALL"

Sanction record type

searchAllLists
boolean

When true, screens against all sanction lists

screeningTypes
required
Array of strings (ApiDowJonesSanctionType)
Items Enum: "SANCTION" "PEP" "ADVERSE_MEDIA" "ADVERSE_MEDIA_ENTITY"

Screening types that need to be screened

djListScreeningSubType
string
Enum: "FUZZY_MATCH" "STARTS_WITH" "EXACT_MATCH"

Screening match type

object (CustomListScreeningSearchData)

Responses

Request samples

Content type
application/json
{
  • "name": "John Doe",
  • "matchEntityType": "PERSON",
  • "searchAllLists": false,
  • "screeningTypes": [
    ],
  • "djListScreeningSubType": "FUZZY_MATCH",
  • "customListSearch": {
    }
}

Response samples

Content type
application/json
[
  • {
    }
]

Screening alerts

Screening alert is created when a particular field of a particular person matches against one of the screening lists. Screening alert has a list of hits, each of which represent one matched record from screening list. Some of the hits can be cleared automatically according to the configured clearance rules and goodlists.

Get screening alert hits

Returns paginated list of screening alert hits by alert id

Authorizations:
OAuth2
path Parameters
screeningAlertId
required
integer <int64> >= 1

ID of the screening alert for which to return hits

query Parameters
page
required
integer >= 0
Default: 0

Number of the page

size
required
integer [ 1 .. 50 ]
Default: 20

Size of the page

Responses

Response samples

Content type
application/json
{
  • "number": 0,
  • "size": 1,
  • "total": 1,
  • "content": [
    ]
}

Risks

Risks levels are assigned to persons according to configured risk rules. It is up to client how to interpret each particular level.

Risk rules are configured in Salv UI.

Person risk is scored every time one of the following triggering events occurs:

  • the person is created
  • the person is updated
  • a transaction is added to the person
  • a monitoring alert is created for the person
  • the status of a monitoring alert of the person is updated
  • the status of a screening alert of the person is changed to any true positive status
  • the status of a screening alert of the person is changed from any of true positive statuses to non true positive status

Person risk scoring happens in the background and the service does not guarantee any time frame for it.

Risk levels can be overridden by client operators for any particular person. They would need to choose one of the preconfigured override reasons, which are also configured in Salv UI.

Fetch person risk factors

Authorizations:
OAuth2
path Parameters
personId
required
string non-empty

ID of person

Responses

Response samples

Content type
application/json
{
  • "finalRisk": "LOW",
  • "risks": [
    ],
  • "activeOverrideLevel": "LOW",
  • "activeOverrideReason": "Sanctioned"
}

Alerts

Salv has multiple properties that are associated with individual alerts and influence alert management process based on their assignment. Using the initial alert ID and TYPE that are generated after different checks are triggered and communicated via API or WEBHOOKS, it is possible to programmatically modify these properties to streamline your business logic.

Attention: To start using this API, the client id used for authentication needs to be created after 16.05.2023. If your integration currently uses credentials created before this date, you must create a NEW credentials pair, and replace the existing one.

Update alert status

Updates specified alert status, optionally includes a note to go with the update.

Authorizations:
OAuth2
Request Body schema: application/json
required
alertType
required
string (ApiAlertType)
Enum: "MONITORING" "SCREENING" "BRIDGE"
alertId
required
integer <int64> >= 0

Salv alert ID, provided after calling screening or monitoring checks endpoint

status
required
string non-empty

Alert status CODE, configured in Salv admin UI

note
string non-empty

Optional note to be added together with alert status change

Responses

Request samples

Content type
application/json
{
  • "alertType": "MONITORING",
  • "alertId": 1234567,
  • "status": "FALSE_POSITIVE",
  • "note": "Documents provided and matched by customer"
}

Unresolved alerts

Endpoints to check if given entity has anything unresolved

Check for unresolved monitoring or screening person alerts Deprecated

Deprecated. Will be removed and replaced with a new more flexible endpoint.

Authorizations:
OAuth2
path Parameters
id
required
string

external ID of the person to check

Responses

Response samples

Content type
application/json
{
  • "hasUnresolvedAlerts": true
}

Check for unresolved monitoring or screening transaction alerts Deprecated

Deprecated. Will be removed and replaced with a new more flexible endpoint.

Authorizations:
OAuth2
path Parameters
id
required
string

external ID of the transaction to check

Responses

Response samples

Content type
application/json
{
  • "hasUnresolvedAlerts": true
}