Factuarea API
MCP server

Connecting a client

Connect Claude Code, Claude Desktop, the MCP Inspector or any MCP client to the Factuarea MCP server — with OAuth 2.1 or an API key, and in test mode.

The Factuarea MCP server speaks Streamable HTTP at https://mcp.factuarea.com. Any MCP-compatible client can connect using one of the two supported credentials:

  • OAuth 2.1 — the client registers itself and the user authorizes it through a consent screen. Best for end-user tools.
  • API key — you pass a fact_live_ / fact_test_ Bearer token directly. Best for your own automations.

The canonical endpoint is the subdomain root, https://mcp.factuarea.com. The previous path form https://mcp.factuarea.com/mcp keeps working as a compatibility alias.

Using Claude Code? The recommended setup is the official factuarea-mcp plugin — two commands and you're connected over OAuth, with a guidance skill bundled in. See the dedicated Claude Code plugin guide. The manual steps below are for other clients or headless setups.

Claude Code

The smoothest path is the Claude Code plugin — it registers the server and bundles a guidance skill in one install. If you prefer to wire the server by hand, Claude Code also supports remote MCP servers over HTTP with built-in OAuth.

With OAuth

Add the server

claude mcp add --transport http factuarea https://mcp.factuarea.com

Authenticate

Inside Claude Code, run the slash command:

/mcp

Pick factuarea, choose Authenticate, and your browser opens the consent screen. Select the company to grant access to, the environment (live or test) and the scopes you want to allow, then confirm. Claude Code stores the resulting token and refreshes it automatically.

Use it

Ask Claude to do something — "list my overdue invoices in test mode" — and it discovers and calls the matching tools.

With an API key

If you'd rather use your own key (no consent flow), pass it as an Authorization header:

claude mcp add --transport http factuarea https://mcp.factuarea.com \
  --header "Authorization: Bearer fact_test_xxxxxxxxxxxxxxxxxxxxxxxx"

The key's scopes determine which tools appear in tools/list. A key with * sees all 232 tools; a narrower key sees only the tools its scopes cover.

Claude Desktop

Claude Desktop connects to remote servers through its configuration file. Add an entry under mcpServers:

{
  "mcpServers": {
    "factuarea": {
      "type": "http",
      "url": "https://mcp.factuarea.com"
    }
  }
}

On the next launch, Claude Desktop discovers the server and walks you through the OAuth consent flow in your browser. To use an API key instead, add a headers object:

{
  "mcpServers": {
    "factuarea": {
      "type": "http",
      "url": "https://mcp.factuarea.com",
      "headers": {
        "Authorization": "Bearer fact_test_xxxxxxxxxxxxxxxxxxxxxxxx"
      }
    }
  }
}

The config file lives at ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows). Restart the app after editing it.

MCP Inspector

The MCP Inspector is the quickest way to explore the catalog and call tools by hand while you build.

Launch it

npx @modelcontextprotocol/inspector

Connect

Set Transport to Streamable HTTP and URL to https://mcp.factuarea.com. For OAuth, the Inspector runs the authorization flow for you. For an API key, add an Authorization: Bearer fact_test_… header under Authentication.

Explore

Open Tools → List Tools to see every tool your credential can reach, inspect its input schema, and run it with sample arguments.

Any MCP client

The server follows the MCP spec, so any compliant client works. The essentials:

  • Endpointhttps://mcp.factuarea.com (the path form …/mcp is a compatibility alias)
  • Transport — Streamable HTTP
  • AuthAuthorization: Bearer <token>, where the token is either an API key (fact_live_ / fact_test_) or an OAuth 2.1 access token
  • Discovery — on a 401, the server returns a WWW-Authenticate header pointing at its Protected Resource Metadata (RFC 9728) so clients can find the authorization server automatically

Tool discovery is paginated; the server returns the full catalog (up to 200 tools) in a single tools/list response by default, and honors nextCursor if your client paginates.

Authenticate

The MCP server accepts two kinds of credential on the same https://mcp.factuarea.com endpoint, for two different audiences. Both arrive as Authorization: Bearer <token>; the server tells them apart by the token's shape (fact_* → API key, anything else → OAuth access token).

Channel policy

This is the single most important rule of the MCP surface:

ChannelWhoHow scopes are chosenReachable tools
API keyThe account owner automating their own companyYou pick the scopes when you create the key — up to the super-scope *232 (everything)
OAuth 2.1A third-party app acting on a user's behalfThe user grants scopes on the consent screen, from a curated catalog218

The model mirrors GitHub: a personal access token (API key) is the owner's own credential and may hold any permission, while an OAuth app is external and is limited to a vetted set of scopes the user explicitly approves.

The 14 tools an OAuth app can never reach (only an API key can) are the most sensitive fiscal and privacy operations:

  • VeriFactu writes (verifactu:write) — 8 tools: register/retry/subsanar VeriFactu records and events, upload/activate/revoke FNMT certificates, update VeriFactu settings. These touch AEAT compliance and are owner-only.
  • GDPR erasure (delivery_notes:gdpr_forget) — 1 tool: erase signature-audit PII (Art. 17). Privileged, admin-only.
  • FacturaE (FACe) (facturae:read / facturae:write) — 5 tools: the FACe B2G operations. Their scopes are not in the OAuth consent catalog yet, so they are API-key only for now.

Everything else — all 218 read/write/transition/send tools — is available to both channels. See Scopes & permissions for the catalog.

API keys

An API key is an opaque Bearer token bound to your company, created in the developer dashboard at Settings → Developers → API Keys. The format and rules are identical to the REST API:

fact_live_<24 alphanumeric characters>   →  production company
fact_test_<24 alphanumeric characters>   →  isolated sandbox company

The prefix is the source of truth for the environment — see Test mode. The secret is shown only once at creation; the backend stores only a bcrypt hash. Pass it to your MCP client as:

Authorization: Bearer fact_test_xxxxxxxxxxxxxxxxxxxxxxxx

For the full key lifecycle — creation, scopes, rotation with grace period, revocation, IP allowlist, expires_at — see the canonical Authentication guide. Keys are shared across the REST and MCP surfaces.

OAuth 2.1

For third-party apps, Factuarea is a full OAuth 2.1 Authorization Server. It supports Dynamic Client Registration, the authorization-code flow with PKCE, and refresh-token rotation. No pre-registration or manual app approval is required — a client registers itself and the user authorizes it. (Interactive clients like Claude Code and the MCP Inspector drive this whole flow for you; the steps below are for building your own client.)

Discovery

Clients discover the server's capabilities through standard metadata endpoints (no /api prefix):

EndpointRFCPurpose
/.well-known/oauth-authorization-server8414Authorization Server Metadata — lists the authorize/token/register/introspect/revoke endpoints, supported scopes, code_challenge_methods_supported: ["S256"].
/.well-known/oauth-protected-resource9728Protected Resource Metadata — declares the MCP resource and which authorization server issues valid tokens.

When an unauthenticated request hits the endpoint, the server responds 401 with a WWW-Authenticate: Bearer ..., resource_metadata="<url>" header so RFC 9728 clients can find the authorization server without guessing.

1. Dynamic Client Registration (RFC 7591)

A client registers itself by POSTing its metadata; the server returns a client_id (and a client_secret for confidential clients):

curl -X POST https://mcp.factuarea.com/api/oauth/register \
  -H "Content-Type: application/json" \
  -d '{
    "client_name": "My Invoicing Assistant",
    "redirect_uris": ["https://myapp.example.com/callback"],
    "token_endpoint_auth_method": "none"
  }'

Public clients (browser/native apps) register with token_endpoint_auth_method: "none" and rely on PKCE; confidential clients use client_secret_basic. Registration is rate-limited to 60 per minute per IP.

2. Authorization with PKCE

Send the user to the authorize endpoint with a PKCE challenge (code_challenge_method=S256 is the only method accepted):

GET https://mcp.factuarea.com/api/oauth/authorize
  ?response_type=code
  &client_id=<client_id>
  &redirect_uri=https://myapp.example.com/callback
  &scope=factuarea.read invoices.write
  &state=<opaque>
  &code_challenge=<base64url(sha256(verifier))>
  &code_challenge_method=S256

This renders the consent screen, where the user:

  1. Picks the company to grant access to (a user may belong to several).
  2. Picks the environmentlive (the real company) or test (an isolated sandbox), Stripe-style. Test is opt-in; absent ⇒ live.
  3. Reviews and selects the scopes to grant. Sensitive scopes are flagged and not pre-checked.

On approval the server redirects back with a single-use code (and your state). Sensitive operations are filtered out of the catalog the user can approve — see the channel policy.

3. Token exchange

Exchange the code for an access token, sending the PKCE verifier:

curl -X POST https://mcp.factuarea.com/api/oauth/token \
  -d grant_type=authorization_code \
  -d code=<code> \
  -d redirect_uri=https://myapp.example.com/callback \
  -d client_id=<client_id> \
  -d code_verifier=<verifier>
{
  "access_token": "<opaque>",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "<opaque>",
  "scope": "profile.read clients.read invoices.read invoices.write"
}

The token persists the expanded, fine-grained scopes (macros like factuarea.read are expanded at issue time). Use the access token as the Bearer credential. The token endpoint is rate-limited to 60 per minute per (client, IP) and requires client authentication (HTTP Basic for confidential clients, client_id in the body for public ones).

4. Refresh-token rotation

Refresh tokens rotate by family: each refresh issues a new access token and a new refresh token, and invalidates the one you used.

curl -X POST https://mcp.factuarea.com/api/oauth/token \
  -d grant_type=refresh_token \
  -d refresh_token=<refresh_token> \
  -d client_id=<client_id>

If a refresh token is replayed (used after rotation — the classic sign of a leak), the server detects the reuse, revokes the entire token family and raises a security alert. Always store and use the latest refresh token only.

Revocation & introspection

EndpointRFCPurpose
POST /api/oauth/revoke7009Revoke an access or refresh token.
POST /api/oauth/introspect7662Check whether a token is active and read its scopes/metadata.

Both require client authentication. Users can also review and revoke connected apps from the Factuarea dashboard, and a company admin who loses access has their tokens revoked automatically on the next call.

Test mode

The MCP server runs against the same two environments as the REST API — live (your real company) and test (an isolated sandbox) — so you can build and validate an agent integration without touching production data, the AEAT, or your clients' inboxes.

How you select test mode depends on the channel:

ChannelHow to use test mode
API keyAuthenticate with a fact_test_ key. The prefix is the source of truth — a fact_test_ token always operates on the sandbox.
OAuth 2.1On the consent screen, pick the Test environment (Stripe-style). Absent ⇒ live. The issued token is bound to that environment.

A test credential operates on a dedicated sandbox company — a technical twin of your real company, provisioned automatically and inheriting its plan, so module/plan gating behaves faithfully. Isolation is structural (test and live data live in separate companies), and external effects are switched off:

EffectIn liveIn test
VeriFactuThe Alta record is created and transmitted to the AEAT.Created locally, but never transmitted to the AEAT.
EmailDocument emails reach real recipients.Not delivered to real recipients.
WebhooksSubscribed events are delivered to your endpoints.Recorded with livemode: false, but not delivered.
FACe (FacturaE)Submissions are presented to the real FACe web service.Simulated — no SOAP call leaves Factuarea; the registry number is synthetic (FACE-SANDBOX-*).

Everything else behaves exactly as in production, and the full set of 232 tools is available in both environments (subject to your scopes and plan). When your flow works end-to-end, switch to live: create a fact_live_ key, or re-run the consent flow and select the live environment.

This is the same sandbox mechanism as the REST API. See the canonical Test mode & sandbox guide for how the sandbox company is provisioned and queried.

On this page