API reference · v1

Tarazu API

APIs that turn Indian bank statements into clean transactions, risk signals with reasons, tamper checks and verified account details.

On this page

Overview

One REST API, four capabilities: statement parsing (PDF, XLS, XLSX, CSV into clean transactions), a suspicious-activity risk score with weighted reasons, statement integrity checks, and account detail extraction. All requests and responses are JSON over HTTPS, amounts are in INR and timestamps are ISO 8601 in IST (+05:30).

Base URLhttps://api.tarazu.example/v1

Risk output is a set of indicators, not a verdict. Responses carry risk.disclaimer: “Indicators only. Not a determination of fraud or mule activity.” Show it wherever you display the score.

Authentication

Send your secret key as a Bearer token on every request. Keys are created in the dashboard. Sandbox keys look like tz_test_… and only ever return mock data; live keys look like tz_live_… and respect your IP allowlist.

http
Authorization: Bearer tz_live_••••••••••••••••

Never ship keys in mobile apps or browsers. Call the API from your backend. For safe retries on POST, send an Idempotency-Key header (any unique string, kept for 24 hours).

Endpoints

POST/statements/analyze

Upload a statement as multipart/form-data. Small files usually complete within the request (status: completed); larger ones return status: processing and finish via webhook or polling.

FieldTypeRequiredDescription
filefileyesPDF, XLS, XLSX or CSV. Max 25 MB, 500 pages.
passwordstringnoPassword for protected PDFs. Used once, never stored.
checksstring[] (JSON)noAny of parse, risk, integrity, account. Default: all four.
consent_refstringyesYour reference to the account holder's recorded consent. 6 to 64 chars.
purposestringnoDeclared purpose, e.g. loan_underwriting. Stored with the consent reference.
declared_profilestringnosalaried, self_employed or business. Improves INCOME_MISMATCH.
external_refstringnoYour application or customer id, echoed back in responses and webhooks.
bash
curl -X POST https://api.tarazu.example/v1/statements/analyze \
  -H "Authorization: Bearer tz_live_••••••••" \
  -F "file=@statement_apr_sep.pdf" \
  -F "password=•••••" \
  -F 'checks=["parse","risk","integrity","account"]' \
  -F "consent_ref=cns_8F2K19QX"

GET/statements/{id}

Fetch the full result for a statement: account, summary, integrity and risk. Transactions are paginated separately.

bash
curl https://api.tarazu.example/v1/statements/stm_01JD4Q7Y3W \
  -H "Authorization: Bearer tz_live_••••••••"

GET/statements/{id}/transactions

Cursor-paginated parsed rows in statement order.

QueryTypeDescription
limitinteger1 to 500, default 100.
cursorstringValue of next_cursor from the previous page.
flaggedbooleanOnly rows that contributed to a reason code.
directioncr | drCredits or debits only.
from, todateInclusive date range inside the statement period.
bash
curl "https://api.tarazu.example/v1/statements/stm_01JD4Q7Y3W/transactions?limit=100&flagged=true" \
  -H "Authorization: Bearer tz_live_••••••••"
{
  "data": [
    {
      "id": "txn_3fd0a1",
      "date": "2026-04-02",
      "time": "23:41",
      "mode": "UPI",
      "direction": "cr",
      "amount_inr": 4500,
      "balance_inr": 5120.4,
      "narration": "UPI/CR/412790110482/upi•••12@okbank",
      "flags": [
        "NIGHT_VELOCITY"
      ]
    }
  ],
  "next_cursor": "c_9Qm2",
  "has_more": true
}

DELETE/statements/{id}

Permanently erases the derived report, transactions and any cached artefacts for a statement. Use it when a data principal withdraws consent or asks for erasure. The audit-log entry (who, when, consent reference) is kept; statement content is not.

bash
curl -X DELETE https://api.tarazu.example/v1/statements/stm_01JD4Q7Y3W \
  -H "Authorization: Bearer $API_KEY"

POST/webhooks/test

Sends a signed sample event to your configured webhook URL so you can test signature verification end to end.

bash
curl -X POST https://api.tarazu.example/v1/webhooks/test \
  -H "Authorization: Bearer tz_test_••••••••" \
  -H "Content-Type: application/json" \
  -d '{"event":"statement.completed"}'

Response schema

POST /statements/analyze and GET /statements/{id} return the same object. Sections you did not request in checks are null.

{
  "id": "stm_01JD4Q7Y3W",
  "status": "completed",
  "processing_ms": 4180,
  "account": {
    "holder_name": "R•••• K•••• S••••",
    "bank": "Sample Co-operative Bank",
    "account_no": "XXXXXXXX4417",
    "ifsc": "SMPL0001234",
    "type": "savings",
    "period": {
      "from": "2026-04-01",
      "to": "2026-09-30"
    }
  },
  "summary": {
    "transactions": 1284,
    "credits_inr": 4821330.5,
    "debits_inr": 4809112,
    "closing_balance_inr": 18630.25
  },
  "integrity": {
    "verdict": "review",
    "checks_passed": 11,
    "checks_total": 13,
    "findings": [
      "font_mismatch_page_4",
      "running_balance_break_row_612"
    ]
  },
  "risk": {
    "score": 78,
    "band": "high",
    "label": "suspicious_activity_indicators",
    "reasons": [
      {
        "code": "PASS_THROUGH",
        "weight": 0.31,
        "detail": "92% of credits leave the account within 30 minutes"
      },
      {
        "code": "MANY_TO_ONE",
        "weight": 0.22,
        "detail": "214 distinct UPI senders, 3 recurring beneficiaries"
      },
      {
        "code": "LOW_RESIDUAL",
        "weight": 0.14,
        "detail": "Median end-of-day balance below ₹600"
      }
    ],
    "disclaimer": "Indicators only. Not a determination of fraud or mule activity."
  }
}
FieldDescription
statusprocessing, completed or failed.
accountExtracted holder name (masked), bank, masked account number, IFSC, type and period.
summaryRow count and INR totals for credits, debits and closing balance.
integrity.verdictpass, review or fail. findings lists check codes that did not pass with location.
risk.score0 to 100. Higher means more suspicious-activity indicators were observed.
risk.bandlow (0 to 39), medium (40 to 69), high (70 to 100).
risk.reasons[]Reason code, its weight in the score (0 to 1) and a plain-language detail.
risk.disclaimerFixed text to display with the score.

Risk reason codes

Each code describes an observed pattern in the statement. None of them on its own, or together, establishes that an account is used for fraud.

CodeNameWhat it means
PASS_THROUGHRapid pass-throughA large share of incoming funds is moved out shortly after arrival, leaving little retained balance.
MANY_TO_ONEMany senders, few beneficiariesCredits arrive from a wide set of counterparties while debits concentrate on a small recurring set.
ONE_TO_MANYFan-out of debitsFunds from a few sources are distributed to an unusually wide set of beneficiaries.
LOW_RESIDUALLow residual balanceEnd-of-day balance stays very low relative to the volume flowing through the account.
ROUND_AMOUNT_BURSTBurst of round amountsClusters of identical or round-figure transfers within a short window.
NIGHT_VELOCITYLate-night velocityElevated transaction count between 23:00 and 05:00 IST compared with the account's daytime pattern.
NEW_ACCOUNT_HIGH_VOLUMEHigh volume soon after openingTurnover in the first months is high compared with typical accounts of the same type.
CASH_SPLIT_DEPOSITSSplit cash depositsSeveral cash deposits of similar size on the same or adjacent days.
DORMANT_REACTIVATIONReactivation after dormancyA long inactive period followed by a sudden rise in activity.
INCOME_MISMATCHInflow pattern differs from declared profileCredit pattern does not resemble salary or business receipts expected for the declared profile, if one is supplied.
CIRCULAR_FLOWCircular flowAmounts sent to a counterparty return in similar size within a short period.

Integrity check codes

Checks that do not apply to the input (for example PDF metadata on a CSV) are reported as pass with a skipped note. Any fail makes the verdict fail; otherwise any review makes it review.

CodeCheckDescription
PDF_PRODUCERMetadata and producerPDF producer/creator matches the issuing bank's known generators; flags consumer editors.
MODIFIED_AFTER_CREATEEdit timestampModification date is not later than creation date.
FONT_CONSISTENCYFont consistencyGlyph sets and font objects are consistent across rows and pages.
TEXT_LAYERText layer presentStatement has a native text layer, not only scanned images.
TEMPLATE_MATCHLayout matches bank templateColumn layout, headers and footers match the issuing bank's format.
PAGE_COUNTPage count and sequencePage numbering is continuous and matches the declared total.
RUNNING_BALANCERunning balance continuityEach row's balance equals the previous balance plus credit minus debit.
OPENING_CLOSINGOpening and closing totalsOpening balance plus net movement equals the closing balance.
DATE_ORDERDate orderTransaction dates are monotonic and fall inside the statement period.
DUPLICATE_TXN_IDDuplicate transaction IDsReference numbers (UTR/RRN) are not repeated across rows.
ARITHMETIC_TOTALSSummary arithmeticPrinted totals equal the sum of parsed rows.
IFSC_VALIDIFSC and branchIFSC format is valid and consistent with the bank name on the statement.
DIGITAL_SIGNATUREDigital signatureEmbedded signature, when present, validates and covers the whole document.

Errors

Errors use standard HTTP status codes and a stable machine-readable code.

json
{
  "error": {
    "code": "password_required",
    "message": "The PDF is encrypted. Send the password field.",
    "request_id": "req_7Hk2PzQ"
  }
}
HTTPCodeMeaning
400invalid_requestA parameter is missing or malformed. The message names the field.
401unauthorizedMissing, malformed or revoked API key.
403ip_not_allowedRequest came from an IP outside the key's allowlist.
403env_mismatchA sandbox key was used against live data, or the reverse.
404not_foundNo statement with this id in your workspace, or it has passed retention.
409duplicate_uploadSame file hash was submitted within 10 minutes; the earlier id is returned.
413file_too_largeFile exceeds 25 MB or 500 pages.
415unsupported_typeOnly PDF, XLS, XLSX and CSV are accepted.
422password_requiredThe PDF is encrypted and no password was sent.
422wrong_passwordThe supplied password did not open the PDF.
422consent_missingconsent_ref is required for every analysis.
422unreadable_statementNo transaction table could be located (for example a blank scan).
429rate_limitedToo many requests. Retry after the seconds given in Retry-After.
500internal_errorUnexpected failure on our side. Safe to retry with the same Idempotency-Key.
503temporarily_unavailablePlanned maintenance or overload. Retry with backoff.

Webhooks and signatures

Events: statement.completed, statement.failed. We retry non-2xx responses with exponential backoff for up to 24 hours. Respond within 10 seconds and process asynchronously.

{
  "id": "evt_01JD4Q8A2C",
  "type": "statement.completed",
  "created_at": "2026-11-07T14:32:04+05:30",
  "data": {
    "id": "stm_01JD4Q7Y3W",
    "status": "completed",
    "risk": {
      "score": 78,
      "band": "high"
    },
    "integrity": {
      "verdict": "review"
    }
  }
}

Every delivery carries an X-Signature header: a timestamp and an HMAC-SHA256 of {timestamp}.{raw body} using your webhook signing secret. Verify against the raw request body before parsing JSON, compare in constant time, and reject timestamps older than five minutes.

javascript
import crypto from "node:crypto";

// header: X-Signature: t=1762506124,v1=5f2c...e9
function verify(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const age = Math.abs(Date.now() / 1000 - Number(parts.t));
  if (age > 300) return false; // reject replays older than 5 minutes
  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${parts.t}.${rawBody}`)
    .digest("hex");
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}

Rate limits

ScopeDefaultNotes
All endpoints120 req / minPer workspace, across keys.
POST /statements/analyze20 req / minConcurrent processing capped at 10 statements.
Sandbox30 req / minMock data only.

Every response includes X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset. On 429, wait for Retry-After seconds. Higher limits are available on request.

Statements contain personal data of the account holder. Under the Digital Personal Data Protection Act, 2023 you act as the Data Fiduciary and Tarazu processes the data on your behalf.

  • 01

    consent_ref is required.

    Every analysis must reference a consent record you hold: who consented, when, for what purpose. Requests without it fail with consent_missing.

  • 02

    Purpose limitation.

    Use results only for the purpose the holder consented to, such as credit underwriting or onboarding checks.

  • 03

    Raw file deleted after processing.

    The uploaded statement and any password are discarded as soon as processing finishes. They are never used for training.

  • 04

    Derived data is configurable.

    Parsed transactions, scores and checks are kept for 30 days by default, configurable from 0 to 180 days per workspace.

  • 05

    Erasure on request.

    Call DELETE /statements/{id} when a holder withdraws consent; derived data is removed within 24 hours.

  • 06

    Data stays in India.

    Processing and storage happen in Indian data centres.

Grievances and data-principal requests: grievance@tarazu.example. Security reports: security@tarazu.example.