Factuarea APIDevelopers

Cuentas gestionadas

Aprovisiona cuentas para tus clientes por API: alta, vale de reclamación, nivel de acceso, fin de la gestión y las rutas que abre una gestionada.

Una cuenta gestionada es la cuenta que creas para uno de tus clientes. Es una cuenta normal, separada de la tuya: tiene su propio NIF, su propio perfil fiscal, su propio plan y, cuando el cliente la reclama, su propio titular. La aprovisionas por API, la paga tu cuenta y conservas un nivel de acceso sobre ella (none, view u operate) hasta que el cliente toma el relevo. Esta guía está escrita para la cuenta que aprovisiona, el proveedor de cuentas gestionadas. Explica el privilegio que necesitas, cómo crear y leer cuentas gestionadas, cómo las reclama el cliente, cómo funcionan tu nivel y el fin de la gestión, qué rutas abre una gestionada y cómo subir el certificado VeriFactu del cliente para que su NIF pueda emitir.

Lo que pagas por cada cuenta gestionada está en precio de las cuentas gestionadas y cambio de pagador. El flujo completo con una clave fact_test_, sin cobros, está en probar cuentas gestionadas. Para crear muchas de una vez, usa la importación masiva. La cuenta que operas tú mismo, y el segmento {account}, están en cuentas y NIF.

La vida de una cuenta gestionada

Dos campos dicen en qué punto está una cuenta gestionada. status es el estado de la propia cuenta, y el bloque management dice si tu cuenta todavía la gestiona.

statusQué significa
provisionedLa creaste y nadie la ha reclamado todavía, así que no tiene titular. La paga tu cuenta.
claimedEl cliente la reclamó con el vale y ahora es su owner. Conservas tu nivel y, hasta que el cliente asuma el pago, sigues pagando tú.
activeEl cliente la lleva y la paga: asumió el pago, o la gestión terminó después de la reclamación.
management.statusQué significa
activeTu cuenta la gestiona.
endedLa gestión terminó y tu cuenta perdió el acceso. Se lee en management, nunca en status.

Una cuenta cuya gestión termina antes de que nadie la reclame sigue en provisioned.

Scopes privilegiados

Las operaciones de aprovisionamiento de esta guía necesitan accounts:read o accounts:write. Son scopes privilegiados, y se distinguen de cualquier otro scope en cuatro cosas:

  • * no los cubre. Una clave que solo tiene * recibe 403 insufficient_scope en estas operaciones. Indica accounts:read y accounts:write de forma explícita, junto a * si quieres también el resto.
  • Los concedemos nosotros, cuenta por cuenta. Factuarea concede el privilegio de proveedor de cuentas a una cuenta, y solo Factuarea puede hacerlo. Pídenoslo: la API no tiene ninguna operación que lo conceda. GET /v1/me te dice si lo tienes, con account.is_provider y account.privileged_scopes. Una clave solo puede llevar accounts:* mientras esa lista los nombre, así que crear una clave así sin el privilegio responde 403 privileged_scope_required.
  • No llegan por OAuth. Usa una API key, también con el servidor MCP.
  • Ningún plan los condiciona. La puerta es el privilegio, no el plan.

La comprobación corre en cada petición y nada sale de una caché. La clave debe listar el scope, tu cuenta debe tener hoy el privilegio y la persona que emitió la clave debe seguir siendo titular o administrador de tu cuenta. Si falla cualquiera de las tres cosas, la respuesta es 403 privileged_scope_required, y las dos causas responden igual. Si revocamos el privilegio, o esa persona deja de ser titular o administrador, la misma clave deja de funcionar en su siguiente petición.

Las operaciones del plan y de la importación tienen sus propias guías: precio de las cuentas gestionadas y cambio de pagador e importación masiva.

El entorno de la clave es el entorno de todo lo que aprovisiona y lee. Una clave fact_live_ trabaja con cuentas gestionadas de producción y una clave fact_test_ con las de pruebas, y cada una ve solo las suyas.

{
  "data": {
    "account": {
      "id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a00",
      "name": "Asesoría Martínez",
      "environment": "live",
      "is_provider": true,
      "privileged_scopes": [
        "accounts:read",
        "accounts:write"
      ]
    }
  }
}

El bloque account de GET /v1/me, abreviado. La misma respuesta lista en client_accounts las cuentas gestionadas que alcanza tu cuenta, con origin: "provisioned", hasta 100; GET /v1/accounts las lista todas.

Aprovisiona una cuenta

POST /v1/accounts crea la cuenta, su NIF y la relación con tu cuenta en una sola llamada. No lleva segmento {account}, porque la cuenta todavía no existe. El proveedor es siempre la cuenta de la clave y el entorno es el de la clave: ninguno va en el cuerpo.

CampoObligatorioNotas
external_refSíTu propia referencia de la cuenta, hasta 128 caracteres, única para tu cuenta. Hace la llamada idempotente.
planSíemprendedor, empresario o integral. enterprise solo lo asignamos nosotros, por contrato: 422 managed_account_plan_not_self_serve.
tax_idSíEl NIF, CIF o NIE del cliente, hasta 20 caracteres, validado con su carácter de control.
legal_nameSíRazón social, de 2 a 100 caracteres. Sin display_name, es también el nombre de la cuenta.
access_levelNoEl nivel que conserva tu cuenta sobre ella: none (por defecto), view u operate.
display_nameNoNombre visible de la cuenta y de su NIF, de 2 a 120 caracteres.
address, city, postal_code, province, countryNoEl domicilio fiscal del cliente. El código postal deriva la zona AEAT.
languageNoes (por defecto), en o ca. El idioma de la cuenta y de su vale.
owner_emailDependeEl email de la persona que será titular de la cuenta. Obligatorio con una clave de producción, y siempre que send_email sea true. Con una clave de pruebas es opcional si no se envía ningún correo.
send_emailNofalse por defecto. Decide quién entrega el vale.
curl -X POST https://api.factuarea.com/v1/accounts \
  -H "Authorization: Bearer $FACTUAREA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "external_ref": "cliente-0042",
    "plan": "empresario",
    "access_level": "operate",
    "tax_id": "B12345674",
    "legal_name": "Talleres García SL",
    "display_name": "Talleres García",
    "language": "es",
    "owner_email": "laura@talleresgarcia.example",
    "send_email": false
  }'
{
  "data": {
    "id": "0199a1f0-1c22-7a10-b111-6a6b6c6d6e6f",
    "object": "managed_account",
    "external_ref": "cliente-0042",
    "status": "provisioned",
    "environment": "live",
    "plan": "empresario",
    "pending_plan": null,
    "billing_interval": "monthly",
    "access_level": "operate",
    "display_name": "Talleres García",
    "language": "es",
    "companies": [
      {
        "id": "0199a1f0-1c22-7a10-b111-7a7b7c7d7e7f",
        "tax_id": "B12345674",
        "name": "Talleres García SL"
      }
    ],
    "claimed": false,
    "claimed_at": null,
    "management": {
      "status": "active",
      "ended_at": null
    },
    "paid_by_provider": true,
    "created_at": "2026-09-27T09:00:00+00:00",
    "claim_token": "c1a1c1a1c1a1c1a1c1a1c1a1c1a1c1a1c1a1c1a1c1a1c1a1",
    "claim_url": "https://app.factuarea.com/account-claims/accept?token=c1a1c1a1c1a1c1a1c1a1c1a1c1a1c1a1c1a1c1a1c1a1c1a1",
    "claim_expires_at": "2026-10-11T09:00:00+00:00"
  }
}

Una cuenta nueva responde 201 Created con Location: /v1/accounts/{id}. El cuerpo es la cuenta gestionada más su vale de reclamación. companies[].id es el {company} que usas en el eje de NIF, e id es el {account} de las rutas que nombran una cuenta gestionada.

La paga tu cuenta. En producción se la cobramos a tu cuenta en la suscripción managed-accounts, y precio de las cuentas gestionadas y cambio de pagador explica cuánto y cuándo. Una clave fact_test_ crea en cambio una cuenta de pruebas: no se cobra nada, no hay vale (claim_token y claim_url son null) y no cuenta para tu tope.

Idempotency-Key es obligatoria, porque el aprovisionamiento no se puede deshacer. Una petición sin ella se rechaza con 422 idempotency_key_required antes de ejecutar ninguna lógica de negocio. Un rechazo no crea nada.

CódigoStatusCuándo
insufficient_scope403La clave no lista accounts:write. * no cuenta.
privileged_scope_required403Tu cuenta no tiene el privilegio, o la persona que emitió la clave ya no es titular ni administrador de ella.
invalid_param_value422Un campo no pasa la validación. error.param lo nombra.
managed_account_plan_not_self_serve422plan es enterprise.
managed_account_provider_in_trial422Tu cuenta está en su trial y pidió una cuenta de producción, que se cobra. Durante el trial, solo cuentas de pruebas.
managed_accounts_limit_reached422Tu cuenta está en su tope de cuentas gestionadas de producción.
business_rule_violation con subcode nif_already_claimed_by_another_account422El tax_id de una cuenta de producción ya pertenece a otra cuenta. Un NIF no puede estar en dos cuentas a la vez. error.param es tax_id.
payment_method_required402Una cuenta de producción necesita un método de pago en tu cuenta, y no hay ninguno. Añade uno y repite la llamada.
provisioning_account_claimed409El external_ref nombra una cuenta que su titular ya reclamó.
managed_account_external_ref_in_other_environment409El external_ref lo usa una cuenta del otro entorno.
idempotency_key_required422La petición no lleva Idempotency-Key.
idempotency_key_reused422Esa clave ya se usó con un cuerpo distinto.

Idempotencia: Idempotency-Key y external_ref

Dos protecciones trabajan juntas, y hacen trabajos distintos.

Idempotency-Key protege una petición. Si se corta la conexión y repites la misma petición con la misma clave, reproducimos la primera respuesta, con la cabecera Idempotent-Replayed: true, y no aprovisionamos dos veces. El vale es la única excepción: nunca lo guardamos en claro, ni siquiera en la caché de idempotencia, así que una repetición responde claim_token y claim_url como null. Si nunca recibiste la primera respuesta, reemite el vale. La misma clave con otro cuerpo es 422 idempotency_key_reused.

external_ref protege la cuenta. Es única para tu cuenta en los dos entornos, así que es la clave natural de tu CRM. Aprovisionar un external_ref que tu cuenta ya aprovisionó, aunque sea con una Idempotency-Key nueva, nunca crea una segunda cuenta:

El external_ref ya es…Qué hace la llamada
Una cuenta bajo tu gestión, en el mismo entorno200 OK con esa misma cuenta. No se crea, cambia ni cobra nada, y no se emite ningún vale. El plan, el nivel y el NIF que envíes se ignoran.
Una cuenta del otro entorno409 managed_account_external_ref_in_other_environment, sin escribir nada. Usa otra referencia.
Una cuenta cuya gestión terminaste antes de que nadie la reclamara200 OK. La gestión se reactiva con el nivel que envíes y sus NIF se reanudan. En una cuenta de producción, tu cuenta vuelve a pagarla y corren las mismas comprobaciones que para una nueva: trial, tope y método de pago.
Una cuenta que su titular ya reclamó409 provisioning_account_claimed, sin reactivar nada.

El vale de reclamación

El vale es lo que necesita el cliente para reclamar la cuenta. Llega en tres campos de la respuesta del aprovisionamiento:

CampoQué es
claim_tokenEl secreto de un solo uso. Guardamos solo su huella, nunca el vale en sí.
claim_urlEl enlace a la página de reclamación de la app. Lleva el token.
claim_expires_atCuándo caduca el vale: 14 días después de emitirlo.

El vale se devuelve una sola vez. claim_token y claim_url aparecen en la respuesta que emite el vale y nunca más: ni en una repetición, ni en un listado, ni en un detalle. Trátalos como una credencial y entrégalos o guárdalos enseguida. Si pierdes uno, reemítelo: el anterior deja de valer.

Lo que recibes depende de la llamada:

Llamadaclaim_token y claim_url
Cuenta de producción nueva, send_email: false (por defecto)Se devuelven, una vez, en el 201.
Cuenta de producción nueva, send_email: truenull. El enlace viaja solo en el correo a owner_email.
Cuenta de pruebas nuevanull. Una cuenta de pruebas no tiene vale.
external_ref ya aprovisionado (200)null. No se emite ningún vale.
Repetición idempotentenull.
Listado y detalleNunca aparecen.

El vale se emite a nombre de owner_email: una persona que inicie sesión con otra dirección no puede usarlo.

Quién entrega el vale: send_email

Eliges una de dos vías, y el vale nunca viaja por las dos.

  • send_email: false (por defecto). La respuesta lleva claim_url una sola vez y lo entregas tú por tu propio canal, por ejemplo tu CRM o tu propio correo. owner_email sigue siendo obligatorio con una clave de producción, porque el vale se emite a su nombre.
  • send_email: true. Enviamos el enlace por correo a owner_email, en el language de la cuenta, y la respuesta lleva null en claim_token y claim_url. owner_email es obligatorio.

Reemite el vale

POST /v1/accounts/{account}/claim-tokens emite un vale nuevo para una cuenta gestionada que su titular todavía no ha reclamado. {account} es el id de la cuenta gestionada. Los vales pendientes dejan de funcionar y el nuevo va al mismo email y en el mismo idioma. El cuerpo es opcional y admite send_email, con el mismo significado que arriba. Necesita una Idempotency-Key.

curl -X POST https://api.factuarea.com/v1/accounts/$MANAGED_ACCOUNT_ID/claim-tokens \
  -H "Authorization: Bearer $FACTUAREA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{ "send_email": false }'
{
  "data": {
    "object": "managed_account_claim",
    "account_id": "0199a1f0-1c22-7a10-b111-6a6b6c6d6e6f",
    "claim_token": "d2b2d2b2d2b2d2b2d2b2d2b2d2b2d2b2d2b2d2b2d2b2d2b2",
    "claim_url": "https://app.factuarea.com/account-claims/accept?token=d2b2d2b2d2b2d2b2d2b2d2b2d2b2d2b2d2b2d2b2d2b2d2b2",
    "expires_at": "2026-10-11T09:00:00+00:00"
  }
}

Con send_email: true, claim_token y claim_url son null, pero expires_at sigue llevando la fecha: la caducidad no es un secreto y una reemisión siempre emite un vale nuevo. Una repetición idempotente devuelve también claim_token y claim_url como null, y conserva expires_at. El vale nuevo dura 14 días, como el primero.

CódigoStatusCuándo
provisioning_account_claimed409El titular ya reclamó la cuenta. No queda nada que reemitir.
managed_account_not_claimable422Es una cuenta de pruebas, y una cuenta de pruebas nunca se reclama.
idempotency_key_required422La petición no lleva Idempotency-Key.
account_not_found404{account} no es una cuenta que gestiones. Consulta qué rutas aceptan una cuenta gestionada.

Lista y lee tus cuentas gestionadas

GET /v1/accounts lista las cuentas que aprovisionó tu cuenta, solo en el entorno de la clave: una clave fact_test_ lista tus cuentas de pruebas y una de producción las de producción. Ningún elemento lleva vale.

ParámetroNotas
statusprovisioned, claimed o active filtran por el estado de la cuenta. ended devuelve las cuentas cuya gestión terminó. Sin status, la lista contiene solo las gestiones vigentes.
external_refTu referencia, coincidencia exacta: cero o un resultado. Hasta 128 caracteres.
limitEntre 1 y 100, 25 por defecto.
starting_afterEl id de la última cuenta de la página anterior, que es también su next_cursor.
curl -G https://api.factuarea.com/v1/accounts \
  -H "Authorization: Bearer $FACTUAREA_API_KEY" \
  --data-urlencode "status=provisioned" \
  --data-urlencode "limit=25"
{
  "data": [
    {
      "id": "0199a1f0-1c22-7a10-b111-6a6b6c6d6e6f",
      "object": "managed_account",
      "external_ref": "cliente-0042",
      "status": "provisioned",
      "environment": "live",
      "plan": "empresario",
      "pending_plan": null,
      "billing_interval": "monthly",
      "access_level": "operate",
      "display_name": "Talleres García",
      "language": "es",
      "companies": [
        {
          "id": "0199a1f0-1c22-7a10-b111-7a7b7c7d7e7f",
          "tax_id": "B12345674",
          "name": "Talleres García SL"
        }
      ],
      "claimed": false,
      "claimed_at": null,
      "management": {
        "status": "active",
        "ended_at": null
      },
      "paid_by_provider": true,
      "created_at": "2026-09-27T09:00:00+00:00"
    }
  ],
  "has_more": false,
  "next_cursor": null
}

Un valor de status desconocido o un external_ref de más de 128 caracteres responde 422, y un parámetro de consulta desconocido o un limit o starting_after mal formado responde 400.

GET /v1/accounts/{account} devuelve una cuenta gestionada por su id, con la misma forma. Funciona con cualquier nivel de acceso, none incluido: las operaciones de aprovisionamiento solo piden que la gestión esté vigente.

CampoSignificado
idEl {account} de las rutas que nombran una cuenta gestionada.
external_refTu propia referencia.
statusprovisioned, claimed o active, como arriba.
environmentlive o test: el entorno de la clave que la aprovisionó. Una cuenta de pruebas nunca se cobra y no se puede reclamar.
plan, pending_planEl plan actual. pending_plan es una bajada que surte efecto al final del periodo, o null.
billing_intervalmonthly: las cuentas gestionadas se facturan cada mes.
access_levelEl nivel que conserva tu cuenta sobre ella.
display_name, languageSu nombre visible, y el idioma de la cuenta y de su vale.
companiesSus NIF, cada uno con id (el {company} del eje de NIF), tax_id y name.
claimed, claimed_atSi el titular la reclamó, y cuándo.
managementstatus (active o ended) y ended_at.
paid_by_providertrue mientras la paga tu cuenta, false cuando el titular asumió el pago.
created_atCuándo se aprovisionó.

Con status=ended, tu cuenta ha perdido el acceso a la cuenta, así que cada elemento se reduce a lo que ya sabías:

{
  "id": "0199a1f0-1c22-7a10-b111-6a6b6c6d6e6f",
  "object": "managed_account",
  "external_ref": "cliente-0042",
  "management": {
    "status": "ended",
    "ended_at": "2026-10-05T10:00:00+00:00"
  }
}

Consumo del aprovisionamiento

GET /v1/accounts/{account}/provisioning-usage te dice cuántas cuentas gestionadas llevas y cuántas más puedes aprovisionar. A diferencia de las rutas anteriores, {account} es tu propia cuenta, el account.id de GET /v1/me: cualquier otro valor responde 404 account_not_found.

{
  "data": {
    "object": "provisioning_usage",
    "managed_accounts": {
      "live": 12,
      "test": 3
    },
    "limit": 50,
    "remaining": 38,
    "managed_accounts_by_plan": {
      "emprendedor": 7,
      "empresario": 5,
      "integral": 0,
      "enterprise": 0
    },
    "provider_paid_additional_nifs": 2,
    "provider_paid_employee_seats": 9
  }
}
CampoSignificado
managed_accounts.live, managed_accounts.testLas cuentas gestionadas cuya gestión está vigente, por entorno. Los dos contadores salen siempre, sea cual sea el entorno de la clave. Las cuentas de pruebas nunca cuentan para el límite.
limit, remainingTu tope y las cuentas de producción que aún puedes aprovisionar. remaining nunca es negativo.
managed_accounts_by_planLas cuentas de producción por plan. Siempre lista todos los planes del catálogo, también los que están a 0.
provider_paid_additional_nifs, provider_paid_employee_seatsLo que paga tu cuenta por tus cuentas gestionadas además de su propia cuota: NIF adicionales y asientos de empleado.

La reclamación

El cliente reclama la cuenta en la app, no por la API. El enlace de reclamación abre una página donde el cliente ve la cuenta que le espera, con su plan y su precio. Una persona nueva elige una contraseña, y quien ya tiene una cuenta de Factuarea inicia sesión. En los dos casos debe usar la dirección a la que se emitió el vale (owner_email): otra dirección se rechaza, igual que un vale ya usado, revocado o caducado.

La reclamación tiene estos efectos:

  • El cliente pasa a ser el owner de la cuenta. claimed es true, claimed_at queda fijado y status pasa de provisioned a claimed. Si ya habías terminado la gestión, la cuenta pasa directamente a active.
  • Conservas tu nivel. La reclamación nunca cambia access_level. Después puedes bajar el nivel, pero ya no subirlo: consulta las reglas del nivel.
  • Sigues pagando hasta que el cliente asuma el pago. Mientras paid_by_provider sea true, la cuenta la pagas tú. Precio de las cuentas gestionadas y cambio de pagador explica cómo pasa el pago al cliente.
  • Te avisamos. Los titulares y administradores de tu cuenta reciben un correo que dice que la cuenta se ha reclamado.
  • El vale se consume. Reemitirlo responde 409 provisioning_account_claimed.

Una cuenta de pruebas no se puede reclamar: 422 managed_account_not_claimable cuando intentas reemitir su vale.

Tu nivel de acceso

PATCH /v1/accounts/{account}/access-level cambia el nivel que conserva tu cuenta sobre una cuenta gestionada. El cuerpo lleva level, que es obligatorio.

curl -X PATCH https://api.factuarea.com/v1/accounts/$MANAGED_ACCOUNT_ID/access-level \
  -H "Authorization: Bearer $FACTUAREA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{ "level": "view" }'

La respuesta es 200 con la cuenta gestionada y su nuevo access_level.

NivelQué alcanzan tus credenciales
noneNada de los datos del cliente. Aprovisionas la cuenta, la pagas y todavía puedes leerla y usar las operaciones de aprovisionamiento, pero ninguno de sus NIF es alcanzable.
viewLos NIF de la cuenta, solo lectura. Una escritura sobre uno de ellos responde 403 company_read_only.
operateLos NIF de la cuenta, para leer y escribir. Nunca te da lo que solo puede hacer la cuenta del propio cliente: sus webhooks de empresa y sus registros de peticiones, por ejemplo, responden 403 forbidden_action.

Antes de la reclamación, puedes subir y bajar el nivel las veces que necesites. Después de la reclamación, puedes bajarlo en cualquier momento, pero subirlo responde 422 access_level_raise_forbidden y no cambia nada: solo el titular puede subirlo, desde su panel. Bajarlo surte efecto al instante y recorta lo que tus credenciales alcanzan en sus NIF desde su siguiente petición.

Termina la gestión

DELETE /v1/accounts/{account}/management termina la gestión de una cuenta gestionada. Desde la siguiente petición tu cuenta pierde el acceso a ella y a sus NIF, y todas las rutas responden 404. Necesita una Idempotency-Key, y la respuesta es 200 con la cuenta y management.status: "ended".

curl -X DELETE https://api.factuarea.com/v1/accounts/$MANAGED_ACCOUNT_ID/management \
  -H "Authorization: Bearer $FACTUAREA_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"
{
  "data": {
    "id": "0199a1f0-1c22-7a10-b111-6a6b6c6d6e6f",
    "object": "managed_account",
    "external_ref": "cliente-0042",
    "status": "provisioned",
    "environment": "live",
    "plan": "empresario",
    "pending_plan": null,
    "billing_interval": "monthly",
    "access_level": "operate",
    "display_name": "Talleres García",
    "language": "es",
    "companies": [
      {
        "id": "0199a1f0-1c22-7a10-b111-7a7b7c7d7e7f",
        "tax_id": "B12345674",
        "name": "Talleres García SL"
      }
    ],
    "claimed": false,
    "claimed_at": null,
    "management": {
      "status": "ended",
      "ended_at": "2026-09-28T10:00:00+00:00"
    },
    "paid_by_provider": true,
    "created_at": "2026-09-27T09:00:00+00:00"
  }
}

Lo que pasa después depende de si el cliente había reclamado la cuenta:

  • Antes de la reclamación. La cuenta sigue en provisioned y no es operable: todos sus NIF pasan a inactive, con suspension_reason: "management_ended". El vale sigue funcionando. La cuenta queda así hasta que el cliente la reclame, o hasta que aprovisiones de nuevo el mismo external_ref, lo que reactiva la gestión.
  • Después de la reclamación. La cuenta pasa a active y el pago pasa al titular. Si el titular no tiene método de pago, la gestión termina igualmente: dejas de pagar y los NIF de la cuenta quedan suspendidos hasta que el titular asuma el pago.
  • Una cuenta enterprise por contrato que pagas de verdad necesita un plan en el que continuar. Sin él, la respuesta es 422 enterprise_contract_transition_required y la gestión sigue vigente. Escríbenos para que fijemos el plan y repite la llamada.

Después, GET /v1/accounts?status=ended sigue listando la cuenta con su id, tu external_ref y el bloque management, para que tu CRM pueda cerrar el ciclo. El cliente también puede terminar la gestión desde su panel.

Qué rutas de {account} aceptan una cuenta gestionada

{account} nombra normalmente tu propia cuenta. Una cuenta gestionada se acepta en una lista cerrada de rutas, y solo cuando tu cuenta la aprovisionó, su gestión está vigente y está en el mismo entorno que la clave.

RutasCuenta gestionada
Las rutas de aprovisionamiento: GET /v1/accounts/{account}, PATCH …/access-level, DELETE …/management, POST …/claim-tokens, PATCH …/plan y GET …/plan/previewSe acepta, con cualquier nivel, none incluido. Las operaciones del plan piden además operate, como explica precio de las cuentas gestionadas y cambio de pagador. Estas rutas aceptan solo una cuenta gestionada: tu propia cuenta responde aquí 404 account_not_found.
Los datos de NIF de la cuenta: GET /v1/accounts/{account}/companies, POST …/companies, GET …/companies/stats, GET …/companies/seat-charge-preview, POST …/companies/activate, GET …/companies/{company}, PATCH …/companies/{company}, DELETE …/companies/{company}, POST …/companies/{company}/activate, POST …/companies/{company}/deactivate, POST …/companies/{company}/verify-creation, GET …/companies/{company}/creation-status y GET …/companies/{company}/issuing-readinessSe aceptan con un nivel distinto de none. Las escrituras necesitan operate: con view, una escritura sobre un NIF responde 403 company_read_only, y registrar o reactivar un NIF, que se factura, responde 403 billing_managed_by_provider.
Todo lo demás del eje de cuenta: miembros, invitaciones, traspasos de NIF, titularidad, API keys, consumo, asientos de empleado, el resumen de plantilla, el consumo del aprovisionamiento, webhooks de cuenta y registros de peticiones404 account_not_found. Son capacidades de la propia cuenta.

Todo aquello en lo que no se acepta una cuenta gestionada responde el mismo 404 account_not_found, con el mismo cuerpo, en todos los casos: una cuenta que no es tuya, una que no existe, una del otro entorno, una cuya gestión terminó, una que te dio acceso cedido en vez de aprovisionarla tú, y un valor que no es un identificador. Nada en la respuesta los distingue, así que no sirve para averiguar qué cuentas existen.

Los NIF de la cuenta gestionada también se alcanzan en el eje de NIF, en /v1/companies/{company}/…, con el companies[].id que has leído arriba. Tus claves los alcanzan como alcanzan tus propios NIF, limitadas por tu nivel y por el ámbito de NIF de cada clave. Para añadir los NIF del cliente a una clave que ya tienes desplegada, envía la lista completa resultante a PATCH /v1/accounts/{account}/api-keys/{api_key}/nif-scope como scoped_company_ids. Un ámbito nuevo nunca puede exceder el de la credencial que lo cambia.

Cuando el cliente asume el pago, la cuenta paga sus propios NIF: tu relación pasa a ser acceso cedido con el mismo nivel, y registrar, activar, desactivar o archivar NIF corresponde a la cuenta que paga. NIF adicionales explica qué puedes añadir mientras pagas tú.

El lado del cliente es de la app

Lo que el cliente hace con una cuenta gestionada ocurre en la app, y nada de ello tiene una operación /v1 ni una tool MCP. Una credencial tuya no puede decidir por el titular. El cliente usa la app para:

  • reclamar la cuenta con el vale;
  • ver qué cuenta gestiona la suya y con qué nivel;
  • subir o bajar el nivel que conservas, que es la única forma de subirlo tras la reclamación;
  • terminar la gestión;
  • asumir el pago.

Todo lo demás de esta guía es tu lado, el del proveedor.

El tope: max_managed_accounts

Cada cuenta que aprovisiona tiene un tope de cuentas gestionadas de producción. Por defecto es 25, vive en tu cuenta como max_managed_accounts y solo lo cambiamos nosotros. provisioning-usage lo devuelve como limit.

  • Qué se cuenta. Solo las cuentas gestionadas de producción con la gestión vigente. Las cuentas de pruebas nunca lo consumen, ni tampoco las cuentas cuya gestión terminó.
  • Al alcanzarlo. Aprovisionar una cuenta de producción más, o reactivar una, responde 422 managed_accounts_limit_reached y no crea nada. Dos peticiones a la vez no pueden superar juntas el tope, porque tu cuenta queda bloqueada mientras se comprueba cada una.
  • Cómo hacer hueco. Termina la gestión de una cuenta que ya no llevas, o pídenos que subamos el tope de tu cuenta.

El certificado VeriFactu del cliente

Cada NIF presenta a VeriFactu con su propio certificado. Un proveedor no firma en nombre de sus clientes, y no existe un modo de representación ni de apoderamiento: lo que puedes hacer con operate es subir el certificado del cliente, para que su NIF pueda emitir.

El certificado debe ser el certificado FNMT del propio cliente, en formato PKCS#12, y debe coincidir con el NIF al que se sube. Necesitas una clave cuyo nivel efectivo sobre ese NIF sea operate, con el scope verifactu:write, y el id del NIF, que sale de companies[].id de la cuenta gestionada. Con view la subida responde 403 company_read_only, y con none responde el mismo 404 que cualquier NIF que no puedes alcanzar.

POST /v1/companies/{company}/verifactu/certificates acepta multipart/form-data con certificate_file (.p12 o .pfx, hasta 100 KB) y certificate_password. Necesita una Idempotency-Key. El certificado subido se activa al instante y los anteriores se desactivan.

curl -X POST https://api.factuarea.com/v1/companies/$CLIENT_COMPANY_ID/verifactu/certificates \
  -H "Authorization: Bearer $FACTUAREA_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -F "certificate_file=@talleres-garcia.p12" \
  -F "certificate_password=$CERTIFICATE_PASSWORD"
{
  "data": {
    "id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a13",
    "object": "verifactu_certificate",
    "subject_nif": "B12345674",
    "subject_name": "Talleres García SL",
    "subject_kind": "juridica",
    "valid_from": "2026-01-01T00:00:00+01:00",
    "valid_to": "2029-01-01T00:00:00+01:00",
    "is_active": true,
    "revoked_at": null,
    "created_at": "2026-01-10T09:00:00+01:00"
  }
}

La subida se rechaza con 422 en estos casos, y error.param nombra el campo:

Código y subcodeparamCuándo
verifactu_not_eligible con certificate_nif_mismatchcertificate_fileEl NIF del certificado no es el de la empresa.
verifactu_not_eligible con certificate_expiredcertificate_fileEl certificado ha caducado o todavía no es válido.
business_rule_violation con invalid_certificate_passwordcertificate_passwordLa contraseña no abre el certificado.

Qué impide emitir a un NIF

GET /v1/accounts/{account}/companies/{company}/issuing-readiness responde si un NIF puede emitir ahora mismo, y por qué no cuando no puede. Funciona con el {account} de una cuenta gestionada y necesita companies:read. can_issue es el veredicto, y blockers es una lista cerrada de motivos, sin un orden concreto.

{
  "data": {
    "can_issue": false,
    "blockers": [
      "verifactu_certificate_missing"
    ],
    "census_status": "verified",
    "has_default_series": true,
    "fiscal_profile_complete": true,
    "operable": true,
    "missing_modules": [],
    "checked_at": "2026-09-30T10:15:00+00:00"
  }
}
BloqueoQué lo impide
tax_id_missingEl perfil de la empresa no tiene NIF.
tax_id_invalid_formatEl NIF está presente, pero no es un NIF, CIF o NIE válido.
census_not_identifiedLa verificación censal de la AEAT no identifica al titular del NIF.
census_revokedLa verificación censal de la AEAT da el NIF por revocado.
census_unavailableLa verificación censal no está disponible, así que no podemos afirmar que el NIF pueda emitir.
fiscal_profile_incompleteFaltan datos fiscales que la emisión exige: domicilio, razón social o régimen.
no_default_seriesEl NIF no tiene serie por defecto para facturas.
company_not_operableEl NIF está en tu ámbito, pero no es operable, por ejemplo porque está inactive.
module_unavailableNo se ha concedido un módulo que la emisión necesita. missing_modules lo nombra.
verifactu_certificate_missingVeriFactu está activo para el NIF y no tiene un certificado vigente: ninguno, revocado o caducado.

verifactu_certificate_missing es un valor de blockers, no un código de error: la operación responde 200 y el catálogo de códigos de error no tiene página para él. Se suma a lo que ya bloquee al NIF, así que un NIF puede llevar varios a la vez. Solo se aplica mientras VeriFactu esté activo para el NIF. Sube el certificado y vuelve a leer issuing-readiness: el bloqueo desaparece cuando el certificado es vigente.

La lista puede crecer con el tiempo. Trata un motivo que no conozcas como un bloqueo que no puedes resolver por la API. La lectura es neutra: mira el estado fiscal y los módulos, y nunca la suscripción, el plan ni los pagos.

Una cuenta de pruebas no tiene VeriFactu: el módulo no está disponible con una clave de pruebas. Probar cuentas gestionadas explica qué deja fuera el camino de pruebas.

Sigue una cuenta gestionada con eventos

Un endpoint de webhooks de cuenta de tu cuenta recibe los eventos de las cuentas gestionadas que aprovisionas cuando cumplen las reglas de entrega de esa guía: account.managed_account_provisioned, account.managed_account_claimed, account.management_ended y account.access_level_changed. Son la forma de saber, sin consultar de forma periódica, que un cliente reclamó su cuenta o que la gestión terminó.

Desde el servidor MCP

Diez tools MCP reflejan las operaciones del proveedor. Cada una necesita el scope de la operación que refleja, que es siempre privilegiado: usa una API key, porque OAuth no lleva accounts:*.

ToolScopeOperación
provision_managed_accountaccounts:writePOST /v1/accounts
list_managed_accountsaccounts:readGET /v1/accounts
get_managed_accountaccounts:readGET /v1/accounts/{account}
get_provisioning_usageaccounts:readGET /v1/accounts/{account}/provisioning-usage
reissue_managed_account_claimaccounts:writePOST /v1/accounts/{account}/claim-tokens
change_managed_account_access_levelaccounts:writePATCH /v1/accounts/{account}/access-level
end_account_managementaccounts:writeDELETE /v1/accounts/{account}/management
change_managed_account_planaccounts:writePATCH /v1/accounts/{account}/plan
preview_managed_account_plan_changeaccounts:readGET /v1/accounts/{account}/plan/preview
preview_managed_account_provisioningaccounts:readGET /v1/accounts/provisioning-preview

La importación masiva no tiene tool MCP: lee ficheros subidos, que el servidor MCP no transporta. Tools MCP lista el resto del catálogo.

Próximos pasos

En esta página

¿Te echamos una mano?Contactar con soporte