Sandbox test operators — headless login tokens for CI¶
When you write automated acceptance tests for an operator-facing flow (mobile
operator app, warehouse terminal, any PDP-gated resource access), your CI needs
a real end-user token that carries the account_id claim — the same shape a
human operator gets after logging in. You cannot script a human login (it is
gated by email OTP + CAPTCHA on purpose), and a bare admin-created user does not
carry account_id.
The sandbox test-operator API solves this: one AK/SK-authenticated call provisions a workforce operator in your own account and returns a real, Keycloak-issued operator token your CI can use immediately.
TL;DR.
POST https://api.flexgalaxy.ai/developer/v1/test-users, signed with your account’s access key (FGAI-HMAC). Pass theservice_idof the product your CI is about to call. You get back{ user_id, email, access_token, expires_in }. Theaccess_tokenis a real operator token (carriesaccount_id+user_type=workforce), valid for 5 minutes. It is addressed to the product you named, plus the DotID base audience — the product’s own API accepts it, and the PDP accepts it everywhere. Delete the operator withDELETE /developer/v1/test-users/{user_id}when the suite finishes.
Prerequisites — one-time account setup¶
Forward this to your Account root or an authorized account admin:
A sandbox account inside an organization. The test operator is created as a workforce user in the Identity Center instance, so the account you provision into must belong to an organization (not a standalone account).
An access key for a principal in that account (Profile → Credentials → Access keys, or the developer credential API).
Grant the
iam:CreateTestUsercapability to that principal — an IAM policy statement scoped to the account:{ "Effect": "Allow", "Action": "iam:CreateTestUser", "Resource": "frn:<accountId>:iam:account/<accountId>" }
This capability is what makes an account a “sandbox”: only accounts an admin deliberately grants it to can mint test operators, and a caller can only ever provision into its own account (the account is taken from the AK/SK token, never from the request body).
Create an operator¶
Sign the request with FGAI-HMAC-SHA256 the same way you sign any other
/developer/v1/* or /identity/v1/* call (see
Integrate with access keys).
POST /developer/v1/test-users
Authorization: FGAI-HMAC-SHA256 Credential=AKIA.../.../identity/fgai4_request, ...
Content-Type: application/json
{ "user_type": "workforce", "service_id": "bazaar" }
service_id names the registered product your CI will call; the minted token is
addressed to that product’s API audience so the product’s API accepts it. Omit
it and the token carries only the DotID base audience — usable against DotID’s own
APIs, but a product API will reject it. An
unregistered id is refused with 400 {"error":"unknown_service_id"}.
user_type is optional (only workforce is supported today) and email is
optional (a @sandbox.flexgalaxy.test address is generated when omitted).
201 response:
{
"user_id": "b3a0de58-318f-41f9-bba5-dfe8fdbaa17d",
"email": "op-2124a427b3@sandbox.flexgalaxy.test",
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUI...",
"expires_in": 300
}
The access_token decodes to a genuine operator token:
{
"iss": "https://auth.flexgalaxy.ai/auth/realms/flexgalaxy",
"aud": ["<the product's audience>", "flexgalaxy-api"],
"sub": "b3a0de58-318f-41f9-bba5-dfe8fdbaa17d",
"account_id": "<your sandbox account UUID>",
"user_type": "workforce",
"exp": "<iat + 300>"
}
(aud with the service_id above; without it, aud is just
"flexgalaxy-api".)
Present it to any FlexGalaxy.AI API as Authorization: Bearer <access_token>. It
validates against the realm JWKS like a real login, resolves the correct
account_id, and is denied on cross-account access — exactly what your acceptance
test asserts. (The policy simulator is a different credential
class: it is called with FGAI-HMAC-signed service-user keys, never this token.)
Short TTL, no refresh. The token lives 5 minutes. For a longer suite, call
POST /developer/v1/test-usersagain for a fresh token (or re-use the same operatoruser_id— it persists until you delete it).
List and delete¶
GET /developer/v1/test-users → [{ "user_id", "email" }, ...]
DELETE /developer/v1/test-users/{user_id} → 204
DELETE removes the operator and its account assignment. Clean up at the end of
your test run so sandbox operators do not accumulate.
Scope and safety¶
The operator is a real workforce user, entitled to your sandbox account by a real Identity Center assignment — the platform enforces that gate, so the token can only ever carry an
account_idyou actually own.It is confined to your own account; it cannot touch another tenant.
Every create and delete is audited (
DeveloperTestUserCreated/DeveloperTestUserDeleted).This is a sandbox/testing facility. Do not use test operators as production identities.