Getting started

This is the zero-to-registered journey: five linear steps that take a brand-new integrator from nothing to a factory-service backend that registers, self-certifies, receives notifications, and polls state-changes. Each step links the relevant SDK example and the error catalog entry you will hit if something is wrong.

Prerequisite — one base URL, one credential

Everything trustmint exposes to you is reached over HTTPS at a single host:

Host

What it serves

https://api.flexgalaxy.ai

the trustmint capability API — the surface this guide uses

https://api.staging.flexgalaxy.ai

the same API, one release ahead, for integration testing

https://console.flexgalaxy.ai

the browser consoles, if you would rather click than call

Paths are capability paths, not service names: /provisioning/v1/… (registrars, enrollment, factory services, thing lifecycle, state changes), /access/v1/… (repositories, scopes, grants), /telemetry/v1, /twins/v1/…, /rules/v1/…, /pki/v1/…. The CLI reference lists every group, and the API reference renders the generated specification.

Two credential kinds exist, and you will use both in this guide:

  • an interactive bearer token from your account’s SSO login, used once to register your backend;

  • an access-key / secret-key pair issued by that registration, which signs every call afterwards with the FGAI-HMAC scheme (FGAI4 derived key — not AWS SigV4). See Registration.

Step 0 — Prove you can reach the API

Two endpoints under /pki/v1/ are public trust-anchor discovery and need no credential, so they are the cleanest first request: a 200 here proves DNS, TLS and routing before any signing question can confuse the diagnosis.

curl -fsS https://api.flexgalaxy.ai/pki/v1/jwks
curl -fsS https://api.flexgalaxy.ai/pki/v1/ca

The first returns a JWKS document — a keys array whose entries carry kty, alg and kid — used to verify signed artifacts such as the identity-blacklist snapshot. The second returns the PEM certificate chain your devices pin. If either fails, stop here: no later step can succeed. If they succeed, the platform is reachable and every remaining failure is about your credential or your payload.

Step 1 — Register a factory-service

Ordering (required for the lifecycle verbs). POST …/things/{id}/provision, …/activate and …/deactivate are served by a provision-role factory service. The row must be the one thingmake resolves for that thing: a model-bound thing (enrolled by model UUID) is served by an ACTIVE provision row carrying that exact model_id; a model-less thing falls back to the registrar-level row (model_id null — not creatable through POST …/factory-services). If the resolved row does not exist, thingmake refuses the verb before changing anything, with 409 and code THM-40200; the thing’s state and its saga count are untouched, so you can register the backend and retry. An identify-role registration does not satisfy this: register the provision role as well ("role":"provision" with "model_id" — the create API requires it) and confirm it is ACTIVE before driving provision/activate/deactivate.

Who runs it: an administrator of the registrar’s owning account — the account root, or a user with account-administrator access — because the call is authorized against that registrar’s account, not against your identity globally (a caller from any other account is refused).

Before it can succeed — grant the platform provisioning trust (one-time, self-serve). Registering a factory service makes the platform provision a service user and an access key inside your account on your behalf, so you grant that capability first. Sign in to https://console.flexgalaxy.ai as the account root, then run the two grant calls below.

You need two things from that sign-in:

  • your bearer token — today there is no console button or CLI login that prints it: copy the session token from your signed-in browser session (developer tools → storage), or mint it programmatically through the SSO PKCE flow. A first-class consent surface is tracked as a platform improvement.

  • your account context id — one call, shown below.

ACCOUNT=<your-account-id>
# 1) bearer -> account context id (pick the entry with "type":"account"; its context_id is $CTX)
curl -fsS https://api.flexgalaxy.ai/identity/v1/contexts \
  -H "Authorization: Bearer $BEARER"

# 2) grant: create service users
curl -fsS -X POST https://api.flexgalaxy.ai/identity/v1/accounts/$ACCOUNT/trust-policies \
  -H "Authorization: Bearer $BEARER" -H "X-Session-Context-Id: $CTX" \
  -H 'Content-Type: application/json' \
  -d '{"target_resource_frn":"frn:'"$ACCOUNT"':iam:user/*","source_account_id":"00000000-0000-0000-0000-000000000000","granted_actions":["iam:User:CreateServiceUser"],"service_namespace":"thingmake"}'

# 3) grant: the access keys for those service users
curl -fsS -X POST https://api.flexgalaxy.ai/identity/v1/accounts/$ACCOUNT/trust-policies \
  -H "Authorization: Bearer $BEARER" -H "X-Session-Context-Id: $CTX" \
  -H 'Content-Type: application/json' \
  -d '{"target_resource_frn":"frn:'"$ACCOUNT"':iam:credential/*","source_account_id":"00000000-0000-0000-0000-000000000000","granted_actions":["iam:AccessKeyAdmin"],"service_namespace":"thingmake"}'

What this grants, honestly: it allows FlexGalaxy platform provisioning services to create service users and access keys in your account; it is used by ThingMake factory provisioning and is not limited to a single product name.

Revoke any time — GET the same path to list the rows and their ids, then DELETE /identity/v1/accounts/$ACCOUNT/trust-policies/{id} with the same two headers. After revocation, factory-service registration is refused with 403 and code THM-20200 (“ThingMake provisioning trust not granted for this account”) on its next attempt; re-grant to restore it.

First get your account credential and create the registrar your factory targets — the console service-user path in Registration §Step 0 — then use the registrar-admin bearer to POST /provisioning/v1/factory-services and capture your one-time <your-access-key-id> / <your-secret-key>. Full walkthrough: Registration.

curl -fsS -X POST https://api.flexgalaxy.ai/provisioning/v1/factory-services \
  -H "Authorization: Bearer <registrar-admin-token>" \
  -H 'Content-Type: application/json' \
  -d '{"registrar_id":"reg-acme-robotics",
       "model_id":"550e8400-e29b-41d4-a716-446655440000",
       "role":"identify",
       "endpoint_url":"https://factory.example.com/v1/identify"}'
  • SDK example: 01-register-factory-service (Go + Python) does exactly this and writes the pair to a git-ignored .aksk.env.

  • The secret key is shown exactly once. No endpoint re-reads it; if you lose it, rotate the credential rather than re-registering.

  • If it fails: this endpoint is token-gated, so failures are foundation-auth codes — bucket 1 of the error catalog (THM-10100 token expired, THM-10110 signature invalid, THM-10120 token missing). The AK/SK signing codes (bucket 2, THM-201xx) apply to the signed requests in later steps, not to this bearer-token call — see Registration for the full split.

Step 2 — Make your first signed call

Confirm the pair works before you build anything on it. Sign a read with FGAI-HMAC — the SDKs and the fgai CLI both do the signing for you. The CLI is published on dl.flexgalaxy.ai for macOS, Linux and Windows:

curl -fsSL https://dl.flexgalaxy.ai/fgai/install.sh | bash

fgai configure                       # stores the access key / secret key in a profile
fgai provisioning factory-services-list

A 200 with your own factory-service in the list means the credential, the signing scheme and the clock skew window are all correct. A THM-201xx means the signature did not verify — Registration documents the canonical string-to-sign.

Step 3 — Unlock device enrollment (factory capability)

Enrolling device identities is a separate, explicitly granted capability — it is not implied by an account-administrator policy, and it is not switched on by approval alone. The order is:

  1. Request the factory capability for your account: fgai provisioning factory-registrations-create (or ask your platform contact to file it on your behalf). The request is recorded as the approval object.

  2. We approve it (platform ops) — you are notified when it is on.

  3. Attach ThingMakeEnrollAccess to the service-user credential your provisioning runs under — your account’s root/admin does this in the console (Service user → Access policies → attach), and it is an explicit attachment: a broad administrator grant does not confer it.

  4. Enroll — pass the model’s UUID, not its vid:pid descriptor. See it with fgai registry models-list (the id field of the model you registered in Device models, or of the models-create response): fgai provisioning enrollments-create --body '{"registrar_id":"…","thing_id":"<serial>","model":"<model-uuid>"}' → the response carries the real thing_id your device will present. The distinction is load-bearing: enrollment resolves a model by UUID only, so a bare descriptor ("model":"<pid>") leaves the thing model-less — and a model-less thing has no API path to a provision backend, because POST …/factory-services requires a model_id. Enroll by UUID so the thing is model-bound and the provision row you register for that model_id can serve it.

  5. Provision + connect — the device SDK runs identify → provision (CSR) → mTLS against your regional plane; see Device SDKs.

A missing capability is refused with THM-10140 naming exactly ThingMakeEnrollAccess; THM-30170 means the capability request has not been filed yet.

Step 4 — Place the thing into your repository

Enrollment creates the thing; it does not, on its own, make the thing readable to you. The account’s home reference is created when the thing is placed into a repository — until then an owner-side read refuses with 404 thing not found and attribute or telemetry reads have no reference to authorize against. See Thing access model for what the reference governs.

Place the thing from the console (Repositories → open the target repository → place), or with the CLI. The default repository is materialized on first touch, so list repositories first, then place:

fgai access repositories-list
fgai access repositories-things --repository <repository-id> \
  --body '{"thing_id":"<thing-id>","joined_via":"user"}'

Verify the reference exists:

fgai access things-owner --thing <thing-id>

→ 200 with home_repository_id, owner_account_id and thing_id. Placing the same pair again is idempotent (ok).

Step 5 — Run the conformance suite against your backend

Self-certify that your backend implements the contract correctly before you wire it into anything. Point your backend’s thingmake-base-url at the suite’s --listen-addr (default :8910), then run the single command. Full walkthrough: Conformance.

thingmake-conformance \
  --backend-url http://your-factory-backend:8080 \
  --access-key   <your-access-key-id> \
  --secret-key   <your-secret-key>
  • Exit 0 means every scenario passed.

  • If it fails: registrar-lifecycle + thing-state-machine codes — buckets 3 and 4 of the error catalog (THM-30xxx, THM-40110 illegal transition, THM-40120 missing Idempotency-Key). The suite names the expected THM-NNNNN in each failure diagnostic.

Step 6 — Receive a thing-state notification

Subscribe to notifications and host an HMAC-verifying receiver so a state-change is pushed to you. Full walkthrough: Webhooks.

fgai provisioning state-change-subscriptions-create --body '{
  "target_url": "https://factory.example.com/hooks/thing-state",
  "acknowledge_polling_still_mandatory": true }'
  • SDK example: 02-receive-state-notification (subscription + listener) and 04-verify-webhook (HMAC verify helper).

  • Remember: acknowledge_polling_still_mandatory=true is required — a subscription does not replace polling.

  • If it fails: subscriptions codes — bucket 6 of the error catalog (THM-60110 polling not acknowledged, THM-60120 non-https target_url, THM-60140 invalid event_filter).

Step 7 — Poll the state-changes feed

Webhooks are best-effort; the durable backstop is the cursor-paged feed. Poll GET /provisioning/v1/state-changes?since=<next_cursor> and persist next_cursor between calls. See the state-changes group in the API reference.

  • SDK example: 03-poll-state-changes (cursor loop).

  • If it fails: state-changes-feed codes — bucket 5 of the error catalog. Most importantly THM-50120 — a cursor older than 72h returns HTTP 410 and you must full-sync via GET /provisioning/v1/things, then resume polling. Also THM-50100 (drop ?registrar_id=) and THM-50110 (malformed cursor).

What next

You now have a backend that registers, certifies, subscribes, and polls. From here: