{
  "openapi": "3.0.2",
  "info": {
    "title": "State Backed API",
    "description": "The official State Backed API for running XState backends as a service.\n\nState Backed turns any XState state machine into a stateful backend.\nSpawn instances of your machines, authorize requests,\nsend events, and read your instances' state without any servers\nor datastores to manage.\n\nView the full State Backed documentation at https://docs.statebacked.dev.\n\nDownload the `smply` CLI at https://npmjs.com/package/smply\nor at https://github.com/state-backed/smply/releases.\n",
    "version": "1.0.0"
  },
  "servers": [
    {
      "url": "https://api.statebacked.dev",
      "description": "Production server"
    }
  ],
  "externalDocs": {
    "description": "State Backed documentation",
    "url": "https://docs.statebacked.dev"
  },
  "x-topics": [
    {
      "title": "Authentication",
      "content": "# End-user JWT authentication\n\nPass the JWT token in the `Authorization` header as `Bearer <token>`.\n\n## Use\n\nState Backed authenticates requests to ensure that they include a valid JWT\nsigned by one of your StateBacked keys.\n\nState Backed also passes the user data from the `act` claim of your JWT to your\nmachine's `allowRead` and `allowWrite` functions to allow you to authorize operations\non machine instances.\n\n## Generation\n\nUse the key (`sbk_...`) and secret (`sbsec_...`) generated from running `smply keys create`\nto sign a JWT token with an `act` claim that includes data about your end user\n(e.g. a `sub` claim with the user's ID).\n\n```\nHS256(\"sbsec_...\", { \"kid\": \"sbk_...\", \"alg\": \"HS256\" }, { \"aud\": \"https://api.statebacked.dev/\", \"act\": { \"sub\": \"...\" }, ... })\n```\n\nYou can also use our token generation library:\n```\nimport { signToken } from \"@statebacked/token\";\n\nconst jwt = await signToken({\n  {\n    stateBackedKeyId: process.env.STATEBACKED_KEY_ID,\n    stateBackedSecretKey: process.env.STATEBACKED_SECRET_KEY,\n  },\n  {\n    sub: \"your-user-id\"\n  },\n  {\n    expires: { in: \"7d\" },\n    issuer: \"https://your-domain.com/\"\n  }\n});\n```\n"
    }
  ],
  "tags": [
    {
      "name": "Machines",
      "description": "# Machine definition operations.\n\nMachine definitions name a logical machine from which you can create\nmultiple *machine instances*.\n\nMachine definitions have multiple *machine definition versions*, one\nof which is named as the current version.\n"
    },
    {
      "name": "Machine versions",
      "description": "# Machine definition version operations.\n\nMachine definition versions contain the actual machine definition,\nwhich consists of a single self-contained javascript file that\ndefault exports an [XState](https://xstate.js.org/docs/) state\nmachine as well as an `allowRead` and `allowWrite` function.\n\nThey are immutable and identified by an ID.\nThe `clientInfo` field is informational only and intended to be used to\nidentify the source of the machine version (e.g. git commit sha,\nsemantic version number, timestamp, etc).\n\nThe `allowRead` and `allowWrite` functions are called to authorize\nread and write operations on machine instances, respectively.\nWrite authorization implies the authority to read the result of\nyour write.\n\nAnything stored in the machine's context under the `public` key\nwill be exposed to authorized readers and writers as `publicContext`.\n\n## Example machine definition\n\n```js\nimport type {\n  AllowRead,\n  AllowWrite,\n} from \"@statebacked/machine-def\";\nimport { createMachine } from \"xstate\";\n\nexport const allowRead: AllowRead = (\n  { machineInstanceName, authContext },\n) => machineInstanceName === authContext.sub;\n\nexport const allowWrite: AllowWrite = (\n  { machineInstanceName, authContext },\n) => machineInstanceName === authContext.sub;\n\nexport default createMachine(...);\n```\n"
    },
    {
      "name": "Machine version migrations",
      "description": "# Machine version migration operations.\n\nMachine version migrations are small snippets of code that migrate\nexisting machine instances from one machine version to another.\nThey consist of a single self-contained javascript file that\nexports `upgradeState` and `upgradeContext` functions.\n\n## Example machine version migration\n\n```js\nimport type {\n  UpgradeState,\n  UpgradeContext,\n} from \"@statebacked/machine-def\";\n\nexport const upgradeState: UpgradeState = (oldState, oldContext) => {\n  // oldState is an array describing a full path to a state in the old version of the machine\n  // (e.g. [\"authenticationPage\", \"login\"] if login is a child state of authenticationPage).\n  // oldContext is the most recent context of the machine. It *may not* be a valid context in\n  // the given state because upgradeState will be called to migrate history states in\n  // addition to current states.\n\n  // a simple renaming of a parent state from \"authenticationPage\" to \"newAuthenticationPage\".\n  return oldState[0] === \"authenticationPage\" ? [\"newAuthenticationPage\"].concat(oldState.slice(1)) : oldState;\n};\n\nexport const upgradeContext: UpgradeContext = (oldStates, newStates, oldContext) => {\n  // oldStates is an array of state paths from the old version of the machine.\n  // (e.g. [[\"a\", \"b\"], [\"a\", \"c\"]] if \"a\" is a parallel state and is in both \"b\" and \"c\").\n  // newStates is an array of state paths from the new version of the machine.\n  // oldContext is the context associated with oldStates in the old version of the machine.\n\n  return {\n    ...oldContext,\n    upgradeCount: oldContext.upgradeCount + 1\n  };\n};\n```\n"
    },
    {
      "name": "Machine instances",
      "description": "# Machine instance operations.\n\nMachine instances are instances of a machine definition.\n\nIf you have a machine definition that controls a user onboarding flow,\nyou would want a machine instance for each user.\n\nIf you have a machine definition that controls access to a collaborative\ndocument, you would want a machine instance for each document.\n\nMachine instances have names that are unique within a machine definition.\n\nYou can send events to machine instances to trigger transitions and can\nread the state of an instance.\n\n## Consistency guarantees\n\nProcessing for a machine instance is coordinated such that there is\na single, linearizable history of events and state transitions.\n\n## Limits\n\nMachine context (represented as JSON) may not exceed 400kb.\n"
    },
    {
      "name": "Logs",
      "description": "# Logs produced while processing transitions and migrations.\n\nState Backed collects any logs you emit while processing\ntransitions and migrations and makes them available via this\nendpoint.\n\nState Backed also emits some logs of its own to provide context.\n\nWe *highly* recommend logging structured JSON objects.\n"
    },
    {
      "name": "Realtime",
      "description": "# Realtime (websocket) endpoints\n\nState Backed allows you to subscribe to machine instance updates\nvia websockets.\n"
    },
    {
      "name": "Tokens",
      "description": "# Endpoints for retrieving State Backed authorization tokens\n\nAll calls to State Backed must include a JWT signed by one of your\nState Backed keys in the authorization header.\n\nThe token should include an `act` claim that contains claims about your end user,\nwhich will be passed into your machine authorization functions (`allowRead` and `allowWrite`)\nas `authContext`. This allows you to, for instance, ensure that users can only send events\nto their own machine instances or instances that your application logic permits.\n\nThese tokens are easily created by the [@statebacked/token](https://www.npmjs.com/package/@statebacked/token)\nlibrary.\n\nHowever, the signing keys to create these tokens **must always** be kept secret and cannot\nbe included in client-side applications.\n\nTo allow you to use any authentication provider you want and to use State Backed without\nany server-side code, you can use our token exchange endpoints to securely exchange\na JWT from your authentication provider for a State Backed JWT with the appropriate user claims.\n\nAfter configuring trusted identity providers and token providers, you will be able to\nexchange a JWT from any of your trusted identity providers for a token with claims and a signature\ngenerated by any of your token providers by posting to the /tokens endpoint.\n\nFirst, configure your trusted identity providers by posting to /idps.\nYou will have to specify the audience and/or issuer that identifies a token from this IDP as well\nas the JWKS url where State Backed can retrieve the appropriate verification keys or\nthe secret key that State Backed can use to verify the token (keys are stored encrypted in the\nState Backed vault).\nYou will also provide a mapping, which extracts claims from the identity provider token\nto make them available to token providers.\n\nThen, configure your token providers by posting to /token-providers.\nYou will give your token provider a service name to identify it (you might want a different\nservice for each app you build, for instance) and a mapping that specifies the\nclaims that should be included in any token generated by that token provider.\n\nAfter that, you have completely secure, end-to-end authenticated user sessions available\nin your State Backed backend without any server-side presence.\n\n## Mappings\n\nMappings are JSON objects whose structure matches the desired output of the mapping and whose\nvalues can refer to items from the input.\n\nA mapping of:\n\n```javascript\n{\n  \"sub.$\": \"$.sub\",\n  \"email.$\": \"$.email\",\n  \"service\": \"my-service\",\n  \"special\": {\n    \"role.$\": \"$.role.id\"\n  }\n}\n```\n\nWith input of:\n\n```javascript\n{\n  \"sub\": \"my-user-id\",\n  \"email\": \"my-email@statebacked.dev\",\n  \"role\": {\n    \"id\": \"my-role\"\n  }\n}\n```\n\nWill produce a result of:\n\n```javascript\n{\n  \"sub\": \"my-user-id\", // mapping keys that end in \".$\" treat values as JSONPath expressions into the input\n  \"email\": \"my-email@statebacked.dev\",\n  \"service\": \"my-service\", // mapping keys that don't end in \".$\" treat values as constants (unless the value is a mapping)\n  \"special\": {\n    \"role\": \"my-role\" // mappings are resolved recursively\n  }\n}\n```\n"
    },
    {
      "name": "Orgs",
      "description": "Manage organizations"
    },
    {
      "name": "Billing",
      "description": "Manage billing"
    },
    {
      "name": "Keys",
      "description": "Manage signing keys"
    }
  ],
  "paths": {
    "/machines": {
      "get": {
        "tags": [
          "Machines"
        ],
        "summary": "List your machines",
        "parameters": [
          {
            "name": "cursor",
            "in": "query",
            "description": "A cursor returned from a previous call to /machines",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "BearerAuth": [
              "machines.read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Your machines",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "machines"
                  ],
                  "properties": {
                    "machines": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "slug",
                          "createdAt"
                        ],
                        "properties": {
                          "slug": {
                            "$ref": "#/components/schemas/MachineSlug"
                          },
                          "createdAt": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "currentVersion": {
                            "$ref": "#/components/schemas/MachineVersionInfo"
                          }
                        }
                      }
                    },
                    "cursor": {
                      "type": "string",
                      "description": "The cursor to use on the next call to retrieve the next page of machines.\nIf no cursor is returned, there are no more pages to retrieve.\n"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create a new machine definition.",
        "tags": [
          "Machines"
        ],
        "security": [
          {
            "BearerAuth": [
              "machines.write"
            ]
          }
        ],
        "description": "Note: No instances of a machine definition can be created until\nyou create a machine definition version for it.\n",
        "requestBody": {
          "$ref": "#/components/requestBodies/CreateMachineDefinition"
        },
        "responses": {
          "201": {
            "description": "The machine definition was created successfully."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/machines/{machineSlug}": {
      "get": {
        "tags": [
          "Machines"
        ],
        "summary": "Get a machine definition.",
        "description": "Retrieve the machine definition\n",
        "parameters": [
          {
            "name": "machineSlug",
            "in": "path",
            "description": "The slug/name for the machine definition.",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/MachineSlug"
            }
          }
        ],
        "security": [
          {
            "BearerAuth": [
              "machines.read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The machine was retrieved successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "machine"
                  ],
                  "properties": {
                    "machine": {
                      "type": "object",
                      "required": [
                        "createdAt",
                        "indexes"
                      ],
                      "properties": {
                        "createdAt": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "currentVersion": {
                          "$ref": "#/components/schemas/MachineVersionInfo"
                        },
                        "indexes": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/IndexName"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      },
      "post": {
        "tags": [
          "Machine instances"
        ],
        "summary": "Create a new machine instance.",
        "description": "Create a new instance of the machine definition with the given slug.\n\nThe `allowWrite` function for the machine definition version will be called\nto authorize the initial transition and, if it fails, a 403 with code\n`rejected-by-machine-authorizer` will be returned.\n\nOtherwise, the state of the machine instance after the initial transition\nwill be returned.\n\nAll top-level events have a 10 second timeout for the machine to settle.\nSettling means that the machine has reached a stable state and has no\nchild services running.\n\nIf the machine does not settle within 10 seconds but has completed at least\none transition successfully, a 200 with the current state will be returned,\nthe child services will be stopped, and error events will be delivered for\neach stopped service before the next event is sent.\n\nIf a machine instance for this (`machineSlug`, instance `slug`) already exists,\na 409 will be returned.\n",
        "parameters": [
          {
            "name": "machineSlug",
            "in": "path",
            "description": "The slug/name for the machine definition to create an instance of.",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/MachineSlug"
            }
          }
        ],
        "security": [
          {
            "BearerAuth": [
              "instances.write"
            ]
          }
        ],
        "requestBody": {
          "$ref": "#/components/requestBodies/CreateMachineInstance"
        },
        "responses": {
          "200": {
            "description": "The machine instance was created successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/State"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      },
      "delete": {
        "tags": [
          "Machines"
        ],
        "summary": "Delete a machine and any versions and migrations associated with it.",
        "description": "Delete a machine and any versions and migrations associated with it.\n\n*THIS IS OBVIOUSLY A DANGEROUS OPERATION AND WILL INTENTIONALLY CAUSE DATA LOSS*\n\nIf any instances exist for the machine, a 409 error will be returned with an `invalid-state` code.\nYou can delete the instances and then retry the machine deletion.\n\nAll versions associated with the machine and all migrations between those versions\nwill be deleted.\n\nThere is no option to recover data after a machine is deleted.\n\nTo prevent accidental deletion, we require two validation parameters:\n  - hmacSha256OfMachineNameWithMachineNameKey - `base64urlEncode(hmacSha256(key = \"machine name\", \"machine name\"))`\n  - dangerDataWillBeDeletedForever - true\n\nA 400 error with the `parameter` set to the name of the incorrect parameter will be returned\nif the validation parameters are incorrect.\n\nThis endpoint requires admin access.\n",
        "parameters": [
          {
            "name": "machineSlug",
            "in": "path",
            "description": "The slug/name for the machine definition to delete.",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/MachineSlug"
            }
          }
        ],
        "security": [
          {
            "BearerAuth": [
              "machines.admin"
            ]
          }
        ],
        "requestBody": {
          "$ref": "#/components/requestBodies/DeleteMachine"
        },
        "responses": {
          "204": {
            "description": "The machine instance was deleted successfully."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/machines/{machineSlug}/i": {
      "get": {
        "tags": [
          "Machine instances"
        ],
        "summary": "List the instances of this machine",
        "parameters": [
          {
            "name": "cursor",
            "in": "query",
            "description": "A cursor returned from a previous call to /machines/{machineSlug}/i",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "BearerAuth": [
              "instances.read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The instances of this machine",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "instances"
                  ],
                  "properties": {
                    "instances": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "slug",
                          "createdAt",
                          "status",
                          "machineVersion"
                        ],
                        "properties": {
                          "slug": {
                            "$ref": "#/components/schemas/MachineInstanceSlug"
                          },
                          "createdAt": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "status": {
                            "$ref": "#/components/schemas/MachineInstanceStatus"
                          },
                          "machineVersion": {
                            "$ref": "#/components/schemas/MachineVersionInfo"
                          }
                        }
                      }
                    },
                    "cursor": {
                      "type": "string",
                      "description": "The cursor to use on the next call to retrieve the next page of instances.\nIf no cursor is returned, there are no more pages to retrieve.\n"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/machines/{machineSlug}/i/{instanceSlug}": {
      "get": {
        "tags": [
          "Machine instances"
        ],
        "summary": "Get the current state of a machine instance.",
        "description": "Retrieve the state of the machine instance that was previously created by\ncalling `POST /machines/{machineSlug}` and may have had events sent to it\nby calling `POST /machines/{machineSlug}/i/{instanceSlug}/events`.\n\nThe `allowRead` function for the machine definition version will be called\nto authorize the read and, if it fails, a 403 with code\n`rejected-by-machine-authorizer` will be returned.\n\nOtherwise, the current state of the machine instance will be returned.\n\nObviously, the state returned may be out of date by the time it is returned\nbecause reads are non-blocking but a the returned state will always be\nself-consistent.\n",
        "parameters": [
          {
            "name": "machineSlug",
            "in": "path",
            "description": "The slug/name for the machine definition.",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/MachineSlug"
            }
          },
          {
            "name": "instanceSlug",
            "in": "path",
            "description": "The slug/name for the machine instance.",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/MachineInstanceSlug"
            }
          }
        ],
        "security": [
          {
            "BearerAuth": [
              "state.read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The state was retrieved successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/State"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      },
      "delete": {
        "tags": [
          "Machine instances"
        ],
        "summary": "Delete a machine instance and any transitions, state, or pending upgrades associated with it.",
        "description": "Delete a machine instance and any transitions, state, or pending upgrades associated with it.\n\n*THIS IS OBVIOUSLY A DANGEROUS OPERATION AND WILL INTENTIONALLY CAUSE DATA LOSS*\n\nAll historical transitions associated with the machine and all current state and context\nwill be deleted.\n\nThere is no option to recover data after an instance is deleted.\n\nTo prevent accidental deletion, we require two validation parameters:\n  - hmacSha256OfMachineInstanceNameWithMachineNameKey - `base64urlEncode(hmacSha256(key = \"machine name\", \"machine instance name\"))`\n  - dangerDataWillBeDeletedForever - true\n\nA 400 error with the `parameter` set to the name of the incorrect parameter will be returned\nif the validation parameters are incorrect.\n\nThis endpoint requires admin access.\n",
        "parameters": [
          {
            "name": "machineSlug",
            "in": "path",
            "description": "The slug/name for the machine definition to delete.",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/MachineSlug"
            }
          },
          {
            "name": "instanceSlug",
            "in": "path",
            "description": "The slug/name for the machine instance.",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/MachineInstanceSlug"
            }
          }
        ],
        "security": [
          {
            "BearerAuth": [
              "instances.admin"
            ]
          }
        ],
        "requestBody": {
          "$ref": "#/components/requestBodies/DeleteMachineInstance"
        },
        "responses": {
          "204": {
            "description": "The machine instance was deleted successfully."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/machines/{machineSlug}/indexes/{index}/query": {
      "get": {
        "tags": [
          "Machine instances"
        ],
        "summary": "Query for machine instances using the indicated index.",
        "description": "Using an index specified during machine creation and defined during machine version creation,\nquery for instances of the provided machine that have an indexed value that matches the\nprovided filters.\n",
        "parameters": [
          {
            "name": "machineSlug",
            "in": "path",
            "description": "The slug/name for the machine definition.",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/MachineSlug"
            }
          },
          {
            "name": "index",
            "in": "path",
            "description": "The name for the machine index.",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/IndexName"
            }
          },
          {
            "name": "op",
            "in": "query",
            "description": "The operation for the filter.",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "eq",
                "ne",
                "gt",
                "gte",
                "lt",
                "lte"
              ]
            }
          },
          {
            "name": "value",
            "in": "query",
            "description": "The value for the filter.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "dir",
            "in": "query",
            "description": "The sort direction for the index.",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "The maximum number of items to return.",
            "required": false,
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The cursor returned from a previous call to query.",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "BearerAuth": [
              "indexes.read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The query was executed successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "instances"
                  ],
                  "properties": {
                    "instances": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "indexValue",
                          "slug"
                        ],
                        "properties": {
                          "indexValue": {
                            "type": "string",
                            "description": "The value of the index property for this instance."
                          },
                          "slug": {
                            "$ref": "#/components/schemas/MachineInstanceSlug"
                          }
                        }
                      }
                    },
                    "cursor": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/machines/{machineSlug}/i/{instanceSlug}/admin": {
      "get": {
        "tags": [
          "Machine instances"
        ],
        "summary": "Get the administrative state of an instance",
        "description": "Retrieve the state of the machine instance that was previously created by\ncalling `POST /machines/{machineSlug}` and may have had events sent to it\nby calling `POST /machines/{machineSlug}/i/{instanceSlug}/events`.\n\nNo machine authorizers will be called to authorize this read and it returns\nprivate context data so this requires instances.admin scope.\n\nThe full context for the instance (instead of only public context) will be returned.\n",
        "parameters": [
          {
            "name": "machineSlug",
            "in": "path",
            "description": "The slug/name for the machine definition.",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/MachineSlug"
            }
          },
          {
            "name": "instanceSlug",
            "in": "path",
            "description": "The slug/name for the machine instance.",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/MachineInstanceSlug"
            }
          }
        ],
        "security": [
          {
            "BearerAuth": [
              "instances.admin"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The instance was retrieved successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdministrativeInstanceState"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/machines/{machineSlug}/i/{instanceSlug}/events": {
      "get": {
        "tags": [
          "Machine instances"
        ],
        "summary": "List the transitions for this machine instance",
        "parameters": [
          {
            "name": "cursor",
            "in": "query",
            "description": "A cursor returned from a previous call to /machines/{machineSlug}/i/{instanceSlug}/events",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "BearerAuth": [
              "instances.admin"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The transitions for this machine instance",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "transitions"
                  ],
                  "properties": {
                    "transitions": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "createdAt",
                          "state",
                          "event"
                        ],
                        "properties": {
                          "createdAt": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "state": {
                            "$ref": "#/components/schemas/StateValue"
                          },
                          "event": {
                            "$ref": "#/components/schemas/Event"
                          }
                        }
                      }
                    },
                    "cursor": {
                      "type": "string",
                      "description": "The cursor to use on the next call to retrieve the next page of transitions.\nIf no cursor is returned, there are no more pages to retrieve.\n"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Machine instances"
        ],
        "summary": "Send an event to a machine instance.",
        "description": "Send an event to the machine instance that was previously created by\ncalling `POST /machines/{machineSlug}`.\n\nThe `allowWrite` function for the machine definition version will be called\nto authorize the send and, if it fails, a 403 with code\n`rejected-by-machine-authorizer` will be returned.\n\nOtherwise, the state of the machine instance after any transitions resulting\nfrom the event will be returned.\n\nThe request will wait for the machine to settle before returning a response.\nSettling means that the machine has reached a stable state and has no\nchild services running.\n\nAll top-level events have a 10 second timeout for the machine to settle.\n\nIf the machine does not settle within 10 seconds but has completed at least\none transition successfully, a 200 with the current state will be returned,\nthe child services will be stopped, and error events will be delivered for\neach stopped service before the next event is sent.\n",
        "parameters": [
          {
            "name": "machineSlug",
            "in": "path",
            "description": "The slug/name for the machine definition.",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/MachineSlug"
            }
          },
          {
            "name": "instanceSlug",
            "in": "path",
            "description": "The slug/name for the machine instance.",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/MachineInstanceSlug"
            }
          }
        ],
        "security": [
          {
            "BearerAuth": [
              "instances.write"
            ]
          }
        ],
        "requestBody": {
          "$ref": "#/components/requestBodies/SendMachineInstanceEvent"
        },
        "responses": {
          "200": {
            "description": "The event was delivered successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/State"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/machines/{machineSlug}/i/{instanceSlug}/v": {
      "put": {
        "tags": [
          "Machine instances"
        ],
        "summary": "Update the desired machine version for an existing instance.",
        "description": "Set the desired machine version for an existing instance.\n\nThe instance will not be upgraded immediately but will be upgraded\nthe next time an event is sent to it from a settled state.\n\nA 400 with code \"no-migration-path\" will be returned if there is\nno path through the set of existing migrations from the current\ninstance version to the desired instance version.\n",
        "parameters": [
          {
            "name": "machineSlug",
            "in": "path",
            "description": "The slug/name for the machine definition.",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/MachineSlug"
            }
          },
          {
            "name": "instanceSlug",
            "in": "path",
            "description": "The slug/name for the machine instance.",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/MachineInstanceSlug"
            }
          }
        ],
        "security": [
          {
            "BearerAuth": [
              "instances.write"
            ]
          }
        ],
        "requestBody": {
          "$ref": "#/components/requestBodies/UpdateDesiredMachineInstanceVersion"
        },
        "responses": {
          "201": {
            "description": "The desired version was recorded successfully and will be applied\nthe next time an event is sent to the instance from a settled state.\n"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/machines/{machineSlug}/i/{instanceSlug}/status": {
      "put": {
        "tags": [
          "Machine instances"
        ],
        "summary": "Update the status of a machine instance",
        "description": "Set the status of the machine.\n\nMachines in the 'paused' status will reject any events sent to them\nwith a 409 error with a code of \"invalid-state\".\n\n'running' instances will accept events normally.\n\nIt is **dangerous** to set an instance's status to 'paused'!\nYou will drop events and, because delayed events are only retried\n5 times (with ~30 seconds between each try), some delayed events\nmay be dropped and **never** sent to your machine.\n\nThis exists **purely** to stop a runaway machine instance that is\nstuck in a loop of creating too many events.\n\nThis endpoint requires admin access.\n",
        "parameters": [
          {
            "name": "machineSlug",
            "in": "path",
            "description": "The slug/name for the machine definition.",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/MachineSlug"
            }
          },
          {
            "name": "instanceSlug",
            "in": "path",
            "description": "The slug/name for the machine instance.",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/MachineInstanceSlug"
            }
          }
        ],
        "security": [
          {
            "BearerAuth": [
              "instances.admin"
            ]
          }
        ],
        "requestBody": {
          "$ref": "#/components/requestBodies/UpdateMachineInstanceStatus"
        },
        "responses": {
          "204": {
            "description": "The instance status was set.\n"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/machines/{machineSlug}/v": {
      "get": {
        "tags": [
          "Machine versions"
        ],
        "summary": "List the machine versions for this machine",
        "parameters": [
          {
            "name": "cursor",
            "in": "query",
            "description": "A cursor returned from a previous call to /machines/{machineSlug}/v",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "BearerAuth": [
              "machine-versions.read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The machine versions for this machine",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "versions"
                  ],
                  "properties": {
                    "versions": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MachineVersionInfo"
                      }
                    },
                    "cursor": {
                      "type": "string",
                      "description": "The cursor to use on the next call to retrieve the next page of machine versions.\nIf no cursor is returned, there are no more pages to retrieve.\n"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Machine versions"
        ],
        "summary": "Provisionally create a new machine definition version.",
        "description": "This operation returns a code upload URL and fields that can be used\nto upload the code for the machine definition version.\n\nOnce the code is uploaded, call `PUT /machines/:machineSlug/v/:machineDefinitionVersionId`\nwith the `machineDefinitionVersionId` returned from this operation to\nfinalize the creation of the machine definition version.\n",
        "parameters": [
          {
            "name": "machineSlug",
            "in": "path",
            "description": "The slug/name for the machine definition this version is related to.",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/MachineSlug"
            }
          },
          {
            "name": "gzip",
            "in": "query",
            "description": "Will the code be uploaded gzipped?",
            "required": false,
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "security": [
          {
            "BearerAuth": [
              "machine-versions.write"
            ]
          }
        ],
        "requestBody": {
          "$ref": "#/components/requestBodies/ProvisionallyCreateMachineDefinitionVersion"
        },
        "responses": {
          "200": {
            "description": "The machine definition version was provisionally created successfully.\n\nNow, post the code for the machine definition version as follows:\n\n```\nconst { codeUploadFields, codeUploadUrl } = await provisionalVersionCreationResponse.json();\nconst uploadForm = new FormData();\nfor (const [key, value] of Object.entries(codeUploadFields)) {\n  uploadForm.append(key, value as string);\n}\nuploadForm.set(\"content-type\", \"application/javascript\");\nuploadForm.append(\n  \"file\",\n  new Blob([\"javascript-code-here\"], {\n    type: \"application/javascript\",\n  }),\n  \"your-file-name.js\",\n);\nconst uploadRes = await fetch(\n  codeUploadUrl,\n  {\n    method: \"POST\",\n    body: uploadForm,\n  },\n);\n```\n\nAnd then finalize the creation of the machine definition version by\ncalling `PUT /machines/:machineSlug/v/:machineDefinitionVersionId` with\nthe `machineDefinitionVersionId` returned from this operation.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "machineVersionId",
                    "codeUploadUrl",
                    "codeUploadFields"
                  ],
                  "properties": {
                    "machineVersionId": {
                      "$ref": "#/components/schemas/SignedMachineVersionId"
                    },
                    "codeUploadUrl": {
                      "type": "string",
                      "description": "The URL to upload the machine definition version code to."
                    },
                    "codeUploadFields": {
                      "type": "object",
                      "description": "The fields that must be included as form data in the upload request.",
                      "additionalProperties": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/machines/{machineSlug}/v/{signedMachineVersionId}": {
      "put": {
        "tags": [
          "Machine versions"
        ],
        "summary": "Finalize creation of a machine definition version.",
        "description": "After retrieving the `machineDefinitionVersionId` and code upload\ninstructions from `POST /machines/:machineSlug/v`, and after\nuploading the code as described, call this operation to finalize\nthe creation of the machine definition version.\n\nAfter this operation, you can create instances of the machine\ndefinition with  this version.\n",
        "parameters": [
          {
            "name": "machineSlug",
            "in": "path",
            "description": "The slug/name for the machine definition this version is related to.",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/MachineSlug"
            }
          },
          {
            "name": "signedMachineVersionId",
            "in": "path",
            "description": "The signed machine version id returned from `POST /machines/:machineSlug/v`.",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/SignedMachineVersionId"
            }
          }
        ],
        "security": [
          {
            "BearerAuth": [
              "machine-versions.write"
            ]
          }
        ],
        "requestBody": {
          "$ref": "#/components/requestBodies/CreateMachineDefinitionVersion"
        },
        "responses": {
          "200": {
            "description": "The version was created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "machineVersionId"
                  ],
                  "properties": {
                    "machineVersionId": {
                      "$ref": "#/components/schemas/MachineVersionId"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/machines/{machineSlug}/migrations": {
      "post": {
        "tags": [
          "Machine version migrations"
        ],
        "summary": "Provisionally create a new machine version migration.",
        "description": "This operation returns a code upload URL and fields that can be used\nto upload the code for the machine version migration.\n\nOnce the code is uploaded, call `PUT /machines/:machineSlug/migrations/:machineVersionMigrationId`\nwith the `machineVersionMigrationId` returned from this operation to\nfinalize the creation of the machine version migration.\n",
        "parameters": [
          {
            "name": "machineSlug",
            "in": "path",
            "description": "The slug/name for the machine definition this version is related to.",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/MachineSlug"
            }
          },
          {
            "name": "gzip",
            "in": "query",
            "description": "Will the code be uploaded gzipped?",
            "required": false,
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "security": [
          {
            "BearerAuth": [
              "machine-versions.write"
            ]
          }
        ],
        "requestBody": {
          "$ref": "#/components/requestBodies/ProvisionallyCreateMachineVersionMigration"
        },
        "responses": {
          "200": {
            "description": "The machine version migration was provisionally created successfully.\n\nNow, post the code for the machine version migration as follows:\n\n```\nconst { codeUploadFields, codeUploadUrl } = await provisionalVersionMigrationCreationResponse.json();\nconst uploadForm = new FormData();\nfor (const [key, value] of Object.entries(codeUploadFields)) {\n  uploadForm.append(key, value as string);\n}\nuploadForm.set(\"content-type\", \"application/javascript\");\nuploadForm.append(\n  \"file\",\n  new Blob([\"javascript-code-here\"], {\n    type: \"application/javascript\",\n  }),\n  \"your-file-name.js\",\n);\nconst uploadRes = await fetch(\n  codeUploadUrl,\n  {\n    method: \"POST\",\n    body: uploadForm,\n  },\n);\n```\n\nAnd then finalize the creation of the machine version migration by\ncalling `PUT /machines/:machineSlug/migrations/:machineVersionMigrationId` with\nthe `machineVersionMigrationId` returned from this operation.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "machineVersionMigrationId",
                    "codeUploadUrl",
                    "codeUploadFields"
                  ],
                  "properties": {
                    "machineVersionMigrationId": {
                      "$ref": "#/components/schemas/SignedMachineVersionMigrationId"
                    },
                    "codeUploadUrl": {
                      "type": "string",
                      "description": "The URL to upload the machine definition version code to."
                    },
                    "codeUploadFields": {
                      "type": "object",
                      "description": "The fields that must be included as form data in the upload request.",
                      "additionalProperties": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/machines/{machineSlug}/migrations/{signedMachineVersionMigrationId}": {
      "put": {
        "tags": [
          "Machine version migrations"
        ],
        "summary": "Finalize creation of a machine version migration.",
        "description": "After retrieving the `machineVersionMigrationId` and code upload\ninstructions from `POST /machines/:machineSlug/migrations`, and after\nuploading the code as described, call this operation to finalize\nthe creation of the machine version migration.\n\nAfter this operation, you can upgrade existing machine instances\nusing this migration.\n",
        "parameters": [
          {
            "name": "machineSlug",
            "in": "path",
            "description": "The slug/name for the machine definition this version is related to.",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/MachineSlug"
            }
          },
          {
            "name": "signedMachineVersionMigrationId",
            "in": "path",
            "description": "The signed machine version id returned from `POST /machines/:machineSlug/v`.",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/SignedMachineVersionMigrationId"
            }
          }
        ],
        "security": [
          {
            "BearerAuth": [
              "machine-versions.write"
            ]
          }
        ],
        "requestBody": {
          "$ref": "#/components/requestBodies/CreateMachineVersionMigration"
        },
        "responses": {
          "200": {
            "description": "The version was created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "machineVersionMigrationId"
                  ],
                  "properties": {
                    "machineVersionId": {
                      "$ref": "#/components/schemas/MachineVersionMigrationId"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/logs": {
      "get": {
        "tags": [
          "Logs"
        ],
        "summary": "Retrieve logs for a time range.",
        "description": "Retrieve logs starting at the `from` time, optionally\nfiltered by `to`, `machine`, `instance`, and `version`.\n\nYou will receive at most 100 log entries (each consisting\nof potentially multiple lines) but you may receive fewer\nentries due to various partitioning schemes.\n\nYou may retry the call by specifying the returned `maxTimestamp`\nas the new `from` time to retrieve additional logs.\n",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "description": "The ISO-8601 timestamp of the earilest-timestamped log to retrieve",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/Timestamp"
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "The ISO-8601 timestamp of the latest-timestamped log to retrieve",
            "required": false,
            "schema": {
              "$ref": "#/components/schemas/Timestamp"
            }
          },
          {
            "name": "machine",
            "in": "query",
            "description": "The name of the machine to retrieve logs for",
            "required": false,
            "schema": {
              "$ref": "#/components/schemas/MachineSlug"
            }
          },
          {
            "name": "instance",
            "in": "query",
            "description": "The name of the machine instance to retrieve logs for",
            "required": false,
            "schema": {
              "$ref": "#/components/schemas/MachineInstanceSlug"
            }
          },
          {
            "name": "version",
            "in": "query",
            "description": "The id of the machine version to retrieve logs for",
            "required": false,
            "schema": {
              "$ref": "#/components/schemas/MachineVersionId"
            }
          }
        ],
        "security": [
          {
            "BearerAuth": [
              "logs.read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The requested logs.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "maxTimestamp",
                    "logs"
                  ],
                  "properties": {
                    "maxTimestamp": {
                      "$ref": "#/components/schemas/Timestamp"
                    },
                    "logs": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "timestamp",
                          "orgId",
                          "machineName",
                          "instanceName",
                          "machineVersionId",
                          "outputType",
                          "log"
                        ],
                        "properties": {
                          "timestamp": {
                            "$ref": "#/components/schemas/Timestamp"
                          },
                          "orgId": {
                            "type": "string",
                            "description": "The ID of the organization that owns the machines that produced this log."
                          },
                          "machineName": {
                            "$ref": "#/components/schemas/MachineSlug"
                          },
                          "instanceName": {
                            "$ref": "#/components/schemas/MachineInstanceSlug"
                          },
                          "machineVersionId": {
                            "$ref": "#/components/schemas/MachineVersionId"
                          },
                          "outputType": {
                            "type": "string",
                            "enum": [
                              "stdout",
                              "stderr"
                            ]
                          },
                          "log": {
                            "type": "string",
                            "description": "Raw log output"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/rt": {
      "get": {
        "tags": [
          "Realtime"
        ],
        "summary": "Subscribe to real-time updates of machine instances.",
        "description": "This is a websocket endpoint.\nConnect and send WSToServerMsg messages and receive WSToClientMsg messages.\n",
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "description": "JWT signed with your State Backed key. Same as the token typically passed in the authorization header.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/idps": {
      "get": {
        "tags": [
          "Tokens"
        ],
        "summary": "List the identity providers configured for your org",
        "parameters": [
          {
            "name": "cursor",
            "in": "query",
            "description": "A cursor returned from a previous call to /idps",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "BearerAuth": [
              "tokens.admin"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The trusted identity providers for your organization",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "idps"
                  ],
                  "properties": {
                    "idps": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "algs",
                          "mapping"
                        ],
                        "properties": {
                          "iss": {
                            "type": "string"
                          },
                          "aud": {
                            "type": "string"
                          },
                          "algs": {
                            "type": "array",
                            "items": {
                              "$ref": "#/components/schemas/SigningAlgorithm"
                            }
                          },
                          "jwksUrl": {
                            "type": "string"
                          },
                          "mapping": {
                            "type": "object",
                            "additionalProperties": true
                          }
                        }
                      }
                    },
                    "cursor": {
                      "type": "string",
                      "description": "The cursor to use on the next call to retrieve the next page of identity providers.\nIf no cursor is returned, there are no more pages to retrieve.\n"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Tokens"
        ],
        "summary": "Upsert an identity provider",
        "description": "Token exchange involves exchanging an identity provider-signed token for a\nState Backed-signed token. By adding an identity provider configuration to\nState Backed, you are instructing State Backed to trust any valid token\nfrom that identity provider when evaluating whether to allow a token exchange.\nYou are also extracting the claims from that token that you want to make available\nto your token providers to include in the State Backed token.\n\nFor example, if you are using Auth0 as your identity provider, you can configure\nState Backed to trust your Auth0 tokens by calling:\n\n```bash\ncurl -XPOST https://statebacked.dev/idps \\\n  -H 'authorization: Bearer sbsk_...'\n  --data '{\n    \"aud\": \"https://<your-auth0-domain>.us.auth0.com/api/v2/\",\n    \"iss\": \"https://<your-auth0-domain>.us.auth0.com/\",\n    \"jwksUrl\": \"https://<your-auth0-domain>.us.auth0.com/.well-known/jwks.json\",\n    \"algs\": [\"RS256\"],\n    \"mapping\": {\n      \"sub.$\": \"$.sub\",\n      \"email.$\": \"$.email\",\n      \"provider\": \"auth0\",\n    }\n  }'\n```\n\nState Backed uses the audience (`aud`) and issuer (`iss`) claims in any tokens\nprovided for exchange to identify the identity provider to use for verification.\n\nIn this example, token providers would be have access to `sub`, `email`, and `provider`\nclaims that they could include in the resultant State Backed token.\n\nUpserts may change algorithms, mappings, keys or jwksUrls.\n\nThis endpoint requires admin access.\n",
        "security": [
          {
            "BearerAuth": [
              "tokens.admin"
            ]
          }
        ],
        "requestBody": {
          "$ref": "#/components/requestBodies/UpsertIdentityProvider"
        },
        "responses": {
          "204": {
            "description": "The identity provider was created or updated."
          }
        }
      },
      "delete": {
        "tags": [
          "Tokens"
        ],
        "summary": "Delete an identity provider",
        "description": "Delete the identity provider and immediately reject any token exchange\nrequests that provide tokens signed by the idp.\n",
        "security": [
          {
            "BearerAuth": [
              "tokens.admin"
            ]
          }
        ],
        "requestBody": {
          "$ref": "#/components/requestBodies/DeleteIdentityProvider"
        },
        "responses": {
          "204": {
            "description": "The identity provider was deleted."
          }
        }
      }
    },
    "/token-providers": {
      "get": {
        "tags": [
          "Tokens"
        ],
        "summary": "List the token providers configured for your org",
        "parameters": [
          {
            "name": "cursor",
            "in": "query",
            "description": "A cursor returned from a previous call to /token-providers",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "BearerAuth": [
              "tokens.admin"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The token providers for your organization",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "tokenProviders"
                  ],
                  "properties": {
                    "tokenProviders": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "service",
                          "keyId",
                          "mapping"
                        ],
                        "properties": {
                          "service": {
                            "type": "string"
                          },
                          "keyId": {
                            "$ref": "#/components/schemas/KeyId"
                          },
                          "mapping": {
                            "type": "object",
                            "additionalProperties": true
                          }
                        }
                      }
                    },
                    "cursor": {
                      "type": "string",
                      "description": "The cursor to use on the next call to retrieve the next page of token providers.\nIf no cursor is returned, there are no more pages to retrieve.\n"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Tokens"
        ],
        "summary": "Upsert a token provider",
        "description": "Token exchange involves exchanging an identity provider-signed token for a\nState Backed-signed token.\n\nToken providers are responsible for creating State Backed tokens from a standardized\nclaim set extracted from identity provider tokens by their mappings.\n\nToken providers are identified by a service name.\nYou might, for instance, want a service name for each application that you host\nwith State Backed.\n\nToken providers also specify the State Backed key to use to sign the tokens they\ngenerate and a mapping that creates the claims for the generated token.\n\nFor example, if your identity provider mappings extract claims like this:\n\n```\n{\n  \"sub\": \"your-sub\",\n  \"email\": \"your-email\",\n  \"provider\": \"identity-provider\"\n}\n```\n\nyou could create a token provider like this:\n\n```bash\ncurl -XPOST https://statebacked.dev/token-providers \\\n  -H 'authorization: Bearer sbsk_...'\n  --data '{\n    \"keyId\": \"sbk_...\", // ID for a previously-created State Backed key\n    \"service\": \"your-app\", // any identifier for your token provider\n    \"mapping\": {\n      \"sub.$\": \"$.sub\",\n      \"email.$\": \"$.email\",\n      \"provider.$\": \"$.provider\",\n    }\n  }'\n```\n\nThat token provider would allow you to exchange any of your identity provider-\nsigned tokens for a State Backed token that includes the sub, email, and provider\nclaims, all of which would be available for your use in `allowRead` and `allowWrite`\nfunctions in your machine definitions.\n\nUpserts may change key ids and mappings.\n\nThis endpoint requires admin access.\n",
        "security": [
          {
            "BearerAuth": [
              "tokens.admin"
            ]
          }
        ],
        "requestBody": {
          "$ref": "#/components/requestBodies/UpsertTokenProvider"
        },
        "responses": {
          "204": {
            "description": "The token provider was created or updated."
          }
        }
      }
    },
    "/token-providers/{service}": {
      "delete": {
        "tags": [
          "Tokens"
        ],
        "summary": "Delete a token provider",
        "description": "Delete the token provider and immediately reject any token exchange\nrequests that request tokens using the token provider's service.\n",
        "parameters": [
          {
            "name": "service",
            "in": "path",
            "description": "The service identifier for the token provider to delete",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-zA-Z0-9_-]{1,128}$"
            }
          }
        ],
        "security": [
          {
            "BearerAuth": [
              "tokens.admin"
            ]
          }
        ],
        "responses": {
          "204": {
            "description": "The token provider was deleted."
          }
        }
      }
    },
    "/tokens": {
      "post": {
        "tags": [
          "Tokens"
        ],
        "summary": "Exchange an identity provider-signed token for a State Backed token",
        "description": "Once you have configured at least one identity provider (by posting to /idps)\nand at least one token provider (by posting to /token-providers), you can exchange\nany identity provider token for a token generated by one of your token providers.\n\nThis allows you to have completely secure, end-to-end authorization with your\nState Backed machine instances without any server-side code while using your\nidentity provider of choice.\n\nThis endpoint should generally conform to https://datatracker.ietf.org/doc/html/rfc8693\n",
        "requestBody": {
          "$ref": "#/components/requestBodies/ExchangeToken"
        },
        "responses": {
          "200": {
            "description": "Your State Backed token",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "access_token",
                    "issued_token_type",
                    "token_type"
                  ],
                  "properties": {
                    "access_token": {
                      "type": "string",
                      "description": "Your State Backed access token."
                    },
                    "issued_token_type": {
                      "type": "string",
                      "const": "urn:ietf:params:oauth:token-type:access_token"
                    },
                    "token_type": {
                      "type": "string",
                      "const": "Bearer"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/orgs": {
      "get": {
        "tags": [
          "Orgs"
        ],
        "summary": "List your organizations",
        "parameters": [
          {
            "name": "cursor",
            "in": "query",
            "description": "A cursor returned from a previous call to /orgs",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "BearerAuth": [
              "org.read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Your organizations",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "orgs"
                  ],
                  "properties": {
                    "orgs": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "id",
                          "name",
                          "role",
                          "createdAt",
                          "limits"
                        ],
                        "properties": {
                          "id": {
                            "$ref": "#/components/schemas/OrgId"
                          },
                          "name": {
                            "type": "string"
                          },
                          "role": {
                            "$ref": "#/components/schemas/OrgMemberRole"
                          },
                          "createdAt": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "limits": {
                            "type": "object",
                            "required": [
                              "monthlyEventsLimit",
                              "monthlyReadsLimit"
                            ],
                            "properties": {
                              "monthlyEventsLimit": {
                                "type": "number"
                              },
                              "monthlyReadsLimit": {
                                "type": "number"
                              }
                            }
                          }
                        }
                      }
                    },
                    "cursor": {
                      "type": "string",
                      "description": "The cursor to use on the next call to retrieve the next page of organizations.\nIf no cursor is returned, there are no more pages to retrieve.\n"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Orgs"
        ],
        "description": "Create an org\n",
        "requestBody": {
          "$ref": "#/components/requestBodies/CreateOrg"
        },
        "responses": {
          "200": {
            "description": "New org",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "id"
                  ],
                  "properties": {
                    "id": {
                      "$ref": "#/components/schemas/OrgId"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/keys": {
      "get": {
        "tags": [
          "Keys"
        ],
        "summary": "List your keys",
        "parameters": [
          {
            "name": "cursor",
            "in": "query",
            "description": "A cursor returned from a previous call to /keys",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "BearerAuth": [
              "tokens.admin"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Your keys",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "keys"
                  ],
                  "properties": {
                    "keys": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "id",
                          "name",
                          "scopes",
                          "createdAt"
                        ],
                        "properties": {
                          "id": {
                            "$ref": "#/components/schemas/KeyId"
                          },
                          "name": {
                            "type": "string"
                          },
                          "scopes": {
                            "type": "array",
                            "items": {
                              "$ref": "#/components/schemas/KeyScope"
                            }
                          },
                          "createdAt": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "cursor": {
                      "type": "string",
                      "description": "The cursor to use on the next call to retrieve the next page of keys.\nIf no cursor is returned, there are no more pages to retrieve.\n"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Keys"
        ],
        "description": "Create a key\n",
        "security": [
          {
            "BearerAuth": [
              "org.keys.write"
            ]
          }
        ],
        "requestBody": {
          "$ref": "#/components/requestBodies/CreateKey"
        },
        "responses": {
          "200": {
            "description": "New key",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "id",
                    "key"
                  ],
                  "properties": {
                    "id": {
                      "$ref": "#/components/schemas/KeyId"
                    },
                    "key": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/keys/{keyId}": {
      "delete": {
        "parameters": [
          {
            "name": "keyId",
            "in": "path",
            "description": "The key ID to delete",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/KeyId"
            }
          }
        ],
        "tags": [
          "Keys"
        ],
        "description": "Delete a key\n",
        "security": [
          {
            "BearerAuth": [
              "org.keys.write"
            ]
          }
        ],
        "responses": {
          "204": {
            "description": "The key was deleted"
          }
        }
      }
    },
    "/billing": {
      "get": {
        "tags": [
          "Billing"
        ],
        "summary": "Retrieve the link to your organization's billing portal",
        "security": [
          {
            "BearerAuth": [
              "org.write"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The link to your billing portal",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "url"
                  ],
                  "properties": {
                    "url": {
                      "type": "string",
                      "description": "The link to your billing portal"
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT"
      }
    },
    "requestBodies": {
      "CreateMachineDefinition": {
        "description": "Request to create a machine definition.",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "required": [
                "slug"
              ],
              "properties": {
                "slug": {
                  "$ref": "#/components/schemas/MachineSlug"
                },
                "indexes": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/IndexName"
                  }
                }
              }
            }
          }
        }
      },
      "ProvisionallyCreateMachineDefinitionVersion": {
        "description": "Request to provisionally create a machine definition version.",
        "content": {
          "application/json": {
            "schema": {
              "type": "object"
            }
          }
        }
      },
      "CreateMachineDefinitionVersion": {
        "description": "Finalize creation of a machine definition version.",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "clientInfo": {
                  "type": "string",
                  "description": "Informational only. Any string that describes this version.\nGood uses would be a semantic version number or git commit.\n"
                },
                "makeCurrent": {
                  "type": "boolean",
                  "description": "Whether to make this version the current version for the machine definition.\nIf `true`, the current version will be set to this version.\nIf `false`, the current version will not be changed.\n"
                },
                "indexSelectors": {
                  "type": "object",
                  "description": "Mapping from index names (must match index names from the corresponding machine)\nto JSON path selectors into machine context.\nOn every state update, the pointed-to value in machine context will be extracted\nand used to index the machine.\n",
                  "additionalProperties": {
                    "type": "string"
                  }
                }
              }
            }
          }
        }
      },
      "ProvisionallyCreateMachineVersionMigration": {
        "description": "Request to provisionally create a machine version migration.",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "required": [
                "fromMachineVersionId",
                "toMachineVersionId"
              ],
              "properties": {
                "fromMachineVersionId": {
                  "$ref": "#/components/schemas/MachineVersionId"
                },
                "toMachineVersionId": {
                  "$ref": "#/components/schemas/MachineVersionId"
                }
              }
            }
          }
        }
      },
      "CreateMachineVersionMigration": {
        "description": "Finalize creation of a machine version migration.",
        "content": {
          "application/json": {
            "schema": {
              "type": "object"
            }
          }
        }
      },
      "CreateMachineInstance": {
        "description": "Request to create an instance of a machine.\n\nIf machineVersionId is provided, creates an instance of the machine\ndefinition version with that ID. Otherwise, creates an instance of the\ncurrent version of the machine definition.\n",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "required": [
                "slug"
              ],
              "properties": {
                "slug": {
                  "$ref": "#/components/schemas/MachineInstanceSlug"
                },
                "context": {
                  "type": "object",
                  "description": "The initial context for the machine instance.\nDefaults to `{}`.\n",
                  "additionalProperties": true
                },
                "machineVersionId": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/MachineVersionId"
                    },
                    {
                      "description": "The ID of the machine definition version to use for this instance.\nIf not provided, creates an instance of the current version of the machine definition.\n"
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "SendMachineInstanceEvent": {
        "description": "Request to send an event to an instance of a machine.\n",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "required": [
                "event"
              ],
              "properties": {
                "event": {
                  "$ref": "#/components/schemas/Event"
                }
              }
            }
          }
        }
      },
      "UpdateDesiredMachineInstanceVersion": {
        "description": "Request to update the machine version for an existing instance.\n",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "required": [
                "targetMachineVersionId"
              ],
              "properties": {
                "targetMachineVersionId": {
                  "$ref": "#/components/schemas/MachineVersionId"
                }
              }
            }
          }
        }
      },
      "UpdateMachineInstanceStatus": {
        "description": "Request to update the status of an existing instance.\n",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "required": [
                "status"
              ],
              "properties": {
                "status": {
                  "$ref": "#/components/schemas/MachineInstanceStatus"
                }
              }
            }
          }
        }
      },
      "DeleteMachine": {
        "description": "Validation parameters to prove that you really do want to delete the machine.",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "description": "Validation parameters to prove that you really do want to delete the machine.",
              "required": [
                "dangerDataWillBeDeletedForever",
                "hmacSha256OfMachineNameWithMachineNameKey"
              ],
              "properties": {
                "dangerDataWillBeDeletedForever": {
                  "type": "boolean",
                  "description": "Just to ensure that you understand the ramifications of this action.",
                  "const": true
                },
                "hmacSha256OfMachineNameWithMachineNameKey": {
                  "type": "string",
                  "description": "This parameter serves to verify that you have read the documentation prior to\ndeleting a machine and have taken the time to consider whether you really want to do so.\n\nProvide `base64urlEncode(hmacSha256(key = \"machine name\", \"machine name\"))`\n"
                }
              }
            }
          }
        }
      },
      "DeleteMachineInstance": {
        "description": "Validation parameters to prove that you really do want to delete the instance.",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "description": "Validation parameters to prove that you really do want to delete the instance.",
              "required": [
                "dangerDataWillBeDeletedForever",
                "hmacSha256OfMachineInstanceNameWithMachineNameKey"
              ],
              "properties": {
                "dangerDataWillBeDeletedForever": {
                  "type": "boolean",
                  "description": "Just to ensure that you understand the ramifications of this action.",
                  "const": true
                },
                "hmacSha256OfMachineInstanceNameWithMachineNameKey": {
                  "type": "string",
                  "description": "This parameter serves to verify that you have read the documentation prior to\ndeleting a machine and have taken the time to consider whether you really want to do so.\n\nProvide `base64urlEncode(hmacSha256(key = \"machine name\", \"machine instance name\"))`\n"
                }
              }
            }
          }
        }
      },
      "UpsertIdentityProvider": {
        "description": "Identity provider configuration",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "description": "Identity provider configuration",
              "required": [
                "algs",
                "mapping"
              ],
              "properties": {
                "key": {
                  "type": "string",
                  "description": "base64url-encoded key to use to verify token signatures (one of key or jwksUrl must be provided)"
                },
                "jwksUrl": {
                  "type": "string",
                  "description": "Absolute URL at which to find a JWKS key set to verify token signatures (one of key or jwksUrl must be provided)"
                },
                "aud": {
                  "type": "string",
                  "description": "The audience claim that identifies tokens from this identity provider (one of aud or iss must be provided)"
                },
                "iss": {
                  "type": "string",
                  "description": "The issuer claim that identifies tokens from this identity provider (one of aud or iss must be provided)"
                },
                "algs": {
                  "type": "array",
                  "description": "Allowed signing algorithms",
                  "items": {
                    "$ref": "#/components/schemas/SigningAlgorithm"
                  }
                },
                "mapping": {
                  "type": "object",
                  "description": "A mapping object that extracts claims from the identity provider tokens that token providers\ncan reference when creating the claims for State Backed tokens.\n\nThe values of properties that end in \".$\" are treated as JSONPath references into the\nclaim set of the provided token.\n\nSo a mapping of `{ \"sub.$\": \"$.sub\" }` with identity provider claims of `{ \"sub\": \"user-123\" }`\nwill result in `{ \"sub\": \"user-123\" }` as the input claims into any token provider.\n",
                  "additionalProperties": true
                }
              }
            }
          }
        }
      },
      "DeleteIdentityProvider": {
        "description": "Identity provider to delete",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "description": "Identity provider to delete",
              "properties": {
                "aud": {
                  "type": "string",
                  "description": "Audience for the identity provider (at least one of aud or iss must be provided)."
                },
                "iss": {
                  "type": "string",
                  "description": "Issuer for the identity provider (at least one of aud or iss must be provided)."
                }
              }
            }
          }
        }
      },
      "UpsertTokenProvider": {
        "description": "Token provider configuration",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "description": "Token provider configuration",
              "required": [
                "keyId",
                "service",
                "mapping"
              ],
              "properties": {
                "keyId": {
                  "type": "string",
                  "description": "ID of the State Backed key (created using smply keys create or posting to /keys)\nto use to sign  tokens created by this token provider.\n"
                },
                "service": {
                  "type": "string",
                  "pattern": "^[a-zA-Z0-9_-]{1,128}$",
                  "description": "The name of this token provider. This name is used to request tokens from the /tokens endpoint"
                },
                "mapping": {
                  "type": "object",
                  "description": "A mapping object that creates the claim set for the State Backed token and may reference\nthe claims extracted by the identity provider mappings.\n\nThe values of properties that end in \".$\" are treated as JSONPath references into the\nclaim set of the provided token.\n\nSo a mapping of `{ \"sub.$\": \"$.sub\" }` with identity provider claims of `{ \"sub\": \"user-123\" }`\nwill result in `{ \"sub\": \"user-123\" }` as the State Backed token claims.\n",
                  "additionalProperties": true
                }
              }
            }
          }
        }
      },
      "ExchangeToken": {
        "description": "Exchange an identity provider-signed token for a State Backed token.",
        "content": {
          "application/x-www-form-urlencoded": {
            "schema": {
              "type": "object",
              "description": "Token exchange request",
              "required": [
                "grant_type",
                "audience",
                "requested_token_type",
                "subject_token"
              ],
              "properties": {
                "grant_type": {
                  "type": "string",
                  "description": "The type of grant being requested",
                  "const": "urn:ietf:params:oauth:grant-type:token-exchange"
                },
                "audience": {
                  "type": "string",
                  "description": "Identifies the token provider service to use to generate the token.\n\nMust be of the form: `https://tokens.statebacked.dev/<your-org-id>/<token-provider-service-id>`\n\nWhere `your-org-id` can be found via `smply orgs list` and `token-provider-service-id`\nis the `service` that you passed in your post to /token-providers.\n",
                  "example": "https://tokens.statebacked.dev/org_yourorg/your-service"
                },
                "requested_token-type": {
                  "type": "string",
                  "description": "The type of token being requested",
                  "const": "urn:ietf:params:oauth:token-type:access_token"
                },
                "subject_token": {
                  "type": "string",
                  "description": "A JWT signed by one of your configured identity providers (based on configurations posted to /idps)"
                }
              }
            }
          }
        }
      },
      "CreateKey": {
        "description": "Create a key",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "description": "Key creation request",
              "required": [
                "name"
              ],
              "properties": {
                "name": {
                  "type": "string"
                },
                "scopes": {
                  "type": "array",
                  "description": "The scopes that any request with a token signed by this key will have access to.\nYou must either pass `scopes` or `use`.\n",
                  "items": {
                    "$ref": "#/components/schemas/KeyScope"
                  }
                },
                "use": {
                  "type": "string",
                  "description": "The intended use for this key. This is a shorthand way to set a reasonable set of scopes.\nYou must either pass `scopes` or `use`.\n",
                  "enum": [
                    "production",
                    "ci"
                  ]
                }
              }
            }
          }
        }
      },
      "CreateOrg": {
        "description": "Create an org",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "description": "Org creation request",
              "required": [
                "name"
              ],
              "properties": {
                "name": {
                  "type": "string"
                },
                "allowAnonymousAccess": {
                  "type": "boolean",
                  "default": true,
                  "description": "If true, a key, trusted identity provider, and token provider\nthat support anonymous access will be created for the org.\nThe usual authorization checks still apply at the machine level\nso individual reads and writes for anonymous access depends on\nyour `allowRead` and `allowWrite` implementations.\nAuth context for anonymous tokens includes Session ID (`sid`),\nDevice ID (`did`), and `{ \"auth\": \"anonymous\" }`.\n"
                }
              }
            }
          }
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "The request was malformed.",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "error": {
                  "type": "string",
                  "description": "A description of the error."
                },
                "code": {
                  "type": "string",
                  "description": "A code specifying the type of error.\n\n- `specify-org` indicates that the user has access to multiple orgs and the operation requires specifying an organization. Pass the `x-statebacked-org-id` header to specify an org ID.\n- `invalid-parameter` indicates that one of the provided parameters was incorrect\n",
                  "enum": [
                    "specify-org",
                    "invalid-parameter"
                  ]
                },
                "parameter": {
                  "type": "string",
                  "description": "The name of the invalid parameter"
                }
              }
            }
          }
        }
      },
      "Forbidden": {
        "description": "The request was unauthorized.",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "error": {
                  "type": "string",
                  "description": "A description of the error."
                },
                "code": {
                  "type": "string",
                  "description": "A code specifying the type of error.",
                  "enum": [
                    "missing-scope",
                    "rejected-by-machine-authorizer",
                    "missing-user",
                    "missing-org"
                  ]
                }
              }
            }
          }
        }
      },
      "Conflict": {
        "description": "The resource already exists.",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "code": {
                  "type": "string",
                  "description": "Machine-readable identifier for the type of error",
                  "enum": [
                    "invalid-state"
                  ]
                },
                "error": {
                  "type": "string",
                  "description": "Human-readable identifier for the error"
                }
              }
            }
          }
        }
      }
    },
    "schemas": {
      "MachineSlug": {
        "type": "string",
        "description": "An identifier for the machine definition. Must be unique within your organization.",
        "pattern": "^[a-zA-Z0-9_-]{1,128}$",
        "minLength": 1,
        "example": "my-machine"
      },
      "SignedMachineVersionId": {
        "type": "string",
        "description": "The signed machine definition version ID."
      },
      "SignedMachineVersionMigrationId": {
        "type": "string",
        "description": "The signed machine version migration ID."
      },
      "MachineVersionId": {
        "type": "string",
        "description": "The ID of a machine definition version."
      },
      "MachineVersionMigrationId": {
        "type": "string",
        "description": "The ID of a machine version migration."
      },
      "MachineInstanceSlug": {
        "type": "string",
        "description": "An identifier for the machine instance. Must be unique within the instances for the associated machine definition.",
        "pattern": "^[a-zA-Z0-9_-]{1,128}$",
        "minLength": 1,
        "example": "user-1234"
      },
      "MachineInstanceStatus": {
        "type": "string",
        "description": "The status of a machine instance.",
        "enum": [
          "running",
          "paused"
        ]
      },
      "StateValue": {
        "description": "The state of the machine instance.\n\nFor a machine instance with in a single, top-level state, this will be a string.\nFor a machine instance in a hierarchically-nested state, it will be an object\nmapping parent states to child states.\nFor a machine instance in a parallel state, it will be an object with multiple\nkeys.\n",
        "oneOf": [
          {
            "$ref": "#/components/schemas/SimpleStateValue"
          },
          {
            "$ref": "#/components/schemas/CompoundStateValue"
          }
        ]
      },
      "SimpleStateValue": {
        "type": "string",
        "description": "A simple state"
      },
      "CompoundStateValue": {
        "type": "object",
        "description": "A compound state",
        "additionalProperties": {
          "$ref": "#/components/schemas/StateValue"
        }
      },
      "AdministrativeInstanceState": {
        "type": "object",
        "description": "The full state of a machine instance.\n",
        "required": [
          "state",
          "context",
          "tags",
          "done",
          "status",
          "machineVersion",
          "createdAt"
        ],
        "properties": {
          "state": {
            "$ref": "#/components/schemas/StateValue"
          },
          "context": {
            "description": "The full context of the machine instance.\n",
            "type": "object",
            "additionalProperties": true
          },
          "status": {
            "$ref": "#/components/schemas/MachineInstanceStatus"
          },
          "tags": {
            "type": "array",
            "description": "Array of tags for the current states",
            "items": {
              "type": "string"
            }
          },
          "done": {
            "type": "boolean",
            "description": "Is the state machine complete"
          },
          "machineVersion": {
            "$ref": "#/components/schemas/MachineVersionInfo"
          },
          "desiredMachineVersion": {
            "$ref": "#/components/schemas/MachineVersionInfo"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "State": {
        "type": "object",
        "description": "The state of a machine instance.\n",
        "examples": {
          "Simple state without public context": {
            "value": {
              "state": "idle",
              "done": false,
              "tags": [
                "some-tag"
              ]
            }
          },
          "Hierarchical state with public context": {
            "value": {
              "state": {
                "parent": {
                  "nested1": "idle"
                }
              },
              "publicContext": {
                "user": "u_1234"
              },
              "done": false,
              "tags": [
                "some-tag"
              ]
            }
          },
          "Parallel state with public context": {
            "values": {
              "state": {
                "parent": {
                  "nested1": "idle",
                  "nested2": "idle"
                }
              },
              "publicContext": {
                "connectedDocuments": [
                  "doc_1234",
                  "doc_5678"
                ]
              },
              "done": false,
              "tags": [
                "some-tag"
              ]
            }
          }
        },
        "required": [
          "ts",
          "state",
          "tags",
          "done"
        ],
        "properties": {
          "ts": {
            "type": "number",
            "description": "Timestamp for when the state was created"
          },
          "state": {
            "$ref": "#/components/schemas/StateValue"
          },
          "publicContext": {
            "description": "The public context of the machine instance.\n\nThis includes all context under the `public` key.\n",
            "type": "object",
            "additionalProperties": true
          },
          "tags": {
            "type": "array",
            "description": "Array of tags for the current states",
            "items": {
              "type": "string"
            }
          },
          "done": {
            "type": "boolean",
            "description": "Is the state machine complete"
          }
        }
      },
      "Event": {
        "description": "An event to send to a machine instance.\n",
        "oneOf": [
          {
            "$ref": "#/components/schemas/EventWithPayload"
          },
          {
            "$ref": "#/components/schemas/EventWithoutPayload"
          }
        ]
      },
      "EventWithPayload": {
        "type": "object",
        "description": "An event to send to a machine instance with a payload.\n\nEvent types and payloads are user-defined for a given machine definition.\n",
        "required": [
          "type"
        ],
        "additionalProperties": true,
        "properties": {
          "type": {
            "type": "string",
            "description": "The type of the event.\n"
          }
        }
      },
      "EventWithoutPayload": {
        "type": "string",
        "description": "An event to send to a machine.\n\nEvent types are user-defined for a given machine definition.\n"
      },
      "Timestamp": {
        "type": "string",
        "description": "A timestamp\n",
        "format": "date-time"
      },
      "WSToClientInstanceUpdateMsg": {
        "type": "object",
        "description": "Websocket message sent to the client when an instance has been updated.\nClients will receive instance update messages after subscribing to an instance.\n",
        "required": [
          "type",
          "ts",
          "machineName",
          "machineInstanceName",
          "publicContext",
          "state",
          "tags",
          "done"
        ],
        "properties": {
          "type": {
            "type": "string",
            "const": "instance-update"
          },
          "machineName": {
            "$ref": "#/components/schemas/MachineSlug"
          },
          "machineInstanceName": {
            "$ref": "#/components/schemas/MachineInstanceSlug"
          },
          "publicContext": {
            "type": "object",
            "additionalProperties": true
          },
          "ts": {
            "type": "number",
            "description": "Timestamp for when the state was created"
          },
          "state": {
            "$ref": "#/components/schemas/StateValue"
          },
          "tags": {
            "type": "array",
            "description": "Array of tags for the current states",
            "items": {
              "type": "string"
            }
          },
          "done": {
            "type": "boolean",
            "description": "Is the state machine complete"
          }
        }
      },
      "WSToClientErrorMsg": {
        "type": "object",
        "description": "Websocket message sent to the client to indicate that an error has occurred\nin processing a previously-sent message, identified by `requestId`.\n",
        "required": [
          "type",
          "requestId",
          "status"
        ],
        "properties": {
          "type": {
            "type": "string",
            "const": "error"
          },
          "requestId": {
            "type": "string",
            "description": "Request ID that caused the error."
          },
          "status": {
            "type": "number",
            "description": "HTTP status code corresponding to the error."
          },
          "code": {
            "type": "string",
            "description": "Error code"
          }
        }
      },
      "WSToClientMsg": {
        "description": "Websocket messages that may be sent to the client",
        "oneOf": [
          {
            "$ref": "#/components/schemas/WSToClientInstanceUpdateMsg"
          },
          {
            "$ref": "#/components/schemas/WSToClientErrorMsg"
          }
        ]
      },
      "WSToServerSubscribeToInstanceMsg": {
        "type": "object",
        "description": "Websocket message sent to the server to subscribe to a machine instance\n",
        "required": [
          "type",
          "requestId",
          "machineName",
          "machineInstanceName"
        ],
        "properties": {
          "type": {
            "type": "string",
            "const": "subscribe-to-instance"
          },
          "requestId": {
            "type": "string",
            "description": "ID for this request. Must be unique per connection. Used to associate errors with the request."
          },
          "machineName": {
            "$ref": "#/components/schemas/MachineSlug"
          },
          "machineInstanceName": {
            "$ref": "#/components/schemas/MachineInstanceSlug"
          }
        }
      },
      "WSToServerUnsubscribeFromInstanceMsg": {
        "type": "object",
        "description": "Websocket message sent to the server to unsubscribe from a machine instance\n",
        "required": [
          "type",
          "requestId",
          "machineName",
          "machineInstanceName"
        ],
        "properties": {
          "type": {
            "type": "string",
            "const": "unsubscribe-from-instance"
          },
          "requestId": {
            "type": "string",
            "description": "ID for this request. Must be unique per connection. Used to associate errors with the request."
          },
          "machineName": {
            "$ref": "#/components/schemas/MachineSlug"
          },
          "machineInstanceName": {
            "$ref": "#/components/schemas/MachineInstanceSlug"
          }
        }
      },
      "WSToServerPingMsg": {
        "type": "object",
        "description": "Websocket message sent to the server to keep the connection and subscriptions alive.\nMust be sent at least every 5 minutes or the connection and subscriptions will be canceled\nand will need to be reestablished.\n",
        "required": [
          "type"
        ],
        "properties": {
          "type": {
            "type": "string",
            "const": "ping"
          }
        }
      },
      "WSToServerMsg": {
        "description": "Websocket messages that may be sent to the server.",
        "oneOf": [
          {
            "$ref": "#/components/schemas/WSToServerSubscribeToInstanceMsg"
          },
          {
            "$ref": "#/components/schemas/WSToServerUnsubscribeFromInstanceMsg"
          },
          {
            "$ref": "#/components/schemas/WSToServerPingMsg"
          }
        ]
      },
      "OrgId": {
        "type": "string",
        "description": "An identifier for an organization",
        "example": "org_uHvZHpF4STWvMg8BKVCUTg"
      },
      "OrgMemberRole": {
        "type": "string",
        "description": "The role that a member has in an organization",
        "enum": [
          "admin",
          "read",
          "write"
        ]
      },
      "KeyId": {
        "type": "string",
        "description": "An identifier for a key",
        "example": "sbk_nXzdtCxESemgtxS5JX-LrA"
      },
      "KeyScope": {
        "type": "string",
        "description": "An authorization scope associated with a key.\nAny request that is signed by a JWT with this key will have access to the scopes associated with that key.\n",
        "enum": [
          "events.write",
          "events.read",
          "state.read",
          "instances.read",
          "instances.write",
          "instances.admin",
          "machines.read",
          "machines.write",
          "machines.admin",
          "machine-versions.read",
          "machine-versions.write",
          "analytics.read",
          "org.read",
          "org.write",
          "org.keys.write",
          "org-members.write",
          "logs.read",
          "tokens.admin"
        ]
      },
      "SigningAlgorithm": {
        "type": "string",
        "enum": [
          "HS256",
          "HS384",
          "HS512",
          "PS256",
          "PS384",
          "PS512",
          "RS256",
          "RS384",
          "RS512",
          "ES256",
          "ES384",
          "ES512",
          "EdDSA"
        ]
      },
      "MachineVersionInfo": {
        "type": "object",
        "required": [
          "id",
          "createdAt",
          "clientInfo"
        ],
        "properties": {
          "id": {
            "$ref": "#/components/schemas/MachineVersionId"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "clientInfo": {
            "type": "string"
          }
        }
      },
      "IndexName": {
        "type": "string",
        "description": "Name of an index on machines",
        "pattern": "^[a-zA-Z0-9_-]{1,128}$"
      }
    }
  }
}
