# SMS_Analysis_V1 — SMS Financial Intelligence API

**SMS_Analysis_V1** · version `v1` · status `live`

## Authentication

Every request must carry your API key in the `X-API-Key` header. Your key is
issued to you separately by ops and is never shown in this document.

```
X-API-Key: <your-key>
```

## Base URL

All endpoints are served through the gateway:

```
https://api.pehchaantech.com
```

## Endpoints

| Method | Path | Full URL | Timeout |
| --- | --- | --- | --- |
| `POST` | `/sms/v1/ingest` | `https://api.pehchaantech.com/sms/v1/ingest` | 10000 ms |
| `POST` | `/sms/v1/report` | `https://api.pehchaantech.com/sms/v1/report` | 5000 ms |
| `POST` | `/sms/v1/summary` | `https://api.pehchaantech.com/sms/v1/summary` | 5000 ms |

Rate limit: **60 requests/minute** · **100000 requests/day**, counted per API key.

## Overview

Turn a consented SMS inbox into a structured financial-identity profile: income, obligations, credit-seeking behaviour, cash-flow health and 25 more families of derived signal.

Analysis is ASYNCHRONOUS and takes three calls. POST /sms/v1/ingest with the raw messages and you immediately receive an sms_id — submission never waits on analysis, so uploading 50,000 messages is as fast as uploading 50. Then redeem that id at POST /sms/v1/summary (the decision-grade headline, ~2 KB) or POST /sms/v1/report (all 29 families with their underlying evidence, plus cross-family correlations and our own watchlist/registry data joined in). Poll the endpoint you actually want: while analysis is running it returns 202 with status "processing" and a Retry-After header — there is no separate status endpoint.

Two things to internalise before you integrate. First, `null` means NOT DETECTED, not zero — a null monthly income means we could not establish a salary, so gate your rules on the `coverage` map rather than on null checks. Second, the sms_id is a CREDENTIAL: anyone holding it can redeem the analysis, so keep it server-side and never put it in a browser URL or a client-side log. It expires after 7 days.

## Request example

```json
{
  "messages": [
    {
      "body": "Rs.85,000 credited to a/c XX1234 by SALARY ACME PVT LTD",
      "sender": "VK-HDFCBK",
      "sentAtMs": 1751360400000
    },
    {
      "body": "Rs 15000 debited via NACH EMI to PNB HOUSING FINANCE",
      "sender": "AX-HDFCBK",
      "sentAt": "2025-07-05T06:00:00Z"
    }
  ]
}
```

## Response example

```json
{
  "sms_id": "sms_9dQ2mF7xK1pR4vT8zL0bN3sW",
  "status": "processing",
  "success": true,
  "expires_at": "2026-07-28T11:04:12Z",
  "description": "Inbox accepted and queued for analysis",
  "messageCount": 4820
}
```

## Fields

| Field | Description |
| --- | --- |
| `messages` | Required on /ingest. 1–50,000 items, 10 MB max. This is the ENTIRE request body — you do not send a reference number or idempotency key; the sms_id we return identifies the whole lifecycle. |
| `messages[].body` | Required. The raw SMS text, 1–2000 characters. |
| `messages[].sender` | Optional sender header, e.g. VK-HDFCBK. Used for bank and app attribution — supplying it materially improves detection. |
| `messages[].sentAtMs / sentAt` | One is required. sentAtMs is epoch milliseconds and is preferred (no parsing); sentAt accepts any ISO-8601 timestamp. Rows with an empty body OR no parseable timestamp are silently dropped before analysis — you will see this as meta.analysed being lower than meta.messageCount. If those differ a lot, check your timestamp format. |
| `sms_id` | Required on /summary and /report. Returned by /ingest in the body and the X-Sms-Id header. Treat it as a bearer credential; it is sent in the request BODY so it never lands in access logs or Referer headers. Valid for 7 days. |
| `result.coverage` | Summary only. Per-family populated \| empty \| degraded. READ THIS FIRST: null means "not detected", not zero, and coverage is how you tell "we found no salary signal" apart from "the salary is zero". |
| `result (report)` | All 29 families verbatim, including the evidence arrays behind every summary count. bureauChecks[].checks[].evidence carries up to 140 characters of the original SMS for each detected credit enquiry — the only place raw message text is returned. |
| `correlations` | Report only. Cross-family analysis no single detector produces: affordability (total fixed commitment incl. subscriptions, utilities and rent), employerAgreement, creditHunger, stressComposite, lifestyleVsIncome, geoConsistency, wealthCorroboration, confidenceMap. Each carries {value, inputs, confidence}. |
| `referenceData` | Report only. Our reference data joined to theirs: darkPatterns (predatory subscriptions they are actually paying for, with the reason each is flagged and the parent entities that appear on NACH lines), lenderExposure, appTaxonomy, and datasetVersions for reconciliation. |

## Errors

| Code | Meaning |
| --- | --- |
| `processing (HTTP 202)` | On /ingest: accepted and queued. On /summary or /report: analysis is still running — retry after Retry-After (1 s). Typical end-to-end is under a second, so the first or second poll usually succeeds. |
| `completed (HTTP 200)` | Analysis ready — consume result. |
| `no_data (HTTP 200)` | Processed, but nothing in the inbox was analysable. Check your timestamp format and meta.analysed. Do not retry the read. |
| `failed (HTTP 200)` | Analysis failed permanently. Deliberately a 200, not a 5xx: the read itself worked, so retrying it will never help — submit the inbox again. |
| `not_found (HTTP 404)` | Unknown sms_id. Check the value you sent. |
| `expired (HTTP 410)` | The sms_id existed but passed its 7-day retention window. Distinct from 404 on purpose. Submit the inbox again. |
| `422` | Validation failed — empty messages array, over 50,000 messages, a body longer than 2000 chars, or a malformed sms_id. |
| `503 (ingest)` | The ingest queue is at capacity. Retry after Retry-After (2 s) — this is backpressure, not breakage. |
| `401 / 403` | Missing or invalid API key, or the key is not granted this API. Note a uat key on a prod route returns 404, not 403. |
| `429` | Rate limited — respect the Retry-After header. |
| `502 / 504` | Storage or gateway problem — retry with exponential backoff. |

## Changelog

- **2026-07-21** — Initial release: asynchronous three-call flow (ingest → summary / report), 29-family profile, cross-family correlations, dark-pattern and lender reference data, 7-day sms_id retention.

---

_Generated from the pehchaantech developer portal · canonical page: https://docs.pehchaantech.com/docs/sms-analysis-v1 · last updated: 2026-07-21T13:05:12.617Z._
