openapi: "3.0.2"

info:
  title: State Backed API
  description: |
    The official State Backed API for running XState backends as a service.

    State Backed turns any XState state machine into a stateful backend.
    Spawn instances of your machines, authorize requests,
    send events, and read your instances' state without any servers
    or datastores to manage.

    View the full State Backed documentation at https://docs.statebacked.dev.

    Download the `smply` CLI at https://npmjs.com/package/smply
    or at https://github.com/state-backed/smply/releases.
  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

      Pass the JWT token in the `Authorization` header as `Bearer <token>`.

      ## Use

      State Backed authenticates requests to ensure that they include a valid JWT
      signed by one of your StateBacked keys.

      State Backed also passes the user data from the `act` claim of your JWT to your
      machine's `allowRead` and `allowWrite` functions to allow you to authorize operations
      on machine instances.

      ## Generation

      Use the key (`sbk_...`) and secret (`sbsec_...`) generated from running `smply keys create`
      to sign a JWT token with an `act` claim that includes data about your end user
      (e.g. a `sub` claim with the user's ID).

      ```
      HS256("sbsec_...", { "kid": "sbk_...", "alg": "HS256" }, { "aud": "https://api.statebacked.dev/", "act": { "sub": "..." }, ... })
      ```

      You can also use our token generation library:
      ```
      import { signToken } from "@statebacked/token";

      const jwt = await signToken({
        {
          stateBackedKeyId: process.env.STATEBACKED_KEY_ID,
          stateBackedSecretKey: process.env.STATEBACKED_SECRET_KEY,
        },
        {
          sub: "your-user-id"
        },
        {
          expires: { in: "7d" },
          issuer: "https://your-domain.com/"
        }
      });
      ```

tags:
  - name: Machines
    description: |
      # Machine definition operations.

      Machine definitions name a logical machine from which you can create
      multiple *machine instances*.

      Machine definitions have multiple *machine definition versions*, one
      of which is named as the current version.
  - name: Machine versions
    description: |
      # Machine definition version operations.

      Machine definition versions contain the actual machine definition,
      which consists of a single self-contained javascript file that
      default exports an [XState](https://xstate.js.org/docs/) state
      machine as well as an `allowRead` and `allowWrite` function.

      They are immutable and identified by an ID.
      The `clientInfo` field is informational only and intended to be used to
      identify the source of the machine version (e.g. git commit sha,
      semantic version number, timestamp, etc).

      The `allowRead` and `allowWrite` functions are called to authorize
      read and write operations on machine instances, respectively.
      Write authorization implies the authority to read the result of
      your write.

      Anything stored in the machine's context under the `public` key
      will be exposed to authorized readers and writers as `publicContext`.

      ## Example machine definition

      ```js
      import type {
        AllowRead,
        AllowWrite,
      } from "@statebacked/machine-def";
      import { createMachine } from "xstate";

      export const allowRead: AllowRead = (
        { machineInstanceName, authContext },
      ) => machineInstanceName === authContext.sub;

      export const allowWrite: AllowWrite = (
        { machineInstanceName, authContext },
      ) => machineInstanceName === authContext.sub;

      export default createMachine(...);
      ```
  - name: Machine version migrations
    description: |
      # Machine version migration operations.

      Machine version migrations are small snippets of code that migrate
      existing machine instances from one machine version to another.
      They consist of a single self-contained javascript file that
      exports `upgradeState` and `upgradeContext` functions.

      ## Example machine version migration

      ```js
      import type {
        UpgradeState,
        UpgradeContext,
      } from "@statebacked/machine-def";

      export const upgradeState: UpgradeState = (oldState, oldContext) => {
        // oldState is an array describing a full path to a state in the old version of the machine
        // (e.g. ["authenticationPage", "login"] if login is a child state of authenticationPage).
        // oldContext is the most recent context of the machine. It *may not* be a valid context in
        // the given state because upgradeState will be called to migrate history states in
        // addition to current states.

        // a simple renaming of a parent state from "authenticationPage" to "newAuthenticationPage".
        return oldState[0] === "authenticationPage" ? ["newAuthenticationPage"].concat(oldState.slice(1)) : oldState;
      };

      export const upgradeContext: UpgradeContext = (oldStates, newStates, oldContext) => {
        // oldStates is an array of state paths from the old version of the machine.
        // (e.g. [["a", "b"], ["a", "c"]] if "a" is a parallel state and is in both "b" and "c").
        // newStates is an array of state paths from the new version of the machine.
        // oldContext is the context associated with oldStates in the old version of the machine.

        return {
          ...oldContext,
          upgradeCount: oldContext.upgradeCount + 1
        };
      };
      ```
  - name: Machine instances
    description: |
      # Machine instance operations.

      Machine instances are instances of a machine definition.

      If you have a machine definition that controls a user onboarding flow,
      you would want a machine instance for each user.

      If you have a machine definition that controls access to a collaborative
      document, you would want a machine instance for each document.

      Machine instances have names that are unique within a machine definition.

      You can send events to machine instances to trigger transitions and can
      read the state of an instance.

      ## Consistency guarantees

      Processing for a machine instance is coordinated such that there is
      a single, linearizable history of events and state transitions.

      ## Limits

      Machine context (represented as JSON) may not exceed 400kb.
  - name: Logs
    description: |
      # Logs produced while processing transitions and migrations.

      State Backed collects any logs you emit while processing
      transitions and migrations and makes them available via this
      endpoint.

      State Backed also emits some logs of its own to provide context.

      We *highly* recommend logging structured JSON objects.
  - name: Realtime
    description: |
      # Realtime (websocket) endpoints

      State Backed allows you to subscribe to machine instance updates
      via websockets.
  - name: Tokens
    description: |
      # Endpoints for retrieving State Backed authorization tokens

      All calls to State Backed must include a JWT signed by one of your
      State Backed keys in the authorization header.

      The token should include an `act` claim that contains claims about your end user,
      which will be passed into your machine authorization functions (`allowRead` and `allowWrite`)
      as `authContext`. This allows you to, for instance, ensure that users can only send events
      to their own machine instances or instances that your application logic permits.

      These tokens are easily created by the [@statebacked/token](https://www.npmjs.com/package/@statebacked/token)
      library.

      However, the signing keys to create these tokens **must always** be kept secret and cannot
      be included in client-side applications.

      To allow you to use any authentication provider you want and to use State Backed without
      any server-side code, you can use our token exchange endpoints to securely exchange
      a JWT from your authentication provider for a State Backed JWT with the appropriate user claims.

      After configuring trusted identity providers and token providers, you will be able to
      exchange a JWT from any of your trusted identity providers for a token with claims and a signature
      generated by any of your token providers by posting to the /tokens endpoint.

      First, configure your trusted identity providers by posting to /idps.
      You will have to specify the audience and/or issuer that identifies a token from this IDP as well
      as the JWKS url where State Backed can retrieve the appropriate verification keys or
      the secret key that State Backed can use to verify the token (keys are stored encrypted in the
      State Backed vault).
      You will also provide a mapping, which extracts claims from the identity provider token
      to make them available to token providers.

      Then, configure your token providers by posting to /token-providers.
      You will give your token provider a service name to identify it (you might want a different
      service for each app you build, for instance) and a mapping that specifies the
      claims that should be included in any token generated by that token provider.

      After that, you have completely secure, end-to-end authenticated user sessions available
      in your State Backed backend without any server-side presence.

      ## Mappings

      Mappings are JSON objects whose structure matches the desired output of the mapping and whose
      values can refer to items from the input.

      A mapping of:

      ```javascript
      {
        "sub.$": "$.sub",
        "email.$": "$.email",
        "service": "my-service",
        "special": {
          "role.$": "$.role.id"
        }
      }
      ```

      With input of:

      ```javascript
      {
        "sub": "my-user-id",
        "email": "my-email@statebacked.dev",
        "role": {
          "id": "my-role"
        }
      }
      ```

      Will produce a result of:

      ```javascript
      {
        "sub": "my-user-id", // mapping keys that end in ".$" treat values as JSONPath expressions into the input
        "email": "my-email@statebacked.dev",
        "service": "my-service", // mapping keys that don't end in ".$" treat values as constants (unless the value is a mapping)
        "special": {
          "role": "my-role" // mappings are resolved recursively
        }
      }
      ```
  - 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.
                      If no cursor is returned, there are no more pages to retrieve.
    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
        you create a machine definition version for it.
      requestBody:
        $ref: "#/components/requestBodies/CreateMachineDefinition"
      responses:
        "201":
          description: The machine definition was created successfully.
        "403":
          $ref: "#/components/responses/Forbidden"
        "400":
          $ref: "#/components/responses/BadRequest"
  /machines/{machineSlug}:
    get:
      tags:
        - Machines
      summary: Get a machine definition.
      description: |
        Retrieve the machine definition
      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.

        The `allowWrite` function for the machine definition version will be called
        to authorize the initial transition and, if it fails, a 403 with code
        `rejected-by-machine-authorizer` will be returned.

        Otherwise, the state of the machine instance after the initial transition
        will be returned.

        All top-level events have a 10 second timeout for the machine to settle.
        Settling means that the machine has reached a stable state and has no
        child services running.

        If the machine does not settle within 10 seconds but has completed at least
        one transition successfully, a 200 with the current state will be returned,
        the child services will be stopped, and error events will be delivered for
        each stopped service before the next event is sent.

        If a machine instance for this (`machineSlug`, instance `slug`) already exists,
        a 409 will be returned.
      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.

        *THIS IS OBVIOUSLY A DANGEROUS OPERATION AND WILL INTENTIONALLY CAUSE DATA LOSS*

        If any instances exist for the machine, a 409 error will be returned with an `invalid-state` code.
        You can delete the instances and then retry the machine deletion.

        All versions associated with the machine and all migrations between those versions
        will be deleted.

        There is no option to recover data after a machine is deleted.

        To prevent accidental deletion, we require two validation parameters:
          - hmacSha256OfMachineNameWithMachineNameKey - `base64urlEncode(hmacSha256(key = "machine name", "machine name"))`
          - dangerDataWillBeDeletedForever - true

        A 400 error with the `parameter` set to the name of the incorrect parameter will be returned
        if the validation parameters are incorrect.

        This endpoint requires admin access.
      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.
                      If no cursor is returned, there are no more pages to retrieve.
  /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
        calling `POST /machines/{machineSlug}` and may have had events sent to it
        by calling `POST /machines/{machineSlug}/i/{instanceSlug}/events`.

        The `allowRead` function for the machine definition version will be called
        to authorize the read and, if it fails, a 403 with code
        `rejected-by-machine-authorizer` will be returned.

        Otherwise, the current state of the machine instance will be returned.

        Obviously, the state returned may be out of date by the time it is returned
        because reads are non-blocking but a the returned state will always be
        self-consistent.
      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.

        *THIS IS OBVIOUSLY A DANGEROUS OPERATION AND WILL INTENTIONALLY CAUSE DATA LOSS*

        All historical transitions associated with the machine and all current state and context
        will be deleted.

        There is no option to recover data after an instance is deleted.

        To prevent accidental deletion, we require two validation parameters:
          - hmacSha256OfMachineInstanceNameWithMachineNameKey - `base64urlEncode(hmacSha256(key = "machine name", "machine instance name"))`
          - dangerDataWillBeDeletedForever - true

        A 400 error with the `parameter` set to the name of the incorrect parameter will be returned
        if the validation parameters are incorrect.

        This endpoint requires admin access.
      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,
        query for instances of the provided machine that have an indexed value that matches the
        provided filters.
      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
        calling `POST /machines/{machineSlug}` and may have had events sent to it
        by calling `POST /machines/{machineSlug}/i/{instanceSlug}/events`.

        No machine authorizers will be called to authorize this read and it returns
        private context data so this requires instances.admin scope.

        The full context for the instance (instead of only public context) will be returned.
      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.
                      If no cursor is returned, there are no more pages to retrieve.
    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
        calling `POST /machines/{machineSlug}`.

        The `allowWrite` function for the machine definition version will be called
        to authorize the send and, if it fails, a 403 with code
        `rejected-by-machine-authorizer` will be returned.

        Otherwise, the state of the machine instance after any transitions resulting
        from the event will be returned.

        The request will wait for the machine to settle before returning a response.
        Settling means that the machine has reached a stable state and has no
        child services running.

        All top-level events have a 10 second timeout for the machine to settle.

        If the machine does not settle within 10 seconds but has completed at least
        one transition successfully, a 200 with the current state will be returned,
        the child services will be stopped, and error events will be delivered for
        each stopped service before the next event is sent.
      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.

        The instance will not be upgraded immediately but will be upgraded
        the next time an event is sent to it from a settled state.

        A 400 with code "no-migration-path" will be returned if there is
        no path through the set of existing migrations from the current
        instance version to the desired instance version.
      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
            the next time an event is sent to the instance from a settled state.
        "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.

        Machines in the 'paused' status will reject any events sent to them
        with a 409 error with a code of "invalid-state".

        'running' instances will accept events normally.

        It is **dangerous** to set an instance's status to 'paused'!
        You will drop events and, because delayed events are only retried
        5 times (with ~30 seconds between each try), some delayed events
        may be dropped and **never** sent to your machine.

        This exists **purely** to stop a runaway machine instance that is
        stuck in a loop of creating too many events.

        This endpoint requires admin access.
      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.
        "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.
                      If no cursor is returned, there are no more pages to retrieve.
    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
        to upload the code for the machine definition version.

        Once the code is uploaded, call `PUT /machines/:machineSlug/v/:machineDefinitionVersionId`
        with the `machineDefinitionVersionId` returned from this operation to
        finalize the creation of the machine definition version.
      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.

            Now, post the code for the machine definition version as follows:

            ```
            const { codeUploadFields, codeUploadUrl } = await provisionalVersionCreationResponse.json();
            const uploadForm = new FormData();
            for (const [key, value] of Object.entries(codeUploadFields)) {
              uploadForm.append(key, value as string);
            }
            uploadForm.set("content-type", "application/javascript");
            uploadForm.append(
              "file",
              new Blob(["javascript-code-here"], {
                type: "application/javascript",
              }),
              "your-file-name.js",
            );
            const uploadRes = await fetch(
              codeUploadUrl,
              {
                method: "POST",
                body: uploadForm,
              },
            );
            ```

            And then finalize the creation of the machine definition version by
            calling `PUT /machines/:machineSlug/v/:machineDefinitionVersionId` with
            the `machineDefinitionVersionId` returned from this operation.
          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
        "403":
          $ref: "#/components/responses/Forbidden"
        "400":
          $ref: "#/components/responses/BadRequest"
  /machines/{machineSlug}/v/{signedMachineVersionId}:
    put:
      tags:
        - Machine versions
      summary: Finalize creation of a machine definition version.
      description: |
        After retrieving the `machineDefinitionVersionId` and code upload
        instructions from `POST /machines/:machineSlug/v`, and after
        uploading the code as described, call this operation to finalize
        the creation of the machine definition version.

        After this operation, you can create instances of the machine
        definition with  this version.
      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
        to upload the code for the machine version migration.

        Once the code is uploaded, call `PUT /machines/:machineSlug/migrations/:machineVersionMigrationId`
        with the `machineVersionMigrationId` returned from this operation to
        finalize the creation of the machine version migration.
      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.

            Now, post the code for the machine version migration as follows:

            ```
            const { codeUploadFields, codeUploadUrl } = await provisionalVersionMigrationCreationResponse.json();
            const uploadForm = new FormData();
            for (const [key, value] of Object.entries(codeUploadFields)) {
              uploadForm.append(key, value as string);
            }
            uploadForm.set("content-type", "application/javascript");
            uploadForm.append(
              "file",
              new Blob(["javascript-code-here"], {
                type: "application/javascript",
              }),
              "your-file-name.js",
            );
            const uploadRes = await fetch(
              codeUploadUrl,
              {
                method: "POST",
                body: uploadForm,
              },
            );
            ```

            And then finalize the creation of the machine version migration by
            calling `PUT /machines/:machineSlug/migrations/:machineVersionMigrationId` with
            the `machineVersionMigrationId` returned from this operation.
          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
        "403":
          $ref: "#/components/responses/Forbidden"
        "400":
          $ref: "#/components/responses/BadRequest"
  /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
        instructions from `POST /machines/:machineSlug/migrations`, and after
        uploading the code as described, call this operation to finalize
        the creation of the machine version migration.

        After this operation, you can upgrade existing machine instances
        using this migration.
      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
        filtered by `to`, `machine`, `instance`, and `version`.

        You will receive at most 100 log entries (each consisting
        of potentially multiple lines) but you may receive fewer
        entries due to various partitioning schemes.

        You may retry the call by specifying the returned `maxTimestamp`
        as the new `from` time to retrieve additional logs.
      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.
        Connect and send WSToServerMsg messages and receive WSToClientMsg messages.
      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.
                      If no cursor is returned, there are no more pages to retrieve.
    post:
      tags:
        - Tokens
      summary: Upsert an identity provider
      description: |
        Token exchange involves exchanging an identity provider-signed token for a
        State Backed-signed token. By adding an identity provider configuration to
        State Backed, you are instructing State Backed to trust any valid token
        from that identity provider when evaluating whether to allow a token exchange.
        You are also extracting the claims from that token that you want to make available
        to your token providers to include in the State Backed token.

        For example, if you are using Auth0 as your identity provider, you can configure
        State Backed to trust your Auth0 tokens by calling:

        ```bash
        curl -XPOST https://statebacked.dev/idps \
          -H 'authorization: Bearer sbsk_...'
          --data '{
            "aud": "https://<your-auth0-domain>.us.auth0.com/api/v2/",
            "iss": "https://<your-auth0-domain>.us.auth0.com/",
            "jwksUrl": "https://<your-auth0-domain>.us.auth0.com/.well-known/jwks.json",
            "algs": ["RS256"],
            "mapping": {
              "sub.$": "$.sub",
              "email.$": "$.email",
              "provider": "auth0",
            }
          }'
        ```

        State Backed uses the audience (`aud`) and issuer (`iss`) claims in any tokens
        provided for exchange to identify the identity provider to use for verification.

        In this example, token providers would be have access to `sub`, `email`, and `provider`
        claims that they could include in the resultant State Backed token.

        Upserts may change algorithms, mappings, keys or jwksUrls.

        This endpoint requires admin access.
      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
        requests that provide tokens signed by the idp.
      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.
                      If no cursor is returned, there are no more pages to retrieve.
    post:
      tags:
        - Tokens
      summary: Upsert a token provider
      description: |
        Token exchange involves exchanging an identity provider-signed token for a
        State Backed-signed token.

        Token providers are responsible for creating State Backed tokens from a standardized
        claim set extracted from identity provider tokens by their mappings.

        Token providers are identified by a service name.
        You might, for instance, want a service name for each application that you host
        with State Backed.

        Token providers also specify the State Backed key to use to sign the tokens they
        generate and a mapping that creates the claims for the generated token.

        For example, if your identity provider mappings extract claims like this:

        ```
        {
          "sub": "your-sub",
          "email": "your-email",
          "provider": "identity-provider"
        }
        ```

        you could create a token provider like this:

        ```bash
        curl -XPOST https://statebacked.dev/token-providers \
          -H 'authorization: Bearer sbsk_...'
          --data '{
            "keyId": "sbk_...", // ID for a previously-created State Backed key
            "service": "your-app", // any identifier for your token provider
            "mapping": {
              "sub.$": "$.sub",
              "email.$": "$.email",
              "provider.$": "$.provider",
            }
          }'
        ```

        That token provider would allow you to exchange any of your identity provider-
        signed tokens for a State Backed token that includes the sub, email, and provider
        claims, all of which would be available for your use in `allowRead` and `allowWrite`
        functions in your machine definitions.

        Upserts may change key ids and mappings.

        This endpoint requires admin access.
      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
        requests that request tokens using the token provider's service.
      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)
        and at least one token provider (by posting to /token-providers), you can exchange
        any identity provider token for a token generated by one of your token providers.

        This allows you to have completely secure, end-to-end authorization with your
        State Backed machine instances without any server-side code while using your
        identity provider of choice.

        This endpoint should generally conform to https://datatracker.ietf.org/doc/html/rfc8693
      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.
                      If no cursor is returned, there are no more pages to retrieve.
    post:
      tags:
        - Orgs
      description: |
        Create an org
      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.
                      If no cursor is returned, there are no more pages to retrieve.
    post:
      tags:
        - Keys
      description: |
        Create a key
      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
      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.
                  Good uses would be a semantic version number or git commit.
              makeCurrent:
                type: boolean
                description: |
                  Whether to make this version the current version for the machine definition.
                  If `true`, the current version will be set to this version.
                  If `false`, the current version will not be changed.
              indexSelectors:
                type: object
                description: |
                  Mapping from index names (must match index names from the corresponding machine)
                  to JSON path selectors into machine context.
                  On every state update, the pointed-to value in machine context will be extracted
                  and used to index the machine.
                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.

        If machineVersionId is provided, creates an instance of the machine
        definition version with that ID. Otherwise, creates an instance of the
        current version of the machine definition.
      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.
                  Defaults to `{}`.
                additionalProperties: true
              machineVersionId:
                allOf:
                  - $ref: "#/components/schemas/MachineVersionId"
                  - description: |
                      The ID of the machine definition version to use for this instance.
                      If not provided, creates an instance of the current version of the machine definition.
    SendMachineInstanceEvent:
      description: |
        Request to send an event to an instance of a machine.
      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.
      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.
      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
                  deleting a machine and have taken the time to consider whether you really want to do so.

                  Provide `base64urlEncode(hmacSha256(key = "machine name", "machine name"))`
    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
                  deleting a machine and have taken the time to consider whether you really want to do so.

                  Provide `base64urlEncode(hmacSha256(key = "machine name", "machine instance name"))`
    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
                  can reference when creating the claims for State Backed tokens.

                  The values of properties that end in ".$" are treated as JSONPath references into the
                  claim set of the provided token.

                  So a mapping of `{ "sub.$": "$.sub" }` with identity provider claims of `{ "sub": "user-123" }`
                  will result in `{ "sub": "user-123" }` as the input claims into any token provider.
                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)
                  to use to sign  tokens created by this token provider.
              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
                  the claims extracted by the identity provider mappings.

                  The values of properties that end in ".$" are treated as JSONPath references into the
                  claim set of the provided token.

                  So a mapping of `{ "sub.$": "$.sub" }` with identity provider claims of `{ "sub": "user-123" }`
                  will result in `{ "sub": "user-123" }` as the State Backed token claims.
                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.

                  Must be of the form: `https://tokens.statebacked.dev/<your-org-id>/<token-provider-service-id>`

                  Where `your-org-id` can be found via `smply orgs list` and `token-provider-service-id`
                  is the `service` that you passed in your post to /token-providers.
                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.
                  You must either pass `scopes` or `use`.
                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.
                  You must either pass `scopes` or `use`.
                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
                  that support anonymous access will be created for the org.
                  The usual authorization checks still apply at the machine level
                  so individual reads and writes for anonymous access depends on
                  your `allowRead` and `allowWrite` implementations.
                  Auth context for anonymous tokens includes Session ID (`sid`),
                  Device ID (`did`), and `{ "auth": "anonymous" }`.

  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.

                  - `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.
                  - `invalid-parameter` indicates that one of the provided parameters was incorrect
                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.

        For a machine instance with in a single, top-level state, this will be a string.
        For a machine instance in a hierarchically-nested state, it will be an object
        mapping parent states to child states.
        For a machine instance in a parallel state, it will be an object with multiple
        keys.
      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.
      required:
        - state
        - context
        - tags
        - done
        - status
        - machineVersion
        - createdAt
      properties:
        state:
          $ref: "#/components/schemas/StateValue"
        context:
          description: |
            The full context of the machine instance.
          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.
      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.

            This includes all context under the `public` key.
          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.
      oneOf:
        - $ref: "#/components/schemas/EventWithPayload"
        - $ref: "#/components/schemas/EventWithoutPayload"
    EventWithPayload:
      type: object
      description: |
        An event to send to a machine instance with a payload.

        Event types and payloads are user-defined for a given machine definition.
      required:
        - type
      additionalProperties: true
      properties:
        type:
          type: string
          description: |
            The type of the event.
    EventWithoutPayload:
      type: string
      description: |
        An event to send to a machine.

        Event types are user-defined for a given machine definition.
    Timestamp:
      type: string
      description: |
        A timestamp
      format: date-time
    WSToClientInstanceUpdateMsg:
      type: object
      description: |
        Websocket message sent to the client when an instance has been updated.
        Clients will receive instance update messages after subscribing to an instance.
      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
        in processing a previously-sent message, identified by `requestId`.
      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
      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
      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.
        Must be sent at least every 5 minutes or the connection and subscriptions will be canceled
        and will need to be reestablished.
      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.
        Any request that is signed by a JWT with this key will have access to the scopes associated with that key.
      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}$"
