Agency API · v1

Integrate COD reconciliation
into your ops stack.

A single POST endpoint reconciles any Shopify export against any courier remittance file. It returns structured flags, shortfall amounts, and overdue timelines your team can act on immediately.

Typical response in 800ms–2s
HTTPS only · Bearer auth
JSON in · JSON out
Sandbox

Try it before you subscribe.

Your sandbox key works against a read-only dataset of 500 real-but-anonymised COD orders. Every flag type is represented so you can test your integration end-to-end before committing to a plan.

Your free sandbox key

No account required. No credit card. Returns realistic mock data, not live brand data. Switch to a live key after subscribing.

pt_sandbox_demo_k3y_8f2a1c9d4e7b0f5a
50 requests / day
All flag types present
Anonymised orders only
Same response schema as live

Evaluating for your agency? The sandbox key passes any CSV. You can test with a sample Shopify export and a sample Delhivery remittance file. The response will be structured identically to production. Use X-Sandbox: true to confirm you're hitting the sandbox endpoint.

Authentication

Bearer token, nothing else.

Every request must include your API key as a Bearer token in the Authorization header. Live keys are generated post-subscription from the Agency → API panel.

HTTP Header
Authorization: Bearer pt_live_your_key_here
Content-Type: application/json

Keep your live key secret. It has full access to all brands under your agency account. Never expose it in client-side code or public repos. Rotate it from the API panel if compromised. The old key is instantly invalidated.

Quickstart

Reconcile in one call.

Pass your Shopify orders export and the courier remittance file as CSV strings. The API parses, matches, and returns a structured array of every order with its reconciliation status.

cURL
curl -X POST https://api.paytrace.in/reconcile \
  -H "Authorization: Bearer pt_sandbox_demo_k3y_8f2a1c9d4e7b0f5a" \
  -H "Content-Type: application/json" \
  -d '{
    "courier": "delhivery",
    "shopify_csv": "Name,Email,Financial Status,Paid at,Fulfillment Status,...\n#1001,...",
    "courier_csv": "AWB No,Order ID,COD Amount,Remitted Amount,Remittance Date,...\nDEL123,...",
    "brand_id": "brand_nykaa_01"
  }'

Example response

JSON Response
{
  "ok": true,
  "job_id": "recon_a7f2c1d9_1712345678",
  "meta": {
    "courier": "delhivery",
    "brand_id": "brand_nykaa_01",
    "shopify_rows": 312,
    "courier_rows": 298,
    "matched": 289,
    "unmatched": 9,
    "processed_at": "2025-04-06T10:32:11Z"
  },
  "summary": {
    "total_cod_expected": 2845,  // in rupees (₹2,845.00)
    "total_remitted": 2612,
    "total_shortfall": 233,
    "flags": {
      "MISSING": 4,
      "SHORT_PAID": 7,
      "OVERDUE": 3,
      "RTO_ANOMALY": 1,
      "MINOR_DIFF": 2,
      "RECONCILED": 272
    }
  },
  "results": [
    {
      "awb": "DEL1234567890",
      "order_id": "#1082",
      "cod_amount": 899,          // rupees (₹899.00)
      "remitted": 0,
      "shortfall": 899,
      "days_overdue": 18,
      "flag": "MISSING",
      "status": "fulfilled"
    },
    {
      "awb": "DEL9876543210",
      "order_id": "#1097",
      "cod_amount": 1499,
      "remitted": 999,
      "shortfall": 500,
      "days_overdue": 0,
      "flag": "SHORT_PAID",
      "status": "fulfilled"
    },
    {
      "awb": "DEL5551234567",
      "order_id": "#1043",
      "cod_amount": 599,
      "remitted": 599,
      "shortfall": 0,
      "days_overdue": 0,
      "flag": "RECONCILED",
      "status": "fulfilled"
    }
    // ... remaining results
  ]
}
Endpoint

POST /reconcile

The primary endpoint. Accepts a Shopify CSV and a courier remittance CSV. Reconciles them order-by-order and returns every flag with shortfall and timeline data.

POST https://api.paytrace.in/reconcile Reconcile Shopify + courier

Request body

Field Type Required Description
shopify_csv string required Full text content of the Shopify orders export CSV. UTF-8 encoded.
courier_csv string required Full text content of the courier remittance CSV. Supported couriers: Delhivery, Shiprocket, XpressBees, Ecom Express.
courier string required Courier identifier. One of: delhivery shiprocket xpressbees ecom_express
brand_id string optional Your internal brand identifier. Returned as-is in the response and used to associate results with a brand in the agency dashboard.
overdue_threshold_days integer optional Days after delivery before an unremitted COD is flagged OVERDUE. Defaults to 7. Range: 1–30.
minor_gap_threshold integer optional Shortfall in rupees below which a discrepancy is flagged MINOR_DIFF instead of SHORT_PAID. Defaults to 2 (₹2). Range: 0–10.
webhook_url string (url) optional POST callback URL when processing completes. Useful for large files processed asynchronously. See Webhooks →

Response fields

FieldTypeDescription
okbooleanAlways present. true on success, false on error.
job_idstringUnique run identifier. Use with GET /status/{job_id} for async polling.
meta.shopify_rowsintegerNumber of rows parsed from the Shopify CSV.
meta.courier_rowsintegerNumber of rows parsed from the courier CSV.
meta.matchedintegerOrders successfully matched between both files by AWB or order ID.
summary.total_cod_expectedintegerTotal COD amount expected across all orders, in rupees (₹).
summary.total_shortfallintegerTotal unrecovered amount across all flagged orders, in rupees (₹).
summary.flagsobjectCount of each flag type in the results. See Flag reference →
results[].awbstringCourier AWB / tracking number.
results[].order_idstringShopify order name (e.g. #1082).
results[].cod_amountintegerCOD amount collected on delivery, in rupees (₹).
results[].remitted_amountintegerAmount the courier has remitted so far, in rupees (₹). 0 if not yet remitted.
results[].shortfallintegercod_amount − remitted_amount, in rupees (₹). 0 if fully reconciled.
results[].days_overdueintegerDays past the expected remittance window. 0 if within window or reconciled.
results[].flagstringReconciliation status. One of: RECONCILED MISSING SHORT_PAID OVERDUE RTO_ANOMALY MINOR_DIFF
Endpoint

POST /reconcile/batch

Process multiple brands in a single call. Returns a batch_id immediately; results are delivered via webhook or polled with GET /status/{batch_id}. Ideal for agencies running weekly reconciliations across many brands.

POST https://api.paytrace.in/reconcile/batch Multi-brand async run
FieldTypeRequiredDescription
jobs array required Array of reconciliation jobs. Each job has the same fields as POST /reconcile plus a required brand_id. Max 20 per batch.
webhook_url string (url) optional Called once all jobs complete. Payload contains the full batch results array.
Batch response
{
  "ok": true,
  "batch_id": "batch_b2f1a9c3_1712345678",
  "jobs": 5,
  "status": "queued",
  "estimated_completion_ms": 12000
}
Endpoint

GET /status/{job_id}

Poll the status of an async reconciliation or batch job. Returns status queued, processing, or complete. The full results payload is included once done.

GET https://api.paytrace.in/status/{job_id} Poll async job status

Polling interval: We recommend polling every 2 seconds. Most jobs complete in under 3s. For large files (10,000+ rows) allow up to 15s. Results are cached for 24 hours after completion.

Reference

Flag types: what each means.

Every order in the results array carries exactly one flag. Here's what each flag means and what action it implies for your clients.

Flag Meaning Shortfall Recommended action
MISSING Courier delivered the order and collected COD, but no remittance appears in the courier report at all. 100% of cod_amount Raise a dispute immediately. Include the AWB, days_overdue and order date in your escalation letter.
SHORT_PAID Courier remitted a lower amount than the COD collected. Common due to manual entry errors or partial cycles. cod_amount − remitted_amount Dispute the delta. Attach the remittance entry showing the low amount. Most couriers resolve in 7–14 days.
OVERDUE No remittance received within the courier's standard SLA window (typically 7 days post-delivery). 100% of cod_amount Follow up with courier's ops team. May resolve in the next remittance cycle without a formal dispute.
RTO_ANOMALY Order shows as RTO (returned) in the courier system but COD was still collected. Courier remitted nothing. High-severity. 100% of cod_amount Escalate to courier's fraud team. Request delivery proof (OTP, signature). Absence is grounds for full recovery.
MINOR_DIFF Remittance is slightly below COD amount, within the minor_gap_threshold (default ₹2). Likely a rounding difference. ≤ threshold (default ₹2) Monitor but usually not worth disputing. Batch with next cycle's discrepancies if your courier allows it.
RECONCILED COD amount matches remittance exactly. No action required. ₹0 None. These orders are fully settled.
Reference

Error codes.

All errors return a consistent JSON envelope with a machine-readable code, a human-readable message and a hint to help resolve the issue fast.

HTTPError codeWhen it occursFix
200 n/a Successful reconciliation. n/a
401 INVALID_KEY API key missing, malformed, or revoked. Check Authorization: Bearer <key> header. Regenerate from Agency → API panel if needed.
402 PLAN_REQUIRED Sandbox key used on a live endpoint, or live key on an expired plan. Upgrade to Agency plan. Sandbox keys return X-Sandbox: true header.
413 FILE_TOO_LARGE CSV strings exceed 10 MB per request body. Split into smaller date ranges, or use POST /reconcile/batch with chunked jobs.
422 COLUMN_NOT_FOUND Required column (AWB, Order ID, COD amount) missing from one of the CSVs. Check supported column names for your courier under Supported couriers →
422 COURIER_MISMATCH The courier param doesn't match the format of the CSV provided. Verify you're passing the correct courier identifier. Each courier has a distinct column schema.
422 EMPTY_CSV One or both CSVs have no data rows (just a header or completely empty). Ensure your export includes data, not just column headers.
429 RATE_LIMITED Exceeded rate limits (see below). Implement exponential backoff. Check Retry-After response header for wait time in seconds.
500 INTERNAL_ERROR Unexpected server error. Retry once after 5s. If persistent, contact help@paytrace.in with your job_id.
Reference

Rate limits.

Rate limits are per API key, counted on a rolling 60-second window. Headers on every response show your current usage.

Sandbox key
50
requests / day
Agency plan
120
requests / minute
Batch jobs
20
brands per batch call
Rate limit headers
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1712345738     // Unix timestamp
Retry-After: 14                  // seconds (only present on 429)
Reference

Supported couriers.

PayTrace auto-detects column names from common export formats. These are the accepted values for the courier parameter and the columns we look for in each report.

CourierParameterAWB columnCOD amount columnRemittance date column
Delhivery delhivery AWB No COD Amount Remittance Date
Shiprocket shiprocket AWB Code Total Amount Payment Date
XpressBees xpressbees Waybill Number COD Collected Settlement Date
Ecom Express ecom_express AWB Number COD Amount Remittance Date

Courier not listed? Email help@paytrace.in with a sample remittance CSV. We typically add support within 48 hours. You can also pass "courier": "custom" with explicit column overrides. See custom courier docs →

Guide

Agency integration pattern.

Most agencies integrate PayTrace into their weekly ops cadence. They fetch Shopify exports per brand, pull courier remittances, run the batch endpoint and surface flags in a Notion or Google Sheet for client review.

Python: weekly batch script
import requests, csv, time

PAYTRACE_KEY = "pt_live_your_key_here"
BASE_URL      = "https://api.paytrace.in"

def run_weekly_reconciliation(brands):
    # brands = [{ id, shopify_csv, courier_csv, courier }, ...]

    # 1. Fire batch job
    resp = requests.post(
        f"{BASE_URL}/reconcile/batch",
        headers={"Authorization": f"Bearer {PAYTRACE_KEY}"},
        json={
            "jobs": [
                {
                    "brand_id":    b["id"],
                    "courier":     b["courier"],
                    "shopify_csv": b["shopify_csv"],
                    "courier_csv": b["courier_csv"],
                }
                for b in brands
            ],
            "webhook_url": "https://your-agency.com/hooks/paytrace"
        }
    )
    batch_id = resp.json()["batch_id"]

    # 2. Poll until done (or wait for webhook)
    while True:
        status_resp = requests.get(
            f"{BASE_URL}/status/{batch_id}",
            headers={"Authorization": f"Bearer {PAYTRACE_KEY}"}
        ).json()

        if status_resp["status"] == "complete":
            break
        time.sleep(2)

    # 3. Collect and triage results per brand
    results = status_resp["results"]
    actionable = [
        r for r in results
        if r["flag"] in ("MISSING", "SHORT_PAID", "RTO_ANOMALY")
    ]

    return actionable
Guide

Webhooks.

Pass a webhook_url in any reconciliation call and we'll POST the full results to your endpoint when processing completes. No polling needed.

Webhook payload
{
  "event": "reconciliation.complete",
  "job_id": "recon_a7f2c1d9_1712345678",
  "brand_id": "brand_nykaa_01",
  "summary": { /* same as /reconcile response summary */ },
  "results": [ /* same as /reconcile response results */ ],
  "signature": "sha256=abc123..."  // HMAC-SHA256 of payload body
}

Verify webhook signatures. Compare the signature header against an HMAC-SHA256 of the raw request body using your API key as the secret. Reject any webhook that fails verification. We retry failed deliveries 3 times over 30 minutes.

Guide

White-label reports.

Agency plan accounts can configure a white-label profile. Your agency name and email appear on all exported Excel reports and dispute letters instead of "PayTrace". Set it once via the dashboard or via API.

Agency plan feature. White-label branding is available on the Agency plan (₹3,999/month). Configure it from Agency → White-label Settings in the dashboard, or by passing white_label: { agency_name, agency_email } in any reconcile call to override per-brand.