AccountImports

Import managed accounts from a file

Provisions the managed accounts described in an uploaded file, switches each one on in Live, and leaves them ready to invoice. It is the same act as calling POST /v1/accounts once per row and then POST /v1/companies/{company_id}/activations, with the bookkeeping done for you.

Idempotency and re-runs

  • Not atomic: each row is processed and reported independently, and a row that fails leaves the rows already provisioned in place. statistics.accounts_created is how many accounts this call actually created.
  • Declarative and re-runnable: each pass applies only what is missing — an external_ref you already provisioned is reconciled, not duplicated, and so are its series and its customers. That is the recovery path for anything that went wrong: fix the cause and upload the same file again; there is no resume and no partial state to clean up.
  • Idempotency-Key: required, but the real guarantee is in the data. Rows are idempotent by external_ref, so the same file uploaded twice creates nothing twice even under a different key.
  • Dry run: to see what this would do without writing anything, use POST /v1/accounts/imports/preview, a separate operation with no effects at all — the import is never governed by a boolean flag.

Files and limits

  • accounts_file: describes the accounts, one per row.
  • customers_file: optional, and holds a list of customers applied to every account of the import, new and pre-existing alike, so a new managed account is born knowing all the customers and a new customer reaches all the accounts on the next pass. It is the same CSV that GET /v1/templates/customer-import describes, and it is idempotent by tax id.
  • options.apply_customers_to_own_company: lands those customers on your own company as well — the one in focus, never one chosen for you. That outcome comes back apart, in own_company_customers, and stays out of statistics.customers_created.
  • Limits: 5 MB per file, 100 rows in the accounts file and 1,000 in the customers file. A larger population is imported in passes, which costs nothing because the file is declarative.

Live activation

Live activation is part of the act: every row is weighed against the same verdict the account state publishes, and only rows entitled to Live are executed; the rest come back BLOCKED with the reason. The import never opens a checkout, so it never charges you by surprise: settle your billing once and re-upload.

If your agreement caps the NIFs you can have in Live, each row switched on takes a place, in file order; the rows that no longer fit come back BLOCKED with NIF_LIMIT_REACHED, the same code POST /v1/companies/{company_id}/activations answers with.

Claim tokens

account.claim_token and account.claim_url: each newly provisioned row carries them in this response and nowhere else, so persist them before discarding it. A lost token is re-issued with POST /v1/accounts/{account_id}/claim-tokens.

Re-uploading the file never breaks the links you already handed out: a row whose account is still unclaimed and holds a valid claim link comes back with claim_token and claim_url null and account.claim_link_already_issued: true. Only an account with no valid link left (expired, or never issued) gets a fresh one.

post/v1/accounts/imports

Request

  • Base URL: https://app.beel.es/api
  • URL: https://app.beel.es/api/v1/accounts/imports
  • Auth: HTTP bearer

Headers

Idempotency-Keystring required

Same key as Idempotency-Key above, but required: the operation writes many rows per call, so a retry without a key would import the same file twice. A missing key answers 400 IDEMPOTENCY_KEY_REQUIRED.

Response

The import ran, in the same format as the preview. The status is fixed: it reports that the import ran, not that any particular row succeeded. metadata.is_dry_run is false, and the write counters in statistics are what actually reached the database — they can all be 0 when every row was already there or blocked.

successboolean required

Example response

{
  "success": true,
  "data": {
    "metadata": {
      "total_rows": 19,
      "processing_time_ms": 18400,
      "environment": "PROD"
    },
    "accounts_validation": [
      {
        "row_number": 2,
        "status": "VALID",
        "live_activation_verdict": "CHECKOUT_REQUIRED",
        "customers": {
          "created": 3,
          "already_existed": 37
        },
        "errors": [
          {
            "code": "LIVE_ACTIVATION_NOT_ENTITLED",
            "column": "direccion_codigo_postal"
          }
        ],
        "warnings": [
          {
            "code": "LIVE_ACTIVATION_NOT_ENTITLED",
            "column": "direccion_codigo_postal"
          }
        ]
      }
    ],
    "customers_source": {
      "total_rows": 42,
      "valid": 40,
      "invalid": 2,
      "rejected": [
        {
          "errors": [
            {
              "code": "LIVE_ACTIVATION_NOT_ENTITLED",
              "column": "direccion_codigo_postal"
            }
          ]
        }
      ]
    },
    "own_company_customers": {
      "created": 3,
      "already_existed": 37
    },
    "statistics": {
      "total_rows": 19,
      "valid": 11,
      "with_warnings": 1,
      "already_existed": 6,
      "blocked": 1,
      "importable": 12,
      "accounts_created": 12,
      "live_activations_created": 12,
      "series_created": 24,
      "customers_created": 24
    }
  },
  "meta": {
    "timestamp": "2025-01-15T10:30:00Z",
    "request_id": "4bf92f3577b34da6a3ce929d0e0e4736"
  }
}

Changes

No recorded changes to this operation across all 1 revision of this API.