---
title: "Apply a bulk import of managed accounts"
method: POST
path: "/accounts/imports"
tags: ["Account"]
---

# Apply a bulk import of managed accounts

`POST /accounts/imports`

## Request

- Base URL: `https://api.factuarea.com/v1`
- URL: `https://api.factuarea.com/v1/accounts/imports`
- Auth: one of:
  - HTTP bearer
  - API key in header `X-API-Key`

Apply a bulk import of managed accounts and own tax IDs from files (`multipart/form-data`); returns 201.
- Declarative and NOT atomic: each row is applied on its own; failures come back as `BLOCKED` or `ERROR`.
- `Idempotency-Key` required; vouchers are returned EXACTLY ONCE, or emailed with `options.send_email`.
- Requires the privileged `accounts:write` scope.

## Headers

- `Idempotency-Key` string, required
- `Factuarea-Version` string, date

## Response `201`

- object
  - `data` ManagedAccountImport, required: Result of APPLYING a bulk import: every applicable row applied on its own (a row that fails does not undo the others), the statistics of the execution and, for every new live `managed_account` row, its claim voucher. The voucher (`claim_token`, `claim_url`) is returned ONCE, in this response only; it is `null` in every other row, when the link was emailed to the holder (`send_email: true`), for a test import and in an idempotent replay.
    - `id` string, uuid, required: Opaque identifier (UUID v7) of the execution.
    - `object` 'managed_account_import', required: Always `managed_account_import`.
    - `status` 'processing' | 'completed' | 'failed', required: Status of the execution. The response is returned once it ends: `completed`, or `failed` when it could not finish (the rows already applied stay applied).
    - `stats` object, required: Statistics of the execution: rows by status, accounts created and reconciled, customers queued and amounts in cents.
      - `total_rows` integer, required
      - `valid` integer, required
      - `warning` integer, required
      - `already_exists` integer, required
      - `blocked` integer, required
      - `error` integer, required
      - `created` integer, required: Accounts and tax IDs created.
      - `reconciled` integer, required: Existing accounts and tax IDs reconciled.
      - `customers_queued` integer, required: Customers queued for import.
      - `monthly_amount_cents` integer, required: Monthly net amount added to your subscription, in cents.
      - `proration_amount_cents` integer, required: Prorated net amount charged at once, in cents.
      - `tax_amount_cents` integer: VAT of the amount charged at once, in cents (already rounded per account). 0 in imports saved before VAT was charged and in test imports.
      - `total_amount_cents` integer: Amount actually charged at once with VAT (prorated amount plus `tax_amount_cents`), in cents. 0 in imports saved before VAT was charged and in test imports.
    - `rows` object[], required: Rows of the accounts file, in file order, with their claim voucher.
      - `line` integer, required: Line of the row in the accounts file.
      - `mode` 'own_nif' | 'managed_account' | 'null', nullable, required: `managed_account` provisions a separate account for a client; `own_nif` registers the tax ID as an additional tax ID of YOUR account. `null` when the row does not carry a valid `mode` (the row is `ERROR`).
      - `external_ref` string, nullable, required: Your own reference of the managed account (`managed_account` rows), unique per provider: it is what makes a new import of the same file reconcile instead of duplicating. `null` in `own_nif` rows.
      - `tax_id` string, nullable, required: Tax ID of the row as read from the file (one tax ID per row), or `null` when the row does not carry one.
      - `status` 'VALID' | 'WARNING' | 'ALREADY_EXISTS' | 'BLOCKED' | 'ERROR', required: `VALID` (it will be / was applied), `WARNING` (it will be / was applied with a remark in `code`), `ALREADY_EXISTS` (the account or the tax ID already exists: it is reconciled, never duplicated nor charged twice), `BLOCKED` (a business rule prevents applying it; the reason is in `code`) or `ERROR` (the row is wrong; the columns are in `errors`, or the reason in `code`). Only `VALID`, `WARNING` and `ALREADY_EXISTS` rows are applied.
      - `code` string, nullable, required: Reason of a `WARNING`, `BLOCKED` or `ERROR`, or `null`. Report codes: `managed_account_claimed`, `access_level_below_relation`, `plan_change_not_confirmed`, `plan_change_failed` (`WARNING`; an existing account whose profile was updated but whose confirmed plan change failed is `WARNING` with the reason of that failure in `errors.plan`); `managed_account_plan_not_self_serve`, `managed_account_provider_in_trial`, `managed_accounts_limit_reached`, `billing_not_current`, `payment_method_required`, `nif_transfer_required`, `additional_nif_not_in_plan`, `additional_nif_limit_reached`, `managed_account_external_ref_in_other_environment`, `managed_account_no_longer_managed`, `management_ended`, `access_level_raise_forbidden`, `access_level_above_relation`, `plan_change_not_paid_by_provider`, `plan_change_requires_operate`, `forbidden_action` (`BLOCKED`); `live_credential_required`, `insufficient_scope`, `row_apply_failed` (`ERROR`). A row the domain rejects while being applied is `BLOCKED` with the public code (or subcode) of that rejection, so treat unknown values as a `BLOCKED` reason. A column error is reported in `errors` (and its code in `error_codes`), with `code` `null`; so is a value the account or tax ID rejects while the row is applied (`ERROR`, message in `errors.row`), which never stops the rest of the import.
      - `errors` object, required: Errors by column: the key is the column of the file (or `row` for an error of the whole row) and the value its message, in Spanish. Always an object, `{}` when there is none.
      - `error_codes` object, required: Stable code of each error of `errors`, with the SAME keys (the column of the file, or `row` / `plan` for an error of the whole row or of its plan change): `required`, `invalid_mode`, `invalid_plan`, `plan_not_allowed`, `invalid_tax_id`, `invalid_email`, `invalid_language`, `invalid_access_level`, `too_long`, `too_short`, `duplicate_in_file`, `tax_id_mismatch`, `unknown_column`, `duplicate_column`, `unnamed_column` and `column_count_mismatch` (column errors), `invalid_value`, `row_rejected` and `row_apply_failed` (`row`), `plan_change_rejected` (`plan`; the concrete reason of `row_rejected` and `plan_change_rejected` is the row `code`). Language-independent: use it, not the Spanish message of `errors`, to react to or translate an error. Always an object, `{}` when there is none.
      - `plan` string, nullable, required: Plan of a `managed_account` row (`emprendedor`, `empresario` or `integral`), or `null`. An `own_nif` row inherits the plan of your account and carries no plan, and a row of status `ERROR` rejected while reading the file (it carries `errors` per column) carries `null` too; an `ERROR` raised while applying keeps the plan requested.
      - `monthly_amount_cents` integer, required: Monthly net amount the row adds to your subscription, in cents (0 when it is not charged: test import, row not applicable, already existing, trial).
      - `proration_amount_cents` integer, required: Net amount prorated to the end of your current billing period that applying the row charges at once, in cents.
      - `account_id` string, uuid, nullable, required: Opaque identifier (UUID v7) of the managed account the row created or reconciled, or `null`.
      - `customers_status` 'none' | 'queued' | 'blocked', required: Status of the customers of `customers_file` for this row: `none` (no customers for it), `queued` (queued for import into its tax ID) or `blocked` (reason in `customers_code`).
      - `customers_queued` integer, required: Customers queued for import into the tax ID of the row.
      - `name` string, nullable, required: Name of the tax ID read from the file, or `null` when the row carries none. The columns read from the file (`values`) are never published.
      - `company_id` string, uuid, nullable, required: Opaque identifier (UUID v7) of the tax ID the row registered or reconciled, only for `own_nif` rows (the tax ID of a `managed_account` row belongs to ANOTHER account, so it is not published); `null` otherwise.
      - `customers_code` 'nif_not_applied' | 'operate_access_required' | 'account_admin_required' | 'nif_not_in_key_scope' | 'insufficient_scope' | 'customers_import_failed' | 'null', nullable, required: Reason of `customers_status: blocked`: the row itself was not applied (`nif_not_applied`), your account does not `operate` the managed account, the issuer is not an owner or admin of your own tax ID, your own tax ID is outside the tax ID scope of the key and was not registered by this import (`nif_not_in_key_scope`), the key lacks `contacts:write`, or the contact importer failed.
      - `claim_token` string, nullable, required: Plaintext claim voucher of the managed account the row created, returned ONCE. Treat it as a credential. `null` when there is no voucher to deliver.
      - `claim_url` string, uri, nullable, required: Claim link carrying the voucher, to deliver to the holder yourself, returned ONCE. `null` when there is no voucher to deliver.
    - `own_company_customers` object, nullable, required: Outcome of the `apply_customers_to_own_company` option: the customers of `customers_file` WITHOUT `account_ref`, imported into your own company in focus (the only tax ID of the credential scope, or the only tax ID of your account). `null` when the option is off. `ERROR` `own_company_focus_required` when your account has several tax IDs and none is in focus; `ERROR` `insufficient_scope` without `contacts:write`; `ERROR` `live_credential_required` with a test key; `BLOCKED` `account_admin_required` or `operate_access_required` when the person who issued the key cannot write in that company; `BLOCKED` `nif_not_in_key_scope` when that company is outside the tax ID scope of the key; `BLOCKED` `customers_import_failed` when the contact importer failed. `unmatched_customers` counts the customers whose `account_ref` matches no row of `accounts_file`: they are imported nowhere.
      - `status` 'VALID' | 'BLOCKED' | 'ERROR', required: `VALID` (customers queued), `BLOCKED` or `ERROR`.
      - `code` 'own_company_focus_required' | 'insufficient_scope' | 'live_credential_required' | 'account_admin_required' | 'operate_access_required' | 'nif_not_in_key_scope' | 'customers_import_failed' | 'null', nullable, required: Reason of a `BLOCKED` or `ERROR`, or `null`.
      - `customers_queued` integer, required: Customers queued for import into your own company.
      - `unmatched_customers` integer, required: Customers of `customers_file` whose `account_ref` matches no row of `accounts_file`, so they are not imported anywhere.

## Other responses

- `400`: `parameter_unknown` with `subcode` `retired_active_profile_header`: the retired `X-Active-Profile` header was sent; the company goes in the path. `parameter_invalid_format` on `Factuarea-Version`: the header is not a `YYYY-MM-DD` date. `unsupported_api_version`: the `Factuarea-Version` date is not a supported version.
- `401`: Missing or invalid API key.
- `403`: The request is authenticated but not allowed: the API key lacks the required scope (`insufficient_scope`), the plan does not include the public API or this feature (`addon_not_active`, `feature_not_available_in_plan`), or the module is not available with a test key (`module_not_available_in_sandbox`).
- `409`: The request conflicts with data that already exists or with a request running at the same time. `import_in_progress`: either another application of a bulk import of your account is still running (imports of the same provider are applied one at a time) or the platform is already applying the maximum number of simultaneous imports across all providers, in which case the `message` says so. Nothing was applied and no daily quota was spent. Wait for it to end and send the same files again with a NEW `Idempotency-Key`: rows the first import already applied come back as `ALREADY_EXISTS`, never duplicated nor charged twice.
- `413`: One of the uploaded files is larger than 5 MB, so nothing was read, classified or applied. The catalog code is `import_file_too_large` with `error.type: invalid_request_error`, and `error.param` names the offending file (`accounts_file` or `customers_file`). Split the file into several imports and send them one after another. The whole multipart request is also capped at 11 MB; above that the transport rejects it first with `payload_too_large`, before any file is inspected.
- `422`: Validation failed, or a managed account rule rejected the operation: `managed_account_plan_not_self_serve` (`enterprise` is only assigned by Factuarea by contract), `managed_account_provider_in_trial` (a paid plan while your account is in trial), `managed_accounts_limit_reached` (the managed account limit of your account), `managed_account_not_claimable` (a test account has no claim voucher), `access_level_raise_forbidden` (once the holder claimed the account only the holder can raise your level), `enterprise_contract_transition_required` (moving to or from `enterprise` is a contract change) and `business_rule_violation` with subcode `nif_already_claimed_by_another_account` (provisioning a tax ID that already belongs to another account). The `error.param` field identifies which input is invalid, if any. `idempotency_key_reused`: the `Idempotency-Key` was already used with a different body; send a new key or the same body. `invalid_param_format` on `Idempotency-Key`: the key must be 1 to 255 printable ASCII characters. `idempotency_key_required`: this operation requires the `Idempotency-Key` header; the message names the rejected operation.
- `429`: Rate limit exceeded. Retry after the duration in `Retry-After`.
- `500`: Unexpected server error.

---

[API](https://skmtc.dev/factuarea/apis/factuarea-public-api.md) · [All operations](https://skmtc.dev/factuarea/apis/factuarea-public-api/llms.txt) · [OpenAPI document](https://skmtc.dev/factuarea/apis/factuarea-public-api/revisions/718e77934422?raw)
