Empresas gestionadas
Da de alta, aprovisiona y opera empresas hijas bajo tu tenant maestro — el modelo de gestoría desde la API v1, con cobro per-seat y un ciclo de vida activa/desactivada.
Una empresa gestionada es una subcuenta hija que creas y operas bajo tu propio tenant maestro. Es el modelo de gestoría: una asesoría (la maestra) mantiene un único juego de credenciales y, mediante ellas, da de alta y gestiona muchas empresas clientes, cada una aislada de las demás.
Aprovisionas cada empresa hija y luego la manejas de dos formas: emites una API
key hija acotada a ella, o mantienes tu master key y
cambias de empresa objetivo por petición con el header
X-Active-Profile. Esta página cubre las empresas en
sí — crearlas, aprovisionarlas, su ciclo de vida activa/desactivada, el cobro de
asientos y el archivado.
Once endpoints bajo /v1/companies gestionan las empresas.
| Operación | Endpoint | Scope |
|---|---|---|
| Listar empresas | GET /v1/companies | companies:read |
| Crear una empresa | POST /v1/companies | companies:write |
| Recuperar una empresa | GET /v1/companies/{id} | companies:read |
| Actualizar una empresa | PATCH /v1/companies/{id} | companies:write |
| Archivar una empresa | DELETE /v1/companies/{id} | companies:delete |
| Consultar el estado de creación | GET /v1/companies/{id}/creation-status | companies:read |
| Verificar (reconciliar) la creación | POST /v1/companies/{id}/verify-creation | companies:write |
| Desactivar una empresa | POST /v1/companies/{id}/deactivate | companies:write |
| Reactivar una empresa | POST /v1/companies/{id}/activate | companies:write |
| Activar empresas en bloque | POST /v1/companies/activate | companies:write |
| Previsualizar el cobro del asiento | GET /v1/companies/seat-charge-preview | companies:read |
{id} es el id de la empresa — un UUID v7 opaco, no su tax_id. Mira los
esquemas completos en la
Referencia de la API.
Crea una empresa gestionada
POST /v1/companies da de alta una nueva empresa hija bajo tu tenant maestro.
name y tax_id son los únicos campos obligatorios; el resto del perfil
(razón social, dirección fiscal, datos de contacto) es opcional y se puede enviar
en la misma petición. El tax_id (NIF / CIF / NIE) debe ser único entre las
empresas que ya gestionas; un duplicado devuelve 409. Requiere el scope
companies:write.
curl -X POST https://api.factuarea.com/v1/companies \
-H "Authorization: Bearer fact_live_8KqW3pXnR2VbY7TcA9eFmN5z" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"name": "Talleres García SL",
"tax_id": "B12345678"
}'Respuesta (201):
{
"data": {
"object": "company",
"id": "01931b3e-7c4a-7f2e-9a8b-4d6e7f8a9b0c",
"name": "Talleres García SL",
"business_name": "Talleres García, Sociedad Limitada",
"tax_id": "B12345678",
"status": "active",
"address": "Calle Mayor 1",
"city": "Madrid",
"postal_code": "28013",
"province": "Madrid",
"country_aeat_zone": "peninsula",
"email": "contacto@talleresgarcia.es",
"phone": null,
"logo_url": null,
"created_at": "2026-01-15T09:30:00+00:00",
"updated_at": null
}
}Cuerpo de la petición
| Campo | Obligatorio | Notas |
|---|---|---|
name | sí | Nombre comercial (1–255 caracteres). |
tax_id | sí | Identificador fiscal español (NIF / CIF / NIE). Inmutable tras el alta. |
business_name | no | Razón social, hasta 100 caracteres. |
address | no | Dirección fiscal. |
city | no | Ciudad de la sede fiscal. |
postal_code | no | Código postal — de él se deriva la zona AEAT (country_aeat_zone en las respuestas). |
province | no | Provincia. |
country | no | País. |
email | no | Email de contacto. |
phone | no | Teléfono de contacto. |
El campo status de la respuesta es el ciclo de vida
activa/desactivada de la empresa, un eje distinto del
provisioning_status que sigue el aprovisionamiento asíncrono.
No hay campo de entrada country_aeat_zone — envías un country de texto libre,
y la zona AEAT se deriva del postal_code.
En el alta solo se aplica validación a nivel de formulario. La comprobación censal con la AEAT es un paso de aprovisionamiento aparte — dar de alta una empresa aquí no la verifica contra el censo en la misma petición.
Identificador fiscal duplicado
Reutilizar un tax_id que ya gestionas devuelve 409 con
resource_already_exists, y existing_resource_id apunta a la empresa que ya lo
tiene:
{
"error": {
"type": "invalid_request_error",
"code": "resource_already_exists",
"message": "Ya gestionas una empresa con este NIF.",
"param": "tax_id",
"existing_resource_id": "01931b3e-7c4a-7f2e-9a8b-4d6e7f8a9b0c"
}
}El tax_id es único por tenant maestro, no a nivel global: dos gestorías
distintas pueden gestionar cada una una empresa con el mismo identificador
fiscal.
Ciclo de vida del aprovisionamiento
Dar de alta una empresa de facturación real no es instantáneo.
POST /v1/companies responde al momento, pero detrás la empresa hija se
aprovisiona de forma asíncrona: se crean una serie de documentos por defecto y
la config fiscal mínima y — en live — se cobra a la gestoría el nuevo asiento.
Hasta que eso termina, la hija aún no es operativa. Dos endpoints exponen el ciclo:
uno para consultarlo y otro para reconciliarlo.
| Operación | Endpoint | Scope |
|---|---|---|
| Consultar el estado de creación | GET /v1/companies/{id}/creation-status | companies:read |
| Verificar (reconciliar) la creación | POST /v1/companies/{id}/verify-creation | companies:write |
Estados de aprovisionamiento
provisioning_status recorre un ciclo de vida pequeño y de un solo sentido:
| Estado | Significado |
|---|---|
pending | La hija se dio de alta; el aprovisionamiento aún no ha empezado. |
awaiting_payment | La gestoría no tiene un método de pago registrado, así que el asiento todavía no se puede cobrar. payment_setup_url apunta a donde el tenant maestro añade uno. |
provisioning | El asiento se cobró (o la hija está en modo de prueba) y el tenant se está configurando. |
active | El aprovisionamiento terminó. La hija es plenamente operativa. |
failed | El aprovisionamiento no pudo completarse — failed_reason indica el motivo. Vuelve a crear la empresa para reintentarlo. |
Con una clave de prueba (fact_test_) no hay llamada a Stripe: la hija pasa
directamente a active, de forma determinista. El camino de awaiting_payment y
el cobro per-seat solo aplican a las claves live.
Cobro per-seat
En live, cada empresa hija activa es un asiento que se cobra dentro de la
suscripción ya existente del tenant maestro — una sola factura recurrente que se lee
como "plan + N clientes". Añadir una hija añade un asiento y cobra el prorrateo de
inmediato por lo que queda del periodo de facturación; archivar una hija quita el
asiento y abona el tiempo no usado en la siguiente factura. La hija no llega a
active hasta que ese cobro inmediato tiene éxito; si falla, la hija acaba en
failed. Previsualiza el importe de antemano con el preview del
asiento.
Como el asiento vive en la propia suscripción del tenant maestro, la hija hereda el plan y los add-ons del maestro, y un impago de la suscripción del maestro suspende la cuenta entera de la gestoría — sus hijas incluidas. No hay una factura separada por hija.
Consulta el estado de creación
GET /v1/companies/{id}/creation-status devuelve el provisioning_status actual y
las marcas de tiempo. Consúltalo tras crear una empresa hasta que llegue a active
(o failed). Requiere el scope companies:read.
curl https://api.factuarea.com/v1/companies/01931b3e-7c4a-7f2e-9a8b-4d6e7f8a9b0c/creation-status \
-H "Authorization: Bearer fact_live_8KqW3pXnR2VbY7TcA9eFmN5z"Respuesta (200):
{
"data": {
"object": "company_creation_status",
"id": "01931b3e-7c4a-7f2e-9a8b-4d6e7f8a9b0c",
"provisioning_status": "active",
"payment_setup_url": null,
"failed_reason": null,
"started_at": "2026-01-15T09:30:00+00:00",
"completed_at": "2026-01-15T09:31:00+00:00"
}
}payment_setup_url está presente solo mientras awaiting_payment, y
failed_reason solo cuando failed; ambos son null en los demás casos.
Verifica la creación
POST /v1/companies/{id}/verify-creation reconcilia una hija contra la suscripción
del tenant maestro y la hace avanzar cuando puede. No lleva cuerpo de petición y
es idempotente. Requiere el scope companies:write.
curl -X POST \
https://api.factuarea.com/v1/companies/01931b3e-7c4a-7f2e-9a8b-4d6e7f8a9b0c/verify-creation \
-H "Authorization: Bearer fact_live_8KqW3pXnR2VbY7TcA9eFmN5z"Llámalo una vez el tenant maestro haya añadido un método de pago, o cuando quieras
sacar a una hija de awaiting_payment:
- Una hija que ya está
activees una operación sin efecto — la llamada se puede repetir sin riesgo. - Mientras
awaiting_payment, si el maestro ya tiene un método de pago, se cobra el asiento prorrateado y la hija pasa aactive. - Si el maestro aún no tiene método de pago, la llamada no tiene efecto ni error
— la hija permanece en
awaiting_payment.
Devuelve el mismo recurso de creation-status que el endpoint de consulta, así que
puedes leer el provisioning_status resultante directamente de la respuesta.
El ciclo de vida activa/desactivada
Aparte del aprovisionamiento, cada hija lleva un status — su ciclo de vida del
vínculo dentro de la gestoría. Es el campo status del recurso de empresa, y
recorre tres estados:
| Estado | Significado |
|---|---|
active | La hija está vinculada y operativa. |
inactive | La hija está desactivada — inaccesible hasta que la reactives (pagando de nuevo su asiento), pero con sus datos intactos y de forma reversible. |
archived | La hija ha sido desvinculada. Es un estado terminal. |
Las transiciones son active ↔ inactive (desactivar / reactivar) y active → archived
o inactive → archived (archivar). archived es terminal.
Desactivar libera el asiento; reactivar lo cobra de nuevo. Esto permite a una gestoría aparcar un cliente entre encargos sin perder su historial, y recuperarlo más tarde.
Desactiva una empresa
POST /v1/companies/{id}/deactivate pasa una hija active a inactive. La
empresa queda inaccesible pero conserva todos sus datos, de forma reversible.
No cobra: el abono prorrateado del asiento liberado se aplica best-effort en la
siguiente factura. Requiere el scope companies:write.
curl -X POST \
https://api.factuarea.com/v1/companies/01931b3e-7c4a-7f2e-9a8b-4d6e7f8a9b0c/deactivate \
-H "Authorization: Bearer fact_live_8KqW3pXnR2VbY7TcA9eFmN5z"Devuelve el recurso de empresa actualizado con status: "inactive".
Reactiva una empresa
POST /v1/companies/{id}/activate devuelve una hija inactive a active. La
reactivación está gateada por un cobro atómico del asiento: primero se cobra el
prorrateo, y solo si el cobro tiene éxito la hija pasa a active. Si el maestro no
tiene método de pago, o el cobro falla, la empresa sigue inactive. Requiere el
scope companies:write.
curl -X POST \
https://api.factuarea.com/v1/companies/01931b3e-7c4a-7f2e-9a8b-4d6e7f8a9b0c/activate \
-H "Authorization: Bearer fact_live_8KqW3pXnR2VbY7TcA9eFmN5z"Devuelve el recurso de empresa actualizado con status: "active".
Activa empresas en bloque
POST /v1/companies/activate reactiva varias hijas en una sola llamada, con un
único cobro conjunto — una factura para todo el lote en vez de una por
empresa. El cuerpo lleva company_ids, una lista de valores id de empresa hija
(1–1000). Requiere el scope companies:write.
curl -X POST https://api.factuarea.com/v1/companies/activate \
-H "Authorization: Bearer fact_live_8KqW3pXnR2VbY7TcA9eFmN5z" \
-H "Content-Type: application/json" \
-d '{
"company_ids": [
"01931b3e-7c4a-7f2e-9a8b-4d6e7f8a9b0c",
"01931b3e-8d5b-7a1f-9c2d-5e6f7a8b9c0d"
]
}'El cobro es atómico a nivel de lote — o todas o ninguna. La propiedad
(404 para una empresa fuera de tu árbol) y la precondición inactive (422)
se validan para cada empresa antes de que corra ningún cobro. La respuesta es
la lista de empresas reactivadas ({ "data": [ … ] }).
Previsualiza el cobro del asiento
GET /v1/companies/seat-charge-preview devuelve lo que costaría añadir o
reactivar empresas hijas, sin cobrar nada. Úsalo para mostrar el prorrateo antes
de un POST /v1/companies o de una activación, y para detectar de antemano el
caso "sin método de pago". Requiere el scope companies:read.
Tiene dos modos:
count(≥1, default 1) — previsualiza el prorrateo conjunto de activar ese número de hijas en un lote.company_ids— una lista de valoresidde hijas concretas, para un preview consciente de la cobertura: el importe es0conalready_covered: truecuando todas siguen cubiertas este periodo, y en caso contrario prorratea solo las no cubiertas. Cuando se envía, manda sobrecount.
curl "https://api.factuarea.com/v1/companies/seat-charge-preview?count=1" \
-H "Authorization: Bearer fact_live_8KqW3pXnR2VbY7TcA9eFmN5z"Respuesta (200):
{
"data": {
"object": "seat_charge_preview",
"amount": 1240,
"tax_amount": 260,
"total": 1500,
"tax_rate": 21,
"currency": "EUR",
"next_invoice_date": "2026-02-01",
"requires_payment_method": false,
"requires_active_plan": false,
"included_in_trial": false,
"already_covered": false,
"is_first_seat": false,
"recurring_quantity": 4,
"recurring_base_cents": 4000,
"recurring_total_cents": 4840
}
}amount es la base imponible del prorrateo en unidades mínimas de la moneda
(céntimos), tax_amount el IVA, y total (amount + tax_amount) lo que se cobra
realmente. tax_rate es el porcentaje de IVA derivado (p. ej. 21) o null si
Stripe Tax no lo calculó. Los campos recurring_* proyectan la cuota mensual
conjunta tras la activación: número total de asientos, base sin IVA y total con
IVA (recurring_total_cents es null cuando el IVA no es calculable).
Cuatro flags mutuamente excluyentes explican un importe 0, por orden de
prioridad:
| Flag | El amount es 0 porque… |
|---|---|
already_covered | Las empresas que activarías ya están incluidas en la suscripción de este periodo — reactivarlas es gratis. |
requires_active_plan | La gestoría no tiene plan vigente y debe contratar uno antes de gestionar empresas. |
included_in_trial | La gestoría está en su periodo de prueba — la empresa se crea gratis (los asientos empiezan a cobrarse cuando el trial se convierte en plan de pago). |
requires_payment_method | La gestoría tiene un plan de pago pero ningún método de pago registrado, y debe añadir uno (Billing Portal) primero. |
is_first_seat es true cuando la activación crea la primera suscripción de
asientos del maestro: el cargo es un mes completo y hoy ancla el día de cobro
mensual del ciclo conjunto.
Lista y recupera empresas
GET /v1/companies devuelve tus empresas gestionadas con paginación por
cursor; GET /v1/companies/{id} devuelve una. Ambas están
acotadas a tu tenant maestro.
curl https://api.factuarea.com/v1/companies?limit=25 \
-H "Authorization: Bearer fact_live_8KqW3pXnR2VbY7TcA9eFmN5z"Una empresa gestionada por un tenant maestro distinto devuelve 404, nunca
403 — la API no revela jamás que existe una empresa que no puedes gestionar.
Actualiza una empresa
PATCH /v1/companies/{id} es una actualización parcial. El único campo
editable es name — el perfil (razón social, dirección fiscal, contacto, zona
AEAT) no es editable aquí, y el tax_id es inmutable y se rechaza si lo
incluyes en el cuerpo. Una empresa debe estar active para editarse. Requiere el
scope companies:write.
curl -X PATCH \
https://api.factuarea.com/v1/companies/01931b3e-7c4a-7f2e-9a8b-4d6e7f8a9b0c \
-H "Authorization: Bearer fact_live_8KqW3pXnR2VbY7TcA9eFmN5z" \
-H "Content-Type: application/json" \
-d '{ "name": "Talleres García e Hijos SL" }'Archiva una empresa
DELETE /v1/companies/{id} archiva la empresa en lugar de borrarla: su
status pasa a archived y deja de aceptar operaciones. Una empresa active o
inactive se puede archivar; archived es terminal. Requiere el scope
companies:delete.
curl -X DELETE \
https://api.factuarea.com/v1/companies/01931b3e-7c4a-7f2e-9a8b-4d6e7f8a9b0c \
-H "Authorization: Bearer fact_live_8KqW3pXnR2VbY7TcA9eFmN5z"El archivado puede quedar bloqueado: si la empresa todavía tiene estado que
lo impide (por ejemplo documentos pendientes), la petición devuelve 422 y la
empresa conserva su estado actual. Resuelve antes la condición que lo bloquea y
luego archívala.
Scopes y aislamiento
Las empresas se protegen con sus propios scopes:
companies:read— listar y recuperar empresas gestionadas, consultar el estado de creación y previsualizar el cobro del asiento.companies:write— crear, actualizar, activar y desactivar empresas gestionadas, y verificar su creación.companies:delete— archivar empresas gestionadas.
Toda operación está acotada a tu tenant maestro. Un id de empresa que pertenece
a otro maestro devuelve 404 — nunca 403. Este aislamiento entre maestros es la
garantía central del modelo de gestoría: un maestro solo ve y actúa sobre sus
propias empresas. La misma garantía rige actuar en nombre de una
hija y sus API keys.