openapi: 3.0.3
info:
  title: FileSure API
  version: "1.0"
  contact:
    name: FileSure Support
    email: helpdesk@filesure.in
    url: https://filesure.in
  license:
    name: Proprietary
    url: https://filesure.in/terms-and-conditions
  description: |
    ## Use with your AI tool

    Most integrations now start in an AI tool, so start there. Create a test key in the [Developer Portal](/portal/dashboard/keys) and put it in an environment variable named `FILESURE_API_KEY` on your machine. Then paste this into Claude, Cursor, Codex or ChatGPT. The prompt tells the tool where the key is, so the key itself never goes into the chat:

    ```text
    Read https://api.filesure.in/llms-full.txt in full before writing any code. It is the complete reference for the FileSure API, which gives programmatic access to the Indian MCA register: companies, LLPs and directors, the documents they have filed, and the financial data extracted from those documents.

    My API key is in the environment variable FILESURE_API_KEY. Read it from there; never print it, never paste it into a file, never ask me to type it. It is a test key: it works on the sandbox companies listed in the reference and nothing is charged.

    Then write a script that takes a company name, resolves it to a CIN, fetches the company's master data, and lists its most recent filings. Follow the reference for the base URL, the header that carries the key, the sandbox identifiers and the response envelope. Do not invent endpoints or fields that are not in the reference.
    ```

    Change the last paragraph to whatever you are building. The reference explains what each call costs and when an unlock is needed, so the tool can tell you before it spends anything. When you are ready for real calls, put a live key in the same variable; those bill your wallet like any other request.

    The same reference is available in four shapes, all generated from the source of this page, so none is ever behind it:

    | What | Where | Use it for |
    |---|---|---|
    | Everything in one file | [`/llms-full.txt`](/llms-full.txt) | Paste or attach the whole reference into one conversation, as above |
    | Index | [`/llms.txt`](/llms.txt) | Hand a tool the map of this reference; it follows the links it needs |
    | One page per endpoint | `/reference/<endpoint>.md`, listed in the index | Tools that index documentation page by page |
    | OpenAPI document | [`/portal/openapi-public.yaml`](/portal/openapi-public.yaml) or [`/portal/openapi-public.json`](/portal/openapi-public.json) | Any tool with an OpenAPI or custom-connector import |

    **Claude.** Paste the prompt, or attach `llms-full.txt` to a Project so every conversation starts with it. A custom connector can import the OpenAPI document.

    **Cursor.** Add `https://api.filesure.in/llms.txt` as a documentation source in Cursor's settings and reference it from chat with `@Docs`, or paste the prompt into a chat.

    **Codex.** Put the prompt in the task, or name `https://api.filesure.in/llms-full.txt` in your project instructions.

    **ChatGPT.** Paste the prompt, give a project `llms-full.txt` as a knowledge file, or import the OpenAPI document as an action.

    All of the above has the tool write code against the API. To let the assistant call FileSure itself, connect it instead: [Connect your AI tool](#description/connect-your-ai-tool).

    ---

    ## Connect your AI tool

    For an assistant that should call FileSure itself rather than write code against it, connect it to the FileSure MCP server. Every action in this reference becomes a tool the assistant can use: find a company, read its record, list its filings, read an extraction, check freshness, and so on. You ask in plain words; the assistant picks the call, makes it with your key, and reads the answer.

    Address: `https://api.filesure.in/mcp`. Tools that can set a header send your key in `Authorization: Bearer` or `x-api-key`; tools that cannot sign in with your FileSure account instead. The setup for each tool is below. The [Developer Portal](/portal/dashboard/keys) shows the same blocks with your key already filled in whenever you create one.

    ### Claude Code

    ```bash
    claude mcp add --transport http filesure https://api.filesure.in/mcp --header "x-api-key: $FILESURE_API_KEY"
    ```

    ### Cursor

    Add to `.cursor/mcp.json` (or the global one in your home folder):

    ```json
    { "mcpServers": { "filesure": { "url": "https://api.filesure.in/mcp", "headers": { "x-api-key": "fsk_test_YOUR_KEY" } } } }
    ```

    ### Codex

    In `~/.codex/config.toml`:

    ```text
    [mcp_servers.filesure]
    url = "https://api.filesure.in/mcp"
    http_headers = { "x-api-key" = "fsk_test_YOUR_KEY" }
    ```

    ### Claude.ai and Claude Desktop

    Add a custom connector with the address `https://api.filesure.in/mcp` and nothing else. The tool sends you to the FileSure portal to sign in with your account; you pick which of your keys it should use and approve, and it is connected. The key itself never leaves FileSure: the tool holds a token bound to that key, and you can disconnect it any time from the API keys page, where connected tools are listed. Revoking the key disconnects it too.

    If a tool cannot sign in, the keyed address is the fallback: `https://api.filesure.in/mcp/k/fsk_test_YOUR_KEY`. A key in a URL can end up in logs and history, so use a test key there, and revoke it from the portal when you are done.

    ### Paid actions ask first

    Unlock, refresh and document fetch cost money. When the assistant reaches for one of them, the connector returns the price at your rate and what it buys instead of running it; the action runs only when the assistant calls again with your confirmation. Nothing is spent without a decision.

    ### Every call is an ordinary API call

    Each tool call goes through the API with your key: it is billed at your rates, shows in your usage, and obeys the sandbox and your rate limit. Start with a test key; the assistant then has every tool on the sandbox companies and nothing is charged.

    What each tool does, what it takes, what it returns and what it costs: [Tools of the connector](#description/tools-of-the-connector) for the rules they share, and the [Tool reference](#description/tool-reference) for each one.

    ---

    <!-- generated: connector tool pages, scripts/generate-tool-docs.ts; do not edit by hand -->

    ## Tools of the connector

    The FileSure connector gives an AI tool sixteen tools. Each one is an ordinary call to this API with your key: it is billed at your rates, shows in your usage, obeys your rate limit, and on a test key works on the sandbox and costs nothing. This page is the rules every tool follows; the [Tool reference](#description/tool-reference) describes each tool. How to connect a tool is on [Connect your AI tool](#description/connect-your-ai-tool).

    ### The tools

    - **Sandbox identifiers**: `list_sandbox_entities`
    - **Look up the register**: `find_company`, `get_company`, `list_filings`, `find_director`, `get_director`
    - **Documents and financials**: `unlock_company`, `get_unlock_status`, `get_filing_document`, `list_extraction_forms`, `list_extraction_years`, `get_extraction`
    - **Keep a company current**: `check_freshness`, `refresh_company`, `fetch_documents`
    - **Your account**: `get_account`

    Tools that read the register are billed per call at your rate for the endpoint behind them; the free ones (`list_sandbox_entities`, `get_unlock_status`, `check_freshness`, `get_account`) cost nothing on either key. Prices in the reference are the default rate card; if your account has negotiated rates, those apply.

    ### Paid actions ask first

    `unlock_company`, `refresh_company`, `fetch_documents` spend money. Each takes a `confirm` flag. Called without it, the tool runs nothing: it looks up your rate for the action and returns a `quote` with the price, what it buys and how to proceed, and nothing is charged. The assistant is told to ask you before calling again with `confirm: true`, which runs the action and charges it. An action that is refused or fails is never charged, whatever the reason.

    ### What a result looks like

    Every tool returns exactly one of four results:

    | Result | When | What it carries |
    |---|---|---|
    | `success` | the API answered with JSON | the endpoint's `data` object unchanged, a one-line summary, and what the call cost and left in the wallet |
    | `file` | a filing PDF | the PDF as a resource, up to 10 MB, with its name, type and size, and a summary |
    | `quote` | a paid action called without `confirm: true` | your price for the action, what it buys, how to proceed; nothing ran |
    | `error` | any refusal or failure | the API's error (`code`, `message` and any extras such as the sandbox lists), the HTTP status, and a plain explanation |

    In the connector, the text of a result is the summary followed by the data itself as JSON, so a client that shows the model only the text still gives it every name and identifier; the same data is also sent as structured content. A PDF larger than the inline limit comes back as a description with its size and the advice to read the extraction instead.

    ### When a call is refused

    An `error` result carries the API's code and message, and the connector adds a line telling the assistant what to do next:

    | Code | What the assistant is told |
    |---|---|
    | `MISSING_API_KEY` | No API key was sent. The connector needs the customer’s key. |
    | `INVALID_API_KEY` | The API key is not recognised. Check it was copied whole. |
    | `API_KEY_REVOKED` | This key was revoked in the developer portal; a new one is needed. |
    | `NO_ACCESS` | The account holds no price for this endpoint, so it cannot call it. Support can enable it. |
    | `SANDBOX_ONLY` | A test key only works on the sandbox companies and directors. The allowed ones are listed in this error (sandboxCompanies, sandboxDirectors) and by list_sandbox_entities; use one of those, or a live key. |
    | `UNLOCK_REQUIRED` | This needs an active company unlock first. unlock_company buys a year of access. |
    | `INSUFFICIENT_BALANCE` | The wallet cannot cover this call. Top up in the developer portal. |
    | `ALREADY_UNLOCKED` | The company is already unlocked; nothing was charged. |
    | `NOTHING_TO_FETCH` | Every listed filing already has its document; nothing to fetch, nothing charged. |
    | `RATE_LIMITED` | Too many requests in the last minute. Wait for the Retry-After seconds and try again. |
    | `NOT_FOUND` | FileSure does not hold this record. |
    | `COMPANY_NOT_FOUND` | FileSure does not hold this company. |
    | `INVALID_CIN` | The identifier failed format validation. |
    | `INVALID_DIN` | The DIN failed format validation. |
    | `DOC_NOT_AVAILABLE` | The document is not on file yet. Check freshness; fetch_documents downloads what is missing. |

    ### Which form of the key each client uses

    | Client | How the connector gets your key | Where it is set up |
    |---|---|---|
    | Claude.ai and Claude Desktop | sign in with your FileSure account and pick a key; no key in the address | [Connect your AI tool](#description/connect-your-ai-tool) |
    | Claude Code | the key in an `x-api-key` header on the connector address | [Connect your AI tool](#description/connect-your-ai-tool) |
    | Cursor | the key in an `x-api-key` header on the connector address | [Connect your AI tool](#description/connect-your-ai-tool) |
    | Codex | the key in an `x-api-key` header on the connector address | [Connect your AI tool](#description/connect-your-ai-tool) |
    | Anything that can send neither | the keyed address, kept as a fallback | [Connect your AI tool](#description/connect-your-ai-tool) |

    Revoking the key in the developer portal disconnects every tool that used it.

    ---

    ## Tool reference

    One entry per tool, generated from the definitions the connector serves, in the order the connector lists them. "Rate" is the default rate card for the endpoint behind the tool; negotiated rates apply if your account has them. "Inputs" are what the assistant passes; identifiers are validated with the same rules as the API, so a malformed CIN or DIN is refused before any call.

    ### list_sandbox_entities

    Call this first when the account is on a test key (fsk_test_). Lists every company and director a test key can use: legal name, the brand it is known by, identifier, status, city and state for companies; name and DIN for directors. Free, on test and live keys. On a test key, find_company and find_director search only this set, and any other identifier is refused with SANDBOX_ONLY.

    - **Calls:** `GET /v1/sandbox`
    - **Rate:** Free
    - **Kind:** read-only
    - **Results:** `success`, `error`

    No inputs.

    ---

    ### find_company

    Turn a company name (or a CIN, FCIN or LLPIN you are unsure about) into ranked candidates with their identifier, status and registered address. Call this first whenever you only have a name; every other company tool needs the identifier it returns. On a test key it searches only the sandbox companies (see list_sandbox_entities), by legal name or brand. Billed per call at the account’s companies.resolve rate.

    - **Calls:** `GET /v1/companies/resolve`
    - **Rate:** ₹5 a call (`companies.resolve`)
    - **Kind:** read-only
    - **Results:** `success`, `error`

    | Input | Type | Required | Meaning |
    |---|---|---|---|
    | `query` | string | yes | Company name, or an exact CIN, FCIN or LLPIN. |
    | `state` | string | no | Narrow to a registered state, e.g. Maharashtra. |
    | `city` | string | no | Narrow to a registered city. |
    | `limit` | integer | no | How many candidates to return, 1 to 20. Default 10. |

    ---

    ### get_company

    The company’s master data as MCA publishes it: names, status, class, capital, registered address, directors with their roles, and charges. Use for any question about what a company is; use list_filings for what it has filed and get_extraction for financial figures. Billed per call at the account’s companies.master rate. Dates in this record are MM/DD/YYYY.

    - **Calls:** `GET /v1/companies/{cin}`
    - **Rate:** ₹5 a call (`companies.master`)
    - **Kind:** read-only
    - **Results:** `success`, `error`

    | Input | Type | Required | Meaning |
    |---|---|---|---|
    | `companyId` | string | yes | Company identifier: CIN, FCIN or LLPIN, as returned by find_company. |
    | `idType` | one of `cin`, `fcin`, `llpin` | no | Set only to force tighter validation; auto-detected otherwise. |

    ---

    ### list_filings

    The list of documents a company has filed with MCA (form, date, category, pages), paginated, with a filingId per row for get_filing_document. This is the index, not the documents. Rows are ordered by when FileSure recorded them, newest first; sort on dateOfFiling (DD/MM/YYYY) yourself for strict date order. Billed per call at the account’s companies.filings.list rate.

    - **Calls:** `GET /v1/companies/{cin}/filings`
    - **Rate:** ₹5 a call (`companies.filings.list`)
    - **Kind:** read-only
    - **Results:** `success`, `error`

    | Input | Type | Required | Meaning |
    |---|---|---|---|
    | `companyId` | string | yes | Company identifier: CIN, FCIN or LLPIN, as returned by find_company. |
    | `page` | integer | no | Page number, from 1. |
    | `limit` | integer | no | Rows per page, up to 200. Default 50. |
    | `formId` | string | no | Only this form, e.g. AOC-4, MGT-7, LLP Form 8. |
    | `year` | integer | no | Only filings for this calendar year. |
    | `documentCategory` | string | no | Only this MCA document category. |

    ---

    ### find_director

    Turn a person’s name into ranked director candidates with their DIN and current companies. Call this first when you only have a name; get_director needs the DIN it returns. On a test key it searches only the sandbox directors (see list_sandbox_entities). Billed per call at the account’s directors.resolve rate.

    - **Calls:** `GET /v1/directors/resolve`
    - **Rate:** ₹5 a call (`directors.resolve`)
    - **Kind:** read-only
    - **Results:** `success`, `error`

    | Input | Type | Required | Meaning |
    |---|---|---|---|
    | `query` | string | yes | Director name, or an exact 8-digit DIN. |
    | `limit` | integer | no | How many candidates to return, 1 to 20. Default 10. |

    ---

    ### get_director

    A director’s profile as MCA holds it (name, nationality, qualification, DIN status) and the companies they are associated with. Phone and email are not here; they sit behind a separate paid contact unlock that this connector does not offer. Billed per call at the account’s directors.profile rate.

    - **Calls:** `GET /v1/directors/{din}`
    - **Rate:** ₹5 a call (`directors.profile`)
    - **Kind:** read-only
    - **Results:** `success`, `error`

    | Input | Type | Required | Meaning |
    |---|---|---|---|
    | `din` | string | yes | Director Identification Number, 8 digits, as returned by find_director. |

    ---

    ### unlock_company

    Buy a year of access to every document this company has filed, in any year, and start the first download; extractions become readable as documents land. This costs money (₹330 by default). Call without confirm to get the account’s price and what it buys; ask the user; then call with confirm: true. Re-unlocking an unlocked company is refused free of charge.

    - **Calls:** `POST /v1/companies/{cin}/unlock`
    - **Rate:** ₹330 (`companies.unlock`)
    - **Kind:** paid action, asks first
    - **Results:** `success`, `quote` (without `confirm: true`), `error`

    | Input | Type | Required | Meaning |
    |---|---|---|---|
    | `companyId` | string | yes | Company identifier: CIN, FCIN or LLPIN, as returned by find_company. |
    | `confirm` | boolean | no | This action costs money. Leave unset to get the price and what it buys without doing anything; set true, after the user agrees, to run it. |

    ---

    ### get_unlock_status

    Whether the account holds an active unlock for this company, until when, the price of one if not, and the progress of the document download behind it (pending, in_progress, success). Free. Poll this after unlock_company or fetch_documents to know when documents and extractions are ready.

    - **Calls:** `GET /v1/companies/{cin}/unlock`
    - **Rate:** Free
    - **Kind:** read-only
    - **Results:** `success`, `error`

    | Input | Type | Required | Meaning |
    |---|---|---|---|
    | `companyId` | string | yes | Company identifier: CIN, FCIN or LLPIN, as returned by find_company. |

    ---

    ### get_filing_document

    The PDF of one filing, by the filingId from list_filings. Needs an active unlock (otherwise returns the unlock state, free). Use it only when the user wants the source document; for any figure, prefer get_extraction, which is structured and small. PDFs over 10 MB come back as a description with size and pages instead of bytes. Billed per call at the account’s companies.filings.download rate.

    - **Calls:** `GET /v1/companies/{cin}/filings/{filingId}/download`
    - **Rate:** 5 paisa a call (`companies.filings.download`)
    - **Kind:** read-only
    - **Results:** `file` (the PDF), or `success` when no document is returned, `error`

    | Input | Type | Required | Meaning |
    |---|---|---|---|
    | `companyId` | string | yes | Company identifier: CIN, FCIN or LLPIN, as returned by find_company. |
    | `filingId` | string | yes | The filingId of a row from list_filings. |

    ---

    ### list_extraction_forms

    Which form types have structured data extracted for this company (for example AOC-4 financial statements, MGT-7 annual returns, PAS-3 allotments, charges). Start here before get_extraction. Needs an active unlock (otherwise returns the unlock state). Billed per call at the account’s companies.extractions.list rate.

    - **Calls:** `GET /v1/companies/{cin}/extractions`
    - **Rate:** 5 paisa a call (`companies.extractions.list`)
    - **Kind:** read-only
    - **Results:** `success`, `error`

    | Input | Type | Required | Meaning |
    |---|---|---|---|
    | `companyId` | string | yes | Company identifier: CIN, FCIN or LLPIN, as returned by find_company. |

    ---

    ### list_extraction_years

    For one form type, which years have extracted data (for AOC-4 and MGT-7), or the latest snapshot and filings (PAS-3), or the per-charge summaries (charges). Use the formType names from list_extraction_forms. Needs an active unlock. Billed per call at the account’s companies.extractions.years rate.

    - **Calls:** `GET /v1/companies/{cin}/extractions/{formType}`
    - **Rate:** 5 paisa a call (`companies.extractions.years`)
    - **Kind:** read-only
    - **Results:** `success`, `error`

    | Input | Type | Required | Meaning |
    |---|---|---|---|
    | `companyId` | string | yes | Company identifier: CIN, FCIN or LLPIN, as returned by find_company. |
    | `formType` | string | yes | A form type from list_extraction_forms, e.g. AOC-4. |

    ---

    ### get_extraction

    The structured data extracted from one filing: balance sheet, profit and loss, shareholding, allotments, and more, as JSON. Use this for any figure or fact from a filing; it is precise and small, unlike the PDF. Pick formType and year from list_extraction_years. Needs an active unlock. Billed per call at the account’s companies.extractions.data rate.

    - **Calls:** `GET /v1/companies/{cin}/extractions/{formType}/{year}`
    - **Rate:** 5 paisa a call (`companies.extractions.data`)
    - **Kind:** read-only
    - **Results:** `success`, `error`

    | Input | Type | Required | Meaning |
    |---|---|---|---|
    | `companyId` | string | yes | Company identifier: CIN, FCIN or LLPIN, as returned by find_company. |
    | `formType` | string | yes | A form type from list_extraction_forms, e.g. AOC-4. |
    | `year` | integer | yes | A year from list_extraction_years. |
    | `scope` | one of `standalone`, `consolidated` | no | For financial statements: standalone (default) or consolidated. |

    ---

    ### check_freshness

    How current FileSure’s copy of this company is: when the record was last refreshed, when the filing list was last read from MCA, how many listed filings have no document on file, whether a refresh is running or still fresh, and the account’s unlock state. Free. Ask this before refresh_company or fetch_documents so you only pay when it is worth it.

    - **Calls:** `GET /v1/companies/{cin}/freshness`
    - **Rate:** Free
    - **Kind:** read-only
    - **Results:** `success`, `error`

    | Input | Type | Required | Meaning |
    |---|---|---|---|
    | `companyId` | string | yes | Company identifier: CIN, FCIN or LLPIN, as returned by find_company. |

    ---

    ### refresh_company

    Re-read this company’s record and filing list from MCA right now: master data, directors, common data and the list of filings. It fetches no documents. One refresh per company per 24 hours is shared by everyone, so inside that window you get the recent result. This costs money (₹1 by default). Call without confirm for the price; then with confirm: true after the user agrees. Use check_freshness first.

    - **Calls:** `POST /v1/companies/{cin}/update`
    - **Rate:** ₹1 (`companies.update`)
    - **Kind:** paid action, asks first
    - **Results:** `success`, `quote` (without `confirm: true`), `error`

    | Input | Type | Required | Meaning |
    |---|---|---|---|
    | `companyId` | string | yes | Company identifier: CIN, FCIN or LLPIN, as returned by find_company. |
    | `confirm` | boolean | no | This action costs money. Leave unset to get the price and what it buys without doing anything; set true, after the user agrees, to run it. |

    ---

    ### fetch_documents

    Download the documents an unlocked company is still missing, typically filings made after the unlock. Needs the account’s active unlock; refused free of charge when nothing is missing. This costs money (₹150 by default). Call without confirm for the price; then with confirm: true after the user agrees. check_freshness tells you how many documents are missing.

    - **Calls:** `POST /v1/companies/{cin}/documents/fetch`
    - **Rate:** ₹150 (`companies.documents.fetch`)
    - **Kind:** paid action, asks first
    - **Results:** `success`, `quote` (without `confirm: true`), `error`

    | Input | Type | Required | Meaning |
    |---|---|---|---|
    | `companyId` | string | yes | Company identifier: CIN, FCIN or LLPIN, as returned by find_company. |
    | `confirm` | boolean | no | This action costs money. Leave unset to get the price and what it buys without doing anything; set true, after the user agrees, to run it. |

    ---

    ### get_account

    The account’s wallet balance and its usage over the last 30 days, plus the price it pays for every endpoint. Free. Use it to answer "what will this cost me" and "how much is left" before a paid action.

    - **Calls:** `GET /v1/account/usage and GET /v1/account/pricing`
    - **Rate:** Free
    - **Kind:** read-only
    - **Results:** `success`, `error`

    No inputs.

    ---

    <!-- generated: end of connector tool pages -->

    ## How FileSure data works

    FileSure gives you programmatic access to what the Ministry of Corporate Affairs (MCA) holds on every company, LLP and director in India. Everything the API returns is one of three kinds of thing, and knowing which one you are asking for tells you what it costs and whether you need to unlock anything first.

    ![Three kinds of data and the one gate between them.](/portal/diagrams/data-tiers.svg)


    ### The register: plain JSON, no unlock

    The record MCA keeps on a company or director: name, status, registered address, capital, directors, charges, and the **list** of documents the company has filed. For a director: profile and the companies they sit on. This is what most integrations need, and it is just a call: ₹5, answered from what we hold, returned as JSON.

    ### Documents: per company, behind an unlock

    The filings themselves, as PDFs. MCA charges a fee to hand these over, so we do not hold every PDF for every company in advance. You **unlock** a company once (₹330), we fetch every document it has ever filed from MCA, and you can download any of them for a year. The price is for the whole history, not per year of filings.

    ![What an unlock gives you, and for how long.](/portal/diagrams/unlock-lifecycle.svg)


    ### Extractions: structured data from those documents

    Financial statements, shareholding, charges and other forms, read out of the PDFs into JSON. Extractions come with the unlock: once a company's documents are in, its extractions are queryable too, at 5 paisa a call.

    ### Time: how current is what we hold?

    The register is a copy, and copies age. Three calls deal with that, and they are the only part of the API that costs money without returning data:

    - **Freshness check** (free) tells you how old our record is, when the filing list was last read from MCA, and how many listed filings we do not yet hold a PDF for. Ask this first; it tells you whether either of the next two is worth paying for.
    - **Refresh** (₹1) re-reads the company's record and filing list from MCA right now. It refreshes the register for everyone, which is why it costs a token ₹1 rather than a price. One refresh per company per 24 hours; if someone refreshed it before you inside that window, you get their result.
    - **Fetch new documents** (₹150) downloads the filings that appeared since your unlock. Needs your active unlock; if nothing is missing, it costs nothing.

    Four words, used the same way everywhere in this reference: **lookup** (read the register), **unlock** (buy a year of a company's documents), **refresh** (bring its record up to date), **fetch** (download what is missing).

    ![The life of one company on your account.](/portal/diagrams/company-lifecycle.svg)


    ---

    ## Quickstart

    Everything below runs on a test key against a sandbox company, so it costs nothing. Swap in a live key and the same calls work on any company in India.

    **1. Turn a name into a CIN.**

    ```bash
    curl "https://api.filesure.in/v1/companies/resolve?q=cars24" \
      -H "x-api-key: fsk_test_…"
    ```

    ```json
    {
      "data": { "candidates": [
        { "cin": "U74999HR2015FTC056386", "company": "CARS24 SERVICES PRIVATE LIMITED", "companyStatus": "Active" }
      ] },
      "meta": { "requestId": "…", "priceChargedPaisa": 0, "walletBalanceAfterPaisa": 0 }
    }
    ```

    **2. Read the register.**

    ```bash
    curl "https://api.filesure.in/v1/companies/U74999HR2015FTC056386" \
      -H "x-api-key: fsk_test_…"
    ```

    ```json
    {
      "data": {
        "cin": "U74999HR2015FTC056386",
        "company": "CARS24 SERVICES PRIVATE LIMITED",
        "masterData": {
          "companyData": { "companyStatus": "Active", "dateOfIncorporation": "08/12/2015",
                           "authorisedCapital": 100000000, "paidupCapital": 76934000, "classOfCompany": "Private" },
          "directorData": [ { "DIN": "07347299", "FirstName": "VIKRAM", "LastName": "CHOPRA", "dateOfAppointment": "08/12/2015" } ],
          "indexChargesData": [ { "chName": "HDFC BANK LIMITED", "chargeAmount": 5000000000, "dateOfCreation": "03/15/2022" } ]
        }
      },
      "meta": { "requestId": "…", "priceChargedPaisa": 0, "walletBalanceAfterPaisa": 0 }
    }
    ```

    Abridged: the real response carries every field in the MCA record, named exactly as MCA names them.

    **3. See what it has filed.**

    ```bash
    curl "https://api.filesure.in/v1/companies/U74999HR2015FTC056386/filings?limit=3" \
      -H "x-api-key: fsk_test_…"
    ```

    ```json
    {
      "data": [
        { "filingId": "flg_…", "formId": "AOC-4", "dateOfFiling": "28/11/2025", "year": 2025 },
        { "filingId": "flg_…", "formId": "MGT-7", "dateOfFiling": "27/11/2025", "year": 2025 }
      ],
      "meta": { "page": 1, "limit": 3, "total": 654, "requestId": "…" }
    }
    ```

    That is the register, and for many integrations it is all you need. The `filingId` on each row is the handle for the PDF behind it; downloading one is where the unlock comes in, and the next page tells you which call to reach for.

    ---

    ## Which call do I need?

    Three questions, and every path ends at an endpoint:

    ![Which call do I need?](/portal/diagrams/which-call.svg)


    Or, as a table:

    | You want to… | Call | Needs an unlock? | Cost |
    |---|---|---|---|
    | See what a test key can use | `GET /v1/sandbox` | No | Free |
    | Turn a company name into a CIN | `GET /v1/companies/resolve` | No | ₹5 |
    | Get a company's details, directors and charges | `GET /v1/companies/{cin}` | No | ₹5 |
    | See what a company has filed (the list, not the PDFs) | `GET /v1/companies/{cin}/filings` | No | ₹5 |
    | Turn a director's name into a DIN | `GET /v1/directors/resolve` | No | ₹5 |
    | Get a director's profile and companies | `GET /v1/directors/{din}` | No | ₹5 |
    | Download a filing PDF | `GET …/filings/{filingId}/download` | Yes, company | 5 paisa |
    | Get financials or other extracted data | `GET …/extractions/…` | Yes, company | 5 paisa |
    | Open a company's documents for a year | `POST /v1/companies/{cin}/unlock` | Creates one | ₹330 |
    | Know whether our copy is current | `GET /v1/companies/{cin}/freshness` | No | Free |
    | Bring a company's record up to date | `POST /v1/companies/{cin}/update` | No | ₹1 |
    | Download filings made since your unlock | `POST /v1/companies/{cin}/documents/fetch` | Yes, company | ₹150 |
    | Get a director's phone and email | `POST /v1/directors/{din}/unlock` then `GET …/contact` | Yes, director | ₹299, then 5 paisa |
    | Check your balance and usage, top up | `/v1/account/…` | No | Free |

    A typical first integration is three calls: resolve the name, read the master data, list the filings. If you need the PDFs or the numbers inside them, add the unlock. If you keep watching the same companies, add the freshness check and refresh.

    ---

    ## Keys and the sandbox

    Every request carries your key in the `x-api-key` header. Keys come in two kinds and you can hold several of each:

    - **Live keys** (`fsk_live_…`) work on any company or director and are billed against your wallet.
    - **Test keys** (`fsk_test_…`) work only on a fixed list of well-known companies and directors, are never billed, and return the same shapes as live keys. `GET /v1/sandbox` lists them. Use them to build and test; switch the key when you go live. A test key outside the list gets `403 SANDBOX_ONLY`, and that error carries the list.

    Create and revoke keys from the developer portal. Revoking a key takes effect immediately.

    Every key is rate-limited: 60 requests a minute on a live key, 30 on a test key. Past that you get `429 RATE_LIMITED` with a `Retry-After` header and nothing is charged. The refresh has its own, lower ceiling, stated on its page.

    Generate keys in the [Developer Portal](/portal/dashboard/keys).

    ---

    ## Testing with a test key

    A test key never reaches a company outside the sandbox, so the first call is to see what the sandbox holds. Everything below runs on a test key and costs nothing; the same calls work on a live key against any company in India.

    1. **See what you can use.** `GET /v1/sandbox` returns the sandbox companies (legal name, the brand each is known by, identifier, status, city and state) and directors (name and DIN).
    2. **Find the company.** `GET /v1/companies/resolve?q=zomato` searches only the sandbox on a test key, by legal name or brand, and returns Eternal Limited with its CIN. When nothing matches, the response carries a `hint` with the list's address instead of a live company you could not go on to use.
    3. **Read the record.** `GET /v1/companies/{cin}` for the master data, directors and charges.
    4. **See what it has filed.** `GET /v1/companies/{cin}/filings` for the filing index; each row carries a `filingId` for the download.
    5. **Read the numbers.** `GET /v1/companies/{cin}/extractions` lists the form types with extracted data, `…/extractions/AOC-4` the years, and `…/extractions/AOC-4/{year}` the financial statements as JSON. A test key counts as unlocked on every sandbox company, so these and `GET …/filings/{filingId}/download` answer without an unlock.

    In the connector the same chain is `list_sandbox_entities`, `find_company`, `get_company`, `list_filings`, `list_extraction_forms`, `list_extraction_years`, `get_extraction`.

    What the set holds: every company has its documents on file and AOC-4 and MGT-7 data, most of them across many years. The two LLPs hold their filed documents but no extractions, because LLPs file neither form. Zepto, incorporated in 2024, has a single year of statements.

    A `403 SANDBOX_ONLY` on any call means the identifier is outside the sandbox. The error carries `sandboxCompanies`, `sandboxDirectors` and `nextStep`, so you can pick a working identifier from the response itself.

    ---

    ## Billing and the wallet

    ![What happens to every request, and where it can be refused for free.](/portal/diagrams/request-flow.svg)


    Your account has a prepaid wallet in rupees. Every billed call deducts its price when it succeeds; a call that fails is never charged, whatever the reason. The response tells you what happened: `meta.priceChargedPaisa` is what the call cost and `meta.walletBalanceAfterPaisa` is what is left, both in paisa (₹1 = 100 paisa).

    Prices are per endpoint and per account. The defaults are on the rate card; if your account has negotiated rates, those apply instead. An endpoint your account has no price for returns `403 NO_ACCESS`. That is how access is controlled; there is no separate permission list.

    Unlocks are the one purchase that lasts: a company unlock is one year of documents and extractions for that CIN, and every download or extraction inside that year is only the per-call charge. Re-unlocking an already-unlocked company returns `409 ALREADY_UNLOCKED` and charges nothing.

    When the wallet cannot cover a call you get `402 INSUFFICIENT_BALANCE` and nothing runs. Top up from the portal or with `POST /v1/account/wallet/recharge`.

    ---

    ## Responses and errors

    Every response is JSON with the same envelope: `{ data, meta }` on success, `{ error, meta }` on failure. `meta.requestId` is on every response and is also sent as the `X-Request-ID` header; quote it when you write to support.

    Errors carry a machine-readable `error.code` and a human `error.message`. A `403 NO_ACCESS` also carries `error.endpoint` and `error.catalogPricePaisa`, the default price of the endpoint you were refused, so you have a number to quote when asking for access. The codes you will meet most:

    | Code | Status | Meaning |
    |---|---|---|
    | `MISSING_API_KEY` / `INVALID_API_KEY` / `API_KEY_REVOKED` | 401 | No key, an unknown key, or a revoked one |
    | `INVALID_CIN` / `INVALID_DIN` / `INVALID_ID_TYPE` | 400 | The identifier (or the `idType` query value) failed format validation |
    | `NOT_FOUND` / `COMPANY_NOT_FOUND` | 404 | We do not hold that company or director |
    | `NO_ACCESS` | 403 | Your account has no price for this endpoint |
    | `SANDBOX_ONLY` | 403 | Test key used outside the sandbox; the error lists the sandbox identifiers and a `nextStep` |
    | `UNLOCK_REQUIRED` | 403 | This call needs an active unlock first |
    | `INSUFFICIENT_BALANCE` | 402 | Wallet cannot cover the call |
    | `ALREADY_UNLOCKED` | 409 | You already hold an active unlock |
    | `NOTHING_TO_FETCH` | 409 | Every listed filing already has its PDF |
    | `RATE_LIMITED` | 429 | Slow down; `Retry-After` says by how much |

    Lists are paginated with `?page=` and `?limit=` (default 50, maximum 200). Identifiers are the 21-character CIN, the 6-character FCIN for foreign companies, the 8-character LLPIN for LLPs, and the 8-digit DIN for directors.

    Dates arrive as MCA strings and the format depends on the source: company, director and charge records use MM/DD/YYYY (for example `dateOfIncorporation`), while filing records use DD/MM/YYYY (`dateOfFiling`). Each field's description says which. The endpoint examples abbreviate `meta`; the real response always carries `requestId`, and billed calls carry `priceChargedPaisa` and `walletBalanceAfterPaisa` as well.

    ---

    ## Support

    - **Service status**: [filesure-api.checkly-status-page.com](https://filesure-api.checkly-status-page.com) shows whether the API and the MCP connector are working, with incident updates and uptime history. Subscribe there to hear about incidents by email.
    - **Email**: [helpdesk@filesure.in](mailto:helpdesk@filesure.in)
    - **Phone**: +91 8104946419
    - **Website**: [filesure.in](https://filesure.in)

servers:
  - url: https://api.filesure.in
    description: Production
  - url: http://localhost:3000
    description: Local dev (default port)
security:
  - apiKey: []
tags:
  - name: Sandbox identifiers
    description: |
      Test API keys (`fsk_test_*`) work only on the companies, LLPs and directors
      below. Calls return real MCA data with no wallet deduction. Any other
      identifier returns `403 SANDBOX_ONLY`, and that error carries these same
      lists so a caller can recover without leaving the API. Use them for
      integration testing without spending credits.

      You do not need to copy this page: `GET /v1/sandbox` returns the same
      companies and directors as JSON, free on either key. On a test key the name
      searches (`GET /v1/companies/resolve`, `GET /v1/directors/resolve`) look
      only at this set, by legal name or by the brand in the "Known as" column.
      The flow is written up under [Testing with a test key](#description/testing-with-a-test-key).

      Live keys (`fsk_live_*`) are not restricted and follow each endpoint's
      pricing behavior (billed or free, as documented per endpoint).

      ## Sandbox CINs & LLPINs (52)

      | CIN | Company | Known as |
      |---|---|---|
      | U74999HR2015FTC056386 | CARS24 SERVICES PRIVATE LIMITED | Cars24 |
      | U51109KA2012PTC066107 | FLIPKART INTERNET PRIVATE LIMITED | Flipkart |
      | U62099KA2013PLC097389 | RAZORPAY SOFTWARE LIMITED | Razorpay |
      | L74900KA2015PLC082263 | MEESHO LIMITED | Meesho |
      | L33100DL2008PLC178355 | LENSKART SOLUTIONS LIMITED | Lenskart |
      | U72200KA2015PTC082063 | SORTING HAT TECHNOLOGIES PRIVATE LIMITED | Unacademy |
      | U74900GJ2015PTC107035 | OYO HOTELS AND HOMES PRIVATE LIMITED | OYO |
      | U63090GJ2012PLC107088 | ORAVEL STAYS LIMITED | OYO parent, Oravel Stays |
      | U93090MH2018PTC308253 | DREAMPLUG TECHNOLOGIES PRIVATE LIMITED | CRED |
      | U74900DL2009PTC189166 | RKSV SECURITIES INDIA PRIVATE LIMITED | Upstox |
      | U72900KA2016PTC093868 | HIVELOOP TECHNOLOGY PRIVATE LIMITED | Udaan |
      | U72900MH2007PTC171875 | SPORTA TECHNOLOGIES PRIVATE LIMITED | Dream11 |
      | U74999KA2015PTC103797 | MOHALLA TECH PRIVATE LIMITED | ShareChat |
      | U72900KA2011PTC060216 | INMOBI TECHNOLOGY SERVICES PRIVATE LIMITED | InMobi |
      | U60100MH2019PLC323444 | API HOLDINGS LIMITED | PharmEasy |
      | U72900KA2010PTC086596 | ANI TECHNOLOGIES PRIVATE LIMITED | Ola Cabs |
      | L40100KA2013PLC093769 | ATHER ENERGY LIMITED | Ather Energy |
      | U52210TG2015PTC097115 | ROPPEN TRANSPORTATION SERVICES PRIVATE LIMITED | Rapido |
      | U72900KA2011PTC060958 | VEDANTU INNOVATIONS PRIVATE LIMITED | Vedantu |
      | U74999TN2016PTC176669 | CUREFIT HEALTHCARE PRIVATE LIMITED | Cult.fit |
      | U51101MH2011PTC224903 | MANASH LIFESTYLE PRIVATE LIMITED | Purplle |
      | U52300MH2013PLC249758 | IMAGINE MARKETING LIMITED | boAt |
      | U74900KA2014PTC077652 | NOBROKER TECHNOLOGIES SOLUTIONS PRIVATE LIMITED | NoBroker |
      | L74140DL2014PLC274413 | URBAN COMPANY LIMITED | Urban Company |
      | U74999DL2018PTC331205 | RESILIENT INNOVATIONS PRIVATE LIMITED | BharatPe |
      | U74110KA2016PTC120161 | ACKO TECHNOLOGY & SERVICES PRIVATE LIMITED | Acko |
      | U74999KA2018FTC113333 | GALACTUS FUNWARE TECHNOLOGY PRIVATE LIMITED | MPL |
      | U74140MH2019PTC328769 | AMICA FINANCIAL TECHNOLOGIES PRIVATE LIMITED | Jupiter |
      | U80902MH2012PTC258559 | UPGRAD EDUCATION PRIVATE LIMITED | upGrad |
      | U24299DL2021PTC380760 | GLOBALBEES BRANDS PRIVATE LIMITED | GlobalBees |
      | U66000MH2013PTC249565 | TURTLEMINT INSURANCE BROKING SERVICES PRIVATE LIMITED | Turtlemint |
      | U74999MH2012PTC237035 | LEADERSHIP BOULEVARD PRIVATE LIMITED | LEAD School |
      | U74130KA2010PTC052192 | INNOVATIVE RETAIL CONCEPTS PRIVATE LIMITED | BigBasket |
      | U51909KA2011PTC060707 | SUPERMARKET GROCERY SUPPLIES PRIVATE LIMITED | BigBasket supply arm |
      | U62099KA2024PTC194937 | KIRANAKART SOFTWARE SOLUTIONS PRIVATE LIMITED | Zepto |
      | U65999DL2019FTC353020 | PINE LABS FINANCE PRIVATE LIMITED | Pine Labs |
      | U74900KA2015PTC080321 | DELIGHTFUL GOURMET PRIVATE LIMITED | Licious |
      | U28100KA2020PTC135505 | ZETWERK FABPLUS PRIVATE LIMITED | Zetwerk |
      | U74900TG2015PTC101793 | DARWINBOX DIGITAL SOLUTIONS PRIVATE LIMITED | Darwinbox |
      | U72900DL2018PTC331409 | POSTMAN MEDIA PRIVATE LIMITED | Postman |
      | U74140GJ2015PLC154393 | OFB TECH LIMITED | OfBusiness |
      | U65990DL2022PTC401899 | OXYZO FINVEST PRIVATE LIMITED | Oxyzo |
      | U72900TN2020PTC137251 | CREDAVENUE PRIVATE LIMITED | Yubi, formerly CredAvenue |
      | U67200KA2017PTC166507 | OPEN FINANCIAL TECHNOLOGIES PRIVATE LIMITED | Open |
      | U72900KA2015PTC080871 | GARAGEPRENEURS INTERNET PRIVATE LIMITED | slice |
      | L93030DL2010PLC198141 | ETERNAL LIMITED | Eternal, formerly Zomato |
      | L74110KA2013PLC096530 | SWIGGY LIMITED | Swiggy |
      | L52600MH2012PLC230136 | FSN E-COMMERCE VENTURES LIMITED | Nykaa |
      | L72200DL2000PLC108985 | ONE 97 COMMUNICATIONS LIMITED | Paytm |
      | L63090DL2011PLC221234 | DELHIVERY LIMITED | Delhivery |
      | ACK-2998 | SUGEE TWENTY SEVEN DEVELOPERS LLP |  |
      | ACX-7976 | ZALPE FOOD & BEVERAGES LLP |  |

      ## Sandbox DINs (451)

      Derived from the directors of the sandbox CINs above.

      | DIN | DIN | DIN | DIN | DIN | DIN |
      |---|---|---|---|---|---|
      | 00002157 | 00002615 | 00002803 | 00003423 | 00003633 | 00003882 |
      | 00004223 | 00004771 | 00006486 | 00007347 | 00008886 | 00010499 |
      | 00012214 | 00012870 | 00013580 | 00017880 | 00017944 | 00018234 |
      | 00022157 | 00024141 | 00031034 | 00036043 | 00037022 | 00040491 |
      | 00040789 | 00046081 | 00054553 | 00056826 | 00058105 | 00059201 |
      | 00059877 | 00062650 | 00065640 | 00067073 | 00074964 | 00108347 |
      | 00109854 | 00118188 | 00118324 | 00125058 | 00133351 | 00162957 |
      | 00177699 | 00187429 | 00222708 | 00253613 | 00272372 | 00281547 |
      | 00307229 | 00322784 | 00337276 | 00361030 | 00381741 | 00394065 |
      | 00405142 | 00466521 | 00507827 | 00508259 | 00521511 | 00555052 |
      | 00570124 | 00644360 | 00677638 | 00677965 | 00706336 | 00754512 |
      | 00766821 | 00863123 | 00871445 | 01049871 | 01096264 | 01099294 |
      | 01113742 | 01164185 | 01173669 | 01237902 | 01243445 | 01338251 |
      | 01338477 | 01384344 | 01388140 | 01432123 | 01449885 | 01461055 |
      | 01469375 | 01494407 | 01592796 | 01653176 | 01679598 | 01730685 |
      | 01755822 | 01797971 | 01802995 | 01827653 | 01837379 | 01874769 |
      | 01893686 | 01902890 | 01913013 | 01930079 | 01947911 | 02005518 |
      | 02014353 | 02040991 | 02046291 | 02057007 | 02069428 | 02070081 |
      | 02092948 | 02102783 | 02122751 | 02124077 | 02126100 | 02131404 |
      | 02132315 | 02144558 | 02159016 | 02175753 | 02181034 | 02227607 |
      | 02242466 | 02249682 | 02339751 | 02356492 | 02376801 | 02442753 |
      | 02466181 | 02470016 | 02499607 | 02528942 | 02590433 | 02613583 |
      | 02670178 | 02741174 | 02748363 | 02844650 | 02848515 | 02853367 |
      | 02853403 | 02870609 | 02945481 | 02968574 | 02993708 | 03024803 |
      | 03090626 | 03090814 | 03098172 | 03103474 | 03118947 | 03145392 |
      | 03172733 | 03258070 | 03266967 | 03284823 | 03287473 | 03328890 |
      | 03341028 | 03399650 | 03404629 | 03430136 | 03431848 | 03440936 |
      | 03441515 | 03450221 | 03488061 | 03523267 | 03534101 | 03545900 |
      | 03549431 | 03559152 | 03565167 | 03566737 | 03579584 | 03579776 |
      | 03581311 | 03584898 | 03604399 | 03605392 | 03617181 | 05002534 |
      | 05014753 | 05116855 | 05131571 | 05132272 | 05132286 | 05138366 |
      | 05169635 | 05177838 | 05185378 | 05186193 | 05192249 | 05195656 |
      | 05223910 | 05244077 | 05251806 | 05277865 | 05318899 | 05323714 |
      | 05323737 | 05325285 | 05325741 | 05328267 | 05336659 | 05341082 |
      | 06364184 | 06371682 | 06392463 | 06449636 | 06454495 | 06502272 |
      | 06512080 | 06527810 | 06549915 | 06552579 | 06556746 | 06557158 |
      | 06557679 | 06575810 | 06594510 | 06610582 | 06618646 | 06652017 |
      | 06659730 | 06660799 | 06661731 | 06663764 | 06666246 | 06672135 |
      | 06680073 | 06682759 | 06686145 | 06732021 | 06735472 | 06754654 |
      | 06764019 | 06794418 | 06796621 | 06798956 | 06824179 | 06848801 |
      | 06891864 | 06912294 | 06940578 | 06946611 | 06998824 | 06999772 |
      | 07002169 | 07005029 | 07005033 | 07005253 | 07013113 | 07018743 |
      | 07018744 | 07019019 | 07031462 | 07031464 | 07082038 | 07106615 |
      | 07107975 | 07121539 | 07121802 | 07129633 | 07135817 | 07168514 |
      | 07197443 | 07202923 | 07203452 | 07206780 | 07209950 | 07219194 |
      | 07221836 | 07225910 | 07238872 | 07245972 | 07248661 | 07248672 |
      | 07251075 | 07254037 | 07298703 | 07304038 | 07312305 | 07315528 |
      | 07318865 | 07323472 | 07333270 | 07337772 | 07339751 | 07339752 |
      | 07406331 | 07434021 | 07439364 | 07485688 | 07505290 | 07517101 |
      | 07553913 | 07582619 | 07596310 | 07630166 | 07634689 | 07639288 |
      | 07661578 | 07696873 | 07720350 | 07736862 | 07756379 | 07767248 |
      | 07779526 | 07806792 | 07820090 | 07825610 | 07847243 | 07868696 |
      | 07878167 | 07929995 | 07931382 | 07948982 | 07955350 | 07972892 |
      | 07984221 | 07986644 | 08006199 | 08073534 | 08087425 | 08090416 |
      | 08090417 | 08113520 | 08137143 | 08154941 | 08166016 | 08174465 |
      | 08178251 | 08189873 | 08222884 | 08223390 | 08225312 | 08225313 |
      | 08239898 | 08277445 | 08284722 | 08303261 | 08343545 | 08351358 |
      | 08354909 | 08355220 | 08383621 | 08407641 | 08417798 | 08501575 |
      | 08505775 | 08507514 | 08524150 | 08658846 | 08661466 | 08682099 |
      | 08712047 | 08736307 | 08742229 | 08743508 | 08776136 | 08780334 |
      | 08780335 | 08808558 | 08821475 | 08839209 | 08908841 | 08935969 |
      | 08950500 | 08959036 | 09043859 | 09075331 | 09092519 | 09114153 |
      | 09129636 | 09136934 | 09155801 | 09166446 | 09196992 | 09218485 |
      | 09247644 | 09258341 | 09298721 | 09365919 | 09367772 | 09376632 |
      | 09389414 | 09398202 | 09408470 | 09431299 | 09434542 | 09440372 |
      | 09471450 | 09475452 | 09490014 | 09500698 | 09521316 | 09577436 |
      | 09577495 | 09580591 | 09588432 | 09632942 | 09637916 | 09660723 |
      | 09748791 | 09749539 | 09813415 | 09817635 | 10041633 | 10044673 |
      | 10049059 | 10056096 | 10061648 | 10090589 | 10105558 | 10197152 |
      | 10209423 | 10214230 | 10237124 | 10243913 | 10411559 | 10427117 |
      | 10462333 | 10475712 | 10511184 | 10511270 | 10589911 | 10593910 |
      | 10656028 | 10712707 | 10720049 | 10775163 | 10939877 | 11056907 |
      | 11061694 | 11077148 | 11086018 | 11116635 | 11222871 | 11304281 |
      | 11335707 | 11382912 | 11544170 | 11544199 | 11583385 | 11611722 |
      | 11620355 | 11632627 | 11692470 | 11692471 | 11692472 | 11745292 |
      | 11767043 |

  - name: Look up the register
    description: >-
      The record MCA holds on a company or director, as plain JSON. Resolve a name to an identifier, read master data,
      directors and charges, or list what a company has filed. ₹5 a call, no unlock needed.
  - name: Documents and financials
    description: >-
      The filing PDFs and the data extracted from them. Unlock a company once (₹330 for every document it has filed, in any year; access lasts one year) and the first download runs;
      then download any filing and read any extraction for 5 paisa a call. Filings made after the unlock are picked up under
      Keep a company current.
  - name: Keep a company current
    description: >-
      How current is our copy of a company, and what to do about it. The freshness check is free; a refresh (₹1) re-reads
      the record and the filing list from MCA; a document fetch (₹150) downloads the listed filings still missing a PDF.
  - name: Director contact
    description: >-
      A director's phone and email, behind a separate per-director unlock (₹299 by default for new accounts; access lasts one year),
      then read for 5 paisa a call.
  - name: Your account
    description: >-
      Wallet balance and usage, the wallet ledger, top-ups, your billing profile and GST invoices. All free, on live and
      test keys alike.
paths:
  /v1/sandbox:
    get:
      tags:
        - Sandbox identifiers
      summary: List the sandbox companies and directors
      description: |
        **What it returns**

        Every company and director a test key can use. For companies: the legal name, the brand it is known by (`knownAs`, `null` when there is none), the identifier and its type, status, city and state, sorted by name. For directors: name and DIN, sorted by DIN. The same lists appear in the "Sandbox identifiers" section of this reference and inside every `403 SANDBOX_ONLY` error.

        **Billing**

        Free on test and live keys. Nothing is written to your usage.

        **Sandbox behavior**

        This is the sandbox. On a test key, call it first: `GET /v1/companies/resolve` and `GET /v1/directors/resolve` search only what it lists, and every other endpoint refuses identifiers that are not here.

        **Common errors**

        - `401 MISSING_API_KEY` / `INVALID_API_KEY`: no key, or an unknown one

      operationId: listSandboxEntities
      responses:
        "200":
          description: The sandbox companies and directors
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SandboxEntitiesResponse"
              examples:
                sandbox:
                  summary: Two of each (the real response carries the whole set)
                  value:
                    data:
                      companies:
                        - cin: U74999HR2015FTC056386
                          company: CARS24 SERVICES PRIVATE LIMITED
                          knownAs: Cars24
                          idType: cin
                          companyStatus: Active
                          city: Gurugram
                          state: Haryana
                        - cin: L93030DL2010PLC198141
                          company: ETERNAL LIMITED
                          knownAs: Eternal, formerly Zomato
                          idType: cin
                          companyStatus: Active
                          city: NEW DELHI
                          state: Delhi
                      directors:
                        - din: "11692470"
                          fullName: RAHUL MANIKRAO ZALPE
                        - din: "11692472"
                          fullName: NILESH KASHINATH HIWALE
                    meta:
                      companies: 52
                      directors: 451
                      requestId: "…"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /v1/companies/resolve:
    get:
      tags:
        - Look up the register
      summary: Resolve company name to CIN
      description: |
        **What it returns**

        Ranked CIN candidates for a free-text company name (or a single hit when `q` is already a valid CIN/FCIN/LLPIN). Each candidate carries inline disambiguation fields — registered address, status, incorporation date, current directors/promoters, and NIC/activity code — plus a Typesense `matchScore` (higher = better).

        Optional `state` and `city` query filters narrow results to companies whose registered address matches.

        **Billing**

        Billed per call at your `companies.resolve` rate (see the [rate card](/pricing.html)).

        **Sandbox behavior**

        Test keys (`fsk_test_*`) are free and search only the sandbox companies: by legal name, by the brand the company is known by, or by exact identifier; `state` and `city` still filter. The response carries `sandbox: true`, and when nothing matches, a `hint` pointing at `GET /v1/sandbox`. A company outside the sandbox is never returned to a test key, because every other endpoint would then refuse it.

        **Common errors**

        - `400 MISSING_QUERY` — `q` (or `name`) is required
        - `503 SEARCH_UNAVAILABLE` — name search requires Typesense; exact CIN lookups still work

      operationId: resolveCompany
      parameters:
        - in: query
          name: q
          required: false
          schema:
            type: string
          description: Company name or exact CIN/FCIN/LLPIN. Alias `name` is also accepted.
        - in: query
          name: name
          required: false
          schema:
            type: string
          description: Alias for `q`.
        - in: query
          name: state
          required: false
          schema:
            type: string
          description: Filter by registered state (e.g. `Maharashtra`).
        - in: query
          name: city
          required: false
          schema:
            type: string
          description: Filter by registered city.
        - in: query
          name: limit
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 20
            default: 10
          description: Maximum number of candidates to return.
      responses:
        "200":
          description: Ranked resolve results (may be an empty `candidates` array)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CompanyResolveResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          $ref: "#/components/responses/InsufficientBalance"
        "403":
          $ref: "#/components/responses/Forbidden"
        "503":
          description: Name search backend unavailable
  /v1/companies/{cin}:
    get:
      tags:
        - Look up the register
      summary: Get company master data
      description: |
        **What it returns**

        The full MCA master-data packet for a company:

        - `companyData`: the MCA master record (registration, type/category/class, capital, status, dates, registered/correspondence addresses)
        - `commonData`: MCA's supplementary record (NIC industry codes, AGM date, balance-sheet date). May be `null` if it has not been fetched yet
        - `directorData[]`: the directors of this company. Rows deduped by DIN; `MCAUserRole[]` arrays merged, with a small drop list applied
        - `indexChargesData[]`: charges (mortgages) on this company, verbatim. Empty `[]` is common (most companies have none)

        Fields are surfaced **as MCA returns them** (camelCase, British spellings like `authorisedCapital`, PascalCase charge addresses); fields are only ever dropped, never renamed. The one addition: the three capital amounts (`paidUpCapital`, `authorisedCapital`, `subscribedCapital`) each carry readable companions beside the raw number — a `…Formatted` compact form (e.g. `₹7.69 Cr`) and a `…Display` full form with Indian digit grouping (e.g. `₹7,69,34,000`). The raw integers are unchanged; the ₹ sign carries the currency. **Address filtering:** only `Registered Address` and `Correspondence Address` are kept; auxiliary address types are dropped. Identifiers supported: 21-char **CIN**, 6-char **FCIN** (foreign), 8-char **LLPIN**. The `?idType=` query param is optional; auto-detect handles all three.

        **Billing**

        Billed per call against your INR wallet at the rate in your pricing row for this endpoint.

        **Sandbox behavior**

        Test keys (`fsk_test_*`) work freely on sandbox-whitelisted CINs/LLPINs (no wallet deduction). Outside the whitelist, test keys return `403 SANDBOX_ONLY`.

        **Common errors**

        - `400 INVALID_CIN` — identifier failed CIN/FCIN/LLPIN format validation
        - `404` — company not found in our cache. The unlock endpoint won't help here either — it requires the CIN to already be in master data. If you need a specific CIN added (or you suspect ours is stale relative to MCA), contact [helpdesk@filesure.in](mailto:helpdesk@filesure.in).

        Plus the global auth/wallet/permission errors — see the [error codes table](#description/responses-and-errors).

        **See also**

        - [List company filings](#tag/look-up-the-register/GET/v1/companies/{cin}/filings)
        - [List extracted form types](#tag/documents-and-financials/GET/v1/companies/{cin}/extractions)
        - [Unlock a company](#tag/documents-and-financials/POST/v1/companies/{cin}/unlock)

      operationId: getCompanyMaster
      parameters:
        - $ref: "#/components/parameters/CompanyIdentifierPath"
        - in: query
          name: idType
          required: false
          schema:
            type: string
            enum:
              - cin
              - fcin
              - llpin
          description: Optional. Tighter validation when set; auto-detect when omitted.
      responses:
        "200":
          description: Master data found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CompanyMasterDataResponse"
              examples:
                cinSuccess:
                  summary: Sandbox CIN — CARS24 (registered company)
                  value:
                    data:
                      cin: U74999HR2015FTC056386
                      company: CARS24 SERVICES PRIVATE LIMITED
                      cinHistory: []
                      nameHistory: []
                      masterData:
                        companyData:
                          cin: U74999HR2015FTC056386
                          companyName: CARS24 SERVICES PRIVATE LIMITED
                          companyType: Company limited by Shares
                          companyOrigin: Indian Non-Government Company
                          companyCategory: Company limited by Shares
                          companySubcategory: Non-government company
                          classOfCompany: Private
                          dateOfIncorporation: 08/12/2015
                          authorisedCapital: 100000000
                          paidupCapital: 76934000
                          numberOfMembers: "0"
                          activeCompliance: ACTIVE compliant
                          companyStatus: Active
                          whetherListedOrNot: Unlisted
                          MCAMDSCompanyAddress:
                            - addressType: Registered Address
                              streetAddress: "Plot No. 78, Sector 44, Gurugram, Haryana, 122001"
                              country: India
                              # truncated for brevity
                        commonData:
                          mainDivisionCode: "74"
                          mainDivisionDescription: Other professional, scientific and technical activities
                          companyAddress:
                            - addressType: Registered Address
                              streetAddress: "Plot No. 78, Sector 44, Gurugram, Haryana, 122001"
                          # truncated for brevity
                        directorData:
                          - DIN: "07347299"
                            PAN: ABCPK1234E
                            FirstName: VIKRAM
                            LastName: CHOPRA
                            dateOfAppointment: 08/12/2015
                            DirectorDisqualified: "false"
                            MCAUserRole:
                              - role: Director
                                designation: Director
                                cin: U74999HR2015FTC056386
                                kmpFlag: "false"
                            # truncated for brevity — typical CARS24 has 5+ directors
                        indexChargesData:
                          - chargeId: "100123456"
                            chName: HDFC BANK LIMITED
                            chargeAmount: 5000000000
                            dateOfCreation: 03/15/2022
                            dateOfModification: null
                            dateOfSatisfaction: null
                            StreetAddress: "HDFC Bank House, Senapati Bapat Marg, Lower Parel, Mumbai"
                            City: Mumbai
                            State: Maharashtra
                            PostalCode: "400013"
                            Country: India
                            # truncated for brevity — CARS24 has multiple charges
                    meta:
                      requestId: "…"
                      createdAt: "2023-04-12T07:18:32.444Z"
                      updatedAt: "2026-04-28T03:21:09.001Z"
                llpinSuccess:
                  summary: Sandbox LLPIN — shape demo
                  value:
                    data:
                      cin: ACK-2998
                      company: SUGEE TWENTY SEVEN DEVELOPERS LLP
                      cinHistory: []
                      nameHistory: []
                      masterData:
                        companyData:
                          cin: ACK-2998
                          companyName: SUGEE TWENTY SEVEN DEVELOPERS LLP
                          companyType: Limited Liability Partnership
                          dateOfIncorporation: 11/11/2024
                          numberOfPartners: "2"
                          numberOfDesignatedPartners: "2"
                          companyStatus: Active
                          # truncated for brevity
                        commonData: null
                        directorData:
                          # truncated — partners list, structurally similar to directorData
                        indexChargesData: []
                    meta:
                      requestId: "…"
                      createdAt: "2023-08-22T14:12:08.901Z"
                      updatedAt: "2026-04-15T11:42:30.004Z"
        "400":
          $ref: "#/components/responses/InvalidCompanyIdentifier"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          $ref: "#/components/responses/InsufficientBalance"
        "403":
          $ref: "#/components/responses/NoAccess"
        "404":
          $ref: "#/components/responses/NotFound"
  /v1/companies/{cin}/filings:
    get:
      tags:
        - Look up the register
      summary: List company filings
      description: |
        **What it returns**

        Paginated list of MCA filings for this company (offset-based, default `limit=50`, max `200`). Filterable by `formId` (e.g. `MGT-7`, `LLP Form 8`, `AOC-4 XBRL`), `year`, and `documentCategory`. Each row carries MCA fields verbatim — `formId`, `documentCategory`, `dateOfFiling` (DD/MM/YYYY string), `fileSize`, `fileType`, `numberOfPages`, etc. Each row also carries an opaque `filingId` token (`flg_…`); pass it as the path param to the download endpoint. The MCA `documentCode` is deliberately not surfaced. Rows are ordered by when we recorded them, newest first; that usually follows the filing date but is not guaranteed, so sort on `dateOfFiling` yourself when strict date order matters.

        Date-range filtering is not supported (because `dateOfFiling` is stored as a DD/MM/YYYY string). Use `?year=` to narrow by calendar year.

        **Billing**

        Billed per call. The list itself is just metadata — the actual PDF retrieval is a separate unlock-gated download per filing.

        **Sandbox behavior**

        Test keys (`fsk_test_*`) work freely on sandbox-whitelisted CINs/LLPINs. Real filings list returned, no wallet deduction. Outside the whitelist: `403 SANDBOX_ONLY`.

        **Common errors**

        - `400 INVALID_CIN` — identifier failed CIN/FCIN/LLPIN format validation
        - `400 INVALID_ID_TYPE` — `?idType=` set to a value outside the enum

        Plus the global auth/wallet/permission errors — see the [error codes table](#description/responses-and-errors).

        **See also**

        - [Get company master data](#tag/look-up-the-register/GET/v1/companies/{cin})
        - [Download filing PDF](#tag/documents-and-financials/GET/v1/companies/{cin}/filings/{filingId}/download)
        - [Unlock a company](#tag/documents-and-financials/POST/v1/companies/{cin}/unlock) (required before PDFs are downloadable)

      operationId: listCompanyFilings
      parameters:
        - $ref: "#/components/parameters/CompanyIdentifierPath"
        - in: query
          name: idType
          required: false
          schema:
            type: string
            enum:
              - cin
              - fcin
              - llpin
          description: Optional. Tighter validation when set; auto-detect when omitted. `INVALID_ID_TYPE` 400 when set to anything outside the enum.
        - in: query
          name: page
          schema:
            type: integer
            default: 1
            minimum: 1
        - in: query
          name: limit
          schema:
            type: integer
            default: 50
            minimum: 1
            maximum: 200
        - in: query
          name: formId
          description: Exact match against the MCA `formId` field.
          schema:
            type: string
          example: MGT-7
        - in: query
          name: year
          description: Exact match against the integer `year` field on the filing doc.
          schema:
            type: integer
          example: 2024
        - in: query
          name: documentCategory
          description: Exact match against the MCA `documentCategory` field.
          schema:
            type: string
          example: Annual Returns and Balance Sheet eForms
      responses:
        "200":
          description: Paginated list of filings (empty `data` array if the company has no filings or doesn't exist).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FilingsListResponse"
              examples:
                paginated:
                  summary: Sandbox CIN — first 3 filings (truncated)
                  description: Real CARS24 has 653 filings; example shows the first 3 of page 1. Pagination uses `meta.page`, `meta.limit`, `meta.total`, `meta.totalPages`.
                  value:
                    data:
                      - filingId: flg_Ui2a1vItyyFYoAGGmefipN0RoT9EtmjHQz4KfzMeVE
                        formId: AOC-4
                        documentCategory: Annual Returns and Balance Sheet eForms
                        attachmentLabel: AOC-4
                        dateOfFiling: 30/09/2024
                        year: 2024
                        fileSize: 412050
                        fileType: pdf
                        numberOfPages: 24
                        description: null
                        createdAt: "2024-10-01T03:14:22.000Z"
                        updatedAt: "2024-10-01T03:14:22.000Z"
                      - filingId: flg_eK4hXp82mZQyaBcDeFgHiJkLmNoPqRsTuVwXyZ
                        formId: MGT-7
                        documentCategory: Annual Returns and Balance Sheet eForms
                        attachmentLabel: MGT-7
                        dateOfFiling: 30/09/2024
                        year: 2024
                        fileSize: 318220
                        fileType: pdf
                        numberOfPages: 18
                        description: null
                        createdAt: "2024-10-01T03:14:25.000Z"
                        updatedAt: "2024-10-01T03:14:25.000Z"
                      - filingId: flg_Q3vRtY8nWxZpLkMjNbVcXdSeFhJuKiOoPaSdF
                        formId: CHG-9
                        documentCategory: Charge Documents
                        attachmentLabel: optional_attachment
                        dateOfFiling: 30/09/2021
                        year: 2021
                        fileSize: 636790
                        fileType: pdf
                        numberOfPages: 36
                        description: null
                        createdAt: "2021-10-02T11:48:09.000Z"
                        updatedAt: "2021-10-02T11:48:09.000Z"
                    meta:
                      page: 1
                      limit: 20
                      total: 653
                      totalPages: 33
                empty:
                  summary: Company with no filings
                  value:
                    data: []
                    meta:
                      page: 1
                      limit: 50
                      total: 0
                      totalPages: 0
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          $ref: "#/components/responses/InsufficientBalance"
        "403":
          $ref: "#/components/responses/NoAccess"
  /v1/directors/resolve:
    get:
      tags:
        - Look up the register
      summary: Resolve director name to DIN
      description: |
        **What it returns**

        Ranked DIN candidates for a free-text director name (or a single hit when `q` is already an 8-digit DIN). Each candidate includes status, associated company names, directorship count, and a Typesense `matchScore`.

        **Billing**

        Billed per call at your `directors.resolve` rate (see the [rate card](/pricing.html)).

        **Sandbox behavior**

        Test keys (`fsk_test_*`) are free and search only the sandbox directors, by name or exact DIN. The response carries `sandbox: true`, and when nothing matches, a `hint` pointing at `GET /v1/sandbox`.

        **Common errors**

        - `400 MISSING_QUERY` — `q` (or `name`) is required
        - `503 SEARCH_UNAVAILABLE` — name search requires Typesense; exact DIN lookups still work

      operationId: resolveDirector
      parameters:
        - in: query
          name: q
          required: false
          schema:
            type: string
          description: Director name or exact 8-digit DIN. Alias `name` is also accepted.
        - in: query
          name: name
          required: false
          schema:
            type: string
          description: Alias for `q`.
        - in: query
          name: limit
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 20
            default: 10
          description: Maximum number of candidates to return.
      responses:
        "200":
          description: Ranked resolve results (may be an empty `candidates` array)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DirectorResolveResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          $ref: "#/components/responses/InsufficientBalance"
        "403":
          $ref: "#/components/responses/Forbidden"
        "503":
          description: Name search backend unavailable
  /v1/directors/{din}:
    get:
      tags:
        - Look up the register
      summary: Get director profile
      description: |
        **What it returns**

        The director's identity (DIN, name), the verbatim MCA `companyData[]` array (the per-company role records MCA returns for a DIN) and `mcaSignatoryCessationMasterHistory[]`, the appointment/cessation ledger for past company roles (each row: `cin`, `accountName`, `designation`, `appointmentDate`, `cessationDate`, and usually `accountStatus`). Use history when `companyData[]` is empty or lacks cessation dates.

        Each `companyData[]` row carries MCA-shaped fields (`ucin`, `cin_LLPIN`, `nameOfTheCompany`, `role`, `designation`, `directorFlag`, `companyStatus`, `roleEffectiveDate`, `cessationDate`, plus LLP-specific contribution fields and body-corporate metadata).

        Contact-tier fields are deliberately **excluded** here — query [GET /v1/directors/{din}/contact](#tag/director-contact/GET/v1/directors/{din}/contact) for those (separate unlock):

        `pan`, `aadhaarNumber`, `mobileNumber`, `emailAddress`, `passportNumber`, `dob`, `birthPlace`, `drivingLicenseNumber`, `votersIdNumber`, `addresses`, `fathersFirstName`/`Middle`/`Last`. Dropped both at the top level and within each `companyData[]` and `mcaSignatoryCessationMasterHistory[]` row.

        Per-row drops (both arrays): FileSure/MCA-internal IDs (`accountId`, `userId`, `userName`, `approverId`, `companyId`, `srn`, `v2UserId`, `bodyCorpInsideIndiaId`, `bodyCorpOutsideIndiaId`), duplicates of top-level director identity (`din` — always the DIN in the path, returned once as `data.din` — plus `firstName`, `middleName`, `lastName`, `gender`, `nationality`, `educationalQualification`), operational flags (`flagged`, `oldFlag`). Everything else passes through verbatim — MCA naming, no flattened display structures.

        **Billing**

        Billed per call.

        **Sandbox behavior**

        Test keys (`fsk_test_*`) work freely on sandbox-whitelisted DINs. Outside the whitelist: `403 SANDBOX_ONLY`.

        **Common errors**

        - `400 INVALID_DIN` — DIN failed 8-digit format validation
        - `404` — director not found in our cache

        Plus the global auth/wallet/permission errors — see the [error codes table](#description/responses-and-errors).

        **See also**

        - [Get director contact](#tag/director-contact/GET/v1/directors/{din}/contact) (unlock-gated; PII fields)
        - [Unlock director's contact](#tag/director-contact/POST/v1/directors/{din}/unlock)
        - [Get company master data](#tag/look-up-the-register/GET/v1/companies/{cin})

      operationId: getDirectorMaster
      parameters:
        - $ref: "#/components/parameters/DinPath"
      responses:
        "200":
          description: Director master data
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DirectorMasterDataResponse"
              examples:
                cars24Director:
                  summary: Sandbox DIN — 1 company role (truncated)
                  description: Real directors typically sit on 2–10 companies; example shows 1 representative `companyData[]` row.
                  value:
                    data:
                      din: "00002157"
                      firstName: AJAY
                      lastName: VERMA
                      gender: MALE
                      nationality: India
                      educationalQualification: Graduate
                      companyData:
                        - ucin: U74999HR2015FTC056386
                          cin_LLPIN: U74999HR2015FTC056386
                          nameOfTheCompany: CARS24 SERVICES PRIVATE LIMITED
                          role: Director
                          designation: Director
                          directorFlag: "true"
                          companyStatus: Active
                          roleEffectiveDate: "2015-02-02"
                          cessationDate: null
                    meta:
                      updatedAt: "2026-04-15T11:42:30.004Z"
                      source: MCA
        "400":
          $ref: "#/components/responses/InvalidDin"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          $ref: "#/components/responses/InsufficientBalance"
        "403":
          $ref: "#/components/responses/NoAccess"
        "404":
          $ref: "#/components/responses/DirectorNotFound"
  /v1/companies/{cin}/unlock:
    post:
      tags:
        - Documents and financials
      summary: Unlock a company
      description: |
        **What it returns**

        On a live key with an active pricing row: `202 Accepted` with the freshly-created unlock record (1-year expiry). The `data.job` field is `null` immediately after the POST — the background refresh job hasn't been registered yet. Poll [GET /v1/companies/{cin}/unlock](#tag/documents-and-financials/GET/v1/companies/{cin}/unlock) seconds later and the response will carry a populated `job` object whose `processingStages` track the asynchronous download + extraction. On a `fsk_test_*` key with a sandbox-whitelisted CIN: `200 OK` with a synthetic unlock and `job: null` — no real writes, no MCA fetch.

        **What happens next.** The unlock starts the first document download. Poll [GET /v1/companies/{cin}/unlock](#tag/documents-and-financials/GET/v1/companies/{cin}/unlock) for progress (`processingStages.documentDownloadV3.status` goes `pending` → `in_progress` → `success`); downloads and extractions work as documents land, and the window lasts one year. How the unlock relates to the refresh and the document fetch is explained in [How FileSure data works](#description/how-filesure-data-works).

        **Idempotency:** repeating this on a CIN with an active unlock returns `409 ALREADY_UNLOCKED` with the existing unlock's expiry; no double charge.

        **Billing**

        Pay-gate. Charges the company-unlock fee from your wallet up front: your `companies.unlock` rate (see the [rate card](/pricing.html)). Wallet must have sufficient balance; otherwise `402 INSUFFICIENT_BALANCE` with no unlock created.

        **Sandbox behavior**

        `fsk_test_*` keys on a sandbox-whitelisted CIN return `200 OK` with a synthetic unlock (`{ unlocked: true, sandbox: true, job: null }`) and skip all real writes / MCA fetches. Test keys on non-sandbox CINs return `403 SANDBOX_ONLY`.

        **Common errors**

        - `400 INVALID_CIN` — CIN/FCIN/LLPIN format validation failed
        - `402 INSUFFICIENT_BALANCE` — wallet under the unlock price
        - `403 NO_ACCESS` — no pricing row for `companies.unlock`
        - `403 SANDBOX_ONLY` — test key on a non-sandbox CIN
        - `404` — CIN not in our master-data cache
        - `409 ALREADY_UNLOCKED` — unlock already exists; no double charge

        Plus the global auth/wallet/permission errors — see the [error codes table](#description/responses-and-errors).

        **See also**

        - [Cascade diagram](#description/how-filesure-data-works) (visual: POST → wallet deduct → background refresh → filings/extractions queryable)
        - [Get unlock status](#tag/documents-and-financials/GET/v1/companies/{cin}/unlock) (poll job progress + grab zip URLs)
        - [Download filing PDF](#tag/documents-and-financials/GET/v1/companies/{cin}/filings/{filingId}/download)
        - [Get extracted data](#tag/documents-and-financials/GET/v1/companies/{cin}/extractions/{formType}/{year})

      operationId: unlockCompany
      parameters:
        - $ref: "#/components/parameters/CinPath"
      responses:
        "200":
          description: Sandbox synthetic unlock (test key on a sandbox CIN).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UnlockResponse"
              examples:
                sandboxSynthetic:
                  summary: Test key on a sandbox CIN — synthetic unlock (no real writes)
                  value:
                    data:
                      cin: U74999HR2015FTC056386
                      unlocked: true
                      unlockedAt: null
                      expiresAt: null
                      sandbox: true
                      job: null
                    meta: {}
        "202":
          description: Unlock created; download + extraction job queued.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UnlockResponse"
              examples:
                firstUnlock:
                  summary: Live key — unlock created, job queued (job materialises seconds later)
                  description: Right after POST, `data.job` is `null` because the background refresh hasn't been registered yet. Poll [GET /v1/companies/{cin}/unlock](#tag/documents-and-financials/GET/v1/companies/{cin}/unlock) to see job progress once it has materialised.
                  value:
                    data:
                      cin: U74999HR2015FTC056386
                      unlocked: true
                      unlockedAt: "2026-05-01T07:30:00.000Z"
                      expiresAt: "2027-05-01T07:30:00.000Z"
                      unlockPrice: 33000
                      job: null
                    meta: {}
        "400":
          $ref: "#/components/responses/InvalidCompanyIdentifier"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          $ref: "#/components/responses/InsufficientBalance"
        "403":
          description: "`NO_ACCESS` (no pricing record) or `SANDBOX_ONLY` (test key on non-sandbox CIN)."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                noAccess:
                  summary: Customer has no pricing for companies.unlock
                  value:
                    error:
                      code: NO_ACCESS
                      message: No access to endpoint 'companies.unlock'. Contact support to enable access.
                      endpoint: companies.unlock
                      catalogPricePaisa: 33000
                sandboxOnly:
                  summary: Test key on a non-sandbox CIN
                  value:
                    error:
                      code: SANDBOX_ONLY
                      message: A test key only works on the sandbox companies and directors. They are listed in this response and at GET /v1/sandbox. Use one of those, or a live key.
                      sandboxCompanies:
                        - cin: U74999HR2015FTC056386
                          company: CARS24 SERVICES PRIVATE LIMITED
                          knownAs: Cars24
                        - cin: L93030DL2010PLC198141
                          company: ETERNAL LIMITED
                          knownAs: Eternal, formerly Zomato
                      sandboxDirectors:
                        - din: "11692472"
                          fullName: NILESH KASHINATH HIWALE
                      nextStep: GET /v1/sandbox
        "404":
          description: CIN passed format validation but is not present in our master data.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                notFound:
                  value:
                    error:
                      code: NOT_FOUND
                      message: No company found for CIN U99999XX2020XYZ999999.
        "409":
          description: Active unlock already exists for this CIN. No double charge.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AlreadyUnlocked"
    get:
      tags:
        - Documents and financials
      summary: Get unlock status
      description: |
        **What it returns**

        Unlock state and live job progress for this CIN. The same envelope is used regardless of state. Three branches:

        - **Active unlock:** `unlocked: true`, `unlockedAt`, `expiresAt`, plus a `job` object carrying live progress from the background refresh. The `processingStages.documentDownloadV3.status` field is the primary progress signal (`pending` → `in_progress` → `success`, usually within minutes of the unlock POST). When download is complete, `documentDownloadV3` also surfaces `zipFiles[]` — direct download URLs to per-batch zip archives of the company's PDFs (use these instead of bulk-download).
        - **No unlock:** `unlocked: false`, `unlockPrice` (or `null` if your customer has no pricing for `companies.unlock`).
        - **Sandbox** (test key + sandbox CIN): synthetic `unlocked: true, sandbox: true, job: null`. No real reads or writes.

        Read-only — does not advance the cascade or write to the refresh job.

        **Billing**

        Free. No wallet deduction; not subject to pricing rows.

        **Sandbox behavior**

        Test keys (`fsk_test_*`) on a sandbox-whitelisted CIN return the synthetic branch above. Outside the whitelist: `403 SANDBOX_ONLY`.

        **Common errors**

        - `400 INVALID_CIN` — CIN/FCIN/LLPIN format validation failed

        Plus the global auth/permission errors — see the [error codes table](#description/responses-and-errors).

        **See also**

        - [Cascade diagram](#description/how-filesure-data-works) (visual: where each `processingStages.*` step sits in the pipeline)
        - [Unlock a company](#tag/documents-and-financials/POST/v1/companies/{cin}/unlock) (start the cascade)
        - [Download filing PDF](#tag/documents-and-financials/GET/v1/companies/{cin}/filings/{filingId}/download)

      operationId: getCompanyUnlock
      parameters:
        - $ref: "#/components/parameters/CinPath"
      responses:
        "200":
          description: Unlock state for this CIN (any of the three branches above).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UnlockResponse"
              examples:
                completedWithZipUrls:
                  summary: Unlocked, download stage complete (with zip URLs)
                  description: When `documentDownloadV3.status` reaches `success`, `zipFiles[]` carries direct download URLs — fetch the bundled PDFs without iterating over individual filing IDs.
                  value:
                    data:
                      cin: U74999HR2015FTC056386
                      unlocked: true
                      unlockedAt: "2026-05-01T07:30:00.000Z"
                      expiresAt: "2027-05-01T07:30:00.000Z"
                      unlockPrice: 33000
                      job:
                        id: "67ef88c3a9b1c2d3e4f56789"
                        processingStages:
                          documentDownloadV3:
                            status: success
                            lastUpdated: "2026-05-01T04:40:08.074Z"
                            totalZipFiles: 2
                            zipFiles:
                              - filename: U74999HR2015FTC056386_documents_20260501_043919_batch_1.zip
                                container: zip-files
                                blob_url: https://mcavpd.blob.core.windows.net/zip-files/U74999HR2015FTC056386_documents_20260501_043919_batch_1.zip
                              - filename: U74999HR2015FTC056386_documents_20260501_043947_batch_2.zip
                                container: zip-files
                                blob_url: https://mcavpd.blob.core.windows.net/zip-files/U74999HR2015FTC056386_documents_20260501_043947_batch_2.zip
                          financials:
                            status: success
                          financialExTriggered: true
                        createdAt: "2026-05-01T04:30:00.500Z"
                        updatedAt: "2026-05-01T04:40:08.074Z"
                    meta: {}
                inProgress:
                  summary: Unlocked, download stage still running
                  value:
                    data:
                      cin: U74999HR2015FTC056386
                      unlocked: true
                      unlockedAt: "2026-05-01T07:30:00.000Z"
                      expiresAt: "2027-05-01T07:30:00.000Z"
                      unlockPrice: 33000
                      job:
                        id: "67ef88c3a9b1c2d3e4f56789"
                        processingStages:
                          documentDownloadV3:
                            status: in_progress
                            lastUpdated: "2026-05-01T07:33:00.000Z"
                        createdAt: "2026-05-01T07:30:00.500Z"
                        updatedAt: "2026-05-01T07:33:00.000Z"
                    meta: {}
                notUnlocked:
                  summary: Customer hasn't unlocked this CIN
                  value:
                    data:
                      cin: U74999HR2015FTC056386
                      unlocked: false
                      unlockPrice: 33000
                      job: null
                    meta: {}
        "400":
          $ref: "#/components/responses/InvalidCompanyIdentifier"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          description: Test key on a non-sandbox CIN.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/companies/{cin}/filings/{filingId}/download:
    get:
      tags:
        - Documents and financials
      summary: Download filing PDF
      description: |
        **What it returns**

        The actual PDF binary stream when the company is unlocked and the document has been downloaded into our cache. Otherwise returns a `200` JSON unlock-status payload (Content-Type `application/json`) with `{ unlocked: false, unlockPrice }`. PDFs stream from object storage; the API picks the correct backend per CIN. `filingId` is the opaque `flg_…` token from the [list endpoint](#tag/look-up-the-register/GET/v1/companies/{cin}/filings); the underlying MCA `documentCode` is not surfaced.

        **Billing**

        Unlock-gated. Needs an active company unlock for this CIN, plus the per-call download fee; inside the unlock's year every download is just that fee. Without an active unlock, this endpoint returns the unlock-status JSON instead of the file (no charge). What an unlock covers: [How FileSure data works](#description/how-filesure-data-works).

        **Sandbox behavior**

        Test keys (`fsk_test_*`) on a sandbox-whitelisted CIN return a synthetic unlock state. The PDF returned is the real cached file when available, or the unlock-status JSON; no wallet deduction. Outside the sandbox: `403 SANDBOX_ONLY`.

        **Common errors**

        - `400 INVALID_CIN` — CIN/FCIN/LLPIN format validation failed
        - `400 INVALID_FILING_ID` — token not in `flg_…` format or failed integrity check
        - `404 DOC_NOT_AVAILABLE` — filing exists in our index but the PDF isn't downloaded yet. Right after an unlock, wait for the download job (watch `GET /v1/companies/{cin}/unlock`). If the unlock finished long ago and the filing is newer, `POST /v1/companies/{cin}/documents/fetch` (₹150) downloads it; `GET /v1/companies/{cin}/freshness` (free) shows how many are missing. `POST /v1/companies/{cin}/update` does **not** fetch PDFs — it refreshes the record and the filing list only

        Plus the global auth/wallet/permission errors — see the [error codes table](#description/responses-and-errors).

        **See also**

        - [List company filings](#tag/look-up-the-register/GET/v1/companies/{cin}/filings) (where `filingId` comes from)
        - [Unlock a company](#tag/documents-and-financials/POST/v1/companies/{cin}/unlock)
        - [Get unlock status](#tag/documents-and-financials/GET/v1/companies/{cin}/unlock)

      operationId: downloadFiling
      parameters:
        - $ref: "#/components/parameters/CompanyIdentifierPath"
        - in: path
          name: filingId
          required: true
          description: Opaque filing identifier from the list endpoint.
          schema:
            type: string
            pattern: ^flg_[A-Za-z0-9_-]+$
          example: flg_Ui2a1vItyyFYoAGGmefipN0RoT9EtmjHQz4KfzMeVE
      responses:
        "200":
          description: PDF binary stream (when unlocked) OR unlock-status JSON (when not unlocked — Content-Type `application/json`).
          content:
            application/pdf:
              schema:
                type: string
                format: binary
            application/json:
              schema:
                $ref: "#/components/schemas/UnlockStatus"
              examples:
                notUnlocked:
                  summary: Customer hasn't unlocked this CIN yet
                  description: Returned with HTTP 200 and Content-Type `application/json` — no wallet deduction. Direct the customer to the unlock endpoint.
                  value:
                    data:
                      unlocked: false
                      unlockPrice: 33000
                    meta: {}
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          description: >
            `SANDBOX_ONLY` — test key used outside the sandbox whitelist. Note: a customer without an active unlock for this CIN does NOT 403; they receive a
            `200` with the unlock-status JSON above.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Filing not found, or PDF not yet downloaded (`DOC_NOT_AVAILABLE`).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                docNotAvailable:
                  summary: PDF not yet downloaded into our cache
                  value:
                    error:
                      code: DOC_NOT_AVAILABLE
                      message: The PDF for this filing has not been downloaded yet. PDFs are fetched by the company unlock — check progress with GET /v1/companies/{cin}/unlock. POST /v1/companies/{cin}/update refreshes the filing list only and does not fetch PDFs.
  /v1/companies/{cin}/extractions:
    get:
      tags:
        - Documents and financials
      summary: List extracted form types
      description: |
        **What it returns**

        The MCA form types FileSure has extracted structured data for on this CIN. The array is sorted alphabetically and contains any subset of:

        - `AOC-4` — annual financial statements (balance sheet, P&L, cash flow). XBRL extraction from MCA Form AOC-4.
        - `CHARGES` — charges (mortgages, hypothecations, secured loans) registered against the company's assets. Multi-level structure: one CIN has many charges; each charge has a lifecycle of CREATION → MODIFICATION* → SATISFACTION events. Addressed by MCA's public `charge_id` string (numeric, e.g. `"10596825"`).
        - `MGT-7` — annual return (share capital, directors, KMP, meetings, holding/subsidiary, share-holding pattern). Covers both MGT-7 (large companies) and MGT-7A (small companies / OPC) — the response carries `form_type` to indicate which variant was actually filed.
        - `PAS-3` — share allotment events (Return of Allotment). Per-filing equity / preference / debt capital structure + per-allotment details (date, type, mode, security, total amount). Multiple filings per company across its lifetime — addressed by an opaque `filing_id` token (not a year).

        Empty array when no extractions exist for the CIN.

        **Billing**

        Billed per call.

        **Sandbox behavior**

        Test keys (`fsk_test_*`) work freely on sandbox-whitelisted CINs/LLPINs. Outside the whitelist: `403 SANDBOX_ONLY`.

        **Common errors**

        - `400 INVALID_CIN` — identifier failed CIN/FCIN/LLPIN format validation

        Plus the global auth/wallet/permission errors — see the [error codes table](#description/responses-and-errors).

        **See also**

        - [List extracted years](#tag/documents-and-financials/GET/v1/companies/{cin}/extractions/{formType})
        - [Get extracted data](#tag/documents-and-financials/GET/v1/companies/{cin}/extractions/{formType}/{year})

      operationId: listExtractions
      parameters:
        - $ref: "#/components/parameters/CompanyIdentifierPath"
      responses:
        "200":
          description: List of available form types
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ExtractionsListResponse"
              examples:
                allExtractions:
                  summary: Listed company — all four form types extracted
                  value:
                    data:
                      availableFormTypes: ["AOC-4", "CHARGES", "MGT-7", "PAS-3"]
                    meta: {}
                aoc4ChargesMgt7:
                  summary: Established company with debt — financials + annual return + charges
                  value:
                    data:
                      availableFormTypes: ["AOC-4", "CHARGES", "MGT-7"]
                    meta: {}
                aoc4AndMgt7:
                  summary: Mid-cap — financials + annual return (no debt, no allotments)
                  value:
                    data:
                      availableFormTypes: ["AOC-4", "MGT-7"]
                    meta: {}
                chargesOnly:
                  summary: Older LLP — only charges history extracted
                  value:
                    data:
                      availableFormTypes: ["CHARGES"]
                    meta: {}
                noExtractions:
                  summary: Newly-incorporated company — no extractions yet
                  value:
                    data:
                      availableFormTypes: []
                    meta: {}
        "400":
          $ref: "#/components/responses/InvalidCompanyIdentifier"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          $ref: "#/components/responses/InsufficientBalance"
        "403":
          $ref: "#/components/responses/NoAccess"
  /v1/companies/{cin}/extractions/{formType}:
    get:
      tags:
        - Documents and financials
      summary: List extracted years
      description: |
        **What it returns — depends on `formType`**

        The shape of this endpoint's response differs significantly by form type because the per-form lifecycle differs. `formType` is case-insensitive.

        - **AOC-4** — returns `{formType, availableYears}` with the calendar end-years of `period_end` (e.g. `"2024-03-31"` → `2024`). Union across `filing_scope` (standalone + consolidated); pick scope on the data endpoint.
        - **MGT-7** — returns `{formType, availableYears}` with the calendar year of `fyEnd` (financial year end). MGT-7 + MGT-7A both contribute to the same list (the form-variant distinction lives on the data endpoint's `form_type` field). No `filing_scope` split.
        - **PAS-3** — returns `{form_type, latest_snapshot, filings[]}`. Different shape because PAS-3 is event-based (one filing per share allotment, multiple per year). `latest_snapshot` is the consolidated capital structure as-of the most recent allotment date. Each entry in `filings[]` carries a `filing_id` opaque token (use it on the data endpoint), `filing_date`, `as_of_date`, optional `srn`, and `allotment_count`. Sorted by `filing_date` descending.
        - **CHARGES** — returns `{form_type, charges[]}`. One entry per `charge_id` (MCA's public numeric charge identifier — e.g. `"10596825"`). Each summary carries `charge_id`, `status` (`ACTIVE` / `SATISFIED` / `OPEN`), `holder_name` + `holder_category` (lender details), `amount_inr` + `amount_crore` (current outstanding), `counts` (creation/modification/satisfaction event counts), `first_event_date`, `latest_event_date`, `latest_event_type`. Sorted by `latest_event_date` descending. Hand the `charge_id` to the data endpoint for the full lifecycle of events.

        **Relationship to the master-data endpoint** — `/v1/companies/{cin}` already includes a flat `indexChargesData[]` inline summary (suitable for at-a-glance views in a company profile UI). The dedicated `/extractions/CHARGES` endpoint **augments** that — it surfaces the full lifecycle history (CREATION → MODIFICATION* → SATISFACTION) and per-event details. Use the master-data inline summary for quick views; use these dedicated endpoints for due-diligence-grade lifecycle analysis.

        **Billing**

        Billed per call.

        **Sandbox behavior**

        Test keys (`fsk_test_*`) work freely on sandbox-whitelisted CINs/LLPINs. Outside the whitelist: `403 SANDBOX_ONLY`.

        **Common errors**

        - `400 INVALID_CIN` — identifier failed CIN/FCIN/LLPIN format validation
        - `404 FORM_TYPE_NOT_AVAILABLE` — form type unknown to FileSure (supported: `AOC-4`, `CHARGES`, `MGT-7`, `PAS-3`)

        Plus the global auth/wallet/permission errors — see the [error codes table](#description/responses-and-errors).

        **See also**

        - [List extracted form types](#tag/documents-and-financials/GET/v1/companies/{cin}/extractions)
        - [Get extracted data](#tag/documents-and-financials/GET/v1/companies/{cin}/extractions/{formType}/{year})

      operationId: listExtractionYears
      parameters:
        - $ref: "#/components/parameters/CompanyIdentifierPath"
        - $ref: "#/components/parameters/FormTypePath"
      responses:
        "200":
          description: |
            Available years for AOC-4 / MGT-7, OR latest snapshot + filings list for PAS-3,
            OR per-charge summaries for CHARGES. Shape depends on `formType` — see the
            per-form details in the endpoint description.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/ExtractionYearsResponse"
                  - $ref: "#/components/schemas/Pas3FilingsListResponse"
                  - $ref: "#/components/schemas/ChargesListResponse"
              examples:
                cars24AOC4Years:
                  summary: AOC-4 — years available (descending)
                  value:
                    data:
                      formType: AOC-4
                      availableYears: [2025, 2024, 2022, 2021, 2020, 2019, 2018, 2017, 2016]
                    meta: {}
                listedCompanyMGT7Years:
                  summary: MGT-7 — years (one annual return per FY)
                  value:
                    data:
                      formType: MGT-7
                      availableYears: [2025, 2024, 2023, 2022, 2021, 2020, 2019]
                    meta: {}
                companyWithDebtCharges:
                  summary: CHARGES — per-charge summaries (different shape than AOC-4/MGT-7)
                  description: |
                    Real charges list for a CIN with multi-event lifecycle. Sorted by latest_event_date desc.
                    Each summary carries enough to identify the charge + its current state at a glance —
                    drill into the detail endpoint via `charge_id` for the full event timeline.
                  value:
                    data:
                      form_type: CHARGES
                      charges:
                        - charge_id: "10596825"
                          status: ACTIVE
                          holder_name: HDFC BANK LIMITED
                          holder_category: Private Sector Bank
                          amount_inr: 1249843306
                          amount_crore: 124.9843306
                          counts:
                            creation: 1
                            modification: 14
                            satisfaction: 0
                          first_event_date: "2015-08-29T00:00:00.000Z"
                          latest_event_date: "2024-05-04T00:00:00.000Z"
                          latest_event_type: MODIFICATION
                        - charge_id: "100459847"
                          status: ACTIVE
                          holder_name: HDFC BANK LIMITED
                          holder_category: Private Sector Bank
                          amount_inr: 2992000
                          amount_crore: 0.2992
                          counts:
                            creation: 1
                            modification: 0
                            satisfaction: 0
                          first_event_date: "2021-05-04T00:00:00.000Z"
                          latest_event_date: "2021-05-04T00:00:00.000Z"
                          latest_event_type: CREATION
                    meta:
                      createdAt: "2026-04-15T10:22:00.000Z"
                      updatedAt: "2026-05-08T03:14:00.000Z"
                listedCompanyPAS3Filings:
                  summary: PAS-3 — latest snapshot + filings list (different shape than AOC-4/MGT-7)
                  description: |
                    Real PAS-3 list for a high-cardinality listed company. Capital amounts surface as
                    strings (BSON Decimal128) to preserve precision; share counts as JS numbers when
                    they fit in `Number.MAX_SAFE_INTEGER` (else strings).
                  value:
                    data:
                      form_type: PAS-3
                      latest_snapshot:
                        filing_id: flg_xKpQyNwz3FdJaT_l5sA9bKp4XmHfVc_pQk1nR5tWzE2YuJrCqL3mNbDgHvSf
                        as_of_date: "2025-04-30T00:00:00.000Z"
                        filing_date: "2025-05-29T00:00:00.000Z"
                        srn: AB4296232
                        equity:
                          authorised:
                            shares: 49000000000
                            nominal: "10"
                            total: "490000000000.00"
                          issued:
                            shares: 13532472634
                            nominal: "10"
                            total: "135324726340.00"
                          subscribed:
                            shares: 13532472634
                            nominal: "10"
                            total: "135324726340.00"
                          paid_up:
                            shares: 13532472634
                            nominal: "10"
                            total: "135324726340.00"
                      filings:
                        - filing_id: flg_xKpQyNwz3FdJaT_l5sA9bKp4XmHfVc_pQk1nR5tWzE2YuJrCqL3mNbDgHvSf
                          filing_date: "2025-05-29T00:00:00.000Z"
                          as_of_date: "2025-04-30T00:00:00.000Z"
                          srn: AB4296232
                          allotment_count: 1
                        - filing_id: flg_2bMc7VxNqRfDjY_t8wL5pXk3FzEhSa_nWy6oQ4mUrJiKvBtCgL9pHbNzGfM7
                          filing_date: "2024-11-22T00:00:00.000Z"
                          as_of_date: "2024-10-31T00:00:00.000Z"
                          srn: AB1812685
                          allotment_count: 1
                    meta:
                      createdAt: "2024-10-22T03:14:00.000Z"
                      updatedAt: "2025-05-29T07:55:46.687Z"
        "400":
          $ref: "#/components/responses/InvalidCompanyIdentifier"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          $ref: "#/components/responses/InsufficientBalance"
        "403":
          $ref: "#/components/responses/NoAccess"
        "404":
          description: Form type is not extracted by FileSure
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                formTypeNotAvailable:
                  summary: Unknown form type
                  value:
                    error:
                      code: FORM_TYPE_NOT_AVAILABLE
                      message: "Form type \"XYZ-99\" is not extracted. Supported: AOC-4, CHARGES, MGT-7, PAS-3."
  /v1/companies/{cin}/extractions/{formType}/{year}:
    get:
      tags:
        - Documents and financials
      summary: Get extracted data
      description: |
        **What it returns — depends on `formType`**

        Structured data extracted from the requested form filing. The path parameter formerly named `{year}` is reused per-form:

        - **AOC-4** — `{year}` is the calendar end-year (e.g. `2024`). Returns three arrays of `{qname, value, unit}` triplets covering balance sheet, profit & loss, and cash flow (XBRL output). Standalone vs consolidated are stored as separate extractions; pick via `?scope=` (default `standalone`). If only one scope exists for a year, requesting the other returns `404` with a hint.
        - **MGT-7** — `{year}` is the calendar year of `fyEnd` (e.g. `2024` matches a financial year ending Mar 2024). Returns annual return contents: registration details (`form_type` indicating MGT-7 vs MGT-7A, `fy_start`, `fy_end`, `agm_date`, `srn`, `filing_date`), principal business activities, holding/subsidiary companies, share capital + share-holding pattern, turnover + net worth, directors + KMP, meetings + attendance, remuneration. `?scope=` is ignored on MGT-7.
        - **PAS-3** — `{year}` slot reused for an opaque **`filing_id`** token (`flg_...`). Obtain a `filing_id` from the list endpoint above, then drop it into this URL slot. Returns full filing payload: per-event `capital_structure` (equity + preference + debt) + chronological `allotments[]` (each with date, type, mode, securities, amounts). The same token format is also used by the [filings download endpoint](#tag/documents-and-financials/GET/v1/companies/{cin}/filings/{filingId}/download) — tokens are reversible only by the server (AES-encrypted `(cin, documentCode)`), so a token from one CIN's URL won't work on another CIN's URL (returns 404).
        - **CHARGES** — `{year}` slot reused for the **`charge_id`** (MCA's public numeric charge identifier, e.g. `"10596825"`). Obtain a `charge_id` from the list endpoint above. Returns full charge lifecycle: `current` consolidated state (omitted on `SATISFIED` charges where there's nothing currently active) + `counts` + `events[]` — chronologically ascending CREATION → MODIFICATION* → SATISFACTION events. Each event carries `event_type`, `event_date`, `filing_date`, `srn`, plus the per-event detail subdocs (`charge` with amount + roi + repay terms, `holder` with bank name + category + address, `security`, `asset_particulars`, `desc_of_modification` for MODIFICATION events, `satisfaction` for SATISFACTION events). Older events may have only event-type + dates (variable richness — surface populated fields only, no nulls).

        All fields are surfaced verbatim from MCA's filed form — no UI flattening, no derived totals. Customers compose their own views from the raw data.

        **Billing**

        Unlock-gated. Needs an active company unlock for this CIN, the same unlock that gates filing downloads (one fee covers every extraction of every form type), plus the per-call fee. Without an active unlock this endpoint returns the unlock-status JSON instead of the data (no charge). What an unlock covers: [How FileSure data works](#description/how-filesure-data-works).

        **Sandbox behavior**

        Test keys (`fsk_test_*`) on a sandbox-whitelisted CIN return a synthetic unlock state and the real cached extraction; no wallet deduction. Outside the sandbox: `403 SANDBOX_ONLY`.

        **Common errors**

        - `400 INVALID_CIN` — CIN/FCIN/LLPIN format validation failed
        - `400 INVALID_YEAR` — AOC-4/MGT-7: year out of range (must be 1900..currentYear+1)
        - `400 INVALID_FILING_ID` — PAS-3: path slot is not a valid `flg_*` token (malformed, or AES-tampered)
        - `400 INVALID_CHARGE_ID` — CHARGES: path slot is not a numeric string
        - `400 INVALID_QUERY` — `?scope=` set to a value outside the enum (AOC-4 only — ignored on MGT-7/PAS-3/CHARGES)
        - `404 EXTRACTION_NOT_AVAILABLE` — AOC-4/MGT-7: no extraction for this `(form, year)`. AOC-4 hint may point to the other `?scope=`
        - `404 FILING_NOT_FOUND` — PAS-3: token decodes to a `(cin, documentCode)` that doesn't have a matching event in `pas3_events`, OR the decoded `cin` doesn't match the URL `cin` (cross-CIN paste defense)
        - `404 CHARGE_NOT_FOUND` — CHARGES: no charge with the given `charge_id` exists for this CIN
        - `404 FORM_TYPE_NOT_AVAILABLE` — form type unknown (supported: `AOC-4`, `CHARGES`, `MGT-7`, `PAS-3`)

        Plus the global auth/wallet/permission errors — see the [error codes table](#description/responses-and-errors).

        **See also**

        - [List extracted form types](#tag/documents-and-financials/GET/v1/companies/{cin}/extractions)
        - [List extracted years](#tag/documents-and-financials/GET/v1/companies/{cin}/extractions/{formType})
        - [Unlock a company](#tag/documents-and-financials/POST/v1/companies/{cin}/unlock)

      operationId: getExtractionData
      parameters:
        - $ref: "#/components/parameters/CompanyIdentifierPath"
        - $ref: "#/components/parameters/FormTypePath"
        - in: path
          name: year
          required: true
          schema:
            type: string
            pattern: "^[0-9]+$|^flg_[A-Za-z0-9_-]+$"
          example: 2024
          description: |
            Per-form axis (path-slot name reused historically):
            - **AOC-4 / MGT-7**: 4-digit calendar end-year (e.g. `2024`). Must be in `[1900, currentYear+1]`.
            - **PAS-3**: opaque `flg_*` filing_id token from the list endpoint's `filings[].filing_id`.
            - **CHARGES**: numeric `charge_id` (e.g. `"10596825"`) from the list endpoint's `charges[].charge_id`.
        - in: query
          name: scope
          required: false
          schema:
            type: string
            enum:
              - standalone
              - consolidated
            default: standalone
          description: Which filing scope to return. Defaults to standalone.
      responses:
        "200":
          description: Extraction data (shape depends on `formType`), OR unlock-status payload when not unlocked.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/ExtractionDataResponse"
                  - $ref: "#/components/schemas/Mgt7ExtractionDataResponse"
                  - $ref: "#/components/schemas/Pas3ExtractionDataResponse"
                  - $ref: "#/components/schemas/ChargesDetailResponse"
                  - $ref: "#/components/schemas/UnlockStatus"
              examples:
                aoc4Unlocked:
                  summary: AOC-4 standalone for 2024 (truncated, CARS24)
                  description: Real AOC-4 extractions have 80–100 rows per array; example shows 1 representative row each.
                  value:
                    data:
                      cin: U74999HR2015FTC056386
                      filing_scope: standalone
                      period_end: "2024-03-31"
                      period_start: "2023-04-01"
                      taxonomy_id: in-ca:Schedule3IndAS
                      balance_sheet:
                        - qname: in-ca:Equity
                          value: 12450000000
                          unit: INR
                      profit_and_loss:
                        - qname: in-ca:RevenueFromOperations
                          value: 89400000000
                          unit: INR
                      cash_flow:
                        - qname: in-ca:CashFlowsFromUsedInOperatingActivities
                          value: -3210000000
                          unit: INR
                      metadata:
                        source:
                          dateOfFiling: 30/09/2024
                          paymentDate: 28/09/2024
                          year: 2024
                    meta:
                      createdAt: "2024-09-30T14:22:18.000Z"
                      updatedAt: "2024-10-12T08:05:42.000Z"
                mgt7Unlocked:
                  summary: MGT-7 annual return for 2025 (truncated, listed company L15400TG2009PLC062658)
                  description: |
                    Listed company MGT-7 with full sections. Real responses have all populated MCA sections;
                    example trims nested arrays to one representative row. `form_type` reflects which actual
                    filing variant was used (`MGT-7` for standard, `MGT-7A` for small companies / OPC).
                  value:
                    data:
                      cin: L15400TG2009PLC062658
                      form_type: MGT-7
                      srn: AC0745199
                      fy_start: "2024-04-01T00:00:00.000Z"
                      fy_end: "2025-03-31T00:00:00.000Z"
                      agm_date: "2025-09-24T00:00:00.000Z"
                      filing_date: "2025-12-28T00:00:00.000Z"
                      is_opc: false
                      is_share_listed: true
                      number_stock_exchange: 1
                      stock_exchanges:
                        - "BSE"
                      num_business_actv: 3
                      business_activities:
                        - main_group_code: "N"
                          main_group_desc: "Administrative and support service activities"
                          activity_code: "80"
                          activity_desc: "Security and investigation activities"
                          turnover_pct: 29.89
                      num_rtas: 1
                      rtas:
                        - cin_other_reg_no: "U72400TG2017PTC117649"
                          name_of_rta: "KFin Technologies Limited"
                          address1: "Selenium Tower-B, Plot 31 & 32"
                      num_holding_subs: 2
                      holding_subs: []
                      share_capital:
                        equity:
                          num_classes: 1
                          totals:
                            shares:
                              authorised: 23000000
                              issued: 20288122
                              subscribed: 20288122
                              paid_up: 20288122
                            amount:
                              authorised: 115000000
                              issued: 101440610
                              subscribed: 101440610
                              paid_up: 101440610
                        preference:
                          num_classes: 0
                          totals:
                            shares: { authorised: 0, issued: 0, subscribed: 0, paid_up: 0 }
                            amount: { authorised: 0, issued: 0, subscribed: 0, paid_up: 0 }
                        unclassified_total: 0
                      share_holding_pattern:
                        promoter: { equity_pct: 50.78, preference_pct: 0 }
                        public: { equity_pct: 49.22, preference_pct: 0 }
                      turnover: 6887017470
                      net_worth: 888754630
                      meetings:
                        members_total: 1
                        board_total: 4
                        committee_total: 12
                        directors_attendance: []
                      kmp:
                        - din: "00037022"
                          name: "Prem Kishan Dass Gupta"
                          designation: "MANAGING DIRECTOR"
                      remuneration:
                        directors: []
                        total: 0
                    meta:
                      createdAt: "2026-04-20T08:34:29.719Z"
                      updatedAt: "2026-04-20T08:34:29.719Z"
                pas3Unlocked:
                  summary: PAS-3 — full filing detail for a single share allotment (listed company, L17110MH1973PLC019786)
                  description: |
                    The `:filing_id` path slot carries a `flg_*` token (from the list endpoint). Returns
                    the per-event `capital_structure` (equity + preference + debt) + chronological
                    `allotments[]` array. Decimal128 values surface as strings to preserve precision.
                  value:
                    data:
                      cin: L17110MH1973PLC019786
                      form_type: PAS-3
                      filing_id: flg_xKpQyNwz3FdJaT_l5sA9bKp4XmHfVc_pQk1nR5tWzE2YuJrCqL3mNbDgHvSf
                      filing_date: "2025-05-29T00:00:00.000Z"
                      srn: AB4296232
                      capital_structure:
                        equity:
                          authorised:
                            shares: 49000000000
                            nominal: "10"
                            total: "490000000000.00"
                          issued:
                            shares: 13532472634
                            nominal: "10"
                            total: "135324726340.00"
                          subscribed:
                            shares: 13532472634
                            nominal: "10"
                            total: "135324726340.00"
                          paid_up:
                            shares: 13532472634
                            nominal: "10"
                            total: "135324726340.00"
                        preference:
                          authorised: { shares: 1000000000, nominal: "10", total: "10000000000.00" }
                          issued: { shares: 0, nominal: "0", total: "0.00" }
                          subscribed: { shares: 0, nominal: "0", total: "0.00" }
                          paid_up: { shares: 0, nominal: "0", total: "0.00" }
                        debt:
                          debentures: "283888000000"
                          secured_loans: "73708977566.46"
                          others: "1610154091933.08"
                      allotments:
                        - allotment_date: "2025-04-30T00:00:00.000Z"
                          security_type: Equity
                          allotment_type: "Employee stock option Plan (ESOP)"
                          details: "PARI-PASSU WITH EXISTING FULLY PAID-UP EQ SHARES"
                          terms_in_attachment: false
                          mode: Cash
                          num_securities_allotted: 99736
                          nominal_per_security: "10"
                          premium_per_security: "0"
                          discount_per_security: "0"
                          total_amount: "997360"
                          non_cash: null
                    meta:
                      createdAt: "2025-05-29T07:55:46.687Z"
                      updatedAt: "2025-05-29T07:55:46.687Z"
                chargesUnlocked:
                  summary: CHARGES — full lifecycle for one charge (HDFC working-capital, U27100PB1996PLC017827)
                  description: |
                    Real charge with 15 events (1 creation + 14 modifications, no satisfaction — still
                    ACTIVE). `current` block carries the consolidated state-as-of-now; `events[]` is
                    chronologically ascending. Older events have less rich per-event detail (variable
                    richness — surface populated fields only, no nulls).
                  value:
                    data:
                      cin: U27100PB1996PLC017827
                      form_type: CHARGES
                      charge_id: "10596825"
                      status: ACTIVE
                      counts:
                        creation: 1
                        modification: 14
                        satisfaction: 0
                      current:
                        amount_inr: 1249843306
                        amount_crore: 124.9843306
                        roi_pct: "9.50%"
                        holder_name: HDFC BANK LIMITED
                        holder_category: Private Sector Bank
                        num_holders: 1
                        flags:
                          is_joint: false
                          is_consortium: false
                          is_pari_passu: false
                        property_type_raw:
                          - "Movable property - Inventory"
                          - "Movable property - Motor Vehicle (Hypothecation)"
                          - "Movable property - Others"
                          - "Book debts"
                        property_types:
                          - MOVABLE_INVENTORY
                          - VEHICLES
                          - MOVABLE_PROPERTY
                          - BOOK_DEBTS
                        instrument_desc: "Supplementary Letter of Hypothecation."
                      events:
                        - event_type: CREATION
                          event_date: "2015-08-29T00:00:00.000Z"
                          filing_date: "2015-09-15T00:00:00.000Z"
                          reg_date: "2015-09-15T00:00:00.000Z"
                          srn: C67177063
                          charge:
                            amount_inr: 365000000
                            amount_crore: 36.5
                            amount_words: "Rupees Thirty Six Crore Fifty Lacs only"
                            roi_pct: "Interest at the rate as stipulated in the Bank's Sanction Letter."
                            nature_facility: MFA
                            terms_of_repay: "MFA repayable on demand and/or as stipulated in the Bank's Sanction Letter."
                          holder:
                            is_joint: false
                            is_consortium: false
                            is_pari_passu: false
                            num_holders: 1
                            category: PVTB
                            name_of_chg_holder: HDFC BANK LIMITED
                            name: HDFC BANK LIMITED
                            add_line1: "HDFC BANK HOUSE, SENAPATI BAPAT MARG"
                            city: MUMBAI
                            state: MH
                            pin_code: "400013"
                            country: IN
                        - event_type: MODIFICATION
                          event_date: "2020-06-04T00:00:00.000Z"
                          filing_date: "2020-07-03T00:00:00.000Z"
                          srn: R44284271
                          desc_of_modification: "The charge shall now stand increased from Rs.3050 Lakhs to Rs.3650 Lakhs in favour of the Bank against the security of Stocks & Book Debts, Plant & Machinery and vehicles and immovable properties..."
                          charge:
                            amount_inr: 365000000
                            amount_crore: 36.5
                          holder:
                            num_holders: 1
                            category: PVTB
                            name_of_chg_holder: HDFC BANK LIMITED
                    meta:
                      createdAt: "2026-04-15T10:22:00.000Z"
                      updatedAt: "2024-05-08T03:14:00.000Z"
                notUnlocked:
                  summary: Customer hasn't unlocked this CIN yet
                  value:
                    data:
                      unlocked: false
                      unlockPrice: 33000
                    meta: {}
        "400":
          description: Invalid CIN, year out of range, or unknown scope value
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                invalidYear:
                  summary: AOC-4 / MGT-7 — year out of range
                  value:
                    error:
                      code: INVALID_YEAR
                      message: "`year` must be between 1900 and the current calendar year + 1."
                invalidFilingId:
                  summary: PAS-3 — path slot is not a valid flg_* token
                  value:
                    error:
                      code: INVALID_FILING_ID
                      message: "`filing_id` is not a valid identifier."
                invalidChargeId:
                  summary: CHARGES — path slot is not numeric
                  value:
                    error:
                      code: INVALID_CHARGE_ID
                      message: "`charge_id` path parameter must be a numeric string."
                invalidScope:
                  value:
                    error:
                      code: INVALID_QUERY
                      message: "`scope` must be one of: standalone, consolidated."
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          description: >
            `SANDBOX_ONLY` — test key (`fsk_test_*`) used outside the sandbox-whitelisted CIN list. `unlockCheck()` doesn't 403 live keys for missing pricing —
            when no unlock and no pricing record exist, it returns the 200 not-unlocked envelope with `unlockPrice: null`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                error:
                  code: SANDBOX_ONLY
                  message: A test key only works on the sandbox companies and directors. They are listed in this response and at GET /v1/sandbox. Use one of those, or a live key.
                  sandboxCompanies:
                    - cin: U74999HR2015FTC056386
                      company: CARS24 SERVICES PRIVATE LIMITED
                      knownAs: Cars24
                    - cin: L93030DL2010PLC198141
                      company: ETERNAL LIMITED
                      knownAs: Eternal, formerly Zomato
                  sandboxDirectors:
                    - din: "11692472"
                      fullName: NILESH KASHINATH HIWALE
                  nextStep: GET /v1/sandbox
        "404":
          description: No extraction available for the requested form/year/scope
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                missingYear:
                  summary: AOC-4 / MGT-7 — year has no extraction
                  value:
                    error:
                      code: EXTRACTION_NOT_AVAILABLE
                      message: No AOC-4 extraction available for 1995.
                wrongScope:
                  summary: AOC-4 — other scope is available
                  value:
                    error:
                      code: EXTRACTION_NOT_AVAILABLE
                      message: No AOC-4 consolidated extraction for 2024. The standalone extraction is available — try ?scope=standalone.
                pas3FilingNotFound:
                  summary: PAS-3 — token decodes successfully but no matching event for this CIN
                  description: |
                    Returned when the `flg_*` token decodes (valid AES) but either (a) the decoded
                    documentCode doesn't match a PAS-3 event for this CIN, or (b) the decoded `cin`
                    inside the token differs from the URL CIN (cross-CIN paste defense).
                  value:
                    error:
                      code: FILING_NOT_FOUND
                      message: 'No PAS-3 filing matches filing_id "flg_xKpQy..." for this company.'
                chargeNotFound:
                  summary: CHARGES — no charge with that charge_id exists for this CIN
                  value:
                    error:
                      code: CHARGE_NOT_FOUND
                      message: 'No charge with charge_id "9999999999" exists for this company.'
                unknownFormType:
                  value:
                    error:
                      code: FORM_TYPE_NOT_AVAILABLE
                      message: "Form type \"XYZ-99\" is not extracted. Supported: AOC-4, CHARGES, MGT-7, PAS-3."
  /v1/companies/{cin}/freshness:
    get:
      tags:
        - Keep a company current
      summary: How current is our copy of this company?
      description: |
        Dates and counts only — no company data — so you can decide whether to pay for a refresh
        before you do. **Free.** Covered by the per-key rate limit like the account calls.

        **The flow this call starts**

        1. `GET /v1/companies/{cin}/freshness` — free. Is the record stale? Are PDFs missing?
        2. `POST /v1/companies/{cin}/update` — ₹1. Refreshes the record and the filing list from MCA. No PDFs.
        3. `POST /v1/companies/{cin}/documents/fetch` — ₹150. Downloads the PDFs that are still missing (needs your unlock).

        **What the fields mean**

        | Field | Meaning |
        |---|---|
        | `data.updatedAt` | When the company's master data, directors, common data and charges were last written. They are refreshed together, so one date is enough. |
        | `filings.total` / `downloaded` / `missing` | Our filing index for the company: rows, rows with a PDF on file, rows without. `missing > 0` is what a document job would fetch. |
        | `filings.listRefreshedAt` | When the filing list was last re-read from MCA (a data refresh or a filing-list refresh). |
        | `refresh` | The newest data refresh for this company, whoever asked for it: `status`, `lastCompletedAt` and `freshUntil` — until then a `POST /update` joins the finished refresh (`alreadyFresh: true`) instead of fetching again. |
        | `documentJob` | The newest document download job for this company: `status` (`pending` → `in_progress` → `success`) and its counters. `null` if none has run. |
        | `unlock` | Whether **you** hold an active unlock for this company, and when it expires. |

        **Sandbox behavior**

        `fsk_test_*` keys on a whitelisted CIN return a synthetic, settled shape with `sandbox: true`. Outside the whitelist: `403 SANDBOX_ONLY`.

        **Common errors**

        - `400 INVALID_CIN` — format validation failed
        - `404 COMPANY_NOT_FOUND` — we do not hold this company
      operationId: getCompanyFreshness
      parameters:
        - $ref: "#/components/parameters/CinPath"
      responses:
        '200':
          description: Freshness of our data for this company
          content:
            application/json:
              example:
                data:
                  cin: U62013KA2024PTC188236
                  data:
                    updatedAt: "2026-09-13T22:10:04.000Z"
                  filings:
                    total: 30
                    downloaded: 21
                    missing: 9
                    listRefreshedAt: "2026-09-13T22:10:19.000Z"
                  refresh:
                    jobId: 6aa7f0c1b22e2991f01371aa
                    status: completed
                    requestedAt: "2026-09-13T22:09:52.000Z"
                    lastCompletedAt: "2026-09-13T22:10:19.000Z"
                    freshUntil: "2026-09-14T22:10:19.000Z"
                  documentJob:
                    status: success
                    totalDocuments: 21
                    downloadedDocuments: 21
                    pendingDocuments: 0
                    createdAt: "2026-08-03T06:49:12.000Z"
                    updatedAt: "2026-08-03T07:02:40.000Z"
                  unlock:
                    active: true
                    unlockedAt: "2026-08-16T09:12:40.000Z"
                    expiresAt: "2027-08-16T09:12:40.000Z"
                meta: {}
        '400':
          $ref: "#/components/responses/InvalidCompanyIdentifier"
        '401':
          $ref: "#/components/responses/Unauthorized"
        '403':
          description: Test key on a non-sandbox CIN.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        '404':
          description: We do not hold this company.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/companies/{cin}/update:
    post:
      tags:
        - Keep a company current
      summary: Refresh a company from MCA
      operationId: updateCompany
      description: |
        Fetches this company again from MCA, now, and updates what we hold: its master data, its
        director list, its common data and its filing list. Returns `202 Accepted` with a job id.
        **₹1** — a nominal fee, because the refresh also improves our own data.

        **This refreshes data, not documents.** It fetches no PDFs. Unlock buys you the company's
        *documents* — PDFs and extracted financials — for a year; once unlocked, new filings the
        refresh finds are downloaded by `POST /v1/companies/{cin}/documents/fetch` (₹150). Update
        refreshes the *record*: name, status, registered address, capital, directors, charges, and
        the list of filings. If you unlocked a company last year and want to know whether anything
        has changed since, this is the call — and it is free to find out first with
        `GET /v1/companies/{cin}/freshness`.

        Where this sits in the flow, and what the other two calls do: [How FileSure data works](#description/how-filesure-data-works).

        **What to do next is in the response.** `documents.newFilings` is how many filings the
        refresh added to our list; once the job is `completed`, `documents.missing` is how many
        listed filings have no PDF on file and `documents.nextStep` names the document job when that
        is above zero.

        **It is asynchronous.** A live MCA fetch takes seconds to minutes and can fail on captcha or
        upstream quota, so the response comes back immediately with a job id. Poll
        `GET /v1/companies/{cin}/update/status` for progress.

        **Stages are reported separately**, because a partial refresh is the normal case rather than
        the exception:

        | Stage | What it refreshes |
        |---|---|
        | `masterData` | Company name, status, dates, address, capital |
        | `directors` | The board as MCA currently lists it |
        | `commonData` | PAN and associated identifiers |
        | `documents` | The filing index on our side |

        A stage reads `completed`, `failed` or `skipped`. `skipped` means we chose not to run it —
        the director list is read from the master-data response, so it is skipped when that stage
        fails. It does not mean an error.

        **One refresh per company per 24 hours, shared.** If someone else already asked for this
        company inside that window you join their job rather than starting a second scrape, and the
        response carries `joinedExisting: true`. Where their refresh has already finished you also
        get `alreadyFresh: true` — the data was current before you asked, and no new fetch was run.
        `freshUntil` says when the window ends (`cooldownUntil` is the same value, kept for older
        integrations). Every call is charged, including one that joins.

        **Rate limit:** 10 companies per minute, separate from the general per-key limit. Each
        refresh is a live MCA round trip, not a cached read.

        **Billing**

        Charges your `companies.update` rate (see the [rate card](/pricing.html)) on success, including when you join a
        running or freshly-completed refresh. If the refresh cannot be queued you get `503` and are
        **not** charged.

        **Sandbox behavior**

        `fsk_test_*` keys on a whitelisted CIN return a synthetic completed job. No MCA fetch, no
        charge, no job record.

        **Common errors**

        - `400 INVALID_CIN` — format validation failed
        - `402 INSUFFICIENT_BALANCE` — wallet below the update fee; nothing queued
        - `404 COMPANY_NOT_FOUND` — we do not hold this company; no charge
        - `429 RATE_LIMITED` — past 10 refreshes/minute; no charge
        - `409 REFRESH_IN_PROGRESS` — a filing-list-only refresh (`POST /v1/companies/{cin}/filings/refresh`) is running for this company; retry after `Retry-After` seconds to start the full refresh. No charge
        - `503 UPDATE_QUEUE_UNAVAILABLE` — could not queue; no charge, safe to retry
      parameters:
        - name: cin
          in: path
          required: true
          schema:
            type: string
          example: U74999HR2015FTC056386
      responses:
        '202':
          description: Refresh queued, or joined to one already running
          content:
            application/json:
              example:
                data:
                  cin: U74999HR2015FTC056386
                  jobId: 6a7c6b00b22e2991f0137105
                  status: pending
                  requestedAt: "2026-08-12T12:45:52.000Z"
                  lastCompletedAt: null
                  freshUntil: null
                  cooldownUntil: null
                  stages:
                    masterData: { status: pending }
                    directors: { status: pending }
                    commonData: { status: pending }
                    documents: { status: pending }
                meta:
                  priceChargedPaisa: 100
                  walletBalanceAfterPaisa: 44190
        '402': { description: Insufficient wallet balance }
        '404': { description: Company not found }
        '429': { description: Refresh rate limit exceeded }
        '503': { description: Could not queue the refresh — not charged }

  /v1/companies/{cin}/update/status:
    get:
      tags:
        - Keep a company current
      summary: Progress of a company refresh
      operationId: getCompanyUpdateStatus
      description: |
        Per-stage progress for the most recent refresh of this company. Free.

        The job is per *company*, not per customer — if you joined someone else's refresh, this
        shows you its progress.

        `status` is `pending`, `in_progress`, `completed` or `failed`. It reads `failed` when any
        stage failed, so check the individual stages to see what did land: three of four refreshed
        is a normal outcome and better than none.

        `freshUntil` (and its older alias `cooldownUntil`) is set only on a successful refresh —
        24 hours after it completed. A failed one never blocks a retry.

        `documents` says what the refresh found: `newFilings` added to our list, and once the job
        is `completed`, `missing` (listed filings with no PDF on file) and `nextStep`, which names
        `POST /v1/companies/{cin}/documents/fetch` when there is something to download.
      parameters:
        - name: cin
          in: path
          required: true
          schema:
            type: string
          example: U74999HR2015FTC056386
      responses:
        '200':
          description: Current refresh state
          content:
            application/json:
              example:
                data:
                  cin: U74999HR2015FTC056386
                  jobId: 6a7c6b00b22e2991f0137105
                  status: completed
                  requestedAt: "2026-08-12T12:45:52.000Z"
                  lastCompletedAt: "2026-08-12T12:46:07.000Z"
                  freshUntil: "2026-08-13T12:46:07.000Z"
                  cooldownUntil: "2026-08-13T12:46:07.000Z"
                  stages:
                    masterData: { status: completed }
                    directors: { status: completed }
                    commonData: { status: completed }
                    documents: { status: completed }
                  documents:
                    newFilings: 9
                    missing: 9
                    nextStep: POST /v1/companies/{cin}/documents/fetch

  /v1/companies/{cin}/documents/fetch:
    post:
      tags:
        - Keep a company current
      summary: Fetch new documents for an unlocked company
      description: |
        Queues a download of the filings whose PDFs we do not hold yet, for a company you have
        already unlocked. Returns `202 Accepted`; watch progress at
        [GET /v1/companies/{cin}/unlock](#tag/documents-and-financials/GET/v1/companies/{cin}/unlock).

        **When to use it.** The ₹330 unlock includes the first download. Months later the company
        has filed more, and a data refresh (`POST /v1/companies/{cin}/update`) has listed the new
        filings — but a refresh never fetches PDFs. This call does: it starts a new download job
        for everything in the filing list that is still missing a PDF.

        **The flow**

        1. `POST /v1/companies/{cin}/unlock` — once, opens a 1-year window and runs the first download.
        2. `POST /v1/companies/{cin}/update` — ₹1, pulls the current filing list from MCA.
        3. `POST /v1/companies/{cin}/documents/fetch` — ₹150, downloads the PDFs that appeared.

        **Rules**

        - **Needs your active unlock** for this company. Without one: `403 UNLOCK_REQUIRED` with
          `nextStep` pointing at the unlock, and **no charge**.
        - **Nothing missing, nothing charged.** If every filing we list already has its PDF (or we
          hold no filing list yet), you get `409 NOTHING_TO_FETCH` with the counts and a `nextStep`
          pointing at the data refresh. No charge.
        - **Joining a running job is still charged.** If a download job for this company is already
          running, the response carries `joinedExisting: true` and the call is charged at the full
          rate — the job will pick up your missing filings.
        - **Charged only once the job is queued.** If it cannot be queued you get `503` and are not
          charged.

        `filings` in the response counts our filing index: `total`, `downloaded` (PDF on file) and
        `missing` (what this job will fetch). New MCA filings we have not indexed yet are not in it —
        run the data refresh first.

        **Billing**

        Charges your `companies.documents.fetch` rate (see the [rate card](/pricing.html)) on `202`. All other
        responses are free.

        **Sandbox behavior**

        `fsk_test_*` keys on a whitelisted CIN return a synthetic `202` with `sandbox: true`. No
        unlock is needed, nothing is queued, no charge.

        **Common errors**

        - `400 INVALID_CIN` — format validation failed
        - `402 INSUFFICIENT_BALANCE` — wallet below the document-job fee; nothing queued
        - `403 UNLOCK_REQUIRED` — no active unlock for this company; no charge
        - `403 NO_ACCESS` — no pricing row for `companies.documents.fetch`
        - `404 COMPANY_NOT_FOUND` — we do not hold this company; no charge
        - `409 NOTHING_TO_FETCH` — every listed filing already has its PDF; no charge
        - `503 DOCUMENT_JOB_QUEUE_UNAVAILABLE` — could not queue; no charge, safe to retry
      operationId: fetchCompanyDocuments
      parameters:
        - $ref: "#/components/parameters/CinPath"
      responses:
        '202':
          description: Document job queued, or joined to one already running
          content:
            application/json:
              examples:
                queued:
                  summary: New job queued
                  value:
                    data:
                      cin: U62013KA2024PTC188236
                      queued: true
                      joinedExisting: false
                      filings: { total: 30, downloaded: 21, missing: 9 }
                      unlockExpiresAt: "2027-08-16T09:12:40.000Z"
                      progress: GET /v1/companies/{cin}/unlock
                    meta:
                      priceChargedPaisa: 15000
                      walletBalanceAfterPaisa: 44190
                joined:
                  summary: Joined a job already running — still charged
                  value:
                    data:
                      cin: U62013KA2024PTC188236
                      queued: true
                      joinedExisting: true
                      filings: { total: 30, downloaded: 21, missing: 9 }
                      unlockExpiresAt: "2027-08-16T09:12:40.000Z"
                      progress: GET /v1/companies/{cin}/unlock
                    meta:
                      priceChargedPaisa: 15000
                      walletBalanceAfterPaisa: 29190
        '400':
          $ref: "#/components/responses/InvalidCompanyIdentifier"
        '401':
          $ref: "#/components/responses/Unauthorized"
        '402':
          $ref: "#/components/responses/InsufficientBalance"
        '403':
          description: "`UNLOCK_REQUIRED` (no active unlock — no charge), `NO_ACCESS` (no pricing row) or `SANDBOX_ONLY` (test key on a non-sandbox CIN)."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                unlockRequired:
                  value:
                    error:
                      code: UNLOCK_REQUIRED
                      message: A document job needs an active unlock for this company. Unlock it first; the unlock includes the first download. No charge was made.
                      nextStep: POST /v1/companies/{cin}/unlock
                      unlockPrice: 33000
        '404':
          description: We do not hold this company. No charge.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        '409':
          description: Every listed filing already has its PDF — nothing to fetch, no charge.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                nothingToFetch:
                  value:
                    error:
                      code: NOTHING_TO_FETCH
                      message: Every filing we list for this company already has its PDF. Run a data refresh to pick up new MCA filings first. No charge was made.
                      nextStep: POST /v1/companies/{cin}/update
                      filings: { total: 30, downloaded: 30, missing: 0 }
        '503':
          description: Could not queue the document job — not charged, safe to retry.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/directors/{din}/unlock:
    post:
      tags:
        - Director contact
      summary: Unlock a director's contact
      description: |
        Creates a 1-year unlock for this DIN's contact tier (mobile + email) and unlocks [GET /v1/directors/{din}/contact](#tag/director-contact/GET/v1/directors/{din}/contact) for the customer for the next 365 days. The cascade here is structurally different from the company unlock — no background download, no extraction job. Instead:

        1. **Hot path** (cached contact <365 days old, mobile or email present): atomic wallet deduction + unlock create + `200 OK`. Sub-200 ms; no MCA fetch.
        2. **Cold path** (cached contact stale or missing): triggers an upstream refresh and polls our cache for up to ~10 seconds. If the refresh lands fresh contact within the window, the API deducts the wallet and returns `200`. If it doesn't land, returns `422 CONTACT_NOT_AVAILABLE` with `lastUpdateAttempt` for diagnosis — **wallet is not deducted**.
        3. **DIN never seeded:** if we have no record of this DIN at all (its containing CIN was never refreshed), returns `422 CONTACT_NOT_AVAILABLE` with `nextStep: POST /v1/companies/{cin}/unlock` — that endpoint seeds the chain. No upstream call, no deduction.

        **Idempotency:** repeating this on a DIN with an active contact unlock returns `409 ALREADY_UNLOCKED` — no double charge.

        **Billing**

        Pay-gate. Charges the director-contact unlock fee from your wallet (your `directors.unlock` rate, see the [rate card](/pricing.html)). Wallet must have sufficient balance. The wallet-deduction guard ensures: if the upstream refresh fails or times out, wallet is not deducted.

        **Sandbox behavior**

        `fsk_test_*` keys on a sandbox-whitelisted DIN return `200 OK` with a synthetic unlock (`{ unlocked: true, sandbox: true }`) — no real writes, no upstream refresh, no real contact data ever leaked. Test keys on non-sandbox DINs return `403 SANDBOX_ONLY`.

        **Common errors**

        - `503 SERVICE_UNAVAILABLE` — director-contact unlock disabled by ops (`DIRECTOR_CONTACT_UNLOCK_ENABLED=false`). Wallet **not** deducted
        - `400 INVALID_DIN` — DIN failed 8-digit format validation
        - `402 INSUFFICIENT_BALANCE` — wallet under the unlock price
        - `403 NO_ACCESS` — no pricing row for `directors.unlock`
        - `403 SANDBOX_ONLY` — test key on a non-sandbox DIN
        - `409 ALREADY_UNLOCKED` — unlock already exists for this DIN
        - `422 CONTACT_NOT_AVAILABLE` — upstream refresh failed/timed out, OR DIN not in our cache. Wallet **not** deducted

        Plus the global auth/wallet/permission errors — see the [error codes table](#description/responses-and-errors).

        **See also**

        - [Cascade diagram](#description/how-filesure-data-works) (visual: hot path vs cold path with the ~10s upstream-refresh poll)
        - [Get director contact unlock status](#tag/director-contact/GET/v1/directors/{din}/unlock)
        - [Get director contact](#tag/director-contact/GET/v1/directors/{din}/contact) (the gated read)
        - [Unlock a company](#tag/documents-and-financials/POST/v1/companies/{cin}/unlock) (seeds the chain when 422 nextStep is given)

      operationId: unlockDirectorContact
      parameters:
        - $ref: "#/components/parameters/DinPath"
      responses:
        "200":
          description: |
            Unlock created (live key) or sandbox synthetic (test key on sandbox DIN).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DirectorUnlockResponse"
        "400":
          $ref: "#/components/responses/InvalidDin"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          $ref: "#/components/responses/InsufficientBalance"
        "403":
          description: "`NO_ACCESS` (no pricing record) or `SANDBOX_ONLY` (test key on non-sandbox DIN)."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                noAccess:
                  summary: Customer has no pricing for directors.unlock
                  value:
                    error:
                      code: NO_ACCESS
                      message: No access to endpoint 'directors.unlock'. Contact support to enable access.
                      endpoint: directors.unlock
                      catalogPricePaisa: 29900
                sandboxOnly:
                  summary: Test key on a non-sandbox DIN
                  value:
                    error:
                      code: SANDBOX_ONLY
                      message: A test key only works on the sandbox companies and directors. They are listed in this response and at GET /v1/sandbox. Use one of those, or a live key.
                      sandboxCompanies:
                        - cin: U74999HR2015FTC056386
                          company: CARS24 SERVICES PRIVATE LIMITED
                          knownAs: Cars24
                        - cin: L93030DL2010PLC198141
                          company: ETERNAL LIMITED
                          knownAs: Eternal, formerly Zomato
                      sandboxDirectors:
                        - din: "11692472"
                          fullName: NILESH KASHINATH HIWALE
                      nextStep: GET /v1/sandbox
        "409":
          description: Active contact unlock already exists for this DIN. No double charge.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AlreadyUnlocked"
        "422":
          description: |
            Contact data could not be made available (refresh failed/timed out, or DIN
            row missing). Wallet is not deducted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ContactNotAvailable"
              examples:
                missingRow:
                  summary: DIN never seen by our master-data chain
                  value:
                    error:
                      code: CONTACT_NOT_AVAILABLE
                      message: We don't have contact data for this DIN yet. Update a containing company first.
                      nextStep: POST /v1/companies/:cin/unlock
                refreshTimeout:
                  summary: Upstream refresh did not land contact in the polling window
                  value:
                    error:
                      code: CONTACT_NOT_AVAILABLE
                      message: Contact refresh did not return contact data within the allowed window.
                      lastUpdateAttempt:
                        status: error
                        timestamp: "2026-05-01T07:30:00.000Z"
                        errorMessage: No data found in parsed resStr
    get:
      tags:
        - Director contact
      summary: Get director contact unlock status
      description: |
        **What it returns**

        Unlock state for this DIN's contact tier, plus freshness metadata (`contactUpdatedAt` + `lastUpdateAttempt`) so customers can self-detect stale data. Three branches:

        - **Active unlock:** `unlocked: true`, `unlockedAt`, `expiresAt`, plus `contactUpdatedAt` and `lastUpdateAttempt` so customers can self-detect stale data.
        - **No unlock:** `unlocked: false`, `unlockPrice` (or `null` if customer has no pricing for `directors.unlock`). Same freshness metadata included.
        - **Sandbox** (test key + sandbox DIN): synthetic `unlocked: true, sandbox: true`. No real contact metadata leaked.

        Read-only — doesn't trigger an upstream refresh or write to our cache.

        **Billing**

        Free. No wallet deduction; not subject to pricing rows.

        **Sandbox behavior**

        Test keys (`fsk_test_*`) on a sandbox-whitelisted DIN return the synthetic branch above. Outside the whitelist: `403 SANDBOX_ONLY`.

        **Common errors**

        - `400 INVALID_DIN` — DIN failed 8-digit format validation

        Plus the global auth/permission errors — see the [error codes table](#description/responses-and-errors).

        **See also**

        - [Unlock director's contact](#tag/director-contact/POST/v1/directors/{din}/unlock)
        - [Get director contact](#tag/director-contact/GET/v1/directors/{din}/contact)

      operationId: getDirectorContactUnlock
      parameters:
        - $ref: "#/components/parameters/DinPath"
      responses:
        "200":
          description: Unlock state for this DIN.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DirectorUnlockResponse"
              examples:
                unlocked:
                  summary: Active unlock with fresh contact data
                  value:
                    data:
                      din: "00002157"
                      unlocked: true
                      unlockedAt: "2026-05-01T07:30:00.000Z"
                      expiresAt: "2027-05-01T07:30:00.000Z"
                      unlockPrice: 29900
                      contactUpdatedAt: "2026-04-15T12:00:00.000Z"
                      lastUpdateAttempt:
                        status: success
                        timestamp: "2026-04-15T12:00:00.000Z"
                    meta: {}
                notUnlocked:
                  summary: Customer hasn't unlocked this DIN's contact
                  value:
                    data:
                      din: "00002157"
                      unlocked: false
                      unlockPrice: 29900
                      contactUpdatedAt: "2026-04-15T12:00:00.000Z"
                      lastUpdateAttempt:
                        status: success
                        timestamp: "2026-04-15T12:00:00.000Z"
                    meta: {}
        "400":
          $ref: "#/components/responses/InvalidDin"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          description: Test key on a non-sandbox DIN.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/directors/{din}/contact:
    get:
      tags:
        - Director contact
      summary: Get director contact
      description: |
        **What it returns**

        Director contact details — email, phone, and any other PII fields the MCA contact tier carries for this DIN. Returned only when the customer has an active contact-unlock for this DIN; otherwise returns the unlock-status JSON.

        Director-contact data is sourced from a separate refresh path, **not** the company download cascade. If our cached contact is fresh (<365 days old), the unlock returns instantly. Otherwise the unlock POST triggers an upstream refresh and polls for up to ~10 seconds; see [POST /v1/directors/{din}/unlock](#tag/director-contact/POST/v1/directors/{din}/unlock) for how that works.

        **Billing**

        Unlock-gated. The contact unlock is per-DIN, separate from the company-level unlock. Without an active contact unlock for this DIN, this endpoint returns `200 { unlocked: false, unlockPrice }` instead of the contact data, with no charge. Inside the unlock window (1 year), each call is metered at your `directors.contact` rate (see the [rate card](/pricing.html)).

        **Sandbox behavior**

        Test keys (`fsk_test_*`) on a sandbox-whitelisted DIN return a synthetic unlock state and the cached contact when present. Outside the sandbox: `403 SANDBOX_ONLY`.

        **Common errors**

        - `400 INVALID_DIN` — DIN failed 8-digit format validation
        - `404` — director not found in our cache. The contact unlock won't seed the row either (it'll return `422 CONTACT_NOT_AVAILABLE`); instead, unlock a containing company first via [POST /v1/companies/{cin}/unlock](#tag/documents-and-financials/POST/v1/companies/{cin}/unlock), which seeds the whole director chain, then retry the contact unlock for this DIN.

        Plus the global auth/wallet/permission errors — see the [error codes table](#description/responses-and-errors).

        **See also**

        - [Get director profile](#tag/look-up-the-register/GET/v1/directors/{din}) (no unlock; non-PII fields)
        - [Unlock director's contact](#tag/director-contact/POST/v1/directors/{din}/unlock)
        - [Get director contact unlock status](#tag/director-contact/GET/v1/directors/{din}/unlock)

      operationId: getDirectorContact
      parameters:
        - $ref: "#/components/parameters/DinPath"
      responses:
        "200":
          description: Contact data (when unlocked) or unlock status (when not unlocked).
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/SuccessEnvelope"
                  - $ref: "#/components/schemas/UnlockStatus"
              examples:
                unlockedSuccess:
                  summary: Sandbox DIN — contact unlocked (truncated)
                  value:
                    data:
                      din: "00002157"
                      mobileNumber: "+91 98xxxxxx00"
                      emailAddress: example@cars24.in
                    meta: {}
                notUnlocked:
                  summary: Customer hasn't unlocked this DIN's contact yet
                  value:
                    data:
                      unlocked: false
                      unlockPrice: 29900
                    meta: {}
  /v1/account/usage:
    get:
      tags:
        - Your account
      summary: Get wallet + usage
      description: |
        **What it returns**

        Your current wallet balance plus a per-endpoint breakdown of usage in the last 30 days. Money values are integers in **paisa** (1/100 of an INR rupee — e.g. `balancePaisa: 1000000` is ₹10,000.00). The `byEndpoint` array is sorted by `spendPaisa` descending and only contains endpoints actually called in the window — endpoints with zero usage are absent.

        Use this endpoint to surface "Wallet: ₹XYZ" + "Top spend: companies.master at ₹ABC" in your own dashboards. The 30-day window is rolling.

        **Billing**

        Free. No wallet deduction; not subject to pricing rows. Works with both `fsk_live_*` and `fsk_test_*` keys.

        **Sandbox behavior**

        Test keys see the same response shape with the same wallet (test/live keys share one wallet per customer). Sandbox-only test calls don't deduct, so they don't show up in `spendPaisa` totals — but they DO appear in usage logs with `priceChargedPaisa: 0`.

        **Common errors**

        Just the global auth errors — see the [error codes table](#description/responses-and-errors).

        **See also**

        - The [Developer Portal usage page](/portal/dashboard/usage) for an interactive view of the same data

      operationId: getAccountUsage
      responses:
        "200":
          description: Wallet balance and 30-day usage summary.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AccountUsageResponse"
              examples:
                activeCustomer:
                  summary: Active customer with realistic usage (truncated)
                  description: Real customers may show 5–10 endpoints in `byEndpoint`; example shows top 3 spenders.
                  value:
                    data:
                      wallet:
                        balancePaisa: 873500
                        currency: INR
                      usage:
                        last30Days:
                          totalCalls: 2340
                          totalSpendPaisa: 126500
                          byEndpoint:
                            - endpoint: companies.master
                              calls: 412
                              spendPaisa: 41200
                            - endpoint: companies.unlock
                              calls: 3
                              spendPaisa: 66000
                            - endpoint: companies.filings.download
                              calls: 1925
                              spendPaisa: 19250
                    meta:
                      generatedAt: "2026-05-01T07:45:12.000Z"
                newCustomer:
                  summary: New customer with no spend yet
                  value:
                    data:
                      wallet:
                        balancePaisa: 1000000
                        currency: INR
                      usage:
                        last30Days:
                          totalCalls: 0
                          totalSpendPaisa: 0
                          byEndpoint: []
                    meta:
                      generatedAt: "2026-05-01T07:45:12.000Z"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          description: |
            `ACCOUNT_DISABLED` — the customer account associated with this API key has been deactivated. Contact support to re-enable.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/account/pricing:
    get:
      tags:
        - Your account
      summary: Your rate card
      description: |
        **What it returns**

        The price your account pays for each endpoint, in **paisa**, read from the same pricing rows every billed call is charged from, so what you see here is exactly what a call will cost. One row per endpoint key you hold a price for; an endpoint with no row is one your account cannot call (`403 NO_ACCESS`). `defaultPricePaisa` is the published rate-card price for that endpoint, so you can see where your account has a negotiated rate; it is `null` for endpoints not on the public rate card.

        Use it to show your own users what an action will cost before they take it. Unlocks appear under their keys (`companies.unlock`, `directors.unlock`) at the unlock fee; per-call endpoints at the per-call fee.

        **Billing**

        Free. No wallet deduction; not subject to pricing rows.

        **Sandbox behavior**

        Test keys see the same rates as live keys (one rate card per account); sandbox calls are never charged regardless.

        **Common errors**

        Just the global auth errors, see the [error codes table](#description/responses-and-errors).

        **See also**

        - [Billing and the wallet](#description/billing-and-the-wallet)
        - [Get wallet + usage](#tag/your-account/GET/v1/account/usage)
      operationId: getAccountPricing
      responses:
        "200":
          description: The caller's effective rates.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AccountPricingResponse"
              example:
                data:
                  currency: INR
                  rates:
                    - endpoint: companies.documents.fetch
                      pricePerCallPaisa: 15000
                      defaultPricePaisa: 15000
                      effectiveFrom: "2026-09-14T00:00:00.000Z"
                    - endpoint: companies.master
                      pricePerCallPaisa: 500
                      defaultPricePaisa: 500
                      effectiveFrom: "2026-07-27T00:00:00.000Z"
                    - endpoint: companies.unlock
                      pricePerCallPaisa: 33000
                      defaultPricePaisa: 33000
                      effectiveFrom: "2026-07-27T00:00:00.000Z"
                    - endpoint: companies.update
                      pricePerCallPaisa: 100
                      defaultPricePaisa: 100
                      effectiveFrom: "2026-09-14T00:00:00.000Z"
                meta:
                  requestId: "…"
                  generatedAt: "2026-09-15T09:30:00.000Z"
        "401":
          $ref: "#/components/responses/Unauthorized"
  /v1/account/wallet/transactions:
    get:
      tags:
        - Your account
      summary: Wallet ledger
      description: |
        Every credit to your wallet, newest first: top-ups, refunds and manual credits. Charges are not
        ledger rows; they are usage, and appear in [usage](#tag/your-account/GET/v1/account/usage) and on
        every metered response as `meta.priceChargedPaisa`.

        `type` is `recharge`, `refund` or `admin_credit`. `balanceAfterPaisa` is the wallet balance right
        after that row landed, so the ledger reads as a running statement. `referenceId` links a top-up
        to its recharge order or a refund to what it refunds.

        **Billing:** free. **Sandbox:** same response on a test key; test and live keys share one wallet.
      operationId: getWalletTransactions
      parameters:
        - name: page
          in: query
          schema: { type: integer, minimum: 1, default: 1 }
        - name: limit
          in: query
          schema: { type: integer, minimum: 1, maximum: 200, default: 50 }
      responses:
        "200":
          description: Ledger rows, newest first.
          content:
            application/json:
              example:
                data:
                  - id: 6aa7a0c1b22e2991f01372ff
                    type: recharge
                    source: zoho
                    amountPaisa: 1000000
                    balanceAfterPaisa: 1129390
                    referenceId: pay_0123456789
                    metadata: null
                    createdAt: "2026-09-10T08:12:40.000Z"
                  - id: 6a9db6f052dc117ec46157c2
                    type: refund
                    source: system
                    amountPaisa: 15000
                    balanceAfterPaisa: 129390
                    referenceId: null
                    metadata:
                      reason: Refund of a charge that did no work
                    createdAt: "2026-09-06T18:55:02.000Z"
                meta:
                  page: 1
                  limit: 50
                  total: 2
                  totalPages: 1
                  requestId: "…"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /v1/account/wallet/recharge:
    post:
      tags:
        - Your account
      summary: Top up the wallet
      description: |
        Creates a payment order for the amount of credit you want and returns the session that opens the
        hosted checkout. GST at 18% is added on top: `amountPaisa` is the credit you receive,
        `totalPaisa` is what you pay. Credit lands in the wallet when the payment is confirmed; read
        [recharge status](#tag/your-account/GET/v1/account/wallet/recharge/{orderId}/status) to follow it,
        and expect a GST invoice against the order once it is paid.

        Requires a live key (`403 SANDBOX_NOT_ALLOWED` on a test key) and a completed
        [billing profile](#tag/your-account/GET/v1/account/billing), since the invoice needs it.
        Credit must be between ₹10,000 and ₹10,00,000 per order.

        **Billing:** creating the order is free; you pay at the checkout.
      operationId: createWalletRecharge
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [amountPaisa]
              properties:
                amountPaisa:
                  type: integer
                  description: Credit to add, in paisa. ₹10,000 to ₹10,00,000.
                  example: 1000000
      responses:
        "200":
          description: Payment order created; open the checkout with `paymentsSessionId`.
          content:
            application/json:
              example:
                data:
                  orderId: 6aa7a0c1b22e2991f01372ff
                  paymentsSessionId: 2000000012345678901
                  accountId: "60064718354"
                  amountPaisa: 1000000
                  gstPaisa: 180000
                  totalPaisa: 1180000
                meta:
                  requestId: "…"
        "400":
          description: "`INVALID_AMOUNT` (not a positive integer) or `AMOUNT_OUT_OF_RANGE` (outside ₹10,000 to ₹10,00,000)."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          description: "`SANDBOX_NOT_ALLOWED` on a test key, or `BILLING_INCOMPLETE` until the billing profile is set."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/account/wallet/recharge/{orderId}/status:
    get:
      tags:
        - Your account
      summary: Recharge status
      description: |
        The state of one top-up order: `pending` until the payment is confirmed, then `paid` (credit is in
        the wallet), or `failed` / `expired`. Read-only; polling it never credits anything. A payment that
        was confirmed but has not yet shown here is picked up by a periodic check within about fifteen
        minutes.

        **Billing:** free.
      operationId: getWalletRechargeStatus
      parameters:
        - name: orderId
          in: path
          required: true
          schema: { type: string }
          description: The `orderId` returned when the order was created.
      responses:
        "200":
          description: Order state.
          content:
            application/json:
              example:
                data:
                  orderId: 6aa7a0c1b22e2991f01372ff
                  status: paid
                  amountPaisa: 1000000
                  gstPaisa: 180000
                  totalPaisa: 1180000
                  currency: INR
                  paymentsSessionId: 2000000012345678901
                  createdAt: "2026-09-10T08:12:40.000Z"
                  paidAt: "2026-09-10T08:14:03.000Z"
                meta:
                  requestId: "…"
        "400":
          description: "`INVALID_ORDER_ID`"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          description: "`RECHARGE_NOT_FOUND`: no such order on your account."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/account/billing:
    get:
      tags:
        - Your account
      summary: Billing profile
      description: |
        The name, address and tax details your GST invoices are issued to. `null` until you set it with
        [PUT /v1/account/billing](#tag/your-account/PUT/v1/account/billing); a top-up needs it.

        **Billing:** free.
      operationId: getBillingProfile
      responses:
        "200":
          description: "The profile, or `billing: null`."
          content:
            application/json:
              example:
                data:
                  billing:
                    billingType: business
                    businessName: Example Analytics Private Limited
                    contactName: Priya Nair
                    email: finance@example.in
                    phone: "+91 98xxxxxx00"
                    gstin: 27AAAAA0000A1Z5
                    address: 4th Floor, 12 MG Road
                    city: Mumbai
                    state: "27"
                    zipCode: "400001"
                    country: India
                meta:
                  requestId: "…"
        "401":
          $ref: "#/components/responses/Unauthorized"
    put:
      tags:
        - Your account
      summary: Set the billing profile
      description: |
        Replaces the billing profile. `billingType` is `business` or `consumer`. A business must give
        `businessName`, a valid `gstin` and an `address`; the state of supply is read from the GSTIN. A
        consumer in India must give a two-digit `state` code. `contactName` is always required; `email`,
        `phone`, `city` and `zipCode` are optional. Values are trimmed and the GSTIN upper-cased before
        saving; the response returns the saved profile.

        **Billing:** free.
      operationId: putBillingProfile
      requestBody:
        required: true
        content:
          application/json:
            example:
              billingType: business
              businessName: Example Analytics Private Limited
              contactName: Priya Nair
              email: finance@example.in
              gstin: 27AAAAA0000A1Z5
              address: 4th Floor, 12 MG Road
              city: Mumbai
              zipCode: "400001"
              country: India
      responses:
        "200":
          description: The saved profile.
          content:
            application/json:
              example:
                data:
                  billing:
                    billingType: business
                    businessName: Example Analytics Private Limited
                    contactName: Priya Nair
                    email: finance@example.in
                    gstin: 27AAAAA0000A1Z5
                    address: 4th Floor, 12 MG Road
                    city: Mumbai
                    state: "27"
                    zipCode: "400001"
                    country: India
                meta:
                  requestId: "…"
        "400":
          description: "`INVALID_BILLING`, `INVALID_BILLING_TYPE`, `MISSING_CONTACT_NAME`, `MISSING_BUSINESS_NAME`, `MISSING_GSTIN`, `INVALID_GSTIN`, `MISSING_ADDRESS`, `MISSING_STATE` or `INVALID_STATE`; the message names the field."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /v1/account/recharges:
    get:
      tags:
        - Your account
      summary: Top-up history
      description: |
        Every top-up order on your account, newest first, with its payment state and invoice state.
        `canDownloadInvoice` is `true` once the GST invoice for a paid order has been issued; fetch it
        from [the invoice endpoint](#tag/your-account/GET/v1/account/invoices/{rechargeId}/pdf).

        **Billing:** free.
      operationId: getRecharges
      parameters:
        - name: page
          in: query
          schema: { type: integer, minimum: 1, default: 1 }
        - name: limit
          in: query
          schema: { type: integer, minimum: 1, maximum: 200, default: 50 }
      responses:
        "200":
          description: Orders, newest first.
          content:
            application/json:
              example:
                data:
                  - orderId: 6aa7a0c1b22e2991f01372ff
                    amountPaisa: 1000000
                    gstPaisa: 180000
                    totalPaisa: 1180000
                    status: paid
                    invoiceStatus: invoiced
                    invoiceNumber: FS/2026-27/000123
                    invoiceDate: "2026-09-10"
                    canDownloadInvoice: true
                    createdAt: "2026-09-10T08:12:40.000Z"
                    paidAt: "2026-09-10T08:14:03.000Z"
                meta:
                  page: 1
                  limit: 50
                  total: 1
                  totalPages: 1
                  requestId: "…"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /v1/account/invoices/{rechargeId}/pdf:
    get:
      tags:
        - Your account
      summary: Download a GST invoice
      description: |
        The GST invoice for one paid top-up, as a PDF. This is the one account endpoint that does not
        return the JSON envelope: the response body is the file, with a `Content-Disposition` filename
        of the invoice number.

        **Billing:** free.
      operationId: getInvoicePdf
      parameters:
        - name: rechargeId
          in: path
          required: true
          schema: { type: string }
          description: The `orderId` of a paid top-up.
      responses:
        "200":
          description: The invoice PDF.
          content:
            application/pdf:
              schema:
                type: string
                format: binary
        "400":
          description: "`INVALID_ORDER_ID`"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          description: "`RECHARGE_NOT_FOUND`: no such order on your account."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: "`INVOICE_NOT_READY`: the order is not paid yet, or its invoice has not been issued yet."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "503":
          description: "`INVOICING_UNAVAILABLE`: invoicing is temporarily unavailable; try again later."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
components:
  securitySchemes:
    apiKey:
      type: apiKey
      in: header
      name: x-api-key
      description: |
        FileSure API key. Format: `fsk_live_<uuid>` (production) or `fsk_test_<uuid>` (sandbox).

        Generate from the [Developer Portal](/portal/dashboard/keys).
  parameters:
    CinPath:
      in: path
      name: cin
      required: true
      schema:
        type: string
      example: U74999HR2015FTC056386
      description: 21-character Corporate Identification Number (CIN).
    CompanyIdentifierPath:
      in: path
      name: cin
      required: true
      schema:
        type: string
      description: >
        Company identifier. One of three formats — accepted on `/v1/companies/:cin`:


        - **CIN** — 21-character Indian Corporate Identification Number (e.g., U72900PB2022PTC055860)

        - **FCIN** — 6-character Foreign Company Identification Number (e.g., F07030)

        - **LLPIN** — 8-character LLP Identification Number (e.g., AAA-1234)


        Pair with the optional `?idType=cin|fcin|llpin` query param for stricter validation; auto-detect when omitted.


        Test keys (`fsk_test_*`) can only call identifiers in the **Sandbox** allowlist — see the Sandbox tag in the sidebar for the full list of 50 sample CINs and 2 LLPINs.
      example: U72200KA2013PLC097389
    DinPath:
      in: path
      name: din
      required: true
      schema:
        type: string
      example: "08087425"
      description: >
        8-digit Director Identification Number (DIN). Test keys (`fsk_test_*`) can only call DINs in the
        **Sandbox** allowlist — see the Sandbox tag in the sidebar for the full list.
    FormTypePath:
      in: path
      name: formType
      required: true
      schema:
        type: string
      example: AOC-4
      description: |
        MCA form whose extraction to access. Today only `AOC-4` (annual financials) is extracted; future
        forms will be added as additional extractors come online. Case-insensitive — `aoc-4` and `AOC-4`
        both match.
  schemas:
    SuccessEnvelope:
      type: object
      required:
        - data
        - meta
      properties:
        data:
          oneOf:
            - type: object
            - type: array
        meta:
          type: object
          properties:
            page:
              type: integer
            limit:
              type: integer
            total:
              type: integer
            totalPages:
              type: integer
            priceChargedPaisa:
              type: integer
              description: |
                Amount charged to the wallet for this call, in paisa. Present on every
                billed and unlock-gated 2xx response. `0` on sandbox calls (test keys
                never deduct).
              example: 100
            walletBalanceAfterPaisa:
              type: integer
              description: |
                Wallet balance after this call's deduction, in paisa. Present on every
                billed and unlock-gated 2xx response. `0` on sandbox calls.
              example: 999900
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              example: NO_ACCESS
            message:
              type: string
    NoAccessError:
      allOf:
        - $ref: "#/components/schemas/Error"
        - type: object
          properties:
            error:
              type: object
              properties:
                endpoint:
                  type: string
                  description: |
                    The endpoint key for which no pricing was found. Present on
                    `NO_ACCESS` only.
                catalogPricePaisa:
                  type: integer
                  nullable: true
                  description: |
                    Default rate-card price (paisa) for this endpoint, or `null`
                    if the endpoint has no default. Lets the caller see what they
                    would pay if access were enabled. Present on `NO_ACCESS` only.
    UnlockStatus:
      type: object
      description: |
        Returned by unlock-gated endpoints when the customer has no active unlock.
        `data` is locked to `{unlocked, unlockPrice}` (no extra properties) so the
        envelope is unambiguously distinguishable from real success payloads under
        a `oneOf` — required for spec validators to accept the data fetch route's
        `oneOf: [ExtractionDataResponse, UnlockStatus]` shape.
      required:
        - data
        - meta
      properties:
        data:
          type: object
          required:
            - unlocked
          additionalProperties: false
          properties:
            unlocked:
              type: boolean
              example: false
            unlockPrice:
              type: integer
              nullable: true
              description: Price in paisa to unlock this resource. `null` if no pricing assigned.
              example: 20000
        meta:
          type: object
    UnlockResponse:
      type: object
      description: |
        Returned by both `POST /unlock` (`202` — fresh unlock created) and `GET /unlock`
        (`200` — status check). The same envelope is used regardless of state; field
        presence varies (e.g., `expiresAt: null` and `sandbox: true` for synthetic
        sandbox responses).
      required:
        - data
        - meta
      properties:
        data:
          type: object
          required:
            - cin
            - unlocked
            - job
          properties:
            cin:
              type: string
              example: U72900PB2022PTC055860
            unlocked:
              type: boolean
            unlockedAt:
              type: string
              format: date-time
              nullable: true
              description: When the unlock was created. `null` for sandbox synthetic responses.
            expiresAt:
              type: string
              format: date-time
              nullable: true
              description: 1 year after `unlockedAt`. `null` for sandbox.
            unlockPrice:
              type: integer
              nullable: true
              description: Per-unlock price in paisa (₹ × 100). `null` if no pricing exists for this customer.
              example: 1000
            sandbox:
              type: boolean
              description: Present only on synthetic sandbox responses (test key + sandbox CIN).
              example: true
            job:
              nullable: true
              description: |
                Live snapshot of the background refresh job for this unlock. `null` immediately
                after `POST /unlock` (the refresh hasn't been registered yet) or for sandbox responses.
              allOf:
                - $ref: "#/components/schemas/UnlockJob"
        meta:
          type: object
    UnlockJob:
      type: object
      description: |
        Live snapshot of the download + extraction job for this CIN. Watch
        `processingStages.documentDownloadV3.status` for the primary download
        signal — progresses `pending` → `in_progress` → `success`, usually within
        minutes once the downloader picks up the job.

        Field set is intentionally narrow: we surface only the document-download
        stage, the financial extraction stage, and the two `*ExTriggered` flags.
        Internal identifiers, persistence metadata, internal email-notification
        tracking, and legacy stages (always-pending) are not surfaced.
      required:
        - id
        - processingStages
      properties:
        id:
          type: string
          description: Internal job ID, useful for support / debugging.
          example: 69e35b8a39d43c8b8b579a4e
        processingStages:
          type: object
          description: |
            Per-stage progress. Stage keys are added as work progresses —
            `documentDownloadV3` is always present once the background refresh
            has registered the job; `financials`, `financialExTriggered`, and
            `mgt7ExTriggered` appear after the document download completes and
            extraction kicks off.
          properties:
            documentDownloadV3:
              type: object
              description: PDF download from MCA. The status field is the primary completion signal.
              required:
                - status
                - lastUpdated
              properties:
                status:
                  type: string
                  enum:
                    - pending
                    - in_progress
                    - success
                  example: pending
                totalDocuments:
                  type: integer
                  example: 0
                downloadedDocuments:
                  type: integer
                  example: 0
                pendingDocuments:
                  type: integer
                  example: 0
                errorDocuments:
                  type: integer
                completionPercentage:
                  type: number
                  example: 0
                lastUpdated:
                  type: string
                  format: date-time
                completedAt:
                  type: string
                  format: date-time
                totalZipFiles:
                  type: integer
                zipFiles:
                  type: array
                  items:
                    type: object
                    properties:
                      filename:
                        type: string
                      blob_url:
                        type: string
                        format: uri
                      total_size_bytes:
                        type: integer
                      successful_files:
                        type: integer
                      createdAt:
                        type: string
                        format: date-time
            financials:
              type: object
              description: |
                Present only after the financial-extraction pipeline runs (post-V3 success).
                `perFiling[]` reports per-period extraction status; `summary` aggregates counts.
              properties:
                status:
                  type: string
                  enum:
                    - pending
                    - in_progress
                    - success
                    - failed
                progress:
                  type: number
                startedAt:
                  type: string
                  format: date-time
                completedAt:
                  type: string
                  format: date-time
                summary:
                  type: object
                  properties:
                    filingsSeen:
                      type: integer
                    processed:
                      type: integer
                    skipped:
                      type: integer
                    failed:
                      type: integer
                perFiling:
                  type: array
                  items:
                    type: object
            financialExTriggered:
              type: boolean
              description: True once the financial-extraction trigger has fired downstream.
            mgt7ExTriggered:
              type: boolean
              description: True once the MGT-7 extraction trigger has fired downstream.
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    DirectorUnlockResponse:
      type: object
      description: |
        Returned by `POST /v1/directors/{din}/unlock` (`200`) and
        `GET /v1/directors/{din}/unlock` (`200`). Same envelope used regardless
        of unlock state.
      required:
        - data
        - meta
      properties:
        data:
          type: object
          required:
            - din
            - unlocked
          properties:
            din:
              type: string
              example: "08087425"
            unlocked:
              type: boolean
            unlockedAt:
              type: string
              format: date-time
              nullable: true
              description: When the unlock was created. `null` for sandbox or no-unlock states.
            expiresAt:
              type: string
              format: date-time
              nullable: true
              description: 1 year after `unlockedAt`. `null` for sandbox or no-unlock states.
            unlockPrice:
              type: integer
              nullable: true
              description: Per-unlock price in paisa (₹ × 100). `null` if no pricing exists for this customer.
              example: 1000
            sandbox:
              type: boolean
              description: Present only on synthetic sandbox responses (test key + sandbox DIN).
              example: true
            contactUpdatedAt:
              type: string
              format: date-time
              nullable: true
              description: |
                When this DIN's cached contact was last refreshed. Use it to self-detect
                stale contact data (we treat anything <365 days old as fresh and won't
                re-trigger refresh on the next unlock attempt). `null` for sandbox.
            lastUpdateAttempt:
              nullable: true
              description: |
                Latest upstream refresh attempt status. Surfaces success/failure
                diagnostics. `null` for sandbox or when no refresh has ever been attempted.
              type: object
              properties:
                status:
                  type: string
                  description: "`success` or `error`."
                  example: success
                timestamp:
                  type: string
                  format: date-time
                  nullable: true
                errorMessage:
                  type: string
                  nullable: true
        meta:
          type: object
    ContactNotAvailable:
      type: object
      description: |
        422 body when contact data could not be made available — either we have
        no record of this DIN at all (need to seed via a containing-company unlock),
        or the upstream refresh failed/timed out within the polling window.
        Wallet is **not** deducted in either case.
      required:
        - error
      properties:
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              example: CONTACT_NOT_AVAILABLE
            message:
              type: string
              example: We don't have contact data for this DIN yet. Update a containing company first.
            nextStep:
              type: string
              description: Hint at the next user-actionable endpoint (only present when the row is missing entirely).
              example: POST /v1/companies/:cin/unlock
            lastUpdateAttempt:
              nullable: true
              description: Latest upstream refresh attempt status (only present on refresh-failure paths).
              type: object
              properties:
                status:
                  type: string
                  example: error
                timestamp:
                  type: string
                  format: date-time
                  nullable: true
                errorMessage:
                  type: string
                  nullable: true
    AlreadyUnlocked:
      type: object
      description: 409 conflict body when an active unlock already exists for this CIN.
      required:
        - error
      properties:
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              example: ALREADY_UNLOCKED
            message:
              type: string
              example: An active unlock exists for this CIN. Use GET /unlock to check status.
            unlockedAt:
              type: string
              format: date-time
            expiresAt:
              type: string
              format: date-time
            jobId:
              type: string
              nullable: true
              description: "Background refresh job ID, if it has already been registered; `null` otherwise."
    FilingListItem:
      type: object
      required:
        - createdAt
        - updatedAt
      description: >
        Raw verbatim MCA fields, with one deliberate omission: MCA's internal `documentCode` is never surfaced. The opaque `filingId` (`flg_…`) is the customer-facing handle — pass it to the download endpoint.


        Strings `"NULL"` (literal) are normalised to JSON `null` in the response.
      properties:
        filingId:
          type: string
          pattern: ^flg_[A-Za-z0-9_-]+$
          description: Opaque identifier — AES-encrypted token over `(cin, documentCode)`. Pass to the download endpoint as `:filingId`.
          example: flg_Ui2a1vItyyFYoAGGmefipN0RoT9EtmjHQz4KfzMeVE
        formId:
          type: string
          nullable: true
          example: MGT-7
        documentCategory:
          type: string
          nullable: true
          example: Annual Returns and Balance Sheet eForms
        attachmentLabel:
          type: string
          nullable: true
          example: SIGNEDDPT3
        dateOfFiling:
          type: string
          nullable: true
          description: DD/MM/YYYY string verbatim from MCA.
          example: 14/06/2021
        dscUploadDate:
          type: string
          nullable: true
        paymentDate:
          type: string
          nullable: true
          example: 14/06/2021
        fileName:
          type: string
          nullable: true
          example: Form DPT-3
        fileSize:
          type: integer
          nullable: true
          description: File size in bytes.
          example: 2349981
        fileType:
          type: string
          nullable: true
          example: pdf
        numberOfPages:
          type: integer
          nullable: true
          example: 7
        year:
          type: integer
          nullable: true
          example: 2021
        description:
          type: string
          nullable: true
        createdAt:
          type: string
          format: date-time
          nullable: true
          description: >-
            When this filing row was first written to our cache. ISO 8601. `null` for LLP rows (LLP filings have no source timestamps).
        updatedAt:
          type: string
          format: date-time
          nullable: true
          description: >-
            Last write to this filing row (refresh / re-download). ISO 8601. `null` for LLP rows — same reason as `createdAt`.
    FilingsListResponse:
      type: object
      required:
        - data
        - meta
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/FilingListItem"
        meta:
          type: object
          properties:
            page:
              type: integer
              example: 1
            limit:
              type: integer
              example: 50
            total:
              type: integer
              example: 8743
            totalPages:
              type: integer
              example: 175
    AccountPricingResponse:
      type: object
      required: [data, meta]
      properties:
        data:
          type: object
          required: [currency, rates]
          properties:
            currency:
              type: string
              example: INR
            rates:
              type: array
              items:
                type: object
                required: [endpoint, pricePerCallPaisa, defaultPricePaisa, effectiveFrom]
                properties:
                  endpoint:
                    type: string
                    description: Endpoint key, the same key shown in usage and in 403 NO_ACCESS errors.
                    example: companies.master
                  pricePerCallPaisa:
                    type: integer
                    description: What your account pays per call (or per unlock), in paisa.
                    example: 500
                  defaultPricePaisa:
                    type: integer
                    nullable: true
                    description: The published rate-card price for this endpoint, or null if it is not on the public card.
                    example: 500
                  effectiveFrom:
                    type: string
                    format: date-time
        meta:
          type: object
          properties:
            requestId:
              type: string
            generatedAt:
              type: string
              format: date-time
    AccountUsageResponse:
      type: object
      required:
        - data
        - meta
      properties:
        data:
          type: object
          required:
            - wallet
            - usage
          properties:
            wallet:
              type: object
              required:
                - balancePaisa
                - currency
              properties:
                balancePaisa:
                  type: integer
                  description: Current wallet balance in paisa (1/100 INR). Hard-blocks at 0 — top up before further billed calls.
                  example: 50000
                currency:
                  type: string
                  example: INR
            usage:
              type: object
              required:
                - last30Days
              properties:
                last30Days:
                  type: object
                  required:
                    - totalCalls
                    - totalSpendPaisa
                    - byEndpoint
                  properties:
                    totalCalls:
                      type: integer
                      example: 42
                    totalSpendPaisa:
                      type: integer
                      description: Total spend in paisa across all endpoints in the last 30 days.
                      example: 12500
                    byEndpoint:
                      type: array
                      description: Per-endpoint breakdown, sorted by `spendPaisa` descending. Only includes endpoints with at least one call in the window.
                      items:
                        type: object
                        required:
                          - endpoint
                          - calls
                          - spendPaisa
                        properties:
                          endpoint:
                            type: string
                            description: Endpoint identifier (dotted-constant form, e.g. `companies.master`).
                            example: companies.master
                          calls:
                            type: integer
                            example: 30
                          spendPaisa:
                            type: integer
                            example: 9000
        meta:
          type: object
          required:
            - generatedAt
          properties:
            generatedAt:
              type: string
              format: date-time
              description: ISO timestamp when the response was computed.
    ExtractionsListResponse:
      type: object
      required:
        - data
        - meta
      properties:
        data:
          type: object
          required:
            - availableFormTypes
          properties:
            availableFormTypes:
              type: array
              items:
                type: string
                example: AOC-4
              description: Sorted list of form types FileSure has extracted for this CIN. Empty array when no extractions exist.
        meta:
          type: object
          example: {}
    ExtractionYearsResponse:
      type: object
      required:
        - data
        - meta
      properties:
        data:
          type: object
          required:
            - formType
            - availableYears
          properties:
            formType:
              type: string
              example: AOC-4
              description: Canonical form type, normalized to upper case.
            availableYears:
              type: array
              description: Calendar end-years (descending) where any extraction exists for this form type. Union across `filing_scope` — pick scope on the data endpoint.
              items:
                type: integer
                example: 2024
        meta:
          type: object
          example: {}
    ExtractionDataResponse:
      type: object
      description: |
        Extracted financial statement for a specific (cin, formType, year, scope). Surfaced verbatim from the
        extraction pipeline — no derived fields, no UI shaping. Each fact in `balance_sheet`, `profit_and_loss`,
        and `cash_flow` carries a `qname` (XBRL element name), `value` (numeric or string), and `unit`.
        Optional verbosity fields (`decimals`, `labels`, `context`, `order_hint`) appear only when enabled
        upstream — design for mixed presence.
      required:
        - data
        - meta
      properties:
        data:
          type: object
          required:
            - cin
            - period_end
            - filing_scope
            - balance_sheet
            - profit_and_loss
            - cash_flow
            - metadata
          properties:
            cin:
              type: string
              example: U72900PB2022PTC055860
            period_start:
              type: string
              nullable: true
              description: Reporting period start date — string `YYYY-MM-DD`.
              example: "2023-04-01"
            period_end:
              type: string
              description: Reporting period end date — string `YYYY-MM-DD`.
              example: "2024-03-31"
            filing_scope:
              type: string
              enum:
                - standalone
                - consolidated
                - unknown
              example: standalone
            taxonomy_id:
              type: string
              nullable: true
              description: XBRL taxonomy used for the extraction.
              example: in-gaap-2016
            balance_sheet:
              type: array
              description: >-
                Balance-sheet facts. Each item is an object with `qname`, `value`, `unit` and optional verbosity fields — passthrough so additions surface
                without redeploy.
              items:
                type: object
                additionalProperties: true
            profit_and_loss:
              type: array
              items:
                type: object
                additionalProperties: true
            cash_flow:
              type: array
              items:
                type: object
                additionalProperties: true
            metadata:
              type: object
              description: >
                Filing-source metadata. `metadata.source` carries `dateOfFiling`, `paymentDate`, `year`, etc. — but **never** the MCA-internal `documentCode`,
                storage pointers (`storage_account`, `container`, `azureFileName`), or download URLs.
              additionalProperties: true
        meta:
          type: object
          required:
            - createdAt
            - updatedAt
          properties:
            createdAt:
              type: string
              format: date-time
              nullable: true
              description: When FileSure first extracted this filing.
            updatedAt:
              type: string
              format: date-time
              nullable: true
              description: Last write to this extraction (re-runs are idempotent).
    Mgt7ExtractionDataResponse:
      type: object
      description: |
        Extracted MGT-7 (Annual Return) data for a specific (cin, year). Surfaced verbatim from MCA's filed
        form — no derived totals, no UI flattening. The `form_type` field indicates which variant was
        actually filed (`MGT-7` for large companies, `MGT-7A` for small companies / OPC); MGT-7A omits
        some sections (no `kmp`, no `remuneration`).

        Sub-objects (`share_capital`, `share_holding_pattern`, `meetings`, `kmp`, `business_activities`,
        etc.) use `additionalProperties: true` so MCA additions and Tanim's extractor enhancements surface
        without redeploying this spec.
      required:
        - data
        - meta
      properties:
        data:
          type: object
          required:
            - cin
            - form_type
            - fy_end
          properties:
            cin:
              type: string
              example: L15400TG2009PLC062658
            form_type:
              type: string
              enum:
                - MGT-7
                - MGT-7A
              description: Which actual filing variant. MGT-7A omits some sections (no kmp, no remuneration); the field exists so customer code can branch on it.
              example: MGT-7
            srn:
              type: string
              description: MCA Service Request Number for this filing (the public reference shown on MCA portal).
              example: AC0745199
            fy_start:
              type: string
              format: date-time
              nullable: true
              description: Financial year start (ISO 8601, UTC midnight). Omitted when MCA's form left it blank.
              example: "2024-04-01T00:00:00.000Z"
            fy_end:
              type: string
              format: date-time
              description: Financial year end (ISO 8601, UTC midnight). Always present.
              example: "2025-03-31T00:00:00.000Z"
            agm_date:
              type: string
              format: date-time
              nullable: true
              description: Annual General Meeting date.
            filing_date:
              type: string
              format: date-time
              nullable: true
              description: Date MCA accepted the filing.
            is_opc:
              type: boolean
              description: One-Person-Company flag.
            is_share_listed:
              type: boolean
              description: Whether the company's shares are listed on any stock exchange.
            number_stock_exchange:
              type: integer
              description: Count of stock exchanges the company is listed on. `0` for unlisted companies.
            stock_exchanges:
              type: array
              description: List of exchange identifiers. Empty array for unlisted companies.
              items:
                type: string
            num_business_actv:
              type: integer
            business_activities:
              type: array
              description: Principal business activities, each carrying NIC code + description + turnover %.
              items:
                type: object
                additionalProperties: true
            num_rtas:
              type: integer
              description: Count of Registrar and Transfer Agents.
            rtas:
              type: array
              description: Registrar/Transfer Agent records. Empty when the company has none (common for unlisted).
              items:
                type: object
                additionalProperties: true
            num_holding_subs:
              type: integer
              description: Count of holding / subsidiary / associate / joint-venture companies.
            holding_subs:
              type: array
              description: Holding/subsidiary/associate corporate records.
              items:
                type: object
                additionalProperties: true
            share_capital:
              type: object
              description: |
                Authorised/issued/subscribed/paid-up breakdown for equity and preference shares, plus
                an unclassified-total and the paid-up breakup. Numeric values are in INR rupees
                (not paise — MCA filings use whole-rupee units).
              additionalProperties: true
            share_holding_pattern:
              type: object
              nullable: true
              description: |
                Promoter / public split + FII details. Surfaced as `null` for OPCs (single-shareholder
                companies — there's no "shareholding pattern" to report).
              additionalProperties: true
            turnover:
              oneOf:
                - type: number
                - type: string
              description: |
                Total turnover for the fiscal year, in INR rupees. Surfaced as a JS number when the value
                fits in `Number.MAX_SAFE_INTEGER` (≈ 9 quadrillion); else as a numeric string to preserve
                precision. Customers should treat large monetary values defensively.
              example: 6887017470
            net_worth:
              type: number
              description: Company net worth (assets − liabilities) in INR rupees.
              example: 888754630
            kmp:
              type: array
              description: |
                Directors + Key Managerial Personnel. **MGT-7 only** — omitted entirely on MGT-7A filings.
                Each entry carries `din`, `name`, `designation`, etc. — keys are MCA's own.
              items:
                type: object
                additionalProperties: true
            meetings:
              type: object
              description: |
                Meeting counts + attendance for Members, Board, and Committees. Keys: `board_meetings`,
                `board_total`, `members_meetings`, `members_total`, `committee_meetings`, `committee_total`,
                `directors_attendance`.
              additionalProperties: true
            remuneration:
              type: object
              description: |
                Directors + Managing Director + CFO + Company Secretary remuneration details.
                **MGT-7 only** — omitted entirely on MGT-7A filings.
              additionalProperties: true
        meta:
          type: object
          required:
            - createdAt
            - updatedAt
          properties:
            createdAt:
              type: string
              format: date-time
              nullable: true
              description: When FileSure first extracted this MGT-7 filing.
            updatedAt:
              type: string
              format: date-time
              nullable: true
              description: Last write to this extraction (re-runs are idempotent — upsert on `(cin, formId, fyEnd)`).
    Pas3CapitalColumn:
      type: object
      description: |
        One column of an AOC-1 / PAS-3 capital structure block (authorised, issued, subscribed, paid_up).
        Share counts surface as JS number when within `Number.MAX_SAFE_INTEGER`; nominal + total are
        strings (BSON Decimal128) to preserve precision.
      properties:
        shares:
          oneOf:
            - type: integer
            - type: string
          description: Share count for this column. JS integer when safe; otherwise string.
        nominal:
          type: string
          description: Nominal (face) value per share, as a decimal string.
        total:
          type: string
          description: Total monetary value for this column (shares × nominal), as a decimal string.
    Pas3ShareCapital:
      type: object
      description: Authorised / issued / subscribed / paid_up breakdown for one share class.
      properties:
        authorised:
          $ref: "#/components/schemas/Pas3CapitalColumn"
        issued:
          $ref: "#/components/schemas/Pas3CapitalColumn"
        subscribed:
          $ref: "#/components/schemas/Pas3CapitalColumn"
        paid_up:
          $ref: "#/components/schemas/Pas3CapitalColumn"
    Pas3DebtStructure:
      type: object
      description: Per-filing debt structure at allotment time.
      properties:
        debentures:
          type: string
          description: Outstanding debentures value, as a decimal string.
        secured_loans:
          type: string
        others:
          type: string
    Pas3CapitalStructure:
      type: object
      description: |
        Capital structure as-of the filing. Carries equity + preference share capital and a debt block.
        Numbers are surfaced verbatim from MCA's form (no derived totals).
      properties:
        equity:
          $ref: "#/components/schemas/Pas3ShareCapital"
        preference:
          $ref: "#/components/schemas/Pas3ShareCapital"
        debt:
          $ref: "#/components/schemas/Pas3DebtStructure"
          nullable: true
    Pas3FilingSummary:
      type: object
      description: |
        Compact per-filing summary for the list endpoint. Enough for the customer to decide which
        filing to fetch the detail for — pass `filing_id` into the detail URL.
      required:
        - filing_id
      properties:
        filing_id:
          type: string
          description: Opaque server-derived token (`flg_<base64url>`). AES-encrypted `(cin, documentCode)`. Deterministic per `(cin, docId)`, server-side decoded.
          pattern: "^flg_[A-Za-z0-9_-]+$"
          example: flg_xKpQyNwz3FdJaT_l5sA9bKp4XmHfVc_pQk1nR5tWzE2YuJrCqL3mNbDgHvSf
        filing_date:
          type: string
          format: date-time
          description: Date MCA accepted the filing (ISO 8601, UTC).
        as_of_date:
          type: string
          format: date-time
          nullable: true
          description: |
            Capital-structure-as-of date — derived from the latest `allotment_date` in this filing.
            Null when the filing has no allotments (defensive — shouldn't happen on real data).
        srn:
          type: string
          description: |
            MCA Service Request Number for this filing. **Omitted when null** — Tanim's XFA-variant
            extractors don't populate srn on every event (~79% of historical events have null srn);
            omission is normal, not an error.
        allotment_count:
          type: integer
          description: Number of allotment tranches in this filing.
    Pas3Snapshot:
      type: object
      description: |
        Latest capital-structure snapshot for the CIN. Carries an opaque `filing_id` pointing to the
        underlying source filing (use it on the detail endpoint), the `as_of_date` (capital structure
        in effect as of), `filing_date` (when the filing was accepted), optional `srn`, and the full
        capital structure subdocs (equity + preference + debt).
      properties:
        filing_id:
          type: string
          description: Opaque `flg_*` token addressing the underlying source filing.
        as_of_date:
          type: string
          format: date-time
        filing_date:
          type: string
          format: date-time
        srn:
          type: string
        equity:
          $ref: "#/components/schemas/Pas3ShareCapital"
        preference:
          $ref: "#/components/schemas/Pas3ShareCapital"
        debt:
          $ref: "#/components/schemas/Pas3DebtStructure"
          nullable: true
    Pas3FilingsListResponse:
      type: object
      description: |
        PAS-3 list-endpoint response shape. Different from `ExtractionYearsResponse` because PAS-3 is
        event-based (one filing per share allotment, multiple per year). Returns the most-recent
        consolidated `latest_snapshot` + a sorted list of per-filing summaries — `filing_id` is the
        token to use on the detail endpoint.
      required:
        - data
        - meta
      properties:
        data:
          type: object
          required:
            - form_type
            - latest_snapshot
            - filings
          properties:
            form_type:
              type: string
              enum:
                - PAS-3
              example: PAS-3
            latest_snapshot:
              $ref: "#/components/schemas/Pas3Snapshot"
              nullable: true
              description: |
                Most-recent capital-structure snapshot. `null` when the CIN has no PAS-3 events at all
                (but in that case `filings: []` too — use either signal).
            filings:
              type: array
              description: Per-filing summaries, sorted by `filing_date` descending.
              items:
                $ref: "#/components/schemas/Pas3FilingSummary"
        meta:
          type: object
          required:
            - createdAt
            - updatedAt
          properties:
            createdAt:
              type: string
              format: date-time
              nullable: true
            updatedAt:
              type: string
              format: date-time
              nullable: true
    Pas3Allotment:
      type: object
      description: |
        One allotment tranche within a PAS-3 filing. Multi-tranche filings (rare — typically used for
        compound transactions) carry several entries here. Field set varies by `allotment_type`:
        cash allotments populate `total_amount`, non-cash allotments populate the `non_cash` subdoc.
      properties:
        allotment_date:
          type: string
          format: date-time
        security_type:
          type: string
          description: MCA-coded security type — e.g. `Equity`, `Preference`, `Debenture`, `Other`.
        allotment_type:
          type: string
          description: |
            MCA-coded allotment reason — examples: `Employee stock option Plan (ESOP)`,
            `Right Issue`, `Bonus issue`, `Conversion of debentures`, `Private placement`, etc.
        details:
          type: string
          description: Free-text additional details (often blank for routine ESOP allotments).
        terms_in_attachment:
          type: boolean
          description: Whether full terms live in an attached PDF rather than in form fields.
        mode:
          type: string
          enum:
            - Cash
            - Other than cash
            - Partly cash and partly non-cash
          description: Consideration mode.
        num_securities_allotted:
          type: integer
        nominal_per_security:
          type: string
          description: Face value per share, as a decimal string.
        premium_per_security:
          type: string
        discount_per_security:
          type: string
        total_amount:
          type: string
          description: Total consideration in INR, as a decimal string.
        non_cash:
          type: object
          nullable: true
          description: |
            Non-cash consideration breakdown (property, services, goodwill, etc.). Null for cash allotments.
          additionalProperties: true
    Pas3ExtractionDataResponse:
      type: object
      description: |
        PAS-3 (Return of Allotment) detail-endpoint response. Addressed by `filing_id` (an opaque
        `flg_*` token), the response returns the per-event `capital_structure` (equity + preference +
        debt subdocs) + chronological `allotments[]` for that one filing.

        Decimal128 values surface as strings to preserve precision. BSON Long (share counts) surface
        as JS number when in safe range; else as string.
      required:
        - data
        - meta
      properties:
        data:
          type: object
          required:
            - cin
            - form_type
            - filing_id
          properties:
            cin:
              type: string
              example: L17110MH1973PLC019786
            form_type:
              type: string
              enum:
                - PAS-3
              example: PAS-3
            filing_id:
              type: string
              description: The same opaque token used in the URL — surfaced back for bookmarkable response cohesion.
            filing_date:
              type: string
              format: date-time
            srn:
              type: string
              description: MCA Service Request Number. Omitted when null (XFA extractors).
            capital_structure:
              $ref: "#/components/schemas/Pas3CapitalStructure"
            allotments:
              type: array
              description: Allotment tranches within this filing. Usually one entry; multi-tranche filings are rare.
              items:
                $ref: "#/components/schemas/Pas3Allotment"
        meta:
          type: object
          required:
            - createdAt
            - updatedAt
          properties:
            createdAt:
              type: string
              format: date-time
              nullable: true
              description: When FileSure first extracted this PAS-3 filing.
            updatedAt:
              type: string
              format: date-time
              nullable: true
              description: Last write to this extraction (re-runs are idempotent — upsert on `(cin, docId)`).
    ChargeFlags:
      type: object
      description: Per-charge flags (multi-bank / consortium / pari-passu).
      properties:
        is_joint:
          type: boolean
        is_consortium:
          type: boolean
        is_pari_passu:
          type: boolean
    ChargeCurrent:
      type: object
      description: |
        Current consolidated state of a charge (computed by the worker on every event). Omitted entirely
        from the detail response when the source `current` is empty `{}` (typical for SATISFIED charges
        where there's nothing currently active).
      properties:
        amount_inr:
          type: integer
          description: Current outstanding amount in INR rupees (after modifications).
        amount_crore:
          type: number
        roi_pct:
          type: string
          description: Rate of interest — often free-text (`"9.50%"` or `"as per Bank's Sanction Letter"`).
        holder_name:
          type: string
          description: Primary lender name.
        holder_category:
          type: string
          description: MCA-coded category — examples `"Private Sector Bank"`, `"Public Sector Bank"`, `"NBFC"`.
        num_holders:
          type: integer
        flags:
          $ref: "#/components/schemas/ChargeFlags"
        property_type_raw:
          type: array
          description: MCA's original property-type classifications, verbatim.
          items:
            type: string
        property_types:
          type: array
          description: Normalized property-type enum.
          items:
            type: string
        instrument_desc:
          type: string
    ChargeSummary:
      type: object
      description: Per-charge summary entry in the list endpoint's `charges[]` array.
      required:
        - charge_id
      properties:
        charge_id:
          type: string
          description: MCA's public charge identifier (numeric string).
          pattern: "^[0-9]+$"
          example: "10596825"
        status:
          type: string
          enum:
            - ACTIVE
            - SATISFIED
            - OPEN
        holder_name:
          type: string
        holder_category:
          type: string
        amount_inr:
          type: integer
        amount_crore:
          type: number
        counts:
          type: object
          properties:
            creation:
              type: integer
            modification:
              type: integer
            satisfaction:
              type: integer
        first_event_date:
          type: string
          format: date-time
          description: Earliest event_date in the charge's timeline.
        latest_event_date:
          type: string
          format: date-time
        latest_event_type:
          type: string
          enum:
            - CREATION
            - MODIFICATION
            - SATISFACTION
    ChargeEventCharge:
      type: object
      description: Per-event loan / facility detail. Populated on CREATION + MODIFICATION events; usually absent on SATISFACTION events.
      properties:
        amount_inr:
          type: integer
        amount_crore:
          type: number
        amount_words:
          type: string
        date_of_security:
          type: string
          format: date-time
          nullable: true
        roi_pct:
          type: string
        repay_months:
          type: string
        terms_of_repay:
          type: string
        nature_facility:
          type: string
          nullable: true
        date_of_disburs:
          type: string
          format: date-time
          nullable: true
        margin:
          type: string
        extent_ops_chg:
          type: string
      additionalProperties: true
    ChargeEventHolder:
      type: object
      description: Per-event holder (lender) detail. For consortium charges, the `holders[]` nested array carries co-lenders.
      properties:
        is_joint:
          type: boolean
        is_consortium:
          type: boolean
        is_pari_passu:
          type: boolean
        num_holders:
          type: integer
        lead_bank:
          type: string
          nullable: true
        category:
          type: string
          description: MCA short code (e.g. `PVTB` = Private Bank, `PUB` = Public Bank).
        name_of_chg_holder:
          type: string
        name:
          type: string
        add_line1:
          type: string
        add_line2:
          type: string
        city:
          type: string
        state:
          type: string
        pin_code:
          type: string
        country:
          type: string
        holders:
          type: array
          description: Co-lender entries (consortium charges only). Each entry has the same shape as the primary holder.
          items:
            type: object
            additionalProperties: true
    ChargeEvent:
      type: object
      description: |
        One lifecycle event for a charge. Variable richness — older events may have only `event_type`
        + dates + `srn`. Newer / fully-extracted events have the full set of subdocs. Surface only
        populated fields; null values are omitted entirely (no null placeholders).
      required:
        - event_type
      properties:
        event_type:
          type: string
          enum:
            - CREATION
            - MODIFICATION
            - SATISFACTION
        event_date:
          type: string
          format: date-time
        filing_date:
          type: string
          format: date-time
        reg_date:
          type: string
          format: date-time
        sat_date:
          type: string
          format: date-time
          description: Date of satisfaction. Populated only on SATISFACTION events.
        srn:
          type: string
        charge:
          $ref: "#/components/schemas/ChargeEventCharge"
        holder:
          $ref: "#/components/schemas/ChargeEventHolder"
        security:
          type: object
          additionalProperties: true
        asset_particulars:
          type: object
          additionalProperties: true
        desc_of_modification:
          type: string
          description: Free-text description of the modification (MODIFICATION events).
        satisfaction:
          type: object
          additionalProperties: true
          description: Closure details (SATISFACTION events).
    ChargesListResponse:
      type: object
      description: |
        CHARGES list-endpoint response shape. One entry per `charge_id` for the CIN, sorted by
        `latest_event_date` descending so most-recently-active charges appear first.
      required:
        - data
        - meta
      properties:
        data:
          type: object
          required:
            - form_type
            - charges
          properties:
            form_type:
              type: string
              enum:
                - CHARGES
              example: CHARGES
            charges:
              type: array
              items:
                $ref: "#/components/schemas/ChargeSummary"
        meta:
          type: object
          required:
            - createdAt
            - updatedAt
          properties:
            createdAt:
              type: string
              format: date-time
              nullable: true
            updatedAt:
              type: string
              format: date-time
              nullable: true
    ChargesDetailResponse:
      type: object
      description: |
        CHARGES detail-endpoint response. Returns the full lifecycle for one charge_id — `current`
        consolidated state (omitted for SATISFIED charges with empty current), `counts`, and
        chronologically-ascending `events[]` with per-event detail subdocs.
      required:
        - data
        - meta
      properties:
        data:
          type: object
          required:
            - cin
            - form_type
            - charge_id
            - events
          properties:
            cin:
              type: string
              example: U27100PB1996PLC017827
            form_type:
              type: string
              enum:
                - CHARGES
              example: CHARGES
            charge_id:
              type: string
              pattern: "^[0-9]+$"
            status:
              type: string
              enum:
                - ACTIVE
                - SATISFIED
                - OPEN
            counts:
              type: object
              properties:
                creation:
                  type: integer
                modification:
                  type: integer
                satisfaction:
                  type: integer
            current:
              $ref: "#/components/schemas/ChargeCurrent"
              description: |
                Current consolidated state. **Omitted entirely** when source `current` is empty
                (typical for SATISFIED charges).
            events:
              type: array
              description: Lifecycle events, sorted by `event_date` ascending (chronological).
              items:
                $ref: "#/components/schemas/ChargeEvent"
        meta:
          type: object
          required:
            - createdAt
            - updatedAt
          properties:
            createdAt:
              type: string
              format: date-time
              nullable: true
              description: When FileSure first extracted this charge.
            updatedAt:
              type: string
              format: date-time
              nullable: true
              description: Last write to this extraction (re-runs are idempotent — upsert on `(cin, chargeId)`).
    SandboxEntitiesResponse:
      type: object
      required:
        - data
        - meta
      properties:
        data:
          type: object
          required:
            - companies
            - directors
          properties:
            companies:
              type: array
              items:
                type: object
                required:
                  - cin
                  - company
                  - knownAs
                  - idType
                  - companyStatus
                  - city
                  - state
                properties:
                  cin:
                    type: string
                    description: CIN, or LLPIN for an LLP.
                    example: L93030DL2010PLC198141
                  company:
                    type: string
                    description: Legal name as MCA records it.
                    example: ETERNAL LIMITED
                  knownAs:
                    type: string
                    nullable: true
                    description: The brand the company is known by, when it differs from the legal name.
                    example: Eternal, formerly Zomato
                  idType:
                    type: string
                    enum: [cin, fcin, llpin]
                  companyStatus:
                    type: string
                    nullable: true
                    example: Active
                  city:
                    type: string
                    nullable: true
                    example: NEW DELHI
                  state:
                    type: string
                    nullable: true
                    example: Delhi
            directors:
              type: array
              items:
                type: object
                required:
                  - din
                  - fullName
                properties:
                  din:
                    type: string
                    example: "11692472"
                  fullName:
                    type: string
                    nullable: true
                    example: NILESH KASHINATH HIWALE
        meta:
          type: object
          properties:
            companies:
              type: integer
              description: How many companies the sandbox holds.
            directors:
              type: integer
              description: How many directors the sandbox holds.
            requestId:
              type: string
    CompanyResolveResponse:
      type: object
      required:
        - data
        - meta
      properties:
        data:
          type: object
          required:
            - query
            - candidates
          properties:
            query:
              type: string
              example: swiggy
            candidates:
              type: array
              items:
                $ref: "#/components/schemas/CompanyResolveCandidate"
            sandbox:
              type: boolean
              description: Present and `true` on a test key, whose search covers only the sandbox.
            hint:
              type: string
              description: On a test key with no match, where the sandbox identifiers are listed.
        meta:
          type: object
          properties:
            requestId:
              type: string
            priceChargedPaisa:
              type: integer
            walletBalanceAfterPaisa:
              type: integer
    CompanyResolveCandidate:
      type: object
      required:
        - cin
        - company
        - idType
        - directors
        - matchScore
      properties:
        cin:
          type: string
          example: L74110KA2013PLC096530
        company:
          type: string
          example: SWIGGY LIMITED
        idType:
          type: string
          enum:
            - cin
            - fcin
            - llpin
        companyStatus:
          type: string
          nullable: true
          example: Active
        dateOfIncorporation:
          type: string
          nullable: true
          description: MM/DD/YYYY string as MCA returns it. Every date in company, director and charge records uses this format; filing records use DD/MM/YYYY.
          example: 09/12/2013
        registeredAddress:
          type: object
          nullable: true
          properties:
            streetAddress:
              type: string
              nullable: true
            city:
              type: string
              nullable: true
            state:
              type: string
              nullable: true
            postalCode:
              type: string
              nullable: true
            country:
              type: string
              nullable: true
        nicCode:
          type: string
          nullable: true
        mainDivisionDescription:
          type: string
          nullable: true
        directors:
          type: array
          items:
            type: object
            properties:
              din:
                type: string
                nullable: true
              fullName:
                type: string
                nullable: true
              designation:
                type: string
                nullable: true
              isPromoter:
                type: boolean
        matchScore:
          type: number
          description: Typesense relevance score (higher = better match)
    DirectorResolveResponse:
      type: object
      required:
        - data
        - meta
      properties:
        data:
          type: object
          required:
            - query
            - candidates
          properties:
            query:
              type: string
              example: amit sharma
            candidates:
              type: array
              items:
                $ref: "#/components/schemas/DirectorResolveCandidate"
            sandbox:
              type: boolean
              description: Present and `true` on a test key, whose search covers only the sandbox.
            hint:
              type: string
              description: On a test key with no match, where the sandbox identifiers are listed.
        meta:
          type: object
          properties:
            requestId:
              type: string
            priceChargedPaisa:
              type: integer
            walletBalanceAfterPaisa:
              type: integer
    DirectorResolveCandidate:
      type: object
      required:
        - din
        - companies
        - totalDirectorshipCount
        - matchScore
      properties:
        din:
          type: string
          example: "00002157"
        fullName:
          type: string
          nullable: true
        status:
          type: string
          nullable: true
        dinAllocationDate:
          type: string
          nullable: true
        personType:
          type: string
          nullable: true
        companies:
          type: array
          items:
            type: string
        totalDirectorshipCount:
          type: integer
        matchScore:
          type: number
    CompanyMasterDataResponse:
      type: object
      required:
        - data
        - meta
      properties:
        data:
          type: object
          required:
            - cin
            - company
            - cinHistory
            - nameHistory
            - masterData
          properties:
            cin:
              type: string
              example: U72900PB2022PTC055860
            company:
              type: string
              example: ARKIZE SOLUTIONS PRIVATE LIMITED
            cinHistory:
              type: array
              description: Observed CIN changes. Empty array if none.
              items:
                type: object
                properties:
                  oldCin:
                    type: string
                  detectedAt:
                    type: string
                    format: date-time
            nameHistory:
              type: array
              description: Observed name changes. Empty array if none.
              items:
                type: object
                properties:
                  oldName:
                    type: string
                  detectedAt:
                    type: string
                    format: date-time
            masterData:
              type: object
              description: |
                The MCA master-data packet. Always-present keys with sensible defaults:
                `null` for missing object blocks, `[]` for missing arrays.
              required:
                - companyData
                - commonData
                - directorData
                - indexChargesData
              properties:
                companyData:
                  type: object
                  nullable: true
                  description: |
                    The MCA master record: companyType, companyOrigin, registrationNumber,
                    dateOfIncorporation, MCAMDSCompanyAddress[] (registered + correspondence only),
                    authorisedCapital, paidUpCapital, whetherListedOrNot, mainDivisionDescription, etc.
                    Surfaced verbatim from MCA (camelCase, British spellings preserved). 9 always-empty
                    fields are dropped: `ARDefaulter2Yrs`, `ARDefaulter3Yrs`, `BSDefaulter3Yrs`,
                    `establishmentDate`, `incorporationDateObj`, `numberOfMembers`, `previousFirm_companyDetails`,
                    `smallLLPFlag`, `statementDate`. `null` if not yet fetched.

                    Capital amounts additionally carry readable companions, added beside the
                    raw numbers rather than replacing them: `paidUpCapitalFormatted`, `authorisedCapitalFormatted` and
                    `subscribedCapitalFormatted` (compact, e.g. `₹7.69 Cr`),
                    `paidUpCapitalDisplay`, `authorisedCapitalDisplay` and
                    `subscribedCapitalDisplay` (full, with Indian digit grouping, e.g.
                    `₹7,69,34,000`). The raw integers are unchanged. No separate currency
                    field — the ₹ sign carries it.
                  additionalProperties: true
                commonData:
                  type: object
                  nullable: true
                  description: |
                    MCA's supplementary record: NIC codes 1/2/3 with descriptions, AGM date,
                    balance-sheet date, compliance flags, etc. Surfaced verbatim. `null` if not yet
                    fetched (this stage runs after `companyData`). Two drop categories applied:
                    (1) 14 fields that duplicate `companyData` 1:1 are stripped here (canonical lives
                    there): `companyName`, `cin`, `pan`, `companyType`, `companyOrigin`, `companyCategory`,
                    `companySubcategory`, `classOfCompany`, `whetherListedOrNot`, `numberOfMembers`,
                    `numberOfPartners`, `numberOfDesignatedPartners`, `authorisedCapital`, `paidupCapital`.
                    (2) 16 always-empty fields: `NoOfMembersExcludingProposedEmployees`, `agmDate`,
                    `amalgamatedDate`, `dateOfBalanceSheet`, `dateOfIncorporation`, `establishmentDt`,
                    `inc24Flag`, `inspectionFlag`, `maxNoOfMembersExcludingProposedEmployees`, `officeType`,
                    `otherOfficeType`, `phone`, `section8LicenseNumber`, `statusChangeDate`, `vanishFlag`,
                    `whetherListedOrNot`.
                  additionalProperties: true
                directorData:
                  type: array
                  description: |
                    Directors-of-this-company list. Rows deduped by DIN — same-DIN duplicates collapsed
                    (preferring populated `PAN`), `MCAUserRole[]` arrays merged with composite-key
                    dedup (`role + cin + currentDesignationDate + roleEffectiveDate`). All-blank rows
                    (DIN + PAN + name all empty) are dropped. Each item carries `DIN`, `PAN`,
                    `FirstName`, `MiddleName`, `LastName`, `dateOfAppointment`, `DirectorDisqualified`,
                    `contactAddress[]`, `MCAUserRole[]`. Inside each `MCAUserRole` entry, 13 fields are
                    dropped: 4 MCA-internal IDs (`userId`, `companyId`, `userName`, `approverId`),
                    3 always-empty (`directorDeathDate`, `opcType`, `shareholdingPercentage`),
                    1 single-value noise (`oidFlag`), and 5 duplicates of director-row fields
                    (`firstName`, `middleName`, `lastName`, `din`, `pan`). All other role fields
                    (designation, role, kmpFlag, opcFlag, dob, mobileNumber, emailAddress, etc.)
                    pass through. Filter `MCAUserRole[]` yourself to derive current/past/executive
                    categorization. Empty `[]` is rare.
                  items:
                    type: object
                    additionalProperties: true
                indexChargesData:
                  type: array
                  description: |
                    Charges (mortgages) on this company verbatim. PascalCase address fields preserved
                    (`StreetAddress`, `StreetAddress2`, `Country`, `Locality`, `State`, `District`,
                    `City`, `PostalCode`). Always-empty fields dropped: `StreetAddress3`, `StreetAddress4`,
                    `registeredName`. `chargeHolderName` (16-value bucket) and `chName` (actual entity
                    name, 53 distinct values) are both kept — they're different fields, not duplicates.
                    Empty `[]` is common — most companies have no charges. Detailed CHG-1/CHG-4 form
                    data (rate of interest, instrument description, particulars) is **not** in this
                    response — query the Filings/Extractions APIs for that.
                  items:
                    type: object
                    additionalProperties: true
              additionalProperties: true
        meta:
          type: object
          description: |
            Data freshness signal — stored timestamps from our cached company
            data, surfaced verbatim. ISO 8601 strings; `null` on legacy rows that
            pre-date timestamping.
          required:
            - createdAt
            - updatedAt
          properties:
            createdAt:
              type: string
              format: date-time
              nullable: true
              description: When FileSure first fetched this CIN.
            updatedAt:
              type: string
              format: date-time
              nullable: true
              description: Last write to this doc (refresh from MCA).
    DirectorMasterDataResponse:
      type: object
      required:
        - data
        - meta
      properties:
        data:
          type: object
          required:
            - din
            - companyData
            - mcaSignatoryCessationMasterHistory
          properties:
            din:
              type: string
              example: "00002157"
            firstName:
              type: string
              nullable: true
              example: ARUN
            middleName:
              type: string
              nullable: true
            lastName:
              type: string
              nullable: true
              example: GUPTA
            fullName:
              type: string
              nullable: true
              example: ARUN GUPTA
            companyData:
              type: array
              description: |
                Per-company role records verbatim from MCA's director lookup
                response. Each row is the company-side view of this director's relationship — role,
                designation, directorship dates, company metadata. PII (`pan`, `mobileNumber`, etc.),
                MCA / FileSure-internal IDs (`accountId`, `userId`, `srn`, `companyId`, etc.), per-row
                dupes of top-level identity fields, and operational flags (`flagged`, `oldFlag`) are
                stripped. Everything else passes through. Customers filter / aggregate this themselves
                to derive views like "current directorships" or "ceased". Empty `[]` is rare — almost
                every DIN has at least one row.
              items:
                type: object
                additionalProperties: true
            mcaSignatoryCessationMasterHistory:
              type: array
              description: |
                Past (and sometimes current) company appointments with cessation dates from MCA's
                signatory cessation ledger. Complements `companyData[]` when that array is empty or
                rows lack `cessationDate`. Rows carry `cin`, `accountName`, `designation`,
                `appointmentDate`, `cessationDate` and usually `accountStatus`. PII, internal IDs and
                the per-row `din` (always equal to `data.din`) are stripped with the same rules as
                `companyData[]`. Empty `[]` when no history is cached for this DIN.
              items:
                type: object
                additionalProperties: true
        meta:
          type: object
          required:
            - updatedAt
            - source
          properties:
            updatedAt:
              type: string
              format: date-time
              nullable: true
              description: >-
                ISO timestamp of the most recent write to this director's cached record. `null` on legacy rows that pre-date timestamping. No `createdAt` is surfaced — the upstream source doesn't carry one.
            source:
              type: string
              enum:
                - MCA
              example: MCA
  responses:
    InvalidCompanyIdentifier:
      description: Identifier failed format validation, or unknown idType value
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          examples:
            invalidCin:
              summary: Identifier doesn't match CIN/FCIN/LLPIN format
              value:
                error:
                  code: INVALID_CIN
                  message: The provided identifier failed format validation. Expected CIN, FCIN, or LLPIN.
            invalidIdType:
              summary: Unknown idType query value
              value:
                error:
                  code: INVALID_ID_TYPE
                  message: "Unknown idType. Valid values: cin, fcin, llpin."
    Unauthorized:
      description: API key missing, invalid, or revoked
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          examples:
            missing:
              summary: Header absent
              value:
                error:
                  code: MISSING_API_KEY
                  message: API key is required. Pass it via the x-api-key header.
            invalid:
              summary: Key not recognised
              value:
                error:
                  code: INVALID_API_KEY
                  message: The provided API key is not valid.
            revoked:
              summary: Key revoked
              value:
                error:
                  code: API_KEY_REVOKED
                  message: This API key has been revoked.
    InsufficientBalance:
      description: Wallet balance is below the endpoint price
      content:
        application/json:
          schema:
            allOf:
              - $ref: "#/components/schemas/Error"
              - type: object
                properties:
                  error:
                    type: object
                    properties:
                      balancePaisa:
                        type: integer
                      requiredPaisa:
                        type: integer
          example:
            error:
              code: INSUFFICIENT_BALANCE
              message: Insufficient wallet balance. Please recharge.
              balancePaisa: 0
              requiredPaisa: 500
    NoAccess:
      description: No pricing assigned, or test key tried to access non-sandbox CIN/DIN
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/NoAccessError"
          examples:
            noPricing:
              summary: No pricing record (live key)
              value:
                error:
                  code: NO_ACCESS
                  message: No access to endpoint 'companies.master'. Contact support to enable access.
                  endpoint: companies.master
                  catalogPricePaisa: 500
            sandboxOnly:
              summary: Test key outside sandbox whitelist
              value:
                error:
                  code: SANDBOX_ONLY
                  message: A test key only works on the sandbox companies and directors. They are listed in this response and at GET /v1/sandbox. Use one of those, or a live key.
                  sandboxCompanies:
                    - cin: U74999HR2015FTC056386
                      company: CARS24 SERVICES PRIVATE LIMITED
                      knownAs: Cars24
                    - cin: L93030DL2010PLC198141
                      company: ETERNAL LIMITED
                      knownAs: Eternal, formerly Zomato
                  sandboxDirectors:
                    - din: "11692472"
                      fullName: NILESH KASHINATH HIWALE
                  nextStep: GET /v1/sandbox
    Forbidden:
      description: |
        No pricing assigned for this endpoint (live key), or the account is disabled. A test key is
        never refused here: its search is scoped to the sandbox instead (see `sandbox` and `hint` in
        the response), so `SANDBOX_ONLY` does not occur.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/NoAccessError"
          examples:
            noPricing:
              summary: No pricing record (live key)
              value:
                error:
                  code: NO_ACCESS
                  message: No access to endpoint 'companies.resolve'. Contact support to enable access.
                  endpoint: companies.resolve
                  catalogPricePaisa: 500
            accountDisabled:
              summary: Customer account deactivated
              value:
                error:
                  code: ACCOUNT_DISABLED
                  message: Your account has been disabled. Contact support.
    NotFound:
      description: Resource not found in master data
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
    InvalidDin:
      description: DIN failed format validation (must be 8 numeric digits)
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            error:
              code: INVALID_DIN
              message: DIN must be 8 numeric digits.
    DirectorNotFound:
      description: No director found for the given DIN
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            error:
              code: DIRECTOR_NOT_FOUND
              message: No director found for DIN 12345678.
    BadRequest:
      description: Request validation failed (invalid identifier, malformed query param, etc.)
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
