openapi: 3.0.3
info:
  title: Vybit OAuth2 API
  version: 1.0.0
  x-logo:
    url: 'https://vybit.net/images/vybit_logo100px.png'
    altText: 'Vybit Logo'
  description: |
    <div style="text-align: center; margin: 20px 0;">
      <img src="https://vybit.net/images/vybit_logo100px.png" alt="Vybit Logo" style="max-width: 120px;" />
    </div>

    OAuth2 authentication endpoints for the Vybit notification platform.

    This API provides secure OAuth2 authentication for third-party applications to obtain
    access tokens for Vybit user accounts.

    ## Authentication Flow

    Vybit uses the standard OAuth2 Authorization Code flow:

    1. **Authorization Request**: Direct users to `https://app.vybit.net` with OAuth parameters
    2. **Authorization Grant**: User authorizes your app and is redirected back
    3. **Token Exchange**: Exchange authorization code for access token at `/service/token`
    4. **API Access**: Use the access token with the [Developer API](https://developer.vybit.net/api-reference) (`Authorization: Bearer <token>`)

    ## PKCE Support

    Vybit supports [Proof Key for Code Exchange (PKCE, RFC 7636)](https://datatracker.ietf.org/doc/html/rfc7636)
    for public clients that cannot securely store a client secret, such as:

    - **Native/mobile applications**
    - **Single-page applications (SPAs)**
    - **MCP (Model Context Protocol) clients**
    - **CLI tools and desktop apps**

    With PKCE, the client generates a `code_verifier` and its SHA-256 hash (`code_challenge`)
    to prove authorization request ownership during token exchange — no `client_secret` required.

    PKCE can also be used alongside `client_secret` for additional security.

    ## OpenID Connect

    Vybit is also an [OpenID Connect](https://openid.net/specs/openid-connect-core-1_0.html)
    provider on top of the same authorization code flow. Discovery is published at
    `https://app.vybit.net/.well-known/openid-configuration`.

    - Request `scope=openid email rw` (and optionally a `nonce`) at the authorization endpoint
    - The token response then includes a signed RS256 `id_token` (verify it with the keys at `/.well-known/jwks.json`)
    - Call `/oauth/userinfo` with the access token to read `sub`, `email`, and `email_verified`

    `sub` is the stable Vybit account key of the person who authorized the app. `email_verified`
    is `true` only when the sign in provider verified that address.

    ## Getting Started

    1. Create a developer account at [developer.vybit.net](https://developer.vybit.net)
    2. Register your application to get client credentials
    3. Use the [@vybit/oauth2-sdk](https://www.npmjs.com/package/@vybit/oauth2-sdk) for the auth flow
    4. Use the [@vybit/api-sdk](https://www.npmjs.com/package/@vybit/api-sdk) with `{ accessToken }` for API calls

    ## Legacy Endpoints

    The `/rest/vybit_list` and `/fire/{triggerKey}` endpoints below are legacy OAuth2 API
    endpoints. For new integrations, use the [Developer API](https://developer.vybit.net/api-reference)
    with Bearer token authentication, which provides full API access.
    
  contact:
    name: Vybit Developer Support
    url: https://gitlab.com/flatirontek/vybit-sdk
    email: developer@vybit.net
  license:
    name: MIT
    url: https://opensource.org/licenses/MIT
  termsOfService: https://vybit.net/terms

servers:
  - url: https://app.vybit.net
    description: Production Vybit server

paths:
  /:
    get:
      summary: OAuth2 Authorization Endpoint
      description: |
        Initiates the OAuth2 authorization flow. Direct users to this endpoint
        to request permission to access their Vybit account.
        
        After user authorization, they will be redirected to your `redirect_uri`
        with an authorization code that can be exchanged for an access token.
        
        Note: This is the root domain with OAuth2 query parameters, not a separate
        /oauth/authorize endpoint.
        
      operationId: authorize
      tags:
        - OAuth2 Authorization
      parameters:
        - name: client_id
          in: query
          required: true
          description: Your application's client ID from Vybit developer console
          schema:
            type: string
            example: "your-client-id-123"
        - name: redirect_uri
          in: query
          required: true
          description: URI to redirect to after authorization (must match one of the redirect URIs registered for the client in the developer portal)
          schema:
            type: string
            format: uri
            example: "https://yourapp.com/oauth/callback"
        - name: response_type
          in: query
          required: true
          description: Must be "code" for authorization code flow
          schema:
            type: string
            enum: ["code"]
            example: "code"
        - name: state
          in: query
          required: false
          description: Opaque value to maintain state between request and callback (recommended for security)
          schema:
            type: string
            minLength: 8
            maxLength: 255
            example: "random-state-string-123"
        - name: code_challenge
          in: query
          required: false
          description: |
            PKCE code challenge (RFC 7636). Base64url-encoded SHA-256 hash of the `code_verifier`.
            Required when using PKCE for public clients that cannot store a `client_secret`.
          schema:
            type: string
            example: "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM"
        - name: code_challenge_method
          in: query
          required: false
          description: |
            PKCE code challenge method. Must be `S256` (SHA-256). Required when `code_challenge` is provided.
          schema:
            type: string
            enum: ["S256"]
            default: "S256"
            example: "S256"
        - name: scope
          in: query
          required: false
          description: |
            Space separated scopes. `rw` (full account access) is always granted. Add `openid`
            to receive an `id_token`, and `email` for the `email` and `email_verified` claims.
            Unsupported scopes are ignored.
          schema:
            type: string
            default: "rw"
            example: "openid email rw"
        - name: nonce
          in: query
          required: false
          description: |
            OpenID Connect nonce, echoed back in the `id_token` to prevent replay. Up to 512 characters.
          schema:
            type: string
            maxLength: 512
            example: "n-0S6_WzA2Mj"
      responses:
        '302':
          description: |
            Redirect to authorization page or back to redirect_uri.
            
            **Success**: User is redirected to `redirect_uri` with authorization code:
            `https://yourapp.com/callback?code=AUTHORIZATION_CODE&state=STATE_VALUE`
            
            **Denied**: User is redirected to `redirect_uri` with error:
            `https://yourapp.com/callback?error=access_denied&state=STATE_VALUE`
          headers:
            Location:
              description: Redirect URL
              schema:
                type: string
                example: "https://yourapp.com/callback?code=abc123&state=xyz789"
        '400':
          description: Invalid request parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: "invalid_request"
                error_description: "Missing required parameter: client_id"

  /service/token:
    post:
      summary: OAuth2 Token Exchange Endpoint
      description: |
        Exchange an authorization code for an access token.
        
        Call this endpoint after receiving an authorization code from the
        authorization callback to get an access token for API requests.
        
      operationId: exchangeToken
      tags:
        - OAuth2 Token Exchange
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              required:
                - grant_type
                - code
                - client_id
              properties:
                grant_type:
                  type: string
                  enum: ["authorization_code"]
                  description: Must be "authorization_code"
                  example: "authorization_code"
                code:
                  type: string
                  description: Authorization code received from authorization callback
                  example: "abc123def456"
                client_id:
                  type: string
                  description: Your application's client ID
                  example: "your-client-id-123"
                client_secret:
                  type: string
                  description: |
                    Your application's client secret. Required for confidential clients.
                    Can be omitted when using PKCE with `code_verifier` for public clients.
                  example: "your-client-secret-456"
                code_verifier:
                  type: string
                  description: |
                    PKCE code verifier (RFC 7636). The original random string used to generate
                    the `code_challenge` sent in the authorization request. Required when
                    `code_challenge` was used in the authorization step. Can be used instead of
                    or in addition to `client_secret`.
                  example: "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
                redirect_uri:
                  type: string
                  format: uri
                  description: |
                    The `redirect_uri` sent in the authorization request. Recommended; when sent,
                    it must match the one the authorization code was issued for.
                  example: "https://yourapp.com/oauth/callback"
      responses:
        '200':
          description: Successful token exchange
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TokenResponse'
              example:
                access_token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
                token_type: "Bearer"
                scope: "openid email rw"
                id_token: "eyJhbGciOiJSUzI1NiIsImtpZCI6Ii4uLiJ9..."
        '400':
          description: PKCE verification failed, or the `redirect_uri` does not match the authorization request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TokenErrorResponse'
              examples:
                invalid_verifier:
                  summary: Wrong code_verifier
                  value:
                    errors:
                      - message: "invalid code_verifier"
                missing_verifier:
                  summary: Code was issued with a code_challenge
                  value:
                    errors:
                      - message: "code_verifier required"
                redirect_mismatch:
                  summary: redirect_uri differs from the authorization request
                  value:
                    errors:
                      - message: "redirect_uri mismatch"
        '401':
          description: |
            Missing parameters, an invalid, expired, or already used authorization code,
            or invalid client credentials. Authorization codes are single use.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TokenErrorResponse'
              examples:
                invalid_code:
                  summary: Invalid, expired, or already used authorization code
                  value:
                    errors:
                      - message: "invalid or expired access code"
                invalid_client:
                  summary: Invalid client credentials
                  value:
                    errors:
                      - message: "invalid parameter value"
                missing_parameters:
                  summary: Missing code, client_id, or both client_secret and code_verifier
                  value:
                    errors:
                      - message: "missing required parameters"

  /service/test:
    get:
      summary: Token Validation Endpoint
      description: |
        Validate an access token and check if it's still active.
        
        Use this endpoint to verify that an access token is valid
        before making API requests.
        
      operationId: validateToken
      tags:
        - Token Validation
      security:
        - BearerAuth: []
      responses:
        '200':
          description: Token is valid
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationResponse'
              example:
                status: "ok"
        '401':
          description: Invalid or expired token
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: "invalid_token"
                error_description: "The access token is invalid or expired"

  /oauth/userinfo:
    get:
      summary: OpenID Connect UserInfo Endpoint
      description: |
        Returns claims about the person who authorized the access token. Requires a token
        granted with the `openid` scope; `email` and `email_verified` are included only
        when the `email` scope was also granted. POST is accepted as well.
      operationId: userinfo
      tags:
        - OpenID Connect
      security:
        - BearerAuth: []
      responses:
        '200':
          description: Claims for the authorized person
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserInfoResponse'
              example:
                sub: "bbxope6xhryminef"
                email: "pat@example.com"
                email_verified: true
        '401':
          description: Missing, invalid, or expired access token (see the `WWW-Authenticate` header)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: "invalid_token"
                error_description: "The access token is invalid or expired"
        '403':
          description: The access token was not granted the `openid` scope
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: "insufficient_scope"
                error_description: "The openid scope is required"

  /.well-known/openid-configuration:
    get:
      summary: OpenID Connect Discovery
      description: |
        OpenID Provider metadata. The `issuer` is `https://app.vybit.net`, and
        `/.well-known/oauth-authorization-server` serves the same document, so clients that
        only read OAuth 2.0 metadata still find the UserInfo and JWKS endpoints.
      operationId: openidConfiguration
      tags:
        - OpenID Connect
      responses:
        '200':
          description: Provider metadata
          content:
            application/json:
              schema:
                type: object
              example:
                issuer: "https://app.vybit.net"
                authorization_endpoint: "https://app.vybit.net/"
                token_endpoint: "https://app.vybit.net/service/token"
                userinfo_endpoint: "https://app.vybit.net/oauth/userinfo"
                jwks_uri: "https://app.vybit.net/.well-known/jwks.json"
                registration_endpoint: "https://app.vybit.net/oauth/register"
                scopes_supported: ["openid", "email", "rw"]
                response_types_supported: ["code"]
                response_modes_supported: ["query"]
                grant_types_supported: ["authorization_code"]
                subject_types_supported: ["public"]
                id_token_signing_alg_values_supported: ["RS256"]
                token_endpoint_auth_methods_supported: ["client_secret_post", "none"]
                code_challenge_methods_supported: ["S256"]
                claims_supported: ["iss", "sub", "aud", "exp", "iat", "nonce", "email", "email_verified"]

  /.well-known/jwks.json:
    get:
      summary: ID Token Signing Keys
      description: Public keys (JWK Set) for verifying `id_token` signatures. Match on `kid`.
      operationId: jwks
      tags:
        - OpenID Connect
      responses:
        '200':
          description: JWK Set
          content:
            application/json:
              schema:
                type: object
              example:
                keys:
                  - kty: "RSA"
                    use: "sig"
                    alg: "RS256"
                    kid: "0QcxPapUWkSpHrn6NCO1UJiOeYsc0Ip6sTxR7Yta7xo"
                    n: "vX3k..."
                    e: "AQAB"

  /rest/vybit_list:
    get:
      summary: Get User's Vybits (Legacy)
      deprecated: true
      description: |
        **Deprecated**: Use the [Developer API](https://developer.vybit.net/api-reference) `GET /vybits` endpoint with Bearer token authentication instead.

        Retrieve a list of the authenticated user's vybits (notifications).

      operationId: getVybitList
      tags:
        - Vybit Management (Legacy)
      security:
        - BearerAuth: []
      responses:
        '200':
          description: List of user's vybits
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Vybit'
              example:
                - name: "My Alert"
                  triggerKey: "abc123def456"
                - name: "Notification Sound"
                  triggerKey: "def456ghi789"
                - name: "Status Update"
                  triggerKey: "ghi789jkl012"
        '401':
          description: Invalid or expired token
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /fire/{triggerKey}:
    post:
      summary: Trigger Vybit Notification (Legacy)
      deprecated: true
      description: |
        **Deprecated**: Use the [Developer API](https://developer.vybit.net/api-reference) `POST /vybit/{key}/trigger` endpoint with Bearer token authentication instead.

        Trigger a vybit notification for the authenticated user.

      operationId: triggerVybit
      tags:
        - Vybit Triggering (Legacy)
      parameters:
        - name: triggerKey
          in: path
          required: true
          description: The trigger key of the vybit to fire
          schema:
            type: string
            example: "abc123def456"
      security:
        - BearerAuth: []
      requestBody:
        required: false
        description: Optional parameters for the notification
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TriggerOptions'
            example:
              message: "Hello from your app!"
              imageUrl: "https://example.com/image.jpg"
              linkUrl: "https://example.com"
              log: "Triggered from API integration"
      responses:
        '200':
          description: Vybit triggered successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TriggerResponse'
              example:
                result: 1
                plk: "bbxope6xhryminef"
        '401':
          description: Invalid or expired token
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Trigger key not found or not accessible
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: |
        Access token obtained from the token exchange endpoint.
        
        Include in the Authorization header as: `Bearer your_access_token`

  schemas:
    TokenResponse:
      type: object
      required:
        - access_token
        - token_type
      properties:
        access_token:
          type: string
          description: The JWT access token for authenticated API requests
          example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
        token_type:
          type: string
          description: Type of token (always "Bearer")
          example: "Bearer"
        scope:
          type: string
          description: Space separated scopes granted to the access token
          example: "openid email rw"
        id_token:
          type: string
          description: |
            OpenID Connect ID token (RS256 JWT), present only when `openid` was granted. Claims:
            `iss`, `sub`, `aud` (your client_id), `exp` (1 hour), `iat`, `nonce` (when sent at
            authorization), and `email` / `email_verified` when `email` was granted.
          example: "eyJhbGciOiJSUzI1NiIsImtpZCI6Ii4uLiJ9..."

    UserInfoResponse:
      type: object
      required:
        - sub
      properties:
        sub:
          type: string
          description: Stable Vybit account key of the person who authorized the app
          example: "bbxope6xhryminef"
        email:
          type: string
          format: email
          description: Account email address (requires the `email` scope)
          example: "pat@example.com"
        email_verified:
          type: boolean
          description: True only when the sign in provider verified this address (requires the `email` scope)
          example: true

    ValidationResponse:
      type: object
      required:
        - status
      properties:
        status:
          type: string
          description: Validation status
          enum: ["ok"]
          example: "ok"

    Vybit:
      type: object
      required:
        - name
        - triggerKey
      properties:
        name:
          type: string
          description: Display name of the vybit
          example: "My Alert"
        triggerKey:
          type: string
          description: Unique trigger key for this vybit
          example: "abc123def456"
      description: A vybit notification configuration

    TriggerOptions:
      type: object
      properties:
        message:
          type: string
          description: Optional message to display with notification
          example: "Hello from your app!"
          maxLength: 500
        imageUrl:
          type: string
          format: uri
          description: Optional image URL to attach to notification (must be a direct link to a JPG, PNG, or GIF image)
          example: "https://example.com/image.jpg"
        linkUrl:
          type: string
          format: uri
          description: Optional redirect URL when notification is tapped
          example: "https://example.com"
        log:
          type: string
          description: Optional content to append to the Vybit log (supports hyperlinks)
          example: "Triggered from API integration"
          maxLength: 1000
      description: Optional parameters for triggering a vybit notification

    TriggerResponse:
      type: object
      required:
        - result
        - plk
      properties:
        result:
          type: integer
          description: Result code (1 = success)
          example: 1
        plk:
          type: string
          description: Processing key for tracking the notification
          example: "bbxope6xhryminef"

    ErrorResponse:
      type: object
      required:
        - error
      properties:
        error:
          type: string
          description: Error code
          example: "invalid_request"
        error_description:
          type: string
          description: Human-readable error description
          example: "Missing required parameter: client_id"

    TokenErrorResponse:
      type: object
      required:
        - errors
      properties:
        errors:
          type: array
          items:
            type: object
            required:
              - message
            properties:
              message:
                type: string
                description: Human-readable error description
                example: "invalid or expired access code"

    OAuthErrorResponse:
      type: object
      required:
        - error
      properties:
        error:
          type: string
          description: OAuth2 error code
          enum:
            - "invalid_request"
            - "invalid_client"
            - "invalid_grant"
            - "unauthorized_client"
            - "unsupported_grant_type"
            - "invalid_scope"
          example: "invalid_grant"
        error_description:
          type: string
          description: Human-readable error description
          example: "The authorization code is invalid or expired"
        error_uri:
          type: string
          format: uri
          description: URI identifying a human-readable web page with error information
          example: "https://vybit.net/docs/oauth2/errors#invalid_grant"

tags:
  - name: OAuth2 Authorization
    description: |
      Endpoints for initiating the OAuth2 authorization flow.
      Direct users to these endpoints to request access permissions.
  - name: OAuth2 Token Exchange
    description: |
      Endpoints for exchanging authorization codes for access tokens.
      Use these endpoints server-side with your client credentials.
  - name: OpenID Connect
    description: |
      OpenID Connect discovery, signing keys, and the UserInfo endpoint.
  - name: Token Validation
    description: |
      Endpoints for validating and checking access tokens.
      Use these to verify tokens before making API requests.
  - name: Vybit Management (Legacy)
    description: |
      Legacy endpoints for retrieving user's vybit configurations.
      For new integrations, use the [Developer API](https://developer.vybit.net/api-reference) with Bearer token authentication.
  - name: Vybit Triggering (Legacy)
    description: |
      Legacy endpoints for triggering vybit notifications.
      For new integrations, use the [Developer API](https://developer.vybit.net/api-reference) with Bearer token authentication.

externalDocs:
  description: Vybit OAuth2 SDK and Documentation
  url: https://gitlab.com/flatirontek/vybit-sdk