iChek Developer Guide
One account and one set of API keys for every iChek product.
Address API
Make sure every customer gives you a delivery address that actually works. iChek fills in city and state from the PIN code, catches wrong or incomplete addresses while the customer is still typing, and returns a clean, standardized address you can ship to.
Create a free accountTry the live demoQuick start
- Create an account. The free plan includes 1,000 checks a month.
- Open Install & settings in your dashboard. You get two keys:
pk_…is the publishable key. It's safe to put in web pages and is used by the widget.sk_…is the secret key. It's for your server only. Never put it in a web page or app.
- Pick how you connect:
You have… Use Coding needed Any website checkout (custom, Magento, OpenCart…) Checkout widget: one script tag Paste one line WooCommerce WordPress plugin None Your own backend or mobile app REST API A few lines A spreadsheet of existing addresses Bulk upload in the dashboard None - Before going live, add your website domain under Allowed website domains so your publishable key only works on your site.
Checkout widget
Paste this on your checkout or address page, just before </body>. Use your own publishable key:
<script src="https://api.ichek.info/widget.js" data-key="pk_YOUR_KEY" async></script>
That's all. The widget finds the address fields on the page by itself, including forms that appear later, like WooCommerce's. Then it:
- fills city and state as soon as a valid PIN code is typed (it works with text boxes and state drop-downs)
- checks the address when the customer leaves a field, and shows hints right under the address box
- offers a "Use this address" button with the cleaned-up version
- adds hidden fields to your form so your server receives the result:
ichek_status,ichek_score,ichek_check_id
Options
| Attribute | What it does |
|---|---|
data-block-invalid="true" | If the address is INVALID, the customer must fix it or click "Continue anyway" before the form submits. |
data-lang="hi" | Shows the widget's own labels in Hindi. |
data-auto="false" | Turns off auto-detection. Tell the widget your fields yourself (below). |
Point it at your fields (optional)
<script src="https://api.ichek.info/widget.js" data-key="pk_YOUR_KEY" data-auto="false"></script>
<script>
iChek.attach({
pincode: '#zip', city: '#city', state: '#state',
line1: '#address1', line2: '#address2', landmark: '#landmark', phone: '#phone',
onResult: function (r) { console.log(r.status, r.score, r.standardized.full); }
});
</script>
Every check also fires an ichek:result event on the form, with the result in event.detail.
WooCommerce plugin
- Download ichek-woocommerce.zip.
- In WordPress, go to Plugins → Add New → Upload Plugin, choose the zip, then Install and Activate.
- Go to WooCommerce → iChek and paste your publishable key. The iChek URL is already filled in. Click Save.
The widget now runs on your checkout and cart pages. Each order stores the address check result, which shows in the order screen under the shipping address.
REST API
| Base URL | https://api.ichek.info/api/v1 |
|---|---|
| Authentication | Header X-API-Key: sk_YOUR_SECRET_KEY |
| Format | JSON in, JSON out (UTF-8) |
| Interactive reference | https://api.ichek.info/docs (try calls in the browser) |
POST/address/verify
Checks one address and returns a score, the problems found, hints to show the customer and a standardized address.
| Field | Required | Description |
|---|---|---|
line1 | yes | House / flat number, building, street |
line2 | Area / locality | |
landmark | Nearby landmark | |
city | City / town (filled from the PIN if empty) | |
state | State name or code (KA, Karnataka…). Filled from the PIN if empty. | |
pincode | yes (India) | 6-digit PIN code |
phone | Mobile number. +91 and spaces are fine. | |
country | ISO code. Defaults to IN. Other countries get basic checks only. |
curl -X POST https://api.ichek.info/api/v1/address/verify \
-H "X-API-Key: sk_YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{"line1":"flat 204 lotus towers, 5th cross","line2":"indiranagar",
"city":"Bangalore","state":"KA","pincode":"560038","phone":"+91 98200 13457"}'
<?php
$ch = curl_init('https://api.ichek.info/api/v1/address/verify');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['X-API-Key: sk_YOUR_SECRET_KEY', 'Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode([
'line1' => 'flat 204 lotus towers, 5th cross', 'line2' => 'indiranagar',
'city' => 'Bangalore', 'state' => 'KA', 'pincode' => '560038', 'phone' => '+91 98200 13457',
]),
]);
$r = json_decode(curl_exec($ch), true);
if ($r['status'] === 'INVALID') {
// ask the customer to fix: implode("\n", $r['suggestions'])
}
echo $r['standardized']['full'];
import requests
r = requests.post(
"https://api.ichek.info/api/v1/address/verify",
headers={"X-API-Key": "sk_YOUR_SECRET_KEY"},
json={"line1": "flat 204 lotus towers, 5th cross", "line2": "indiranagar",
"city": "Bangalore", "state": "KA", "pincode": "560038", "phone": "+91 98200 13457"},
timeout=10,
).json()
print(r["status"], r["score"], r["standardized"]["full"])
for hint in r["suggestions"]:
print("-", hint)
// Node 18+ (server side; never expose sk_ keys in the browser)
const res = await fetch("https://api.ichek.info/api/v1/address/verify", {
method: "POST",
headers: { "X-API-Key": "sk_YOUR_SECRET_KEY", "Content-Type": "application/json" },
body: JSON.stringify({ line1: "flat 204 lotus towers, 5th cross", line2: "indiranagar",
city: "Bangalore", state: "KA", pincode: "560038", phone: "+91 98200 13457" }),
});
const r = await res.json();
console.log(r.status, r.score, r.standardized.full);
Response: a good address
{
"score": 95,
"status": "VERIFIED",
"standardized": {
"line1": "Flat 204 Lotus Towers, 5th Cross",
"line2": "Indiranagar",
"landmark": "",
"city": "Bangalore",
"state": "Karnataka",
"pincode": "560038",
"country": "IN",
"full": "Flat 204 Lotus Towers, 5th Cross, Indiranagar, Bangalore, Karnataka 560038"
},
"issues": [
{ "code": "NO_LANDMARK", "severity": "info", "message": "No landmark given", "penalty": 5 }
],
"suggestions": [ "Add a nearby landmark (helps the courier)" ],
"pincode_info": { "pin": "560038", "found": true, "state": "Karnataka",
"districts": ["Bengaluru"], "offices": ["HAL II Stage", "Indiranagar"] },
"location": null,
"phone": "9820013457",
"phone_valid": true,
"country": "IN"
}
Response: an address that needs fixing ("line1": "near temple", "state": "Bihar", "pincode": "560038")
{
"score": 40,
"status": "INVALID",
"issues": [
{ "code": "STATE_PIN_MISMATCH", "severity": "error", "message": "PIN 560038 belongs to Karnataka, not Bihar", "penalty": 25 },
{ "code": "LINE_SHORT", "severity": "warn", "message": "Address is very short", "penalty": 20 },
{ "code": "NO_HOUSE_NO", "severity": "warn", "message": "No house / flat / plot number", "penalty": 15 }
],
"suggestions": [
"PIN and state don't match — is the PIN or the state wrong? (Karnataka)",
"Add building, street and area",
"Add house or flat number"
],
...
}
GET/pincode/{pin}
Looks up a PIN code and returns its state, districts and post offices. Useful for building your own auto-fill.
curl https://api.ichek.info/api/v1/pincode/560038 -H "X-API-Key: sk_YOUR_SECRET_KEY"
{ "pin": "560038", "valid_format": true, "found": true, "state": "Karnataka",
"districts": ["Bengaluru"], "offices": ["HAL II Stage", "Indiranagar"], "source": "api" }
found is false when the PIN doesn't exist. It is null when only the PIN's range could be checked, because the lookup service was unreachable.
Response fields
| Field | Meaning |
|---|---|
score | 0–100. Starts at 100, and each problem subtracts its penalty. |
status | VERIFIED (80+): ship it · NEEDS_REVIEW (50–79): show the hints · INVALID (<50): ask the customer to fix it |
standardized | The cleaned address: proper capitalization, abbreviations expanded (opp → Opposite, rd → Road), canonical state name, city/state filled from the PIN, and full as one line. |
issues[] | Each problem: code, severity (error / warn / info), message, penalty |
suggestions[] | Short, customer-friendly hints, ready to show on screen. |
pincode_info | What we know about the PIN code (state, districts, post offices). |
location | {lat, lon, precision} when available, otherwise null. |
phone, phone_valid | The normalized 10-digit mobile number, and whether it's a valid Indian mobile. |
Issue codes
| Code | Severity | Meaning |
|---|---|---|
PIN_MISSING / PIN_FORMAT | error | No PIN, or not 6 digits |
PIN_NOT_FOUND | error | The PIN doesn't exist in India Post records |
STATE_PIN_MISMATCH | error | The state doesn't match the PIN code |
JUNK_TEXT | error | Test or placeholder text ("test", "asdf", "xyz"…) |
LINE_MISSING | error | No street address |
CITY_PIN_MISMATCH | warn | The city isn't in the PIN code's area |
LINE_SHORT / LINE_FEW_WORDS | warn | The address is too short to find |
NO_HOUSE_NO | warn | No house / flat / plot number |
STATE_UNKNOWN, CITY_MISSING, PHONE_INVALID | warn | Unrecognized state, empty city, or invalid mobile |
NO_LANDMARK, STATE_FILLED, CITY_FILLED, PIN_UNVERIFIED | info | Hints and auto-fills |
Bulk CSV / Excel
In the dashboard, go to Bulk upload → Addresses and upload a .csv or .xlsx file of up to 5,000 rows. Column names are matched flexibly ("Pin Code", "Mobile", "Address 1", Shopify/WooCommerce exports…). You get a score, status, standardized address and hints for every row, and can download the results as CSV.
From code: POST /api/v1/bulk/verify with multipart fields file and mode=addresses.
Errors & limits
| HTTP | Meaning | What to do |
|---|---|---|
| 401 | Missing or wrong key | Check X-API-Key (server) or data-key (widget) |
| 402 | Monthly quota used up | Upgrade your plan, or wait for next month |
| 403 | Domain not allowed, or Pro feature | Add your domain in Settings, or upgrade to Pro |
| 429 | Too many requests from one IP (widget: 60 per minute) | Slow down and retry |
| 400 / 422 | Bad input | The response body says which field |
Identity verification (KYC)
Confirm that a customer is who they say they are, using a photo of their Aadhaar, PAN card, passport, driving licence or voter ID and a live selfie. iChek reads the card, checks the number rules, matches the selfie against the photo on the card and gives you a decision with the reasons. Every check records the customer's consent (time, IP address, device and, if allowed, location).
See what your customer sees: try the KYC demo with your own ID. Nothing is saved.
| Check | What it does |
|---|---|
| Document number | Aadhaar check digit (Verhoeff), PAN structure and holder type, passport MRZ check digits and expiry, driving licence and EPIC format |
| Aadhaar Secure QR | Reads the QR code on the card: name, date of birth and photo, signed by UIDAI. Only the last 4 digits of an Aadhaar number are stored. |
| Face match | Compares the live selfie with the photo on the card and returns a similarity score |
| Name & date of birth | Compares what's printed on the card with the name and DOB you send |
| Photo quality | Blur, glare and darkness. Customers are asked to retake bad photos before they submit. |
| Re-used documents | Warns if the same document was already used in another of your verifications |
Decisions: VERIFIED (all checks passed), REVIEW (a person should look: open it in the dashboard and Approve or Reject), FAILED (a critical check failed). After a review the status becomes APPROVED or REJECTED. Links that are never used become EXPIRED.
POST/kyc/sessions
The easiest way. Create a link, then send it to the customer by SMS, WhatsApp or email (the dashboard has buttons for this). The customer opens it on their phone, gives consent, photographs their ID with the camera and takes a selfie. No app to install.
| Field | Required | Description |
|---|---|---|
reference | Your customer or application ID | |
full_name | Name to match against the card | |
dob | Date of birth to match, YYYY-MM-DD | |
id_types | Documents the customer may use: aadhaar, pan, passport, dl, voter. Default: all. | |
purpose | Shown in the consent text, e.g. "opening your account" | |
phone, email | For your records | |
callback_url | Webhook for the result (otherwise the one in Settings is used) | |
redirect_url | Where the customer is sent when they finish | |
video | true adds a recorded video call with an agent after the photos (video KYC) |
curl -X POST https://api.ichek.info/api/v1/kyc/sessions \
-H "X-API-Key: sk_YOUR_SECRET_KEY" -H "Content-Type: application/json" \
-d '{"reference":"CUST-1042","full_name":"Asha Mehta","dob":"1990-04-12",
"id_types":["aadhaar","pan"],"purpose":"opening your account"}'
{"id":"kyc_3f9c…","status":"CREATED","url":"https://api.ichek.info/verify/Xb7…","expires_at":"…"}
Links work for 72 hours and can be used once. The customer gets 3 tries at the photos.
POST/kyc/verify
If your own app already captures the photos, send them directly as multipart/form-data. You must have collected the customer's consent and send consent=true.
| Field | Required | Description |
|---|---|---|
id_type | yes | aadhaar, pan, passport, dl or voter |
front | yes | Photo of the ID (JPG/PNG, up to 8 MB) |
back | Back of the card (Aadhaar QR is usually here) | |
selfie | Live photo of the customer, for face match | |
full_name, dob, id_number, reference | Details to match against the card | |
consent | yes | true |
curl -X POST https://api.ichek.info/api/v1/kyc/verify -H "X-API-Key: sk_YOUR_SECRET_KEY" \
-F id_type=pan -F consent=true -F full_name="Asha Mehta" \
-F front=@pan.jpg -F selfie=@selfie.jpg
Results & webhook
| Endpoint | Use |
|---|---|
GET/kyc/sessions/{id} | Full result: status, checks[] (code, label, pass/warn/fail, detail), extracted (details read from the card, such as number, name and DOB), face.similarity, consent record and history |
GET/kyc/sessions | List, with ?status=REVIEW or ?q= name / reference |
POST/kyc/sessions/{id}/review | {"decision":"APPROVED | REJECTED","note":"…"} |
GET/kyc/sessions/{id}/report.pdf | Verification report with photos, checks and consent, for your files |
GET/kyc/sessions/{id}/files/{front|back|selfie} | The photos (until they are deleted) |
DELETE/kyc/sessions/{id}/data | Delete the photos now, e.g. when a customer asks. The result and audit trail are kept. |
| Webhook | When a customer finishes, iChek POSTs {"event":"kyc.completed","data":{…}} to your callback URL, signed with X-iChek-Signature (HMAC-SHA256 of the body using your secret key). Always confirm with GET /kyc/sessions/{id} before acting. |
Video KYC with an agent
Create the link with "video": true. After the ID photo and selfie, the customer's status becomes VIDEO and the same link takes them to a video call: they join the queue when an agent is online during your working hours, or book a time (we email the booking and a reminder when you send their email). Calls are taken by your own team in the dashboard's agent console, or by the iChek team if that is switched on for your account.
On the call the agent sees the submitted photos, the automatic checks and the customer's live location; asks random questions (with the answers to check against), gives liveness instructions that appear on the customer's screen, and captures the face (compared with the selfie and ID photo) and the original ID / PAN held up to the camera (read and compared with the submitted number). Approval is only possible once the required checklist items, questions, captures and recording are in place. The whole call is recorded with a burnt-in time stamp, stored encrypted, and fingerprinted (SHA-256).
| Where | What you get |
|---|---|
GET /kyc/sessions/{id} | video_calls[]: status, decision (APPROVED / REJECTED / RESCHEDULE / DISCONNECTED), agent, times and duration, location and whether it is in India, questions with the agent's marks, liveness prompts, checklist, capture results and recording (size, SHA-256) |
GET/vkyc/calls/{call_id}/recording | The recording (WebM), with your key or dashboard login |
GET/vkyc/calls/{call_id}/captures/{face|doc|pan} | Frames the agent captured |
| Webhook | kyc.completed is sent after the call, with video_calls (decision, agent, duration, recording SHA-256) |
| Second check | If you switch on "second check" in Video KYC settings, a video approval goes to REVIEW until another person approves it with {"decision":"APPROVED","reviewer":"Name"} |
Free accounts include 10 identity checks and 2 video calls a month; paid plans include more. A check is counted when photos are submitted. Over the limit, the API returns 402.
RTO risk scoring PRO
For COD-heavy stores. Every order gets a risk level (LOW / REVIEW / HIGH) with reasons. The score uses address quality, COD vs prepaid, order value, the customer's past RTOs and failed deliveries, one phone used with many addresses (or one address with many phones), the PIN code's RTO rate and bursts of orders. Your team works a review queue (call, then Verify or Reject) before dispatch. Courier outcomes flow back, so the rules can be tuned from real results.
| Endpoint | Use |
|---|---|
POST/order/score | Address fields plus order_id, customer_name, payment_method (COD/PREPAID) and order_value. Returns risk_level, risk_score and risk_reasons. |
POST/order/outcome | {"order_id":"1001","outcome":"delivered | ndr | rto | cancelled"} |
POST/order/verify | {"order_id":"1001","action":"verified | rejected","note":"called customer"} |
GET/orders/risk | Orders waiting for a decision, highest risk first |
| Shopify / WooCommerce | No code: paste the webhook URL from your dashboard into your store's webhook settings, and every new order is scored automatically. |
| Alerts | Set a callback URL. REVIEW/HIGH orders are POSTed to it and signed with X-iChek-Signature (HMAC-SHA256 of the body using your secret key). |
Downloads
- WooCommerce plugin (zip)
- OpenAPI specification (import into Postman, Insomnia, or code generators)
- Sample addresses CSV for trying bulk upload
- widget.js (source, no dependencies)
PIN code data: India Post. Questions? Contact us through ichek.info.