Provisioning organizations & accounts at scale¶
When you onboard a large customer — thousands of OUs and member accounts in one go — call the bulk and paginated APIs below rather than looping the single-entity verbs. They are idempotent, apply backpressure, and come with published latency targets you can budget retries against.
All paths are under the public gateway at https://api.flexgalaxy.ai, signed with
FGAI-HMAC-SHA256 (see Integrate an external app with AK/SK).
Management-account context is supplied with the X-Account-Id header. The wire
is snake_case.
Create OUs in bulk¶
POST /organizations/v1/{org}/ous/batch — synchronous, up to 500 OUs per
call.
Items are topologically sorted, so you can create a whole subtree in one request and reference a not-yet-created parent by
parent_ref.Each item commits in its own transaction: one bad item (name collision, quota trip, bad parent) is rejected on its own and never discards the rest. You get a per-item accepted/rejected result set.
Requires an
Idempotency-Keyheader. Re-POSTing the same key replays the exact same result instead of creating duplicates.
Import member accounts in bulk¶
POST /accounts/v1/provision/batch — asynchronous, up to 1000 accounts
per call.
Up-front rejects (quota headroom, duplicate email, unknown OU) come back immediately in the response.
Everything accepted is enqueued on a durable relay and returns a
batch_id.Poll
GET /accounts/v1/provision/batch/{batchId}for{total, pending, completed, failed, items[]}.Also
Idempotency-Key-guarded.
Account provisioning is asynchronous by design — each account is stood up in
the background (identity realm, root user, org-access role). A 202/enqueue is
fast; the account becomes live a few seconds later. Drive large onboards as a
sustained background import, not a single burst: submit, poll the batch, and
honor Retry-After (below). Reconcile any in-flight single-provision work with
GET /accounts/v1/provision/jobs.
Enterprise onboarding — placeholder accounts & lazy materialization¶
For an enterprise import where most accounts will not be used immediately (a franchise rolling out thousands of locations, say), use the onboarding path instead of provisioning every account up front. It creates cheap placeholder accounts now and defers the expensive per-account setup — identity realm, authz projection — to first use.
POST /organizations/v1/{org}/onboarding/accounts — synchronous 202, bulk
placeholder create.
{ "ou_node_id": "<ou>", "accounts": [ { "name": "warehouse-01" }, { "name": "warehouse-02" } ] }
Each returned account is { "status": "PLACEHOLDER", "realm_status": "NONE" } — a
row, nothing more. A 20K-account import is just that many rows and stays fast.
Accounts materialize lazily on first use:
Call |
Transition |
|---|---|
|
first operator grant → |
|
first service user → materializes the account realm |
Teardown mirrors it:
Call |
Effect |
|---|---|
|
transition to terminal |
|
|
Read the roster with the lifecycle view, optionally filtered by status:
GET /organizations/v1/{org}/account-lifecycle?status=PLACEHOLDER|MATERIALIZING|ACTIVE|CLOSED
The status values, the full lifecycle row shape and the filter semantics are in Account lifecycle status.
All of the above is available on the CLI as fgai org onboard-accounts,
onboard-grant-operator, onboard-provision-service-user, onboard-close,
onboard-purge, and account-lifecycle (see
CLI reference).
Read without pulling the whole forest¶
No endpoint returns the entire org tree. Page and scope your reads:
GET /organizations/v1/{org}/ous?page=&size=→ a page ofOuSummary(sizecapped at 200). Each item carrieschild_count/has_children, so you can render a lazy tree without pre-fetching descendants. Add?parentId=<ou>to fetch just one OU’s direct children.GET /organizations/v1/{org}/accounts?page=&size=andGET /organizations/v1/{org}/ous/{ou}/accounts?page=&size=→ paginated (the OU filter runs in the database).GET /organizations/v1/{org}/tree?rootOuId=<ou>&depth=<n>→ a subtree, depth-bounded. Walk the tree branch-by-branch rather than loading every node at once.
Backpressure & rate limits¶
Backpressure. When the provisioning backlog is over its high-water mark,
POST /accounts/v1/provisionand/provision/batchreturn429with aRetry-Afterheader and a structured body (error.code = "provisioning-backpressure", withdetails.backlog / highWaterMark / retryAfterSeconds). Batches are rejected whole — never partially enqueued — so a retry is clean.Rate limit. A single
POST /accounts/v1/provisionis capped per management account. Over the cap →429,error.code = "rate-limited",Retry-After. Use/provision/batchfor bulk.
Always honor Retry-After and retry with the same Idempotency-Key.
// 429 backpressure response
{
"status": 429,
"error": {
"code": "provisioning-backpressure",
"message": "Provisioning queue is over capacity (backlog … >= high-water mark …); retry after 30s.",
"details": { "backlog": 5000, "highWaterMark": 5000, "retryAfterSeconds": 30 }
}
}
Latency targets (SLOs)¶
Budget your client timeouts and retry backoff against these:
Operation |
Target |
|---|---|
OU create (single) |
p95 ~200 ms |
OU |
p95 ~2 s |
Paginated tree / list read (size ≤ 200) |
p95 ~300 ms |
Member provision — accept ( |
p95 ~400 ms |
Member provision — complete (account live) |
~7 s (async) |
For very large onboards, plan for a sustained background import through the
batch + relay path and pace submission to Retry-After — that is the supported
path for high-volume onboarding.