API overview
The DigiFIR API is the single HTTP interface through which every app, the citizen portal, the officials' console and outside systems reach the backend. It is defined contract-first in OpenAPI 3.1 (openapi/digifir.yaml in the backend repository). The request validators and both SDK clients are generated from that file, and a conformance test fails the build if any operation in the contract has no implementation.
| Item | Value |
|---|---|
| Contract | openapi/digifir.yaml, title "digiFIR common backend API", version 1.0.0 |
| Operations | 230, in 26 groups |
| Base path | /v1 |
| Pilot server | https://api.pilot.digifir.gov.in (host name to be confirmed), hosted in India on NIC/NBF or a MeitY-empanelled cloud |
| Development server | http://127.0.0.1:8080, with simulators for every outside system |
| Formats | JSON; server-sent events for streams; RFC 9457 problem details for errors |
| Licence | Proprietary, Youcric Elite India Pvt. Ltd. |
Operation groups
| Group | Operations | What it covers |
|---|---|---|
| Admin | 40 | Registrar and operator desk: institutions, officials, enrolment, revocation, reference data, configuration, erasure plans |
| Identity | 25 | Citizen identity proofing, DID registration, device keys, consent and contact details |
| Cases | 17 | Case read views shared by police, court and auditors |
| Police | 17 | Police app read views and off-chain operations: desk, cases, evidence, general diary, station tools |
| Court | 16 | Court app read views: inbox, referrals, reports, records, time limits, insights |
| Directory | 14 | Reference data: states, stations, courts, laboratories, sections of law |
| Me | 10 | The signed-in person: profile, preferences, sessions, account deletion |
| Explorer | 10 | The ledger explorer and verifier: blocks, transactions, record history, integrity reports |
| Console | 8 | Linking the desktop console to an official's phone |
| Sessions | 7 | Device-key challenge and response sessions |
| Lab | 7 | Lab app read views: parcels, exhibits, stages, sealed reports |
| Officials | 6 | In-person enrolment of officials' device keys, and the directory lookup before sign-in |
| Files | 6 | Presigned, resumable uploads of encrypted files, and downloads |
| Documents | 5 | Receipts, FIR copies, laboratory report PDFs, BSA s.63 certificates, and registering their hashes |
| Notifications | 5 | Push device registration, preferences, the in-app notification list |
| Actions | 4 | The signed-action relay: one endpoint for every action type, bundles, status |
| Exhibits | 4 | Exhibit and custody-chain read views |
| ESign | 4 | Aadhaar eSign through a CCA-licensed provider for the s.173(1)(ii) signature |
| SimBinding | 4 | The SIM check, used as a risk signal |
| Webhooks | 4 | Inbound callbacks from SMS, email and eSign providers, each with its own signature check |
| Health | 4 | Liveness, readiness, public status, Prometheus metrics |
| Citizen | 3 | Citizen app and portal read views: my complaints, timeline, copies |
| Events | 3 | The server-sent event stream, long polling, the list of channels |
| Geo | 3 | Address and place lookups through an India-hosted provider |
| PrivateReads | 2 | Signed private reads, single and batch |
| Verification | 2 | Public document verification by SHA-256, and checking an official's ID shown on their phone |
Authentication
DigiFIR has no passwords and no long-lived refresh tokens. A session proves possession of a registered device key.
| Scheme | Used for |
|---|---|
bearerAuth | Every signed-in call. An ES256 JWT signed by the backend's HSM-held token key, valid 900 seconds, bound to the device key and the app installation. Roles and jurisdiction are read from the ledger's projection on every request, never from the token, so a revocation or posting change applies as soon as its block is projected. |
X-Digifir-Proofing-Token | Steps of a citizen's identity proofing before a session exists |
X-Digifir-Enrolment-Token | Steps of an official's in-person enrolment |
X-Digifir-Key-Addition-Token | Adding a device key to an existing DID |
internalMtls | Operator routes and /metrics, reachable only through the internal ingress with mutual TLS |
Refreshing a session needs a fresh challenge signed by the same key. Session payloads have their own types (session.open, session.refresh) that are never action types, so a session signature can never be replayed as a ledger action.
Pre-session routes also require app attestation in X-Digifir-Attestation: Play Integrity on Android, App Attest on iPhone, reCAPTCHA Enterprise on the web portal, or key attestation on a managed phone. This replaces Firebase App Check.
Request headers
| Header | Purpose |
|---|---|
X-Digifir-App | <app>/<semver> on every app call. Versions below the configured minimum get 426 and the apps show an "update the app" screen. |
X-Digifir-Install | The installation ID; access tokens are bound to it |
X-Request-Id | Accepted if it is a UUID, otherwise generated; echoed in responses, logs and problem details |
Idempotency-Key | On operations that create off-chain records; actions are idempotent by their own ID |
Prefer: respond-async | Answer 202 at once instead of waiting for the ledger commit |
Errors
Every error is application/problem+json (RFC 9457). An example, sent to the court app when a magistrate decides a final report that was already decided:
{
"type": "https://digifir.in/problems/err-state",
"title": "The record state does not allow this action",
"status": 409,
"code": "ERR_STATE",
"appCode": "ALREADY_DECIDED",
"currentStatus": "COGNIZANCE_TAKEN",
"instance": "urn:digifir:request:7d1c..."
}codeis stable and the same for every client. Chaincode codes (ERR_*) pass through unchanged; the chaincode overview lists them.appCodeis the code the calling app already switches on, chosen byX-Digifir-App, so each app's error handling kept working through the migration from Firebase.- Optional members include
field(the offending member),guard,reason,retryable,retryAfterSeconds,txIdand the raw chaincode error.
Rate limits
Limits are token buckets in Redis, charged before the request body is read. A refusal is 429 RATE_LIMITED with Retry-After and the RateLimit headers.
| Route class | Limit |
|---|---|
| Session challenges and opens | 30 per installation and 300 per IP per hour; 5 failed proofs per key per hour |
| Signed actions | Citizens: 10 new complaints per DID per hour, 120 actions per hour. Officials: 600 per key per hour |
| Private reads | 600 targets per key per hour |
| Public verification | 30 per minute and 300 per day per IP |
| Enrolments and SIM checks | 10 per installation and 60 per IP per hour |
| Everything else | 1 200 per session per hour, or per client address without a session |
Security of the transport
- TLS 1.2 or later with HSTS. Two ingresses: a public one, and an internal one with mutual TLS for operator routes.
- CORS without credentials, limited to configured origins; a strict content security policy on HTML responses.
- Request heads up to 32 KiB; bodies 64 KiB by default and 17 MiB for actions, with tighter limits applied once the action type is known.
Sources: digifir-backend openapi/digifir.yaml, docs/architecture/BACKEND.md sections 1.4, 2, 13 and 15, docs/architecture/NOT_YET.md.