Pro 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
Try it free

Try it before you subscribe.

The GET /demo endpoint runs a full reconciliation against a real sample dataset: no account, no API key, no credit card required. It uses an anonymised Shopify export and Shiprocket remittance file covering Feb–Mar 2026, with every flag type represented.

One-line demo call

No request body. No auth header. Returns the same { results } shape as a live reconciliation run.

curl https://api.paytrace.in/demo
No auth required
All flag types present
Anonymised orders only
Same response schema as live

Evaluating for your agency? Call GET /demo to see exactly what a reconciliation response looks like before writing any integration code. The dataset includes MISSING, SHORT_PAID, OVERDUE, RTO_ANOMALY, MINOR_DIFF, OVERPAID, and RECONCILED rows so you can build and test your downstream flag-handling logic end-to-end.

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 Pro → 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_live_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "shopify": "Name,Email,Financial Status,Paid at,Fulfillment Status,...\n#1001,...",
    "courier": "AWB No,Order ID,COD Amount,Remitted Amount,Remittance Date,...\nDEL123,...",
    "courierName": "delhivery",
    "brand": "brand_nykaa_01"
  }'

Example response

JSON Response
{
  "results": [
    {
      "awb": "DEL1234567890",
      "order_id": "#1082",
      "cod_amount": 899,
      "remitted": 0,
      "shortfall": 899,
      "days_overdue": 18,
      "flag": "MISSING",
      "flag_reason": "Not remitted",
      "status": "fulfilled",
      "match_mode": "awb"
    },
    {
      "awb": "DEL9876543210",
      "order_id": "#1097",
      "cod_amount": 1499,
      "remitted": 999,
      "shortfall": 500,
      "days_overdue": 0,
      "flag": "SHORT_PAID",
      "flag_reason": "Shortfall detected",
      "status": "fulfilled",
      "match_mode": "awb"
    },
    {
      "awb": "DEL5551234567",
      "order_id": "#1043",
      "cod_amount": 599,
      "remitted": 599,
      "shortfall": 0,
      "days_overdue": 0,
      "flag": "RECONCILED",
      "flag_reason": "Fully remitted",
      "status": "fulfilled",
      "match_mode": "awb"
    }
    // ... 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 string required Full text content of the Shopify orders export CSV. UTF-8 encoded. Maximum 50,000 rows.
courier string required Full text content of the courier remittance CSV. Supported couriers: Delhivery, Shiprocket, XpressBees, Shadowfax, Ekart, Ecom Express. Maximum 50,000 rows.
courierName string optional Courier identifier string (e.g. delhivery, shiprocket). Always stored in audit history so you can filter and group runs by courier. On Enterprise plans, also used to save and re-use detected column mappings across runs, skipping re-detection on subsequent calls for the same courier. Has no column-mapping effect on Pro plan.
brand string optional Your brand label for this run (e.g. "brand_nykaa_01" or "Nykaa Fashion"). Stored in audit history so you can filter runs by brand. Defaults to "default" if omitted.
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.

Response fields

FieldTypeDescription
resultsarrayArray of reconciliation result objects, one per order row. Always present on a 200 response.
duplicatebooleanPresent and true when an identical run was detected within the last 30 seconds. Match is on: same user + brand + courierName + identical row and flag counts. Results are still returned and the run is not counted against your monthly limit.
run_idstringUUID identifying this reconciliation run. Included when a duplicate is detected. Use for support queries.
cycle_guidanceobjectMetadata about the remittance-timing calculation, not a per-row result. offset_days (integer): the number of days used to decide when an unmatched order counts as overdue; offset_source ("explicit" or "default"): whether that came from your own Settings override or the platform default; batch_as_of (ISO date): the most recent order date in this batch, the reference point every "days overdue" figure is measured against; courier_coverage_complete (boolean): true once your courier file's own dates prove every order's full remittance window was actually checked (see the next field), making any remaining AWAITING_NEXT_CYCLE rows resolve directly to MISSING/OVERDUE instead of waiting on a day-count guess; courier_data_through (ISO date or null): the latest date actually present in your courier file; guaranteed_coverage_days (integer): how far past batch_as_of your courier file needs to reach for coverage to count as complete (14, or your own overdue_threshold_days if you've set it higher); awaiting_next_cycle_count (integer): how many rows are still waiting on more remittance data.
results[].awbstringCourier AWB / tracking number.
results[].order_idstringShopify order name (e.g. #1082).
results[].cod_amountnumberCOD amount collected on delivery, in rupees (₹).
results[].remittednumberAmount the courier has remitted so far, in rupees (₹). 0 if not yet remitted.
results[].shortfallnumberUnrecovered amount in rupees (₹). Positive = courier owes money. Negative = courier overpaid. 0 if fully reconciled.
results[].days_overdueintegerDays past the expected remittance window. Non-zero only on OVERDUE and MISSING rows.
results[].flagstringReconciliation status. One of: RECONCILED MISSING SHORT_PAID OVERDUE RTO_ANOMALY MINOR_DIFF OVERPAID AWAITING_NEXT_CYCLE. Treat unrecognized values as non-final/no-action rather than erroring. New statuses may be added over time.
results[].flag_reasonstringHuman-readable explanation for the flag (e.g. "Shortfall detected", "Not remitted"). Useful for display in client-facing dashboards without needing to map flag codes yourself.
results[].statusstringShopify fulfilment status for this order (e.g. fulfilled, unfulfilled). Useful for filtering RTO and cancelled orders.
results[].match_modestringHow this row was matched between the two files. awb = matched on tracking number (preferred). order_id = matched on Shopify order ID (fallback when AWB not present in Shopify export).
Endpoint

POST /generate-api-key

Generates a new live API key for your account. Requires an active Pro plan subscription. Any previously issued key for your account is immediately deactivated. There is always only one active key per account.

POST https://api.paytrace.in/generate-api-key Issue a new live API key

This endpoint must be called with a Supabase session token, not a pt_live_* API key — this is enforced server-side, so a leaked live key can never be used to mint or rotate a fresh one. Use your dashboard session from the browser. The returned key is shown exactly once: store it securely. Calling this endpoint rotates your key immediately.

Request body

No request body required. The authenticated user's email is used to identify the account.

Response

JSON Response
{
  "key":    "pt_live_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",  // full key: shown once, store immediately
  "prefix": "pt_live_a1b2c"                             // safe prefix for display / identification
}

All pt_live_* keys are passed as a Bearer token in subsequent API calls. Only the SHA-256 hash of the key is stored. PayTrace cannot recover a lost key. If you lose it, call this endpoint again to rotate.

Coming Soon

POST /reconcile/batch

Process multiple brands in a single call. Results will be delivered via webhook or polled with GET /status/{batch_id}. Ideal for agencies running weekly reconciliations across many brands.

This endpoint is not yet available. Batch processing is on the roadmap for Pro plan users. In the meantime, call POST /reconcile once per brand in sequence. Email help@paytrace.in to register your interest and get notified on launch.

POST https://api.paytrace.in/reconcile/batch Multi-brand async run (coming soon)
FieldTypeRequiredDescription
jobs array required Array of reconciliation jobs. Each job has the same fields as POST /reconcile plus a required brand. 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
}
Coming Soon

GET /status/{job_id}

Poll the status of an async batch job. Will return queued, processing, or complete, with the full results payload once done. Available when POST /reconcile/batch launches.

GET https://api.paytrace.in/status/{job_id} Poll async job status (coming soon)

Not yet available. This endpoint will be released alongside POST /reconcile/batch. The current POST /reconcile endpoint is synchronous and returns results directly in the response body.

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 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.
OVERPAID Courier remitted more than the COD amount collected. Can happen due to duplicate remittance runs or manual credit errors. Negative (courier paid excess) Verify against bank statement. If confirmed, acknowledge to courier to offset against a future cycle rather than raising a dispute.
RECONCILED COD amount matches remittance exactly. No action required. ₹0 None. These orders are fully settled.
AWAITING_NEXT_CYCLE No remittance found yet, but the order is still within the account's normal remittance window (overdue_threshold_days, default 7), not old enough to call confirmed-overdue. Distinct from MISSING: this is the "give it more time" case, not a dispute case. ₹0 (not charged against amount_at_risk) None yet. Re-run the reconciliation after your next remittance file. These typically resolve to RECONCILED on their own. Only escalate if a row is still in this state well past your courier's usual cycle.
Reference

Error codes.

All errors return a JSON object with a single error field containing a human-readable message string.

Error shape
{ "error": "Plain English description of what went wrong" }
HTTPWhen it occursFix
200 Successful reconciliation. results array is present. n/a
400 Invalid JSON body, missing shopify or courier fields, either CSV exceeds 50,000 rows, or a CSV column that can't be detected or validated. Check the error message: it names the missing or ambiguous column. If hitting the row limit, split your export into smaller date ranges. Verify your CSV exports match the expected formats for your courier.
401 API key missing, malformed, or expired. Check Authorization: Bearer <key> header. Regenerate from Pro → API panel if needed.
403 Subscription has ended, account setup is incomplete, or account does not have Pro access. Check subscription status in the dashboard. Contact help@paytrace.in if you believe this is an error.
403 phone_not_verified Phone number on the account has not been verified. All API calls are blocked until verification is complete. Log into the dashboard and complete phone OTP verification under Account Settings. Takes under 60 seconds.
415 Content-Type header is missing or not application/json on a POST request. Add Content-Type: application/json to your request headers.
413 Request body exceeds 25 MB. Split into smaller date ranges or reduce the number of rows per call.
429 rate limit More than 10 requests in a 60-second window. Key is blocked for 5 minutes on breach. Implement a 0.5s delay between sequential calls. Check the Retry-After header for the exact block duration in seconds.
429 run limit Monthly reconciliation run cap reached. Pro plan: 10 runs/month. Solo plan: 2 runs/month. (Subscribers on these plans before the September 2026 pricing update keep their prior limits, 16 and 4 respectively, until they change plans.) Resets on your billing cycle date. Wait for the next billing cycle, or contact help@paytrace.in to discuss higher limits. The error message includes the exact limit for your account, e.g. "Run limit of 10/month reached".
500 Unexpected server error. Retry once after 5s. If persistent, contact help@paytrace.in.
Reference

Rate limits.

Rate limits are per API key, enforced on a rolling 60-second window. Exceeding the limit returns a 429 and blocks the key for 5 minutes.

Pro plan
10
requests / minute
Breach penalty
5 min
key blocked on breach
Monthly run cap
16
reconciliation runs

No rate-limit headers are sent. The API does not return X-RateLimit-* headers. If you receive a 429, respect the Retry-After header value (seconds) before retrying. For scripted multi-brand workflows, add a 0.5s pause between calls to stay well within limits.

GET /demo limits

GET /demo is rate-limited per IP: 20 requests/minute, with a 60-second block on breach. No auth required. Returns Retry-After on a 429.

429 Response
HTTP/1.1 429 Too Many Requests
Retry-After: 300   // seconds blocked (5 minutes on breach)

{ "error": "Rate limit exceeded" }
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
Shadowfax shadowfax AWB Number COD Amount (INR) Remittance Date
Ekart ekart AWB Number COD Amount (INR) 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

Pro integration pattern.

Most agencies integrate PayTrace into their weekly ops cadence: fetching Shopify exports per brand, pulling courier remittances, running reconciliations sequentially and surfacing flags in a Notion or Google Sheet for client review.

Python: weekly batch script
import requests, time

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

def run_weekly_reconciliation(brands):
    # brands = [{ brand, shopify_csv, courier_csv, courierName }, ...]
    all_flagged = []

    for b in brands:
        resp = requests.post(
            f"{BASE_URL}/reconcile",
            headers={"Authorization": f"Bearer {PAYTRACE_KEY}"},
            json={
                "shopify":     b["shopify_csv"],
                "courier":     b["courier_csv"],
                "courierName": b["courierName"],
                "brand":       b["brand"],
            }
        )
        resp.raise_for_status()
        results = resp.json()["results"]

        # Collect actionable flags for this brand
        flagged = [
            {**r, "brand": b["brand"]}
            for r in results
            if r["flag"] in ("MISSING", "SHORT_PAID", "RTO_ANOMALY", "OVERDUE")
        ]
        all_flagged.extend(flagged)

        # Be a good API citizen: small pause between brands
        time.sleep(0.5)

    return all_flagged
Enterprise only

Webhooks.

PayTrace can POST a summary to your endpoint automatically after every reconciliation run — whether that run came from a live POST /reconcile call or from an hourly Shopify autopilot pass. This is a real, shipped feature, not on the roadmap — but it's configured from the dashboard, not from the API request itself, and it's available on the Enterprise plan only (not Pro).

Configure, don't call. Turn this on from Enterprise → Alerts in the dashboard: set your endpoint URL and a signing secret. There is no field on POST /reconcile that enables or overrides this per-request — it fires automatically for every run once configured, on both API-triggered and autopilot-triggered reconciliations.

Delivery: one POST per completed run, with a 5-second timeout and one automatic retry. A non-2xx response from your endpoint counts as a failed delivery. The payload is a run summary, not the full results array:

Webhook payload
{
  "brand": "brand_nykaa_01",
  "run_at": "2026-09-28T06:00:00.000Z",
  "flagged_count": 14,
  "amount_risk": 18420,
  "run_id": "recon_a7f2c1d9_1712345678"
}
HTTP Header
X-PayTrace-Sig: hex-encoded HMAC-SHA256 of the raw request body

Not signed with your API key. X-PayTrace-Sig is computed with the separate signing secret you set alongside the webhook URL in Enterprise → Alerts — verify against that secret, not your pt_live_* key.

Guide

White-label reports.

Both Pro and Enterprise accounts can configure a white-label profile. Your agency name and email appear on exported Excel reports and dispute letters instead of "PayTrace". This is a dashboard setting — there is no request field on POST /reconcile that sets or overrides it.

Pro plan (₹3,999/month): configure it from Pro → White-label Settings in the dashboard. This is saved per-browser, not synced to your account server-side — set it again if you switch devices or clear site data.

Enterprise plan: white-label name and email are saved server-side on your account and apply consistently everywhere, including the Excel audit exports and dispute letters generated from the Enterprise dashboard.