openapi: 3.1.0
info:
  title: DevCycle Bucketing API
  description:
    Documents the DevCycle Bucketing API which provides an API interface to User Bucketing and for Server SDKs configured to use Cloud Bucketing.
  version: 1.3.0

servers:
  - url: https://bucketing-api.devcycle.com/

tags:
  - name: EdgeDB
  - name: Bucketing API
  - name: Feature Opt-in
  - name: OpenFeature Remote Evaluation API (OFREP)
    description:
      "See the OpenFeature documentation for more information: https://github.com/open-feature/protocol"

paths:
  /v1/variables/{key}:
    post:
      summary: Get variable by key for user data
      tags:
        - Bucketing API
      operationId: 'getVariableByKey'
      security:
        - bearerAuth: []
      parameters:
        - name: key
          in: path
          description: Variable key
          required: true
          schema:
            type: string
            pattern: '^[a-z0-9-_.]+$'
            example: "my-variable-key"

      requestBody:
        $ref: '#/components/requestBodies/UserRequestBody'

      responses:
        200:
          description: Variable response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Variable'
              example:
                _id: "614ef6ea475129459160721a"
                _feature: "614ef6aa473928459060721a"
                key: "my-variable-key"
                type: "String"
                value: "feature-enabled"
        400:
          $ref: '#/components/responses/BadRequest'
        401:
          $ref: '#/components/responses/UnauthorizedError'
        404:
          $ref: '#/components/responses/NotFound'
        500:
          $ref: '#/components/responses/InternalServerError'

  /v1/variables:
    post:
      summary: Get all variables by key for user data
      tags:
        - Bucketing API
      security:
        - bearerAuth: []
      operationId: 'getVariables'
      requestBody:
        $ref: '#/components/requestBodies/UserRequestBody'
      responses:
        200:
          description: Variables response (map of variable keys to Variable objects)
          content:
            application/json:
              schema:
                type: object
                additionalProperties:
                  $ref: '#/components/schemas/Variable'
              example:
                "my-string-var":
                  _id: "614ef6ea475129459160721a"
                  _feature: "614ef6aa473928459060721a"
                  key: "my-string-var"
                  type: "String"
                  value: "enabled"
                "my-boolean-var":
                  _id: "614ef6ea475139459160721b"
                  _feature: "614ef6aa473928459060721b"
                  key: "my-boolean-var"
                  type: "Boolean"
                  value: true
        400:
          $ref: '#/components/responses/BadRequest'
        401:
          $ref: '#/components/responses/UnauthorizedError'
        404:
          $ref: '#/components/responses/NotFound'
        500:
          $ref: '#/components/responses/InternalServerError'

  /v1/features:
    post:
      summary: Get all features by key for user data
      tags:
        - Bucketing API
      operationId: 'getFeatures'
      security:
        - bearerAuth: []
      requestBody:
        $ref: '#/components/requestBodies/UserRequestBody'
      responses:
        200:
          description: Features response (map of Feature keys to Feature objects)
          content:
            application/json:
              schema:
                type: object
                additionalProperties:
                  $ref: '#/components/schemas/Feature'
              example:
                "new-dashboard":
                  _id: "614ef6aa473928459060721a"
                  key: "new-dashboard"
                  type: "release"
                  _variation: "614ef6ea4753284590607216"
                  variationName: "Variation On"
                  variationKey: "variation-on"
        400:
          $ref: '#/components/responses/BadRequest'
        401:
          $ref: '#/components/responses/UnauthorizedError'
        404:
          $ref: '#/components/responses/NotFound'
        500:
          $ref: '#/components/responses/InternalServerError'

  /v1/track:
    post:
      summary: Post events to DevCycle for user
      tags:
        - Bucketing API
      security:
        - bearerAuth: []
      operationId: 'postEvents'
      requestBody:
        $ref: '#/components/requestBodies/TrackRequestBody'
      responses:
        201:
          description: Events received successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    example: "Successfully received 2 events."
        400:
          $ref: '#/components/responses/BadRequest'
        401:
          $ref: '#/components/responses/UnauthorizedError'
        404:
          $ref: '#/components/responses/NotFound'
        500:
          $ref: '#/components/responses/InternalServerError'

  /v1/edgedb/{user_id}:
    patch:
      summary: Update EdgeDB data based on unique id and project id
      tags:
        - EdgeDB
      parameters:
        - in: path
          name: user_id
          schema:
            type: string
          required: true
          description: A unique id used to fetch and update the EdgeDB data
        - name: sdkKey
          in: query
          description: SDK Key as defined in the dashboard for your DevCycle environment.
            One of the Authorization header or "sdkKey" query parameter is required to be set.
          required: false
          schema:
            type: string

      requestBody:
        description: User properties to update the EdgeDB data
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UserDataEdgeDB'

      responses:
        200:
          description: Client SDK API Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EdgeDBApiResponse'
        400:
          $ref: '#/components/responses/BadRequest'
        401:
          $ref: '#/components/responses/UnauthorizedError'
        404:
          $ref: '#/components/responses/NotFound'
        500:
          $ref: '#/components/responses/InternalServerError'

  /v1/optInConfig:
    get:
      summary: Fetch opt-in config
      description: Fetch a project config to display features available for Opt-in
      tags:
        - Feature Opt-in
      parameters:
        - name: sdkKey
          in: query
          description: SDK Key as defined in the dashboard for your DevCycle environment.
            One of the Authorization header or "sdkKey" query parameter is required to be set.
          required: false
          schema:
            type: string
        - name: user_id
          in: query
          description: Unique id to identify the user
          required: true
          schema:
            type: string
      responses:
        200:
          description: Opt-in Config Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OptInConfigResponse'
              example:
                settings:
                  _project: "614df3aa473928459060721a"
                  enabled: true
                  title: "Feature Preview"
                  description: "Opt in to preview upcoming features"
                  imageURL: "https://example.com/preview.png"
                  colors:
                    primary: "#0066ff"
                    secondary: "#ffffff"
                  poweredByAlignment: "center"
                features:
                  - id: "614ef6aa473928459060721a"
                    name: "New Dashboard"
                    description: "Try our redesigned dashboard interface"
                    resolution: "explicit"
                    status: true
                  - id: "614ef6aa473928459060721b"
                    name: "Beta Analytics"
                    description: "Access to new analytics features"
                    resolution: "implicit"
                    status: false
        400:
          $ref: '#/components/responses/BadRequest'
        401:
          $ref: '#/components/responses/UnauthorizedError'
        500:
          $ref: '#/components/responses/InternalServerError'

  /v1/optIns/{user_id}:
    put:
      summary: Update opt-in preferences for a user
      tags:
        - Feature Opt-in
      parameters:
        - name: user_id
          in: path
          description: Unique user_id to identify the user
          required: true
          schema:
            type: string
        - name: sdkKey
          in: query
          description: SDK Key as defined in the dashboard for your DevCycle environment.
            One of the Authorization header or "sdkKey" query parameter is required to be set.
          required: false
          schema:
            type: string
      requestBody:
        description: A map of feature IDs to boolean values. The value should be true if the user has opted in, false otherwise
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UserOptInData'
            example:
              "614ef6aa473928459060721a": true
              "614ef6aa473928459060721b": false
      responses:
        200:
          description: Opt-in update success response
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    example: "Successfully updated user opt-in data for environment with key 'production'."
        400:
          $ref: '#/components/responses/BadRequest'
        401:
          $ref: '#/components/responses/UnauthorizedError'
        403:
          $ref: '#/components/responses/ForbiddenError'
        500:
          $ref: '#/components/responses/InternalServerError'

  /ofrep/v1/evaluate/flags/{key}:
    post:
      tags:
        - OpenFeature Remote Evaluation API (OFREP)
      summary:
        OFREP Single Flag Evaluation Request
      description:
        Return a single flag for a user context
      parameters:
        - name: key
          in: path
          required: true
          schema:
            type: string
            pattern: '^[a-z0-9-_.]+$'
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ofrep-evaluationRequest'
      responses:
        '200':
          description: OFREP successful evaluation response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ofrep-evaluationSuccess'
        '400':
          description: Bad evaluation request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ofrep-evaluationFailure'
        '404':
          description: Flag not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ofrep-flagNotFound'
        '401':
          description: Unauthorized - You need credentials to access the API
        '403':
          description: Forbidden - You are not authorized to access the API
        '429':
          description: Rate limit reached on the Flag Management System
          headers:
            Retry-Later:
              description: Indicates when to retry the request again
              schema:
                type: string
                format: date-time
                examples:
                  - '2024-02-07T12:00:00Z'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ofrep-generalErrorResponse'

  /ofrep/v1/evaluate/flags:
    post:
      tags:
        - OpenFeature Remote Evaluation API (OFREP)
      summary:
        OFREP Bulk Evaluation Request
      description:
        Return all flag values for a user context.
      parameters:
        - in: header
          name: If-None-Match
          description: The request will be processed only if ETag doesn't match any of the values listed.
          schema:
            type: string
          required: false
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ofrep-bulkEvaluationRequest'
      responses:
        '200':
          description: OFREP successful evaluation response
          headers:
            ETag:
              schema:
                type: string
              description: Entity tag used for cache validation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ofrep-bulkEvaluationSuccess'
        '304':
          description: Bulk evaluation is not modified
        '400':
          description: Bad evaluation request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ofrep-bulkEvaluationFailure'
        '401':
          description: Unauthorized - You need credentials to access the API
        '403':
          description: Forbidden - You are not authorized to access the API
        '429':
          description: Rate limit reached on the Flag Management System
          headers:
            Retry-Later:
              description: Indicates when to retry the request again
              schema:
                type: string
                format: date-time
                examples:
                  - '2024-02-07T12:00:00Z'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ofrep-generalErrorResponse'
components:
  securitySchemes:
    bearerAuth:
      type: apiKey
      in: header
      name: Authorization
      description: 'Enter your DevCycle SDK token'

  schemas:
    UserDataEdgeDB:
      type: object
      properties:
        user_id:
          description: Unique id to identify the user
          type: string

        email:
          description: User's email used to identify the user on the dashaboard / target audiences
          type: string

        name:
          description: User's name used to idenify the user on the dashaboard / target audiences
          type: string

        language:
          description: User's language in ISO 639-1 format
          additionalProperties:
            type: string
            maxLength: 2

        country:
          description: User's country in ISO 3166 alpha-2 format
          additionalProperties:
            type: string
            maxLength: 2

        customData:
          description: User's custom data to target the user with, data will be logged to DevCycle for use in dashboard.
          type: object

        lastSeenDate:
          description: Date the user was last seen being used, Unix epoch timestamp format
          type: number

      required:
        - user_id

    UserData:
      type: object
      properties:
        user_id:
          type: string
          description: Unique id to identify the user
        email:
          type: string
          description: User's email used to identify the user on the dashboard / target audiences
        name:
          type: string
          description: User's name used to identify the user on the dashboard / target audiences
        language:
          type: string
          description: User's language in ISO 639-1 format
          maxLength: 2
        country:
          type: string
          description: User's country in ISO 3166 alpha-2 format
          maxLength: 2
        appVersion:
          type: string
          description: App Version of the running application
        appBuild:
          type: string
          description: App Build number of the running application
        customData:
          type: object
          description: User's custom data to target the user with, data will be logged to DevCycle for use in dashboard.
        privateCustomData:
          type: object
          description:
            User's custom data to target the user with, data will not be logged to DevCycle
            only used for feature bucketing.
        createdDate:
          type: number
          description: Date the user was created, Unix epoch timestamp format
        lastSeenDate:
          type: number
          description: Date the user was created, Unix epoch timestamp format
        platform:
          type: string
          description: Platform the Client SDK is running on
        platformVersion:
          type: string
          description: Version of the platform the Client SDK is running on
        deviceModel:
          type: string
          description: User's device model
        sdkType:
          type: string
          description: DevCycle SDK type
          enum: [api, server, client, mobile]
        sdkVersion:
          type: string
          description: DevCycle SDK Version
      required:
        - user_id

    UserDataAndEventsBody:
      type: object
      properties:
        events:
          type: array
          items:
            $ref: '#/components/schemas/Event'
        user:
          $ref: '#/components/schemas/UserData'

    UserOptInData:
      type: object
      additionalProperties:
        type: boolean

    EvalReason:
      type: object
      description: The reason the User was Served this particular value
      properties:
        reason:
          type: string
          description: Evaluation reason
          enum: [TARGETING_MATCH, DEFAULT, STATIC]
        details:
          type: string
          description: Detailed description of evaluation
        target_id:
          type: string
          description: ID of the targeting rule that matched
      required:
        - reason

    Feature:
      type: object
      properties:
        _id:
          type: string
          description: unique database id
        key:
          type: string
          pattern: '^[a-z0-9-_.]+$'
          description: Unique key by Project, can be used in the SDK / API to reference by 'key' rather than _id.
        type:
          type: string
          description: Feature type
          enum: [ 'release', 'experiment', 'permission', 'ops' ]
        _variation:
          type: string
          description: Bucketed feature variation ID
        variationName:
          type: string
          description: Bucketed feature variation Name
        variationKey:
          type: string
          description: Bucketed feature variation Key
        eval:
          $ref: '#/components/schemas/EvalReason'
      required:
        - _id
        - key
        - type
        - _variation

    Variable:
      type: object
      properties:
        _id:
          type: string
          description: unique database id
        _feature:
          type: string
          description: Feature ID that this variable belongs to
        key:
          type: string
          pattern: '^[a-z0-9-_.]+$'
          description: Unique key by Project, can be used in the SDK / API to reference by 'key' rather than _id.
        type:
          type: string
          description: Variable type
          enum: [ 'String', 'Boolean', 'Number', 'JSON' ]
        value:
          description: Variable value can be a string, number, boolean, or JSON
          oneOf:
            - type: string
            - type: number
            - type: boolean
            - type: object
        eval:
          $ref: '#/components/schemas/EvalReason'
      required:
        - _id
        - key
        - type
        - value

    Event:
      type: object
      properties:
        type:
          type: string
          description: Custom event type
        target:
          type: string
          description: Custom event target / subject of event. Contextual to event type
        date:
          type: number
          description: Unix epoch time the event occurred according to client
        value:
          type: number
          description: Value for numerical events. Contextual to event type
        metaData:
          type: object
          description: Extra JSON metadata for event. Contextual to event type
      required:
        - type

    EdgeDBApiResponse:
      type: object
      properties:
        message:
          description: Response message of the request
          type: string
      required:
        - message

    OptInConfigResponse:
      type: object
      properties:
        settings:
          type: object
          properties:
            _project:
              type: string
              description: Project ID
            enabled:
              type: boolean
              description: Whether opt-in is enabled for this project
            title:
              type: string
              description: Title for the opt-in UI
            description:
              type: string
              description: Description for the opt-in UI
            imageURL:
              type: string
              description: Image URL for the opt-in UI
            colors:
              type: object
              properties:
                primary:
                  type: string
                  description: Primary color hex code
                secondary:
                  type: string
                  description: Secondary color hex code
            poweredByAlignment:
              type: string
              description: Alignment for "powered by" text
              enum: [left, center, right]
        features:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
                description: Feature ID
              name:
                type: string
                description: Public name of the feature
              description:
                type: string
                description: Public description of the feature
              resolution:
                type: string
                enum: [explicit, implicit]
                description: Whether the opt-in status was explicitly set by the user or implicitly defaulted
              status:
                type: boolean
                description: Current opt-in status for this feature
      required:
        - settings
        - features

    ErrorResponse:
      type: object
      properties:
        statusCode:
          type: number
          description: Error response status code
        message:
          type: string
          description: Error message
        data:
          type: object
          description: Additional error information detailing the error reasoning
      required:
        - message

    # OpenFeature Remote Evaluation API Schemas
    ofrep-bulkEvaluationRequest:
      description: Evaluate multiple flags in one request
      properties:
        context:
          $ref: '#/components/schemas/ofrep-context'
    ofrep-bulkEvaluationSuccess:
      description: Success response for the bulk evaluation request
      type: object
      properties:
        flags:
          type: array
          items:
            oneOf:
              - $ref: "#/components/schemas/ofrep-evaluationSuccess"
              - $ref: '#/components/schemas/ofrep-evaluationFailure'

    ofrep-bulkEvaluationFailure:
      description: Bulk evaluation failure response
      properties:
        errorCode:
          type: string
          description: An appropriate  code specific to the bulk evaluation error. See https://openfeature.dev/specification/types#error-code
        errorDetails:
          type: string
          description: Optional error details description for logging or other needs
      required:
        - errorCode

    ofrep-evaluationRequest:
      description: Flag evaluation request
      properties:
        context:
          $ref: '#/components/schemas/ofrep-context'

    ofrep-evaluationSuccess:
      description: Flag evaluation success response.
      allOf:
        - properties:
            key:
              $ref: "#/components/schemas/ofrep-key"
            reason:
              type: string
              enum: [ STATIC,TARGETING_MATCH,SPLIT,DISABLED,UNKNOWN ]
              description: An OpenFeature reason for the evaluation
            variant:
              type: string
              description: Variant of the evaluated flag value
            metadata:
              type: object
              description: Arbitrary metadata supporting flag evaluation
        - oneOf:
            - $ref: "#/components/schemas/ofrep-booleanFlag"
            - $ref: "#/components/schemas/ofrep-stringFlag"
            - $ref: "#/components/schemas/ofrep-integerFlag"
            - $ref: "#/components/schemas/ofrep-floatFlag"
            - $ref: "#/components/schemas/ofrep-objectFlag"

    ofrep-evaluationFailure:
      description: Flag evaluation failure response
      properties:
        key:
          $ref: '#/components/schemas/ofrep-key'
        errorCode:
          type: string
          enum: [ PARSE_ERROR,TARGETING_KEY_MISSING,INVALID_CONTEXT,GENERAL ]
          description: OpenFeature compatible error code. See https://openfeature.dev/specification/types#error-code
        errorDetails:
          $ref: '#/components/schemas/ofrep-errorDetails'
      required:
        - key
        - errorCode

    ofrep-flagNotFound:
      description: Flag not found response
      properties:
        key:
          $ref: '#/components/schemas/ofrep-key'
        errorCode:
          type: string
          enum: [ FLAG_NOT_FOUND ]
        errorDetails:
          $ref: '#/components/schemas/ofrep-errorDetails'
      required:
        - key
        - errorCode

    ofrep-generalErrorResponse:
      description: A general error response from the service
      properties:
        errorDetails:
          $ref: '#/components/schemas/ofrep-errorDetails'

    ofrep-key:
      type: string
      pattern: '^[a-z0-9-_.]+$'
      description: Feature flag key
      examples:
        - my-flag

    ofrep-context:
      type: object
      description: Context information for flag evaluation

    ofrep-booleanFlag:
      description: A boolean typed flag value
      properties:
        value:
          type: boolean
          description: Flag evaluation result
      required:
        - value

    ofrep-stringFlag:
      description: A string typed flag value
      properties:
        value:
          type: string
          description: Flag evaluation result
          examples:
            - "my-flag-value"
      required:
        - value

    ofrep-integerFlag:
      description: An integer typed flag value
      properties:
        value:
          type: integer
          description: Flag evaluation result
          examples:
            - 3
      required:
        - value

    ofrep-floatFlag:
      description: A float typed flag value
      properties:
        value:
          type: number
          description: Flag evaluation result
          examples:
            - 3.1415
      required:
        - value

    ofrep-objectFlag:
      description: An object typed flag value
      properties:
        value:
          type: object
          description: Flag evaluation result
      required:
        - value

    ofrep-errorDetails:
      type: string
      description: An error description for logging or other needs

  requestBodies:
    UserRequestBody:
      required: true
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/UserData'
          example:
            user_id: "user_123456"
            email: "user@example.com"
            name: "John Doe"
            country: "US"
            customData:
              plan: "premium"
              signupDate: "2024-01-01"

    TrackRequestBody:
      required: true
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/UserDataAndEventsBody'
          example:
            user:
              user_id: "user_123456"
              email: "user@example.com"
              customData:
                plan: "premium"
            events:
              - type: "purchase"
                value: 99.99
                metaData:
                  item: "subscription"
                  currency: "USD"
              - type: "feature_click"
                target: "new-dashboard-button"

  responses:
    # 400
    BadRequest:
      description: Bad Request
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'

    # 401
    UnauthorizedError:
      description: Missing 'sdkKey' query parameter or 'Authorization' header
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'

    # 403
    ForbiddenError:
      description: Forbidden - Feature not enabled or insufficient permissions
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'

    # 404
    NotFound:
      description: Not Found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'

    # 500
    InternalServerError:
      description: Internal Server Error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
