Factuarea API

AEAT census verification

Check your company's — and your clients' — name + NIF pair against the AEAT census before issuing VeriFactu invoices — deterministic states, sandbox magic NIFs and fail-open behavior.

Factuarea can check that your company's registered name + NIF pair is correctly identified in the AEAT census — the same identification the AEAT performs when it receives your VeriFactu invoice records. A pair that is not censused gets the submission rejected (AEAT error 4104, holder not identified), so verifying early — right after registration and whenever your fiscal data changes — saves you rejected submissions later.

POST /v1/account/census-verification
  • Scope: account:read
  • Request body: none — the check always runs against the persisted name and tax_id of the authenticated account. It never accepts an arbitrary NIF or name in the payload.
  • Side effect: the result is stored as a snapshot on your company (visible in the Factuarea app settings). Changing your company name or tax_id resets the snapshot until you verify again.

This is the verification Factuarea also offers in the app during onboarding. Negative results are informational: they never block registration, invoicing or any other operation — they just warn you that VeriFactu submissions may be rejected until the census data is fixed.

Calling the endpoint

curl -X POST https://api.factuarea.com/v1/account/census-verification \
  -H "Authorization: Bearer fact_test_3pXnR2VbY7TcA9eFmN5z8KqW" \
  -H "Idempotency-Key: $(uuidgen)"

Response (200):

{
  "data": {
    "object": "census_verification",
    "status": "identified",
    "verified_name": "ACME SOLUTIONS SL",
    "checked_at": "2026-06-10T22:15:04+00:00"
  }
}

verified_name is the company name that was contrasted against the census (the persisted one). checked_at is the moment of the verification, ISO 8601.

With the official SDKs:

import { Factuarea } from "@factuarea/sdk";

const factuarea = new Factuarea({ apiKey: process.env.FACTUAREA_API_KEY! });

const result = await factuarea.account.verifyCensus();
// result.data.status → "identified" | "not_identified" | …
<?php

use Factuarea\Sdk\Custom\FactuareaClient;

$factuarea = FactuareaClient::create(getenv('FACTUAREA_API_KEY'));

$response = $factuarea->account->publicApiV1AccountVerifyCensus();

Verify your clients too

The AEAT runs the same identification on the recipient of every VeriFactu invoice record: a client whose name + NIF pair is not censused gets the submission rejected with AEAT error 1239 (recipient not identified).

Factuarea deliberately does not validate clients against the census when you create them — and that is by design, not an oversight: foreign clients have no Spanish census entry, newly incorporated companies may take days to appear, simplified B2C invoices carry no recipient NIF at all, and the AEAT service itself can be down (the whole feature is fail-open). Blocking client creation on the census would break all those legitimate flows.

The recommended pattern instead: verify the name + NIF pair right before issuing VeriFactu invoices to that client with the dedicated endpoint:

POST /v1/clients/census-verification
  • Scope: clients:read
  • Request body: tax_id (NIF/CIF/NIE) and name — the pair is checked together, exactly as the AEAT will check it on submission. It doesn't need to match an existing client: the check is stateless and persists nothing on your clients.
  • Rate limit: 5 verifications per minute, like the account endpoint.
curl -X POST https://api.factuarea.com/v1/clients/census-verification \
  -H "Authorization: Bearer fact_live_3pXnR2VbY7TcA9eFmN5z8KqW" \
  -H "Content-Type: application/json" \
  -d '{"tax_id": "B12345674", "name": "CONSTRUCCIONES PEREZ SL"}'

Response (200):

{
  "data": {
    "object": "census_verification",
    "status": "identified",
    "verified_name": "CONSTRUCCIONES PEREZ SL",
    "checked_at": "2026-06-11T09:30:00Z"
  }
}

The status values are the same six as the account verification (table below). How to act on them for a client:

  • identified — issue normally.
  • not_identified — a VeriFactu submission to this recipient is guaranteed to be rejected with 1239. Ask the client for their exact registered name and NIF before issuing.
  • not_identified_similar — (individuals) use the exact name as registered with the AEAT.
  • unavailable — the AEAT could not answer; the check is informational, so you may issue anyway and retry the verification later.

If a rejected submission slips through anyway, all is not lost: subsanación lets you fix the data and resubmit the same record. Census verification up front plus subsanación as the safety net covers the whole 1239 lifecycle.

Possible states

status is always one of these six values:

statusMeaningWhat to do
identifiedThe name + NIF pair matches the census.Nothing — you are ready for VeriFactu.
not_identifiedThe NIF is not identified in the census with that name.Review your fiscal data: exact registered name and NIF. Submissions risk rejection.
not_identified_similar(Individuals only.) The NIF exists but the name only partially matches.Use the exact name as registered with the AEAT.
identified_inactiveThe NIF is identified but registered as inactive (baja) in the census.Check your census situation with the AEAT.
identified_revokedThe NIF is identified but has been revoked.Check your census situation with the AEAT.
unavailableThe AEAT service could not be reached or returned an unrecognized response.Retry later. This is not an error.

Branch on status, the values are a frozen contract. Note that negative states are not HTTP errors: every completed verification returns 200.

Fail-open by design

The verification never breaks your flow because the AEAT is down:

  • AEAT timeout, SOAP fault or unrecognized response → 200 with status: unavailable. Never a 5xx for this cause.
  • Results are cached server-side for a short period, so immediate retries of the same pair don't hit the AEAT again (unavailable is cached for only a few seconds so you can retry soon).

Errors

The only business error is a company without fiscal data:

{
  "error": {
    "type": "invalid_request_error",
    "code": "census_requires_tax_id",
    "message": "Configura primero los datos fiscales de tu empresa para verificar el censo.",
    "param": "tax_id"
  }
}
HTTPcodeWhen
401missing_api_key / invalid_api_keyMissing or invalid API key.
403insufficient_scopeThe key lacks the account:read scope.
422census_requires_tax_idThe account has no tax_id configured yet.
429rate_limit_exceededMore than 5 verifications per minute.

See the error envelope guide for the full error contract.

Rate limit

The endpoint is limited to 5 verifications per minute per account, independently of your key's global rate-limit tier. Above that you get a 429 — wait for the window to reset and retry.

Test mode: magic NIFs

With a fact_test_ key (test mode) the verification never reaches the AEAT. The sandbox returns deterministic states based on the sandbox company's tax_id, so you can exercise every branch of your integration:

Sandbox tax_idReturned status
00000000Tidentified
11111111Hnot_identified
22222222Jnot_identified_similar
33333333Pidentified_inactive
44444444Aidentified_revoked
55555555Kunavailable
any other NIFidentified (default)

All magic NIFs carry a valid control letter, so they pass standard NIF validation. Set the sandbox company's tax_id to the magic value you want to test, then call the endpoint with your fact_test_ key:

curl -X POST https://api.factuarea.com/v1/account/census-verification \
  -H "Authorization: Bearer fact_test_3pXnR2VbY7TcA9eFmN5z8KqW" \
  -H "Idempotency-Key: $(uuidgen)"
{
  "data": {
    "object": "census_verification",
    "status": "identified_revoked",
    "verified_name": "SANDBOX COMPANY SL",
    "checked_at": "2026-06-10T22:15:04+00:00"
  }
}

In live mode the real AEAT census is consulted with Factuarea's platform certificate. The states reflect the AEAT response verbatim — Factuarea never invents a state.

On this page