> ## Documentation Index
> Fetch the complete documentation index at: https://docs.melso.ai/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Melso MCP: https://mcp.melso.ai/mcp (OAuth; one company per connection). Documentation-search MCP: https://docs.melso.ai/mcp; it does not act on customer accounts.
>
> Use the live tool list and respect company membership. Ask the person to approve the exact invoice or tax return version in the signed-in app. Melso's professionals prepare and file included returns after approval; an AI assistant does not file. A prepared file or an approval is not evidence of filing: only the AEAT's reference and receipt are. Keep credentials out of chat and logs.

# Read a fiscal document

> Reads an invoice or receipt (PDF or photo, up to 15 MB), stores it in Documents and records it as an expense or an income.

<Check>
  **Live capability.** Melso runs this in production today. Partner keys reach it once your partner account is enabled. See [Availability](/api/availability).
</Check>

`review` lists what to check. A document already recorded
answers `200` with `duplicate` `true` and the existing record.


## OpenAPI

````yaml api/openapi.yaml POST /companies/{company_id}/documents/extract
openapi: 3.1.0
info:
  title: Melso API
  version: 1.0.0-preview
  summary: Spanish tax and labour compliance for the companies on your platform.
  description: |
    The Melso API lets an HR, payroll, banking or ERP platform run the
    Spanish tax and labour obligations of the companies it serves: invoices,
    books, tax returns, Social Security and employment notifications.

    Melso calculates and prepares. A person at the company approves the exact
    version. Melso's professionals file under the company's representation, and
    the filing is proven only by the administration's reference and receipt.

    Every operation states its availability in `x-melso-status`:

    - `live`: Melso runs this capability in production today. Partner keys
      reach it once a partner account is enabled.
    - `preview`: a contract proposed to design partners. Not callable yet.

    `x-melso-actor: person` marks a decision a signed-in person makes in
    Melso; a partner sends them the approval link instead.
  contact:
    name: Melso partnerships
    email: hello@melso.ai
    url: https://docs.melso.ai/api/overview
servers:
  - url: https://api.melso.ai/v1
    description: API. Keys and access are issued per partner.
security:
  - partnerKey: []
tags:
  - name: Companies
    description: The companies you operate for, their fiscal profile and hosted onboarding.
  - name: Mandates
    description: >-
      The company's representation of Melso before the AEAT, the TGSS and the
      SEPE.
  - name: Webhook endpoints
    description: Where Melso sends signed events about your companies.
  - name: Invoicing setup
    description: Registration of the company as a VERI*FACTU or TicketBAI invoice issuer.
  - name: Customers
    description: The company's invoice customers.
  - name: Invoices
    description: >-
      Drafts, approval, legal registration, delivery and payment of the
      company's invoices.
  - name: Expenses
    description: >-
      Purchase invoices and receipts, with their AEAT category and
      deductibility.
  - name: Income
    description: Income invoiced outside Melso.
  - name: Documents
    description: >-
      Files the company gives Melso, and Melso's structured reading of an
      invoice or receipt.
  - name: Banking
    description: >-
      Bank accounts, movements, statements and reconciliation with invoices and
      expenses.
  - name: Accounting
    description: Journal, imported books and the company's financial statements.
  - name: Tax calendar
    description: The forms the company must file, with their periods and deadlines.
  - name: Tax returns
    description: The returns Melso calculates, their approval and their filing.
  - name: Corporate tax
    description: The year's corporate tax (modelo 200) and its instalments (modelo 202).
  - name: Tax estimates
    description: What the company should set aside for taxes, as of today.
  - name: Cases
    description: Gestiones, the procedures Melso's professionals carry out for the company.
  - name: Administration notifications
    description: Electronic notifications from the administration to the company.
  - name: Certificates
    description: >-
      Official certificates such as being up to date with the AEAT or the Social
      Security.
  - name: Employees
    description: The people the company employs.
  - name: Payroll runs
    description: >-
      The payroll results your platform calculated, for Melso to settle and
      declare.
  - name: Social Security
    description: >-
      Affiliation (altas, bajas and variaciones) and monthly contribution
      settlements.
  - name: Employment notifications
    description: >-
      Contrat@ contract notifications and Certific@2 company certificates to the
      SEPE.
paths:
  /companies/{company_id}/documents/extract:
    parameters:
      - $ref: '#/components/parameters/CompanyId'
    post:
      tags:
        - Documents
      summary: Read a fiscal document
      description: |
        Reads an invoice or a receipt (a PDF with text, or a JPEG, PNG, WebP or
        GIF photo, up to 15 MB), stores it in Documents and records it as an
        expense or an income, depending on whether the company received or
        issued it. `review` lists what to check. A document already recorded
        answers `200` with `duplicate` `true` and the existing record.
      operationId: extractFiscalDocument
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required:
                - file
              properties:
                file:
                  type: string
                  format: binary
                direction:
                  type: string
                  enum:
                    - income
                    - expense
                  description: >-
                    Leave it out to let Melso decide from the parties on the
                    document.
                counterparty_country:
                  type: string
                  description: ISO 3166-1 alpha-2, when the document does not show it.
      responses:
        '200':
          description: The document was already recorded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExtractionResult'
        '201':
          description: The new record.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExtractionResult'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/PaymentRequired'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          description: The file is larger than 15 MB.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '415':
          description: The file type is not supported.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: |
            The file is not a fiscal document, cannot be read, or its direction,
            VAT treatment or country cannot be decided. `code` says which:
            `not_fiscal`, `unreadable`, `pdf_without_text`, `direction_unknown`,
            `vat_treatment_unknown`, `country_unknown` or `exchange_rate`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
components:
  parameters:
    CompanyId:
      name: company_id
      in: path
      required: true
      description: >-
        The company's ID. Every company-scoped call works in exactly this
        company.
      schema:
        type: string
        format: uuid
  schemas:
    ExtractionResult:
      type: object
      required:
        - direction
        - record
        - review
        - duplicate
      properties:
        direction:
          type: string
          enum:
            - income
            - expense
        record:
          oneOf:
            - $ref: '#/components/schemas/Expense'
            - $ref: '#/components/schemas/Income'
        review:
          type: array
          description: What to check.
          items:
            type: string
            enum:
              - totals_mismatch
              - vat_rate_mismatch
              - missing_number
              - missing_counterparty_tax_id
              - converted_currency
              - ticket_vat_not_deductible
              - reverse_charge
              - possible_duplicate
              - recipient_tax_id_mismatch
        duplicate:
          type: boolean
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: string
          description: What went wrong, for a person to read.
        code:
          type: string
          description: >-
            A stable code to branch on, such as `invoicing_not_ready` or
            `version_conflict`.
        request_id:
          type: string
          description: Quote it when you contact Melso.
    Expense:
      allOf:
        - $ref: '#/components/schemas/ExpenseFields'
        - type: object
          required:
            - id
            - supplier_name
            - expense_date
            - subtotal
            - vat_amount
            - total
            - deductible
            - version
            - created_at
          properties:
            id:
              type: string
              format: uuid
            vat_lines:
              type: array
              items:
                $ref: '#/components/schemas/VatLine'
            items:
              type: array
              items:
                type: object
                properties:
                  description:
                    type: string
                  quantity:
                    $ref: '#/components/schemas/Decimal'
                  unit_price:
                    $ref: '#/components/schemas/Decimal'
                  amount:
                    $ref: '#/components/schemas/Amount'
            category_proposed:
              type: boolean
              description: The category is Melso's proposal, not yet confirmed.
            booked_account:
              type: string
              description: The account it is booked to.
            deductible_reason:
              type:
                - string
                - 'null'
            deductibility:
              type: object
              description: Melso's estimate of deductibility, and the rule it applied.
              properties:
                estimate:
                  type: string
                  enum:
                    - likely
                    - review
                    - unlikely
                rule:
                  type: string
            source:
              type: string
              enum:
                - upload
                - whatsapp
                - gmail
                - melso
                - import
            review_reasons:
              type: array
              items:
                type: string
            currency:
              type: string
            exchange_rate:
              type:
                - string
                - 'null'
            version:
              type: integer
            created_at:
              type: string
              format: date-time
            updated_at:
              type: string
              format: date-time
    Income:
      allOf:
        - $ref: '#/components/schemas/IncomeInput'
        - type: object
          required:
            - id
            - customer_name
            - issue_date
            - subtotal
            - vat_amount
            - total
            - version
            - created_at
          properties:
            id:
              type: string
              format: uuid
            subtotal:
              $ref: '#/components/schemas/Amount'
            vat_amount:
              $ref: '#/components/schemas/Amount'
            total:
              $ref: '#/components/schemas/Amount'
              description: '`subtotal` plus `vat_amount`; IRPF withheld is not subtracted.'
            currency:
              type: string
            exchange_rate:
              type:
                - string
                - 'null'
            source:
              type: string
              enum:
                - upload
                - whatsapp
                - gmail
                - import
            review_reasons:
              type: array
              items:
                type: string
            version:
              type: integer
            created_at:
              type: string
              format: date-time
            updated_at:
              type: string
              format: date-time
    ExpenseFields:
      type: object
      properties:
        supplier_name:
          type: string
          minLength: 1
          maxLength: 200
        supplier_tax_id:
          type: string
        supplier_country:
          type: string
          default: ES
        document_number:
          type: string
          maxLength: 100
        expense_date:
          type: string
          format: date
        operation_date:
          type: string
          format: date
        description:
          type: string
          maxLength: 500
        subtotal:
          $ref: '#/components/schemas/Amount'
        vat_rate:
          $ref: '#/components/schemas/Decimal'
        vat_amount:
          $ref: '#/components/schemas/Amount'
        total:
          $ref: '#/components/schemas/Amount'
        irpf_rate:
          $ref: '#/components/schemas/Decimal'
        irpf_amount:
          $ref: '#/components/schemas/Amount'
        vat_treatment:
          $ref: '#/components/schemas/VatTreatment'
        category:
          $ref: '#/components/schemas/ExpenseCategory'
        account:
          type: string
          description: >-
            The PGC account to book it to, such as `621` or `629`, for an
            empresa. It sets the category.
        deductible:
          $ref: '#/components/schemas/Deductible'
        file_id:
          type: string
          format: uuid
          description: The document in Documents that supports it.
    VatLine:
      type: object
      required:
        - rate
        - base
        - vat
      properties:
        rate:
          $ref: '#/components/schemas/Decimal'
        base:
          $ref: '#/components/schemas/Amount'
        vat:
          $ref: '#/components/schemas/Amount'
    Decimal:
      type: string
      pattern: ^-?[0-9]{1,10}(\.[0-9]{1,6})?$
      description: A decimal number as a string, such as `"21"` or `"0.5"`.
    Amount:
      type: string
      pattern: ^-?[0-9]+\.[0-9]{2}$
      description: >-
        An amount in euros as a decimal string with two decimals, such as
        `"121.00"`.
      examples:
        - '121.00'
    IncomeInput:
      type: object
      required:
        - customer_name
        - issue_date
        - vat_lines
      additionalProperties: false
      properties:
        customer_name:
          type: string
          maxLength: 200
        customer_tax_id:
          type: string
          maxLength: 32
        customer_country:
          type: string
          default: ES
        document_number:
          type: string
          maxLength: 100
          description: Unique in the company.
        issue_date:
          type: string
          format: date
        operation_date:
          type: string
          format: date
        description:
          type: string
          maxLength: 200
        vat_lines:
          type: array
          minItems: 1
          maxItems: 10
          items:
            $ref: '#/components/schemas/VatLine'
          description: >-
            One line per VAT rate. A line with a decimal rate is recargo de
            equivalencia.
        vat_treatment:
          type: string
          enum:
            - taxable
            - exempt
            - reverse_charge
            - not_subject
          default: taxable
        irpf_rate:
          $ref: '#/components/schemas/Decimal'
        irpf_amount:
          $ref: '#/components/schemas/Amount'
        file_id:
          type: string
          format: uuid
    VatTreatment:
      type: string
      enum:
        - taxable
        - exempt
        - reverse_charge
        - not_subject
        - import
      description: >-
        `reverse_charge` (inversión del sujeto pasivo) and `exempt` or
        `not_subject` carry no VAT amount.
    ExpenseCategory:
      type: string
      pattern: ^G[0-9]{2}$
      description: >-
        The AEAT expense concept, from `G01` to `G48`, such as `G03` (rent) or
        `G05` (company Social Security).
    Deductible:
      type: string
      enum:
        - 'yes'
        - 'no'
        - unsure
  responses:
    BadRequest:
      description: >-
        The request is malformed or a field is invalid. Unknown fields are
        refused.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: lines[0].vat_rate must be 21, 10, 4 or 0
            code: invalid_invoice
            request_id: req_01JAB3K9X2
    Unauthorized:
      description: The key is missing, revoked or invalid.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: invalid token
            code: unauthorized
            request_id: req_01JAB3K9X2
    PaymentRequired:
      description: >-
        The company's plan does not include this service, or its allowance is
        used up.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: The tax service is not active for this company
            code: tax_service_required
            request_id: req_01JAB3K9X2
    Conflict:
      description: >-
        The object changed or is not in a state that allows this. Read it again
        and decide.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: The tax return changed or is not ready for approval
            code: version_conflict
            request_id: req_01JAB3K9X2
  securitySchemes:
    partnerKey:
      type: http
      scheme: bearer
      description: |
        Your partner secret key, `mk_live_…` for real companies or `mk_test_…`
        for test companies. Keep it on your servers. See
        [Authentication](/api/authentication).

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.