Reviews API Reference Guide
The Reviews API allows you to retrieve information about a credit card scan performed by your end user. You can obtain redacted versions of the scan images for review purposes, which we refer to as "blackout images.”
Getting Started
The Reviews API provides access to the blackout images. There are two flavors. The first flavor is a typical shared secret based access. Provide a token and a scanID
to obtain a pre-signed S3 URL pointing to the redacted image of the card stored on our server.
The second flavor of the Reviews API introduces two types of users: admins and reviewers. Admins receive a token that allows them to create a temporary token for reviewers to access a specific scan image. Reviewers are given time-limited access to the blackout images but are not allowed to create temporary tokens. Additionally, reviewer tokens provide access to a temporary URL. After one hour, the URL pointing to the blackout image will expire.
The following diagram summarizes a possible workflow.
To authenticate requests to the Reviews API, a dedicated key is required. To identify an admin, include the X-REVIEWER-ID request header, which contains a unique identifier for each admin. We will provide identifiers upon request. To identify a reviewer, include the authorization: Bearer temporary_token request header, where the temporary_token is generated by your admins to identify a reviewer.
Endpoints
Get end-user redacted card image presigned url
No OpenAPI specification URL provided
Notice that the imageSrc
attribute of the response is always defined as the redacted image doesn't need to exist on S3 to create the presigned URL. An additional attribute status
provides that missing information. status
takes 3 values: READY
, UPLOADING
or FILENOTFOUND
.
- Python
- Node
- Curl
import requests
url = "https://api.dyneti.com/reviews/image"
headers = {
"X-REVIEW-ID": "your_access_token"
}
params = {
"scanId": "your_scan_id",
"ExpiresIn": 3600
}
response = requests.get(url, headers=headers, params=params)
if response.status_code == 200:
data = response.json()
image_url = data["imageSrc"]
print("Image URL:", image_url)
else:
print("Error:", response.text)
const axios = require("axios");
const url = "https://api.dyneti.com/reviews/image";
const headers = {
"X-REVIEW-ID": "your_access_token"
};
const params = {
scanId: "your_scan_id",
ExpiresIn: 3600
};
axios
.get(url, { headers, params })
.then((response) => {
if (response.status === 200) {
const data = response.data;
const imageSrc = data.imageSrc;
console.log("Image URL:", imageSrc);
} else {
console.log("Error:", response.statusText);
}
})
.catch((error) => {
console.error(error);
});
curl -X GET "https://api.dyneti.com/reviews/image?scanId=your_scan_id&ExpiresIn=3600" \
-H "X-REVIEW-ID: your_access_token"
Get end-user redacted card image in jpeg format
No OpenAPI specification URL provided
If only a tight crop image is available, it will be returned with status code 200 even if the tight
query parameter is not set to true.
- Python
- Node
- Curl
import requests
scanid = 'your_scan_id'
headers = {'X-REVIEW-ID': 'your_access_token'}
api_url = f'https://api.dyneti.com/reviews/image/{scanid}.jpg'
# To request a tight crop instead, add the tight query parameter:
# api_url += '?tight=true'
response = requests.get(api_url, headers=headers)
if response.status_code == 200:
with open(f'{scanid}.jpg', 'wb') as f:
f.write(response.content)
const axios = require('axios');
const fs = require('fs');
const scanid = 'your_scan_id';
let api_url = `https://api.dyneti.com/reviews/image/${scanid}.jpg`;
// To request a tight crop instead, add the tight query parameter:
// api_url += "?tight=true"
axios.get(api_url, {
responseType: 'stream',
headers: { 'X-REVIEW-ID': 'accessToken' },
}).then((response) => {
response.data.pipe(fs.createWriteStream(`${scanid}.jpg`));
});
scanid="your_scan_id"
# Be careful that your shell doesn't add escapes to the curly braces when pasting.
curl -H "X-REVIEW-ID: AccessToken" -o ${scanid}.jpg https://api.dyneti.com/reviews/image/${scanid}.jpg
# Add the tight query parameter to request a tight crop. The ? usually does need to be escaped: \?.
curl -H "X-REVIEW-ID: AccessToken" -o ${scanid}.jpg https://api.dyneti.com/reviews/image/${scanid}.jpg\?tight=true
Posting feedback
No OpenAPI specification URL provided
Accepted shortcodes are:
shortcode | comment |
---|---|
approved | Approved - card looks ok |
denied_bin_mismatch | Card bin or last 4 mismatch with image |
denied_screen_display | Image is being displayed on a screen |
denied_fake_card | Image is a fake/modified physical card |
denied_digitally_altered | Image is digitally altered |
denied_branding_mismatch | Card branding/network is different from the bin checker |
denied_fraud_vector | Known fraud vector |
denied_other | Other (deny) |
no_action_no_image_available | No action taken because no image is available |
no_action_poor_image_quality | No action taken due to poor image quality |
no_action_bad_redaction_middle_digits | No action taken due to middle digits' visibility |
no_action_bad_redaction_cvv | No action taken because the cvv is readable. |
no_action_bad_redaction_full_name | No action taken because the full cardholder name is visible |
no_action_bad_redaction_face | No action taken because an identifiable human face is visible |
no_action_bad_redaction_last_four | No action taken because the last four digits are not readable |
no_action_other | No action taken for another reason |
Notice that you can add any additional content in the key annotationInfo
- Python
- Node
- Curl
import requests
url = "https://api.dyneti.com/reviews/feedback"
payload = {
"scanId": "123456",
"annotationShortCode": "approved",
"annotationInfo": {"key": "value"}
}
headers = {
"Content-Type": "application/json",
"X-REVIEW-ID": "<admin-access-token>"
}
requests.post(url, json=payload, headers=headers)
const axios = require('axios');
const url = "https://api.dyneti.com/reviews/feedback";
const payload = {
scanId: "123456",
annotationShortCode: "ABC",
annotationInfo: {"key": "value"}
};
const headers = {
"Content-Type": "application/json",
"X-REVIEW-ID": "<admin-access-token>"
};
axios.post(url, payload, { headers })
.then((response) => {
console.log(response.data);
})
.catch((error) => {
console.error(error);
});
curl -X POST -H "Content-Type: application/json" -H "X-REVIEW-ID: <admin-access-token>" -d '{
"scanId": "123456",
"annotationShortCode": "ABC",
"annotationInfo": {"key": "value"}
}' "https://api.dyneti.com/reviews/feedback"
Reviewers management
Generate Reviewer Authorization Token
GET
https://api.dyneti.com/reviews/authorize
This endpoint generates an authorization token (JWT) that grants permission to access blackout images. It is used by admin only and requires the X-REVIEWER-ID header with appropriate credential.
Query Parameters
Name | Type | Description |
---|---|---|
reviewer* | How you want your reviewer to be identified. |
Headers
Name | Type | Description |
---|---|---|
X-REVIEWER-ID* | String | Token provided to the admin |
- 200: OK Credit card scan information
- 400: Bad Request Missing reviewer query parameter
- 403: Forbidden Missing or invalid X-REVIEWER-ID credential
- 500: Internal Server Error Server error occurred
- 401: Unauthorized Failed to create reviewer token
JSON Object containing your reviewer's temporary token
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyZXZpZXJfaWQiOiJ0ZXN0aWQiLCJyZXZpZXdfYWNjb3VudCI6ImFkbWluIiwidXNlcl9pZCI6MTIzNDU2Nzg5MCwiYXV0aG9yaXplZEZvcl9pZCI6IjEyMzQ1Njc4OTAiLCJpYXQiOjE2MzI4OTc5NjIsImV4cCI6MTYzMzQ5Nzk2Mn0.6tkJLtV1IvpDjF3a7m5GMsINNrz0Wi6GCB3Mz3HjPE0"
}
Fields description:
token
- the temporary token associated with a reviewer.
- Curl
- Python
- Node
curl -X GET "https://api.dyneti.com/reviews/authorize?reviewer=review1" \
-H "X-REVIEW-ID: example_0123456789u0ALMrmV2RXa7YZMdZjAOoTxjM3EbVnRz6SQ5TFzY1"
Response:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyZXZpZXJfaWQiOiJ0ZXN0aWQiLCJyZXZpZXdfYWNjb3VudCI6ImFkbWluIiwidXNlcl9pZCI6MTIzNDU2Nzg5MCwiYXV0aG9yaXplZEZvcl9pZCI6IjEyMzQ1Njc4OTAiLCJpYXQiOjE2MzI4OTc5NjIsImV4cCI6MTYzMzQ5Nzk2Mn0.6tkJLtV1IvpDjF3a7m5GMsINNrz0Wi6GCB3Mz3HjPE0"
}
import requests
url = 'https://api.dyneti.com/reviews/authorize'
headers = {
'X-REVIEW-ID': 'example_0123456789u0ALMrmV2RXa7YZMdZjAOoTxjM3EbVnRz6SQ5TFzY1'
}
params = {
'reviewer': 'review1'
}
response = requests.get(url, headers=headers, params=params)
print(response.json())
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyZXZpZXJfaWQiOiJ0ZXN0aWQiLCJyZXZpZXdfYWNjb3VudCI6ImFkbWluIiwidXNlcl9pZCI6MTIzNDU2Nzg5MCwiYXV0aG9yaXplZEZvcl9pZCI6IjEyMzQ1Njc4OTAiLCJpYXQiOjE2MzI4OTc5NjIsImV4cCI6MTYzMzQ5Nzk2Mn0.6tkJLtV1IvpDjF3a7m5GMsINNrz0Wi6GCB3Mz3HjPE0"
}
const axios = require('axios');
const options = {
url: 'https://api.dyneti.com/reviews/authorize',
method: 'GET',
headers: {
'X-REVIEW-ID': 'example_0123456789u0ALMrmV2RXa7YZMdZjAOoTxjM3EbVnRz6SQ5TFzY1'
},
params: {
'reviewer': 'review1'
}
};
axios(options)
.then(response => {
console.log(response.data);
});
Response
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyZXZpZXJfaWQiOiJ0ZXN0aWQiLCJyZXZpZXdfYWNjb3VudCI6ImFkbWluIiwidXNlcl9pZCI6MTIzNDU2Nzg5MCwiYXV0aG9yaXplZEZvcl9pZCI6IjEyMzQ1Njc4OTAiLCJpYXQiOjE2MzI4OTc5NjIsImV4cCI6MTYzMzQ5Nzk2Mn0.6tkJLtV1IvpDjF3a7m5GMsINNrz0Wi6GCB3Mz3HjPE0"
}
Get blackout image using a JWT token
Get Blackout Image temporary URL
POST
https://api.dyneti.com/reviews/b
This endpoint provides information about a credit card scan performed by your end-user.
Query Parameters
Name | Type | Description |
---|---|---|
scanId* | String | scan id obtained from scan information. |
Headers
Name | Type | Description |
---|---|---|
authorization* | String | Formatted as "Bearer: token". This token is obtained using this endpoint |
- 400: Bad Request Missing scanId query parameter
- 403: Forbidden Unauthorized or expired authorization token
- 500: Internal Server Error Server error occurred
- 401: Unauthorized Missing or bad authorization header
- 200: OK Success
Return a JSON object containing the temporary URL to the blackout image.
{
"imageSrc": "<https://blackout-images.s3.amazon.com/folder/some-image.jpg>"
}
- Curl
- Python
- Node
curl -H "authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyZXZpZXJfaWQiOiJ0ZXN0aWQiLCJyZXZpZXdfYWNjb3VudCI6ImFkbWluIiwidXNlcl9pZCI6MTIzNDU2Nzg5MCwiYXV0aG9yaXplZEZvcl9pZCI6IjEyMzQ1Njc4OTAiLCJpYXQiOjE2MzI4OTc5NjIsImV4cCI6MTYzMzQ5Nzk2Mn0.6tkJLtV1IvpDjF3a7m5GMsINNrz0Wi6GCB3Mz3HjPE0" \
-X GET "https://api.dyneti.com/reviews/b?scanId=80881ebe-0100-496f-8cc7-353026bd95b8"
Response
{
imageSrc: "https://...../80881ebe-0100-496f-8cc7-353026bd95b8.jpg"
}
import requests
import json
url = 'https://api.dyneti.com/reviews/b'
headers = {
'authorization': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyZXZpZXJfaWQiOiJ0ZXN0aWQiLCJyZXZpZXdfYWNjb3VudCI6ImFkbWluIiwidXNlcl9pZCI6MTIzNDU2Nzg5MCwiYXV0aG9yaXplZEZvcl9pZCI6IjEyMzQ1Njc4OTAiLCJpYXQiOjE2MzI4OTc5NjIsImV4cCI6MTYzMzQ5Nzk2Mn0.6tkJLtV1IvpDjF3a7m5GMsINNrz0Wi6GCB3Mz3HjPE0'
}
params = {
'scanId': '80881ebe-0100-496f-8cc7-353026bd95b8'
}
response = requests.get(url, headers=headers, params=params)
if response.status_code == 200:
print(response.json())
Response
{
imageSrc: "https://...../80881ebe-0100-496f-8cc7-353026bd95b8.jpg"
}
const axios = require('axios');
const options = {
url: 'https://api.dev.dyneti.com/reviews/b',
method: 'GET',
headers: {
'authorization': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyZXZpZXJfaWQiOiJ0ZXN0aWQiLCJyZXZpZXdfYWNjb3VudCI6ImFkbWluIiwidXNlcl9pZCI6MTIzNDU2Nzg5MCwiYXV0aG9yaXplZEZvcl9pZCI6IjEyMzQ1Njc4OTAiLCJpYXQiOjE2MzI4OTc5NjIsImV4cCI6MTYzMzQ5Nzk2Mn0.6tkJLtV1IvpDjF3a7m5GMsINNrz0Wi6GCB3Mz3HjPE0'
},
params: {
'scanId': '80881ebe-0100-496f-8cc7-353026bd95b8'
}
};
axios(options)
.then(response => {
console.log(JSON.stringify(response.data));
});
Response
{
"imageSrc" : "https://...../80881ebe-0100-496f-8cc7-353026bd95b8.jpg"
}