env, not through process.env. Pass your API key to the client explicitly.
Store the key as a secret binding and construct the client with new StateMachines({ apiKey: env.STATEMACHINES_API_KEY }). Never include the key in a browser bundle or return it in an HTTP response.
Give the operation enough lifetime
Environment startup can outlast a short HTTP request. Choose a job owner that can await creation, startup, the app operation, and deletion, such as a Queue consumer, a Workflow step or a Durable Object. The function below is for that owner to await. It is not an HTTP handler or a fire-and-forget task.wait: false, obtains the environment ID, and waits for readiness inside try. Its finally awaits deletion even when startup or the app request fails. The example reads the response body so the Worker does not leave it unread.
Calling this function without awaiting it does not provide reliable cleanup. The surrounding execution must remain alive until the function finishes.
Split a job across requests when needed
For a request-driven workflow, create withwait: false, persist the returned environment ID with the job, and read its status in later requests. Only call the app when the status is running.
The job must retain ownership of deletion on success, failure, and cancellation. Persist the idempotency key before creation if a retry must recover an uncertain response. See operation recovery.
Do not return raw credentials so a browser can poll an app. Management polling belongs on the trusted server using the persisted environment ID.
Set independent deadlines
attemptTimeoutMs bounds each management HTTP attempt. timeoutMs bounds an SDK wait. A caller’s signal can cancel the wait, but it does not delete the environment.
Choose deadlines that fit the surrounding execution and leave time for cleanup. If the execution cannot guarantee that lifetime, use persisted job ownership rather than increasing a request timeout alone.