Appearance
POST /apiv2/screening
Pesan penapisan (screening) AML untuk alamat blockchain. Ini adalah kontrak versi 2: satu bentuk respons untuk setiap penyedia dan setiap status pesanan, angka desimal sebagai string, dan format kesalahan tunggal.
Metode ini menggantikan POST /apiv2/aml, yang tetap berfungsi dan tidak akan dihapus tanpa pemberitahuan.
URL Endpoint
POST https://netts.io/apiv2/screeningHeader Permintaan
| Header | Wajib | Deskripsi |
|---|---|---|
| Content-Type | Ya | application/json |
| X-API-KEY | Ya | Kunci API Anda dari dasbor Netts |
| X-Idempotency-Key | Tidak | Kunci Anda sendiri untuk pengulangan (retry) yang aman. Lihat Idempotensi |
Isi Permintaan
json
{
"address": "YOUR_ADDRESS_HERE",
"network": "trx",
"provider": "elliptic",
"wait_for_result": true
}Parameter
| Parameter | Tipe | Wajib | Deskripsi |
|---|---|---|---|
| address | string | Ya | Alamat yang akan ditapis, 10–128 karakter |
| network | string | Ya | Ticker jaringan. Ticker yang dicakup oleh masing-masing penyedia dicantumkan oleh GET /apiv2/screening/providers; tabel lengkap jaringan beserta namanya ada di sini |
| provider | string | Ya | elliptic atau bitok. Tidak ada nilai default |
| wait_for_result | boolean | Tidak | true menunggu hasil hingga 15 detik. Default false |
| language | string | Tidak | Bahasa laporan. Hanya en |
Field yang tidak dikenal akan ditolak. Body yang memuat field yang tidak ada dalam tabel di atas akan mengembalikan 400 dengan kode 4001. Pada versi 1 field yang tidak dikenal diabaikan secara diam-diam, dan kesalahan ketik pada wait membuat pemanggil menunggu hasil yang tidak akan pernah tiba secara sinkron.
provider wajib diisi dan tidak memiliki nilai default. Pada versi 1 penyedia yang diabaikan berarti Elliptic, sehingga pemanggil yang tidak memilih tetap membayar untuk penyedia yang tidak pernah mereka sebutkan.
provider adalah string bebas dalam skema, bukan sebuah enumerasi. Saat ini dua nilai diterima; penyedia ketiga tidak boleh menjadi perubahan yang merusak (breaking change) bagi siapa saja yang memvalidasi respons terhadap skema. Daftar saat ini, jaringan yang dicakup masing-masing penyedia, dan skala penilaian masing-masing penyedia berasal dari GET /apiv2/screening/providers.
Contoh Permintaan
cURL — tunggu hasilnya
bash
curl -X POST https://netts.io/apiv2/screening \
-H "Content-Type: application/json" \
-H "X-API-KEY: your_api_key" \
-d '{
"address": "YOUR_ADDRESS_HERE",
"network": "trx",
"provider": "elliptic",
"wait_for_result": true
}'cURL — terima dan lakukan polling
bash
curl -X POST https://netts.io/apiv2/screening \
-H "Content-Type: application/json" \
-H "X-API-KEY: your_api_key" \
-d '{
"address": "YOUR_ADDRESS_HERE",
"network": "trx",
"provider": "bitok"
}'Responsnya adalah 202 Accepted dengan header Location yang mengarah ke pesanan tersebut.
Python
python
import requests
from decimal import Decimal
url = "https://netts.io/apiv2/screening"
headers = {
"Content-Type": "application/json",
"X-API-KEY": "your_api_key",
}
payload = {
"address": "YOUR_ADDRESS_HERE",
"network": "trx",
"provider": "elliptic",
"wait_for_result": True,
}
response = requests.post(url, headers=headers, json=payload)
if response.status_code in (200, 202):
body = response.json()
print("Order:", body["order"]["client_order_id"])
print("Status:", body["order"]["status"])
if body["risk"]["score"] is not None:
# Parse provider numbers as Decimal, never as float
print("Score:", Decimal(body["risk"]["score"]), "of", body["risk"]["scale"]["max"])
print("Level:", body["risk"]["level"], "-", body["risk"]["level_source"])
print("Charged:", body["billing"]["charged_amount"], body["billing"]["charged_currency"])
else:
problem = response.json()
print(problem["code"], problem["title"], "-", problem["detail"])Kode Respons
| Situasi | Kode | Header |
|---|---|---|
| Pesanan dibuat, penapisan berjalan di latar belakang | 202 Accepted | Location: /apiv2/screening/{client_order_id} |
Hasil ada di dalam respons (wait_for_result) | 200 OK | — |
| Hasil digunakan kembali dari pemeriksaan baru-baru ini, tidak ada biaya | 200 OK | — |
| Alamat tidak memiliki aktivitas blockchain, tidak ada biaya | 200 OK | — |
| Kesalahan | lihat Kesalahan | Content-Type: application/problem+json |
Respons yang membawa hasil penapisan dikirim dengan Cache-Control: private, no-store.
Respons
json
{
"schema_version": 2,
"order": {
"client_order_id": "A90D21F68C9AEA2",
"status": "completed",
"api_version": "v2",
"cache_hit": false,
"created_at": "2026-09-13T08:14:29.614988Z",
"started_at": "2026-09-13T08:14:30.288251Z",
"completed_at": "2026-09-13T08:14:34.993174Z"
},
"request": {
"address": "YOUR_ADDRESS_HERE",
"network": "trx",
"provider": "elliptic"
},
"billing": {
"charged": true,
"price_usdt": "0.98",
"base_amount": "2.882421",
"markup_amount": "0",
"charged_amount": "2.882421",
"charged_currency": "TRX",
"exchange_rate": "0.33999200",
"payment_status": "pending"
},
"precheck": {
"activity_checked": true,
"activity_status": "active",
"source": "tron-address-checker"
},
"check": {
"provider": "elliptic",
"provider_check_id": "b4903a5d-9f22-4075-b51b-beb40b419b97",
"checked_at": "2026-09-13T08:14:32.144000Z",
"status": "completed",
"provider_status": "complete"
},
"risk": {
"score": "0.9634087310611608",
"scale": { "min": 0, "max": 10 },
"level": "low",
"level_source": "computed",
"provider_level": null,
"policy": "netts-risk-v1",
"by_direction": {
"source": "0.12332156899576921",
"destination": "0.9634087310611608"
}
},
"sanctions": {
"self": false,
"self_entities": null,
"exposure": {
"entity": "HTX (formerly Huobi) - Post-Sanctions - UK Sanctions List - 26 May 2026",
"category": "Exchange",
"share_fraction": "0.004409451392423414",
"proximity": "indirect",
"hops": 3,
"direction": "source",
"rule_name": "Sanctioned, TF & CSAM"
},
"items": []
},
"exposure": [
{
"category": "Exchange",
"share_fraction": "0.1888065152442461",
"direction": "source",
"hops": 2,
"is_screened_address": false,
"entities": [
{ "name": "Binance", "category": "Exchange", "is_vasp": true,
"is_primary": true, "is_after_sanction_date": null }
]
}
],
"rules": [
{ "name": "Sanctioned, TF & CSAM", "type": "exposure", "level": null,
"score": "0.061426483114820525", "share_fraction": null, "category": null,
"proximity": null, "direction": "source", "detected_at": null }
],
"entities": [
{ "name": "Unknown", "category": "Unknown", "is_vasp": null,
"is_primary": true, "is_after_sanction_date": false }
],
"primary_entity": {
"name": "Unknown", "category": "Unknown", "is_vasp": null,
"is_primary": true, "is_after_sanction_date": false
},
"sanctioned_entities": [],
"wallet": {
"inflow_usd": "354663.6116669158",
"outflow_usd": "80248.63081808022"
},
"provider_data": { }
}Kumpulan field tidak pernah berubah
Setiap blok yang tercantum di atas selalu ada dalam setiap respons, apa pun penyedianya dan apa pun status pesanannya. Data yang tidak disediakan oleh penyedia bernilai null; daftar yang kosong bernilai [], bukan null; blok yang belum memiliki data diisi dengan nilai null daripada dihilangkan. Satu parser dapat menangani pemeriksaan yang baru saja diterima maupun pemeriksaan yang sama setelah selesai.
Dua konsekuensi untuk kode Anda:
- abaikan field yang tidak Anda ketahui. Field baru ditambahkan ke blok-blok ini tanpa versi baru. Menolak field yang tidak dikenal adalah kesalahan di sisi Anda, bukan kami;
provider_databukan bagian dari kontrak. Strukturnya mengikuti penyedia, dan berubah saat penyedia berubah. Segala hal yang dijamin oleh kontrak berada di blok-blok di atas.
order
| Field | Tipe | Deskripsi |
|---|---|---|
| client_order_id | string | Pengidentifikasi pesanan, digunakan untuk membaca hasil nanti |
| status | string | pending, processing, completed, skipped, failed |
| api_version | string | Kontrak yang membuat pesanan |
| cache_hit | boolean | true ketika hasil terbaru digunakan kembali dan tidak ada biaya yang dikenakan |
| created_at | string | RFC 3339, UTC, mikrodetik |
| started_at | string | null | Saat pemanggilan ke penyedia dimulai. null untuk skipped |
| completed_at | string | null | Saat hasil diterima |
| error | string | Hanya untuk failed: alasan kegagalan |
| reason | string | Hanya untuk skipped: address_inactive |
Semua stempel waktu menggunakan UTC, RFC 3339, dengan akhiran Z dan presisi mikrodetik.
billing
| Field | Tipe | Deskripsi |
|---|---|---|
| charged | boolean | Apakah saldo dipotong |
| price_usdt | string | Harga resmi (list price) penyedia dalam USDT |
| base_amount | string | Harga pesanan dalam mata uang yang ditagihkan, tanpa markup sub-pengguna |
| markup_amount | string | Markup sub-pengguna. "0" untuk akun langsung |
| charged_amount | string | Jumlah yang sebenarnya dipotong dari saldo |
| charged_currency | string | TRX |
| exchange_rate | string | null | Kurs yang digunakan untuk konversi |
| payment_status | string | paid, pending, failed, not_charged |
payment_status bernilai pending untuk waktu singkat setelah pemeriksaan berhasil: biaya awalnya ditahan dan diselesaikan dalam waktu satu jam. failed berarti dana telah dikembalikan. not_charged berarti tagihan tidak pernah dibuat — hasil yang digunakan kembali atau alamat yang dilewati.
precheck
Sebelum penapisan berbayar, alamat diperiksa aktivitas blockchain-nya. Alamat tanpa aktivitas tidak dikirim ke penyedia dan tidak dikenakan biaya.
| Field | Tipe | Deskripsi |
|---|---|---|
| activity_checked | boolean | Apakah pemeriksaan dijalankan. false pada jaringan di mana fitur ini tidak ada |
| activity_status | string | active, inactive, unknown |
| source | string | null | Nama mekanisme |
unknown tidak menghentikan penapisan berbayar: jika layanan aktivitas tidak tersedia, alamat akan diperlakukan sebagai aktif.
check
| Field | Tipe | Deskripsi |
|---|---|---|
| provider | string | Penyedia yang melakukan pemeriksaan |
| provider_check_id | string | null | Pengidentifikasi milik penyedia itu sendiri — sebutkan ini saat menyanggah hasil kepada mereka |
| checked_at | string | null | Kapan penyedia menghasilkan hasil tersebut |
| status | string | Lihat tabel di bawah |
| provider_status | string | null | Redaksi asli dari penyedia, tanpa perubahan |
order.status | check.status | Arti |
|---|---|---|
pending | pending | Pesanan diterima, belum dimulai |
processing | running | Penyedia sedang memprosesnya |
completed | completed | Hasil diterima |
failed | failed | Ditolak sebelum atau selama panggilan ke penyedia |
skipped | not_performed | Alamat tidak memiliki aktivitas; penyedia tidak pernah dipanggil dan tidak ada biaya |
risk
| Field | Tipe | Deskripsi |
|---|---|---|
| score | string | null | Skor milik penyedia itu sendiri, sebagai string desimal |
| scale | object | Nilai min dan max dari skala penyedia tersebut |
| level | string | none, low, medium, high, severe |
| level_source | string | computed — kami menetapkan level dari skor; provider — penyedia yang menyatakannya |
| provider_level | string | null | Istilah milik penyedia itu sendiri, jika menyediakannya |
| policy | string | Nama kebijakan ambang batas (threshold), netts-risk-v1 |
| by_direction | object | Skor yang dipecah menjadi source dan destination, jika penyedia memecahnya |
Skor tidak pernah diskalakan ulang. Elliptic berjalan pada 0–10 dan BitOK berjalan pada 0–1, dan nilai 7 pada satu skala bukanlah 0.7 pada skala lainnya dalam arti apa pun yang bermakna. Skala disertakan dalam respons agar integrasi yang ditulis untuk satu penyedia tidak salah membaca penyedia lain setelah adanya perubahan konfigurasi tunggal.
Tingkat (level) menggunakan satu kosakata di seluruh API. Di mana penyedia menyatakan levelnya sendiri, kami meneruskannya dan menyatakannya di level_source; jika tidak, kami menetapkan level dari skor dengan ambang batas netts-risk-v1 dan menyatakan hal tersebut. Kata yang sama muncul di respons API, dasbor, dan laporan PDF untuk pemeriksaan yang sama.
exposure[], rules[], entities[]
exposure[] memecah dana berdasarkan kategori pihak lawan (counterparty). rules[] mencantumkan aturan penyedia yang terpicu. entities[] mencantumkan entitas tempat alamat itu sendiri berada; primary_entity memilih salah satunya berdasarkan aturan tetap — entitas yang ditandai penyedia sebagai utama, jika tidak ada maka yang pertama, jika tidak ada maka null. sanctioned_entities[] menampung entitas dari entities[] yang ditandai aktif setelah tanggal sanksi.
Porsi adalah pecahan, bukan persentase
Setiap porsi dalam respons adalah satu field tunggal, share_fraction, sebuah string desimal antara "0" dan "1".
text
Elliptic melaporkan 31.574732212596924 % -> "share_fraction": "0.31574732212596924"
BitOK melaporkan 0.8488 -> "share_fraction": "0.8488"Penyedia memiliki perbedaan satuan: sepertiga exposure yang sama muncul sebagai 31.57 dari satu pihak dan 0.3157 dari pihak lainnya. Field tunggal yang memuat keduanya tidak mungkin dibaca tanpa mengetahui penyedianya. Angka milik penyedia dalam satuannya sendiri tetap berada di provider_data.
Angka adalah string
Setiap angka yang berasal dari penyedia — skor, porsi, volume USD, dan setiap jumlah dalam billing — adalah string desimal.
json
"score": "0.9634087310611608"Mem-parsing nilai tersebut sebagai angka JSON di JavaScript, Go, atau bahasa lain dengan floating point biner akan menghasilkan nilai perkiraan, dan nilai yang Anda cetak tidak lagi cocok dengan nilai yang dikeluarkan penyedia. Parsing field-field ini dengan tipe desimal: Decimal di Python, BigDecimal di Java, decimal.Decimal atau string di JavaScript.
Field yang merupakan milik kami dan bukan milik penyedia — scale.min, scale.max, hops — adalah angka JSON biasa.
Menggunakan kembali hasil terbaru
Ketika Anda menapis alamat, jaringan, dan penyedia yang sama lagi dalam 60 detik, hasil sebelumnya akan dikembalikan dan tidak ada biaya yang dikenakan.
Setiap permintaan tetap membuat pesanannya sendiri dengan client_order_id masing-masing; hasil yang digunakan kembali ditandai "cache_hit": true dan blok billing-nya melaporkan "charged": false dengan "payment_status": "not_charged". Pengidentifikasi pesanan asal hasil tersebut tidak diungkapkan — data tersebut mungkin milik akun lain.
Penggunaan kembali hanya terjadi dalam satu akun. Hasil yang ditapis oleh pihak lain tidak akan pernah dikembalikan kepada Anda.
Idempotensi
Kirim X-Idempotency-Key dengan nilai milik Anda sendiri untuk membuat percobaan ulang (retry) menjadi aman: kunci yang sama dengan body yang sama akan mengembalikan respons yang tersimpan alih-alih memesan pemeriksaan kedua.
| Situasi | Kode | Respons |
|---|---|---|
| Permintaan pertama dengan kunci ini masih berjalan | 409 | 4090 |
| Kunci yang sama, body permintaan yang berbeda | 409 | 4093 |
| Kunci yang sama, body yang sama, sudah selesai | kode yang tersimpan | respons yang tersimpan |
Jika Anda tidak mengirimkan header tersebut, sebuah kunci akan dibuatkan untuk Anda dari kunci API, alamat, penyedia, dan alamat IP Anda, dalam jangka waktu dua detik. Kunci ini melindungi dari klik ganda dan pengulangan gateway, bukan dari pengulangan satu menit kemudian: permintaan yang terakhir adalah pesanan baru yang sesungguhnya dan dikenakan biaya.
Kunci dicakup per endpoint. Nilai yang sama yang dikirim ke POST /apiv2/aml dan ke endpoint ini adalah dua janji independen mengenai dua permintaan yang berbeda — bodynya berbeda, begitu pula responsnya. Menggunakan kembali kunci Anda saat memindahkan integrasi dari versi 1 ke versi 2 adalah aman: ini tidak mengembalikan respons versi 1 maupun dihitung sebagai kunci yang sama yang digunakan dengan body berbeda.
Kesalahan
Setiap kesalahan yang dihasilkan oleh aplikasi menggunakan RFC 9457 dengan Content-Type: application/problem+json:
json
{
"type": "https://doc.netts.io/api/v2/errors/insufficient-funds",
"title": "Insufficient funds",
"status": 403,
"detail": "Insufficient funds. Required: 2.888742 TRX, Available: 1.203000 TRX",
"instance": "/apiv2/screening",
"code": 1004,
"required_trx": "2.888742",
"available_trx": "1.203000"
}type, title, status, detail, dan instance adalah field standar. Nilai numerik code dipertahankan sebagai ekstensi agar integrasi yang ditulis untuk versi 1 dapat terus mencocokkannya. Field tambahan bergantung pada jenis kesalahan dan harus diabaikan jika Anda tidak mengetahuinya.
| Kode | HTTP | Arti |
|---|---|---|
4000 | 400 | Body bukan JSON yang valid |
4001 | 400 | Ada field yang gagal divalidasi, atau field yang tidak dikenal terkirim |
4002 | 403 | Penyedia tidak tersedia untuk akun Anda |
4003 | 400 | Pengidentifikasi pesanan tidak sesuai format |
4004 | 400 | Penyedia tidak mendukung jaringan yang diminta |
4010 | 401 | Tidak ada kunci API |
4011 | 401 | Kunci API atau alamat IP tidak diterima |
4040 | 404 | Pesanan tidak ditemukan |
4041 | 404 | Akun tidak ditemukan |
4090 | 409 | Permintaan dengan kunci idempotensi ini masih berjalan |
4091 | 409 | Permintaan duplikat |
4093 | 409 | Kunci idempotensi ini digunakan dengan body yang berbeda |
1004 | 403 | Saldo tidak mencukupi |
5000 | 500 | Kesalahan internal |
5001 | 500 | Pemotongan biaya gagal diproses |
5002 | 500 | Pesanan tidak terbuat |
5030 | 503 | Penyedia tidak tersedia |
Kesalahan yang tidak menggunakan format ini
Beberapa kegagalan terjadi di tingkat gateway, sebelum mencapai aplikasi, dan kegagalan tersebut tetap memakai format bawaan gateway. Perlakukan respons apa pun yang Content-Type-nya bukan application/problem+json sebagai salah satu dari kondisi berikut:
| Situasi | HTTP | Body |
|---|---|---|
| Tidak ada kunci API, atau kunci tidak diterima | 401 | {"detail":{"code":-1,"msg":"Invalid or missing API key"}} |
| Batas laju terlampaui | 429 | {"message":"API rate limit exceeded"} |
| Path tidak diketahui, atau metode yang tidak dilayani oleh rute tersebut | 404 / 405 | {"detail":"Method Not Allowed"} |
Batas Laju
Batas ini digunakan bersama dengan POST /apiv2/aml dan path AML lainnya: 5 permintaan per detik dan 150 per menit. Beralih ke endpoint ini tidak memberi Anda kuota tambahan kedua.
Catatan
- Harga: Elliptic $0.98, BitOK $0.50 per pemeriksaan, dipotong dari saldo TRX sesuai kurs pada saat pemotongan biaya.
- Waktu pemrosesan: sebagian besar pemeriksaan selesai dalam beberapa detik; alamat dengan riwayat transaksi panjang dapat memakan waktu hingga tiga menit. Gunakan mode asinkron dan baca hasilnya dengan GET /apiv2/screening/{client_order_id}.
- Alamat tidak aktif mengembalikan
skippeddan tidak dikenakan biaya. - Respons mentah penyedia tidak pernah dikembalikan.
provider_dataadalah proyeksi yang telah ditinjau; field milik akun kami di sisi penyedia dan bukan milik alamat yang ditapis tidak akan dipublikasikan kepada siapa pun.
Laporan
Endpoint ini mengembalikan JSON dan tidak ada format lainnya. Tidak tersedia PDF maupun Markdown.
Semua bagian pembentuk laporan sudah ada di dalam respons: blok yang telah disatukan dan provider_data. Melakukan rendering di sisi Anda akan memberikan dokumen yang benar-benar Anda inginkan — branding Anda, bahasa Anda, tata letak Anda — hal ini sangat relevan jika Anda menjual kembali hasil pemeriksaan, karena laporan yang memuat nama kami bukanlah dokumen yang tepat untuk diserahkan ke pelanggan Anda sendiri.
Jika Anda memerlukan laporan sebagai bukti untuk pihak ketiga — bank, regulator, mitra transaksi — perlu diingat bahwa PDF yang tidak ditandatangani bukanlah bukti sah siapa pun yang membuatnya: dokumen tersebut dapat diubah di editor teks dalam satu menit. Artefak yang dapat diverifikasi memerlukan tanda tangan digital atau halaman verifikasi publik, dan itu adalah fitur yang berbeda. Jika itu kebutuhan Anda, beri tahu kami apa saja yang disyaratkan oleh mitra transaksi Anda.
Laporan PDF yang dapat dibaca manusia memang tersedia untuk pemeriksaan yang sama di dasbor Netts, dalam tujuh belas bahasa.
Lihat Juga
- GET /apiv2/screening/{client_order_id} — baca pemeriksaan
- GET /apiv2/screening/history — pemeriksaan Anda, dengan paginasi kursor
- GET /apiv2/screening/providers — penyedia, harga, jaringan, skala
- GET /apiv2/screening/price — harga untuk satu penyedia
- Sanksi dalam hasil AML — informasi yang disampaikan
sanctionsdan flag penyedia