Registration — get an access-key / secret-key

This is the first thing a factory integrator does: register a factory-service backend with TrustMint and obtain the access-key-id / secret-key pair that signs every subsequent call. After this walkthrough you can drive the Thing lifecycle, poll the API reference endpoints, and wire up webhooks.

Credentials in this guide are placeholders. Wherever you see <your-access-key-id> or <your-secret-key>, substitute the real values you capture from the 201 response. Never commit a real secret-key — the SDK examples write it to a git-ignored .aksk.env (see SDK quickstart).

Step 1 — register the factory-service (JWT-gated)

Registration itself is gated by a bearer JWT (a registrar-admin identity from the platform IdP), not by AK/SK — AK/SK does not exist yet at this point. Mint a registrar JWT against Keycloak, then:

POST /api/v1/factory-services
Authorization: Bearer <registrar-admin-jwt>
Content-Type: application/json

{
  "registrar_id": "reg-acme-robotics",
  "model_id": "550e8400-e29b-41d4-a716-446655440000",
  "role": "identify",
  "endpoint_url": "https://factory.example.com/v1/identify"
}

A 201 Created returns the one-time credential pair:

{
  "id": "fs-...",
  "registrar_id": "reg-acme-robotics",
  "model_id": "550e8400-e29b-41d4-a716-446655440000",
  "role": "identify",
  "endpoint_url": "https://factory.example.com/v1/identify",
  "access_key_id": "<your-access-key-id>",
  "secret_access_key": "<your-secret-key>",
  "state": "active",
  "rotation_pending": false
}

role is only identify or provision. Register one backend per (registrar_id, model_id, role) tuple you actually implement. endpoint_url is the thingmake-to-factory HTTP backend URL for that role, not a device-facing DDI endpoint. There is no platform default or registrar-wide fallback for a model-bearing device.

The secret_access_key is shown exactly once. Capture it immediately; there is no endpoint that re-reads it. The Go and Python 01-register-factory-service examples do exactly this and persist the pair to .aksk.env — see SDK quickstart.

Where do AK/SK come from? Registration provisions a real DotID service user for your factory-service and issues it an access key through DotID’s service-user / access-key APIs. The secret_access_key is revealed exactly once in this 201 response — TrustMint never stores it (only the access_key_id plus the DotID handles are persisted; the plaintext secret column was removed in the v2.11 secret-de-storage cut). DotID is the issuing authority, and TrustMint holds no factory secret at rest. If you lose the secret you cannot re-read it — rotate the credential (which provisions a fresh access key with a short grace window, again returning the new secret reveal-once) and re-capture it.

If registration is rejected, the response is application/problem+json carrying a THM-NNNNN code. The foundation-auth codes (THM-101xx — JWT expired / signature invalid / missing) live in bucket 1; see the error catalog.

Step 2 — sign every later request (FGAI-HMAC-SHA256)

Once you hold <your-access-key-id> / <your-secret-key>, factory-service runtime calls (/api/v1/things/*, /api/v1/state-changes, /api/v1/identity-blacklist*, subscriptions) are authenticated with the FGAI-HMAC-SHA256 scheme.

Registrar-administrative calls such as factory-service registration and the newer device-model registry (/api/v1/thingmake/vendors, /api/v1/thingmake/models, /api/v1/thingmake/software) are bearer-JWT gated by the platform IdP/PDP. Keep that split visible in examples: use JWT to create or administer registrar resources; use AK/SK for factory runtime callbacks and feeds.

ℹ️ FGAI-HMAC-SHA256 is the SigV4 algorithm — but not aws-sdk-compatible

This is the AWS SigV4 derived-key-chain algorithm, so the SigV4 mental model applies directly: a kDate→kRegion→kService signing-key chain, a canonical request, and an Authorization header carrying Credential / SignedHeaders / Signature. BUT it is FGAI-branded — the chain terminates in fgai4_request (not AWS’s aws4_request) and the scheme name is FGAI-HMAC-SHA256 (not AWS4-HMAC-SHA256). It is therefore NOT wire-compatible with the stock aws-sdk request-signer. Do not bolt an aws-sdk signer onto these endpoints — it will produce a signature DotID rejects. Use the FGAI SDK (below), which emits the FGAI-branded wire bytes.

Canonical request (six components, \n-joined — byte-identical to DotID’s SigV4HmacVerifier.canonicalRequest):

canonical =
  METHOD                + "\n" +
  CANONICAL_PATH        + "\n" +   // "/" when empty
  CANONICAL_QUERY       + "\n" +   // sorted percent-encoded k=v, or "" when none
  CANONICAL_HEADERS     + "\n" +   // each "name:value\n", names lower-cased + sorted
  SIGNED_HEADERS        + "\n" +   // ";"-joined sorted lower-case names
  SHA256_HEX(BODY)

where the signed header set is host;x-fgai-content-sha256;x-fgai-date.

Derived signing-key chain (then sign the canonical request with kSigning):

kSecret   = "FGAI4" + <your-secret-key>
kDate     = HMAC_SHA256(kSecret,  <yyyyMMdd>)
kRegion   = HMAC_SHA256(kDate,    "us-east-1")
kService  = HMAC_SHA256(kRegion,  "thingmake")
kSigning  = HMAC_SHA256(kService, "fgai4_request")
signature = HEX(HMAC_SHA256(kSigning, canonical))

The full recipe (with the frozen scope literals and the worked golden vector) is in services/thingmake/sdks/testdata/README.md.

Headers sent on every signed request:

Header

Value

Authorization

FGAI-HMAC-SHA256 Credential=<akid>/<yyyyMMdd>/us-east-1/thingmake/fgai4_request, SignedHeaders=host;x-fgai-content-sha256;x-fgai-date, Signature=<64-lower-hex>

X-FGAI-Date

compact yyyyMMddTHHmmssZ UTC; must be within a ±5 min skew window

X-FGAI-Content-SHA256

lower-case hex SHA-256 of the request body (equals canonical component 6)

You should not hand-roll this. The SDK signers emit canonical strings byte-identical to the server’s verifier:

  • Gosigning.NewSigningTransport(ak, sk, http.DefaultTransport)

  • Pythonhttpx.Client(auth=AKSKAuth(ak, sk)) or requests.Session() + AKSKAuthRequests(ak, sk)

See the SDK quickstart for the signing helpers and the services/thingmake/sdks/README.md source.

When signing fails

Signing/auth failures return 401 with a factory-auth code (THM-201xx, bucket 2 of the error catalog):

Code

DotID failure mode

Meaning

Fix

THM-20110

MISSING_HEADER

a required signing header (Authorization / X-FGAI-Date / X-FGAI-Content-SHA256) is missing

attach all three signing headers

THM-20120

INVALID_SIGNATURE

the Signature did not match, or X-FGAI-Content-SHA256 ≠ the body hash

re-compute against the exact canonical request + derived key

THM-20130

EXPIRED

X-FGAI-Date outside the ±5 min skew window

re-sign with a fresh X-FGAI-Date

THM-20140

UNKNOWN_KEY

access-key id in the Credential not recognised

confirm the AK is current + bound to an active factory-service

THM-20150

REPLAY

a duplicate Authorization Signature was replayed

re-sign with a fresh X-FGAI-Date

(THM-20100 is the generic factory-auth-failed fallback for an unclassified signing failure.)

The full bucket-2 table (titles + descriptions) is in the error catalog, sourced from ../../services/thingmake/docs/errors/thingmake-error-catalog.md.

What next