# Tax and legal-entity data (/tax-and-entity-data)

See which tax filings are due, review past returns, download tax documents, and access information about businesses you represent, with your permission.



These APIs let an app show someone their tax records and information about
businesses they represent. The person signs in and chooses what to share. For a
business, they must select it and remain an active representative.

## What each tax information API does [#what-each-tax-information-api-does]

These four APIs help people keep track of VAT and annual income tax:

| API                                                              | What it does for the user                                                                                                                                                                                                                                                |
| ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| [Tax overview](/reference/oauth-get-tax-summary)                 | Shows which filing periods are open or overdue and their due dates. It also shows whether a recorded lump-sum tax election is active or expired. This is an overview of filing obligations, not a payment balance or proof that every tax obligation has been satisfied. |
| [Filing history](/reference/oauth-list-tax-filings)              | Lists returns already submitted, with the option to narrow the list by tax type and year. It helps someone find a past VAT or annual income-tax filing; unfinished drafts are not included.                                                                              |
| [Filing details](/reference/oauth-get-tax-filing)                | Opens one submitted return to show its reported figures, calculated tax, submission date, linked invoice status, and available documents.                                                                                                                                |
| [Download tax documents](/reference/oauth-download-tax-document) | Downloads an assessment PDF showing the tax calculation for a filed return. Annual income-tax filings also support a separate filed-return PDF; VAT currently provides the assessment PDF.                                                                               |

These APIs only show or download information. They do not prepare, change, submit,
or pay a return. To prepare and submit an original quarterly VAT filing in US
dollars, use the [Quarterly VAT APIs](/quarterly-vat#what-each-vat-api-does), with
the person's separate permission to file.

## Available data [#available-data]

An authorized user can:

* view personal tax obligations, including open and overdue periods;
* list submitted income-tax and VAT filings;
* inspect a submitted filing and its calculated amounts;
* download an assessment or filed-return PDF;
* list legal entities selected during OAuth consent;
* read a selected entity's profile and available documents; and
* perform the same read-only tax operations for a selected entity they still
  actively represent.

Tax summaries are calculated without creating draft statements. Use the
[HTTP walkthrough](#prepare-your-first-request) to connect your own app, or the
[official CLI](#use-the-official-cli) to explore records from the terminal.

## Access boundaries [#access-boundaries]

| Data                     | Required scope                    | Additional checks                                                                                 |
| ------------------------ | --------------------------------- | ------------------------------------------------------------------------------------------------- |
| Personal tax records     | `eprospera:person.tax.read`       | The OAuth user can only request their own records.                                                |
| Legal-entity profile     | `eprospera:entity.read`           | The entity must be selected during consent.                                                       |
| Legal-entity documents   | `eprospera:entity.documents.read` | The entity must be selected during consent.                                                       |
| Personal documents       | `eprospera:person.documents.read` | The OAuth user can only request their own documents, such as the signed Agreement of Coexistence. |
| Legal-entity tax records | `eprospera:entity.tax.read`       | The entity must be selected during consent and the user must remain an active representative.     |

Selecting an entity during consent does not permanently bypass membership
checks. If the user stops representing that entity, subsequent requests stop
returning its data.

## Direct API endpoints [#direct-api-endpoints]

| Operation               | Endpoint                                                                                               |
| ----------------------- | ------------------------------------------------------------------------------------------------------ |
| List consented entities | [GET `/api/v1/me/legal-entities`](/reference/oauth-get-legal-entities)                                 |
| Read one entity         | [GET `/api/v1/me/legal-entities/{id}`](/reference/oauth-get-legal-entities-id)                         |
| Read entity documents   | [GET `/api/v1/me/legal-entities/{id}/documents`](/reference/oauth-get-legal-entities-id-documents)     |
| Read personal documents | [GET `/api/v1/me/natural-person/documents`](/reference/oauth-get-natural-person-documents)             |
| Read tax status         | [GET `/api/v1/me/tax/summary`](/reference/oauth-get-tax-summary)                                       |
| List submitted filings  | [GET `/api/v1/me/tax/filings`](/reference/oauth-list-tax-filings)                                      |
| Read one filing         | [GET `/api/v1/me/tax/filings/{filingId}`](/reference/oauth-get-tax-filing)                             |
| Download a tax document | [GET `/api/v1/me/tax/filings/{filingId}/documents/{document}`](/reference/oauth-download-tax-document) |

Use `subject=personal` for the authenticated user's records or
`subject=<entity-uuid>` for a consented legal entity. Tax endpoints accept the
matching personal or entity tax scope; having one does not grant the other.

## Prepare your first request [#prepare-your-first-request]

Request an OAuth client through the [Help Center chat](https://help.eprospera.com/en/chat)
and complete the [OAuth authorization flow](/oauth-overview#authorization-flow).
Ask for personal tax read permission, business tax read permission, or both,
according to what your app needs. For example, an app that reads selected
businesses' tax records can request:

```text
openid profile eprospera:entity.tax.read
```

Use the resulting **access token**, not an ID token, Standard Key, Agent Key, or
Partner Key. The person must select the businesses during consent. An entity-read
permission alone does not grant access to its taxes.

These terminal examples use `curl` with `--fail-with-body` support and `jq`. Start
with a separately provisioned [staging client](/testing-in-staging#oauth-in-staging):

```bash
export BASE_URL='https://stg.portal.eprospera.com'
export ACCESS_TOKEN='<your-staging-oauth-access-token>'
TAX_SUBJECT='personal'
```

To read business records instead, set `TAX_SUBJECT` to a business UUID returned
by the tax overview or the [consented entity list](/reference/oauth-get-legal-entities).
The latter also requires `eprospera:entity.read`. Do not send an RPN as `subject`.
Omitting `subject` from summary/list requests returns data for all taxpayers
accessible through the token. Sending `subject=` with no value is invalid.

All IDs, names, dates, and amounts shown below are fictional. Replace them with
your own authorized values. Stop on HTTP errors before using the response;
`--fail-with-body` returns a nonzero exit code and keeps the error body visible.

## Read the tax overview [#read-the-tax-overview]

Call [tax summary](/reference/oauth-get-tax-summary) to show outstanding filing
periods without creating a draft:

```bash
TAX_SUMMARY="$(curl --fail-with-body -sS -G \
  "$BASE_URL/api/v1/me/tax/summary" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  --data-urlencode "subject=$TAX_SUBJECT")"
printf '%s\n' "$TAX_SUMMARY" | jq .
```

Example response for a person with an open VAT quarter:

```json
{
  "data": {
    "subjects": [
      {
        "subject": {
          "type": "natural_person",
          "id": "33333333-3333-4333-8333-333333333333",
          "name": "Alex Example",
          "residentPermitNumber": "90123456789012"
        },
        "obligations": [
          {
            "type": "vat",
            "period": { "year": 2026, "quarter": 3 },
            "dueDate": "2026-10-15T23:59:59.999Z",
            "status": "open",
            "filingId": null
          }
        ],
        "lumpSumElections": []
      }
    ]
  }
}
```

* Show the returned taxpayer name, type of tax, period, due date, and `open` or
  `overdue` status. Use the returned due date rather than calculating a deadline
  in your app; reviewed requirements can affect the result.
* VAT uses quarter numbers 1–4. Annual income-tax periods use `quarter: null`.
* An obligation's `filingId` may refer to an unfinished draft. The submitted-filing
  endpoint below will not return drafts. Use the separately authorized
  [VAT draft workflow](/quarterly-vat) to work on an original USD VAT draft.
* `lumpSumElections` shows recorded personal lump-sum tax elections. It is an empty
  list for businesses and when no personal election is recorded.
* An empty obligations list means no open/overdue periods were returned. It is
  not a payment statement or a Certificate of Good Standing.

If you omit `subject` and get `subjects: []`, check which permissions and businesses
were granted. An explicitly requested taxpayer outside that access returns `404`.

## List submitted filings [#list-submitted-filings]

Call [filing history](/reference/oauth-list-tax-filings) to find completed returns:

```bash
TAX_TYPE='vat'
TAX_YEAR=2026
TAX_FILINGS="$(curl --fail-with-body -sS -G \
  "$BASE_URL/api/v1/me/tax/filings" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  --data-urlencode "subject=$TAX_SUBJECT" \
  --data-urlencode "type=$TAX_TYPE" \
  --data-urlencode "year=$TAX_YEAR" \
  --data-urlencode 'limit=20')"
printf '%s\n' "$TAX_FILINGS" | jq '{
  filings: [.data[] | {id, type, period, submittedAt}],
  nextCursor: .meta.nextCursor
}'
```

Example output from the `jq` selection after a return has been submitted:

```json
{
  "filings": [
    {
      "id": "44444444-4444-4444-8444-444444444444",
      "type": "vat",
      "period": { "year": 2026, "quarter": 3 },
      "submittedAt": "2026-10-04T18:00:00.000Z"
    }
  ],
  "nextCursor": null
}
```

The HTTP response contains full filing objects in `data`, with the same fields
as [filing details](#read-a-submitted-filing), plus `meta.nextCursor`. The example
above only prints the fields needed for a history list. Results are newest first
by submission date, with ID as the tie-breaker.

| Query parameter | How to use it                                                                      |
| --------------- | ---------------------------------------------------------------------------------- |
| `subject`       | `personal` or a consented business UUID. Omit to include all accessible taxpayers. |
| `type`          | `vat` or `income`. Omit to include both.                                           |
| `year`          | The filing's tax year, from 2020 through 9999. Omit for all years.                 |
| `limit`         | Page size, 1–100; defaults to 50.                                                  |
| `cursor`        | The previous response's `meta.nextCursor`. Omit on the first request.              |

An empty `data` array with `nextCursor: null` means no submitted filings matched
these filters. It does not tell you whether a draft exists or a return is due.

### Fetch the next page [#fetch-the-next-page]

If `meta.nextCursor` is non-null, pass it back unchanged and URL-encoded. Keep
the same subject, tax type, and year while paging. Do not decode it or use a
filing ID as the cursor.

```bash
NEXT_CURSOR="$(printf '%s' "$TAX_FILINGS" | jq -r '.meta.nextCursor // empty')"
if [ -n "$NEXT_CURSOR" ]; then
  TAX_FILINGS="$(curl --fail-with-body -sS -G \
    "$BASE_URL/api/v1/me/tax/filings" \
    -H "Authorization: Bearer $ACCESS_TOKEN" \
    --data-urlencode "subject=$TAX_SUBJECT" \
    --data-urlencode "type=$TAX_TYPE" \
    --data-urlencode "year=$TAX_YEAR" \
    --data-urlencode 'limit=20' \
    --data-urlencode "cursor=$NEXT_CURSOR")"
  printf '%s\n' "$TAX_FILINGS" | jq .
fi
```

Repeat for each returned cursor until it is null. A malformed cursor returns
`400` with `error: "invalid_cursor"`; restart from the first page if necessary.

## Read a submitted filing [#read-a-submitted-filing]

Let the person select an ID from filing history, then call
[filing details](/reference/oauth-get-tax-filing). Replace the fictional ID here
with that selection:

```bash
FILING_ID='44444444-4444-4444-8444-444444444444'
TAX_FILING="$(curl --fail-with-body -sS \
  "$BASE_URL/api/v1/me/tax/filings/$FILING_ID" \
  -H "Authorization: Bearer $ACCESS_TOKEN")"
printf '%s\n' "$TAX_FILING" | jq .
```

Example **200** response for a submitted personal VAT return:

```json
{
  "data": {
    "id": "44444444-4444-4444-8444-444444444444",
    "subject": {
      "type": "natural_person",
      "id": "33333333-3333-4333-8333-333333333333",
      "name": "Alex Example",
      "residentPermitNumber": "90123456789012"
    },
    "type": "vat",
    "period": { "year": 2026, "quarter": 3 },
    "submittedAt": "2026-10-04T18:00:00.000Z",
    "amended": false,
    "currency": "usd",
    "accountingMethod": "cash",
    "reported": { "grossIncome": "0", "taxCredit": "0", "taxWithheld": "0" },
    "usdEquivalent": null,
    "taxLiability": "0",
    "exchangeRate": null,
    "invoice": null,
    "availableDocuments": ["assessment"]
  }
}
```

`reported` holds the original figures in the filing's `currency`. Amounts are
decimal strings, and a `null` figure means unavailable, not zero. BTC filings can
also include frozen USD equivalents and exchange-rate information; do not replace
those with today's rate. `taxLiability` is the USD liability recorded for the
filing. `amended` identifies an amended return.

The linked `invoice` can be null, as in a zero-tax filing. Its status is useful
context, but do not infer the user's total outstanding balance from this one
return. This endpoint reads submitted returns only; use the VAT draft endpoint
to track a filing still being prepared.

## Download a tax document [#download-a-tax-document]

Read `availableDocuments` on the selected filing and call
[download tax document](/reference/oauth-download-tax-document):

| Document     | Available for                               | Result                       |
| ------------ | ------------------------------------------- | ---------------------------- |
| `assessment` | Submitted VAT and annual income-tax filings | Notice of assessment PDF.    |
| `return`     | Submitted annual income-tax filings         | Filed income-tax return PDF. |

Download an assessment into a new private directory:

```bash
umask 077
DOWNLOAD_DIR="$(mktemp -d)"
curl --fail -sS \
  "$BASE_URL/api/v1/me/tax/filings/$FILING_ID/documents/assessment" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -o "$DOWNLOAD_DIR/assessment.pdf"
```

A successful response is binary PDF data with `Content-Type: application/pdf`
and a download filename in `Content-Disposition`. It has no JSON `data` wrapper.
The example uses `--fail` so an HTTP error body is not saved as a PDF. Check the
exit status before opening the file, and keep downloaded tax documents private.

For a filing whose `availableDocuments` contains `return`, use the same command
with `/documents/return` and a suitable output filename. VAT has no separate
filed-return PDF through this endpoint.

Assessments accept `?history_years=1` through `5`, defaulting to `1`, to control
the included assessment history. Do not send `history_years` for a `return`
download; that combination returns `400`.

## Handle missing records and errors [#handle-missing-records-and-errors]

Check HTTP status first. Error responses contain `error`; a `data` field is not
present on every response. For example, an inaccessible submitted filing returns
**404**:

```json
{ "error": "Tax filing not found" }
```

| Result                        | What your app should do                                                                                                                       |
| ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `200` with an empty list      | Show an empty state for the selected filters. Check the overview separately for outstanding periods.                                          |
| `400`                         | Fix the subject, filters, cursor, or document request. A VAT `return` download is unsupported.                                                |
| `401`                         | Refresh an expired access token when supported, or reconnect if it was revoked or refresh fails.                                              |
| `403`                         | Request the matching tax read permission and obtain consent. Entity profile access or VAT write access alone is insufficient.                 |
| `404`                         | Check the ID, environment, consented business, and current representative role. A draft cannot be read through the submitted-filing endpoint. |
| `500` or a connection failure | Retry these read requests with backoff. Do not treat missing results as proof of a missing filing or no tax due.                              |

For [VAT write errors and recovery](/quarterly-vat#recover-from-errors-and-interrupted-requests),
follow the separate filing guide; interrupted writes need different handling.

## Use the official CLI [#use-the-official-cli]

Install the [official CLI](/cli#install), then authorize it in a browser. For
staging, select the environment before login and keep it selected for later commands:

```bash
export EPROSPERA_ENV=staging
export EPROSPERA_BASE_URL=https://stg.portal.eprospera.com
eprospera auth login --oauth
```

The browser consent page shows the requested permissions and lets the user
select which legal entities to share. The CLI client has no embedded secret and
requests only the published user and entity scopes.

List the entities available to the CLI, then replace the sample `ENTITY_ID`
with a returned business ID before reading its details:

```bash
eprospera --json me legal-entities list
ENTITY_ID='11111111-1111-4111-8111-111111111111'
eprospera --json me legal-entities get "$ENTITY_ID"
eprospera --json me legal-entities documents "$ENTITY_ID"
```

Read personal tax information, selecting a returned `FILING_ID` before reading
or downloading that filing:

```bash
eprospera --json tax status --subject personal
eprospera --json tax list --subject personal --type income --year 2025
FILING_ID='44444444-4444-4444-8444-444444444444'
eprospera --json tax get "$FILING_ID"
eprospera tax download "$FILING_ID" --document assessment
```

For entity taxes, use a consented business UUID and select a filing from that
business's returned list before downloading:

```bash
eprospera --json tax status --subject "$ENTITY_ID"
eprospera --json tax list --subject "$ENTITY_ID" --type vat --year 2026
FILING_ID='22222222-2222-4222-8222-222222222222'
eprospera tax download "$FILING_ID" --document assessment --output vat-assessment.pdf
```

`tax list` returns an opaque cursor for the next page when more filings are
available. Pass that value back with `--cursor`; do not parse or modify it.

## Entity detail fields [#entity-detail-fields]

Both [API-key entity detail reads](/reference/get-legal-entities-id) and
[consented OAuth entity detail reads](/reference/oauth-get-legal-entities-id)
return the same additional profile fields. OAuth still requires consent and
current representation; API keys still require representation, with Agent Keys
limited to API-incorporated entities.

| Field                     | Meaning                                                                                                               |
| ------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| `id`                      | Legal-entity UUID used in API paths.                                                                                  |
| `residentPermitNumber`    | 14-digit RPN, or `null` when unavailable.                                                                             |
| `jurisdictionOfFormation` | Recorded jurisdiction of formation.                                                                                   |
| `formationDate`           | Recorded formation date, formatted `YYYY-MM-DD`.                                                                      |
| `status`                  | `dissolved` when dissolution has taken effect; otherwise `active` if residency is current, or `inactive`.             |
| `residencyStatus`         | Whether a residency term is currently active.                                                                         |
| `residency`               | Current residency type, effective date, and termination date, or `null`.                                              |
| `registeredAgent`         | Current agent's provider ID, name, attention line, website, RPN, term dates, and office/mailing addresses, or `null`. |
| `registeredAddress`       | Current registered agent's office address, or `null`.                                                                 |
| `principalOfficeAddress`  | The entity's separately recorded principal-office address.                                                            |

Only terms that have started and have not terminated are returned. Missing
registered-office data is not replaced with a principal-office or mailing
address. The detail fields are additive; the entity-list endpoint remains a
summary. This API does not expose a separate `registrationNumber`; use the UUID
and RPN as distinct identifiers.

An `active` status is a residency/registration indicator, not proof of tax
compliance or good standing. Use the
[Certificate of Good Standing workflow](/entity-filings#request-a-certificate-of-good-standing)
when a certificate is required.

## Handling sensitive information [#handling-sensitive-information]

Tax amounts, filing history, assessments, returns, and some legal-entity
documents are confidential financial information.

* Do not place access tokens, refresh tokens, tax responses, or downloaded
  documents in source control or shared logs.
* Treat JSON and human-readable terminal output as sensitive; shell redirection
  and CI log capture can persist it.
* Tax PDFs are created with owner-only file permissions on supported platforms.
  The CLI refuses to overwrite an existing file unless `--yes` is supplied.
* Use `eprospera --yes auth logout` to revoke the remote OAuth session and
  remove the locally stored credential.

The CLI stores credentials in the operating-system keychain when available. Its
protected local-file fallback is intended for a single-user workstation, not a
shared machine.
