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 the service_id of the product your CI is about to call. You get back { user_id, email, access_token, expires_in }. The access_token is a real operator token (carries account_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 with DELETE /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:

  1. 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).

  2. An access key for a principal in that account (Profile → Credentials → Access keys, or the developer credential API).

  3. Grant the iam:CreateTestUser capability 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-users again for a fresh token (or re-use the same operator user_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_id you 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.