Documentation

API Reference

The FraudDetect API checks a customer's COD delivery history across all five major Bangladeshi couriers — Steadfast, Pathao, RedX, Paperfly, and Carrybee — and returns an aggregated success/cancel score in a single HTTP request. Use it to flag risky orders before dispatch.

Base URL

connect.chowdhury.bd

Protocol

HTTPS only

Format

JSON

What a single API call gives you

Delivery history from 5 couriers aggregated instantly
Aggregated success_ratio and cancel_ratio (0–100)
Per-courier breakdown: success, cancel, total counts
Sub-500ms response times with cached courier data
Consistent JSON schema across all couriers
Standard HTTP status codes and error messages

Quick Start

From zero to your first fraud check in under five minutes.

01

Create a free account

Register at frauddetect.chowdhury.bd/register. No credit card required.

02

Generate an API key

Go to your Dashboard, click New API Key, give it a name, and copy the key. Store it securely — it is shown only once.

03

Make your first request

Replace YOUR_API_KEY and send the request below. You'll get live courier data back immediately.

First request
curl -X POST https://connect.chowdhury.bd/api/v1/check \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"phone":"01711223344"}'

Expected response

A 200 OK with a JSON body containing per-courier stats and an aggregate object. See Response Schema for the full field reference.

Authentication

Every request must include your API key in the X-Api-Key request header. There is no OAuth or session-based authentication — the key itself is the credential.

Header nameX-Api-Key
LocationHTTP request header (not query string or body)
FormatRaw 40-character alphanumeric string
Authentication header
curl -X POST https://connect.chowdhury.bd/api/v1/check \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"phone":"01711223344"}'

Security tips

  • Never embed keys in client-side JavaScript
  • Store keys in environment variables, not config files
  • Rotate keys immediately if compromised
  • Use separate keys per environment (dev/staging/prod)

Key management

  • Create unlimited keys from the dashboard
  • Keys can be revoked instantly — no downtime
  • Label each key for easy identification
  • All keys share the same rate limit quota

Endpoint Reference

The API currently exposes a single endpoint. All requests go to the base URLhttps://connect.chowdhury.bd/api.

POST/v1/check
X-Api-Key headerapplication/json

Check phone fraud risk. Accepts a Bangladeshi mobile number and returns delivery history from all five supported couriers along with an aggregated risk score. Courier data is cached — typical response time is under 500ms.

Request Schema

The request body must be JSON with Content-Type: application/json.

FieldTypeRequiredDescription
phonestringYesBangladeshi mobile number in local 11-digit format. Must start with 01. Do not include country code (+88 or 88).
Request body
# Minimal
curl -X POST https://connect.chowdhury.bd/api/v1/check \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"phone":"01711223344"}'

Response Schema

A successful 200 OK response contains a top-level object with one key per courier and an aggregate summary. Couriers with no data for the phone number return zero counts — they are never omitted from the response.

Response (200 OK)
{
  "steadfast": {
    "success": 3,
    "cancel":  1,
    "total":   4
  },
  "pathao": {
    "success": 5,
    "cancel":  2,
    "total":   7
  },
  "redx": {
    "success": 20,
    "cancel":  5,
    "total":   25
  },
  "paperfly": {
    "success": 0,
    "cancel":  0,
    "total":   1
  },
  "carrybee": {
    "success":     10,
    "cancel":      0,
    "total":       10,
    "fraud_count": 0
  },
  "aggregate": {
    "total_success":    38,
    "total_cancel":     8,
    "total_deliveries": 47,
    "success_ratio":    80.85,
    "cancel_ratio":     17.02,
    "verdict": {
      "level":    "good",
      "title":    "Looks Good",
      "subtitle": "80.85% success across 47 orders. Good track record for COD."
    }
  }
}

Courier object

Each of the five courier keys (steadfast, pathao, redx, paperfly, carrybee) contains the same structure:

FieldTypeRequiredDescription
successintegerNoNumber of successfully delivered orders via this courier for the phone number.
cancelintegerNoNumber of cancelled or returned orders via this courier.
totalintegerNoTotal orders recorded. Equals success + cancel + any pending orders.

Aggregate object

The aggregate key summarises data across all five couriers:

FieldTypeRequiredDescription
total_successintegerNoSum of successful deliveries across all couriers.
total_cancelintegerNoSum of cancelled or returned orders across all couriers.
total_deliveriesintegerNoSum of all resolved deliveries (success + cancel) across all couriers.
success_ratiofloatNoPercentage of orders successfully delivered: (total_success / total_deliveries) × 100. Rounded to 2 decimal places. Returns 0 if no history.
cancel_ratiofloatNoPercentage of orders cancelled or returned. success_ratio + cancel_ratio may not equal 100 if pending orders exist.
verdictobjectNoCOD safety verdict computed from all courier signals. See Verdict object below.

Verdict object

Returned inside aggregate.verdict. Combines delivery history, Pathao rating, and Carrybee fraud reports into a single COD recommendation.

FieldTypeRequiredDescription
levelstringNoMachine-readable risk level. One of: trusted, reliable, good, caution, risky, unknown.
titlestringNoShort human-readable verdict label, e.g. "Trusted Customer — COD Safe" or "High Risk — Avoid COD".
subtitlestringNoOne-sentence explanation of the verdict with the key signals that drove it.
LevelMeaningTypical condition
trustedTrusted Customer — COD Safe≥3 orders, ≥90% success or Pathao excellent/safe, no fraud reports
reliableReliable Customer≥3 orders, 70–89% success, no fraud reports
goodLooks Good1–2 orders with ≥70% success, or limited but clean history
cautionProceed with Caution≥2 orders with 40–69% success rate
riskyHigh Risk — Avoid CODFraud reports, Pathao risky flag, or <40% success on ≥2 orders
unknownNo History FoundZero orders and no Pathao data available

Notes on the data

  • A phone with no history returns zeros in all fields. This is normal — it does not mean the customer is fraudulent.
  • total_deliveries counts only resolved orders (success + cancel). In-transit orders are excluded.
  • Data freshness depends on each courier's API caching — typically within 1–6 hours of the last order update.
  • success_ratio + cancel_ratio can be less than 100 if some orders are still in pending/transit state.

Code Examples

Production-ready examples in all major languages. Each snippet reads the API key from an environment variable (FRAUDDETECT_API_KEY), handles errors, and prints the fraud score.

Basic check

POST /api/v1/check
curl -X POST https://connect.chowdhury.bd/api/v1/check \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"phone":"01711223344"}'

Retry on rate limit (429)

If you exceed 30 requests/minute the API returns 429 Too Many Requests. These examples implement exponential back-off.

Retry with exponential back-off
#!/bin/bash
# Retry with exponential back-off on 429
MAX_RETRIES=3
DELAY=1

for i in $(seq 1 $MAX_RETRIES); do
  STATUS=$(curl -s -o /tmp/fd_resp.json -w "%{http_code}" \
    -X POST https://connect.chowdhury.bd/api/v1/check \
    -H "X-Api-Key: $FRAUDDETECT_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"phone":"01711223344"}')

  if [ "$STATUS" -eq 200 ]; then
    cat /tmp/fd_resp.json
    exit 0
  elif [ "$STATUS" -eq 429 ]; then
    echo "Rate limited, waiting ${DELAY}s..."
    sleep $DELAY
    DELAY=$((DELAY * 2))
  else
    echo "Error: HTTP $STATUS"
    cat /tmp/fd_resp.json
    exit 1
  fi
done
echo "Exceeded retry limit"
exit 1

Score Interpretation

success_ratio is the single most useful field for deciding whether to fulfil a COD order. It represents what percentage of this customer's past orders across all five couriers were successfully delivered.

Suggested thresholds

80 – 100%

Low risk — fulfil

Strong delivery track record. Safe to ship COD without extra checks.

50 – 79%

Medium risk — review

Consider requiring partial prepayment or calling to confirm. History exists but cancel rate is notable.

1 – 49%

High risk — caution

Majority of past orders cancelled. Strongly consider prepayment or declining the COD order.

0 (no data)

New customer

No delivery history found across any courier. Apply your standard new-customer policy.

Using cancel_ratio as a secondary signal

If total_deliveries is low (e.g. 1–3 orders), a high cancel_ratio may not be statistically significant — a single bad order skews a thin history. Use both fields together:

const { aggregate } = result;

function getRiskLevel(aggregate) {
  const { total_deliveries, success_ratio, cancel_ratio } = aggregate;

  if (total_deliveries === 0) return 'new_customer';  // no history

  // Low sample size — be less aggressive
  if (total_deliveries < 3) {
    return cancel_ratio > 66 ? 'high_risk' : 'low_sample';
  }

  if (success_ratio >= 80) return 'safe';
  if (success_ratio >= 50) return 'medium_risk';
  return 'high_risk';
}

console.log(getRiskLevel(aggregate)); // 'safe' | 'medium_risk' | 'high_risk' | ...

Phone Number Format

The phone field must be an 11-digit Bangladeshi mobile number in local format — no country code prefix.

Valid formats
01711223344Grameenphone
01876543210Banglalink
01511223344Teletalk
01611223344Robi
01911223344Airtel
Invalid formats
+8801711223344Remove +88 prefix
8801711223344Remove 88 prefix
1711223344Missing leading 0
017112233Too short (9 digits)
017112233444Too long (12 digits)

Normalize user input before sending:

function normalizePhone(input) {
  let phone = input.trim().replace(/\s+/g, '');
  if (phone.startsWith('+88')) phone = phone.slice(3);
  if (phone.startsWith('88') && phone.length === 13) phone = phone.slice(2);
  if (!/^01[0-9]{9}$/.test(phone)) throw new Error('Invalid phone number');
  return phone;
}

normalizePhone('+8801711223344'); // '01711223344'
normalizePhone('01711223344');   // '01711223344'

Rate Limits

Each API key is rate-limited to protect the service and ensure fair usage.

Limit

30 req / min

Per API key, rolling window

Response

429

Too Many Requests

Strategy

Exponential

Back-off on retry

Need more throughput?

Create multiple API keys — one per environment or service — to multiply your effective limit. Or contact us for an Enterprise plan with higher limits.

Handle 429 with exponential back-off:

Retry logic
async function checkPhoneWithRetry(phone, maxRetries = 3) {
  let delay = 1000; // 1 second

  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    const res = await fetch('https://connect.chowdhury.bd/api/v1/check', {
      method: 'POST',
      headers: {
        'X-Api-Key': process.env.FRAUDDETECT_API_KEY,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ phone }),
    });

    if (res.ok) return res.json();

    if (res.status === 429 && attempt < maxRetries) {
      console.warn(`Rate limited — retrying in ${delay}ms (attempt ${attempt})`);
      await new Promise(r => setTimeout(r, delay));
      delay *= 2; // exponential back-off
      continue;
    }

    const err = await res.json().catch(() => ({}));
    throw new Error(`API error ${res.status}: ${err.message ?? 'unknown'}`);
  }
  throw new Error('Exceeded maximum retries');
}

Error Reference

All errors return a JSON body with a message field. HTTP status codes follow standard semantics.

Error response body
{
  "message": "Unauthenticated."
}

# or for validation errors:
{
  "message": "The phone field is required.",
  "errors": {
    "phone": ["The phone field is required."]
  }
}
200

OK

Cause

Request succeeded.

Fix

Parse the response body — data is ready.

401

Unauthorized

Cause

The X-Api-Key header is missing, empty, or the key does not exist.

Fix

Verify the key is included in every request. Check for typos. Regenerate from the dashboard if needed.

422

Unprocessable Entity

Cause

Request body is missing, malformed JSON, or the phone field fails validation (wrong format, missing, too short/long).

Fix

Ensure Content-Type is application/json, the body is valid JSON, and phone is an 11-digit local number starting with 01.

429

Too Many Requests

Cause

This API key has exceeded 30 requests in the current 60-second window.

Fix

Wait before retrying. Implement exponential back-off. Create additional keys if throughput demand is constant.

500

Internal Server Error

Cause

Unexpected server-side failure. Rare — usually transient.

Fix

Retry after a short delay. If the issue persists beyond a few minutes, contact support at abrar@chowdhury.bd.

503

Service Unavailable

Cause

The server is temporarily down for maintenance or a courier API is unreachable.

Fix

Retry with exponential back-off. The service typically recovers within minutes.

Debugging checklist

X-Api-Key header present and correctCopy key directly from dashboard
Content-Type: application/json setRequired for all POST requests
Phone is 11 digits, starts with 01Strip +88 or 88 prefix if present
Request body is valid JSONValidate with JSONLint or language parser
Under 30 requests/minuteImplement rate-limit aware queuing

Still stuck?

If you've worked through the checklist and the error persists, reach out with your request ID (from the response headers if available) and the full error body.

abrar@chowdhury.bd