# Anatomy of an AML Risk Score
Source: https://docs.dataspike.io/aml-screening/anatomy-of-an-aml-risk-score
How Dataspike turns a screening hit into a percentage and a band — walked through three worked examples.
Every screening hit comes back with a **percentage** and a **band**. Here is how we get from “what we found” to that number — walked through three cases.
> 🎯 The band is the decision signal; the percentage tells you how much evidence sits behind it.
## How the score works
The **band** — Low, Medium or High — is the decision signal. It drives alerts, and a **High** fails an automated identity check outright. The **percentage** is the detail behind it: how much corroborating evidence sits inside that band.
| Band | Range |
| - | - |
| 🟢 **Low** | 0 – 29% |
| 🟡 **Medium** | 30 – 69% |
| 🔴 **High** | 70 – 100% |
**Rule one — the band is the single worst finding.** One sanctions listing makes a case High, and nothing in the record talks it back down. Equally, twenty minor findings stay Low — volume never escalates a band.
**Rule two — the percentage moves only inside that band.** Extra findings of the same severity raise the number with sharply diminishing returns, and it is capped so it can never spill into the band above.
## Case 1 · Country risk alone
*A clean individual whose only exposure is where he is from.*
**John Doe** `Person` `System default profile`
One hit, on a national company register — he is listed as a director. No sanctions, no adverse media, no enforcement history. His citizenship is a jurisdiction on the **FATF grey list**.
| Finding | Factors | Severity |
| - | :-: | - |
| Company register entry *(the register itself is a low-risk source)* | 1 | 🟢 Low |
| “Company registry” label | 1 | 🟢 Low |
| Citizenship — FATF grey list *(default rulebook: grey list = Medium for an individual)* | 1 | 🟡 Medium |
| **Total** | **3** | |
`Medium 30% · Low 5.5% (2 factors) → 1 − (1 − 0.30) × (1 − 0.055) = 0.34`
> 🟡 **34% · Medium** — Nothing in his own history is serious. The grey-listed jurisdiction alone lifts him out of Low, which is exactly what a grey listing is meant to signal.
> ⚡ We read the FATF lists live, so a country moving on or off the grey list changes this score on the next screening — no configuration change needed.
## Case 2 · The client’s own country rule
*The same clean record, but the client has told us which jurisdictions they will not accept.*
**John Doe Inc.** `Company` `Custom profile`
A Venezuelan-registered company with one hit, on a national company register. Nothing adverse at all. This client’s rulebook sets Venezuela to **High for companies** and Medium for individuals.
| Finding | Factors | Severity |
| - | :-: | - |
| Company register entry | 1 | 🟢 Low |
| “Company registry” label | 1 | 🟢 Low |
| Country — Venezuela *(client rulebook, company rate — a rated country replaces the FATF reading entirely)* | 1 | 🔴 High |
| **Total** | **3** | |
`High 70% · Low 5.5% (2 factors) → 1 − (1 − 0.70) × (1 − 0.055) = 0.72`
> 🔴 **72% · High** — A spotless company scores High purely on the client’s own country policy. Run the identical record as an individual and the same rulebook returns Medium instead.
> 🛠️ This is the strongest lever a compliance team has. Country policy is set per client, separately for individuals and companies, and it overrides the FATF default for that country.
## Case 3 · A saturated record
*A real entity from our index, screened on the default profile — what the ceiling looks like.*
**Roman Arkadyevich Abramovich** `Person` `System default profile`
Matched at full-name confidence against 13 lists. Citizenships RU, IL and PT. We never score a record whole — it is broken into individually rateable **risk factors**, and this one yields 37.
| Finding | Factors | Severity |
| - | :-: | - |
| Government sanctions listings *(EU, UK, Swiss, Canadian, Australian, NZ, French, Belgian, Monégasque, Ukrainian)* | 12 | 🔴 High |
| “Sanctions” label on each of those | 12 | 🔴 High |
| UK disqualified directors register | 1 | 🟡 Medium |
| “Finance” and “Legal” labels | 9 | 🟢 Low |
| Citizenships RU, IL, PT *(Russia partially compliant; Israel and Portugal full members — all Low for an individual)* | 3 | 🟢 Low |
| **Total** | **37** | |
`High 70% × 24 factors → 103%, clipped to 100% · Medium 30% · Low 6.9% → combined 1.00`
> 🔴 **100% · High** — Twelve independent government listings, each High on its own. The score saturates: as unambiguous as a screening result gets.
Two things are worth noticing. Twenty-four High factors would have scored **103% on volume alone** — the cap pulls it back to 100%, because the tenth sanctions list cannot count as much as the second. And geography contributed almost nothing here: the default profile sets no country policy of its own, so RU, IL and PT were read straight from FATF and all came back Low. Case 2 is what happens when a client changes that.
## The rulebook behind the numbers
None of these severities are hardcoded. Each client has a **risk profile** deciding what counts as serious. The default ships with these positions:
| Rulebook entry | Default |
| - | - |
| Sanctions, Terrorism, Criminal labels | 🔴 High |
| PEP, Leaks, Debarment, Regulatory warning labels | 🟡 Medium |
| Finance, Legal, Media, Company registry labels | 🟢 Low |
| FATF blacklist country | 🟡 Medium (person) · 🔴 High (company) |
| FATF grey-list country | 🟡 Medium (both) |
| Country policy | Not set — FATF reading is used |
| Per-list severity | Not set — each list’s own rating is used |
> 📌 Profiles are versioned, and every result records which profile and version produced it — so a score from six months ago can always be explained.
## Worth knowing
* **Volume never escalates a band.** Fifty Medium findings still read Medium. Deliberate — it stops a noisy record manufacturing a High — but it means the band alone doesn’t convey how much evidence sits behind it. That is the percentage’s job.
* **Only strong name matches raise the case.** Every matched record gets its own score, but it only lifts the overall search result if the name matched at high confidence. A High-risk record on a weak name stays visible to the analyst without escalating the case.
* **A clean search reports Low, not blank.** Zero matches produces Low rather than an empty result, so “nothing found” and “something minor found” look alike at a glance. Read the hit count alongside the band.
* **Labels are read per listing.** A record carries a rolled-up label summary, but only the labels attached to each individual listing are scored. A label that appears in the summary and nowhere on a listing adds nothing.
> Cases 1 and 2 are constructed illustrations; case 3 is traced from production scoring logic against entity `46840029-b485-34f6-ae82-280d00becb15` under System Default Profile v1. Percentages are rounded; case 3’s stored value is `1.0`.
## Next steps
* [**AML Search**](/aml-screening/search) — run one-time AML searches and read the same score.
* [**Daily Monitoring**](/aml-screening/daily-monitoring) — get alerted when a score changes.
* [**API Reference**](/api) — all parameters and response formats.
# Daily Monitoring
Source: https://docs.dataspike.io/aml-screening/monitoring
Automate daily AML screening to receive alerts for changes in sanctions, PEP status, or adverse media.
Stay proactive with **Dataspike’s Daily Monitoring**, a feature designed to automatically recheck individuals and organizations against global sanctions, watchlists, and PEP databases every 24 hours.\
Receive alerts when new matches, sanctions, or adverse media appear — helping your business stay compliant and responsive to emerging risks.
> ⚡ Daily Monitoring ensures continuous AML compliance without manual intervention.
***
## How it works
Once enabled for an **Applicant**, Dataspike automatically performs background checks each day across:
* International sanctions databases (UN, EU, OFAC, HMT, etc.)
* Politically Exposed Persons (PEP) registries
* Adverse media and enforcement lists
* Domestic high-risk watchlists
Whenever a change is detected — for example, a new sanction or media article — you’ll receive a webhook notification with details of the updated record.
***
## 1. Enable via Dashboard
To enable daily monitoring from the **Dataspike Dashboard**:
1. Navigate to **Applicants → Create Applicant**.
2. Provide the name and relevant applicant details.
3. Toggle **Enable Daily Monitoring** during creation.
4. Save the applicant — monitoring begins automatically.
> 📊 Applicants with monitoring enabled are rechecked daily and visible in the dashboard’s AML section.
***
## 2. Enable via API
You can also enable monitoring programmatically through the **API**, which is ideal for batch onboarding or backend automation.
**Example request:**
```bash theme={null}
curl -X POST "https://api.dataspike.io/api/v3/applicants" -H "ds-api-token: $API_KEY" -H "Content-Type: application/json" --data '{
"external_id": "user_123456",
"full_name": "John Doe",
"enable_aml_monitoring": true
}'
```
**Example response:**
```json theme={null}
{
"id": "01827ed4-c928-7a3c-9a30-7ab7cc169d11",
"full_name": "John Doe",
"aml_monitoring_enabled": true,
"status": "active"
}
```
***
## Webhook Notifications
When a new match or alert appears, Dataspike sends a webhook event to your configured endpoint.
**Example webhook event:**
```json theme={null}
{
"id": "wh_evt_01J23ABCDEF",
"webhook_id": "wh_cfg_089dc912",
"event_type": "AML_SCREENING",
"timestamp": "2025-10-20T12:30:00Z",
"applicant_id": "01827ed4-c928-7a3c-9a30-7ab7cc169d11",
"external_id": "user_4567",
"screening_id": "scrn_023adf9a",
"risk_score": "High",
"payload": {
"requested_name": "John Doe",
"max_risk_score": "High",
"data": [
{
"type": "Person",
"annotation": "Listed on OFAC SDN List",
"risk_score": "High",
"fields": {
"names": [{ "full_name": "John Doe" }],
"citizenships": ["US"],
"sources": ["OFAC Specially Designated Nationals List"]
}
}
],
"completed_at": "2025-10-20T12:29:58Z"
}
}
```
> 🔔 You can view and replay all webhook deliveries from **Dashboard → API → Webhooks → History**.
***
## Managing Monitored Applicants
From the **Dashboard**, you can:
* View all monitored applicants and their last check date.
* Review AML match summaries and scores.
* Pause or disable monitoring for specific applicants.
* Export reports for compliance audits.
***
## Best Practices
* Start in **Sandbox** to verify monitoring setup before production.
* Always configure **webhooks** to receive real-time match notifications.
* Use **`external_id`** to map applicants to your internal user records.
* Periodically review monitoring configurations for inactive or outdated applicants.
***
## Next steps
* [**AML Search**](/aml-screening/search) — learn how to perform one-time AML searches.
* [**Webhook Configuration**](/webhooks) — configure alerts for AML match events.
* [**API Reference**](/api) — explore all applicant management endpoints.
# Search
Source: https://docs.dataspike.io/aml-screening/search
Search and monitor individuals or organizations for AML compliance using the Dataspike dashboard and API.
The **Dataspike AML Search API** and dashboard empower compliance teams to identify individuals and organizations across **global and domestic watchlists**, **sanction databases**, and **politically exposed persons (PEP)** registries with precision and speed.
Our system aggregates intelligence from:
* **All major international sanctions lists** (UN, EU, OFAC, HMT, etc.)
* **Hundreds of domestic enforcement and regulatory lists**
* **High-risk entity databases and industry blacklists**
* **Thousands of global media sources** continuously scanned for adverse news and reputational risk
With advanced filtering options — including name, country, date of birth, and category — Dataspike enables **comprehensive AML compliance coverage** across jurisdictions and industries.
> 🔍 Use the **Dashboard** for quick manual investigations, or the **API** for automated, large-scale screening and continuous monitoring.
***
## Search using Dashboard
Performing a search via the **Dataspike Dashboard** is fast and intuitive. Follow these steps to run your first AML check.
### 1. Navigate to the Reports tab
Go to **PEP & Sanctions → New Search** from the left sidebar in your Dataspike dashboard.\
This section allows you to search, review, and monitor AML data in real time.
***
### 2. Enter Search Parameters
In the **Reports** interface, enter the name of a person or organization you want to search for.\
You can refine your results by adding more details — such as **country** or **date of birth**.
***
### 3. Apply Filters (Optional)
Additional filters allow for deeper targeting. Depending on your setup, you may narrow results by:
* Country or region
* Date of birth
* Category (PEP, sanction, enforcement list)
* Data source or registry
***
### 4. Create an Applicant (Optional)
For ongoing monitoring, you can create a new **Applicant** directly from a search result.\
Enable **Daily Monitoring** to automatically recheck the applicant’s data every 24 hours.\
You’ll receive webhook notifications if new relevant information appears in global AML databases.
> 🕒 Continuous monitoring helps maintain compliance without needing to manually re-run searches.
***
### 5. Export PDF Report (Optional)
You can export a detailed PDF report of any search result.\
The time to generate depends on the number of matches and data complexity.
When the report is ready, you’ll be prompted to **save or download** it to your local device.\
This PDF includes all search details, match summaries, and verification timestamps — ideal for audits or recordkeeping.
***
## Search using Dataspike API
Searching via the **[API](https://docs.dataspike.io/api)** is a more advanced approach that allows you to:
This approach is ideal for backend integrations or batch screening processes.
**Example: basic search using cURL**
```bash theme={null}
curl -X POST "https://api.dataspike.io/api/v3/aml/search" -H "ds-api-token: $API_KEY" -H "Content-Type: application/json" --data '{
"full_name": "John Doe",
"countries": ["GB"],
"date_of_birth": {
"gte": "1987-12-24",
"lte": "1987-12-24"
}
}'
```
**Example response:**
```json theme={null}
{
"requested_name": "John Doe",
"search_uuid": "01234-...-123",
"max_risk_score": "Low",
"data": [
{
"uuid": "a123-...-123",
"type": "Person",
"annotation": "Politically exposed person – former government official.",
"tags": ["PEP", "Finance"],
"risk_score": "Low",
"fields": {
"names": [
{ "full_name": "John Doe", "lang": "en" }
],
"dates_of_birth": ["1987-12-24"],
"citizenships": ["GB"],
"addresses": [
{ "country": "United Kingdom" }
],
"sources": [
{ "name": "UK Sanctions List", "last_updated": "2025-10-01" }
]
},
"updated_at": "2025-10-20T15:45:00Z",
"importance_score": 87
}
]
}
```
Refer to the [**API Reference**](/api#tag/AML-Screening) for complete endpoint documentation, query parameters, and result schemas.
***
## Best Practices
* Always start with **Sandbox** to validate integration before switching to production.
* Use **applicants with daily monitoring** for continuous compliance checks.
* Store only essential AML data to meet privacy and retention requirements.
* Configure **webhooks** to be alerted automatically when new matches appear.
***
## Next steps
* [**Create an Applicant**](/document-verification/first-verification) — link your AML checks to verified identities.
* [**Webhook Configuration**](/webhooks) — set up automated notifications for AML hits.
* [**API Reference**](/api) — view all parameters and response formats.
# AML PDF Report
Source: https://docs.dataspike.io/api-reference/aml-screening/aml-pdf-report
/api-reference/dataspike_openapi.yaml get /api/v3/aml/search/history/{id}/pdf
This route allows to retrieve the old results in PDF format by `search_uuid` from [Search API](/api/search)
# Historical Search Report
Source: https://docs.dataspike.io/api-reference/aml-screening/historical-search-report
/api-reference/dataspike_openapi.yaml get /api/v3/aml/search/history/{id}
This route allows to retrieve the old results of your searches by `search_uuid` from [Search API](/api/search)
# Search
Source: https://docs.dataspike.io/api-reference/aml-screening/search
/api-reference/dataspike_openapi.yaml post /api/v3/aml/search
Search entity name in PEP and Sanction lists
# Change Applicant Search Options
Source: https://docs.dataspike.io/api-reference/applicants/change-applicant-search-options
/api-reference/dataspike_openapi.yaml post /api/v3/applicants/{applicant_id}/search-options
Allow to customize search options for each applicant
# Create New Applicant
Source: https://docs.dataspike.io/api-reference/applicants/create-new-applicant
/api-reference/dataspike_openapi.yaml post /api/v3/applicants
Submit applicant details, such as name, contact information or identification data, in JSON format to create a new applicant profile. The API will respond with a unique applicant ID for further interactions.
# Delete Existing Applicant
Source: https://docs.dataspike.io/api-reference/applicants/delete-existing-applicant
/api-reference/dataspike_openapi.yaml delete /api/v3/applicants/{applicant_id}
The API enables the deletion of an applicant from the applicants list while ensuring the preservation of all data through archiving.
# Enable monitoring
Source: https://docs.dataspike.io/api-reference/applicants/enable-monitoring
/api-reference/dataspike_openapi.yaml post /api/v4/applicants/{applicant_id}/enable-monitoring
Enable monitoring for applicant
# Get Applicant by External id
Source: https://docs.dataspike.io/api-reference/applicants/get-applicant-by-external-id
/api-reference/dataspike_openapi.yaml get /api/v3/applicants/by_external_id/{external_id}
Retrieve detailed information about an existing applicant by providing their unique External ID as a URL parameter.
# Get Applicant PDF Report
Source: https://docs.dataspike.io/api-reference/applicants/get-applicant-pdf-report
/api-reference/dataspike_openapi.yaml get /api/v4/applicants/{applicant_id}/pdf
Retrieve detailed information in PDF report by providing their unique Applicant ID as a URL parameter.
# Get Existing Applicant
Source: https://docs.dataspike.io/api-reference/applicants/get-existing-applicant
/api-reference/dataspike_openapi.yaml get /api/v3/applicants/{applicant_id}
Retrieve detailed information about an existing applicant by providing their unique Applicant ID as a URL parameter.
# Latest AML Report
Source: https://docs.dataspike.io/api-reference/applicants/latest-aml-report
/api-reference/dataspike_openapi.yaml get /api/v3/aml/applicants/{applicant_id}/report
Fetch latest AML Report for an applicant
# Link Telegram profile to Applicant
Source: https://docs.dataspike.io/api-reference/applicants/link-telegram-profile-to-applicant
/api-reference/dataspike_openapi.yaml post /api/v3/applicants/{applicant_id}/link/tg
Link Telegram profile to Applicant using known Telegram Id
# List existing applicants
Source: https://docs.dataspike.io/api-reference/applicants/list-existing-applicants
/api-reference/dataspike_openapi.yaml get /api/v3/applicants
Allows to retrieve a collection of existing applicant profiles within the system.
# Update applicant info
Source: https://docs.dataspike.io/api-reference/applicants/update-applicant-info
/api-reference/dataspike_openapi.yaml post /api/v3/applicants/{applicant_id}
Update provided information of applicant.
Send explicit null to remove the field value. For example: `{"country": null}`

# Extract Data
Source: https://docs.dataspike.io/api-reference/bankbooks/extract-data
/api-reference/dataspike_openapi.yaml post /api/v3/extract/bankbooks
Allow to extract account name and number from uploaded bankbook
# Analyze Audio
Source: https://docs.dataspike.io/api-reference/deepfake-api/analyze-audio
/api-reference/dataspike_openapi.yaml post /api/v4/deepfake/audio/analyze
Detect AI-generated or manipulated **voice recordings**.
This endpoint identifies synthetic voices used in impersonation, fraud calls, or voice authentication bypass attempts.
The API runs asynchronously and returns a `job_id`. Use the job status endpoint to fetch results.
**Use cases:** fraud call detection, voice biometric protection, compliance in financial services.
**Supported formats:** .mp3, .wav, .m4a
# Analyze Document
Source: https://docs.dataspike.io/api-reference/deepfake-api/analyze-document
/api-reference/dataspike_openapi.yaml post /api/v4/deepfake/document/image/analyze
Detect deepfake and manipulation attempts in **identity document images** (e.g., ID cards, passports, driver’s licenses).
Ideal for fraud prevention during digital onboarding and compliance workflows.
**Use cases:** document verification, preventing synthetic ID fraud, regulatory compliance.
**Supported formats:** .jpg, .jpeg, .png
# Analyze Image
Source: https://docs.dataspike.io/api-reference/deepfake-api/analyze-image
/api-reference/dataspike_openapi.yaml post /api/v4/deepfake/image/analyze
Detect deepfake manipulation in human photos (e.g., selfies, liveness images, profile pictures).
This endpoint helps ensure the authenticity of faces and prevents fraud by verifying that an image has not been digitally altered.
**Use cases:** onboarding verification, selfie checks, KYC/AML compliance.
**Supported formats:** .jpg, .jpeg, .png
# Analyze Video
Source: https://docs.dataspike.io/api-reference/deepfake-api/analyze-video
/api-reference/dataspike_openapi.yaml post /api/v4/deepfake/video/analyze
Detect deepfake manipulation in **video streams** such as recorded liveness checks, video KYC, or meeting recordings.
This endpoint runs asynchronously and returns a `job_id`. Use the job status endpoint to fetch results.
**Use cases:** remote onboarding, live video interviews, fraud detection in video calls.
**Supported formats:** .mp4, .mov
# Get Deepfake Job Status
Source: https://docs.dataspike.io/api-reference/deepfake-api/get-deepfake-job-status
/api-reference/dataspike_openapi.yaml get /api/v4/deepfake/job/{job_id}
Retrieve the current status and final results of an asynchronous deepfake analysis (video or audio).
Use the `job_id` provided when submitting media to track progress and fetch results.
**Possible statuses:** queued, completed, failed.
**Use cases:** monitor liveness video verification, audio fraud detection or fetch historical job results.
# Create an email check
Source: https://docs.dataspike.io/api-reference/email-verification/create-an-email-check
/api-reference/dataspike_openapi.yaml post /api/v4/email/checks
Runs email validation checks (syntax, MX, disposable, role-based) and optionally initiates OTP ownership verification.
# Get an email check
Source: https://docs.dataspike.io/api-reference/email-verification/get-an-email-check
/api-reference/dataspike_openapi.yaml get /api/v4/email/checks/{id}
Returns a single email check record by its identifier.
# List email checks
Source: https://docs.dataspike.io/api-reference/email-verification/list-email-checks
/api-reference/dataspike_openapi.yaml get /api/v4/email/checks
Returns a paginated list of email checks for the authenticated organization.
# Resend OTP code
Source: https://docs.dataspike.io/api-reference/email-verification/resend-otp-code
/api-reference/dataspike_openapi.yaml post /api/v4/email/otp/resend
Resends the one-time password to the email address. Subject to rate limiting.
# Verify OTP code
Source: https://docs.dataspike.io/api-reference/email-verification/verify-otp-code
/api-reference/dataspike_openapi.yaml post /api/v4/email/otp/verify
Verifies the one-time password sent to the email address, confirming ownership.
# Verification Error Codes
Source: https://docs.dataspike.io/api-reference/error-codes
Complete reference for verification error codes returned by Dataspike KYC, document, biometric, AML, and KYB checks.
When a verification check fails, the response includes one or more numeric error codes. Each code maps to a specific failure reason. Codes are grouped by range so the leading digits indicate the category.
| Range | Category |
| - | - |
| 500 – 501 | Tampering and device-level edits |
| 1000 – 1099 | OCR and document detection |
| 1100 – 1999 | Data field checks |
| 2000 – 2999 | MRZ parsing and validation |
| 3000 – 3099 | Document expiration |
| 3100 – 3199 | Document mismatch |
| 3200 – 3299 | Age and date of birth |
| 4000 – 4499 | Face detection and comparison |
| 4500 – 4999 | Proof of Address |
| 5000 – 5499 | Liveness |
| 6000 – 6499 | AML |
| 7000 – 7099 | Cross-check (duplicates) |
| 8000 – 8099 | Verification flow |
| 9000 – 9099 | Dependency failures |
| 9500 – 9799 | KYB |
| 10000 | Device fingerprinting |
| 11000 – 11099 | SSN |
***
## Tampering and device edits
| Code | Message |
| - | - |
| 500 | Tampered file found. Please try another one. |
| 501 | Your photo upload might be affected by filters, watermarks, or glare. Please check your phone settings and try uploading again. |
## OCR and document detection
| Code | Message |
| - | - |
| 1001 | Some data fields not recognized. |
| 1012 | Capture a photo of a physical document. Alternatively, try a different document or retake the photo. |
| 1013 | Incorrect document layout. Try another document and ensure all 4 edges are visible in the image. |
| 1014 | Document not recognized or not supported. |
| 1015 | Alignment issue: try again with a straight document against a clear background. |
| 1016 | No face was detected on the document. Ensure no glare or overlapping artifacts. |
| 1017 | Document has poor image quality. |
| 1018 | Document with selfie. |
| 1019 | Mirrored document. |
| 1020 | Deepfake document detected. |
## Data field checks
| Code | Message |
| - | - |
| 1103 | Expiration date not detected. |
## MRZ parsing
| Code | Message |
| - | - |
| 2001 | MRZ on the document doesn't match parsed data. |
| 2006 | Country code not detected or unknown. |
| 2010 | Incorrect fields retrieved from the document. |
| 2011 | Parsed MRZ has incorrect length. |
## Document expiration
| Code | Message |
| - | - |
| 3001 | Document has expired. |
## Document mismatch
| Code | Message |
| - | - |
| 3100 | Document type doesn't match the expected type. |
| 3101 | Document from this country is not accepted. |
| 3102 | Name on the document doesn't match your name. |
## Age and date of birth
| Code | Message |
| - | - |
| 3201 | Applicant age is below the minimum age limit. |
| 3202 | Applicant age exceeds the maximum allowed threshold. |
| 3203 | Applicant age could not be determined based on the provided documents (poor quality, obstruction, etc.). |
| 3204 | The uploaded document does not contain a date of birth. |
## Face detection and comparison
| Code | Message |
| - | - |
| 4001 | Face not detected on the document. |
| 4002 | Face on the selfie doesn't match the document. |
| 4003 | Document and selfie photos are identical. |
## Proof of Address (PoA)
| Code | Message |
| - | - |
| 4500 | Name in PoA document not verified. |
| 4501 | Surname in PoA document not verified. |
| 4502 | Country in PoA document not verified. |
| 4503 | PoA document is not applicable or not supported. |
| 4504 | Uploaded document is outdated. |
| 4505 | Document processing failed. |
| 4506 | Address in PoA document not verified. |
| 4507 | Date not found in PoA. |
## Liveness
| Code | Message |
| - | - |
| 5000 | Liveness check failed. |
| 5001 | Potential deepfake usage detected. |
| 5002 | Potential print-based spoofing detected. |
| 5003 | Face too far away. |
| 5004 | Poor lighting conditions. |
| 5005 | Liveness confirmation inconclusive. |
| 5006 | Face too small. |
| 5007 | Image of a face detected, not the real one. |
| 5008 | Eyes are closed on photo. |
| 5009 | The image is too blurry for the liveness check. Ensure your camera is steady and try again. |
| 5010 | Camera resolution is too low for the liveness check. Please use a device with a higher resolution camera and try again. |
| 5011 | Forehead is not visible in the liveness check. Adjust your position so that your entire face, including your forehead, is visible. |
| 5012 | Chin is not visible in the liveness check. Adjust your position so that your entire face, including your chin, is visible. |
| 5013 | Face not found. |
| 5014 | A mask or obstruction has been detected during the liveness check. Ensure your face is unobstructed and clearly visible, without any masks or filters. |
| 5018 | Potential screen-based spoofing detected. |
## AML
| Code | Message |
| - | - |
| 6000 | Potential high AML risk detected. |
| 6001 | AML check could not run: applicant identity data is missing or incomplete. |
## Cross-check (duplicates)
| Code | Message |
| - | - |
| 7000 | Duplicate with the same document number found. |
| 7001 | Duplicate with the same name and birth date found. |
| 7002 | Duplicate with the same photo found. |
## Verification flow
| Code | Message |
| - | - |
| 8000 | The required documents were not submitted within the designated timeframe. |
| 8001 | This verification was canceled. |
| 8002 | Too many attempts to upload files. |
## Dependency failures
| Code | Message |
| - | - |
| 9000 | One of the previous checks within this verification has failed, and this step was marked as failed as a result. |
## KYB
General KYB errors:
| Code | Message |
| - | - |
| 9500 | Verification of KYB failed. |
| 9501 | Document unreadable or corrupted. |
| 9502 | UBO name not found in submitted documents. |
| 9503 | UBO name does not match data from the document. |
| 9504 | UBO nationality does not match data from the document. |
| 9505 | Unsupported document type for organization verification. |
| 9506 | Submitted documents belong to different organizations. |
| 9507 | Document not valid for organization verification. |
Certificate of incorporation:
| Code | Message |
| - | - |
| 9601 | Certificate of incorporation document unreadable or corrupted. |
| 9602 | UBO name not found in submitted certificate of incorporation document. |
| 9603 | UBO name does not match data from the certificate of incorporation document. |
| 9604 | UBO nationality does not match data from the certificate of incorporation document. |
Shareholders document:
| Code | Message |
| - | - |
| 9701 | Shareholders document unreadable or corrupted. |
| 9702 | UBO name not found in submitted shareholders document. |
| 9703 | UBO name does not match data from the certificate of shareholders document. |
| 9704 | UBO nationality does not match data from the shareholders document. |
## Device fingerprinting
| Code | Message |
| - | - |
| 10000 | Device fingerprinting failed. |
## SSN
| Code | Message |
| - | - |
| 11000 | SSN verification failed. |
| 11001 | Name not verified. |
| 11002 | Date of birth not verified. |
| 11003 | Address not verified. |
| 11004 | SSN not verified. |
# Get Document
Source: https://docs.dataspike.io/api-reference/file-management/get-document
/api-reference/dataspike_openapi.yaml get /api/v3/documents/{document_id}
Retrieve original document
# Preview Document
Source: https://docs.dataspike.io/api-reference/file-management/preview-document
/api-reference/dataspike_openapi.yaml get /api/v3/documents/{document_id}/preview
Preview uploaded document, the compressed version of the original image
# Upload document
Source: https://docs.dataspike.io/api-reference/file-management/upload-document
/api-reference/dataspike_openapi.yaml post /api/v3/upload/{applicant_id}
Allow to upload documents for specified applicant id
# Get KYB verification result
Source: https://docs.dataspike.io/api-reference/kyb-single-checks/get-kyb-verification-result
/api-reference/dataspike_openapi.yaml get /api/v4/kyb/result/{request_id}
Poll this endpoint to retrieve the result of a KYB verification request. The `status` field indicates whether processing is complete.
# TIN Verification
Source: https://docs.dataspike.io/api-reference/kyb-single-checks/tin-verification
/api-reference/dataspike_openapi.yaml post /api/v4/kyb/{applicant_id}/tin-verification
Verify that a business name and TIN/EIN combination matches IRS records (IRS TIN matching).
The request is processed asynchronously. Poll `GET /api/v4/kyb/result/{request_id}` for the result.
# US Company Registry Check
Source: https://docs.dataspike.io/api-reference/kyb-single-checks/us-company-registry-check
/api-reference/dataspike_openapi.yaml post /api/v4/kyb/{applicant_id}/us-registry-check
Look up a company in a US state's Secretary of State registry by company name or SOS ID.
The request is processed asynchronously. Poll `GET /api/v4/kyb/result/{request_id}` for the result.
# Verify corporate document
Source: https://docs.dataspike.io/api-reference/kyb-single-checks/verify-corporate-document
/api-reference/dataspike_openapi.yaml post /api/v4/kyb/{applicant_id}/verify-document
Submit corporate documents (certificate of incorporation, shareholder certificate, etc.) for KYB verification. Optionally provide shareholder data to validate against extracted document information.
The request is processed asynchronously. Poll `GET /api/v4/kyb/result/{request_id}` for the result.
# Create a KYB case
Source: https://docs.dataspike.io/api-reference/kyb/kyb-cases/create-a-kyb-case
https://api.dataspike.io/openapi/kyb/public.json post /api/v4/kyb/cases
Creates a KYB case for a merchant identified by external_id. The backend owns the business applicant: it creates an empty Organization applicant bound to external_id (or reuses the existing one). If that applicant already has a `draft` case, that case is returned with 200 instead of creating a new one; a case mid-remediation is NOT reused, because an officer is waiting on it. Optional `fields` prefills the widget form values grouped by section, e.g. {"business": {"legal_name": "..."}}; keys must match the catalog (GET widget), unknown keys are ignored. Tenant (org + sandbox) comes from the caller's auth. Returns the public_id the customer embeds in the widget flow.
# Re-run the pipeline
Source: https://docs.dataspike.io/api-reference/kyb/kyb-cases/re-run-the-pipeline
https://api.dataspike.io/openapi/kyb/public.json post /api/v4/kyb/cases/{caseId}/rerun
Re-runs the KYB verification pipeline for an already-submitted case WITHOUT re-entering the form; from `remediation_review` this is also how an answered remediation round is accepted for re-checking. Unlocks the stored field values, drops the verification link so the engine creates a fresh run, then re-submits (re-locks fields, under_review, emits kyb_case.submitted). The stored form data is untouched. A re-run re-screens against live providers (Cobalt / IRS / AML) and mints a NEW verification. The case is addressed by its id and scoped to the caller's organization (the API key's tenant); an id in another org returns 404.
# Create a KYB flow-profile
Source: https://docs.dataspike.io/api-reference/kyb/kyb-profiles/create-a-kyb-flow-profile
https://api.dataspike.io/openapi/kyb/public.json post /api/v4/kyb/profiles
Creates a KYB flow-profile for the organization. A profile binds a KYC verification profile (kyc_profile_id, which must belong to the caller's org) and the submit gate behavior (allow_submit_on_failed_kyc) to an optional role. role is "production" or "dashboard" for the two seed profiles selected by case-creation path, or empty for a user-defined profile. owner_kyc_profile_id is optional: when set, owners' personal KYC mints against it instead of kyc_profile_id. There is one production and one dashboard profile per org (a duplicate role returns 409). Tenant comes from the caller's auth.
# Edit a KYB flow-profile
Source: https://docs.dataspike.io/api-reference/kyb/kyb-profiles/edit-a-kyb-flow-profile
https://api.dataspike.io/openapi/kyb/public.json patch /api/v4/kyb/profiles/{kybProfileId}
Partially updates a KYB flow-profile: name, kyc_profile_id, owner_kyc_profile_id (both must belong to the caller's org, same check as on create) and allow_submit_on_failed_kyc. Every field is optional and a field left out keeps its stored value; a body setting none of them is a 400. owner_kyc_profile_id has no way to be cleared back to "same as kyc_profile_id" here, only replaced by another org-owned profile. role is immutable — case creation resolves the seed profile by role, so moving it here would silently repoint that lookup. Tenant comes from the caller's auth; a profile id from another org is a 404.
# Download the KYB investigation report (PDF)
Source: https://docs.dataspike.io/api-reference/kyb/kyb-verifications/download-the-kyb-investigation-report-pdf
https://api.dataspike.io/openapi/kyb/public.json get /api/v4/kyb/verifications/{caseId}/pdf
Server-to-server (API key) twin of the dashboard KYB report download. Renders the KYB investigation report for a case to a PDF (application/pdf, served inline). Tenancy comes from the API key. Returns 503 when PDF generation is not configured for the environment (the gotenberg render service is not wired).
# Get a KYB verification (full detail)
Source: https://docs.dataspike.io/api-reference/kyb/kyb-verifications/get-a-kyb-verification-full-detail
https://api.dataspike.io/openapi/kyb/public.json get /api/v4/kyb/verifications/{caseId}
Server-to-server (API key) twin of the dashboard KYB verification detail: same response, the only difference is tenancy — the organization comes from the API key, not a path param.
Full drill-in for one KYB verification. Bundles everything the case page renders: `application` — all widget-collected data & documents from every step; `checks` — the 3 KYB checks (US registry, TIN, document verification); `document_checks` — the ML doc-manipulation verdict for EACH uploaded file; `company_aml` + `owner_aml` — AML screening for the company and every owner/representative; `cross_check` — declared vs registry vs document field deltas; `risk` — final score/decision (null until the KYB risk scoring service exists).
# List KYB verifications
Source: https://docs.dataspike.io/api-reference/kyb/kyb-verifications/list-kyb-verifications
https://api.dataspike.io/openapi/kyb/public.json get /api/v4/kyb/verifications
Server-to-server (API key) twin of the dashboard KYB verifications list: same {data, has_next} envelope and per-row schema, the only difference is tenancy — the organization comes from the API key (not a path param) and sandbox from the key / the is_sandbox query param.
Paginated list of KYB verifications for the organization, mirroring the KYC (docver) verifications list. Each row carries the table columns (company name, status, decision, risk) PLUS the expanded-row blocks: `company` (Company/Application cards), `form_progress` (widget stepper), `checks_summary` (checks badges + N/M progress) and `stage` — so the expanded row renders without a detail round-trip. Each row's `id` is the KYB CASE id — use it to drill in via GET /api/v4/kyb/verifications/{caseId}.
**Vocabularies.** `status` (stored case lifecycle): draft → under_review (Submit locked the form) → the pipeline writes a terminal status from the risk score: approved (low) | manual_review (medium) | rejected (high) | failed (checks could not be scored). A compliance officer's decision writes approved_manually / rejected_manually (the _manually suffix marks a reviewer-set status). in_remediation while a remediation round is with the merchant; remediation_review once they have answered it and the case waits for the officer to decide or re-run the checks. `stage` (derived, expanded-row header): collecting_data | running_checks | manual_review | in_remediation | remediation_review | completed. `checks_summary.checks[].type`: registry | tin | document_verification | doc_manipulation | aml_company | aml_person | cross_check (the last four appear as their pipeline steps ship). `checks[].status` (execution): pending | completed | failed | skipped | timeout — pending rows carry no result/verdict. `checks[].result` (normalized outcome — drives the badge COLOR and the passed/review/failed counters): clear, not_applicable → passed (green); warning, inconclusive → review (yellow); failed → failed (red). `checks[].verdict` is NOT an enum — it is the provider's normalized verdict to render verbatim as the badge TEXT (registry: `Active`/`Dissolved`/`not_found`/…; tin: `TIN Matched`/`Did Not Match`/`irs_code_`; document_verification: `VERIFIED`/`NOT_VERIFIED`; doc_manipulation later: `AUTHENTIC`/`SUSPICIOUS`/`LIKELY_FORGED`). `form_progress.sections[].state`: completed | current | pending, plus action_required on the synthetic `kyc` step when the representative's KYC came back failed (that one is NOT a remediation). It always describes the ORIGINAL application; a live round has its own stepper in `form_progress_remediation`, covering only the sections it asks about, whose entries are completed | in_remediation, plus a trailing synthetic `custom_asks` step ("Additional requests") when the round carries asks that point at no catalog section. `checks_summary` likewise stays the run a remediation interrupted, and `checks_summary_remediation` is the run the officer's re-run earned: that one is absent for the whole time the case sits in `in_remediation` or `remediation_review`, because no such run exists yet. `risk_level` (Low|Medium|High) comes from the business_risk_score severity once it runs, else null (render 'Not scored'). `risk_score`/`decision` (the full numeric RiskResult) stay null — the KYB score is a level, not a number; the stored `status` carries the decision.
# Answer an open information request
Source: https://docs.dataspike.io/api-reference/kyb/kyb-widget/answer-an-open-information-request
https://api.dataspike.io/openapi/kyb/public.json post /api/v4/kyb/widget/{publicId}/remediation
Autosaves the answers to the round in `remediation[]` of the widget state. Same soft draft validation as the section form (format issues come back in errors[] while the save succeeds); the round is closed by POST /api/v4/kyb/widget/{publicId}/submit, which refuses while any target is still unanswered.
Targets carry their own `section_key` because a round crosses sections. Only the FIELD targets of the live round are accepted: anything else is a 409 `not_requested`, a document group included, since a group is answered by uploading through POST /api/v4/upload/kyb/{publicId}/documents.
A requested field brings its `required_if` siblings with it, so picking "Other" in an asked select lets the specify-field the catalog then makes required be answered in the same body. The widening is one hop off a real ask: a `required_if` field whose trigger the round did not ask for is still a 409 `not_requested`.
A custom ask is answered under `custom` instead, addressed by `item_id` because it has no catalog keys: `{"custom":[{"item_id":"...","text":"..."}]}`. Both lists are optional and a body carrying neither is a 400; anything but an open custom ask of the live round that takes text is a 409 `not_requested`, a file-only ask included. Clearing the text un-answers the ask, which the re-submit then refuses if it is `required`; an ask the officer marked optional may stay empty. Text of nothing but whitespace IS clearing it, both here and at the gate.
`comments` carries the merchant's own note on an item, addressed by `item_id` the way a custom answer is, because that is the one key every kind has: `{"comments":[{"item_id":"...","text":"..."}]}`. Any item of the live round takes one, a document group and a file-only custom ask included. It is not an answer: it resolves nothing, leaves `items_open` where it was and does not open the re-submit gate, so an item carrying only a comment is still outstanding. Empty text erases the note. All three lists are optional and a body carrying none of them is a 400; an `item_id` outside the live round is a 409 `not_requested`.
`remediation` in the response is the round after this write, the same block the widget state carries as `info_request`: `items_open` counts the targets still outstanding.
# Delete a document
Source: https://docs.dataspike.io/api-reference/kyb/kyb-widget/delete-a-document
https://api.dataspike.io/openapi/kyb/public.json delete /api/v4/kyb/widget/{publicId}/documents/{documentId}
Un-tags a document from the case (metadata only); the stored blob is left for history. Scoped to the case so a widget can only remove its own files.
While an information request is open this accepts ONLY a file the applicant uploaded during the round, into a document group that request asked to be re-uploaded. Any other group, every owner government ID, and the files the group already held when the request was sent are a 409 `not_requested`: those are what the checks ran on, and removing one would answer the request by emptying the group. So an upload can always be taken back here, and the document being replaced cannot be.
The upload endpoint (POST /api/v4/upload/kyb/{publicId}/documents, served by docver-upload) applies the group half of the rule with the same code, but NOT the per-file half: a single-file group replaces rather than accumulates, so uploading into one supersedes whatever it held, the requested document included. A superseded file is not deletable here either: it stays on the case as history (`superseded_at` in the widget state) and this endpoint only ever matches a live file, so it answers 404.
An answer to a CUSTOM ask is taken back here too, judged by its ask instead of by the group rules above: it fills no document slot, so there is no group to have been reopened and no snapshot to protect. It is removable while the round holding its ask is live, and it is un-tagged from that ask alone, leaving the other answers to the same ask in place. An answer to an ask the round no longer holds open is a 409 `not_requested`. A sample the officer attached to an ask is not the applicant's to remove: that, and any ask file once the round is over, is an opaque 404.
# Get the widget state
Source: https://docs.dataspike.io/api-reference/kyb/kyb-widget/get-the-widget-state
https://api.dataspike.io/openapi/kyb/public.json get /api/v4/kyb/widget/{publicId}
Returns the full widget state for a case: the server-authoritative section catalog merged with stored draft values, owners, documents and any open remediation notes. Authenticated solely by the public_id in the path. Documents render as slots (`documents[]` on a section): each slot lists its accepted types (`options`), required flag and uploaded files; person government IDs appear on the representative section slot and inside each owner entry. The parties section carries the per-owner form template in `fields` (values live in `owners`). Documents are uploaded via POST /api/v4/upload/kyb/{publicId}/documents (multipart, served by docver-upload).
`kyc.persons[]` carries the representative's own KYC entry (`role: "representative"`), and, once the case has been submitted, each beneficial owner's personal KYC entry. The representative's entry carries `linked`: true whenever it currently shares identity with a beneficial owner selected via PUT .../representative/link, in which case its identity fields (name, dob, etc.) mirror that owner's and cannot be edited directly through PUT .../sections/representative/fields until unlinked. Find which owner by comparing this entry's `person_id` against each `owners[].applicant_id`.
`sections` and `remediation` are TWO FORMS; branch on `status`. While an information request is open (the case is `in_remediation`) `remediation[]` carries the round in catalog order, every entry writable and holding the whole catalog definition of its target (`field` or `documents`, per `target_kind`) plus the officer's `message` and the target's `previous_value`; the whole of `sections` is `editable: false` for the duration. Outside a round `remediation` is absent and `sections` is the form as before.
A field can be answerable without an entry of its own. An ask on a field with a `required_if` dependent reopens that dependent too, so picking "Other" in a reopened select (or ticking the operating address apart from the registered one) lets the specify-fields behind it be answered in the same body. They get no `remediation[]` entry, because the officer asked one question, and they read `editable: false` in `sections` like everything else there; render them under the target from that section's catalog definition. The widening is one hop off a real ask.
A round may also carry asks the catalog does not define (`target_kind: custom`), listed after the catalog ones. Such an entry has no `section_key` / `field_key` and no `previous_value`; it carries a `custom` object with the officer's own `label`, `instruction` and `expectation`, the `resp_type` that says which controls to render (**text**, **file**, **text+file**), `required`, and the `answer_text` entered so far. Answer the text through POST /api/v4/kyb/widget/{publicId}/remediation under `custom`, addressing the ask by the entry's `item_id`; files are uploaded against that same identifier. `item_id` is present on every entry, custom or not.
Every entry also carries `comment` / `comment_at`: the merchant's own note on that ask, absent until they leave one, written back through the same save under `comments` keyed by `item_id`. It rides beside the answer rather than being one, so an entry with a comment and no answer still counts in `items_open` and still stops the re-submit.
The `custom` object carries two file lists, both by identity only (they are in no document slot): `documents` are the answers uploaded so far, each removable through the widget's document delete, and `reference_documents` are read-only examples the officer attached to the ask. Read an example through GET /api/v4/upload/kyb/{publicId}/remediation/reference/{documentId}; an example never counts as an answer.
The form also carries what remediation did to it. A field a round has changed carries `original_value` (what was entered in the widget before any round touched it) and `remediation` (the round the current value came back from, plus `changes_count`); both are absent on a field no round reopened, where `value` IS the original. Asked about and reopened are not the same set: an ask on a field with a `required_if` dependent reopens that dependent too, so the specify-field behind an "Other" carries the pair of the ask that opened it without being a target of its own. A document replaced by a later upload keeps its slot with `superseded_at` set and `superseded_by_document_id` naming its replacement, listed after the live files and never `editable`. So `multiple` no longer bounds `documents[]`: it counts the LIVE files a slot takes, and a single-file slot (`multiple: false`) can list several documents. Read the live files by filtering `superseded_at == null` — one for a single-file slot, none once its last file was deleted — and treat the rest as the record of what came before. Do not read `documents[0]`: live-before-superseded is the order the response happens to be built in, not part of this contract.
# Link/unlink the representative to a beneficial owner
Source: https://docs.dataspike.io/api-reference/kyb/kyb-widget/linkunlink-the-representative-to-a-beneficial-owner
https://api.dataspike.io/openapi/kyb/public.json put /api/v4/kyb/widget/{publicId}/representative/link
Links the case's Authorized Representative to an existing beneficial owner — owner_id null clears the link back to independent manual entry. The link is literal identity sharing: whichever side already carries a KYC verification keeps it, the other side's applicant_id is repointed to match, and the representative section's identity fields are refreshed from the owner's current data. Calling again (same or a different owner_id) is safe and just re-resolves the link.
# Save owners
Source: https://docs.dataspike.io/api-reference/kyb/kyb-widget/save-owners
https://api.dataspike.io/openapi/kyb/public.json put /api/v4/kyb/widget/{publicId}/owners
Replaces the owners list (replace-all). Reconciles person applicants: creates one for any entry without an applicant_id, updates links, and drops links no longer present (the person applicants stay). Owner government IDs are uploaded separately via the documents upload endpoint with `owner_id`.
Closed while an information request is open: remediation reopens individual FIELDS, and the ownership structure has no per-item lock to reopen, so the whole surface is a 409 `not_requested` until the request is answered or withdrawn.
# Save section field values
Source: https://docs.dataspike.io/api-reference/kyb/kyb-widget/save-section-field-values
https://api.dataspike.io/openapi/kyb/public.json put /api/v4/kyb/widget/{publicId}/sections/{section}/fields
Autosaves a fields section (draft). Idempotent, last-write-wins per field. Draft validation is soft: format issues are returned in errors[] while the save still succeeds; hard validation runs at Submit.
This is the INITIAL application's form. While an information request is open (the case is `in_remediation`) it rejects every write with a 409 `not_requested`: the round is answered through POST /api/v4/kyb/widget/{publicId}/remediation instead.
# Start / retry a person's KYC
Source: https://docs.dataspike.io/api-reference/kyb/kyb-widget/start-retry-a-persons-kyc
https://api.dataspike.io/openapi/kyb/public.json post /api/v4/kyb/widget/{publicId}/persons/{personId}/kyc
Creates a fresh KYC verification for the addressed person (personId, taken from the persons[] list in GET state — today the case representative) and points the case at it as the current attempt. Returns verification_url_id, which the widget renders as a QR/link for that person to complete KYC on mobile, plus the initial status to poll. Calling again while pending/passed returns the existing verification (200); after a failed/expired one it mints a new verification (201) — this is the "Try again" action. Uses the organization's default verification profile, unless an optional profile_id is sent in the body to override it (temporary). Authenticated solely by the public_id in the path.
personId also accepts the literal string "representative", which addresses the representative even before that identity has been established yet — e.g. before any beneficial owner has been linked to it. The representative's Person applicant is created lazily on first use of this route (or at link time via PUT .../representative/link), never eagerly at case creation.
# Submit the case
Source: https://docs.dataspike.io/api-reference/kyb/kyb-widget/submit-the-case
https://api.dataspike.io/openapi/kyb/public.json post /api/v4/kyb/widget/{publicId}/submit
Runs hard validation across all required sections: required and conditionally-required fields, at least one beneficial owner, per-owner completeness (every required field of the parties form template plus an attached government ID) and every required document slot including the representative's government ID. On success locks every field and transitions draft to under_review. Idempotent: submitting an already under_review, remediation_review or completed case returns its current state.
This is also the re-submit for an open information request (the case is `in_remediation`): it additionally requires every requested field to be non-empty, answering the request all at once even though the values autosave as they are typed. A request with an empty target left is a 422 `remediation_incomplete` whose `param` names the first one, so the widget can scroll to it. The representative KYC gate is not re-applied on a re-submit. A requested document group answers the same way by holding a file, whether that file is a fresh upload or the one that was already there. Answering the request resolves it, re-locks the fields and parks the case in `remediation_review`: no checks run until a compliance officer approves or rejects the case, re-runs the checks on the corrected data, or opens a follow-up information request.
A CUSTOM ask is answered on the ask itself and its `resp_type` says how: **text** needs `answer_text`, **file** needs at least one file uploaded against the ask, **text+file** needs both. Only an ask the officer marked `required` holds the re-submit up, and there `param` is the ask's `label` rather than a `section.field` pair, since it has no catalog keys to be named by.
# Download an address check PDF report
Source: https://docs.dataspike.io/api-reference/kyt/address-checks/download-an-address-check-pdf-report
https://api.dataspike.io/openapi/kyt/public.json get /api/v4/kyt/checks/address/{id}/report
Generates and streams a PDF risk report for the specified address check. Only available for successful checks.
# Get an address check
Source: https://docs.dataspike.io/api-reference/kyt/address-checks/get-an-address-check
https://api.dataspike.io/openapi/kyt/public.json get /api/v4/kyt/checks/address/{id}
Returns the stored risk assessment for a previously submitted address check.
# Initiate an address check
Source: https://docs.dataspike.io/api-reference/kyt/address-checks/initiate-an-address-check
https://api.dataspike.io/openapi/kyt/public.json put /api/v4/kyt/checks/address
Screens a blockchain address for KYT risk and returns the full risk assessment.
# List address checks
Source: https://docs.dataspike.io/api-reference/kyt/address-checks/list-address-checks
https://api.dataspike.io/openapi/kyt/public.json get /api/v4/kyt/checks/address
Returns a paginated list of address checks for the authenticated organization.
# List alerts for an address check
Source: https://docs.dataspike.io/api-reference/kyt/address-checks/list-alerts-for-an-address-check
https://api.dataspike.io/openapi/kyt/public.json get /api/v4/kyt/checks/address/{id}/alerts
Returns alerts triggered for the specified address check. Supports filtering by date range.
# List entity types
Source: https://docs.dataspike.io/api-reference/kyt/entity-types/list-entity-types
https://api.dataspike.io/openapi/kyt/public.json get /api/v4/kyt/entity-types
Returns all KYT entity types with their machine-readable codes and human-readable labels.
# Create a risk profile
Source: https://docs.dataspike.io/api-reference/kyt/risk-profiles/create-a-risk-profile
https://api.dataspike.io/openapi/kyt/public.json post /api/v4/kyt/risk-profiles
Creates a new risk profile with the given name. Alert thresholds can be configured via the update endpoint.
# Delete a risk profile
Source: https://docs.dataspike.io/api-reference/kyt/risk-profiles/delete-a-risk-profile
https://api.dataspike.io/openapi/kyt/public.json delete /api/v4/kyt/risk-profiles/{id}
Permanently removes a risk profile. Checks associated with this profile are not affected.
# Get a risk profile
Source: https://docs.dataspike.io/api-reference/kyt/risk-profiles/get-a-risk-profile
https://api.dataspike.io/openapi/kyt/public.json get /api/v4/kyt/risk-profiles/{id}
Returns a single risk profile by its identifier.
# List risk profiles
Source: https://docs.dataspike.io/api-reference/kyt/risk-profiles/list-risk-profiles
https://api.dataspike.io/openapi/kyt/public.json get /api/v4/kyt/risk-profiles
Returns a paginated list of risk profiles for the authenticated organization.
# Update a risk profile
Source: https://docs.dataspike.io/api-reference/kyt/risk-profiles/update-a-risk-profile
https://api.dataspike.io/openapi/kyt/public.json put /api/v4/kyt/risk-profiles/{id}
Updates the name and alert thresholds of an existing risk profile. All entity type thresholds must be provided.
# Download a transaction check PDF report
Source: https://docs.dataspike.io/api-reference/kyt/transaction-checks/download-a-transaction-check-pdf-report
https://api.dataspike.io/openapi/kyt/public.json get /api/v4/kyt/checks/transaction/{id}/report
Generates and streams a PDF risk report for the specified transaction check. Only available for successful checks.
# Get a transaction check
Source: https://docs.dataspike.io/api-reference/kyt/transaction-checks/get-a-transaction-check
https://api.dataspike.io/openapi/kyt/public.json get /api/v4/kyt/checks/transaction/{id}
Returns the stored risk assessment for a previously submitted transaction check.
# Initiate a transaction check
Source: https://docs.dataspike.io/api-reference/kyt/transaction-checks/initiate-a-transaction-check
https://api.dataspike.io/openapi/kyt/public.json put /api/v4/kyt/checks/transaction
Screens a blockchain transaction for KYT risk and returns the full risk assessment.
# List alerts for a transaction check
Source: https://docs.dataspike.io/api-reference/kyt/transaction-checks/list-alerts-for-a-transaction-check
https://api.dataspike.io/openapi/kyt/public.json get /api/v4/kyt/checks/transaction/{id}/alerts
Returns alerts triggered for the specified transaction check. Supports filtering by date range.
# List transaction checks
Source: https://docs.dataspike.io/api-reference/kyt/transaction-checks/list-transaction-checks
https://api.dataspike.io/openapi/kyt/public.json get /api/v4/kyt/checks/transaction
Returns a paginated list of transaction checks for the authenticated organization.
# Get a transfer check
Source: https://docs.dataspike.io/api-reference/kyt/transfer-checks/get-a-transfer-check
https://api.dataspike.io/openapi/kyt/public.json get /api/v4/kyt/checks/transfer/{id}
Returns the stored risk assessment for a previously submitted transfer check.
# Initiate a transfer check
Source: https://docs.dataspike.io/api-reference/kyt/transfer-checks/initiate-a-transfer-check
https://api.dataspike.io/openapi/kyt/public.json put /api/v4/kyt/checks/transfer
Screens a blockchain transfer for KYT risk and returns the full risk assessment.
# List alerts for a transfer check
Source: https://docs.dataspike.io/api-reference/kyt/transfer-checks/list-alerts-for-a-transfer-check
https://api.dataspike.io/openapi/kyt/public.json get /api/v4/kyt/checks/transfer/{id}/alerts
Returns alerts triggered for the specified transfer check. Supports filtering by date range.
# List transfer checks
Source: https://docs.dataspike.io/api-reference/kyt/transfer-checks/list-transfer-checks
https://api.dataspike.io/openapi/kyt/public.json get /api/v4/kyt/checks/transfer
Returns a paginated list of transfer checks for the authenticated organization.
# Initiate Non-Document Verification
Source: https://docs.dataspike.io/api-reference/non-document-verification/initiate-non-document-verification
/api-reference/dataspike_openapi.yaml post /api/v4/nondoc/{applicant_id}/verify
Initiates a specific non-document verification check (e.g., Brazil CPF, Nigeria NIN, Nigeria BVN) for an existing applicant.
Requires the specific identifier (CPF, NIN, or BVN) in the request body.
The API checks if the verification type is supported for the applicant's country and enabled for the organization before proceeding.
Results include the final status (`verified` or `failed`) and data returned from the verification source.
# API Overview
Source: https://docs.dataspike.io/api-reference/overview
A high-level guide to how the Dataspike API works, including environments, auth, and the core identity and compliance endpoints.
The Dataspike API is a unified AI interface for identity verification, document intelligence, AML screening, deepfake detection, crypto risk analysis (KYT), and real-time behavioral and device analytics.
All endpoints follow predictable JSON schemas, standard HTTP semantics, and are optimized for low-latency, high-throughput inference at scale.
Whether you’re building onboarding flows, fraud prevention pipelines, or real-time risk engines, Dataspike provides a single, consistent API layer that abstracts complex AI systems into simple, reliable REST calls.
***
## 🌐 Base URLs
Use the appropriate environment for development, testing, or production workloads:
| Environment | Base URL | Purpose |
| - | - | - |
| **Sandbox** | `https://sandboxapi.dataspike.io` | Safe testing environment for local development, QA, and simulated verifications. |
| **Production** | `https://api.dataspike.io` | Live environment for real identity checks, AML screenings, and compliance operations. |
Each environment has its **own API key** and isolated data model.
***
## 🔑 Authentication
All API requests must be authenticated using your environment-specific API key.
You can generate and manage keys from the **Dashboard → API** section:\
👉 [https://dash.dataspike.io/api](https://dash.dataspike.io/api)
Keys for Sandbox and Production are **not interchangeable**.
### Required headers
```http theme={null}
Content-Type: application/json
ds-api-token:
```
* `Content-Type` must always be `application/json`
* `ds-api-token` authenticates your request
* Missing or invalid tokens return `401 Unauthorized`
***
## 📡 Making requests
The API uses standard HTTP methods:
| Method | Purpose |
| - | - |
| **GET** | Retrieve resources |
| **POST** | Create new resources or submit verification data |
| **PUT / PATCH** | Update existing resources |
| **DELETE** | Remove or deactivate resources |
All responses are JSON and include consistent result structures and status codes.
### Example request
```bash theme={null}
curl -X GET "https://api.dataspike.io/v1/applicants" -H "Content-Type: application/json" -H "ds-api-token: "
```
***
## 🔔 Webhooks
Use **webhooks** to receive real-time notifications when key events occur — such as completed verifications, AML matches, or KYT address alerts.
Configure webhooks under:\
**Dashboard → API → Webhooks**
Each environment (Sandbox & Production) has its own webhook configuration and delivery logs.
Common webhook event types include:
* `AML Screening`
* `Docver Check`
* `Deepfake Video Analysis`
Webhook payloads follow the same JSON schemas used by the API.
***
## 🧱 Core API Domains
**Applicants**\
Persistent identity records that link all workflows — verifications, AML checks, KYT crypto analysis, deepfake signals, and behavioral risk events.
**Verification**\
Document checks, biometric matching, liveness detection, and selfie-to-ID comparison. Works for both applicant-linked flows and fully stateless sessions.
**Deepfake Analysis**\
Detection of AI-generated or manipulated identity media across images, uploaded videos, and real-time video streams — including WebRTC, LiveKit, Google Meet, and other streaming platforms.
**AML Screening**\
Global sanctions, watchlist, PEP, and adverse-media checks with automated scoring and continuous monitoring.
**KYT (Crypto Risk Intelligence)**\
Risk evaluation for blockchain addresses and transactions: sanctions exposure, wallet behavior analysis, clustering heuristics, and counterparty intelligence.
**Document Intelligence**\
AI models for document classification, structured data extraction, and fraud pattern detection.
**Webhooks**\
Real-time events for verifications, AML matches, KYT alerts, deepfake outcomes, and applicant updates.
# Get verification
Source: https://docs.dataspike.io/api-reference/sdk/get-verification
/api-reference/dataspike_openapi.yaml get /api/v3/sdk/{short_id}
Get verification status and required checks
# Proceed with an existing Verification
Source: https://docs.dataspike.io/api-reference/sdk/proceed-with-an-existing-verification
/api-reference/dataspike_openapi.yaml post /api/v3/sdk/{short_id}/proceed
To proceed verification further call this method. Should be used after all the required documents has been uploaded.
Recovering from errors:
pending_documents - need to upload missed docs. pending_documents field indicates which documents are missed.
country_unknown - we can't detect country in POI document. Please specify country by set_country API request or use another document.
# Set country for verification
Source: https://docs.dataspike.io/api-reference/sdk/set-country-for-verification
/api-reference/dataspike_openapi.yaml post /api/v3/sdk/{short_id}/set_country
Use this endpoint if country of POI document not auto detected during upload. This manually set applicant country for verification.
# Set custom fields for applicant by short verification id
Source: https://docs.dataspike.io/api-reference/sdk/set-custom-fields-for-applicant-by-short-verification-id
/api-reference/dataspike_openapi.yaml post /api/v3/sdk/{short_id}/fields
This endpoint should be use for setup custom fields for applicant from SDK.
# Upload custom files to verification
Source: https://docs.dataspike.io/api-reference/sdk/upload-custom-files-to-verification
/api-reference/dataspike_openapi.yaml post /api/v3/sdk/{short_id}/upload
Allow uploading custom files to verification if any are enabled in verification profile
# Upload document
Source: https://docs.dataspike.io/api-reference/sdk/upload-document
/api-reference/dataspike_openapi.yaml post /api/v3/upload/sdk/{short_id}
Allow to upload documents for specified verification by short_id
# Create new rule
Source: https://docs.dataspike.io/api-reference/transmon/create-new-rule
/api-reference/dataspike_openapi.yaml post /api/v3/transmon/rules
Create new rule
# Delete existing rule
Source: https://docs.dataspike.io/api-reference/transmon/delete-existing-rule
/api-reference/dataspike_openapi.yaml delete /api/v3/transmon/rules/{rule_id}
Delete existing rule
# List existing rules
Source: https://docs.dataspike.io/api-reference/transmon/list-existing-rules
/api-reference/dataspike_openapi.yaml get /api/v3/transmon/rules/list/{event_type}
List all existing rules for given {event_type}
# Send payment event
Source: https://docs.dataspike.io/api-reference/transmon/send-payment-event
/api-reference/dataspike_openapi.yaml post /api/v3/transmon/payment
Send event of type "payment"
# Send transaction event
Source: https://docs.dataspike.io/api-reference/transmon/send-transaction-event
/api-reference/dataspike_openapi.yaml post /api/v3/transmon/transaction
Send event of type "transaction"
# Update existing rule
Source: https://docs.dataspike.io/api-reference/transmon/update-existing-rule
/api-reference/dataspike_openapi.yaml put /api/v3/transmon/rules/{rule_id}
Update existing rule
# Archive link
Source: https://docs.dataspike.io/api-reference/verification-links/archive-link
/api-reference/dataspike_openapi.yaml delete /api/v3/profiles/link/{link_id}
Archive verification link. That's action disable new verification by this link
# Change link options
Source: https://docs.dataspike.io/api-reference/verification-links/change-link-options
/api-reference/dataspike_openapi.yaml post /api/v3/profiles/link/{link_id}
Change link options.
# Create verification public link
Source: https://docs.dataspike.io/api-reference/verification-links/create-verification-public-link
/api-reference/dataspike_openapi.yaml post /api/v3/profiles/{profile_id}/link
Create verification public link. Verification created with this link will be use this profile. Only one link can be created for one profile.
# Get new verification by public link
Source: https://docs.dataspike.io/api-reference/verification-links/get-new-verification-by-public-link
/api-reference/dataspike_openapi.yaml get /api/v3/sdk/link/{link_id}
Creates new verification by public link. It's api version. Generally available at https://am.dataspike.io/vp/$link_id
# Unarchive public link
Source: https://docs.dataspike.io/api-reference/verification-links/unarchive-public-link
/api-reference/dataspike_openapi.yaml post /api/v3/profiles/link/{link_id}/unarchive
Unarchive public link
# Archive profile
Source: https://docs.dataspike.io/api-reference/verification-profiles/archive-profile
/api-reference/dataspike_openapi.yaml delete /api/v3/profiles/{profile_id}
Archive verification profile by id
# Create profile
Source: https://docs.dataspike.io/api-reference/verification-profiles/create-profile
/api-reference/dataspike_openapi.yaml post /api/v3/profiles
Create verification profile
# Get profile
Source: https://docs.dataspike.io/api-reference/verification-profiles/get-profile
/api-reference/dataspike_openapi.yaml get /api/v3/profiles/{profile_id}
Get verification profile by id
# List profiles
Source: https://docs.dataspike.io/api-reference/verification-profiles/list-profiles
/api-reference/dataspike_openapi.yaml get /api/v3/profiles
List all current verification profiles
# Set default profile
Source: https://docs.dataspike.io/api-reference/verification-profiles/set-default-profile
/api-reference/dataspike_openapi.yaml post /api/v3/profiles/{profile_id}/set_default
Set profile default by id
# Update profile
Source: https://docs.dataspike.io/api-reference/verification-profiles/update-profile
/api-reference/dataspike_openapi.yaml post /api/v3/profiles/{profile_id}
Update verification profile
# Cancel existing Verification
Source: https://docs.dataspike.io/api-reference/verifications/cancel-existing-verification
/api-reference/dataspike_openapi.yaml post /api/v3/verifications/{verification_id}/cancel
Only not started(`proceed` action was not performed yet) verification are allowed to be canceled.
# Create New Verification
Source: https://docs.dataspike.io/api-reference/verifications/create-new-verification
/api-reference/dataspike_openapi.yaml post /api/v3/verifications
Use this endpoint to request a new verification process for an applicant. Developers must provide the applicant's unique identifier and specify the type of verification required.
# Get Existing Verification
Source: https://docs.dataspike.io/api-reference/verifications/get-existing-verification
/api-reference/dataspike_openapi.yaml get /api/v3/verifications/{verification_id}
Retrieve detailed information about an existing verification by providing their unique Verification ID as a URL parameter.
# Get Existing Verification by Short Id
Source: https://docs.dataspike.io/api-reference/verifications/get-existing-verification-by-short-id
/api-reference/dataspike_openapi.yaml get /api/v3/verifications/short/{short_id}
Retrieve detailed information about an existing verification by providing their unique Verification Short ID as a URL parameter.
# Get Verification PDF Report
Source: https://docs.dataspike.io/api-reference/verifications/get-verification-pdf-report
/api-reference/dataspike_openapi.yaml get /api/v4/verifications/{verification_id}/pdf
Retrieve detailed information in PDF report by providing their unique Verification ID as a URL parameter.
# Get Verifications for an existing Applicant
Source: https://docs.dataspike.io/api-reference/verifications/get-verifications-for-an-existing-applicant
/api-reference/dataspike_openapi.yaml get /api/v3/verifications/applicant/{applicant_id}
Retrieve detailed information about an existing verification by providing existing Applicant ID as a URL parameter.
# List existing verifications
Source: https://docs.dataspike.io/api-reference/verifications/list-existing-verifications
/api-reference/dataspike_openapi.yaml get /api/v3/verifications
Allows to retrieve a collection of existing verifications within the system.
# List Verification Tracking Events
Source: https://docs.dataspike.io/api-reference/verifications/list-verification-tracking-events
/api-reference/dataspike_openapi.yaml get /api/v4/verifications/{verification_id}/events
Retrieves a list of verification tracking events by a verification identifier.
# Proceed with an existing Verification
Source: https://docs.dataspike.io/api-reference/verifications/proceed-with-an-existing-verification
/api-reference/dataspike_openapi.yaml post /api/v3/verifications/{verification_id}/proceed
To proceed verification further call this method. Should be used after all the required documents has been uploaded.
# Create Webhook
Source: https://docs.dataspike.io/api-reference/webhooks/create-webhook
/api-reference/dataspike_openapi.yaml post /api/v3/organization/webhooks
Allow to create new webhook with specified parameters
# Delete Webhooks
Source: https://docs.dataspike.io/api-reference/webhooks/delete-webhooks
/api-reference/dataspike_openapi.yaml delete /api/v3/organization/webhooks/{webhook_id}
Allow to delete existing webhooks
# Example of Possible Webhook Events
Source: https://docs.dataspike.io/api-reference/webhooks/example-of-possible-webhook-events
/api-reference/dataspike_openapi.yaml post /webhook
Fake path to describe webhook format. Click `Callbacks` bellow to see possible Webhook Object options. More information about webhooks can be found in the [documentation](https://docs.dataspike.io/webhooks).
# List Webhooks
Source: https://docs.dataspike.io/api-reference/webhooks/list-webhooks
/api-reference/dataspike_openapi.yaml get /api/v3/organization/webhooks
Allow to retrieve configuration of existing webhooks
# Rotate Webhook HMAC Secret
Source: https://docs.dataspike.io/api-reference/webhooks/rotate-webhook-hmac-secret
/api-reference/dataspike_openapi.yaml post /api/v3/organization/webhooks/{webhook_id}/hmac/rotate
Generates a new HMAC signing secret for the specified webhook and invalidates the previous one. Use this if you suspect the current secret is compromised. The new secret is returned once in the response — store it immediately.
# Update Webhook
Source: https://docs.dataspike.io/api-reference/webhooks/update-webhook
/api-reference/dataspike_openapi.yaml put /api/v3/organization/webhooks/{webhook_id}
Allow to update existing webhook with specified parameters
# View Webhook Hisotry
Source: https://docs.dataspike.io/api-reference/webhooks/view-webhook-hisotry
/api-reference/dataspike_openapi.yaml get /api/v3/notifications/webhook/history
View history of the latest webhook notification events
# Analyze Audio
Source: https://docs.dataspike.io/deepfake-detection/analyze/audio-analysis
Quick start for detecting AI/robotic deepfake voice recordings.
The **Audio Analysis API** detects AI-generated or manipulated voice recordings.\
It applies advanced deepfake detection models to evaluate the authenticity of speech, identifying indicators of robotic synthesis, splicing, or cloned voices.
This feature is essential for **fraud prevention**, **voice verification**, and **trust & safety** use cases where detecting synthetic audio is critical.
> The process runs asynchronously — you upload an audio file, receive a `job_id`, and then poll for the final verdict and confidence score.
***
## Endpoint Overview
**Method:** `POST`\
**Path:** `/api/v4/deepfake/audio/analyze`\
**Base URL:** `https://api.dataspike.io`\
**Authentication:** Include your API key in the header\
`ds-api-token: `
**Supported formats:** `.wav`, `.mp3`, `.m4a`\
**Maximum file size:** `10 MB`\
**Content type:** `multipart/form-data` containing one `file` field
## Example Workflow (cURL)
The Audio Analysis API runs asynchronously.\
Each analysis consists of two simple steps:
### 1. Submit an audio file for analysis
Send your audio file to the API to start the deepfake detection job.
```bash theme={null}
curl -X POST "https://api.dataspike.io/api/v4/deepfake/audio/analyze" \
-H "ds-api-token: $DATASPIKE_API_KEY" \
-F "file=@voice.m4a"
```
**Response (example):**
```json theme={null}
{
"id": "01827ed4-c928-7a3c-9a30-7ab7cc169d11",
"status": "queued",
"job_type": "audio"
}
```
The API returns a `job ID` immediately.
This means the analysis has been queued — processing runs asynchronously on the backend.
Keep this id to check progress and retrieve final results.
### 2. Retrieve job results
```bash theme={null}
JOB_ID="01827ed4-c928-7a3c-9a30-7ab7cc169d11"
curl -X GET "https://api.dataspike.io/api/v4/deepfake/job/$JOB_ID" \
-H "ds-api-token: $DATASPIKE_API_KEY"
```
**Completed response (example):**
```json theme={null}
{
"id": "01827ed4-c928-7a3c-9a30-7ab7cc169d11",
"status": "completed",
"job_type": "audio",
"score": 0.87,
"verdict": "deepfake_likely"
}
```
When `status` is `completed`, the response includes:
| Field | Description |
| - | - |
| `score` | Confidence value (0–1). Higher values indicate stronger deepfake likelihood. |
| `verdict` | Human-readable classification — for example `deepfake_likely` or `deepfake_unlikely`. |
> **Tip:** Use a score threshold aligned with your use case (for example, flag recordings with `score > 0.6` for manual review).
### Typical Flow
1. Upload an audio file for analysis.
2. Store the returned `job_id`.
3. Poll the job endpoint every few seconds until `status` becomes `completed`.
4. Read `score` and `verdict` to make automated or manual decisions in your system.
***
## Practical Tips
* **Keep files ≤ 10 MB.** If your recordings are larger, compress (e.g., transcode to mono, lower bitrate) before upload.
* **One file per request.** Use the `file` form field.
* **Backoff when polling.** Poll every 1–3 seconds with exponential backoff to avoid rate limits.
* **Sandbox testing (optional):** `https://sandboxapi.dataspike.io` with the same paths and headers.
* **Handle failures:** If `status` is `failed`, inspect `message`/`error_code` and consider retrying with cleaner audio.
# Analyze Documents
Source: https://docs.dataspike.io/deepfake-detection/analyze/document-analysis
Analyze passports, IDs, and government-issued documents for authenticity and deepfake manipulation.
The **Identity Document Analysis API** evaluates uploaded images of **passports**, **national IDs**, and other **government-issued documents** to verify their authenticity and detect digital tampering or deepfake manipulations.
This API is purpose-built for **KYC**, **onboarding**, and **fraud prevention** workflows, helping ensure that the documents submitted by users are genuine, unaltered, and machine-readable.
> ⚠️ The model is trained primarily on **government-issued identity documents**. Submissions of non-official or stylized documents (e.g., mockups, templates, or artistic edits) may result in **undefined or low-confidence results**.
***
## Endpoint Overview
**Method:** `POST`\
**Path:** `/api/v4/deepfake/document/image/analyze`\
**Base URL:** `https://api.dataspike.io`\
**Authentication:** Include your API key in the header\
`ds-api-token: `
**Supported formats:** `.jpg`, `.jpeg`, `.png`, `.webp`\
**Maximum file size:** `10 MB`\
**Content type:** `multipart/form-data` with a single `file` field
***
## Example Workflow (cURL)
Document analysis requests are processed **synchronously**, meaning the API performs the full analysis during the request and returns the final result immediately — no separate polling step is required.
### Submit a document for analysis
```bash theme={null}
curl -X POST "https://api.dataspike.io/api/v4/deepfake/image/analyze" \
-H "ds-api-token: $DATASPIKE_API_KEY" \
-F "file=@passport.jpg"
```
**Response (example):**
```json theme={null}
{
"id": "01827ed4-c928-7a3c-9a30-7ab7cc169d11",
"status": "queued",
"job_type": "image",
"content_type": "image/jpeg",
"file_size_bytes": 1024,
"score": 0.985,
"verdict": "deepfake_likely",
"sandbox": false
}
```
The response is returned as soon as the analysis completes.\
You can directly read the `score` and `verdict` fields to interpret the result.
| Field | Description |
| - | - |
| `score` | Confidence value (0–1). Higher values indicate a higher likelihood of manipulation or AI-generated content. |
| `verdict` | Classification result — for example `deepfake_likely` or `deepfake_unlikely`. |
> **Tip:** Choose a score threshold appropriate for your verification workflow.\
> For example, flag results with `score > 0.5` for manual review or secondary validation.
***
## Important Guidelines
* **Intended Input:** High-quality scans or photos of **government-issued identity documents** (passports, IDs, licenses).
* **Undefined Results:** Non-standard documents (mockups, templates, low-resolution scans) may yield **undefined** or **low-confidence** outcomes.
* **Lighting and Clarity:** Ensure the document is well-lit, unblurred, and occupies most of the image frame.
* **File Size:** Maximum 10 MB per upload. Resize or compress large images before submission.
* **Rate Limits:** Poll every 1–3 seconds when checking job status. Avoid tight loops to prevent throttling.
***
## Typical Flow
1. Upload the image of the ID/passport using the `/deepfake/image/analyze` endpoint.
2. Receive the job `id` in the response.
3. Poll the `/deepfake/job/{id}` endpoint periodically until `status` becomes `completed`.
4. Read the `score` and `verdict` fields to assess document authenticity.
5. Take action based on your fraud or KYC workflow.
***
## Sandbox Testing
For testing and integration purposes, you can use the sandbox environment:
**Base URL:** `https://sandboxapi.dataspike.io`
This environment behaves identically but uses non-production scoring and does not affect your production quota.
# Analyze Video
Source: https://docs.dataspike.io/deepfake-detection/analyze/video-analysis
Submit videos for authenticity and manipulation detection using the Dataspike Deepfake API.
The **Video Analysis API** enables you to analyze uploaded videos for potential AI-generated or manipulated content.\
This endpoint is designed for scenarios such as identity verification, remote onboarding, or media authenticity checks.
Each analysis produces structured results that indicate whether the media contains signs of **synthetic generation**, **tampering**, or **composited elements**.
***
## How it works
1. Upload your video to a publicly accessible or pre-signed URL.
2. Call the Dataspike Deepfake API endpoint with the video reference and optional applicant context.
3. The system performs frame sampling and authenticity analysis.
4. A structured response is returned containing the detection status, confidence scores, and indicators.
***
## Example request
```bash theme={null}
curl -X POST "https://api.dataspike.io/api/v4/deepfake/video/analyze" \
-H "ds-api-token: $API_KEY" \
-F "file=@/path/to/video.mp4"
```
***
## Example response
```json theme={null}
{
"id": "01827ed4-c928-7a3c-9a30-7ab7cc169d11",
"status": "completed",
"job_type": "video",
"content_type": "video/mp4",
"file_size_bytes": 73400320,
"score": 0.985,
"verdict": "deepfake_unlikely",
"sandbox": false
}
```
## Recommendations
The Video Analysis API is optimized for **person-centric recordings**, such as liveness checks, selfie videos, or short clips from video calls.\
It is not intended for generic or mixed-content footage.
* **Content type** — Use videos showing a **single visible person**, captured from a front-facing camera in natural lighting.\
Avoid compilations, crowd scenes, or edited footage.
* **Duration** — Short clips of **15–30 seconds** are ideal. Longer videos do not improve detection accuracy and may reduce clarity if they contain multiple contexts or transitions.
* **File size** — Keep uploads **under 100 MB** to ensure smooth transmission and faster processing.
* **Format** — Prefer **MP4 (H.264)** or **WebM** for compatibility.
* **Environment** — Steady camera position, minimal background movement, and clear facial visibility yield the most consistent results.
* **Testing** — Always validate your upload and analysis flow in the **Sandbox** environment before switching to production.
These guidelines help the system extract reliable visual and biometric cues, enabling faster and more accurate authenticity analysis.
***
For full parameter details and supported media types, see the [**API Reference**](https://docs.dataspike.io/api).
# Deepfake API vs Docver
Source: https://docs.dataspike.io/deepfake-detection/deepfake-api-vs-docver
When to use the standalone Deepfake API and when you need Docver's full identity-verification flow.
Dataspike ships deepfake detection in two products. They overlap on one capability — spotting manipulated media — but solve different problems.
A single detector. You hand it a file (photo, video, or audio) and it answers one question: **is this fake?** No KYC flow.
An end-to-end identity-verification flow: **is this a real person, is this their genuine document, and is it really them?**
***
## In plain terms
**Deepfake API** is one building block. It doesn't know who is in front of the camera, doesn't match a face against a document, and doesn't check whether the person is live. You drop it into your own process when you only need to catch deepfakes — content moderation, reviewing an interview recording, analyzing a voice message.
**Docver** is a turnkey identity check. During onboarding the user takes a selfie and captures a document, and Docver runs the whole chain at once: it confirms a live person (not a photo, mask, or screen), that the face isn't manipulated, that the document is genuine, and that the selfie matches the document. It's for teams that need full KYC/AML onboarding rather than a standalone detector.
***
## Who it's for
| | Deepfake API | Docver |
| - | - | - |
| **Best for** | HR, proctoring, trust & safety, content moderation | Fintech, crypto, KYC/AML |
| **Scope** | Standalone deepfake detector | Full identity-verification flow |
| **KYC flow** | No | Yes |
***
## Capability comparison
| Check | Deepfake API | Docver |
| - | :-: | :-: |
| Image deepfake | ✅ | ✅ *(in liveness flow)* |
| Video deepfake | ✅ | — |
| Audio deepfake / voice cloning | ✅ | — |
| Liveness & spoofing (PAD) | — | ✅ |
| Face coverage & quality | — | ✅ |
| Document deepfake | ✅ *(standalone)* | ✅ *(in flow)* |
| Document spoofing | — | ✅ |
| Face comparison | — | ✅ |
| Metadata / forensic analysis | ✅ | ✅ |
| Async processing (video / audio) | ✅ | — |
Inside Docver, deepfake detection is just one layer — and only on images. The flow runs: **Liveness + Spoofing → Deepfake (selfie) → Face coverage & quality → Document fraud → Document spoofing → Face comparison.**
# Overview
Source: https://docs.dataspike.io/deepfake-detection/overview
AI-powered media authenticity for images, documents, video, and audio — fast, accurate, and privacy-first.
The **Dataspike Deepfake Detection** system safeguards high-trust workflows by identifying AI-generated and manipulated media across **images**, **documents/IDs**, **videos**, and **audio**.\
It’s built for identity verification, KYC/AML, and digital onboarding where authenticity and compliance are critical.
***
## What it is
Dataspike combines **neural network ensembles**, **forensic signal analysis**, and **AI signature tracing** to detect:
* **Selfie & liveness manipulation** (face swaps, morphing, compositing)
* **Fake or tampered documents/IDs** (edited fields, forged MRZ/barcodes, altered holograms/laminates, reprint/recapture)
* **Video deepfakes** (synthetic face swaps, recomposed scenes)
* **Audio deepfakes** (AI-generated/cloned voices, splice & resynthesis artifacts)
Models are continuously updated to keep pace with new generators (e.g., Veo, Sana, Flux) and emerging attack patterns.
***
## Why it’s powerful
* **Multi-modal analysis** — Supports evaluation of images, identity documents, video, and audio files.
* **Robust detection models** — Designed to operate reliably across varied resolutions, compression levels, and device types.
* **Data-minimization approach** — Media is processed transiently and not stored after analysis.
* **Structured output** — Each analysis produces a consistent response schema with scores and indicators suitable for automation.
* **Flexible deployment** — Available through public API, private cloud, or on-premise environments.
***
## Why it’s fast
* **Optimized inference pipeline** — Uses asynchronous processing and batched GPU workloads to maintain high throughput.
* **Adaptive frame selection** — For video streams, only key frames are analyzed to balance accuracy and latency.
* **Lightweight result delivery** — Responses include compact, pre-aggregated indicators for real-time handling.
* **Edge and mobile support** — Inference components can run locally for latency-sensitive
***
## Ecosystem & Tools
* **📱 Mobile SDK (Android)** — Provides on-device video analysis without requiring API calls.\
Ideal for mobile verification or offline scenarios where low latency is critical.
* [View SDK on GitHub](https://github.com/dataspike-io/Deepfake-MobileSDK-Android)
* [Download Demo App (APK)](https://github.com/dataspike-io/Deepfake-MobileSDK-Android/releases/download/v1.0.0/deepfake-demo-app-1.0.0.apk)
* **🎥 LiveKit Integration** — Enables real-time deepfake detection in live video sessions.\
The integration performs adaptive frame sampling and analysis directly within LiveKit rooms, providing detection results with minimal latency and no persistent storage.
***
## Summary
Dataspike provides **real-time authenticity intelligence** for selfies, **documents/IDs**, video, and **audio**, helping you block synthetic identities, detect voice clones, and catch forged documents — without sacrificing performance or privacy.
# Android SDK
Source: https://docs.dataspike.io/deepfake-detection/real-time/android-sdk
On-device deepfake detection SDK for Android — real-time, offline, and efficient analysis of video and audio streams.
📦 **GitHub:** [Dataspike Deepfake SDK for Android](https://github.com/dataspike-io/Deepfake-MobileSDK-Android)
***
The **Dataspike Deepfake SDK for Android** provides a compact, high-performance framework for detecting AI-generated or manipulated video and audio directly **on-device** — **no internet connection required**.
Built on Dataspike’s deepfake detection engine, the SDK delivers **real-time analysis**, **low latency**, and **enterprise-grade accuracy**, even on mid-tier Android devices. It supports both **video** and **streaming audio** detection and integrates seamlessly with **Jetpack Compose**, **WebRTC**, and **standard Android views**.
> ⚡ **Key advantage:** All inference runs **locally on the device** using a lightweight yet powerful model — ensuring privacy, speed, and reliability in fully offline scenarios.
[Github](https://github.com/dataspike-io/Deepfake-MobileSDK-Android)
***
## Highlights
* 🚀 **On‑device inference** — no internet dependency, no data upload.
* 🧠 **Optimized neural model** — compact yet accurate, built for real‑time response.
* 🎥 **Video & streaming support** — compatible with `SurfaceView`, `TextureView`, and `WebRTC VideoTrack`.
* 🔒 **Privacy-first architecture** — no cloud processing; user content never leaves the device.
* ⚙️ **Kotlin‑first API** — clean, composable, and easy to integrate.
* ⚡ **Ultra‑efficient runtime** — smooth detection at 30+ FPS on mid-tier hardware.
> You can also [download the demo app](https://github.com/dataspike-io/Deepfake-MobileSDK-Android/releases/download/v1.0.0/deepfake-demo-app-1.0.0.apk) showcasing real-time detection on device.
***
## Integration Guide
### 1. Add the Maven Repository
Add the Dataspike SDK Maven repository in your `settings.gradle.kts`:
```kotlin theme={null}
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven { url = uri("https://raw.githubusercontent.com/dataspike-io/Deepfake-MobileSDK-Android/release") }
}
}
```
### 2. Add the SDK Dependency
In your app’s `build.gradle.kts`, include:
```kotlin theme={null}
dependencies {
implementation("io.dataspike:deepfake-sdk:VERSION")
}
```
Replace `VERSION` with the latest SDK release number.
***
## Initialization
Before analysis, initialize the SDK with your valid **license key**.
```kotlin theme={null}
val deepfakeSDK = DeepfakeSDK.getInstance()
val success = deepfakeSDK.initialize(
applicationContext = applicationContext,
license = "YOUR_LICENSE_KEY",
nativeLogLevel = 4, // Optional: Log level (0–5)
deepfakeThreshold = 0.5f, // Optional: Adjust detection sensitivity
)
```
If initialization succeeds, the SDK is ready to analyze both video and audio in real time.
***
## Video or WebRTC Analysis
You can analyze frames directly from a standard Android `View` or a WebRTC `VideoTrack`.\
Provide an `AnalysisCallback` to receive frame-by-frame results.
```kotlin theme={null}
val analysisCallback = object : AnalysisCallback {
override fun onFrameAnalysisResult(result: AnalysisResult, message: String?) {
// Handle frame result here (e.g., show confidence score)
}
override fun onAudioAnalysisResult(result: String) {
// Handle audio analysis result (if applicable)
}
override fun onAnalysisError(error: String) {
// Handle any errors
}
}
// Analyze a view
deepfakeSDK.startAnalysis(yourView, analysisCallback)
// Or analyze a WebRTC track
deepfakeSDK.startAnalysis(yourVideoTrack, analysisCallback)
```
> **Tip:** For real-time streams, prefer WebRTC integration. For local files, attach a `SurfaceView` or `TextureView` with playback controls.
***
## Audio-Only Analysis
To detect manipulation in live or recorded speech, you can start an **audio-only session**:
```kotlin theme={null}
deepfakeSDK.startAudioAnalysis(analysisCallback)
```
Feed raw PCM audio data (16 kHz, `ShortArray`) to the SDK in small chunks:
```kotlin theme={null}
deepfakeSDK.processAudioChunk(yourAudioData)
```
> The SDK processes \~3 s (48,000 samples) per chunk. Shorter segments should be zero‑padded.
***
## Stop and Clean Up
When analysis is complete or the user session ends:
```kotlin theme={null}
deepfakeSDK.stopAnalysis()
deepfakeSDK.shutdownWithTimeout()
```
This releases all SDK resources and gracefully closes background threads.
***
## Performance Notes
* Runs entirely **on-device** — ideal for **offline** or **privacy‑sensitive** apps.
* The deepfake model is **quantized** and **optimized for mobile GPUs**, achieving real-time performance even on **mid-tier Snapdragon and Dimensity** devices.
* Supports continuous analysis of **video + audio streams** with minimal impact on battery or CPU.
* All computations are **ephemeral** — no frame storage or network transfer.
> Typical latency: **10 ms per frame** (mid-tier device, 720p stream).
***
## Licensing
You must provide a valid **license key** during initialization.\
Licenses can be requested through your Dataspike dashboard or account manager.
If the SDK is not initialized or an invalid key is used, detection methods will fail gracefully with an error callback.
***
## Demo Application
A fully functional **demo APK** is available for download:\
👉 [deepfake-demo-app‑1.0.0.apk](https://github.com/dataspike-io/Deepfake-MobileSDK-Android/releases/download/v1.0.0/deepfake-demo-app-1.0.0.apk)
This demo demonstrates:
* Real-time video and audio deepfake detection
* Frame-level confidence scores
* Offline inference and responsive UI updates
***
## Summary
The Dataspike Deepfake SDK enables developers to embed **AI-manipulation detection** directly inside Android apps — without sending data to the cloud.\
It’s optimized for **speed**, **accuracy**, and **privacy**, providing true on-device protection against synthetic media threats.
> 🔒 **Offline. Real-time. Private.**\
> Detect deepfakes anywhere — no connection required.
# LiveKit Plugin
Source: https://docs.dataspike.io/deepfake-detection/real-time/livekit-integration
Integrate Dataspike deepfake detection into LiveKit Agents for realtime video analysis and in-room alerts.
The **Dataspike plugin for LiveKit** adds **real-time detection of AI‑manipulated (deepfake) content** to LiveKit Agent applications.\
It samples frames from remote participants’ video tracks, sends them to the Dataspike API for analysis, and publishes **lightweight detection events** back into the room or to a custom callback.
> **Summary**
>
> * **Manipulation Recognition:** Detects face swaps, compositing, and AI‑generated overlays.
> * **AI Model Detection:** Identifies content associated with modern generative systems (e.g., Veo, Sana, Flux).
> * **Adaptive Sampling:** Selectively samples frames to maintain real‑time responsiveness while minimizing bandwidth.
> * **Realtime Alerts:** Emits compact detection signals for immediate action.
> * **Privacy by Design:** No video or metadata is stored; frames are end‑to‑end encrypted in transit; results are short‑lived.
***
## Installation
```bash theme={null}
pip install dataspike-livekit-plugins
```
### Prerequisites
* **Dataspike API Key** — set as an environment variable:
```bash theme={null}
export DATASPIKE_API_KEY="your_api_key_here"
```
* **LiveKit credentials** (for your room/agent):
```bash theme={null}
export LIVEKIT_API_KEY="..."
export LIVEKIT_API_SECRET="..."
export LIVEKIT_URL="..."
```
Optional (if your Agent uses an LLM/voice stack):
```bash theme={null}
export OPENAI_API_KEY="..."
```
***
## Quick Start (Agent Worker)
The example below shows a **ready-to-use LiveKit Agent Worker** that integrates the Dataspike detector.\
It connects to a LiveKit room, attaches the deepfake detection plugin, and streams frames for real-time analysis.
Simply copy the snippet into a new file (for example, `agent_worker.py`), set your environment variables, and run it.
```python theme={null}
from livekit.plugins import dataspike
from livekit.agents import AgentSession, Agent
async def entrypoint(ctx):
# Connect AgentSession and join the room
await ctx.connect()
session = AgentSession(...)
# Create and start the Dataspike detector
detector = dataspike.DataspikeDetector(
# api_key="YOUR_API_KEY", # optional; defaults to DATASPIKE_API_KEY env
# notification_cb=on_notification, # optional; custom result handling
# normal_fps=0.2, # baseline sampling (frames/sec)
# burst_fps=1.0, # elevated sampling during suspicious states
# quality=75, # JPEG encode quality (0–100)
)
await detector.start(session, room=ctx.room)
# Start your agent logic
await session.start(agent=Agent(instructions="Talk to me!"), room=ctx.room)
```
**Start the worker**
```bash theme={null}
python examples/deepfake-detection/dataspike/agent_worker.py dev
```
### Default Alert Behavior
By default, the detector automatically subscribes to remote participant video tracks and begins adaptive frame sampling.
Detection results are published back into the LiveKit room’s data channel as lightweight JSON alerts under the topic deepfake\_alert.
**Example default payload:**
```json theme={null}
{
"type": "deepfake_alert",
"level": "suspicious",
"participant_id": "PA_abc123",
"track_id": "TRK_xyz789",
"message": "Possible manipulation indicators detected.",
"timestamp_ms": 1730123456789
}
```
***
## Custom Notification Channels
Developers can override this default behavior to route alerts anywhere —
for example, sending them to a webhook, Slack, a moderation API, or your own analytics pipeline.
You can achieve this by passing a custom async callback (notification\_cb) when creating the detector:
```python theme={null}
async def on_notification(event):
# event is a DeepfakeStreamingSchemaResultEvent
# You can push this to any external service
print(f"[ALERT] {event.level.upper()} from {event.participant_id}")
# Example: await post_to_webhook(event.dict())
detector = dataspike.DataspikeDetector(notification_cb=on_notification)
```
> Flexible integration: You have full control over how alerts are processed, logged, or relayed. For example, you can store results in a database, call an external moderation API, or send messages to your in-house alerting system.
***
## Configuration
You can configure the detector via constructor parameters or environment variables.
| Parameter | Type | Default | Description |
| -: | :- | :-: | :- |
| `api_key` | `str` | env:`DATASPIKE_API_KEY` | Dataspike API key. Required unless set via env. |
| `normal_fps` | `float` | `0.2` | Baseline sampling rate (frames/second). |
| `burst_fps` | `float` | `1.0` | Elevated sampling rate when state is suspicious/alert. |
| `quality` | `int` | `75` | JPEG quality used for encoded frames (0–100). |
| `notification_cb` | `Callable` | `None` | Optional async callback invoked for each result event. If omitted, events are published to the room data channel. |
**Environment Variables**
```bash theme={null}
export DATASPIKE_API_KEY="..." # plugin reads this by default
export LIVEKIT_API_KEY="..."
export LIVEKIT_API_SECRET="..."
export LIVEKIT_URL="..."
```
***
## How It Works
1. **Track discovery** — When a remote participant’s video track is subscribed, the detector creates an internal `InputTrack` for sampling.
2. **Adaptive sampling** — Frames are emitted at `normal_fps` during CLEAR state; if results indicate elevated risk (SUSPICIOUS/ALERT), sampling increases up to `burst_fps`.
3. **Encoding** — Frames are JPEG‑encoded (configurable `quality`) and placed on a bounded async queue to avoid backpressure.
4. **Streaming** — Frames are streamed via a secure WebSocket to the Dataspike API; detection signals are returned in real time.
5. **Notifications** — Incoming results update per‑track state and trigger either the default data‑channel publisher or your custom callback.
***
## Privacy & Security
* **No storage** — The plugin and server **do not store** video data or metadata.
* **E2E encryption in transit** — All frame transmissions are encrypted; only short‑lived detection signals are returned.
* **Principle of least data** — Only the encoded frame and minimal metadata required for analysis are transmitted.
* **Key management** — Provide `DATASPIKE_API_KEY` via environment variables or a secret manager; never hard‑code secrets.
***
## Troubleshooting
* **No events received** — Confirm the agent joined the room and remote participants are publishing video tracks. Check data channel permissions.
* **High latency** — Reduce `quality` or `burst_fps`; ensure the network path to the Dataspike API is low‑latency.
* **Rate/bandwidth constraints** — Lower `normal_fps` and/or enable selective subscription to fewer tracks.
* **Auth errors** — Ensure `DATASPIKE_API_KEY` is set in the environment of the worker process.
* **Reconnect loops** — The detector auto‑reconnects with exponential backoff; inspect logs for repeated auth or network failures.
***
## Full Agent Worker Example
```python theme={null}
import logging
from dotenv import load_dotenv
from livekit.agents import (
Agent,
AgentSession,
JobContext,
JobRequest,
WorkerOptions,
WorkerType,
cli,
)
from livekit.plugins import dataspike, openai
logger = logging.getLogger("dataspike-deepfake-example")
logger.setLevel(logging.DEBUG)
load_dotenv()
async def entrypoint(ctx: JobContext):
session = AgentSession(
llm=openai.realtime.RealtimeModel(voice="alloy"),
resume_false_interruption=False,
)
# Configure the Dataspike detector.
# - api_key: optional; if omitted, uses DATASPIKE_API_KEY from environment
# - notification_cb: optional; callback for handling notifications
# (defaults to publishing results in the room data channel)
# - other config: see plugin docs for available parameters
detector = dataspike.DataspikeDetector(
# api_key="YOUR_API_KEY",
# notification_cb=on_notification,
)
# Start the Dataspike detector and attach it to the current session and room.
await detector.start(session, room=ctx.room)
# Launch the main agent, which will automatically join the same room.
await session.start(
agent=Agent(instructions="Talk to me!"),
room=ctx.room,
)
async def on_request(req: JobRequest):
logger.info(f"[worker] job request for room={req.room.name} id={req.id}")
# you can set the agent's participant name/identity here:
await req.accept(name="dataspike-agent")
if __name__ == "__main__":
cli.run_app(
WorkerOptions(
entrypoint_fnc=entrypoint,
request_fnc=on_request,
worker_type=WorkerType.ROOM,
)
)
```
# Pipecat Integration
Source: https://docs.dataspike.io/deepfake-detection/real-time/pipecat-integration
Integrate Dataspike deepfake detection into Pipecat bots for realtime video and audio analysis with intelligent alerts.
**GitHub**: [github.com/dataspike-io/pipecat-deepfake-bot](https://github.com/dataspike-io/pipecat-deepfake-bot)
The **Dataspike integration for Pipecat** adds **real-time detection of AI‑manipulated (deepfake) content** to Pipecat applications.
It processes video frames and audio streams from WebRTC connections, sends them to the Dataspike API for analysis, and triggers **customizable detection callbacks** for immediate action.
> **Summary**
>
> * **Manipulation Recognition:** Detects face swaps, compositing, and AI‑generated video/audio overlays.
> * **AI Model Detection:** Identifies content associated with modern generative systems (e.g., Veo, Sana, Flux, voice cloning).
> * **Adaptive Sampling:** Intelligently adjusts frame rate based on detection state—normal rate during clear states, burst rate when suspicious activity is detected.
> * **Realtime Alerts:** Provides instant notification callbacks for ALERT and state transition events.
> * **Privacy by Design:** No video or metadata is stored; frames are end‑to‑end encrypted in transit; results are ephemeral.
***
## Installation
```bash theme={null}
# Clone the repository
git clone https://github.com/dataspike-io/pipecat-deepfake-bot.git
cd pipecat-deepfake-bot
# Install dependencies using uv (recommended)
uv sync
# Or using pip
pip install -e .
```
### Prerequisites
* **Python 3.10+** — Required for async/await features and type hints.
* **Dataspike API Key** — Set as an environment variable:
```bash theme={null}
export DATASPIKE_API_KEY="your_api_key_here"
```
* **Pipecat Cloud credentials** (for cloud deployment):
```bash theme={null}
# Configured via pcc-deploy.toml or Pipecat Cloud dashboard
```
Optional (for advanced configurations):
```bash theme={null}
export DATASPIKE_WS_URL="wss://api.dataspike.io/api/v4/deepfake/stream"
```
***
## Quick Start
The example below shows a **ready-to-use Pipecat bot** that integrates the Dataspike deepfake processor.
It creates a pipeline that analyzes incoming video and audio streams in real-time.
### Basic Bot Setup
Create a `.env` file with your API key:
```bash theme={null}
DATASPIKE_API_KEY=your_api_key_here
```
Run the bot:
```bash theme={null}
# Using uv
uv run bot.py
# Or using pip
python bot.py
```
**🎮 Interactive Playground**
When running locally, Pipecat automatically starts an interactive playground at **[http://localhost:7860](http://localhost:7860)** where you can:
* **Test video and audio** in real-time with your webcam and microphone
* **See the bot in action** with live deepfake detection
* **Monitor notifications** and detection state transitions (CLEAR → SUSPICIOUS → ALERT)
* **Experiment with configurations** without deploying to production
This makes it incredibly easy to develop, test, and debug your deepfake detection integration locally before deploying to Pipecat Cloud.
**Quick Testing Workflow:**
1. Start the bot: `uv run bot.py`
2. Open [http://localhost:7860](http://localhost:7860) in your browser
3. Grant camera/microphone permissions when prompted
4. Click "Connect" to establish WebRTC connection
5. Speak into your microphone and show your face to the camera
6. Watch the console logs for real-time detection events
7. Test different scenarios (different lighting, angles, voice tones)
> **Tip:** Open your browser's developer console to see detailed logs, WebSocket activity, and frame processing metrics.
### Code Example
```python theme={null}
import os
import aiohttp
from pipecat.pipeline.pipeline import Pipeline
from pipecat.transports.smallwebrtc.transport import SmallWebRTCTransport
from deepfake import DataspikeDeepfakeProcessor, VideoParams, AudioParams
async def run_bot(transport):
async with aiohttp.ClientSession() as session:
# Create the deepfake detection processor
deepfake_processor = DataspikeDeepfakeProcessor(
api_key=os.getenv("DATASPIKE_API_KEY"),
session=session,
video_params=VideoParams(
normal_fps=0.2, # Baseline frame rate
burst_fps=1.0, # Elevated rate during suspicious states
quality=75 # JPEG quality (0-100)
),
audio_params=AudioParams(
sample_rate=16000, # Required: 16kHz
sample_size=48000, # 3 seconds of audio
interval=60 # Seconds between audio samples
),
# notification_cb=custom_handler # Optional: custom callback
)
# Create pipeline: Input → Deepfake Analysis → Output
pipeline = Pipeline([
transport.input(),
deepfake_processor,
transport.output(),
])
# Start processing when client connects
@transport.event_handler("on_client_connected")
async def on_client_connected(transport, client):
await deepfake_processor.start()
deepfake_processor.set_webrtc_connection(client)
deepfake_processor.set_participant_id(client.pc_id)
await transport.capture_participant_video("camera")
```
### Default Alert Behavior
By default, the processor logs detection events to the console. When the system detects potential manipulation, it transitions through three states:
* **CLEAR** — No manipulation detected (normal frame rate)
* **SUSPICIOUS** — Potential signs of manipulation (increased frame rate for verification)
* **ALERT** — High confidence manipulation detected (notification triggered)
**Example default log output:**
```json theme={null}
{
"type": "deepfake_alert",
"level": "alert",
"participant_id": "participant_abc123",
"track_id": "video_track_xyz",
"message": "High likelihood of manipulation detected for participant_abc123.",
"timestamp_ms": 1730123456789
}
```
***
## Custom Notification Channels
Developers can override the default logging behavior to route alerts anywhere—for example, sending them to a webhook, Slack, a moderation API, database, or your own analytics pipeline.
Provide a custom async callback (`notification_cb`) when creating the processor:
```python theme={null}
async def on_deepfake_alert(event):
"""
Custom notification handler for deepfake detection events.
Args:
event: DeepfakeStreamingSchemaResultEvent containing:
- type: EventType (CLEAR, SUSPICIOUS, ALERT)
- participant_id: ID of the participant being analyzed
- track_id: Media track identifier
- timestamp_ms: Detection timestamp
"""
if event.type == EventType.ALERT:
# High confidence deepfake detected
print(f"🚨 ALERT: Deepfake detected for {event.participant_id}")
# Send to external services
await post_to_webhook({
"event": "deepfake_detected",
"participant": event.participant_id,
"track": event.track_id,
"timestamp": event.timestamp_ms
})
# Store in database
await db.store_incident(event)
# Send Slack notification
await slack.send_message(f"⚠️ Deepfake detected: {event.participant_id}")
elif event.type == EventType.CLEAR:
# Returned to normal state (after being in ALERT)
print(f"✅ CLEAR: Manipulation no longer detected for {event.participant_id}")
# Create processor with custom handler
deepfake_processor = DataspikeDeepfakeProcessor(
api_key=os.getenv("DATASPIKE_API_KEY"),
session=session,
notification_cb=on_deepfake_alert
)
```
> **Flexible Integration:** You have full control over how alerts are processed, logged, or relayed. Route detection events to monitoring systems, moderation queues, compliance logs, or real-time dashboards.
***
## Configuration
Configure the processor via constructor parameters or environment variables.
### Video Parameters
| Parameter | Type | Default | Description |
| -: | :- | :-: | :- |
| `burst_fps` | `float` | `1.0` | Frame rate during SUSPICIOUS state (higher for verification) |
| `normal_fps` | `float` | `0.2` | Frame rate during CLEAR state (lower to conserve resources) |
| `quality` | `int` | `75` | JPEG compression quality (0–100, where 100 is highest) |
### Audio Parameters
| Parameter | Type | Default | Description |
| -: | :- | :-: | :- |
| `sample_rate` | `int` | `16000` | Required sampling rate in Hz (must be 16000 for Dataspike API) |
| `sample_size` | `int` | `48000` | Samples per chunk (48000 = 3 seconds at 16kHz) |
| `interval` | `int` | `60` | Minimum seconds between audio sample transmissions |
### Processor Parameters
| Parameter | Type | Default | Description |
| -: | :- | :-: | :- |
| `api_key` | `str` | env:`DATASPIKE_API_KEY` | Dataspike API key. Required unless set via environment. |
| `session` | `ClientSession` | *required* | aiohttp ClientSession for WebSocket connections |
| `video_params` | `VideoParams` | defaults | Video processing configuration |
| `audio_params` | `AudioParams` | defaults | Audio processing configuration |
| `notification_cb` | `Callable` | `None` | Optional async callback for detection events. Defaults to logging. |
**Environment Variables**
```bash theme={null}
export DATASPIKE_API_KEY="..." # Required: Your API key
export DATASPIKE_WS_URL="..." # Optional: Custom WebSocket endpoint
```
***
## How It Works
### Development Flow (Local with Playground)
1. **Start Bot** — Run `uv run bot.py` to launch the bot with the integrated Pipecat playground at [http://localhost:7860](http://localhost:7860)
2. **Connect Browser** — Open the playground in your browser and grant camera/microphone permissions
3. **WebRTC Handshake** — The playground establishes a WebRTC connection to the bot
4. **Real-time Processing** — Your video/audio streams through the pipeline for live deepfake analysis
5. **Instant Feedback** — Detection results appear in console logs and trigger your notification callbacks
### Production Flow (Architecture)
1. **WebRTC Connection** — When a client connects via SmallWebRTC, the bot establishes a bidirectional media connection.
2. **Frame Capture** — The processor intercepts `InputImageRawFrame` (camera only) and `InputAudioRawFrame` from the pipeline.
3. **Adaptive Sampling** — Video frames are sampled at `normal_fps` during CLEAR state. When SUSPICIOUS events occur, sampling increases to `burst_fps` for rapid verification.
4. **Audio Buffering** — Audio is only processed when users are speaking (detected via VAD). Samples are accumulated into 3-second chunks and resampled to 16kHz PCM.
5. **Encoding & Queuing** — Video frames are JPEG-encoded and queued (max 16 items). Stale frames are dropped to prevent blocking.
6. **Streaming** — Frames stream via secure WebSocket to the Dataspike API. Detection results return in real-time.
7. **State Management** — The processor maintains detection state (CLEAR/SUSPICIOUS/ALERT) and triggers notifications on state transitions.
8. **Auto-Reconnection** — If the WebSocket drops, the processor reconnects with exponential backoff (1s → 10s max) and jitter.
***
## Deployment
### Pipecat Cloud
Deploy to [Pipecat Cloud](https://www.pipecat.ai/) for production-grade scaling:
1. **Update `pcc-deploy.toml`**:
```toml theme={null}
agent_name = "dataspike-deepfake-detection"
image = "your_username/dataspike-deepfake-detection:0.1"
image_credentials = "your_dockerhub_image_pull_secret"
secret_set = "dataspike-secrets"
[scaling]
min_agents = 1
```
2. **Build and push Docker image**:
```bash theme={null}
docker build -t your_username/dataspike-deepfake-detection:0.1 .
docker push your_username/dataspike-deepfake-detection:0.1
```
3. **Create secret set** in Pipecat Cloud dashboard with `DATASPIKE_API_KEY`
4. **Deploy**:
```bash theme={null}
pcc deploy
```
For detailed deployment instructions, see the [Pipecat Quickstart Guide](https://docs.pipecat.ai/getting-started/quickstart#step-2%3A-deploy-to-production).
### Docker (Self-Hosted)
```bash theme={null}
# Build the image
docker build -t dataspike-deepfake-bot .
# Run with environment variables
docker run -e DATASPIKE_API_KEY=your_key_here dataspike-deepfake-bot
```
***
## Privacy & Security
* **No Storage** — The processor and Dataspike API **do not store** video, audio, or metadata.
* **E2E Encryption** — All transmissions use TLS/WSS; only ephemeral detection signals are returned.
* **Minimal Data** — Only encoded frames and essential metadata (participant ID, track ID, timestamp) are transmitted.
* **Key Management** — Store `DATASPIKE_API_KEY` in environment variables or secret managers. Never hard-code credentials.
* **Camera Only** — The processor explicitly filters for camera feeds (`transport_source == "camera"`) and ignores screen sharing to minimize unnecessary data transmission.
***
## Troubleshooting
### WebSocket Connection Issues
**Symptom:** "Timeout waiting for WebRTC connection" errors
**Solutions:**
* Verify Pipecat Cloud configuration is correct
* Check that `session_manager` timeout (120s) is sufficient
* Ensure `DATASPIKE_API_KEY` is set correctly
* Review network connectivity to `wss://api.dataspike.io`
### Frame Rate / Performance Issues
**Symptom:** High CPU usage or detection lag
**Solutions:**
* Reduce `video_params.normal_fps` (default: 0.2 fps)
* Lower `video_params.quality` to reduce encoding overhead (default: 75)
* Increase `audio_params.interval` to process audio less frequently (default: 60s)
* Monitor queue size—frequent `QueueFull` warnings indicate overload
### Audio Detection Not Working
**Symptom:** No audio analysis results
**Checklist:**
* User must be actively speaking (audio only processed during `UserStartedSpeakingFrame`)
* Audio buffer must reach `sample_size` (48,000 samples = 3 seconds)
* WebRTC connection must have active audio input track
* VAD (Voice Activity Detection) must be properly configured
### Authentication Errors
**Symptom:** "DATASPIKE\_API\_KEY must be set" or 401 errors
**Solutions:**
* Verify environment variable is set: `echo $DATASPIKE_API_KEY`
* Check API key is valid at [Dataspike Dashboard](https://dataspike.io/)
* Ensure `.env` file is in the correct directory
* For Docker: verify `-e` flag or secret mount is correct
### Local Playground Issues
**Symptom:** Can't access [http://localhost:7860](http://localhost:7860)
**Solutions:**
* Ensure the bot is running (`uv run bot.py` or `python bot.py`)
* Check no other service is using port 7860
* Look for "Playground available at [http://localhost:7860](http://localhost:7860)" in logs
* Try accessing from the same machine where the bot is running
**Symptom:** Webcam/microphone not working in playground
**Solutions:**
* Grant browser permissions for camera and microphone access
* Ensure no other application is using your webcam/microphone
* Try a different browser (Chrome/Edge recommended for WebRTC)
* Check browser console for detailed error messages
***
## Full Bot Example
Complete implementation showing all features:
```python theme={null}
"""
Dataspike Deepfake Detection Bot for Pipecat
Real-time video and audio analysis with custom notifications
"""
import os
import aiohttp
from dotenv import load_dotenv
from loguru import logger
from pipecat.audio.vad.silero import SileroVADAnalyzer
from pipecat.pipeline.pipeline import Pipeline
from pipecat.pipeline.runner import PipelineRunner
from pipecat.pipeline.task import PipelineParams, PipelineTask
from pipecat.transports.smallwebrtc.transport import SmallWebRTCTransport
from pipecat.transports.base_transport import TransportParams
from deepfake import DataspikeDeepfakeProcessor, VideoParams, AudioParams
from schema_pb2 import DeepfakeStreamingSchemaResultEvent, EventType
load_dotenv()
async def custom_notification_handler(event: DeepfakeStreamingSchemaResultEvent):
"""Handle deepfake detection events with custom logic."""
if event.type == EventType.ALERT:
logger.critical(f"🚨 DEEPFAKE ALERT: {event.participant_id}")
# Route to your monitoring/moderation systems
# await send_to_slack(event)
# await store_in_database(event)
# await trigger_moderation_workflow(event)
elif event.type == EventType.CLEAR:
logger.info(f"✅ All clear for {event.participant_id}")
async def run_bot(transport, runner_args):
"""Execute the bot with deepfake detection."""
async with aiohttp.ClientSession() as session:
# Initialize processor with custom configuration
deepfake_processor = DataspikeDeepfakeProcessor(
api_key=os.getenv("DATASPIKE_API_KEY"),
session=session,
video_params=VideoParams(
normal_fps=0.2, # 1 frame every 5 seconds
burst_fps=1.0, # 1 frame per second when suspicious
quality=75 # Good quality/bandwidth balance
),
audio_params=AudioParams(
sample_rate=16000, # Required by API
sample_size=48000, # 3-second chunks
interval=60 # Sample every 60 seconds
),
notification_cb=custom_notification_handler
)
# Build pipeline
pipeline = Pipeline([
transport.input(),
deepfake_processor,
transport.output(),
])
# Configure task
task = PipelineTask(
pipeline,
params=PipelineParams(
allow_interruptions=True,
enable_metrics=True,
enable_usage_metrics=True,
),
)
# Event handlers
@transport.event_handler("on_client_connected")
async def on_client_connected(transport, client):
logger.info(f"Client connected: {client.pc_id}")
await deepfake_processor.start()
deepfake_processor.set_webrtc_connection(client)
deepfake_processor.set_participant_id(client.pc_id)
await transport.capture_participant_video("camera")
@transport.event_handler("on_client_disconnected")
async def on_client_disconnected(transport, participant):
logger.info(f"Client disconnected: {participant}")
await deepfake_processor.stop()
await task.cancel()
# Run the pipeline
runner = PipelineRunner(handle_sigint=runner_args.handle_sigint)
await runner.run(task)
# Entry point
if __name__ == "__main__":
from pipecat.runner.run import main
main()
```
***
## Reference Implementation
For a complete, production-ready implementation, see the **official repository**:
**🔗 [dataspike-io/pipecat-deepfake-bot](https://github.com/dataspike-io/pipecat-deepfake-bot)**
The repository includes:
* Complete bot implementation with comprehensive documentation
* Dockerfile and Pipecat Cloud deployment configuration
* Example custom notification handlers
* Environment configuration templates
* Integration tests and examples
***
## API Reference
### DataspikeDeepfakeProcessor
**Constructor:**
```python theme={null}
DataspikeDeepfakeProcessor(
api_key: str | None = None,
session: aiohttp.ClientSession | None = None,
video_params: VideoParams = VideoParams(),
audio_params: AudioParams = AudioParams(),
notification_cb: Callable[[DeepfakeStreamingSchemaResultEvent], Awaitable[None]] | None = None
)
```
**Methods:**
* `async start()` — Start WebSocket connection and processing loop
* `async stop()` — Stop processor and close connections gracefully
* `set_participant_id(str)` — Set participant identifier for tracking
* `set_webrtc_connection(SmallWebRTCConnection)` — Configure media track access
**State Transitions:**
* `CLEAR` → `SUSPICIOUS` — Potential manipulation detected, frame rate increases
* `SUSPICIOUS` → `ALERT` — High confidence detection, notification triggered
* `ALERT` → `CLEAR` — Manipulation resolved, notification triggered
***
## Support & Resources
* **Dataspike API Documentation:** [docs.dataspike.io](https://docs.dataspike.io)
* **Dataspike Support:** [dataspike.io/contact-us](https://dataspike.io/contact-us?lang=en)
* **Pipecat Framework:** [github.com/pipecat-ai/pipecat](https://github.com/pipecat-ai/pipecat)
* **Pipecat Documentation:** [docs.pipecat.ai](https://docs.pipecat.ai/)
* **This Integration:** [github.com/dataspike-io/pipecat-deepfake-bot](https://github.com/dataspike-io/pipecat-deepfake-bot)
For issues specific to this integration, open an issue on GitHub or contact Dataspike support.
# Data Retention
Source: https://docs.dataspike.io/deepfake-detection/references/data-retention
We process data, we don't keep it — check data is deleted automatically in two stages.
In line with the data-minimization principle (GDPR), check data is deleted automatically in two stages. Both timers start from the moment a result is issued (status `completed`).
| Stage | Retention | Deleted | Retained | API behavior |
| - | - | - | - | - |
| **Raw media** | 30 days | Video, photos, audio, documents | Analysis results | `file_url: null` |
| **Results** | 180 days | Score, metadata, PDF reports | — | Record no longer returned |
* After raw media is deleted (30 days), the analysis results remain and the API returns `file_url: null`.
* After results are deleted (180 days), the record stops being returned and disappears from the checks list.
**Enterprise** — raw-media and result retention windows are configurable by agreement. Talk to your Dataspike account manager.
# Metadata & Forensics
Source: https://docs.dataspike.io/deepfake-detection/references/metadata-forensics
A forensic risk layer that inspects file metadata for traces of editing and AI generation.
Alongside neural detection, the API inspects file metadata for signs of editing or AI generation: traces of professional editors, generative tools, codec and timestamp anomalies, and the presence of a C2PA manifest or watermark.
***
## What it looks for
Detected tools include Adobe Photoshop / Premiere, After Effects, DaVinci Resolve, Final Cut, Stable Diffusion, Midjourney, DALL·E, Synthesia, Runway, Faceswap, and others.
Signals surface as flags such as:
| Flag | Meaning |
| - | - |
| `professional_software` | Traces of a professional editor in the file |
| `ai_generator_detected` | Signature of a known generative tool |
| `suspicious` | Anomalies in codecs, timestamps, or provenance |
***
## How to treat it
Forensic signals are an **additional risk layer, not a standalone verdict**. Traces of editing raise the overall risk score, but on their own they do not imply malicious intent — legitimate media is often edited too.
# Pricing
Source: https://docs.dataspike.io/deepfake-detection/references/pricing
How Deepfake API checks are billed — per image for stills, and per second for audio, video, and streaming.
Static image checks are billed per item; audio, video, and streaming are billed by duration. For video, per-second billing is scaled by resolution and frame-rate multipliers.
***
## Billing units
| Check | What it does | Unit |
| - | - | - |
| Image Check | Detect AI-generated / manipulated faces in images | per image |
| Document Image Check | Analyze the photo on an identity document | per image |
| Audio Check | Detect AI-generated / cloned voice | per second |
| Video Check | Detect synthetic / manipulated faces in video | per second (min. 5 s) |
| LiveKit Streaming | Real-time detection in a live video session | per second of stream |
Audio is rounded up to the whole second.
***
## Video formula
Video is billed per second (minimum 5 s), scaled by resolution and frame rate:
```text theme={null}
cost = max(seconds, 5) × base rate × resolution multiplier × FPS multiplier
```
**Base rate: `$0.005` per second of video.**
The base rate is indicative and used here to make the examples reproducible — confirm the current rate for your plan with Dataspike.
### Resolution multiplier
| Resolution | Multiplier |
| - | :-: |
| ≤ 720p | ×1.0 |
| 1080p | ×1.2 |
| 1440p | ×1.5 |
| 4K | ×2.0 |
### FPS multiplier
| Frame rate | Multiplier |
| - | :-: |
| ≤ 30 FPS | ×1.0 |
| 31–60 FPS | ×1.5 |
The standard tiers cover frame rates up to 60 FPS.
### Worked examples
Each row expands the full formula so the cost is traceable end to end.
| Video | Calculation (`s × base × res × fps`) | Cost |
| - | - | - |
| 2 s @ 720p / 30 FPS | 5 × 0.005 × 1.0 × 1.0 *(5 s minimum applies)* | **≈ \$0.03** |
| 10 s @ 720p / 30 FPS | 10 × 0.005 × 1.0 × 1.0 | **\$0.05** |
| 10 s @ 1080p / 60 FPS | 10 × 0.005 × 1.2 × 1.5 | **\$0.09** |
# Results & Scoring
Source: https://docs.dataspike.io/deepfake-detection/results-scoring
How to read the score and verdict, and how a job moves through queued, completed, and failed.
## Score & verdict
* **`score`** — a `float` from `0` to `1`: the probability that the media is a deepfake (`0` = authentic, `1` = deepfake). Returned only when `status: completed`.
* **`verdict`** — the decision derived from the score: `deepfake_likely` or `deepfake_unlikely`.
The threshold that converts `score` into `verdict` is fixed on the Dataspike side and **cannot be tuned by the client**.
Forensic flags from the [metadata layer](/deepfake-detection/references/metadata-forensics) are an additional risk signal, not a standalone verdict — a flag raises overall risk but does not by itself mean the media is malicious.
***
## Job lifecycle
Images resolve **synchronously** — the result is in the response. Video and audio are **asynchronous**: submitting returns a `job_id`, and you fetch the result from `GET /job/{job_id}`.
| Status | Meaning | Available fields |
| - | - | - |
| `queued` | Accepted and processing | — |
| `completed` | Done | `score`, `verdict` |
| `failed` | Processing error | `error_code`, `message` |
The job is accepted and in the processing queue. Keep polling `/job/{id}`.
`score` and `verdict` are now populated.
`error_code` explains why — see [error codes](/api-reference#error-codes).
# What We Detect
Source: https://docs.dataspike.io/deepfake-detection/what-we-detect
Attack vectors covered by the Deepfake API across images, video, and audio — and the fraud scenarios they map to.
Coverage depends on the media type. Images and video are analyzed for **face manipulation**; audio is analyzed for **voice synthesis and cloning**.
***
## Attack vectors by media type
| Attack vector | Image | Video | Audio |
| - | :-: | :-: | :-: |
| Face swap | ✅ | ✅ | — |
| Manipulated faces | ✅ | ✅ | — |
| Synthetic / AI-generated faces | ✅ | ✅ | — |
| Talking-head deepfakes | — | ✅ | — |
| Lip-sync manipulation | — | ✅ | — |
| Face reenactment | — | ✅ | — |
| Voice cloning | — | — | ✅ |
| Synthetic speech | — | — | ✅ |
***
## Fraud scenarios
Passing verification on behalf of someone else.
Mass sign-ups using AI-generated faces.
Substituting a candidate during a remote interview.
Defeating online exam supervision.
Beating voice-based authentication.
Calls placed with a cloned voice.
Beyond neural detection, every check also runs a [metadata & forensic layer](/deepfake-detection/references/metadata-forensics) that looks for traces of editing and AI generation.
# First Verification
Source: https://docs.dataspike.io/document-verification/first-verification
Create an applicant, start a verification, submit documents, and retrieve results via webhook or API.
# First Document Verification
This guide walks you through your first end-to-end document verification
using the Dataspike API:
1. Create an **Applicant**
2. Create a **Verification**
3. Submit Documents (via hosted link, Web SDK, or your own upload flow)
4. Retrieve the **result** (via webhook or polling)
> 🔐 **Environments:** Use **Sandbox** while integrating.
> **Sandbox** `https://sandboxapi.dataspike.io` • **Production**
> `https://api.dataspike.io`
> Each environment has its **own API key** and data. Switch in
> **Dashboard → API**.
***
## Prerequisites
* Your **API key** from the dashboard (**Dashboard → API**).
* **Sandbox** selected while testing (recommended).
* A server environment capable of making HTTPS requests.
Set your key (example):
```bash theme={null}
export API_KEY=""
```
***
## 1) Create an Applicant
Create a new applicant record. You can include optional metadata like
`external_id`, name, or contact details.
```bash theme={null}
curl -X POST "https://api.dataspike.io/api/v3/applicants" -H "Content-Type: application/json" -H "ds-api-token: $API_KEY" --data '{"external_id": "12345678"}'
```
```python theme={null}
import requests, os
API_KEY = os.environ["API_KEY"]
BASE = "https://api.dataspike.io" # use https://sandboxapi.dataspike.io for Sandbox
r = requests.post(
f"{BASE}/api/v3/applicants",
headers={
"ds-api-token": API_KEY,
"Content-Type": "application/json",
},
json={"external_id": "12345678"},
)
r.raise_for_status()
print(r.json())
```
**Example response**
```json theme={null}
{
"id": "01827ed4-c928-7a3c-9a30-7ab7cc169d11",
"external_id": "12345678"
}
```
***
## 2) Create a Verification
Create a verification linked to your applicant.\
If you don’t specify a `profile_id`, the **default verification profile** is used. You can manage profiles via API or Dashboard.
```bash theme={null}
curl -X POST "https://api.dataspike.io/api/v3/verifications" -H "Content-Type: application/json" -H "ds-api-token: $API_KEY" --data '{"applicant_id": "01827ed4-c928-7a3c-9a30-7ab7cc169d11"}'
```
```python theme={null}
import requests, os
API_KEY = os.environ["API_KEY"]
BASE = "https://api.dataspike.io" # use sandbox base for testing
payload = {
"applicant_id": "01827ed4-c928-7a3c-9a30-7ab7cc169d11",
# "profile_id": "prof_xxx" # optional: choose a specific profile
}
r = requests.post(
f"{BASE}/api/v3/verifications",
headers={"ds-api-token": API_KEY, "Content-Type": "application/json"},
json=payload,
)
r.raise_for_status()
data = r.json()
print(data)
```
**You’ll receive** a payload that includes a **`verification_url`** and short **`verification_url_id`**:
```json theme={null}
{
"id": "01827ed4-c928-7a3c-9a30-7ab7cc169d12",
"verification_url": "https://am.dataspike.io/VF57124F182867E0",
"verification_url_id": "VF57124F182867E0",
"applicant_id": "01827ed4-c928-7a3c-9a30-7ab7cc169d11",
"profile_id": "default"
}
```
***
## 3) Submit Documents (choose one)
You have three options for collecting documents and completing the flow:
### A) Hosted link (fastest)
Send the **`verification_url`** to the user and wait for the webhook event when verification completes.
### B) Web SDK (embedded, recommended)
Embed the widget in your app and initialize it with **`verification_url_id`** (safe to expose).
```javascript theme={null}
DocsVerificationWidget({
id: 'VF57124F182867E0', // verification_url_id
elementId: 'root', // container element
apiUrl: 'https://api.dataspike.io'
});
```
> ⚠️ Never expose your API key in frontend code. Create the verification on your **backend** and only return `verification_url_id` to the client.
### C) Your own upload flow (advanced)
If you already have a document capture UX, you can upload files via [File management API](/api-reference/file-management/upload-document) using the verification ID/URL ID or applicant ID. (See API reference for endpoints and content types.)
***
## 4) Proceed (optional, for custom flows)
If you built a **manual** capture/upload flow, call **proceed** once all docs are submitted to start the verification.
```bash theme={null}
curl -X POST "https://api.dataspike.io/api/v3/verifications/$VERIFICATION_UUID/proceed" -H "ds-api-token: $API_KEY"
```
```python theme={null}
import requests, os
API_KEY = os.environ["API_KEY"]
BASE = "https://api.dataspike.io"
VERIFICATION_UUID = "verif_9a30_7ab7cc169d11"
r = requests.post(
f"{BASE}/api/v3/verifications/{VERIFICATION_UUID}/proceed",
headers={"ds-api-token": API_KEY},
)
r.raise_for_status()
print(r.json())
```
> The **Web SDK** handles this automatically; you don’t need to call `proceed` when using the widget.
***
## 5) Get Results (webhook or poll)
### Option 1 — Webhook (recommended)
Configure webhooks in **Dashboard → API → Webhooks**.\
Enable the endpoint and use **Test** to send a dummy event.\
Requests include an identifying header with the **webhook internal ID**, so you can trace deliveries and verify origin.
**Example webhook payload**
```json theme={null}
{
"event": "DOCVER_EVENT",
"verification_id": "verif_9a30_7ab7cc169d11",
"applicant_id": "01827ed4-c928-7a3c-9a30-7ab7cc169d11",
"status": "approved",
"scores": {
"document_authenticity": 0.98,
"face_match": 0.97,
"liveness": 0.99
},
"occurred_at": "2025-01-01T12:00:00Z"
}
```
### Option 2 — Poll via API
You can query verification status directly if you prefer polling or are still setting up webhooks.
```bash theme={null}
curl -X GET "https://api.dataspike.io/api/v3/verifications/verif_9a30_7ab7cc169d11" -H "ds-api-token: $API_KEY"
```
```python theme={null}
import requests, os
API_KEY = os.environ["API_KEY"]
BASE = "https://api.dataspike.io"
VERIFICATION_UUID = "verif_9a30_7ab7cc169d11"
r = requests.get(
f"{BASE}/api/v3/verifications/{VERIFICATION_UUID}",
headers={"ds-api-token": API_KEY},
)
r.raise_for_status()
print(r.json())
```
***
## Notes & Best Practices
* **Sandbox first:** Use `https://sandboxapi.dataspike.io` and your **Sandbox** API key while integrating.
* **Profiles:** To customize the verification flow, pass a specific `profile_id` when creating the verification.
* **Security:** Keep `applicantId`, `profileId`, and **API keys** on your backend only.
* **Widget:** Prefer the **Web SDK** for fastest integration (it also calls `proceed` for you).
* **Stateless checks:** You can run isolated checks without storing Applicants if you need a lightweight flow.
***
## Next Steps
* Integrate the [Web SDK](https://docs.dataspike.io/web-sdk)
* Run an [AML Screening](https://docs.dataspike.io/aml-screening)
# Overview
Source: https://docs.dataspike.io/document-verification/overview
Choose the right approach for integrating Dataspike document verification: API + Web SDK or fully manual.
Dataspike supports two common integration patterns for document and identity verification.\
Pick the approach that matches your product needs, team resources, and time-to-market.
> 🔐 **Tip:** Build and test in **Sandbox** first (`https://sandboxapi.dataspike.io`), then switch to Production when you’re ready to go live.
***
## Typical Integration Scenarios
### 1) API + DocVer Web SDK (recommended)
**Best for:** Teams that want a fast, secure, and branded verification flow without building a custom UI from scratch.
* Use the **API** to create and manage **Applicants** and **Verifications**.
* Embed the **DocVer Web SDK** in your app to collect documents, selfies, and liveness checks.
* The widget handles capture UX, error states, and progression — you control business logic on the backend.
* Webhooks notify you when the verification completes or changes status.
**Start here:** See the [**First Document Verification**](/document-verification/first-verification) guide.
* [**First Document Verification**](/document-verification/first-verification)
* [**Web SDK Overview**](/web-sdk)
* [**Web SDK Customization**](/web-sdk/customization) — theming, callbacks, and advanced options
***
### 2) Fully Manual Operation
**Best for:** Teams that need complete control over UX, have a pre-existing capture flow, or must operate in constrained environments.
* Build your own **client-side UI** for document capture and user guidance.
* Use the **API** directly to upload files, manage verification state, and trigger `proceed` when all inputs are ready.
* Handle all error states and retries in your application.
* Webhooks still recommended for asynchronous result delivery; polling is available if needed.
**You’ll need to implement:**
* Document & selfie capture UI
* File validation and retry UX
* API uploads + `proceed` call
* Result handling (webhooks or polling)
***
## Choosing the right approach
* **Time-to-market:** Use **API + Web SDK**.
* **Full UX control / legacy UI:** Choose **Fully Manual**.
* **Mixed:** Start with **Web SDK**, then override specific steps via API as needed.
***
## Next steps
* [**Get started quickly**](/document-verification/first-verification) — follow the step-by-step guide for your first verification.
* [**Embed in your app**](/web-sdk) — learn how to integrate the DocVer Web SDK for a seamless verification flow.
* [**Customize look & behavior**](/web-sdk/customization) — personalize your widget’s theme, typography, and callbacks.
# Intro to Dataspike
Source: https://docs.dataspike.io/home/about
An AI platform for real-time identity intelligence, fraud prevention, and compliance automation.
Dataspike is a cloud-native AI platform for real-time identity intelligence.\
We develop and deploy advanced technologies for face recognition, deepfake detection, document verification, and behavioral risk analytics — enabling secure identification and fraud prevention across digital and physical environments.
Our unified infrastructure combines machine learning, computer vision, and data intelligence to deliver instant, verifiable insights.\
From fintech onboarding to crowd analysis in large venues, Dataspike empowers organizations to verify, monitor, and protect identities at any scale — with the same accuracy, speed, and security.
***
## Why choose Dataspike?
Dataspike combines deep AI research with modern cloud engineering to deliver **a scalable, intelligence-driven platform for identity and risk management**.\
Built for developers and enterprises alike, it transforms complex verification and compliance workflows into a single, reliable API layer.
Key advantages include:
* **AI-first architecture** – Proprietary deep-learning models for document parsing, facial recognition, and deepfake detection, optimized for real-time inference.
* **Unified identity model** – All checks—KYC, AML, KYT, and biometric verification—connected through a single Applicant entity for consistent risk scoring and traceability.
* **Developer-centric design** – Simple REST APIs, SDKs, and webhooks with transparent schemas and predictable responses.
* **Elastic scalability** – Cloud-native, event-driven microservices capable of processing millions of verifications per day with regional failover.
* **Security and compliance by default** – Encryption, fine-grained access control, and adherence to global standards including GDPR, SOC 2, and ISO 27001.
* **Automation and intelligence** – Risk scoring, adaptive decisioning, and rule-based workflows that minimize manual review.
* **Continuous insight** – Real-time monitoring and analytics that surface anomalies, fraud trends, and system health across all verification flows.
***
## Core components
The Dataspike ecosystem is built around a single source of truth — the **Applicant**.\
Every verification, screening, and risk assessment can be linked to an applicant entity, providing a unified and auditable view of digital identity across systems.\
At the same time, Dataspike also supports **isolated and stateless checks** for customers who prefer lightweight integrations or single-use verification workflows.
* **Applicants API** – The foundation of the Dataspike platform. Create, update, and manage applicant records that serve as anchors for all verification and risk workflows. Each applicant can have multiple verification sessions, AML screenings, KYT analyses, and behavioral risk assessments associated with them.
* **Verification API** – Perform document, biometric, and liveness verification using AI-driven models. Supports both applicant-linked and standalone checks for maximum flexibility.
* **AML Screening API** – Automatically screen applicants—or individual identities—against global watchlists, sanctions, PEP databases, and adverse media.
* **Deepfake Detection Engine** – Detect AI-generated or manipulated images and videos using deep neural networks trained for media authenticity and fraud prevention.
* **Cybersecurity & Device Intelligence** – Identify anomalies and potential threats using device fingerprinting, network telemetry, and behavioral analytics. Detect compromised sessions, emulated devices, or synthetic traffic in real time.
* **Crypto Risk Intelligence (KYT)** – Monitor blockchain addresses and transactions linked to applicants or independent wallets to assess exposure to sanctioned or high-risk entities.
* **Dashboard** – A centralized interface for managing applicants, monitoring verification pipelines, reviewing alerts, and auditing performance metrics.
* **Web SDK** – Embed identity verification, biometric capture, and onboarding flows directly into web or mobile applications with minimal setup.
* **Webhook Framework** – Receive real-time event notifications for applicant updates, verification results, AML matches, and anomaly detections.
***
## Technology stack
Dataspike is a **cloud-native AI platform**, designed to operate in any environment — from secure enterprise data centers to large-scale public cloud and edge deployments.\
Our infrastructure is optimized for **high-performance computing**, **low-latency inference**, and **geographically distributed workloads**, enabling real-time identity intelligence anywhere it’s needed — from online onboarding to physical access control or crowd analytics.
We engineer every layer of the stack in-house to balance **accuracy, scalability, and privacy**:
* **Machine learning and computer vision** – Proprietary deep-learning models built with PyTorch and TensorFlow for face recognition, document parsing, and deepfake detection.
* **Inference optimization** – Accelerated pipelines using GPU and edge runtimes for sub-second verification, even in constrained environments.
* **Modular microservices architecture** – Cloud-native, event-driven, and deployable across regions or private infrastructure.
* **Secure data processing** – Encryption in transit and at rest, differential privacy for sensitive biometric data, and complete auditability.
* **Analytics and observability** – Real-time metrics for verification accuracy, performance, and fraud anomaly detection.
* **Unified API gateway** – A single interface for all verification, compliance, and AI inference endpoints with rate limiting and fine-grained access control.
Dataspike combines the **portability of modern cloud architecture** with the **precision of advanced AI research**, giving developers and enterprises the freedom to deploy identity and recognition technologies anywhere — at global scale or at the edge.
***
## What can you build with Dataspike?
Dataspike’s modular APIs and AI engines power a wide range of digital identity, compliance, and security solutions — from traditional finance to next-generation Web3 ecosystems.
* **Digital onboarding flows** – Deliver seamless document and biometric verification for fintechs, exchanges, and marketplaces with built-in liveness detection and fraud prevention.
* **Automated compliance systems** – Implement continuous KYC, AML, and KYT screening pipelines to stay audit-ready and reduce manual review effort.
* **Web3 and crypto platforms** – Verify wallet owners, screen blockchain addresses, and assess transactional risk to support compliant DeFi, NFT, and exchange operations.
* **Fraud and risk intelligence tools** – Detect deepfakes, synthetic identities, and compromised devices in real time using Dataspike’s AI-driven risk models.
* **Cybersecurity and device trust** – Strengthen authentication, prevent account takeovers, and flag high-risk devices using fingerprinting and behavioral telemetry.
* **AI identity analytics** – Combine verification, behavioral, and transactional data to train adaptive risk models and automate trust decisions.
* **Edge and vision applications** – Deploy Dataspike’s face recognition and deepfake detection engines in physical environments such as stadiums, airports, and retail venues for crowd analytics and secure access.
Whatever your use case — fintech, enterprise, or Web3 — Dataspike provides the intelligence layer to **verify, monitor, and protect identities everywhere, at global scale.**
# Quickstart Guide
Source: https://docs.dataspike.io/home/quickstart
Get your Dataspike API keys, set up sandbox and production environments, test your first request, and start building with identity and compliance APIs.
## 1. Get your API keys
Create an account at [dash.dataspike.io](https://dash.dataspike.io).
All integrations in Dataspike require authenticated API access.\
You’ll manage your keys from the **[API](https://dash.dataspike.io/api).** tab in the dashboard, where you can switch between **Sandbox** and **Production** environments.
| Environment | Base URL | Description |
| - | - | - |
| **Sandbox** | `https://sandboxapi.dataspike.io` | Use for testing integrations and running simulated verifications without affecting production data. |
| **Production** | `https://api.dataspike.io` | Use for live verifications and real applicant data once your integration is ready. |
Each environment has its **own API key** — they are not interchangeable.\
When you switch environments in the dashboard, all internal entities (such as **Applicants**, **Verifications**, and **Screenings**) will update to reflect the selected context.
In the same **API** tab, you can also configure and test **webhook endpoints** for different event types (e.g. `verification.completed`, `aml.match_found`).\
The dashboard provides webhook delivery logs and replay options, so you can inspect request payloads and verify endpoint responses for each environment.
> The **API tab** lets you switch between Sandbox and Production environments, view keys, and configure webhooks.
***
## 2. Configure a webhook (optional)
Dataspike uses **webhooks** to send real-time event notifications when key actions occur — for example, when a document verification completes, an AML screening match is detected, or a KYT wallet check finishes.
To configure a webhook:
1. Go to **Dashboard → API → Webhooks**.
2. Click **Add Endpoint** and enter your listener URL, e.g. `https://api.yourdomain.com/dataspike/webhook`.
3. Select the event types you want to receive, such as:
* `Document Verification`
* `AML Screening`
* `KYT Wallet Check`
4. **Enable** the webhook to start receiving events.
5. Use the **Test** button to send a dummy event and confirm your endpoint is working successfully.
Webhook requests include an identifying header containing the webhook internal ID, which allows you to trace each delivery and verify that it originates from Dataspike.\
You can view delivery logs, inspect payloads, and replay events directly from the dashboard to test or troubleshoot your integration.
> The **Webhooks** panel lets you configure endpoints, enable or disable them, and test delivery using simulated events.
***
## 3. Next steps
Now that your API key and environment are ready, you can begin exploring Dataspike’s APIs.
Verify government-issued documents and selfies using the Verification API.
Perform AML and sanctions checks against global watchlists using the Screening API.
> 💡 **Tip:** You can perform both applicant-based and standalone checks — choose what fits your integration best.
***
Your environment is now configured and ready for integration.\
Next, explore the [API Reference](/api-reference) for endpoint details or continue with the guides above to build your first workflow.
# Sandbox Mode
Source: https://docs.dataspike.io/home/sandbox-mode
Use Dataspike’s free Sandbox environment to test integrations, webhooks, and workflows safely before going live.
Dataspike provides a dedicated **Sandbox environment** that lets you build and test integrations safely — without using real data or triggering live verifications.\
Every API endpoint supports sandbox mode and returns simulated responses designed to validate that your implementation works correctly.
***
## How it works
You can enable or switch to Sandbox directly from the **[API tab in the dashboard](https://dash.dataspike.io/api)**.\
Each environment (Sandbox and Production) has its own **API key** and corresponding **base URL**:
| Environment | Base URL | Description |
| - | - | - |
| **Sandbox** | `https://sandboxapi.dataspike.io` | Simulated environment for testing API calls and webhook flows. |
| **Production** | `https://api.dataspike.io` | Live environment for real applicant verifications. |
When Sandbox mode is active:
* All API responses contain **synthetic test data** that mimics production behavior.
* Webhooks are triggered as usual, allowing you to test event handling.
* No real applicants, documents, or identities are created.
***
## Webhooks in Sandbox
Webhooks can also be configured and tested while in Sandbox mode.\
Use the **Test** button in the dashboard to send a **dummy event** and verify that your endpoint is working correctly.\
You can review delivery logs, inspect payloads, and replay events for troubleshooting.
> ⚙️ Tip: Ensure your webhook is **enabled** in the dashboard — disabled endpoints will not receive events.
***
## Going live
Once your integration is verified in Sandbox:
1. Switch to the **Production** environment in the dashboard.
2. Replace the Sandbox base URL with the Production one.
3. Update your **API key** to the Production key.
All API calls and webhooks will then operate on real data.
***
## Key points
* ✅ Sandbox mode is **free and unlimited** — perfect for testing integrations.
* 🧩 All Dataspike APIs fully support Sandbox simulation.
* 🔄 Webhooks behave exactly as in production, with test payloads.
* 🚀 Switch to production anytime once your setup is verified.
> Sandbox mode helps you validate your integration safely before handling live verifications.
# Welcome to Dataspike
Source: https://docs.dataspike.io/index
Integrate Dataspike’s AML, KYC, and Verification APIs in minutes.
Create a free account on the Dataspike Dashboard.\
Once logged in, navigate to the **API** section at\
dash.dataspike.io/api to generate your **API key** for authentication.
Learn how to upload and validate ID documents and selfies for instant verification.
Set up AML checks against global watchlists, sanctions, and PEP databases with continuous monitoring.
Detect AI-generated or manipulated media using Dataspike’s machine learning models.
Embed Dataspike’s verification widget directly in your onboarding flow with minimal code.
Analyze crypto transactions and assess wallet risk with KYT monitoring.
***
## Support
Contact our support team for help with integration or compliance.
# Create a Verification
Source: https://docs.dataspike.io/kyb-verification/create-verification
Create a KYB case for a merchant and optionally pre-fill the widget form fields.
A KYB **case** is the container for one merchant's verification. You create it
with an `external_id` (your identifier for the merchant) and receive a
`public_id` to embed in the widget flow. Optionally, you can **pre-fill** form
fields you already know so the merchant does not re-enter them.
## Create a case
`POST /api/v4/kyb/cases` — authenticated with your API key. The backend creates
(or reuses) the business applicant bound to `external_id` and returns the
`public_id`.
```bash theme={null}
export API_KEY=
curl -H "ds-api-token: $API_KEY" \
-H "Content-Type: application/json" \
-X POST "https://api.dataspike.io/api/v4/kyb/cases" \
--data '{ "external_id": "merchant-123" }'
```
Response:
```json theme={null}
{
"id": "0195c1f0-1234-7000-8000-0123456789ab",
"public_id": "KYB0123456789abcdef",
"applicant_id": "0195c1f0-1234-7000-8000-0123456789ab",
"external_id": "merchant-123",
"status": "draft"
}
```
```python theme={null}
import requests
API_KEY = ""
BASE = "https://api.dataspike.io"
HEADERS = {"ds-api-token": API_KEY, "Content-Type": "application/json"}
resp = requests.post(
f"{BASE}/api/v4/kyb/cases",
headers=HEADERS,
json={"external_id": "merchant-123"},
)
public_id = resp.json()["public_id"]
```
If the merchant already has a case in `draft`,
that existing case is returned with **200** instead of creating a new one — so
the call is safe to retry.
## Pre-fill form fields
Pass an optional `fields` object to pre-populate the widget. It is grouped by
**section**, then by **field key**:
```
fields: { "": { "": } }
```
Keys must match the catalog. Unknown sections or fields are ignored, so a typo
never fails the request — it is simply skipped.
The example below pre-fills **one field from each section** to show the shape:
```bash theme={null}
curl -H "ds-api-token: $API_KEY" \
-H "Content-Type: application/json" \
-X POST "https://api.dataspike.io/api/v4/kyb/cases" \
--data '{
"external_id": "merchant-123",
"fields": {
"business": { "legal_name": "Acme Holdings Inc." },
"registration": { "registration_number": "16-1782204" },
"address_contact": { "reg_address_country": "US" },
"representative": { "rep_full_name": "Joe Marrano" },
"volume_banking": { "annual_volume": "$1,000,000 - $5,000,000" }
}
}'
```
```python theme={null}
resp = requests.post(
f"{BASE}/api/v4/kyb/cases",
headers=HEADERS,
json={
"external_id": "merchant-123",
"fields": {
"business": {"legal_name": "Acme Holdings Inc."},
"registration": {"registration_number": "16-1782204"},
"address_contact": {"reg_address_country": "US"},
"representative": {"rep_full_name": "Joe Marrano"},
"volume_banking": {"annual_volume": "$1,000,000 - $5,000,000"},
},
},
)
```
`fields` accepts **scalar values only** (string, number, boolean) — one value
per field key. Fields are flat within a section; there is no nested object.
* **Addresses** are flat sub-fields, not a nested object — e.g.
`reg_address_street`, `reg_address_city`, `reg_address_state`,
`reg_address_postal`, `reg_address_country`.
* **Beneficial owners** and **documents** are not part of `fields` — they are
managed through their own widget endpoints, not pre-fill.
### Field keys by section
The pre-fillable field keys, grouped by section.
| Section | Field keys |
| - | - |
| `business` | `legal_name`, `dba`, `entity_type`, `form_type` |
| `registration` | `registration_type`, `registration_type_other`, `registration_number`, `industry_type`, `mcc`, `incorporation_date`, `website` |
| `address_contact` | `reg_address_street`, `reg_address_city`, `reg_address_state`, `reg_address_postal`, `reg_address_country`, `operating_same_as_reg`, `op_address_street`, `op_address_city`, `op_address_state`, `op_address_postal`, `op_address_country` |
| `representative` | `rep_full_name`, `rep_dob`, `rep_job_title`, `rep_email`, `rep_mobile`, `rep_citizenship`, `rep_ssn`, `rep_address_street`, `rep_address_city`, `rep_address_state`, `rep_address_postal`, `rep_address_country` |
| `volume_banking` | `annual_volume`, `avg_transaction_volume`, `highest_transaction_value`, `bank_name`, `account_number`, `routing_number` |
# Document Verification
Source: https://docs.dataspike.io/kyb-verification/document-verification
Verify corporate documents: company formation, ownership, tax, bank and PCI compliance documents.
Verify corporate documents across several categories: company formation and ownership, tax verification, bank verification, and PCI DSS compliance. The API checks that each document matches its declared type and extracts the relevant information; for ownership documents it also validates shareholder details against user-provided data.
## Supported document types
Each document type has two identifiers: the snake\_case value used as `document_type` when [uploading the file](#step-2-upload-a-document), and the PascalCase value used in the [verification request](#step-3-submit-verification-request).
### Company documents
| Verification type | Upload type | Description |
| - | - | - |
| `KybCertificateOfIncorporation` | `kyb_certificate_of_incorporation` | Certificate of incorporation, articles of association, state registration, or similar formation documents |
| `KybOwnershipDocument` | `kyb_ownership_document` | Shareholder certificate, ownership registry extract, or similar documents listing beneficial owners |
| `KybOperatingAgreement` | `kyb_operating_agreement` | LLC operating agreement or similar company governance agreement |
### Tax verification documents
| Verification type | Upload type | Description |
| - | - | - |
| `KybW9Form` | `kyb_w9_form` | IRS Form W-9 (Request for Taxpayer Identification Number and Certification) |
| `KybEinConfirmationLetter` | `kyb_ein_confirmation_letter` | IRS EIN confirmation letter (CP 575) or EIN verification letter (147C) |
| `KybW8BenForm` | `kyb_w8ben_form` | IRS Form W-8BEN — foreign status certificate for individuals |
| `KybW8BenEForm` | `kyb_w8ben_e_form` | IRS Form W-8BEN-E — foreign status certificate for entities |
| `KybItinAssignmentLetter` | `kyb_itin_assignment_letter` | IRS ITIN assignment letter (CP 565) |
| `KybStateTaxRegistrationCertificate` | `kyb_state_tax_registration_certificate` | State tax registration certificate / sales tax permit |
| `KybFederalTaxReturn` | `kyb_federal_tax_return` | US federal business income tax return (e.g. Form 1120, 1065) |
### Bank verification documents
| Verification type | Upload type | Description |
| - | - | - |
| `KybBankStatement` | `kyb_bank_statement` | Bank statement for a business account |
| `KybBankVerificationLetter` | `kyb_bank_verification_letter` | Bank verification / confirmation letter |
| `KybDirectDepositForm` | `kyb_direct_deposit_form` | Direct deposit authorization form |
| `KybAchForm` | `kyb_ach_form` | ACH authorization form |
| `KybBankReferenceLetter` | `kyb_bank_reference_letter` | Bank reference letter |
### PCI verification documents
| Verification type | Upload type | Description |
| - | - | - |
| `KybSaqA` | `kyb_saq_a` | PCI DSS Self-Assessment Questionnaire A |
| `KybSaqAEp` | `kyb_saq_a_ep` | PCI DSS Self-Assessment Questionnaire A-EP |
| `KybSaqD` | `kyb_saq_d` | PCI DSS Self-Assessment Questionnaire D |
## Step 1: Create an applicant
Create an applicant to associate the verification with.
```bash theme={null}
export API_KEY=
curl -H "ds-api-token: $API_KEY" \
-H "Content-Type: application/json" \
-X POST "https://api.dataspike.io/api/v3/applicants" \
--data '{"external_id": "company-123"}'
```
Response:
```json theme={null}
{
"id": "01827ed4-c928-7a3c-9a30-7ab7cc169d11"
}
```
```python theme={null}
import requests
API_KEY = ""
BASE = "https://api.dataspike.io"
HEADERS = {"ds-api-token": API_KEY, "Content-Type": "application/json"}
resp = requests.post(f"{BASE}/api/v3/applicants", headers=HEADERS, json={"external_id": "company-123"})
applicant_id = resp.json()["id"]
```
## Step 2: Upload a document
Upload the corporate document using the file upload endpoint. Note the returned `document_id` — you will pass it to the verification request.
```bash theme={null}
APPLICANT_ID="01827ed4-c928-7a3c-9a30-7ab7cc169d11"
curl -H "ds-api-token: $API_KEY" \
-X POST "https://api.dataspike.io/api/v3/upload/$APPLICANT_ID" \
-F "file=@certificate_of_incorporation.pdf" \
-F "document_type=kyb_certificate_of_incorporation"
```
Response:
```json theme={null}
{
"document_id": "019c9c39-0b8a-7aa4-a5c0-abc123def456"
}
```
```python theme={null}
with open("certificate_of_incorporation.pdf", "rb") as f:
resp = requests.post(
f"{BASE}/api/v3/upload/{applicant_id}",
headers={"ds-api-token": API_KEY},
files={"file": f},
data={"document_type": "kyb_certificate_of_incorporation"},
)
document_id = resp.json()["document_id"]
```
## Step 3: Submit verification request
Submit the document for KYB verification. Optionally provide shareholder data to validate against extracted information from the document.
```bash theme={null}
curl -H "ds-api-token: $API_KEY" \
-H "Content-Type: application/json" \
-X POST "https://api.dataspike.io/api/v4/kyb/$APPLICANT_ID/verify-document" \
--data '{
"documents": [
{
"doc_path": "019c9c39-0b8a-7aa4-a5c0-abc123def456",
"document_type": "KybCertificateOfIncorporation"
}
],
"user_provided_data": {
"shareholders": [
{
"name": "John Doe",
"first_name": "John",
"last_name": "Doe",
"nationality": "US"
}
]
}
}'
```
Response (HTTP 202):
```json theme={null}
{
"request_id": "019c9e79-8f48-73e1-86e3-317a95f43178",
"status": "initial"
}
```
```python theme={null}
resp = requests.post(
f"{BASE}/api/v4/kyb/{applicant_id}/verify-document",
headers=HEADERS,
json={
"documents": [
{"doc_path": document_id, "document_type": "KybCertificateOfIncorporation"}
],
"user_provided_data": {
"shareholders": [
{"name": "John Doe", "first_name": "John", "last_name": "Doe", "nationality": "US"}
]
},
},
)
request_id = resp.json()["request_id"]
```
### Request body
| Field | Type | Required | Description |
| - | - | - | - |
| `documents` | array | Yes | List of documents to verify |
| `documents[].doc_path` | string | Yes | Document ID returned from the upload endpoint |
| `documents[].document_type` | string | Yes | A PascalCase verification type from [Supported document types](#supported-document-types) (e.g. `KybCertificateOfIncorporation`, `KybW9Form`) |
| `user_provided_data` | object | Conditional | Shareholder data to validate against the document. Required when the request includes `KybCertificateOfIncorporation` or `KybOwnershipDocument`; not used for other document types |
| `user_provided_data.shareholders` | array | Conditional | List of shareholders to verify |
| `user_provided_data.shareholders[].name` | string | No | Full name |
| `user_provided_data.shareholders[].first_name` | string | No | First name |
| `user_provided_data.shareholders[].last_name` | string | No | Last name |
| `user_provided_data.shareholders[].nationality` | string | No | Country code (e.g. `US`) |
## Step 4: Poll for the result
The verification takes approximately 15-30 seconds. Poll the result endpoint until the status changes from `initial`.
```bash theme={null}
REQUEST_ID="019c9e79-8f48-73e1-86e3-317a95f43178"
curl -H "ds-api-token: $API_KEY" \
"https://api.dataspike.io/api/v4/kyb/result/$REQUEST_ID"
```
Example response (verified):
```json theme={null}
{
"request_id": "019c9e79-8f48-73e1-86e3-317a95f43178",
"status": "verified",
"verification_type": "document_verification",
"response_data": {
"result": {
"status": "VERIFIED",
"document_results": [
{
"status": "VERIFIED",
"document": {
"document_type": "KybCertificateOfIncorporation"
},
"extracted_shareholders": [
{
"name": "John Doe",
"first_name": "John",
"last_name": "Doe",
"country": "US"
}
]
}
],
"extracted_company_info": {
"company_name": "Acme Corp.",
"jurisdiction": "US",
"address": "123 Main St, Dover, DE 19901"
}
}
},
"created_at": "2026-02-27T09:41:32.629118Z",
"completed_at": "2026-02-27T09:42:02.004789Z"
}
```
```python theme={null}
import time
while True:
resp = requests.get(f"{BASE}/api/v4/kyb/result/{request_id}", headers=HEADERS)
result = resp.json()
if result["status"] != "initial":
break
time.sleep(5)
print(result)
```
## Extracted document information
For the tax, bank, company and PCI document types, each entry in `response_data.result.document_results[]` carries a family-specific object with the fields extracted from the document. Empty fields are omitted from the response.
| Field | Document types | Example fields |
| - | - | - |
| `extracted_tax_info` | W-9, EIN letter, W-8BEN, W-8BEN-E, ITIN letter, state tax registration, federal tax return | `form_variant`, `business_name`, `tax_id`, `tax_id_type`, `tax_classification`, `address`, `tax_year`, `issue_date`, `signature_date`, `issuer`, `country`, `date_of_birth`, `fatca_status`, `giin` |
| `extracted_bank_info` | Bank statement, verification letter, direct deposit, ACH form, reference letter | `bank_name`, `account_holder_name`, `account_holder_address`, `account_number`, `routing_number`, `account_type`, `statement_period_start`, `statement_period_end`, `issue_date`, `closing_balance` |
| `extracted_company_doc_info` | Operating agreement | `company_name`, `jurisdiction`, `registration_number`, `company_type`, `effective_date`, `address`, `members` |
| `extracted_pci_info` | SAQ A, SAQ A-EP, SAQ D | `saq_type`, `merchant_name`, `assessment_date`, `signature_date`, `compliance_status`, `signatory_name`, `qsa_name` |
Example `document_results[]` entry for a verified W-9:
```json theme={null}
{
"status": "VERIFIED",
"document": {
"doc_path": "019c9c39-0b8a-7aa4-a5c0-abc123def456",
"document_type": "KybW9Form"
},
"extracted_tax_info": {
"form_variant": "W-9",
"business_name": "Acme Web Services LLC",
"tax_id": "82-1234567",
"tax_id_type": "EIN",
"tax_classification": "Limited liability company (C corporation)",
"address": "1201 Market St, Suite 400, Wilmington, DE 19801",
"signature_date": "2026-05-14"
}
}
```
## Verification errors
When a document verification returns `not_verified`, the `response_data.result.document_results[].errors` array contains one or more of the following error codes:
| Error code | Description |
| - | - |
| `SHAREHOLDER_NAME_NOT_FOUND` | A provided shareholder name was not found in the document |
| `SHAREHOLDER_NAME_MISMATCH` | A shareholder name was found but does not match the provided data |
| `SHAREHOLDER_NATIONALITY_MISMATCH` | Shareholder nationality does not match |
| `DOCUMENT_UNREADABLE` | The document could not be parsed or read |
| `DOCUMENT_TYPE_NOT_SUPPORTED` | The document type is not supported |
| `DOCUMENTS_FROM_DIFFERENT_ENTITIES` | Multiple documents appear to belong to different companies |
| `IRRELEVANT_DOCUMENT_PROVIDED` | The provided document does not match the declared document type or is not relevant for KYB verification |
# Overview
Source: https://docs.dataspike.io/kyb-verification/overview
Verify company identity and legitimacy through document analysis and public registry checks.
Know Your Business (KYB) verification allows you to verify company identity, ownership structure, and legal standing through document analysis and public registry checks.
## Verification methods
Dataspike KYB API provides two verification methods:
* **Document Verification** — upload and verify corporate documents to extract and validate company information. Supported categories: company formation and ownership (certificates of incorporation, shareholder certificates, operating agreements), tax verification (W-9, EIN letters, W-8BEN/W-8BEN-E, ITIN letters, state tax registration, federal tax returns), bank verification (statements, verification and reference letters, direct deposit and ACH forms), and PCI DSS compliance (SAQ A, A-EP, D).
* **US Company Registry Check** — look up a company in the US Secretary of State registry by name or SOS ID to verify its registration status, registered agent, and formation details.
## How it works
All KYB operations are **asynchronous**. When you submit a verification request, the API immediately returns a `request_id` with status `initial`. You then poll for the result using the request ID.
```
1. POST /api/v4/kyb/:applicant_id/verify-document → { request_id, status: "initial" }
POST /api/v4/kyb/:applicant_id/us-registry-check → { request_id, status: "initial" }
2. GET /api/v4/kyb/result/:request_id → { request_id, status: "verified", response_data: {...} }
```
## Verification statuses
| Status | Description |
| - | - |
| `initial` | Request accepted, verification in progress |
| `verified` | Verification completed successfully |
| `not_verified` | Verification completed, but checks did not pass |
| `failed` | Verification failed due to an error |
## Prerequisites
* A Dataspike API key (see [Getting Started](/home/quickstart))
* An applicant created via [POST /api/v3/applicants](/api-reference#tag/Applicants/operation/create-applicant)
## Guides
* [Document Verification](/kyb-verification/document-verification) — verify corporate documents and extract company/shareholder information
* [US Company Registry Check](/kyb-verification/us-registry-check) — look up companies in US state registries
# TIN Verification
Source: https://docs.dataspike.io/kyb-verification/tin-verification
Verify that a business name and TIN/EIN matches IRS records.
Verify that a company's legal name and Taxpayer Identification Number (EIN, or SSN for sole proprietors) match IRS records via IRS TIN matching.
## Step 1: Create an applicant
Create an applicant to associate the verification with.
```bash theme={null}
export API_KEY=
curl -H "ds-api-token: $API_KEY" \
-H "Content-Type: application/json" \
-X POST "https://api.dataspike.io/api/v3/applicants" \
--data '{"external_id": "company-456"}'
```
Response:
```json theme={null}
{
"id": "01827ed4-c928-7a3c-9a30-7ab7cc169d11"
}
```
```python theme={null}
import requests
API_KEY = ""
BASE = "https://api.dataspike.io"
HEADERS = {"ds-api-token": API_KEY, "Content-Type": "application/json"}
resp = requests.post(f"{BASE}/api/v3/applicants", headers=HEADERS, json={"external_id": "company-456"})
applicant_id = resp.json()["id"]
```
## Step 2: Submit TIN verification request
Provide the TIN and the legal business name. The TIN may contain dashes (they are ignored); it must contain 9 digits.
```bash theme={null}
APPLICANT_ID="01827ed4-c928-7a3c-9a30-7ab7cc169d11"
curl -H "ds-api-token: $API_KEY" \
-H "Content-Type: application/json" \
-X POST "https://api.dataspike.io/api/v4/kyb/$APPLICANT_ID/tin-verification" \
--data '{
"tin": "36-2382580",
"company_name": "DEERE & COMPANY"
}'
```
Response (HTTP 202):
```json theme={null}
{
"request_id": "019c9e79-4d64-73e1-9f30-5374f0f57975",
"status": "initial"
}
```
```python theme={null}
resp = requests.post(
f"{BASE}/api/v4/kyb/{applicant_id}/tin-verification",
headers=HEADERS,
json={"tin": "36-2382580", "company_name": "DEERE & COMPANY"},
)
request_id = resp.json()["request_id"]
```
### Request body
| Field | Type | Required | Description |
| - | - | - | - |
| `tin` | string | Yes | Taxpayer Identification Number (EIN or SSN). Dashes are ignored; must contain 9 digits. |
| `company_name` | string | Yes | Legal business name to match against the TIN. |
## Step 3: Poll for the result
The check typically completes within a few seconds. Poll the result endpoint until the status changes from `initial`.
```bash theme={null}
REQUEST_ID="019c9e79-4d64-73e1-9f30-5374f0f57975"
curl -H "ds-api-token: $API_KEY" \
"https://api.dataspike.io/api/v4/kyb/result/$REQUEST_ID"
```
Example response (verified):
```json theme={null}
{
"request_id": "019c9e79-4d64-73e1-9f30-5374f0f57975",
"status": "verified",
"verification_type": "tin_verification",
"response_data": {
"result": {
"name": "DEERE & COMPANY",
"tin": "362382580",
"status": "TIN Matched",
"irs_code": 7,
"irs_reason": "TIN and Name combination matches IRS EIN records"
}
},
"created_at": "2026-02-27T09:41:15.822681Z",
"completed_at": "2026-02-27T09:41:18.583502Z"
}
```
```python theme={null}
import time
while True:
resp = requests.get(f"{BASE}/api/v4/kyb/result/{request_id}", headers=HEADERS)
result = resp.json()
if result["status"] != "initial":
break
time.sleep(2)
print(result)
```
## Response data
The `response_data.result` object contains the **full** IRS TIN-matching outcome. Fields the IRS does
not return are omitted. The complete schema is in the
[API reference](/api-reference#tag/KYB-Single-Checks/operation/kyb-get-result).
| Field | Description |
| - | - |
| `name` | Business name that was submitted |
| `tin` | TIN that was submitted (digits only) |
| `status` | Match outcome, e.g. `TIN Matched` or `Did Not Match` |
| `irs_code` | IRS TIN-matching code (`6` = SSN, `7` = EIN, `8` = both match; `3` = no match) |
| `irs_reason` | Human-readable explanation of the IRS code |
| `irs_service_status` | IRS service status, when provided |
| `last_irs_check_date` | Date of the last IRS check, when provided |
## Result statuses
| Status | Meaning |
| - | - |
| `verified` | The name and TIN match IRS records |
| `not_verified` | The name and TIN do not match |
| `failed` | The TIN could not be verified (invalid TIN or an error occurred) |
# US Company Registry Check
Source: https://docs.dataspike.io/kyb-verification/us-registry-check
Verify that a company is registered with a US state's Secretary of State office.
Verify that a company is registered with a US state's Secretary of State office. The check returns registration details including company name, type, registration date, registered agent, and (depending on the state) officer information.
## Step 1: Create an applicant
Create an applicant to associate the verification with.
```bash theme={null}
export API_KEY=
curl -H "ds-api-token: $API_KEY" \
-H "Content-Type: application/json" \
-X POST "https://api.dataspike.io/api/v3/applicants" \
--data '{"external_id": "company-456"}'
```
Response:
```json theme={null}
{
"id": "01827ed4-c928-7a3c-9a30-7ab7cc169d11"
}
```
```python theme={null}
import requests
API_KEY = ""
BASE = "https://api.dataspike.io"
HEADERS = {"ds-api-token": API_KEY, "Content-Type": "application/json"}
resp = requests.post(f"{BASE}/api/v3/applicants", headers=HEADERS, json={"external_id": "company-456"})
applicant_id = resp.json()["id"]
```
## Step 2: Submit registry check request
Provide the company name and state. You can also search by SOS ID (Secretary of State filing number) if you have it.
```bash theme={null}
APPLICANT_ID="01827ed4-c928-7a3c-9a30-7ab7cc169d11"
curl -H "ds-api-token: $API_KEY" \
-H "Content-Type: application/json" \
-X POST "https://api.dataspike.io/api/v4/kyb/$APPLICANT_ID/us-registry-check" \
--data '{
"company_name": "Acme Holdings Inc.",
"state": "delaware"
}'
```
Response (HTTP 202):
```json theme={null}
{
"request_id": "019c9e79-4d64-73e1-9f30-5374f0f57975",
"status": "initial"
}
```
```python theme={null}
resp = requests.post(
f"{BASE}/api/v4/kyb/{applicant_id}/us-registry-check",
headers=HEADERS,
json={"company_name": "Acme Holdings Inc.", "state": "delaware"},
)
request_id = resp.json()["request_id"]
```
### Request body
| Field | Type | Required | Description |
| - | - | - | - |
| `company_name` | string | Conditional | Company name to search for. Required if `sos_id` is not provided. |
| `state` | string | Yes | US state name (e.g. `delaware`, `california`, `new york`) |
| `sos_id` | string | Conditional | Secretary of State filing number. Required if `company_name` is not provided. |
## Step 3: Poll for the result
The registry check typically takes 10-20 seconds. Poll the result endpoint until the status changes from `initial`.
```bash theme={null}
REQUEST_ID="019c9e79-4d64-73e1-9f30-5374f0f57975"
curl -H "ds-api-token: $API_KEY" \
"https://api.dataspike.io/api/v4/kyb/result/$REQUEST_ID"
```
Example response (verified). The result mirrors the **full field set** the state registry provides;
fields a given state does not return are omitted.
```json theme={null}
{
"request_id": "019c9e79-4d64-73e1-9f30-5374f0f57975",
"status": "verified",
"verification_type": "us_company_registry_check",
"response_data": {
"results": [
{
"base_info": {
"company_name": "ACME HOLDINGS INC.",
"registration_number": "7654321",
"status": "Good Standing",
"company_type": "Corporation",
"registration_date": "3/15/2022",
"jurisdiction": "DE",
"registered_address": {
"street": "850 NEW BURTON ROAD SUITE 201",
"city": "DOVER",
"state_or_province": "DE",
"postal_code": "19904",
"country": "US"
},
"associated_persons": [
{
"name": "Jane Smith",
"role": "CEO",
"address": { "street": "1 Main St", "city": "Dover", "state_or_province": "DE", "postal_code": "19901", "country": "US" },
"phone": "+1-302-555-0100",
"email": "jane@acme.example",
"current": "true"
}
]
},
"sos_id": "7654321",
"state_of_formation": "DE",
"mailing_address": { "street": "PO Box 5", "city": "Dover", "state_or_province": "DE", "postal_code": "19903", "country": "US" },
"registered_agent": {
"name": "CORPORATION SERVICE COMPANY",
"address": { "street": "251 LITTLE FALLS DRIVE", "city": "WILMINGTON", "state_or_province": "DE", "postal_code": "19808", "country": "US" }
},
"normalized_status": "active",
"normalized_filing_date": "2022-03-15T00:00:00.000Z",
"entity_sub_type": "Domestic",
"phone_number": "+1-302-555-0148",
"email": "contact@acme.example",
"industry": "Holding companies",
"url": "https://icis.corp.delaware.gov/Ecorp/EntitySearch/...",
"confidence_level": 100,
"ai_confidence_level": 98.5,
"ein": "36-2382580",
"ar_standing": "Current",
"next_report_due_date": "2027-03-01",
"agent_is_commercial": true,
"agent_resigned": false,
"address_match": true,
"assumed_business_names": [
{ "title": "ACME CAPITAL", "status": "Active", "type": "DBA", "effective_date": "2023-01-10", "sos_id": "DBA-99", "joint_venture": false }
],
"documents": [
{ "name": "Certificate of Incorporation", "url": "https://.../doc/123", "date": "3/15/2022" }
],
"ucc_data": [
{
"file_number": "2023-1234567",
"type": "Original",
"filing_date": "2023-06-01",
"status": "Active",
"lapse_date": "2028-06-01",
"image_url": "https://.../ucc/123",
"debtors": [ { "name": "ACME HOLDINGS INC.", "street": "1 Main St", "city": "Dover", "state": "DE", "zip": "19904" } ],
"secured_parties": [ { "name": "BIG BANK NA", "street": "100 Bank St", "city": "New York", "state": "NY", "zip": "10001" } ]
}
],
"history": [
{
"name": "Amendment",
"date": "2024-02-01",
"changes": [
{ "name": "Registered Agent", "action": "Modified", "changed_from": "OLD AGENT LLC", "changed_to": "CORPORATION SERVICE COMPANY" }
]
}
],
"full_data_count": { "officers_count": 1, "documents_count": 1, "ucc_data_count": 1, "assumed_business_names_count": 1, "history_count": 1 }
}
]
},
"created_at": "2026-02-27T09:41:15.822681Z",
"completed_at": "2026-02-27T09:41:30.583502Z"
}
```
```python theme={null}
import time
while True:
resp = requests.get(f"{BASE}/api/v4/kyb/result/{request_id}", headers=HEADERS)
result = resp.json()
if result["status"] != "initial":
break
time.sleep(5)
print(result)
```
## Response data
When the check returns `verified`, `response_data.results` is an array of matching company records.
Each record exposes the **complete set of fields** the state registry makes available — any field a
given state does not return is omitted. The full schema is in the
[API reference](/api-reference#tag/KYB-Single-Checks/operation/kyb-get-result); the tables below group
the fields by purpose.
### Core company info (`base_info`)
| Field | Description |
| - | - |
| `base_info.company_name` | Registered company name |
| `base_info.registration_number` | State filing number |
| `base_info.status` | Status as reported by the state (free text, e.g. `Good Standing`) |
| `base_info.company_type` | Entity type (Corporation, LLC, …) |
| `base_info.registration_date` | Date of formation/registration |
| `base_info.jurisdiction` | State of registration (2-letter code) |
| `base_info.registered_address` | Physical address (object: `street`, `city`, `state_or_province`, `postal_code`, `country`) |
| `base_info.associated_persons` | Officers/directors/members (array; see below). Availability varies by state |
### Identifiers & classification
| Field | Description |
| - | - |
| `sos_id` | Secretary of State filing number |
| `state_of_formation` | State where the company was originally formed |
| `ein` | Employer Identification Number (federal tax ID), when available |
| `entity_sub_type` | Domestic / Foreign / Nonprofit |
| `industry` | Industry classification |
| `url` | Link to the source record on the state website |
| `tax_payer_number` | State taxpayer number (Texas) |
| `ubi_number` | Unified Business Identifier (Washington) |
| `tax_amount_due` | Tax amount due (Delaware) |
| `registrant_name`, `registrant_url` | Registrant name / link (Colorado) |
### Status & dates
| Field | Description |
| - | - |
| `status_reason` | Reason for the status, when the state provides one |
| `normalized_status` | Standardized status (`active` / `inactive`) across states |
| `normalized_filing_date` | Filing date in ISO 8601 |
| `good_standing` | Good-standing indicator (Maryland) |
| `ar_standing` | Annual-report filing standing |
| `next_report_due_date` | Date the next annual report is due |
| `inactive_date` | Date the entity became inactive (may be a date or e.g. `Perpetual`) |
### Addresses & agents
| Field | Description |
| - | - |
| `mailing_address` | Mailing address (address object) |
| `state_address` | In-state physical address (address object) |
| `registered_agent` | Registered agent (`name` + `address`) |
| `service_of_process_agent` | Service-of-process agent (`name` + `address`; e.g. New York) |
| `agent_is_commercial` | Whether the registered agent is a commercial service |
| `agent_resigned`, `agent_resigned_date` | Whether/when the agent resigned |
### Match metadata
| Field | Description |
| - | - |
| `confidence_level` | Levenshtein-based match confidence (query vs found name) |
| `ai_confidence_level` | AI-based match-confidence score (beta) |
| `person_name_match` | Person name from the query that matched an officer or agent |
| `address_match` | Whether the address matched the one provided in the query |
| `search_result_title` | Name on the search results when it differs from the legal name (e.g. a DBA) |
| `messages` | Array of provider notes about anything unexpected |
| `screenshot_url`, `screenshot_succeeded` | Source-record screenshot URL / capture flag (when a screenshot is requested) |
| `ucc_succeeded` | Whether UCC data was retrieved (when UCC data is requested) |
### Collections
| Field | Item shape |
| - | - |
| `associated_persons[]` | `name`, `role`, `address`, `phone`, `email`, `current` |
| `assumed_business_names[]` | DBAs: `title`, `status`, `type`, `effective_date`, `expiration_date`, `sos_id`, `joint_venture` |
| `documents[]` | On-file documents: `name`, `url`, `date`, `comment` |
| `ucc_data[]` | UCC filings: `file_number`, `type`, `filing_date`, `status`, `lapse_date`, `image_url`, `debtors[]`, `secured_parties[]` (each party: `name`, `street`, `city`, `state`, `zip`) |
| `history[]` | Amendment events: `name`, `date`, `changes[]` (each change: `name`, `action`, `changed_from`, `changed_to`, `title`) |
| `full_data_count` | Counts: `officers_count`, `documents_count`, `ucc_data_count`, `assumed_business_names_count`, `history_count` |
The available data varies by state — some states return detailed officers, documents, and history,
while others return only basic registration details. `ucc_data` and `screenshot_url` are returned
only when UCC data / a screenshot are requested for the check.
## Result statuses
| Status | Meaning |
| - | - |
| `verified` | Company found in the state registry |
| `not_verified` | No matching company found |
| `failed` | An error occurred during the check |
# Overview
Source: https://docs.dataspike.io/mcp-server/overview
Connect Claude, Cursor, and other AI agents to the Dataspike API over the Model Context Protocol — AML screening, verifications, KYB, KYT, email checks, and deepfake detection.
The **Dataspike MCP Server** exposes the Dataspike API to AI agents over the
[Model Context Protocol (MCP)](https://modelcontextprotocol.io). Once connected, an LLM host —
Claude Code, Cursor, and other MCP-capable clients — can run compliance tasks for
you in natural language, without writing any integration code.
Through the server an agent can:
* **AML screening** — screen people and organizations against sanctions, PEP, and watchlists, and pull past results.
* **Applicants** — look up applicant profiles and their latest AML reports.
* **Verifications** — inspect KYC verification sessions and their event timelines.
* **KYB** — poll business-verification results.
* **KYT** — retrieve crypto-wallet check reports.
* **Email verification** — fetch email-check results.
* **Deepfake detection** — run authenticity analysis on selfie and ID-document images.
See the full list on the [Tools](/mcp-server/tools) page.
***
## How it works
An MCP host connects to the server over HTTP and discovers the **tools** it exposes. The model
decides which tool to call, and with what arguments, based on your request — you just describe the
task. Results come back as structured data the agent can reason over and summarize.
The managed server is available at:
```
https://mcp.dataspike.io/mcp
```
***
## Authentication
The server authenticates to Dataspike with your API key, passed once in the client configuration as
a bearer token:
```
Authorization: Bearer
```
You configure this in your MCP client settings (see below) — there's no need to include the key in
your messages. Generate an API key in the [Dataspike Dashboard](https://dash.dataspike.io/api).
One tool, `list_aml_sources`, queries a public catalog endpoint and does not require an API key.
Every other tool calls the authenticated Dataspike API.
***
## Connect your client
Claude Desktop connects over OAuth as a **custom connector** — no manual header; you authorize once
with your Dataspike API key. (Custom connectors are in beta — Free, Pro, Max, Team, and Enterprise.)
In Claude Desktop, go to **Customize → Connectors**.
Click **+**, then **Add custom connector**, and enter the URL:
```
https://mcp.dataspike.io/mcp
```
Leave **Advanced settings** empty — the server registers your client automatically. Click **Add**.
Click **Connect**. A Dataspike sign-in page opens — paste your
[Dataspike API key](https://dash.dataspike.io/api) and authorize. The connector shows as
connected and the Dataspike tools become available in chat.
On **Team / Enterprise**, an owner first adds the connector under **Organization settings →
Connectors → Add → Custom → Web**; members then **Connect** it from **Customize → Connectors**.
Add to `.claude/settings.json` in your project root:
```json theme={null}
{
"mcpServers": {
"dataspike": {
"type": "http",
"url": "https://mcp.dataspike.io/mcp",
"headers": {
"Authorization": "Bearer "
}
}
}
}
```
Open **Settings → MCP → Add server** and configure:
* **URL:** `https://mcp.dataspike.io/mcp`
* **Header:** `Authorization: Bearer `
Edit `~/.codex/config.toml` (or `.codex/config.toml` in a trusted project). Codex reads the
bearer token from an environment variable and sends it as `Authorization: Bearer `:
```toml theme={null}
[mcp_servers.dataspike]
url = "https://mcp.dataspike.io/mcp"
bearer_token_env_var = "DATASPIKE_API_KEY"
```
Then export your key before launching Codex:
```bash theme={null}
export DATASPIKE_API_KEY=""
```
***
## Example prompt
Once connected, just ask in plain language:
> Check John Smith for AML, country United Kingdom
The agent picks the right tool (`search_aml`), fills in the arguments, and reports the risk level
and matches.
***
## Next steps
Browse every tool the server exposes, grouped by domain, with parameters.
The underlying Dataspike endpoints, request/response schemas, and parameters.
# Tools
Source: https://docs.dataspike.io/mcp-server/tools
Every tool the Dataspike MCP Server exposes — AML screening, applicants, verifications, KYB, KYT, email checks, and deepfake detection — grouped by domain.
The tools below are what an MCP client discovers after [connecting](/mcp-server/overview) to the
server. The model calls them on your behalf; you rarely need to name a tool explicitly. Each group
links to the matching section of the [API Reference](/api-reference) for full request and response
schemas.
All tools call the authenticated Dataspike API and require your API key, except `list_aml_sources`,
which is anonymous.
***
## AML Screening
Screen individuals and organizations against sanctions, PEP, and watchlists. See [AML Screening in the API Reference](/api#tag/AML-Screening).
| Tool | Description |
| - | - |
| `search_aml` | Screen a person or organization against sanctions, PEP, and watchlists. Returns a risk level and matches. |
| `get_aml_history` | Retrieve a previous AML search result by its UUID. |
| `list_aml_sources` | List the AML datasources Dataspike covers (anonymous — answers *which* lists are covered, not whether a person is on them). |
***
## Applicants
Look up applicant profiles and their AML reports. See [Applicants in the API Reference](/api#tag/Applicants).
| Tool | Description |
| - | - |
| `get_applicant` | Retrieve an applicant profile by Dataspike UUID. |
| `get_applicant_by_external_id` | Retrieve an applicant by your own external identifier. |
| `get_applicant_aml_report` | Latest AML screening report for an applicant. |
| `list_applicants` | List applicant profiles (paginated). |
***
## Verifications
Inspect KYC verification sessions and their timelines. See [Verifications in the API Reference](/api#tag/Verifications).
| Tool | Description |
| - | - |
| `get_verification` | Retrieve a verification (KYC session) by UUID. |
| `get_verification_by_short_id` | Retrieve a verification by its short/public id. |
| `get_applicant_verifications` | List all verifications for an applicant. |
| `get_verification_tracking_events` | Timeline of events for a verification. |
| `list_verifications` | List verifications (paginated, filterable). |
***
## KYB (Business Verification)
See [KYB Verification in the API Reference](/api#tag/KYB-Verification).
| Tool | Description |
| - | - |
| `get_kyb_verification` | Poll a KYB verification result by UUID. |
***
## KYT (Crypto)
See [KYT Crypto in the API Reference](/api#tag/KYT-Crypto).
| Tool | Description |
| - | - |
| `get_crypto_report` | Retrieve a KYT crypto-wallet check report by UUID. |
***
## Email Verification
See [Email Verification in the API Reference](/api#tag/Email-Verification).
| Tool | Description |
| - | - |
| `get_email_check` | Retrieve a single email-verification check by UUID. |
| `list_email_checks` | List email checks (paginated, filterable). |
***
## Deepfake Detection
Run authenticity analysis on images. See [Deepfake API in the API Reference](/api#tag/Deepfake-API).
| Tool | Description |
| - | - |
| `analyze_image` | Run deepfake detection on a selfie/portrait (async job). |
| `analyze_document_image` | Run deepfake detection on an ID-document image (async job). |
| `get_deepfake_job` | Poll a deepfake-analysis job by UUID. |
Image analysis is image-only. Images reach the server through a drag-and-drop upload widget,
available in clients with MCP Apps UI support (Claude Desktop, claude.ai); the `analyze_*` tool is
then called with the uploaded file. `analyze_*` starts an async job — poll `get_deepfake_job` for
the result.
# Integration Guide
Source: https://docs.dataspike.io/mobile-sdk/integration_guide
Integrate Dataspike identity verification into your iOS, Android, and Flutter applications.
## Overview
The Dataspike Mobile SDK enables you to embed a full identity verification flow directly into your mobile application. It is built on an embedded Flutter module and supports native iOS, Android, and Flutter integrations.
| Platform | Minimum Version |
| - | - |
| Android | SDK 21+ |
| iOS | 16.0+ |
Native Swift integration via CocoaPods
Native Kotlin integration via Gradle
Direct Dart package integration
## Prerequisites
Before integrating the SDK, complete these steps:
Install Flutter on your development machine using the [official documentation](https://docs.flutter.dev/install).
For Android, you can alternatively follow the [AAR-based setup](https://docs.flutter.dev/add-to-app/android/project-setup#depend-on-the-android-archive-aar).
Create an account and obtain your API token from the [Dataspike Dashboard](https://dash.dataspike.io/api/settings).
Use the [Create Verification](/api-reference/verifications/create-new-verification) endpoint to generate a `verification_url_id` (also referred to as `shortId`).
***
## iOS Integration
**Example project:** [Sample iOS Project Repository](https://github.com/dataspike-io/MobileSDK-Flutter-iOS)
### 1. Add the Flutter module
From your iOS project root, add the SDK as a Git submodule:
```bash theme={null}
mkdir -p DataspikeModule
git submodule add -b main https://github.com/dataspike-io/MobileSDK-Flutter.git DataspikeModule/dataspike_module
git submodule update --init --recursive
cd DataspikeModule/dataspike_module/dataspike_module
flutter pub get
cd -
```
### 2. Configure CocoaPods
Update your `Podfile` following the [example Podfile](https://github.com/dataspike-io/MobileSDK-Flutter-iOS/blob/master/Podfile) or the [Flutter Podfile guide](https://docs.flutter.dev/add-to-app/ios/project-setup#update-your-podfile).
The following permission flags are **required** in your `Podfile` build settings:
```ruby theme={null}
'PERMISSION_CAMERA=1',
'PERMISSION_PHOTOS=1',
```
Then install dependencies:
```bash theme={null}
pod install --repo-update
```
We recommend creating a **separate iOS target** dedicated to verification with its own scheme and Pod configuration. This prevents simulator build failures and keeps your main app target stable. See [Target Configuration](#target--pod-configuration) for details.
### 3. Configure Xcode
**Disable User Script Sandboxing:**
Open **Xcode → Target → Build Settings** and set **User Script Sandboxing** to **NO**.
**Add required permissions** to your `Info.plist`:
| Key | Required |
| - | - |
| `NSCameraUsageDescription` | Always |
| `NSPhotoLibraryUsageDescription` | Always |
| `NSMotionUsageDescription` | Always |
| `NSLocalNetworkUsageDescription` | Debug only |
| `NSBonjourServices` | Debug only |
For `NSBonjourServices`, add the values `_dartVmService._tcp` and `_googlecast._tcp`.
### 4. Start the verification flow
```swift theme={null}
channel.setMethodCallHandler { call, result in
if call.method == "onVerificationCompleted" {
if let args = call.arguments as? [String: String] {
if args["status"] == "Completed" {
flutterVC.dismiss(animated: true, completion: nil)
}
}
result(nil)
} else {
result(FlutterMethodNotImplemented)
}
}
present(flutterVC, animated: true) {
channel.invokeMethod("startDataspikeFlow", arguments: [
"dsApiToken": "your_token",
"shortId": "your_short_id",
"isDebug": true // set to false for production
])
}
```
For a complete implementation, see the [example ViewController](https://github.com/dataspike-io/MobileSDK-Flutter-iOS/blob/master/flutteriosexample/SomeViewController.swift) or the [Flutter engine guide](https://docs.flutter.dev/add-to-app/ios/add-flutter-screen#create-a-flutterengine).
### 5. Update the submodule
To update the SDK to a specific version:
```bash theme={null}
cd DataspikeModule/dataspike_module
git fetch
git checkout
cd -
git add DataspikeModule/dataspike_module
git commit -m "Update DataspikeModule"
```
***
## Android Integration
**Example project:** [Sample Android Project Repository](https://github.com/dataspike-io/MobileSDK-Flutter-Android)
### 1. Add the Flutter module
From your Android project root:
```bash theme={null}
mkdir -p DataspikeModule
git submodule add -b main https://github.com/dataspike-io/MobileSDK-Flutter.git DataspikeModule/dataspike_module
git submodule update --init --recursive
cd DataspikeModule/dataspike_module/dataspike_module
flutter pub get
cd -
```
### 2. Configure settings.gradle
Update your `settings.gradle.kts` to include the Flutter module:
```kotlin theme={null}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.PREFER_PROJECT)
repositories {
google()
mavenCentral()
}
}
rootProject.name = "YourProjectName"
include(":app")
val filePath = settingsDir.toString() +
"/DataspikeModule/dataspike_module/dataspike_module/.android/include_flutter.groovy"
apply(from = File(filePath))
```
See the [example settings.gradle](https://github.com/dataspike-io/MobileSDK-Flutter-Android/blob/master/settings.gradle.kts) or the [Flutter guide](https://docs.flutter.dev/add-to-app/android/project-setup#updating-settings-gradle).
### 3. Update build.gradle
Follow the [example build.gradle](https://github.com/dataspike-io/MobileSDK-Flutter-Android/blob/master/app/build.gradle.kts) or the [Flutter guide](https://docs.flutter.dev/add-to-app/android/project-setup#updating-appbuild-gradle).
### 4. Add permissions
Add these permissions to your `AndroidManifest.xml`:
```xml theme={null}
```
### 5. Start the verification flow
```kotlin theme={null}
channel.setMethodCallHandler { call, result ->
if (call.method == "onVerificationCompleted") {
val args = call.arguments as? Map<*, *>
val status = args?.get("status")
if (status == "Completed") {
// Handle completion
}
result.success(null)
} else {
result.notImplemented()
}
}
channel.invokeMethod(
"startDataspikeFlow",
mapOf(
"dsApiToken" to "your_token",
"shortId" to "your_short_id",
"isDebug" to true // set to false for production
)
)
```
For a complete implementation, see the [example Activity](https://github.com/dataspike-io/MobileSDK-Flutter-Android/blob/master/app/src/main/java/com/example/implementationflutterexample/MainActivity.kt) or the Flutter guides for [Flutter Screen](https://docs.flutter.dev/add-to-app/android/add-flutter-screen#add-a-normal-flutter-screen) and [Flutter Fragment](https://docs.flutter.dev/add-to-app/android/add-flutter-fragment#add-a-flutterfragment-to-an-activity-with-a-new-flutterengine).
Flutter supports AOT builds only for `armeabi-v7a`, `arm64-v8a`, and `x86_64`. If your app supports additional ABIs (e.g. `mips`, `x86`), you may encounter integration issues. See the [Flutter ABI documentation](https://docs.flutter.dev/deployment/android#android-app-bundles-and-apk-splitting).
***
## Flutter Integration
**Example projects:** [In-repo example](https://github.com/dataspike-io/MobileSDK-Flutter/blob/main/example/lib/main.dart) · [Standalone example](https://github.com/dataspike-io/MobileSDK-Flutter-App)
### 1. Add the dependency
Add the SDK to your `pubspec.yaml`:
```yaml theme={null}
dependencies:
flutter:
sdk: flutter
dataspikemobilesdk:
git:
url: https://github.com/dataspike-io/MobileSDK-Flutter
ref: 1.0.0
```
### 2. Configure platform permissions
Add to your `Info.plist`:
* `NSCameraUsageDescription`
* `NSPhotoLibraryUsageDescription`
* `NSMotionUsageDescription`
* `NSLocalNetworkUsageDescription` (Debug only)
Add to your `Podfile` build settings ([example](https://github.com/dataspike-io/MobileSDK-Flutter/blob/main/example/ios/Podfile)):
```ruby theme={null}
'PERMISSION_CAMERA=1',
'PERMISSION_PHOTOS=1',
```
Add to your `AndroidManifest.xml`:
```xml theme={null}
```
### 3. Start the verification flow
```dart theme={null}
import 'package:dataspikemobilesdk/dataspikemobilesdk.dart';
final _dataspikemobilesdkPlugin = Dataspikemobilesdk();
_dataspikemobilesdkPlugin.startDataspikeFlow(
context: context,
dependencies: .new(
isDebug: true, // set to false for production
dsApiToken: "your_token",
shortId: "your_short_id",
),
callback: (status) {
// Handle verification result
},
);
```
On Apple Silicon machines, Flutter-based integration may require running Xcode under Rosetta for proper testing and dependency compatibility.
***
## Verification Statuses
The `onVerificationCompleted` callback returns one of the following statuses:
| Status | Description |
| - | - |
| `Completed` | The verification flow finished successfully. |
| `Expired` | The verification session expired before completion. |
| `Failed` | The verification flow encountered an error. |
***
## Known Limitations
Verification is fully supported **only on real devices**. iOS simulators may not work correctly. Simulator support improvements are planned for future releases.
In Debug configuration, image loading during verification may be significantly slower. Always test the verification flow using the **Release** build configuration for accurate performance.
To prevent simulator build failures, create a **separate iOS target** for verification:
```ruby theme={null}
target 'MainApp' do
# Regular pods only
end
target 'MainAppVerification' do
pod 'DataSpikeMobileSDK'
end
```
This keeps device-only dependencies isolated from your main app target and CI pipelines.
Flutter supports AOT builds only for `armeabi-v7a`, `arm64-v8a`, and `x86_64`. Remove unsupported ABIs like `mips` or `x86` from your project configuration.
***
## Troubleshooting
If Flutter debugging doesn't work correctly, set up the LLDB init file following the [official guide](https://docs.flutter.dev/add-to-app/ios/project-setup#set-lldb-init-file).
You may also need to set the iOS platform version in the Flutter module podspec:
```ruby theme={null}
s.platform = :ios, '16.0'
```
Flutter tools require local network access for the Dart VM Service. Ensure the required permissions are added to your `Info.plist` and allowed in iOS system settings. See the [official documentation](https://docs.flutter.dev/add-to-app/ios/project-setup#set-local-network-privacy-permissions).
On Apple Silicon Macs, you may encounter simulator architecture issues. Follow the [official guide](https://docs.flutter.dev/add-to-app/ios/project-setup#mitigate-known-issue-with-apple-silicon-macs) to exclude `arm64` for iOS simulators or adjust CocoaPods build settings.
# Getting Started with KYT
Source: https://docs.dataspike.io/wallet-checks-kyt/getting-started
Run your first wallet or transaction check in the Dataspike dashboard, or integrate KYT into your product with a first API call.
You can use KYT in two ways: interactively in the **dashboard** (great for compliance teams and manual reviews) or through the **REST API** (for automated screening inside your product). Both run the same checks and produce the same reports.
***
## In the dashboard
Log in at `dash.dataspike.io`, click the grid icon next to the module name in the top-left corner, and select **KYT** from the dropdown. You land on the **Wallet Scoring** page — the main interface for running checks.
Select the **Network** (Ethereum, Bitcoin, or TRON) and the **Token** — leave it empty for the native asset (ETH/BTC/TRX) or choose USDT/USDC. Paste a **wallet address** or a **transaction TxID** into the input field and click **Scan**. Results are processed in seconds and saved to your history.
Every scan is saved to the **Recently verified wallets** table. Each row shows the wallet/TxID, date, check type, network, token, the risk score with a Low/Moderate/High badge, and **Flags** — the top risk categories detected. Open a row for the full breakdown, or see [How to read a KYT report](wallet-checks-kyt/how-to-read-a-kyt-report).
Click the download icon at the end of any row to save a full AML PDF report — including the risk score, category breakdown, and direct transfers list — for your audit trail.
***
## Your first API call
In `dash.dataspike.io`, go to **API → API Settings** and click **Generate**. Store the key securely.
Never expose the API key in frontend or mobile code. Always proxy KYT requests through your backend.
```bash theme={null}
curl -X PUT "https://api.dataspike.io/api/v4/kyt/checks/address" \
--header "ds-api-token: YOUR_API_KEY" \
--header "Content-Type: application/json" \
--data '{
"blockchain": "eth",
"address": "0x029Ec4db927f58cfBf25FA45AbBd0777d791e3bb",
"token": "",
"directs": false
}'
```
Every request needs the `ds-api-token` header and `Content-Type: application/json`. Use `btc`, `eth`, or `trx` for `blockchain`, and leave `token` empty for the native asset. Store the returned check `id` — you need it to fetch the result and the report.
```bash theme={null}
curl -X GET "https://api.dataspike.io/api/v4/kyt/checks/address/YOUR_CHECK_ID" \
--header "ds-api-token: YOUR_API_KEY"
```
Analysis typically completes in under 5 seconds. While it is still in progress, the API returns `404` with `"No data, please try again later"` — this is normal. Retry after 2–5 seconds with exponential backoff until `status` is `"success"`.
Branch your logic on `risk_score_grade`:
* `low` → auto-approve
* `moderate` → queue for manual review / enhanced due diligence
* `high` → block and escalate
See [Risk scoring](/kyt/risk-scoring) for what each grade means — and note that the raw `risk_score` is a **0–1 coefficient**, not a percentage.
```bash theme={null}
curl -X GET "https://api.dataspike.io/api/v4/kyt/checks/address/YOUR_CHECK_ID/report" \
--header "ds-api-token: YOUR_API_KEY"
```
Store the report with your customer or transaction record as evidence of the automated risk assessment.
***
## Recommended integration pattern
1. On user deposit or wallet connect → submit an address check (`PUT /api/v4/kyt/checks/address`).
2. Store the returned check `id` alongside the user or transaction record in your database.
3. Poll `GET /api/v4/kyt/checks/address/{id}` until `status = "success"`.
4. Branch on `risk_score_grade`: `low` → auto-approve, `moderate` → queue for review, `high` → block and alert.
5. Download and store the PDF report linked to the check `id` for your audit trail.
For screening individual deposits or withdrawals, use a transaction check; for isolating one sender → recipient flow (especially on Bitcoin), use a transfer check. See [How KYT works](/wallet-checks-kyt/how-kyt-works#the-three-check-types).
***
## Rate limits
The KYT API enforces a limit of **24 requests per second per API key**. Requests above the threshold receive `429 Too Many Requests` with a `Retry-After` header. Use exponential backoff starting at 1 second. For bulk screening, process in batches of 20 with a 1-second pause between batches. For very high volumes, contact `service@dataspike.io` to discuss increased limits.
***
## Common response codes
| Code | Meaning |
| - | - |
| `400` | Invalid parameters or missing required field. |
| `401` | Missing or invalid `ds-api-token` header. |
| `403` | Expired license or insufficient check balance. |
| `404` | Address/TX not found on the blockchain, check `id` does not exist, the address has no transaction history, **or the analysis is still in progress** (`"No data, please try again later"` — retry shortly). |
| `422` | Valid JSON but semantically incorrect values. |
| `429` | Rate limit exceeded; honor `Retry-After`. |
The full field mapping, response schemas, and status codes live in the [Result fields reference](/wallet-checks-kyt/result-fields-coverage).
# How KYT Works
Source: https://docs.dataspike.io/wallet-checks-kyt/how-kyt-works
The KYT analysis pipeline step by step, plus the core vocabulary: the three check types, direct and indirect exposure, and entity attribution.
Every KYT check runs the same pipeline, whether you start it from the dashboard or the API. The result is an explainable risk score backed by a category-level breakdown — not a black-box verdict. This page covers how that pipeline works, then the vocabulary you need to read a result.
***
## The pipeline
Enter a wallet address or a transaction TxID, select the blockchain network (Bitcoin, Ethereum, or TRON) and the token — or send the same parameters in an API request. The system detects the request type and starts the appropriate analysis.
The system extracts and aggregates data directly from the blockchain: transaction history, inbound and outbound fund flows, balances, volumes, fees, timing parameters, participants, and linked addresses. Only **public on-chain data** is analyzed — KYT does not process or store personal data. See [Data handling](/wallet-checks-kyt/working-with-results#data-handling).
Addresses in the fund flow are matched against Dataspike's entity database — exchanges, payment systems, DeFi protocols, mixers, darknet services, sanctioned addresses, hacked wallets, fraudulent projects, and more. Related addresses are automatically grouped into clusters and analyzed as a single entity, which improves attribution accuracy. Each identified entity carries a category from the [entity taxonomy](/wallet-checks-kyt/risk-scoring-entity-types#entity-types).
KYT analyzes **direct interactions** (one hop — entities that transacted directly with the address) and **indirect interactions** (n-hop — entities further along the fund trail). For each entity category, it computes the share of funds associated with it. This is how hidden connections to high-risk sources are surfaced even when there was no direct contact.
The exposure shares are combined into a weighted 0–100% **Risk Score**, with high-risk categories weighted more heavily, and classified as **Low**, **Moderate**, or **High**. The full methodology is described in [Risk scoring](/wallet-checks-kyt/risk-scoring-entity-types).
The dashboard shows the score, risk flags, the per-category breakdown, and the direct transfers list. The API returns the same data as JSON. And every check can be exported as an audit-ready AML PDF report.
### Speed and freshness
* Checks typically complete in **seconds** — most in under 5 seconds via the API.
* Dataspike's entity database and risk assessments are **updated continuously** as new transactions appear and entity classifications change. A wallet that scored Low last quarter may score differently today, so re-screen addresses periodically for customers with ongoing activity.
### What's next in the pipeline
Continuous **address monitoring**, **real-time alerts**, and **custom risk profiles** (your own entity weights, thresholds, and trigger rules) are coming soon — see [Feature availability](/wallet-checks-kyt/result-fields-coverage#feature-availability). Today, the pipeline above runs on demand: you submit a check and get a point-in-time assessment.
***
## The three check types
### Address check
Scans the **full history of a wallet**: all inbound and outbound funds, entity connections, and behavioral patterns. Returns a risk score, an entity-category breakdown, and a list of direct counterparties.
### Transaction check
Scores a **specific transaction by its TxID**. Analyzes the sender, the recipient, and the chain of funds behind the transaction.
### Transfer check
Isolates **one specific sender → recipient pair inside a transaction**. Unlike the transaction check (which analyzes the entire TX), the transfer check verifies one particular flow of funds. This matters on UTXO-based chains like Bitcoin, where a single transaction can have many senders and recipients, and in multi-party transactions generally.
The transfer check is currently **API only** — it is not yet exposed in the dashboard UI. Transfer results return an exposure breakdown for that specific flow; the top-level `risk_score` field is `null`, so there is no single verdict for one leg. See [Feature availability](/wallet-checks-kyt/result-fields-coverage#feature-availability).
### Which one do I run?
| Situation | Check | Why |
| - | - | - |
| A customer registers a withdrawal wallet | Address | You care about the wallet's whole history, not one payment. |
| An incoming deposit needs a decision now | Transaction | Scores the specific movement of funds you are about to credit. |
| A Bitcoin transaction has 29 outputs and one is yours | Transfer | Scores only the leg that reached your address. |
| Periodic re-screening of an existing customer | Address | Picks up new activity and newly classified counterparties. |
***
## Direct vs indirect exposure
* **Direct exposure (1 hop)** — entities that transacted directly with the address. These appear by name in the **Direct transfers** table, with amounts sent and received.
* **Indirect exposure (n-hop)** — entities further along the fund trail, reached through intermediate addresses. Indirect analysis is what reveals that funds passed through a mixer two hops before reaching the wallet you are screening.
By default, address checks analyze indirect payments; set `"directs": true` in the API to restrict the analysis to direct interactions only. That usually surfaces less exposure, so keep the parameter consistent when comparing two results for the same address.
***
## Entity attribution: Owner and Type
When Dataspike's database recognizes the entity behind an address, results show:
* **Owner** — the entity name (an exchange, protocol, or service).
* **Type** — its category from the [entity taxonomy](/wallet-checks-kyt/risk-scoring-entity-types#entity-types), e.g. *Licensed exchange service* or *Mixing service*.
In the API, attribution appears as `name`, `type` (machine-readable, e.g. `EXCHANGE_LICENSED`), and `type_label` (human-readable). **Undefined is normal** for individual, non-service wallets — it means the address is not attributed to any recognized entity, not that it is risky.
***
## Score and grade
Every address and transaction check produces a **score** (0–100% in the dashboard and PDF, a 0–1 coefficient in the API) and a **grade** (`low`, `moderate`, `high`, `undefined`). Use the grade as your decision gate.
The methodology is in [Risk scoring](/wallet-checks-kyt/risk-scoring-entity-types); every field including balances and volumes is documented in [Result fields](/wallet-checks-kyt/result-fields-coverage); reading them together on a real case is [How to read a KYT report](/wallet-checks-kyt/how-to-read-a-kyt-report).
# How to Read a KYT Report
Source: https://docs.dataspike.io/wallet-checks-kyt/how-to-read-a-kyt-report
A line-by-line walkthrough of a real Dataspike KYT result: what every field means, how the risk score is built, and what an analyst should do next.
This page walks through a **real KYT result, section by section**: what each field means, how the risk score was built from the numbers you see, and what an analyst is expected to do next.
**Written for:** compliance analysts, MLROs, and risk/ops teams reading results day to day. **Prerequisite:** one completed check ([Getting started](/wallet-checks-kyt/getting-started)). **Reading time:** \~10 minutes.
One check produces **the same data on three surfaces** — the dashboard result page, the downloadable AML PDF, and the API JSON response. Labels differ slightly; the meaning does not. The exact label-to-field mapping is in the [Result fields reference](/wallet-checks-kyt/result-fields-coverage).
***
## Triage in 30 seconds
If you only have half a minute per case, read the report in this order.
1. **Read the grade, not the number.** Low / Moderate / High / Undefined is the verdict. The percentage refines it.
2. **Check the High tier total.** Is it above zero? A **Low** grade with non-zero high-risk exposure still deserves a look — this is the single most common misread of a KYT report.
3. **Open the category breakdown.** *Which* high-risk categories are present? Sanctions, stolen assets, and darknet carry very different consequences than unlicensed gambling.
4. **Scan Direct transfers.** The named counterparties one hop away. This tells you who the address actually deals with, and whether that matches what your customer told you.
Then apply your policy — see the [decision matrix](#decision-matrix) below.
***
## The example used on this page
A real address check: **USDT on TRON**, indirect analysis enabled.
| Field | Value |
| - | - |
| Report title | USDT Address (TRX) |
| Address | `TLj5jNLzaoR94c5WUH9uxpQAY3RZT6gh2y` |
| Owner / Type | Undefined / Undefined |
| Balance | 319 563.46 USDT |
| Sent / Received | 469 032 298.48 / 469 351 861.94 USDT |
| Risk Score | **Low — 23%** |
| Exposure | High 1.64% · Moderate 1.80% · Low 95.69% · Unknown 0.87% |
***
## 1. Header — what exactly was checked
The first line states the **asset and network** (*USDT Address (TRX)*), followed by the full address. This makes the report unambiguous on its own, which matters when it is forwarded to a banking partner or attached to a case file.
**How to read it.** An address is screened **per network and per asset**. The same address string on a different chain, or the same address with a different token selected, is a different check with a different result.
A USDT (TRC-20) check does **not** cover the native TRX balance of the same address, and vice versa. If a customer moves both, run both checks.
***
## 2. Owner and Type — whose address is this
| Field | Meaning |
| - | - |
| **Owner** | The name of the entity controlling the address, if Dataspike's database recognizes it (e.g. *Binance*). |
| **Type** | Its category from the entity taxonomy, e.g. *Licensed exchange service*. |
In the example both are **Undefined** — the address is not attributed to any known service.
**How to read it.** *Undefined* is the normal result for an individual, non-service wallet. It is **not** a risk signal on its own.
When these fields *are* populated, they change the meaning of everything below them: a service address pools funds from thousands of users, so its exposure profile describes **the service**, not your customer. If a customer gives you an address that resolves to an exchange, you are looking at the exchange's wallet — ask for an address they actually control.
***
## 3. Balance, Sent, Received — behavior at a glance
| Field | Example | What it is |
| - | - | - |
| **Balance** | 319 563.46 USDT | Balance in the selected token **at the moment of the check** — a snapshot, not a live figure. |
| **Sent** | 469 032 298.48 USDT | Lifetime total sent from the address. |
| **Received** | 469 351 861.94 USDT | Lifetime total received by the address. |
**How to read it.** Compare the three. Here, roughly 469M USDT has flowed in and almost the same amount out, while the standing balance is a small fraction of that. That is **pass-through behavior** — typical of an exchange, aggregator, or payment processor wallet, not of an end user's savings.
Volume that is wildly inconsistent with a customer's declared profile is a finding in itself, independent of the risk score.
***
## 4. Risk Score and grade — the verdict
The report prints the score, its grade, a plain-language interpretation, and the band legend:
> **Risk Score Low — 23%.** Low suspicious funds rate. The funds or address is likely safe. *0–25% low suspicious rate • 26–74% moderate • 75%+ high*
| Grade | Score | Meaning |
| - | - | - |
| **Low** | 0–25% | Minimal suspicious exposure — funds are likely clean. |
| **Moderate** | 26–74% | Suspicious exposure detected — additional review recommended. |
| **High** | 75–100% | Significant high-risk exposure — funds may be held or frozen by regulated counterparties. |
| **Undefined** | — | Not enough reliable data to classify the source of funds. |
**How to read it.** The score is a **weighted composite** of the exposure shares below it, with high-risk categories weighted more heavily than low-risk ones. It is *not* the sum of the high-risk percentages: 1.64% high-risk exposure produced a 23% score.
Do not try to reconstruct the score by adding up percentages, and do not treat the score as a probability. Use the **grade** as your decision gate and the **category breakdown** as your explanation. Full methodology: [Risk scoring](/wallet-checks-kyt/risk-scoring-entity-types).
**Developers:** in the API, `risk_score` is a **0–1 coefficient**, not a percentage. `"risk_score": "0.227"` is the same result as *23%* in the dashboard. Multiply by 100 before displaying it, and prefer `risk_score_grade` for branching logic.
***
## 5. Exposure by tier — where the funds come from
Four totals covering the traced funds, each printed with its own one-line explanation:
| Tier | Example | What the report says | How to read it |
| - | - | - | - |
| **High** | 1.64% | High-risk funds. High likelihood of funds being frozen or blocked. | Any non-zero value is worth a look. Severity depends on the categories in §6. |
| **Moderate** | 1.80% | Suspicious funds. Regulated exchanges may hold or delay these funds. | Often legitimate (bridges, DEX, unlicensed venues) but context-dependent. |
| **Low** | 95.69% | Low-risk funds with a minimal level of suspicious activity. | Licensed services, mining rewards, verified wallets. Banking partners typically expect this to dominate. |
| **Unknown** | 0.87% | No reliable data on the source of funds. | Not suspicious by itself. Large Unknown shares combined with other signals can indicate obfuscation. |
These four tiers describe **100% of the traced funds**. The risk score does not equal any one of them.
***
## 6. Payments — the category breakdown
This is the **evidence behind the score**, and the section analysts should spend the most time in: every entity category detected in the fund trail, with its share, grouped by tier. It covers both direct (1 hop) and indirect (n-hop) connections.
The High tier in our example:
And the Low tier, which explains why the overall grade is Low:
| Category | Share |
| - | - |
| Licensed exchange service | 95.34% |
| Online wallet | 0.29% |
| P2P Exchange licensed | 0.06% |
**How to read it.**
* **Find the driver.** One or two categories usually account for most of a tier. Here, licensed exchanges dominate the Low tier, and *Illegal service* dominates the High tier.
* **Ask whether it fits the story.** Heavy DEX and bridge exposure is unremarkable for a DeFi trader and odd for a pensioner receiving a one-off payment.
* **Read `<0.01%` as a trace, not as noise.** Tiny exposures are printed rather than hidden on purpose. For most categories a trace is background; for **sanctions, terrorism financing, CSAM, and stolen assets** it is a finding to document.
**Any sanctions exposure warrants escalation, regardless of size.** Supervisors increasingly treat sanctions nexus as a binary matter, not a materiality question. Escalate, document the decision, and keep the PDF.
***
## 7. Direct transfers — the named counterparties
A table of entities that transacted **directly** with the address (1 hop), with lifetime amounts in the selected token. Excerpt from the example:
| Owner | Type | Sent USDT | Received USDT |
| - | - | - | - |
| Kraken | Licensed exchange service | 676.04 | 343 505 933.40 |
| Bybit | Licensed exchange service | 4 214 125.54 | 92 951 113.00 |
| Gate | Licensed exchange service | 9 797 057.20 | 0.00 |
| Binance | Licensed exchange service | 4 027 686.23 | 688 962.48 |
| MEXC | Licensed exchange service | 2 690 381.11 | 0.00 |
**How to read it.**
* **A zero means one-directional.** Gate and MEXC only received funds from this address; there is no return flow.
* **These are names, not categories** — which makes this the starting point for counterparty due diligence and for gathering Travel Rule counterparty information.
* **Direct vs indirect.** Direct transfers are who the address *touched*. The category breakdown in §6 also includes entities further down the trail (n-hop) — which is how a mixer two hops back shows up even though it never appears in this table. See [How KYT works](/wallet-checks-kyt/how-kyt-works#direct-vs-indirect-exposure).
An empty Direct transfers table with a populated category breakdown is normal: it means no *named* entity interacted directly with the address.
***
## 8. Footer — provenance
Every page carries the **generation timestamp in UTC**, the **ID of the account that generated the report**, and page numbers — for example *GENERATED ON 06-MAR-26 10:34:12 UTC BY OC5AB55F497104667, Page 1 of 3*.
This is what makes the PDF usable as audit evidence: an auditor can establish when the assessment was made and by whom. Store the check `id` from the API alongside the file so the result can be re-fetched later.
***
## Two things called "status"
These are frequently confused, especially between engineering and compliance teams.
| | Where it appears | Values | What it tells you |
| - | - | - | - |
| **Processing status** | API field `status` | `pending`, `success` | Whether the *check* has finished computing. Nothing about risk. |
| **Verification status (grade)** | Dashboard badge, PDF, API `risk_score_grade` | `low`, `moderate`, `high`, `undefined` | The *verdict* on the address or transaction. |
Two API responses that look like errors but are statuses:
* **`404` No data, try again later** — the check is still being calculated. Retry in 2–5 seconds with exponential backoff.
* **`404` Empty address** — the address has no transaction history on the selected network. Not an error in your integration; verify the network and token first.
***
## Worked example 1 — a Low grade that still needs EDD
Our TRON address: **23%, Low**. Two findings survive the headline grade.
**Finding 1 — this looks like a service wallet, not an end user.** \~469M USDT in and out, a balance that is a fraction of the flow, and ten named direct counterparties that are all licensed exchanges. The 95.34% *Licensed exchange service* share is exactly what an exchange or aggregator wallet looks like.
*So what:* if this was submitted as a customer's personal wallet, the profile does not match. Request an address the customer controls, or reclassify the relationship as business-to-business and apply the corresponding due diligence.
**Finding 2 — Low grade, non-zero high-risk exposure.** Inside the 23% score sits 1.64% high-risk exposure, including **Sanctions 0.42%** and **Illegal service 0.89%**.
*So what:* under the policy of treating any high-risk exposure as an escalation trigger, this address goes to enhanced due diligence despite the Low badge. Document the source of funds, the reason for the decision, and archive the report.
**Decision:** proceed, but with EDD and a documented rationale — not silent auto-approval.
***
## Worked example 2 — a transaction check
Transaction reports follow the same layout, scoped to a single TxID. A real Bitcoin transaction check:
| Field | Value |
| - | - |
| Report title | Bitcoin Transaction |
| TxID | `20bac599…02d3c61` |
| Block / Date | 939429 / 05-Mar-26 14:32:45 UTC |
| Sender / Type | Undefined / Undefined |
| Receiver / Type | Binance / Licensed exchange service |
| Risk Score | **Low — 14%** |
| Exposure | High 0.77% · Moderate 1.61% · Low 96.90% · Unknown 0.73% |
| High-tier drivers | Scam 0.46% · Drugs trafficking 0.13% · Gambling 0.08% · Mixing service 0.03% |
**How to read it.** Instead of Balance / Sent / Received, a transaction report shows the block, the timestamp, and **Sender and Receiver attribution**. Here the funds moved from an unattributed address to a licensed exchange.
The grade is Low and the composition is dominated by licensed services — but **Mixing service 0.03%** and **Scam 0.46%** are present in the trail. If this is an inbound deposit to your platform, that combination is worth a note in the case file; if you are screening an outbound withdrawal to an exchange, it is context for your counterparty record.
On Bitcoin a single transaction can have many senders and recipients, so a transaction-level score covers the whole TX. To score exactly the leg that concerns you, use the transfer check (API only). Transfer results return an exposure breakdown with `risk_score: null` — there is no single grade for one leg.
***
## Decision matrix
| What you see | Grade | Recommended action |
| - | - | - |
| 0–25%, no high-tier exposure | Low | Proceed. Archive the report. |
| 0–25%, **any** high-tier exposure | Low | Proceed **with EDD**. Document the finding and the rationale. |
| 26–74% | Moderate | Enhanced due diligence: verify source of funds, request documentation, review the category breakdown, put the address on periodic re-screening. |
| 75–100% | High | Hold the operation, manual review, escalate to the MLRO, consider blocking. Archive the PDF as case evidence. |
| Sanctions category present at any share | any | Escalate immediately per your sanctions procedure. |
| Not enough data to classify | Undefined | Do not read as clean. Fall back to other controls: KYC evidence, source-of-funds documents, transaction history. |
Regardless of outcome:
* **Archive the PDF for every check that supports a business decision** and keep it for at least 5 years, or longer where your jurisdiction requires it.
* **Log who decided what, and why.** The report evidences the assessment; your file evidences the decision.
* **Re-screen periodically.** Scores change as new transactions appear and as counterparties are newly classified.
Configurable thresholds and risk profiles are on the roadmap — see [Feature availability](/wallet-checks-kyt/result-fields-coverage#feature-availability).
***
## Edge cases and common misreads
They are not supposed to. The tiers describe the composition of traced funds; the score is a weighted composite of that composition. See [Risk scoring](/wallet-checks-kyt/risk-scoring-entity-types).
Expected. New transactions arrive and entity classifications are added continuously, so an address can move between grades over time. Point-in-time results are why reports carry a generation timestamp — and why re-screening is a control, not a formality.
Normal for individual wallets. It means "not attributed to a known service", not "unknown and therefore suspicious".
There was not enough attributable data to classify the source of funds — for example an address with no recognizable counterparties. Treat it as *no signal*, not as a pass, and rely on your other controls.
Balance is a snapshot taken when the check ran, in the selected token only. Re-run the check for a current figure, and confirm you selected the same asset the explorer is showing.
The exposure was detected but rounds below one hundredth of a percent. It is printed rather than dropped so that severe categories are never silently hidden.
***
## What a KYT report does not tell you
Being explicit about the limits is part of using the tool correctly.
* **It does not identify a person.** Attribution names *services*, not individuals. KYT analyzes public on-chain data only and processes no personal data.
* **Exposure is association, not proof.** A share of funds traced to a high-risk category means the fund trail touched that category — it is a reason to investigate, not a conclusion about your customer's conduct.
* **It is point-in-time.** Every result describes the chain as of the moment of the check.
* **Coverage is bounded.** Bitcoin, Ethereum (ERC-20), and TRON (TRC-20) today — see [Supported networks](/wallet-checks-kyt/result-fields-coverage#supported-networks).
* **Per-category weights are not exposed field by field.** The mechanism is published (weighted exposure, high-risk categories weigh more) and every result ships with the full breakdown that drove it, so results are explainable and reviewable — but the score is not intended to be recomputed by hand.
***
## Next steps
Every field in all three surfaces, plus networks and availability.
Methodology, bands, why small high-risk exposure matters, and the full taxonomy.
Playbooks by industry, exporting reports, and the compliance context.
The pipeline and the vocabulary behind every result.
# KYT FAQ
Source: https://docs.dataspike.io/wallet-checks-kyt/kyt-faq
Frequently asked questions about Dataspike KYT: coverage, risk scoring, reading results, integration, and data handling.
Grouped by who is asking. Interpretation questions link into the walkthrough rather than answering twice.
***
## About the product
A tool for assessing the risk of crypto wallets and transactions. It analyzes the origin of funds, identifies links to high-risk entities, and produces evidence you can archive. See the [Overview](/wallet-checks-kyt/overview).
KYC identifies the *user*; KYT analyzes the *behavior of funds* on the blockchain. KYC tells you who your customer is, KYT tells you where their crypto has been. KYT works standalone, but the pair is what makes a complete AML framework.
Bitcoin, Ethereum (ERC-20), and TRON (TRC-20), covering native assets plus USDT and USDC on Ethereum and TRON. More are on the roadmap — see [Supported networks](/wallet-checks-kyt/result-fields-coverage#supported-networks).
Crypto exchanges, neobanks and fintechs, payment gateways and PSPs, OTC desks and brokers, and any AML team screening crypto wallets. Workflows per business are in [Decision playbooks](/wallet-checks-kyt/working-with-results#decision-playbooks).
Not yet. Address monitoring, alerts, and custom risk profiles are planned; today KYT provides on-demand checks. See [Feature availability](/wallet-checks-kyt/result-fields-coverage#feature-availability).
***
## Reading results
It is a weighted composite of the funds' exposure across entity categories, based on direct and indirect interactions with tagged entities, transaction history, and the share of assets tied to high-risk categories. High-risk exposure weighs more heavily. Details in [Risk scoring](/wallet-checks-kyt/risk-scoring-entity-types).
Yes. Every result includes a per-category breakdown showing which entity types are in the fund trail and what share each accounts for, plus a Direct transfers table naming the immediate counterparties. The walkthrough shows how to use both: [How to read a KYT report](/wallet-checks-kyt/how-to-read-a-kyt-report).
Because the score is weighted, not additive — a small high-risk share inside a large clean flow still produces a Low score. Treat any High-tier exposure as an escalation trigger anyway: see the [decision matrix](/wallet-checks-kyt/how-to-read-a-kyt-report#decision-matrix).
New transactions arrive and entity classifications are added continuously, so every result is a point-in-time record. This is why re-screening is a control rather than a formality.
Two different things depending on where you see it. As **Owner / Type** it means the address is not attributed to a known service — normal for personal wallets. As the **grade** it means there was not enough attributable data to classify the source of funds — treat it as no signal, not as a pass.
Yes to both. It analyzes direct (1-hop) and indirect (n-hop) relationships along the fund trail, and related addresses are grouped into clusters and analyzed as a single entity, which improves attribution accuracy.
***
## Integration
Usually seconds, most under 5 via the API. While analysis runs, the API returns `404` with `"No data, please try again later"` — retry with a short backoff.
The API returns a 0–1 coefficient; the dashboard and PDF show a percentage. `"0.34"` means 34%. Multiply by 100 for display, and branch on `risk_score_grade` to avoid the conversion entirely. See [Result fields](/wallet-checks-kyt/result-fields-coverage).
Yes. The API allows 24 requests per second per key; for bulk jobs batch the requests with backoff, and contact `service@dataspike.io` about higher sustained limits.
Custom profiles and thresholds are planned. Today, use the built-in grades as decision gates and the raw coefficient for any finer logic on your side.
***
## Data and compliance
No. KYT works exclusively with public blockchain data. Stored data covers analysis results, risk scores, aggregated on-chain analytics, reports, and audit logs. See [Data handling](/wallet-checks-kyt/working-with-results#data-handling).
Yes. Reports are timestamped, attributable, and self-contained, suitable for AML teams, internal audit, banking partners, and as supporting evidence for SAR/STR filings. Retention guidance is in [Exporting reports](/wallet-checks-kyt/working-with-results#exporting-and-storing-aml-reports).
No — and no screening tool does on its own. KYT supports Travel Rule workflows by identifying counterparty services one hop away; it is not a Travel Rule messaging protocol. See [Compliance context](/wallet-checks-kyt/working-with-results#compliance-context).
# Overview
Source: https://docs.dataspike.io/wallet-checks-kyt/overview
Screen crypto wallet addresses and transactions for AML risk with Dataspike KYT — instant risk scoring, entity attribution, and audit-ready PDF reports.
**KYT (Know Your Transaction)** is Dataspike's automated compliance system for risk-scoring crypto wallet addresses and transactions. It analyzes public blockchain data, fund flows, and entity classifications to detect exposure to money laundering, sanctions, fraud, stolen funds, mixing services, and other illicit activity — before you onboard a customer, credit a deposit, or process a payout.
KYT works alongside Dataspike's KYC and AML Screening products, so you can cover identity verification and transaction risk in a single platform — or use KYT fully standalone.
***
## What you can do today
* **Score any wallet address** on Bitcoin, Ethereum, or TRON and get a 0–100% risk score with a full entity-category breakdown.
* **Score a specific transaction** by TxID to screen an individual deposit or withdrawal.
* **Screen a specific fund transfer** (one sender → recipient pair inside a transaction) via the API — essential for Bitcoin's UTXO model and multi-party transactions.
* **Download an audit-ready AML PDF report** for any check, suitable for AML teams, internal audit, banking partners, and regulators.
* **Browse and search your check history** in the dashboard or via the API.
* **Automate everything over REST API** — every check available in the dashboard is also available programmatically.
KYT consists of two modules. **Risk Score Wallet** — instant, on-demand scoring of addresses and transactions — is live today in both the dashboard and the API. **Transaction Monitoring** (continuous address monitoring, alerts, and custom risk profiles) is coming soon. See [Feature availability](/wallet-checks-kyt/result-fields-coverage#feature-availability) for the current status of every feature.
***
## Who uses KYT
| Who | What they use it for |
| - | - |
| **Crypto exchanges** | Screen deposits and withdrawals, check wallets at onboarding, identify counterparties. |
| **Neobanks and fintechs** | Verify the source of crypto funds before crediting fiat, with one audit trail across KYC and KYT. |
| **Payment gateways and PSPs** | Check the origin of funds before accepting a crypto payment and protect banking relationships. |
| **OTC desks and brokers** | Screen counterparty wallets and asset history before executing large trades. |
Workflows and typical policies per industry are in [Working with results](/wallet-checks-kyt/working-with-results).
***
## Why it matters
Wallet and transaction screening is a core control in AML/CFT programs for virtual asset service providers. KYT helps you meet expectations set by FATF Recommendations, EU AMLD5/6, and MiCA-era supervision — replacing slow manual reviews with automated, explainable, documented risk decisions. Learn more in [Compliance context](/wallet-checks-kyt/working-with-results#compliance-context).
***
## Start here
Run your first check in the dashboard or make your first API call in minutes.
A real result explained line by line, with a decision matrix. Start here if you review cases.
The analysis pipeline, the three check types, and the core vocabulary.
How the score is built, the entity taxonomy behind it, and how to turn it into policy.
Every field mapped across the dashboard, the PDF report, and the API — plus coverage.
Playbooks per industry, exporting reports, and the compliance framing.
# Result Fields & Coverage
Source: https://docs.dataspike.io/wallet-checks-kyt/result-fields-coverage
Every KYT result field mapped across dashboard, PDF, and API — plus supported networks and assets, and current feature availability.
One check, three surfaces: the **dashboard**, the **AML PDF report**, and the **API response**. The tables below are the lookup between them — useful when an analyst and a developer are looking at the same case and using different words for the same field. This page also documents what KYT covers today: [supported networks](#supported-networks) and [feature availability](#feature-availability).
For the interpretation of these fields in context, see [How to read a KYT report](/wallet-checks-kyt/how-to-read-a-kyt-report).
***
## Address check
| Dashboard | PDF report | API field | Meaning and how to use it |
| - | - | - | - |
| Check ID | — | `id` | Identifier of the check. Store it: it is required to re-fetch the result and to download the PDF. |
| — | — | `status` | Processing status: `pending` (still computing) or `success` (ready). Not a risk value. |
| Network | Header, e.g. *USDT Address (TRX)* | `blockchain` | `btc`, `eth`, or `trx`. Screening is per network. |
| Token | Header | `token` | `""` for the native asset, or `USDT` / `USDC`. |
| Address | Header | `address` | The address that was screened. |
| Owner | Owner | `name` | Entity name if the address is attributed; `null` / *Undefined* otherwise. Undefined is normal for individual wallets. |
| Type | Type | `type`, `type_label` | Entity category. `type` is machine-readable (`EXCHANGE_LICENSED`), `type_label` is for display (*Licensed exchange service*). |
| Balance | Balance | `balance` | Balance in the selected token at the moment of the check. A snapshot. |
| Sent | Sent | `sent` | Lifetime total sent from the address. |
| Received | Received | `received` | Lifetime total received by the address. |
| Risk score badge | Risk Score | `risk_score` | **0–1 coefficient in the API**, 0–100% in the dashboard and PDF. `"0.227"` = 23%. |
| Risk level | Grade next to the score | `risk_score_grade` | `low`, `moderate`, `high`, `undefined`. Use this for decision logic. |
| Direct transfers table | Direct transfers | `direct_entities` | Named entities one hop away, with amounts. Each item: `name`, `type`, `type_label`, `incoming_amount`, `outgoing_amount`. |
| Category breakdown | Payments | `indirect_payments` | Entity categories across the fund trail. Each item: `type`, `type_label`, `risk_score_grade` (the tier), `total_count` (% share), `total_count_coef` (same as coefficient), `total` (absolute amount). |
| — | Footer | — | Generation timestamp (UTC) and the ID of the generating account, printed on every page. |
`indirect_payments[].risk_score_grade` is the **tier of that category** (`low` / `moderate` / `high` / `undefined`), not the score of the check. The tier totals shown in the PDF are the sums of the categories in each tier.
***
## Transaction check
Same fields as above, minus balance-related ones, plus:
| Dashboard | PDF report | API field | Meaning |
| - | - | - | - |
| TxID | Header | `tx_id` / header value | The transaction that was screened. |
| Block | Block number | `block_number` | Block containing the transaction. |
| Date | Date | `timestamp` | Transaction time, UTC. |
| Senders | — | `inputs` | Number of senders in the transaction. Can be large on Bitcoin. |
| Recipients | — | `outputs` | Number of recipients in the transaction. |
| Sender | Sender / Type | `from_entity` | Sender attribution, if identified. Empty array when unattributed. |
| Receiver | Receiver / Type | `to_entity` | Recipient attribution, if identified. |
| Category breakdown | Payments | `indirects` | Same structure as `indirect_payments`. |
Transaction reports contain **no balance field** — a transaction has an amount, not a balance.
***
## Transfer check (API only)
Isolates one sender → recipient pair inside a transaction. Useful on UTXO chains and in multi-party transactions.
| API field | Value | Note |
| - | - | - |
| `risk_score` | `null` | A single leg does not receive an overall score. |
| `risk_score_grade` | `null` | Use the exposure breakdown instead. |
| `indirects` | populated | Exposure breakdown for that specific flow of funds. Shares are relative to the transferred amount, so a single-counterparty transfer can legitimately show `100`. |
***
## Request parameters that change the result
| Parameter | Values | Effect on what you read |
| - | - | - |
| `blockchain` | `btc` / `eth` / `trx` | Screening context. The same address string on another chain is a different subject. |
| `token` | `""` / `USDT` / `USDC` | Empty means the native asset. Balances, amounts, and exposure are all denominated in the selected asset. |
| `directs` | `false` (default) / `true` | `false` analyzes indirect (n-hop) payments; `true` restricts the analysis to direct interactions only, which usually lowers the amount of exposure surfaced. |
When comparing two results for the same address, confirm that `token` and `directs` match. Different parameters produce legitimately different scores.
***
## Statuses and response codes you will see
| Code / value | Meaning | What to do |
| - | - | - |
| `status: "pending"` | Check submitted, analysis running. | Poll `GET .../{id}` again after 2–5 seconds. |
| `status: "success"` | Result is final for this point in time. | Read the result; download the PDF. |
| `404` No data, try again later | Still processing. | Retry with exponential backoff. Not an integration error. |
| `404` Empty address | No transaction history on the selected network. | Verify network and token. An unused address cannot be scored. |
| `404` Not Found | Address / TX does not exist on chain, or the check ID is unknown. | Validate the input. |
| `400` / `422` | Invalid parameters, or valid JSON with semantically incorrect values. | Check parameter values against the tables above. |
| `401` | Missing or invalid `ds-api-token`. | Verify the header. Never expose the key client-side. |
| `403` | Expired license, insufficient check balance, or no access to the resource. | Contact `service@dataspike.io`. |
| `429` | Rate limit exceeded (24 requests/second per key). | Back off and retry. |
| `500` / `503` | Server error or temporary downtime. | Retry with exponential backoff. |
***
## Supported networks
KYT currently supports three blockchain networks. All check types — address, transaction, and transfer — work on all supported networks.
| Network | API value | Native asset | Supported tokens |
| - | - | - | - |
| Bitcoin | `btc` | BTC | — |
| Ethereum | `eth` | ETH | USDT (ERC-20), USDC (ERC-20) |
| TRON | `trx` | TRX | USDT (TRC-20), USDC (TRC-20) |
**Selecting the asset**
* **Dashboard:** choose the Network, then the Token. Leave the token empty to screen the native asset (BTC/ETH/TRX).
* **API:** set `blockchain` to `"btc"`, `"eth"`, or `"trx"`, and `token` to `""` (native), `"USDT"`, or `"USDC"`.
**Network-specific behavior**
* **Bitcoin (UTXO model).** A single transaction can include many senders (inputs) and recipients (outputs). To score one specific flow of funds inside such a transaction, use the transfer check — for Bitcoin, only the `recipient_address` is required to isolate the flow.
* **Ethereum and TRON (account model).** Transfer checks require both `sender_address` and `recipient_address`. Token transfers (USDT/USDC) are screened with the same endpoints — just set the `token` parameter.
An address is screened **per network**: the same entity may control addresses on several chains, and each is checked in its own network context. If an address has no transaction history on the selected network, the API returns `404 Empty address`.
Additional blockchain networks are on the Dataspike roadmap. Only Bitcoin, Ethereum, and TRON are supported today — if you need another chain for your integration, tell us at `service@dataspike.io` so we can factor it into prioritization.
***
## Feature availability
KYT capabilities ship to the API and the dashboard on their own schedules. This table is the single source of truth for what you can rely on today.
| Feature | Dashboard | API |
| - | - | - |
| Address check | Available | Available |
| Transaction check | Available | Available |
| Transfer check (specific fund flow) | Not yet | Available |
| PDF report download | Available | Available |
| List and search past checks | Available | Available |
**Coming soon**
| Feature | Status |
| - | - |
| Risk profile configuration | Planned |
| Address monitoring and alerts | Planned |
| Custom risk thresholds | Planned |
**Risk profiles** will let you define what *your* business considers risky — entity types, threshold amounts, and the acceptable share of high-risk exposure — and apply different profiles per product line or jurisdiction. **Address monitoring** will track subscribed addresses in the background and raise **alerts** when the risk score changes, new suspicious activity appears, or a risk-profile condition triggers. Further ahead, the roadmap includes additional blockchain networks and deeper integration between KYT and Dataspike's AML Screening module.
Timelines are indicative and may change. Until a feature appears in the **Available now** table, don't build production logic that depends on it — and if one of these capabilities is blocking your integration, tell us at `service@dataspike.io`.
# Risk Scoring & Entity Types
Source: https://docs.dataspike.io/wallet-checks-kyt/risk-scoring-entity-types
How the Dataspike KYT risk score is calculated, what the grades mean, how to turn a score into policy — and the full taxonomy of entity categories behind every breakdown.
The KYT risk score answers one question: **how much of the money touching this address or transaction can be traced to sources that regulated institutions treat as a problem?**
This page covers the methodology, how to build policy on it, and the entity taxonomy that drives it. For reading an actual result, see [How to read a KYT report](/wallet-checks-kyt/how-to-read-a-kyt-report).
***
## How the score is built
KYT follows funds in and out of the subject address, across **direct interactions** (1 hop) and **indirect ones** (n-hop, further along the trail).
Addresses along the trail are matched against Dataspike's entity database and clustered. Each recognized entity carries a category — *Licensed exchange service*, *Mixing service*, *Sanctions*, and so on. See the [entity taxonomy](#entity-types) below.
For every category, KYT computes the **share of funds** associated with it. These shares are what you see in the category breakdown of a result.
The shares are combined into a **0–100% score**, with high-risk categories weighted more heavily than the same share of moderate- or low-risk exposure, and the result is classified into a grade.
Inputs to the assessment include interactions with tagged entities, direct and indirect connections, asset movement history, and the proportion of funds tied to high-risk categories.
***
## Bands and grades
| Grade | Score | Interpretation | Typical policy |
| - | - | - | - |
| **Low** | 0–25% | Minimal suspicious exposure | Proceed |
| **Moderate** | 26–74% | Suspicious exposure detected | Enhanced due diligence, monitor |
| **High** | 75–100% | Significant high-risk exposure; funds may be held or frozen by regulated counterparties | Manual review, consider blocking, escalate |
| **Undefined** | — | Not enough reliable data to classify the source of funds | Fall back to other controls |
The band legend is printed on every PDF report, so recipients outside your team can interpret the number without extra context.
**In the API, `risk_score` is a 0–1 coefficient, not a percentage.** `"risk_score": "0.34"` corresponds to **34%** in the dashboard. Multiply by 100 before displaying it, and prefer `risk_score_grade` (`low` │ `moderate` │ `high` │ `undefined`) for decision logic — it needs no conversion.
***
## The score is explainable, not a verdict generator
Every result ships with the breakdown that produced it: the share of funds per entity category, across all tiers, covering direct and indirect connections. Alongside it, the **Direct transfers** table names the counterparties one hop away with amounts.
That means you can always answer three questions about a score:
* **Which categories drove it** — scam, sanctions, darknet, unlicensed venues, bridges.
* **How much of the money** each category accounts for.
* **Who, by name**, the address dealt with directly.
What the score is *not* is a probability that your customer is a criminal, or a figure you should recompute by hand. Per-category weights are not exposed field by field; the published breakdown is the explanation.
***
## Small high-risk exposure still matters
Because the score is a weighted composite, an address can be **Low overall while carrying non-zero high-risk exposure** — a real example is a 23% Low score containing 0.42% sanctions exposure. Supervisory expectations increasingly treat *any* high-risk nexus as something to address, independent of percentage size.
**Recommended policy:** make any High-tier exposure an escalation trigger in its own right. Use the overall grade to prioritize work, not to skip it.
***
## Scores change over time
Assessments update as new transactions appear and as entity classifications are added to the database. An address that scored Low last quarter can score higher today — because it received new funds, or because a counterparty was newly classified.
**Re-screen addresses periodically**, especially for customers with ongoing deposit activity, and treat every stored result as a point-in-time record.
***
## From score to policy
A workable baseline, to be adapted to your risk appetite and jurisdiction:
* **`low`** — approve the deposit, withdrawal, or onboarding step. Archive the report.
* **`moderate`** — hold for enhanced due diligence: verify source of funds, request supporting documentation, review the category breakdown, schedule re-screening.
* **`high`** — block the operation, escalate to compliance, archive the PDF report as case evidence.
* **Any High-tier category present** — flag for review even when the overall grade is low.
* **`undefined`** — do not read as clean; rely on KYC evidence and source-of-funds documents.
The full triage flow, including sanctions handling and edge cases, is in the [decision matrix](/wallet-checks-kyt/how-to-read-a-kyt-report#decision-matrix). Custom risk profiles and configurable thresholds are planned — see [Feature availability](/wallet-checks-kyt/result-fields-coverage#feature-availability).
***
## Entity types
Every entity KYT identifies in a fund flow is classified into a **risk tier**. These categories appear as **Flags** in the dashboard, as line items in the category breakdown, in the PDF report, and in the API as the machine-readable `type` field with a human-readable `type_label` (e.g. `EXCHANGE_LICENSED` / *Licensed exchange service*). The share of funds associated with each category — weighted by tier — is what drives the score above.
### Low-risk entities
| Entity type | Description |
| - | - |
| Licensed exchange service | Exchanges licensed in the EU that allow unlimited withdrawals (e.g. Binance, Bybit, Kraken) |
| P2P Exchange licensed | EU-licensed peer-to-peer trading platforms |
| Online wallet | Funds stored with verified custodial wallet providers |
| Online marketplace | Funds used to purchase goods and services on legal platforms |
| Rewards/commissions | Rewards paid to miners for producing blocks or processing transactions |
| Miner | Newly mined coins |
| Seizure of funds | Assets confiscated by governments and subsequently legalized |
### Moderate-risk entities
| Entity type | Description |
| - | - |
| Decentralized exchange (DEX) | Exchanges that trade via smart contracts (e.g. Uniswap, Curve, 1inch) |
| Bridges | Funds transferred between blockchains using cross-chain bridge services |
| Unlicensed exchange service | Exchanges without an EU license that allow unlimited withdrawals |
| P2P Exchange unlicensed | Peer-to-peer platforms without an EU license |
| Lending protocol | DeFi platforms for lending and borrowing crypto (e.g. Aave, Compound) |
| Payment system | Funds associated with verified payment providers and gateways |
| ATM | Funds obtained from cryptocurrency ATMs |
### High-risk entities
| Entity type | Description |
| - | - |
| Scam | Funds obtained through confirmed fraudulent activity |
| Scam crypto exchange | Exchanges involved in exit scams or illegal operations |
| Stolen assets | Funds from hacked wallets or confirmed theft |
| Sanctions | Addresses of individuals or organizations on OFAC, UN, EU, or other international sanctions lists |
| Sanctioned jurisdiction | Funds associated with countries that do not comply with AML/KYC standards |
| Darknet marketplace | Markets operating on the dark web |
| Darknet service | Darknet-adjacent services linked to severe crimes |
| Illegal service | Funds associated with other illegal activities |
| Mixing service | Coin mixers and tumblers used to obfuscate fund trails |
| Privacy protocol | Protocols that conceal sender, recipient, amounts, or IP addresses |
| Gambling | Funds associated with unlicensed gambling |
| Terrorism financing | Funds used to finance terrorism |
| Malware | Funds obtained through malware attacks or cyberattacks |
| Ransom | Funds obtained through blackmail or ransomware |
| Drugs trafficking | Funds associated with the drug trade |
| Illegal arms trafficking | Funds associated with illegal weapons trade |
| Child abuse material | Funds used for the purchase or distribution of CSAM |
| Extremism | Funds linked to extremist crimes and organizations |
| Seized assets | Assets seized by law enforcement in connection with criminal cases |
**Seizure of funds** (Low) and **Seized assets** (High) are distinct categories: the former covers confiscated assets that were subsequently legalized by the state; the latter covers assets seized in connection with criminal cases.
### Entities with undefined risk
| Entity type | Description |
| - | - |
| Other | A counterparty exists but has not yet been classified in Dataspike's database |
| Undefined | No reliable data on the source of funds |
**Undefined is not a red flag by itself** — it is typical for ordinary personal wallets that don't belong to any recognized service. Evaluate it in context: a wallet that is *mostly* undefined with no attributable counterparties warrants more scrutiny than one with a small undefined remainder.
Dataspike continuously expands and updates entity classifications. New attributions can change the risk picture of an address that was previously unclassified — one more reason to re-screen periodically.
# Working with Results
Source: https://docs.dataspike.io/wallet-checks-kyt/working-with-results
Turn a KYT result into action: decision playbooks by industry, exporting and storing AML reports, and the compliance context behind it all.
A KYT result is only useful once it drives a decision, produces evidence, and stands up to an audit. This page covers all three: when to run each check and what policy to attach ([playbooks](#decision-playbooks)), how to export and keep the [AML reports](#exporting-and-storing-aml-reports), and the [compliance context](#compliance-context) that frames both.
***
## Decision playbooks
The three checks — address, transaction, and transfer — cover most screening workflows. What differs by business is **when** you run them and **what policy** you attach to the result.
### Where checks sit in a customer lifecycle
| Moment | Check | Gate |
| - | - | - |
| Onboarding / wallet registration | Address | Activation of the account or the withdrawal address |
| Incoming deposit | Transaction, or transfer on Bitcoin | Crediting the balance |
| Outgoing withdrawal | Address of the destination | Broadcasting the transaction |
| Periodic review | Address | Continuation of the relationship |
| Escalation / investigation | Any, re-run | Case disposition and SAR decision |
Every grade maps to an action through one shared [decision matrix](/wallet-checks-kyt/how-to-read-a-kyt-report#decision-matrix). Keep a single version of it in your internal policy rather than one per product line.
### Crypto exchanges
**Screen deposits, withdrawals, and users' external wallets.**
* **When you run it.** An address check on any wallet a user registers for withdrawals, gated on the grade. A transaction check on every incoming deposit TxID before crediting — on Bitcoin, a transfer check to isolate the flow that actually reached your deposit address inside a multi-output transaction. An address check on the destination before broadcasting a withdrawal.
* **What to read first.** The Direct transfers table: it names the service behind a deposit, which is your input for counterparty due diligence and Travel Rule information gathering.
* **Typical policy.** Low — auto-credit. Moderate — hold for enhanced due diligence. High — freeze, escalate, archive the report in the case file.
### Neobanks and fintechs
**Verify the source of crypto funds before they touch fiat rails.**
* **When you run it.** When a customer sells crypto or funds an account from a crypto source, run an address check on the originating wallet before crediting fiat.
* **What to read first.** The tier composition. A banking partner reviewing your file will look at how much of the inflow is licensed-exchange exposure and whether any high-risk categories are present at all.
* **Typical policy.** Combine KYT with Dataspike KYC and AML Screening so identity and fund-source screening live in one audit trail, and re-screen active customers' wallets on a schedule.
* **Why it matters.** Documented, explainable screening of crypto inflows is what protects the banking relationship when correspondent banks or auditors ask.
### Payment gateways and PSPs
**Check the origin of funds before accepting a payment.**
* **When you run it.** A transaction check on each incoming payment — or a transfer check for the specific payer → merchant flow — before confirming the payment to the merchant. Batch address checks on merchant settlement wallets on a schedule, respecting the rate limits.
* **What to read first.** The High tier. For a payment you are about to confirm irreversibly, presence beats percentage.
* **Typical policy.** Keep the PDF per accepted payment as evidence of screening; decline or hold before acceptance rather than clawing back afterwards.
### OTC desks and brokers
**De-risk large trades before execution.**
* **When you run it.** Before quoting or settling a block trade, an address check on the counterparty wallet: attribution, lifetime volumes, and the full exposure breakdown.
* **What to read first.** The category breakdown, manually. On a large ticket a small percentage still represents significant absolute value — 1% of a 10M trade is 100k.
* **Typical policy.** Archive the report with the trade record, and re-screen recurring counterparties before each significant trade rather than relying on a past result.
* **Why it matters.** One tainted settlement can freeze funds at the receiving exchange and trigger inquiries. A pre-trade check takes seconds and produces evidence you can show.
### What every playbook shares
* **One policy, written down.** Grades are only useful if the action per grade is agreed in advance, not decided per case.
* **Any High-tier exposure is a trigger**, independent of the overall grade.
* **Archive the report and the decision.** The PDF evidences the assessment; your file evidences the judgment.
* **Re-screen.** A stored result describes the chain at the moment it was run, nothing more.
All of these workflows run over the same REST API — every check available in the dashboard is available programmatically, so manual review tooling and automated screening share one source of truth.
***
## Exporting and storing AML reports
Every KYT check can be exported as a self-contained, timestamped **AML PDF report** — suitable for compliance files, internal audit, banking partners, and as supporting evidence attached to SAR/STR filings.
### Downloading a report
**Dashboard.** Open the check history and click the download icon at the end of any row. The report reflects the check as of the moment it was run.
**API.**
```bash theme={null}
curl -X GET "https://api.dataspike.io/api/v4/kyt/checks/address/YOUR_CHECK_ID/report" \
--header "ds-api-token: YOUR_API_KEY"
```
Transaction reports are available at the equivalent `/kyt/checks/transaction/{id}/report` path. You need the check `id` returned when the check was submitted, which is why storing that `id` alongside your own records is worth doing at integration time.
### What is in a report
| Section | Address report | Transaction report |
| - | - | - |
| Header — asset, network, subject | Token, network, address | Transaction type, TxID, block, date |
| Attribution | Owner and Type of the address | Sender and Receiver, each with Type |
| Volumes | Balance, Sent, Received | — |
| Verdict | Risk Score, grade, plain-language interpretation, band legend | Same |
| Exposure by tier | High / Moderate / Low / Unknown totals with explanations | Same |
| Category breakdown | *Payments* — every entity category with its share, grouped by tier | Same |
| Direct transfers | Named 1-hop counterparties with amounts sent and received | — |
| Footer | Generation timestamp (UTC), generating account ID, page numbers | Same |
Reports are generated for the **selected asset only**. A USDT (TRC-20) report does not describe the native TRX activity of the same address.
### Sharing reports outside your team
Reports are written to stand on their own: the band legend and the meaning of each tier are printed on the document, so a banking partner or auditor can read it without access to the dashboard. Two things to keep in mind:
* **The footer is the provenance.** Do not crop it — the timestamp and generating account are what make the report evidential.
* **A report is point-in-time.** When sharing an older report, state the date of assessment; if the relationship is ongoing, run a fresh check instead.
### Retention
* **Archive the PDF for every check that supports a business decision** — onboarding approval, deposit acceptance, withdrawal release, escalation.
* **Keep reports for at least 5 years**, in line with common AML/CFT record-keeping requirements, or longer where your jurisdiction requires it.
* **Store the check `id` with the file** so the result can be re-fetched and cross-referenced.
* **Record the decision, not just the report.** The PDF evidences the assessment; your case file should evidence who decided what, and on what basis.
***
## Compliance context
Wallet and transaction screening is a core control expected of virtual asset service providers (VASPs), crypto-asset service providers (CASPs), and financial institutions handling crypto flows. KYT gives you the operational tooling for that control: automated risk assessment, explainable results, and audit-ready evidence.
This section describes how KYT supports compliance workflows. It is product documentation, not legal advice — your obligations depend on your license, jurisdiction, and business model, so align your screening policy with your compliance counsel.
### Regulatory frameworks KYT supports
* **FATF Recommendations.** FATF's risk-based approach expects VASPs to identify and assess the ML/TF risk of transactions and counterparties. KYT operationalizes this with per-check risk scores, category-level exposure analysis, and named counterparty identification.
* **EU AMLD5/6 and MiCA-era supervision.** EU-regulated CASPs are expected to run transaction monitoring, apply enhanced due diligence to higher-risk situations, and keep records that evidence their decisions. KYT's grades map naturally onto EDD triggers, and every check can be archived as a PDF report.
* **Travel Rule workflows.** The **Direct Transfers** table names the services one hop away from a screened address — supporting counterparty identification and the information gathering that Travel Rule processes require. KYT is a screening tool, not a Travel Rule messaging protocol; it complements, rather than replaces, your Travel Rule solution.
* **Record-keeping.** Reports are timestamped, attributable, and self-contained. Store them for at least 5 years (or longer where required) as evidence of your screening control.
### Where KYT fits in an AML program
* **Onboarding** — screen customer wallets as part of initial due diligence, alongside identity verification (Dataspike KYC) and sanctions/PEP screening (Dataspike AML Screening).
* **Ongoing monitoring** — screen deposits and withdrawals as they happen; re-screen stored addresses periodically since scores change over time.
* **Escalation and investigation** — use the category breakdown and Direct Transfers to substantiate escalations; attach the PDF report to SAR/STR filings as supporting evidence.
* **Audit and regulator interactions** — the check history plus archived reports give examiners a transparent, reproducible trail of automated decisions.
### Data handling
KYT is designed to be privacy-clean by construction:
**What is analyzed** — exclusively **public blockchain data**: addresses, transaction history, fund flows, balances, volumes, timing, cluster and entity affiliations, and connections to tagged categories. KYT does not process users' personal data as part of the on-chain analysis.
**What is stored** — analysis results, calculated risk scores and risk factors, aggregated on-chain analytics, reports, and audit logs (within the established retention policy). Audit history is available in the platform interface, reports in PDF, and all data via API.
**What is not stored** — user personal data, and any data unrelated to on-chain analytics.
Access is protected with secure API keys, access control, and role separation, in line with standard security measures for corporate and institutional clients.
### Explainability for regulators
Because every score is backed by a per-category exposure breakdown and a named counterparty list, you can always answer the examiner's question: *why did this address get this score, and what did you do about it?* That combination — automated assessment plus documented human response — is the substance of a defensible screening control.
# Configuration
Source: https://docs.dataspike.io/web-sdk/configuration
Integrate the Dataspike Web SDK into your web app for seamless document and identity verification.
📦 **GitHub:** [Dataspike Web Widget SDK](https://github.com/dataspike-io/docver-widget)
***
# Web SDK Overview
The Dataspike Web SDK (DocVer Widget) enables you to embed document and identity verification directly into your web or mobile web applications.\
It provides a secure, customizable user interface that connects to your backend verification flows.
> 💡 **Use case:** Let your users complete KYC verification or document upload directly in your product — no redirects required.
***
## Live Demo
You can preview the widget in action here:\
👉 [Live Demo](https://dash.dataspike.io/widget/demo)
***
## Installation
Choose one of the following installation methods:
**Using NPM**
```bash theme={null}
npm install @dataspike/docver-widget
```
**Using Yarn**
```bash theme={null}
yarn install @dataspike/docver-widget
```
**Using a script tag**
```html theme={null}
```
***
## Basic Usgae
Basic Usage
Render the widget by passing the verification URL ID from your backend:
```javascript theme={null}
import { DocsVerificationWidget } from '@dataspike/docver-widget';
const renderWidget = async () => {
const verificationUrlId = await getVerificationId(); // created on backend
DocsVerificationWidget({
id: verificationUrlId,
elementId: 'root',
apiUrl: 'https://api.dataspike.io',
});
};
```
> ⚠️ Important: Never expose your API key in frontend code. Always create the verification session on your backend and return only the verificationUrlId to the client.
***
## Backend Integration
Your backend is responsible for securely creating and managing verifications before rendering the widget on the client side.
1. **Create a verification** using your Dataspike API key for an existing applicant.
2. **Return only the `verificationUrlId`** to the frontend — this short ID is safe to expose and required for initializing the widget.
3. **Keep sensitive identifiers** such as `applicantId`, `profileId`, and API keys strictly on the server side.
This approach ensures secure communication between your application and the Dataspike API, while preventing direct client access to privileged data.
***
## Next steps
Learn how to personalize the widget’s look and behavior with themes and callbacks.
Review endpoint documentation for creating applicants, verifications, and managing webhook events.
# Customization
Source: https://docs.dataspike.io/web-sdk/customization
Customize the Dataspike Web SDK to match your brand’s style and user experience, with full control over colors, typography, and callbacks.
# Customization & Theming
The Dataspike Web SDK can be fully customized to align with your product’s look and feel.\
You can modify colors, fonts, and typography, and handle user events through callback functions.
> 💡 Theming and event callbacks let you deliver a seamless, branded verification experience that feels native to your product.
***
## Event Callbacks
The widget provides lifecycle callbacks that help you manage user flow and system events.
```javascript theme={null}
DocsVerificationWidget({
id: 'VF57124F182867E0',
elementId: 'root',
apiUrl: 'https://api.dataspike.io',
onInit: async (status, error) => {
console.log('Widget initialized:', status, error);
},
onFinish: async () => {
alert('Verification completed!');
},
});
```
***
## Theming Overview
You can override the default design by passing a theme object when initializing the widget.
Themes can adjust colors, typography, and visual hierarchy to match your brand guidelines.
```javascript theme={null}
const customTheme = {
palette: {
primaryA1: '#5A58DF',
textMain: '#000000',
secondaryA4: '#FF5050',
},
typography: {
fontFamily: 'Mont, sans-serif',
h1: { fontSize: '48px', fontWeight: '600' },
},
};
DocsVerificationWidget({
id: 'VF57124F182867E0',
elementId: 'root',
apiUrl: 'https://api.dataspike.io',
theme: customTheme,
});
```
| **Callback** | **Description** |
| - | - |
| `onInit` | Invoked when the widget loads. Provides the current verification status and any initialization errors. |
| `onFinish` | Triggered when the user completes the verification flow. Use this to update the UI or trigger backend actions (e.g., updating applicant status). |
> ⚙️ Tip: You can dynamically load theme presets for light or dark modes based on user preferences.
***
## Theme Properties
| **Property** | **Type** | **Description** |
| - | - | - |
| `palette` | `object` | Controls the widget’s color palette including text, background, and accent tones. |
| `typography` | `object` | Configures font family, size, weight, and line height for different text elements. |
| `palette.primaryA1` | `string` | Primary brand color used for buttons and interactive highlights. |
| `palette.textMain` | `string` | Main text color used for headings and labels. |
| `typography.h1.fontSize` | `string` | Font size for main headings (e.g., `48px`). |
| `typography.fontFamily` | `string` | Base font family applied throughout the widget. |
For a complete property reference, visit the [**Theme schema on GitHub**](https://github.com/dataspike-io/docver-widget#theming).
***
## Example: Full Custom Theme
```javascript theme={null}
const theme = {
palette: {
textMain: '#1A1A1A',
primaryA1: '#0F62FE',
secondaryA3: '#52C27F',
secondaryA4: '#FF5050',
secondaryA8: '#EDECFC',
},
typography: {
fontFamily: 'Inter, sans-serif',
h1: { fontSize: '42px', fontWeight: '600', lineHeight: '56px' },
text: { fontSize: '14px', lineHeight: '22px' },
},
};
DocsVerificationWidget({
id: 'VF57124F182867E0',
elementId: 'root',
theme,
});
```
***
## Debugging & Troubleshooting
If the widget does not render or behaves unexpectedly, check the following:
1. **Validate the `verificationUrlId`.**\
Make sure it’s created correctly on your backend and returned to the frontend.
2. **Inspect the browser console.**\
Look for JavaScript errors or missing module imports.
3. **Verify the container element.**\
Ensure that the HTML element with the specified `elementId` exists in the DOM before initializing the widget.
4. **Confirm API connectivity.**\
Check that the API endpoint (`https://api.dataspike.io` or the Sandbox equivalent) is reachable from your environment.
> 🧩 **Tip:** Use the browser’s **Network tab** to monitor API requests triggered by the widget. This helps identify failed calls or CORS configuration issues.
***
## Frequently Asked Questions
**Q: Can I use the SDK in Sandbox mode?**\
Yes — the Web SDK fully supports Sandbox mode. All verifications and webhooks return simulated data when initialized with the Sandbox API base URL.
**Q: Does theming affect verification logic?**\
No — theming only controls appearance. The verification process and logic remain unchanged.
**Q: Can I host the SDK myself?**\
Yes. You can install the package via NPM or Yarn, or host the static JS bundle from your own CDN if preferred.
***
Explore the theme schema, and advanced configuration examples.
# Configuration
Source: https://docs.dataspike.io/webhooks/configuration
Set up and manage webhooks to receive real-time verification and AML event notifications from Dataspike.
Webhooks enable your application to receive **real-time event notifications** from the Dataspike platform.\
Instead of polling the API, you can register an endpoint that automatically receives updates for verification results, AML matches, and other key events.
> ⚡ **Use case:** Webhooks are essential for production integrations — they allow your systems to react immediately when verifications complete, AML matches occur, or applicants change status.
***
## 1. Understanding Webhooks
A **webhook** is an HTTP `POST` request sent by Dataspike to your configured endpoint whenever a specific event occurs.
Example flow:
1. You register a webhook URL (e.g. `https://api.yourdomain.com/dataspike/webhook`).
2. A verification completes on Dataspike.
3. Dataspike sends a signed POST request to your URL with the verification data.
Webhook payloads are sent as **JSON objects** and contain the event type, timestamp, and relevant entity data.
**Example payload:**
```json theme={null}
{
"id": "01827ed4-c928-7a3c-9a30-7ab7cc169d11",
"webhook_id": "01827ed4-c928-7a3c-9a30-7ab7cc169d11",
"event_type": "AML_SCREENING",
"timestamp": "2023-07-18T15:32:13Z",
"payload": {}
}
```
***
## 2. Registering Webhook URLs
You can manage webhooks from your **Dashboard → API → Webhooks** tab.
There, you can:
* Add one or more webhook endpoints.
* Assign events (e.g. `AML_SCREENING` and etc.).
* Test and validate your configuration before going live.
Each webhook URL can be specific to an environment:
| Environment | Base URL | Description |
| - | - | - |
| **Sandbox** | `https://sandboxapi.dataspike.io` | For integration testing with simulated data. |
| **Production** | `https://api.dataspike.io` | For live verification and AML events. |
After saving, click **“Send Test”** to verify your endpoint works correctly — a dummy event will be sent immediately.
> 💡 You can register multiple webhooks for different services (e.g. one for AML alerts, one for verifications).
***
## 3. Verifying Webhook Signatures
Dataspike signs every webhook request with HMAC-SHA256 so you can verify it genuinely came from us and the payload has not been tampered with.
### Signature header
Each request includes:
| Header | Description |
| - | - |
| `X-Webhook-Signature` | Signature in the format `t={unix_timestamp},v1={hex_digest}` |
```
X-Webhook-Signature: t=1720018333,v1=a3f4b2c1d5e6...
```
* `t` — Unix timestamp (seconds, UTC) of when the request was signed
* `v1` — HMAC-SHA256 digest of the signed payload, hex-encoded
### How the signature is computed
**Signed payload:** `"{t}.{raw_request_body}"`\
Concatenate the timestamp string, a literal `.`, then the raw JSON body bytes exactly as received — do not re-serialize or pretty-print.
**Key:** hex-decode the secret after stripping the `whsec_` prefix.
### Your signing secret
When you create a webhook, the API response includes `hmac_signing_secret`:
```json theme={null}
{
"id": "01927ed4-c928-7a3c-9a30-7ab7cc169d11",
"hmac_signing_secret": "whsec_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1"
}
```
The signing secret is shown **only once** at creation time. Store it immediately in a secrets manager or environment variable — it cannot be retrieved again.
To replace the secret, call the rotate endpoint:
```
POST /api/v3/organization/webhooks/{webhook_id}/hmac/rotate
```
The response contains a new `hmac_signing_secret`. Update your application with the new value before discarding the old one to avoid dropping events.
### Verification examples
```javascript Node.js theme={null}
import crypto from "crypto";
// Use express.raw({ type: "application/json" }) to preserve the raw body bytes
function verifySignature(secret, rawBody, header) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const key = Buffer.from(secret.replace(/^whsec_/, ""), "hex");
const payload = Buffer.concat([Buffer.from(`${parts.t}.`), rawBody]);
const expected = crypto.createHmac("sha256", key).update(payload).digest("hex");
if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1))) {
throw new Error("Invalid signature");
}
}
```
```python Python theme={null}
import hashlib
import hmac
def verify_signature(secret: str, raw_body: bytes, header: str) -> None:
parts = dict(p.split("=", 1) for p in header.split(","))
key = bytes.fromhex(secret.removeprefix("whsec_"))
payload = parts["t"].encode() + b"." + raw_body
expected = hmac.new(key, payload, hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, parts["v1"]):
raise ValueError("Invalid signature")
```
```go Go theme={null}
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"errors"
"strings"
)
func verifySignature(secret string, rawBody []byte, header string) error {
parts := make(map[string]string)
for _, p := range strings.Split(header, ",") {
if kv := strings.SplitN(p, "=", 2); len(kv) == 2 {
parts[kv[0]] = kv[1]
}
}
key, err := hex.DecodeString(strings.TrimPrefix(secret, "whsec_"))
if err != nil {
return err
}
mac := hmac.New(sha256.New, key)
mac.Write([]byte(parts["t"] + "."))
mac.Write(rawBody)
expected := hex.EncodeToString(mac.Sum(nil))
if !hmac.Equal([]byte(expected), []byte(parts["v1"])) {
return errors.New("invalid signature")
}
return nil
}
```
***
## 4. Handling Webhook Events
Your endpoint should:
* Accept **HTTP POST** requests with a `Content-Type: application/json` header.
* Parse the incoming payload.
* Validate the event type and handle it accordingly.
* Respond with HTTP `200 OK` to acknowledge receipt.
Example Node.js handler:
```javascript theme={null}
import express from "express";
const app = express();
app.use(express.json());
app.post("/dataspike/webhook", (req, res) => {
const event = req.body;
console.log("Received Dataspike event:", event.event);
if (event.event === "DOCVER_EVENT") {
// Handle verification result
} else if (event.event === "AML_SCREENING") {
// Handle AML hit
}
res.sendStatus(200);
});
app.listen(3000, () => console.log("Listening for webhooks on port 3000"));
```
***
## 5. Accessing Webhook History
Dataspike keeps a complete history of all webhook deliveries.
From the **Dashboard → API → Webhooks** section, you can:
* View **successful** and **failed** webhook attempts.
* Inspect **delivery payloads and response codes**.
* **Replay** failed deliveries manually.
This gives you full visibility and control over event delivery reliability.
***
## 6. Testing and Troubleshooting
Before going live:
1. Test your webhook endpoint in the **Sandbox** environment.
2. Use the **“Send Test”** button in the dashboard to ensure it works.
3. Check logs in your app for failed requests or response timeouts.
4. Confirm you return an HTTP `200` within a few seconds of receipt.
If events are not delivered:
* Verify your server accepts **HTTPS POST** requests.
* Ensure your endpoint is reachable from public networks.
* Review **Dashboard → API → Webhooks → History** for error details.
***
## Summary
Once configured, webhooks let you:
* Receive instant updates on verification, AML, and KYT events.
* Automate user state updates or trigger downstream processes.
* Monitor and replay webhook events from the dashboard.
***
## Next steps
* [**First Document Verification**](/document-verification/first-verification) — create applicants and run your first check.
* [**Web SDK Overview**](/web-sdk) — embed document verification into your product.
* [**AML Screening**](/aml-screening) — automate sanctions and watchlist checks.