> ## 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.

# Use cases

> Everyday requests mapped to the Melso MCP tool calls that answer them, with arguments that validate against the tool schemas.

Each use case below starts from something a person asks and lists the calls an
assistant makes, in order. Every example is a `tools/call` request body. The
identifiers are placeholders: use the ones your own company returns.

The connection lists only the tools and actions the company's plan, your role
and the setup state allow. If a call here is not listed for you, read
[Permissions and approvals](/mcp/permissions).

| The person asks | Tools |
| - | - |
| [Which company is this?](#confirm-the-company) | `company` |
| [Record this receipt](#record-a-receipt-or-a-supplier-invoice) | `expenses` |
| [Add this expense by hand](#record-an-expense-from-its-details) | `expenses` |
| [Draft an invoice for a customer](#draft-an-invoice-for-a-customer) | `customers`, `invoices` |
| [Correct an issued invoice](#correct-an-issued-invoice) | `invoices` |
| [This invoice was paid](#record-a-payment-received) | `payments` |
| [What is due this quarter?](#what-is-due-this-quarter) | `taxes` |
| [Explain my 303 and let me approve it](#explain-a-return-and-hand-over-its-approval) | `taxes` |
| [Give me the AEAT import file](#get-the-aeat-import-file) | `taxes` |
| [Fix this record](#fix-a-record) | `expenses`, `income` |
| [Delete this duplicate](#delete-a-duplicate) | `expenses`, `income` |
| [Finish my fiscal profile](#complete-the-fiscal-profile) | `fiscal_profile` |
| [Report a problem](#report-a-problem-or-ask-for-work) | `support_tickets_create` |

## Confirm the company

Do this first on every new connection. [`company`](/mcp/tools/company) takes no
arguments and changes nothing.

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "company",
    "arguments": {}
  }
}
```

Check `name` and `kind` with the person. `kind` is `freelancer` for an autónomo
and `company` for an empresa. If the answer has `onboarding`, the autónomo's
setup is pending: go to [Complete the fiscal profile](#complete-the-fiscal-profile).

## Record a receipt or a supplier invoice

Use [`expenses`](/mcp/tools/expenses). The file goes to Melso in three calls.

<Steps>
  <Step title="Ask for an upload link">
    Send the file's name, type and exact size. Accepted types are PDF, JPEG, PNG,
    WebP and GIF, up to 15 MB.

    ```json theme={null}
    {
      "jsonrpc": "2.0",
      "id": 2,
      "method": "tools/call",
      "params": {
        "name": "expenses",
        "arguments": {
          "action": "upload",
          "filename": "factura-0142.pdf",
          "content_type": "application/pdf",
          "size_bytes": 48211
        }
      }
    }
    ```

    The answer has `upload_id`, `upload_url`, the `headers` to send, `expires_at`
    and `web_upload_url`.
  </Step>

  <Step title="PUT the file to upload_url">
    Send the file's exact bytes with the returned headers. This request goes to the
    upload link, not to the MCP endpoint.
  </Step>

  <Step title="Read the uploaded file">
    ```json theme={null}
    {
      "jsonrpc": "2.0",
      "id": 3,
      "method": "tools/call",
      "params": {
        "name": "expenses",
        "arguments": {
          "action": "from_document",
          "upload_id": "11111111-1111-4111-8111-111111111111"
        }
      }
    }
    ```

    The file is read in the background. The answer has a `job_id`.
  </Step>

  <Step title="See what it became">
    ```json theme={null}
    {
      "jsonrpc": "2.0",
      "id": 4,
      "method": "tools/call",
      "params": {
        "name": "expenses",
        "arguments": {
          "action": "document_status",
          "job_id": "11111111-1111-4111-8111-111111111111"
        }
      }
    }
    ```

    The job is `queued`, `done` or `failed`. A finished job names the expense or
    the income it recorded, with its review codes.
  </Step>
</Steps>

Two failures ask you for something:

* `direction_unknown`: ask whether the company issued or received the
  document, then queue it again with `direction`.
* `country_unknown`: ask the supplier's or customer's country, then retry with
  `counterparty_country` and the direction the error names.

Queue the document again with what the person answered:

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 5,
  "method": "tools/call",
  "params": {
    "name": "expenses",
    "arguments": {
      "action": "from_document",
      "upload_id": "11111111-1111-4111-8111-111111111111",
      "direction": "expense",
      "counterparty_country": "FR"
    }
  }
}
```

<Note>
  A client that cannot upload files shares `web_upload_url` instead. The person
  drops the files in the web app.
</Note>

## Record an expense from its details

When there is no file, record the expense by hand. `total` must equal
`subtotal` plus `vat_amount`. Leave `category` out when you are not sure.

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 6,
  "method": "tools/call",
  "params": {
    "name": "expenses",
    "arguments": {
      "action": "create",
      "supplier_name": "Example Telecom SA",
      "supplier_tax_id": "A00000000",
      "date": "2026-10-02",
      "subtotal": "40.00",
      "vat_amount": "8.40",
      "vat_rate": "21",
      "total": "48.40",
      "category": "G17"
    }
  }
}
```

`deductible` defaults to `unsure`. It is a classification, not approval of a
deduction.

## Draft an invoice for a customer

Use [`customers`](/mcp/tools/customers) and [`invoices`](/mcp/tools/invoices).

<Steps>
  <Step title="Find the customer">
    ```json theme={null}
    {
      "jsonrpc": "2.0",
      "id": 7,
      "method": "tools/call",
      "params": {
        "name": "customers",
        "arguments": {
          "action": "list"
        }
      }
    }
    ```
  </Step>

  <Step title="Save the customer if they are new">
    ```json theme={null}
    {
      "jsonrpc": "2.0",
      "id": 8,
      "method": "tools/call",
      "params": {
        "name": "customers",
        "arguments": {
          "action": "upsert",
          "name": "Example Studio SL",
          "kind": "company",
          "tax_id": "B00000000",
          "address": "Calle Mayor 1, 28013 Madrid",
          "email": "billing@example.com"
        }
      }
    }
    ```
  </Step>

  <Step title="Save an unnumbered draft">
    ```json theme={null}
    {
      "jsonrpc": "2.0",
      "id": 9,
      "method": "tools/call",
      "params": {
        "name": "invoices",
        "arguments": {
          "action": "prepare",
          "customer_id": "11111111-1111-4111-8111-111111111111",
          "issue_date": "2026-10-13",
          "due_date": "2026-11-12",
          "irpf_rate": "15",
          "lines": [
            {
              "description": "Design work, October",
              "quantity": "1",
              "unit_price": "1200.00",
              "vat_rate": "21"
            }
          ]
        }
      }
    }
    ```

    `irpf_rate` is for an autónomo who is a professional. A company's invoice
    leaves it out. To change the draft before it is approved, call `prepare` again
    with the draft's `id`.
  </Step>

  <Step title="Get the approval page">
    ```json theme={null}
    {
      "jsonrpc": "2.0",
      "id": 10,
      "method": "tools/call",
      "params": {
        "name": "invoices",
        "arguments": {
          "action": "request_approval",
          "id": "11111111-1111-4111-8111-111111111111"
        }
      }
    }
    ```

    Show the person the preview and the `approval_url`. The owner issues the
    invoice there, in the signed-in web app.
  </Step>

  <Step title="Check the outcome later">
    ```json theme={null}
    {
      "jsonrpc": "2.0",
      "id": 11,
      "method": "tools/call",
      "params": {
        "name": "invoices",
        "arguments": {
          "action": "get",
          "id": "11111111-1111-4111-8111-111111111111"
        }
      }
    }
    ```
  </Step>
</Steps>

<Warning>
  `request_approval` returns a link, not an outcome. Do not say the invoice was
  issued until `get` shows it.
</Warning>

## Correct an issued invoice

An issued invoice cannot be edited. `correct` prepares a corrective draft with
the differences. A negative `unit_price` credits the original and a positive
one increases it. The reason goes in `notes`, and it is required.

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 12,
  "method": "tools/call",
  "params": {
    "name": "invoices",
    "arguments": {
      "action": "correct",
      "invoice_id": "11111111-1111-4111-8111-111111111111",
      "customer_id": "11111111-1111-4111-8111-111111111111",
      "issue_date": "2026-10-13",
      "notes": "Full credit: the work was cancelled.",
      "lines": [
        {
          "description": "Design work, October",
          "quantity": "1",
          "unit_price": "-1200.00",
          "vat_rate": "21"
        }
      ]
    }
  }
}
```

The original stays as it was. Then call `request_approval` with the corrective
draft's `id`, as in the previous use case.

## Record a payment received

Use [`payments`](/mcp/tools/payments) once the person confirms the invoice was
paid in full.

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 13,
  "method": "tools/call",
  "params": {
    "name": "payments",
    "arguments": {
      "action": "mark_paid",
      "id": "11111111-1111-4111-8111-111111111111"
    }
  }
}
```

It records money already received. It does not charge a card or move money, and
there is no partial payment.

## What is due this quarter

Use [`taxes`](/mcp/tools/taxes).

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 14,
  "method": "tools/call",
  "params": {
    "name": "taxes",
    "arguments": {
      "action": "calendar"
    }
  }
}
```

Each entry has the form, its deadline, the last day to pay by direct debit and
its status. It also says whether Melso calculates the form or only reminds. A
calculated form carries a `filing_id` that links its return.

For the amounts, list the calculated returns:

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 15,
  "method": "tools/call",
  "params": {
    "name": "taxes",
    "arguments": {
      "action": "list"
    }
  }
}
```

A return with `is_preview` true is an estimate. It has an amount only: no
boxes, no approval page and no import file.

## Explain a return and hand over its approval

<Steps>
  <Step title="Read the return">
    ```json theme={null}
    {
      "jsonrpc": "2.0",
      "id": 16,
      "method": "tools/call",
      "params": {
        "name": "taxes",
        "arguments": {
          "action": "get",
          "id": "11111111-1111-4111-8111-111111111111"
        }
      }
    }
    ```

    Use the boxes and the review codes to explain each figure.
  </Step>

  <Step title="Get the approval page">
    ```json theme={null}
    {
      "jsonrpc": "2.0",
      "id": 17,
      "method": "tools/call",
      "params": {
        "name": "taxes",
        "arguments": {
          "action": "request_approval",
          "id": "11111111-1111-4111-8111-111111111111"
        }
      }
    }
    ```

    Share `approval_url`. The owner authorizes the exact version there. For an
    estimate the answer has `approval_required` false and no link: say it is an
    estimate.
  </Step>
</Steps>

Authorization opens a case for Melso's professionals to review and file. Do
not tell the person the return is filed until its status is `filed`. Do not ask
them to file an included return themselves.

## Get the AEAT import file

Available for the 303, 130, 111 and 115.

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 18,
  "method": "tools/call",
  "params": {
    "name": "taxes",
    "arguments": {
      "action": "aeat_file",
      "id": "11111111-1111-4111-8111-111111111111"
    }
  }
}
```

The answer has `filename`, `content_type` and `content_base64`. Write the
decoded bytes unchanged. The file is preparation: it is not an authorization
and not proof of filing.

## Fix a record

Read the record first. `update` needs the `expected_version` you read, and a
stale version is refused.

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 19,
  "method": "tools/call",
  "params": {
    "name": "expenses",
    "arguments": {
      "action": "get",
      "id": "11111111-1111-4111-8111-111111111111"
    }
  }
}
```

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 20,
  "method": "tools/call",
  "params": {
    "name": "expenses",
    "arguments": {
      "action": "update",
      "id": "11111111-1111-4111-8111-111111111111",
      "expected_version": 3,
      "supplier_tax_id": "A00000000",
      "deductible": "yes"
    }
  }
}
```

Income works the same way. `vat_lines` replaces every current line.

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 21,
  "method": "tools/call",
  "params": {
    "name": "income",
    "arguments": {
      "action": "update",
      "id": "11111111-1111-4111-8111-111111111111",
      "expected_version": 1,
      "customer_name": "Example Studio SL",
      "vat_lines": [
        {
          "rate": "21",
          "base": "1000.00",
          "vat": "210.00"
        }
      ]
    }
  }
}
```

Updating an expense clears its review codes and resets any earlier review.

## Delete a duplicate

Ask the person to confirm first.

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 22,
  "method": "tools/call",
  "params": {
    "name": "expenses",
    "arguments": {
      "action": "delete",
      "id": "11111111-1111-4111-8111-111111111111"
    }
  }
}
```

The file stays in the company's documents. The call is refused when a tax
return that covers the record's date was already approved or filed. Do not
retry it: the person makes that correction in the web app.

## Complete the fiscal profile

Use [`fiscal_profile`](/mcp/tools/fiscal-profile). It is listed for the
company's owner or an admin, and it is the tool to use while an autónomo's
setup is pending.

<Steps>
  <Step title="See what is missing">
    ```json theme={null}
    {
      "jsonrpc": "2.0",
      "id": 23,
      "method": "tools/call",
      "params": {
        "name": "fiscal_profile",
        "arguments": {
          "action": "get"
        }
      }
    }
    ```
  </Step>

  <Step title="Save whether they are already registered">
    ```json theme={null}
    {
      "jsonrpc": "2.0",
      "id": 24,
      "method": "tools/call",
      "params": {
        "name": "fiscal_profile",
        "arguments": {
          "action": "set_registration",
          "registered": true
        }
      }
    }
    ```
  </Step>

  <Step title="Save what the person told you">
    Send every field from one message in one call.

    ```json theme={null}
    {
      "jsonrpc": "2.0",
      "id": 25,
      "method": "tools/call",
      "params": {
        "name": "fiscal_profile",
        "arguments": {
          "action": "update",
          "tax_id": "00000000T",
          "legal_name": "Example Person",
          "address": "Calle Mayor 1, 2B",
          "postal_code": "28013",
          "city": "Madrid",
          "activity_type": "profesional",
          "activity_description": "Graphic design for companies",
          "registered_on": "2024-03",
          "client_types": [
            "es_business"
          ]
        }
      }
    }
    ```

    The answer is the whole profile. `errors` names each refused field, `deduced`
    names what the server filled in so you can confirm it, and `missing` lists
    what is still required. Valid fields are saved even when another is refused.
  </Step>
</Steps>

An empresa has no guided setup, and the Melso team sets it up. An autónomo can
also finish in the web app's guided setup. See
[Setup and fiscal profile](/essentials/setup).

## Report a problem or ask for work

Use [`support_tickets_create`](/mcp/tools/support-tickets-create).

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 26,
  "method": "tools/call",
  "params": {
    "name": "support_tickets_create",
    "arguments": {
      "title": "The 303 draft misses an invoice",
      "type": "bug",
      "severity": "degraded",
      "description": "The third quarter 303 does not count an invoice issued on 28 September."
    }
  }
}
```

For work that no tool covers, such as a registration, use `service_request` and
put every detail in the description.

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 27,
  "method": "tools/call",
  "params": {
    "name": "support_tickets_create",
    "arguments": {
      "title": "Change the activity on my registration",
      "type": "service_request",
      "description": "I added a second activity in September and need it reflected in my registration."
    }
  }
}
```

Filing the same title again within 24 hours returns the ticket already filed.
Never put passwords, certificates or other secrets in a ticket.


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