Authenticate Cadrumo with AEAT

Authenticate Cadrumo with Spain’s Tax Agency, the Agencia Estatal de Administración Tributaria (AEAT), to let Cadrumo’s aeat command read information your AEAT identity authorizes. Authentication provides read-only access only. It never files a declaration, makes a payment, acknowledges a notification, or performs representative-write actions.

Run aeat --help before continuing. If it fails, stop before configuring authentication and follow the CLI troubleshooting guide. If that does not restore the command, report the failure with the Cadrumo version and redacted output.

When available to your identity, the command-line interface (CLI) can read filed declarations, expedientes, notifications, and filed justificantes. For Modelo 036/Censo profile facts, use the profile/Censo facts guide. Cadrumo no longer retrieves these facts live.

Before you start

You need:

  • an active profile. aeat config auth configure refuses with No hay un perfil activo until you create one. Start the interactive wizard (a NIF, CIF, DNI, or NIE is a Spanish tax identifier); it prompts for the profile passphrase and offers an optional recovery code once the profile exists:

    1Start the interactive profile wizard, which prompts for the passphrase.
    aeat config profile create me
    aeat config profile create me
    2Confirm a profile is active before configuring AEAT authentication.
    aeat --format json config profile list
    aeat --format json config profile list
    {
      "active_profile": "docs-sequence-sandbox",
      "command": "config.profile.list",
      "notices": [],
      "result": {
        "active_profile": "docs-sequence-sandbox",
        "profiles": [
          {
            "active": true,
            "bucket_id": "<bucket-id>",
            "name": "docs-sequence-sandbox"
          }
        ]
      },
      "schema_version": "2",
      "status": "success"
    }

    Review interactive profile creation and confirm a profile is active.

  • the master-key passphrase that protects your local store; the tool prompts for it.

See supported providers

List providers:

1List the supported authentication providers and their availability.
aeat --format json config auth providers
aeat --format json config auth providers
{
  "active_profile": "docs-sequence-sandbox",
  "command": "config.auth.providers",
  "notices": [],
  "result": {
    "providers": [
      {
        "description": "auth.catalogue.certificate_description",
        "id": "certificate",
        "label": "auth.catalogue.certificate_label"
      },
      {
        "description": "auth.catalogue.clave_movil_description",
        "id": "clave_movil",
        "label": "auth.catalogue.clave_movil_label"
      },
      {
        "description": "auth.catalogue.clave_permanente_description",
        "id": "clave_permanente",
        "label": "auth.catalogue.clave_permanente_label"
      }
    ]
  },
  "schema_version": "2",
  "status": "success"
}

Confirm the tool lists the supported authentication providers.

The list marks each provider as disponible (available now) or reservado (no disponible aún) (reserved, not available yet). Three are available:

  • certificate: your digital certificate file (certificado digital).

  • clave_movil: mobile-based Cl@ve, confirmed on your phone.

  • clave_permanente: Cl@ve Permanente, a DNI/NIE and password login.

Two more are listed but reserved, so you cannot configure them yet:

  • clave_pin: Cl@ve PIN, a one-time code system from AEAT. Reserved.

  • dnie_pkcs: the national ID card (DNI electrónico). Reserved.

Configure one of the available providers.

Configure a provider

Configure the provider you use:

1Configure a provider that needs a file, such as your digital certificate.
aeat config auth configure --provider certificate --file ./certificate.p12
aeat config auth configure --provider certificate --file ./certificate.p12

Use --file for providers that need a file, such as your digital certificate. Keep credential files private and do not share them.

Check local readiness

Check what is configured:

1Show what authentication is configured.
aeat config auth status
aeat config auth status
Authentication is not configured yet.
provider	
configured	False
authenticated	False
available	False
active_profile	docs-sequence-sandbox
active_profile_status	ready
active_profile_registered	True
active_profile_record_present	True
backend_configured	False
backend_available	False
certificate_path	
health_severity	
health_summary	
2Probe the stored credentials locally, without contacting AEAT.
aeat --format json config auth test
aeat --format json config auth test
{
  "active_profile": "docs-sequence-sandbox",
  "command": "config.auth.test",
  "notices": [],
  "result": {
    "active_profile": "docs-sequence-sandbox",
    "active_profile_precondition_action": null,
    "active_profile_record_present": true,
    "active_profile_registered": true,
    "active_profile_status": "ready",
    "authenticated": false,
    "available": false,
    "backend_available": false,
    "backend_configured": false,
    "certificate_path": "",
    "configured": false,
    "health_severity": "",
    "health_summary": "",
    "persisted_session_expired": null,
    "persisted_session_present": false,
    "persisted_session_state": "no_provider",
    "probe_result": "no_provider",
    "probe_summary": "No auth provider is configured; nothing to probe locally.",
    "provider": ""
  },
  "schema_version": "2",
  "status": "success"
}

Confirm the tool reports what is configured and probes it locally.

If you want to inspect a specific provider, use --provider with either command.

Renew your certificate before it expires

A digital certificate has an expiry date. The tool reads that date from the certificate file and warns you as it approaches. This helps you avoid a failed live read caused by an expired certificate.

Check the remaining validity:

1Read the certificate's remaining validity from the local probe.
aeat --format json config auth test
aeat --format json config auth test
{
  "active_profile": "docs-sequence-sandbox",
  "command": "config.auth.test",
  "notices": [],
  "result": {
    "active_profile": "docs-sequence-sandbox",
    "active_profile_precondition_action": null,
    "active_profile_record_present": true,
    "active_profile_registered": true,
    "active_profile_status": "ready",
    "authenticated": false,
    "available": false,
    "backend_available": false,
    "backend_configured": false,
    "certificate_path": "",
    "configured": false,
    "health_severity": "",
    "health_summary": "",
    "persisted_session_expired": null,
    "persisted_session_present": false,
    "persisted_session_state": "no_provider",
    "probe_result": "no_provider",
    "probe_summary": "No auth provider is configured; nothing to probe locally.",
    "provider": ""
  },
  "schema_version": "2",
  "status": "success"
}

Confirm the local probe reports the certificate's remaining validity.

The report tells you how the certificate stands:

  • More than 60 days left: the certificate is valid and reports the days remaining.

  • 60 days or fewer left: a warning appears: The certificate expires in N days. Plan the renewal. Start the renewal now.

  • 14 days or fewer left: the warning is critical. Renew before your next live read.

  • Already expired: The certificate expired N days ago. Renew it before authenticating. A live read is refused until you replace it.

Renew the certificate with the body that issued it, such as the Fábrica Nacional de Moneda y Timbre (FNMT) or AEAT. This happens outside the tool. Download the renewed certificate file (.p12 or .pfx) to your machine.

Point the tool at the renewed file:

1Point the tool at the renewed certificate file.
aeat config auth configure --provider certificate --file ./renewed-certificate.p12
aeat config auth configure --provider certificate --file ./renewed-certificate.p12

If the renewed certificate uses a new password, rotate the stored passphrase for its source:

1Rotate the stored passphrase for the certificate source.
aeat config auth certificate secret set --name personal
aeat config auth certificate secret set --name personal

Confirm the new expiry:

1Read the renewed certificate's expiry from the local probe.
aeat --format json config auth test
aeat --format json config auth test
{
  "active_profile": "docs-sequence-sandbox",
  "command": "config.auth.test",
  "notices": [],
  "result": {
    "active_profile": "docs-sequence-sandbox",
    "active_profile_precondition_action": null,
    "active_profile_record_present": true,
    "active_profile_registered": true,
    "active_profile_status": "ready",
    "authenticated": false,
    "available": false,
    "backend_available": false,
    "backend_configured": false,
    "certificate_path": "",
    "configured": false,
    "health_severity": "",
    "health_summary": "",
    "persisted_session_expired": null,
    "persisted_session_present": false,
    "persisted_session_state": "no_provider",
    "probe_result": "no_provider",
    "probe_summary": "No auth provider is configured; nothing to probe locally.",
    "provider": ""
  },
  "schema_version": "2",
  "status": "success"
}

Confirm the local probe reports the renewed certificate's later expiry.

The report now shows the renewed certificate’s later expiry date.

Manage several certificates

If you act for several entities, register one certificate per entity. Do not reconfigure aeat config auth configure --file every time you switch.

Register each certificate under a name:

1Register a certificate under a name.
aeat config auth certificate register --name personal --file ./personal.p12
aeat config auth certificate register --name personal --file ./personal.p12
2Register another certificate with a friendly name.
aeat config auth certificate register --name apoderado-acme --file ./acme.p12 \
  --friendly-name ACME SL
aeat config auth certificate register --name apoderado-acme --file ./acme.p12 `
  --friendly-name ACME SL

List every registered certificate:

1List every registered certificate.
aeat --format json config auth certificate list
aeat --format json config auth certificate list
{
  "active_profile": "docs-sequence-sandbox",
  "command": "config.auth.certificate.list",
  "notices": [],
  "result": {
    "active_source": "",
    "sources": []
  },
  "schema_version": "2",
  "status": "success"
}

Confirm the tool lists the registered certificates.

Select the one you want active:

1Select the registered certificate you want active.
aeat config auth certificate select --name apoderado-acme
aeat config auth certificate select --name apoderado-acme

Remove a registered certificate you no longer need:

1Remove a registered certificate you no longer need.
aeat --format json config auth certificate remove --name personal
aeat --format json config auth certificate remove --name personal
{
  "active_profile": "docs-sequence-sandbox",
  "command": "config.auth.certificate.remove",
  "notices": [],
  "result": {
    "active": false,
    "certificate_path": "",
    "name": "personal",
    "removed": false
  },
  "schema_version": "2",
  "status": "success"
}

Confirm the tool removes a registered certificate by name.

Check every registered certificate’s expiry

Each registered certificate has its own expiry date. Check all registered certificates in one pass, not only the active one:

1Check every registered certificate's expiry.
aeat --format json config auth certificate check
aeat --format json config auth certificate check
{
  "active_profile": "docs-sequence-sandbox",
  "command": "config.auth.certificate.check",
  "notices": [],
  "result": {
    "entries": [],
    "has_warnings": false
  },
  "schema_version": "2",
  "status": "success"
}

Confirm the tool checks every registered certificate's expiry in one pass.

The report lists each registered certificate with its status:

  • ok: valid, with the days remaining.

  • expiring: within the renewal window (60 days or fewer by default, or 14 days or fewer for the critical window). A warning names the certificate.

  • expired: already expired. A warning names the certificate.

Renew an expiring or expired certificate with the body that issued it, then re-register it under its existing name:

1Re-register a renewed certificate under its existing name.
aeat config auth certificate register --name apoderado-acme --file ./renewed-acme.p12
aeat config auth certificate register --name apoderado-acme --file ./renewed-acme.p12

Re-run aeat config auth certificate check to confirm the new expiry date.

Acquire or verify a live session

When you are ready to use a live-read command:

1Acquire or verify a live AEAT session.
aeat config auth login
aeat config auth login

If you need to intentionally reauthenticate, force a fresh authentication:

1Force a fresh authentication.
aeat config auth login --fresh
aeat config auth login --fresh

If a previous login was interrupted and left the authentication step stuck, use --reset-lock. Run it when login reports another login is in progress but it is not.

End sessions or reset authentication

To end the local certificate-provider session without removing its configuration:

1End the certificate provider's local sessions without removing its configuration.
aeat --format json config auth logout --provider certificate
aeat --format json config auth logout --provider certificate
{
  "active_profile": "docs-sequence-sandbox",
  "command": "config.auth.logout",
  "notices": [],
  "result": {
    "bucket_id": "<bucket-id>",
    "cleared_session_state": false,
    "providers": [
      "certificate"
    ],
    "removed_sessions": 0
  },
  "schema_version": "2",
  "status": "success"
}

Confirm logout ends the local session while preserving the provider configuration.

To remove one provider’s local configuration, sessions, acquisition lock, registered certificates, and stored certificate secrets:

1Remove the certificate provider's local configuration and custody state.
aeat --format json config auth reset --provider certificate --yes
aeat --format json config auth reset --provider certificate --yes
{
  "active_profile": "docs-sequence-sandbox",
  "command": "config.auth.reset",
  "notices": [],
  "result": {
    "bucket_id": "<bucket-id>",
    "cleared_locks": 0,
    "cleared_provider_configuration": true,
    "providers": [
      "certificate"
    ],
    "removed_certificate_secrets": 0,
    "removed_certificate_sources": 0,
    "removed_sessions": 0
  },
  "schema_version": "2",
  "status": "success"
}

Confirm reset removes the selected provider's local authentication state.

To remove local authentication state for every configured provider:

1Remove local authentication state for every configured provider.
aeat --format json config auth reset --all --yes
aeat --format json config auth reset --all --yes
{
  "active_profile": "docs-sequence-sandbox",
  "command": "config.auth.reset",
  "notices": [],
  "result": {
    "bucket_id": "<bucket-id>",
    "cleared_locks": 0,
    "cleared_provider_configuration": true,
    "providers": [
      "certificate",
      "clave_movil",
      "clave_permanente"
    ],
    "removed_certificate_secrets": 0,
    "removed_certificate_sources": 0,
    "removed_sessions": 0
  },
  "schema_version": "2",
  "status": "success"
}

Confirm reset removes local authentication state for all configured providers.

Act for someone else (apoderado)

If you act as an authorized tax representative for another taxpayer, record locally who you represent and the relevant AEAT apoderamiento scopes. An apoderado is an authorized representative, such as a gestor or asesor.

The apoderamiento itself is granted at AEAT. The represented party authorizes you through AEAT’s own apoderamiento procedures. The commands in this section record that grant in your local profile so read commands know whose data they read. They never register, extend, revoke, or renounce an apoderamiento at AEAT. No command writes representation state to AEAT.

See the accepted scopes

List the scope codes the tool accepts:

1List the scope codes the tool accepts.
aeat --format json config auth apoderado scopes list
aeat --format json config auth apoderado scopes list
{
  "active_profile": "docs-sequence-sandbox",
  "command": "config.auth.apoderado.scopes.list",
  "notices": [],
  "result": {
    "catalogue_version": "2026.05.bootstrap",
    "scopes": [
      {
        "code": "CENSO",
        "modelo_codes": [
          "036",
          "037"
        ],
        "name_en": "Censo declarations (registration, modification, deregistration)",
        "name_es": "Censo (modelo 036, 037)"
      },
      {
        "code": "EXPED",
        "modelo_codes": [],
        "name_en": "Filing records and queries",
        "name_es": "Expedientes y consultas"
      },
      {
        "code": "GENERALNT",
        "modelo_codes": [],
        "name_en": "General tax procedures",
        "name_es": "Trámites tributarios (general)"
      },
      {
        "code": "INFORM",
        "modelo_codes": [
          "347",
          "349",
          "720"
        ],
        "name_en": "Informational declarations",
        "name_es": "Declaraciones informativas (modelo 347, 349, 720)"
      },
      {
        "code": "IVA",
        "modelo_codes": [
          "303",
          "390"
        ],
        "name_en": "IVA periodic and annual returns",
        "name_es": "IVA (modelo 303, 390)"
      },
      {
        "code": "NOTIFIC",
        "modelo_codes": [],
        "name_en": "Electronic notifications (DEHú)",
        "name_es": "Notificaciones electrónicas DEHú"
      },
      {
        "code": "PAGOSF",
        "modelo_codes": [
          "130",
          "131"
        ],
        "name_en": "Quarterly self-employed instalments",
        "name_es": "Pagos fraccionados (modelo 130, 131)"
      },
      {
        "code": "RENT",
        "modelo_codes": [
          "100",
          "714"
        ],
        "name_en": "Income tax and wealth declaration",
        "name_es": "Renta y patrimonio (modelo 100, 714)"
      },
      {
        "code": "RETEN",
        "modelo_codes": [
          "111",
          "115",
          "123",
          "180",
          "190",
          "193"
        ],
        "name_en": "Withholding tax declarations",
        "name_es": "Retenciones (modelo 111, 115, 123, 180, 190, 193)"
      }
    ]
  },
  "schema_version": "2",
  "status": "success"
}

Confirm the tool lists the accepted apoderamiento scope codes.

Each scope is an AEAT apoderamiento area. Examples include:

  • RENT for modelos 100 and 714.

  • IVA for modelos 303 and 390.

  • PAGOSF for modelos 130 and 131.

  • RETEN for withholding modelos.

  • GENERALNT, CENSO, INFORM, NOTIFIC, and EXPED for their respective authority areas.

Record who you represent

Set the represented party’s tax identifier (NIF, CIF, DNI, NIE, or NII) and the scopes that match the grant at AEAT:

1Record who you represent and the scopes that match the grant at AEAT.
aeat --format json config auth apoderado configure --represented-nif 11111111H --scope \
  IVA --scope PAGOSF
aeat --format json config auth apoderado configure --represented-nif 11111111H --scope `
  IVA --scope PAGOSF
{
  "active_profile": "docs-sequence-sandbox",
  "command": "config.auth.apoderado.configure",
  "notices": [],
  "result": {
    "bucket_id": "<bucket-id>",
    "catalogue_version": "2026.05.bootstrap",
    "configured_at": "2026-04-01T09:00:00+00:00",
    "granted_scopes": [
      "IVA",
      "PAGOSF"
    ],
    "notes": "",
    "represented_nif": "sha256:33982da1"
  },
  "schema_version": "2",
  "status": "success"
}

Confirm the tool records the represented party and scopes locally.

Repeat --scope for each code. The CLI rejects a comma-separated list. Scope codes are uppercase. Use --scope ALL to record every catalogue scope at once. The CLI rejects unknown codes and lists the accepted codes.

The active profile holds at most one apoderado configuration; configuring again replaces it. The represented identifier is stored encrypted.

Review or retire the configuration

Show what is recorded for the active profile:

1Show the apoderado configuration recorded for the active profile.
aeat --format json config auth apoderado status
aeat --format json config auth apoderado status
{
  "active_profile": "docs-sequence-sandbox",
  "command": "config.auth.apoderado.status",
  "notices": [],
  "result": {
    "bucket_id": "<bucket-id>",
    "catalogue_version": "2026.05.bootstrap",
    "configured": true,
    "configured_at": "2026-04-01T09:00:00+00:00",
    "granted_scopes": [
      "IVA",
      "PAGOSF"
    ],
    "represented_nif": "sha256:33982da1"
  },
  "schema_version": "2",
  "status": "success"
}

Confirm the tool shows the apoderado configuration recorded locally.

aeat config auth apoderado check is the live-verification verb, but the live AEAT read path is sealed. It refuses with a “live verification unavailable” message and points you back to status. Use aeat config auth apoderado status for the offline configuration read.

Remove the configuration when the representation ends:

1Remove the local apoderado record when the representation ends.
aeat --format json config auth apoderado clear
aeat --format json config auth apoderado clear
{
  "active_profile": "docs-sequence-sandbox",
  "command": "config.auth.apoderado.clear",
  "notices": [],
  "result": {
    "bucket_id": "<bucket-id>",
    "cleared": true
  },
  "schema_version": "2",
  "status": "success"
}

Confirm the tool removes the local apoderado record.

Clearing removes only the local record. The apoderamiento at AEAT is unaffected. Revoke it through AEAT’s own procedures.

Next steps

Run aeat config auth test before each live-read task. Follow Renew your certificate before it expires when the command reports an expiry warning.