---
title: "Bulk import IdP users (Google Admin user export)"
method: POST
path: "/organizations/{organizationId}/idp-users/import"
tags: ["Organizations"]
---

# Bulk import IdP users (Google Admin user export)

`POST /organizations/{organizationId}/idp-users/import`

## Request

- Base URL: `https://api.xfa.tech`
- URL: `https://api.xfa.tech/organizations/{organizationId}/idp-users/import`
- Auth: HTTP bearer

Creates or updates up to 500 IdP users (email + recovery email) in one request; created users get source "import". Emails are trimmed and lower-cased. Rows whose email domain is not on the organization's IdP connection allow-list (exact, case-insensitive), malformed or repeated rows are reported as invalid and never written. Each row is enriched with the current recovery email, whether the user already enrolled, and when a link was last sent. Imported users count as users: an import that would take the organization past its user limit (trial limit, or licensed seats) is rejected with 409 user_limit_exceeded before anything is written; a dry run instead returns 200 with every row, the seats and exceeded=true. When the limit cannot be checked (billing settings unavailable, or a known limit with the current users unavailable) the request fails closed with 503 seat_check_unavailable, dry runs included. With dry_run nothing is written and the response previews the outcome. Requires an organization administrator.

## Path parameters

- `organizationId` string, required

## Request body

- IdpUsersImportRequest
  - `dry_run` boolean: Preview only: validate, enrich and count seats without writing
  - `users` object[], required
    - `email` string, required: The IdP sign-in email (trimmed and lower-cased)
    - `recovery_email` string, required: Where the onboarding link is emailed. May equal email.

## Response `200`

Per-row results (or preview) and the seat summary

- IdpUsersImportResponse
  - `dry_run` boolean, required
  - `results` IdpUsersImportRow[], required
    - `index` integer, required: Position of the user in the request
    - `email` string, required: Normalized email
    - `recovery_email` string, required: Normalized recovery email
    - `status` 'created' | 'updated' | 'unchanged' | 'invalid', required: Outcome; in a dry run, what the import would do
    - `error` 'missing_email' | 'invalid_email' | 'missing_recovery_email' | 'invalid_recovery_email' | 'duplicate_email' | 'domain_not_allowed' | 'rejected': Why an invalid row is not written. rejected = refused by the organizations service, see error_message.
    - `error_message` string
    - `current_recovery_email` string: Recovery email before this import; omitted for new users
    - `enrolled` boolean, nullable, required: Whether the user already has a reusable MFA factor; null when unknown or invalid
    - `link_sent_at` string, date-time: When an onboarding link was last emailed; omitted if never
  - `created` integer, required
  - `updated` integer, required
  - `unchanged` integer, required
  - `invalid` integer, required
  - `seats` IdpUsersSeatSummary, required: Seat impact of an import. Imported users count as users. limit is null when no cap applies; current/adding are null when they could not be computed for an organization without a cap.
    - `current` integer, nullable, required
    - `adding` integer, nullable, required
    - `limit` integer, nullable, required
  - `exceeded` boolean, required: Whether the import would take the organization past its user limit. Only a dry run returns true (with every row and seats); a real run over the limit gets 409 user_limit_exceeded instead.

## Other responses

- `400`: invalid_json, no_users or too_many_users
- `403`: Not authorized
- `404`: organization_not_found
- `409`: idp_connection_required, multiple_idp_connections, or (real runs only) user_limit_exceeded with current, adding and limit
- `503`: seat_check_unavailable: the user limit could not be checked; try again later

---

[API](https://skmtc.dev/xfa/apis/xfa-api.md) · [All operations](https://skmtc.dev/xfa/apis/xfa-api/llms.txt) · [OpenAPI document](https://skmtc.dev/xfa/apis/xfa-api/revisions/1a1be0e8972b?raw)
