openapi: 3.1.0
info:
  title: Authlane Control Plane API
  version: 1.0.0
  description: >-
    Configure tenant services, connect external users, read connection state, and obtain
    user-scoped tool definitions. Authlane is a control plane: provider tool traffic goes
    directly from the SaaS runtime to each provider and never passes through this API.
  license:
    name: MIT
    identifier: MIT
servers:
  - url: https://app.authlane.io
    description: Authlane Cloud
  - url: http://localhost:3000
    description: Local development
tags:
  - name: Catalog
  - name: Connections
  - name: Tools
  - name: Connect sessions
  - name: Hosted connect
  - name: OAuth
security:
  - ApiKey: []
paths:
  /api/v1/catalog/services:
    get:
      tags: [Catalog]
      operationId: listCatalogServices
      summary: List tenant-enabled services
      x-authlane-scope: catalog:read
      responses:
        '200':
          description: Tenant service catalog
          headers: &RateLimitedSuccessHeaders
            X-RateLimit-Limit:
              $ref: '#/components/headers/RateLimitLimit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/RateLimitRemaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/RateLimitReset'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CatalogResult'
              examples:
                catalog:
                  $ref: '#/components/examples/CatalogResponseExample'
        '401':
          $ref: '#/components/responses/Error'
        '403':
          $ref: '#/components/responses/Error'
        '429':
          $ref: '#/components/responses/RateLimited'
  /api/v1/users/{externalUserId}/connections:
    get:
      tags: [Connections]
      operationId: listUserConnections
      summary: List effective connection states
      description: Includes tenant-enabled services that are not yet connected.
      x-authlane-scope: connections:read
      parameters:
        - $ref: '#/components/parameters/ExternalUserId'
      responses:
        '200':
          description: Connection state snapshot
          headers: *RateLimitedSuccessHeaders
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConnectionsResult'
              examples:
                connections:
                  $ref: '#/components/examples/ConnectionsResponseExample'
        '400':
          $ref: '#/components/responses/Error'
        '401':
          $ref: '#/components/responses/Error'
        '403':
          $ref: '#/components/responses/Error'
        '429':
          $ref: '#/components/responses/RateLimited'
  /api/v1/users/{externalUserId}/capabilities:
    get:
      tags: [Connections, Tools]
      operationId: getUserCapabilities
      summary: Get states and tool definitions in one hot read
      x-authlane-scope: connections:read
      parameters:
        - $ref: '#/components/parameters/ExternalUserId'
        - $ref: '#/components/parameters/ToolFormat'
      responses:
        '200':
          description: Versioned capability snapshot
          headers: *RateLimitedSuccessHeaders
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CapabilitiesResult'
              examples:
                capabilities:
                  $ref: '#/components/examples/CapabilitiesResponseExample'
        '400':
          $ref: '#/components/responses/Error'
        '401':
          $ref: '#/components/responses/Error'
        '403':
          $ref: '#/components/responses/Error'
        '429':
          $ref: '#/components/responses/RateLimited'
  /api/v1/users/{externalUserId}/tools:
    get:
      tags: [Tools]
      operationId: listUserTools
      summary: List definitions for usable connections
      description: Returns definitions only. Authlane never executes a tool.
      x-authlane-scope: connections:read
      parameters:
        - $ref: '#/components/parameters/ExternalUserId'
        - $ref: '#/components/parameters/ToolFormat'
      responses:
        '200':
          description: Versioned MCP tools or OpenAI functions
          headers: *RateLimitedSuccessHeaders
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ToolsResult'
              examples:
                mcp:
                  $ref: '#/components/examples/ToolsResponseExample'
                openai:
                  $ref: '#/components/examples/OpenAIToolsResponseExample'
        '400':
          $ref: '#/components/responses/Error'
        '401':
          $ref: '#/components/responses/Error'
        '403':
          $ref: '#/components/responses/Error'
        '429':
          $ref: '#/components/responses/RateLimited'
  /api/v1/users/{externalUserId}/connections/{serviceId}/credential-leases:
    post:
      tags: [Connections]
      operationId: createCredentialLease
      summary: Issue access-only credential material
      description: >-
        Server-only, audited, non-cacheable response for direct provider execution.
        OAuth refresh and ID tokens are never returned.
      x-authlane-scope: credentials:issue
      parameters:
        - $ref: '#/components/parameters/ExternalUserId'
        - $ref: '#/components/parameters/ServiceId'
      responses:
        '201':
          description: Short-lived credential lease
          headers:
            X-RateLimit-Limit:
              $ref: '#/components/headers/RateLimitLimit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/RateLimitRemaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/RateLimitReset'
            Cache-Control:
              schema:
                type: string
                const: no-store, private
            Pragma:
              schema:
                type: string
                const: no-cache
            Referrer-Policy:
              schema:
                type: string
                const: no-referrer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CredentialLeaseResult'
              examples:
                oauth:
                  $ref: '#/components/examples/CredentialLeaseResponseExample'
        '400':
          $ref: '#/components/responses/Error'
        '401':
          $ref: '#/components/responses/Error'
        '403':
          $ref: '#/components/responses/Error'
        '404':
          $ref: '#/components/responses/Error'
        '409':
          $ref: '#/components/responses/Error'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/Error'
  /api/v1/connect-sessions:
    post:
      tags: [Connect sessions]
      operationId: createConnectSession
      summary: Create a browser handoff session
      x-authlane-scope: connect-sessions:create
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateConnectSession'
            examples:
              connectSession:
                $ref: '#/components/examples/CreateConnectSessionRequestExample'
      responses:
        '201':
          description: Raw session token returned once
          headers:
            X-RateLimit-Limit:
              $ref: '#/components/headers/RateLimitLimit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/RateLimitRemaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/RateLimitReset'
            Cache-Control:
              schema:
                type: string
                const: no-store, private
            Pragma:
              schema:
                type: string
                const: no-cache
            Referrer-Policy:
              schema:
                type: string
                const: no-referrer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConnectSessionResult'
              examples:
                connectSession:
                  $ref: '#/components/examples/CreateConnectSessionResponseExample'
        '400':
          $ref: '#/components/responses/Error'
        '401':
          $ref: '#/components/responses/Error'
        '403':
          $ref: '#/components/responses/Error'
        '429':
          $ref: '#/components/responses/RateLimited'
  /api/v1/connect/session:
    post:
      tags: [Hosted connect]
      operationId: getHostedConnectSession
      summary: Resolve a hosted connect session
      security:
        - ConnectSession: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ConnectAction'
            examples:
              parentOrigin:
                $ref: '#/components/examples/HostedConnectSessionRequestExample'
      responses:
        '200':
          description: Hosted connect session and effective services
          headers:
            X-RateLimit-Limit:
              $ref: '#/components/headers/RateLimitLimit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/RateLimitRemaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/RateLimitReset'
            Cache-Control:
              schema:
                type: string
                const: no-store, private
            Pragma:
              schema:
                type: string
                const: no-cache
            Referrer-Policy:
              schema:
                type: string
                const: no-referrer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HostedConnectSessionResult'
              examples:
                hostedSession:
                  $ref: '#/components/examples/HostedConnectSessionResponseExample'
        '401':
          $ref: '#/components/responses/Error'
        '403':
          $ref: '#/components/responses/Error'
        '429':
          $ref: '#/components/responses/RateLimited'
  /api/v1/connect/{serviceId}/authorize:
    post:
      tags: [Hosted connect]
      operationId: authorizeConnection
      summary: Begin provider authorization
      security:
        - ConnectSession: []
      parameters:
        - $ref: '#/components/parameters/ServiceId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ConnectAction'
            examples:
              parentOrigin:
                $ref: '#/components/examples/AuthorizeRequestExample'
      responses:
        '200':
          description: Provider authorization URL
          headers:
            X-RateLimit-Limit:
              $ref: '#/components/headers/RateLimitLimit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/RateLimitRemaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/RateLimitReset'
            Cache-Control:
              schema:
                type: string
                const: no-store, private
            Pragma:
              schema:
                type: string
                const: no-cache
            Referrer-Policy:
              schema:
                type: string
                const: no-referrer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthorizeResult'
              examples:
                authorizationUrl:
                  $ref: '#/components/examples/AuthorizeResponseExample'
        '400':
          $ref: '#/components/responses/Error'
        '401':
          $ref: '#/components/responses/Error'
        '403':
          $ref: '#/components/responses/Error'
        '404':
          $ref: '#/components/responses/Error'
        '409':
          $ref: '#/components/responses/Error'
        '429':
          $ref: '#/components/responses/RateLimited'
  /api/v1/connect/{serviceId}:
    delete:
      tags: [Hosted connect]
      operationId: disconnectConnection
      summary: Disconnect an external user's service
      description: Requires a connect session minted after recent tenant-controlled reauthentication.
      security:
        - ConnectSession: []
      parameters:
        - $ref: '#/components/parameters/ServiceId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ConnectAction'
            examples:
              parentOrigin:
                $ref: '#/components/examples/DisconnectRequestExample'
      responses:
        '200':
          description: Updated disconnected state
          headers:
            X-RateLimit-Limit:
              $ref: '#/components/headers/RateLimitLimit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/RateLimitRemaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/RateLimitReset'
            Cache-Control:
              schema:
                type: string
                const: no-store, private
            Pragma:
              schema:
                type: string
                const: no-cache
            Referrer-Policy:
              schema:
                type: string
                const: no-referrer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DisconnectResult'
              examples:
                disconnected:
                  $ref: '#/components/examples/DisconnectResponseExample'
        '401':
          $ref: '#/components/responses/Error'
        '403':
          $ref: '#/components/responses/Error'
        '429':
          $ref: '#/components/responses/RateLimited'
  /api/v1/oauth/{serviceId}/callback:
    get:
      tags: [OAuth]
      operationId: oauthCallback
      summary: Validate state and exchange the provider code
      security: []
      parameters:
        - $ref: '#/components/parameters/ServiceId'
        - name: code
          in: query
          schema:
            type: string
            example: provider-code-redacted
        - name: state
          in: query
          required: true
          schema:
            type: string
            example: opaque-state-redacted
        - name: error
          in: query
          schema:
            type: string
      responses:
        '302':
          description: Redirect to the hosted connect callback
          headers:
            X-RateLimit-Limit:
              $ref: '#/components/headers/RateLimitLimit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/RateLimitRemaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/RateLimitReset'
            Cache-Control:
              schema:
                type: string
                const: no-store, private
            Pragma:
              schema:
                type: string
                const: no-cache
            Referrer-Policy:
              schema:
                type: string
                const: no-referrer
            Location:
              schema:
                type: string
                format: uri
                example: https://app.authlane.io/connect/callback?status=connected&serviceId=github
        '400':
          $ref: '#/components/responses/Error'
        '409':
          $ref: '#/components/responses/Error'
        '429':
          $ref: '#/components/responses/RateLimited'
webhooks:
  connectionLifecycle:
    post:
      operationId: receiveConnectionLifecycleEvent
      summary: Receive a signed connection lifecycle event
      parameters:
        - name: X-Authlane-Signature
          in: header
          required: true
          description: Hex HMAC-SHA256 of `<timestamp>.<raw-body>` using the webhook secret.
          schema:
            type: string
            pattern: '^[0-9a-f]{64}$'
        - name: X-Authlane-Timestamp
          in: header
          required: true
          schema:
            type: string
        - name: X-Authlane-Event
          in: header
          required: true
          schema:
            $ref: '#/components/schemas/WebhookEventName'
        - name: Idempotency-Key
          in: header
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebhookEvent'
            examples:
              connection:
                $ref: '#/components/examples/ConnectionWebhookExample'
      responses:
        '200':
          description: Event accepted
        '204':
          description: Event accepted without a response body
components:
  securitySchemes:
    ApiKey:
      type: http
      scheme: bearer
      bearerFormat: Authlane scoped API key
      description: Server-only tenant API key.
    ConnectSession:
      type: apiKey
      in: header
      name: Authorization
      description: Use `ConnectSession <opaque-token>` from the hosted browser handoff.
  parameters:
    ExternalUserId:
      name: externalUserId
      in: path
      required: true
      description: Opaque 1–255 character identifier from the authenticated SaaS session.
      schema:
        type: string
        minLength: 1
        maxLength: 255
    ServiceId:
      name: serviceId
      in: path
      required: true
      schema:
        type: string
        pattern: '^[a-z0-9-]+$'
    ToolFormat:
      name: format
      in: query
      schema:
        type: string
        enum: [mcp, openai]
        default: mcp
  headers:
    RateLimitLimit:
      description: Maximum requests in the current window.
      schema:
        type: integer
    RateLimitRemaining:
      description: Remaining requests in the current window.
      schema:
        type: integer
    RateLimitReset:
      description: Unix timestamp when the current window resets.
      schema:
        type: integer
  responses:
    Error:
      description: Authlane request error
      headers: *RateLimitedSuccessHeaders
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResult'
          examples:
            error:
              $ref: '#/components/examples/ErrorResponseExample'
    RateLimited:
      description: Rate limit exceeded
      headers:
        X-RateLimit-Limit:
          $ref: '#/components/headers/RateLimitLimit'
        X-RateLimit-Remaining:
          $ref: '#/components/headers/RateLimitRemaining'
        X-RateLimit-Reset:
          $ref: '#/components/headers/RateLimitReset'
        Retry-After:
          schema:
            type: integer
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResult'
          examples:
            rateLimited:
              $ref: '#/components/examples/RateLimitedResponseExample'
  schemas:
    AuthlaneError:
      type: object
      required: [message, code]
      properties:
        message:
          type: string
        code:
          type: string
        hint:
          type: string
        docUrl:
          type: string
          format: uri
        statusCode:
          type: integer
          minimum: 100
          maximum: 599
    ErrorResult:
      type: object
      required: [data, error]
      properties:
        data:
          type: 'null'
        error:
          $ref: '#/components/schemas/AuthlaneError'
    Service:
      type: object
      required: [id, name, authType, enabled]
      properties:
        id:
          type: string
        name:
          type: string
        authType:
          type: string
          enum: [oauth2, api_key]
        enabled:
          type: boolean
    CatalogResult:
      type: object
      required: [data, error]
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/Service'
        error:
          type: 'null'
    Connection:
      type: object
      required: [serviceId, status, connected, expiresAt]
      properties:
        serviceId:
          type: string
        status:
          type: string
          enum: [disconnected, pending, connected, expired, error]
        connected:
          type: boolean
        expiresAt:
          type: [string, 'null']
          format: date-time
        connectedAt:
          type: [string, 'null']
          format: date-time
        lastCheckedAt:
          type: [string, 'null']
          format: date-time
        errorCode:
          type: [string, 'null']
    ConnectionsResult:
      type: object
      required: [data, error]
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/Connection'
        error:
          type: 'null'
    JsonSchema:
      type: object
      additionalProperties: true
    ToolDefinition:
      oneOf:
        - $ref: '#/components/schemas/McpToolDefinition'
        - $ref: '#/components/schemas/OpenAIFunctionDefinition'
    McpToolDefinition:
      type: object
      required: [name, description, inputSchema]
      properties:
        name:
          type: string
        description:
          type: string
        inputSchema:
          $ref: '#/components/schemas/JsonSchema'
    OpenAIFunctionDefinition:
      type: object
      required: [name, description, parameters]
      properties:
        name:
          type: string
        description:
          type: string
        parameters:
          $ref: '#/components/schemas/JsonSchema'
    CapabilityService:
      allOf:
        - $ref: '#/components/schemas/Connection'
        - type: object
          required: [tools]
          properties:
            tools:
              type: array
              items:
                $ref: '#/components/schemas/ToolDefinition'
    CapabilitiesResult:
      type: object
      required: [data, error]
      properties:
        data:
          type: object
          required: [externalUserId, format, version, services]
          properties:
            externalUserId:
              type: string
            format:
              type: string
              enum: [mcp, openai]
            version:
              type: string
            services:
              type: array
              items:
                $ref: '#/components/schemas/CapabilityService'
        error:
          type: 'null'
    ToolsResult:
      type: object
      required: [data, error]
      properties:
        data:
          oneOf:
            - type: object
              required: [tools, version]
              properties:
                tools:
                  type: array
                  items:
                    $ref: '#/components/schemas/McpToolDefinition'
                version:
                  type: string
            - type: object
              required: [functions, version]
              properties:
                functions:
                  type: array
                  items:
                    $ref: '#/components/schemas/OpenAIFunctionDefinition'
                version:
                  type: string
        error:
          type: 'null'
    CredentialLeaseResult:
      type: object
      required: [data, error]
      properties:
        data:
          oneOf:
            - $ref: '#/components/schemas/OAuthCredentialLease'
            - $ref: '#/components/schemas/ApiKeyCredentialLease'
        error:
          type: 'null'
    OAuthCredentialLease:
      type: object
      required: [type, leaseId, accessToken, tokenType, scopes, expiresAt]
      properties:
        type:
          const: oauth2
        leaseId:
          type: string
          format: uuid
        accessToken:
          type: string
        tokenType:
          type: string
        scopes:
          type: array
          items:
            type: string
        expiresAt:
          type: [string, 'null']
          format: date-time
    ApiKeyCredentialLease:
      type: object
      required: [type, leaseId, value, placement, expiresAt]
      properties:
        type:
          const: api_key
        leaseId:
          type: string
          format: uuid
        value:
          type: string
        placement:
          $ref: '#/components/schemas/ApiKeyPlacement'
        expiresAt:
          type: [string, 'null']
          format: date-time
    ApiKeyPlacement:
      oneOf:
        - type: object
          required: [type, name]
          properties:
            type:
              const: header
            name:
              type: string
            prefix:
              type: string
        - type: object
          required: [type, name]
          properties:
            type:
              const: query
            name:
              type: string
    CreateConnectSession:
      type: object
      required: [externalUserId, allowedServices, allowedOrigin]
      properties:
        externalUserId:
          type: string
          minLength: 1
          maxLength: 255
        allowedServices:
          type: array
          description: An empty array snapshots all services currently enabled for the tenant.
          items:
            type: string
            pattern: '^[a-z0-9-]+$'
        allowedOrigin:
          type: string
          format: uri
        expiresInSeconds:
          type: integer
          minimum: 60
          maximum: 900
          default: 600
        reauthenticatedAt:
          type: string
          format: date-time
          description: Required for disconnect and accepted only within the recent-reauthentication window.
    ConnectSessionResult:
      type: object
      required: [data, error]
      properties:
        data:
          type: object
          required: [id, token, url, expiresAt]
          properties:
            id:
              type: string
              format: uuid
            token:
              type: string
            url:
              type: string
              format: uri
            expiresAt:
              type: string
              format: date-time
        error:
          type: 'null'
    HostedConnectService:
      type: object
      required: [id, name, authType, status]
      properties:
        id:
          type: string
        name:
          type: string
        authType:
          type: string
          enum: [oauth2, api_key]
        status:
          type: string
          enum: [disconnected, pending, connected, expired, error]
    HostedConnectSessionResult:
      type: object
      required: [data, error]
      properties:
        data:
          type: object
          required: [externalUserId, services, expiresAt]
          properties:
            externalUserId:
              type: string
            services:
              type: array
              items:
                $ref: '#/components/schemas/HostedConnectService'
            expiresAt:
              type: string
              format: date-time
        error:
          type: 'null'
    ConnectAction:
      type: object
      required: [parentOrigin]
      properties:
        parentOrigin:
          type: string
          format: uri
    AuthorizeResult:
      type: object
      required: [data, error]
      properties:
        data:
          type: object
          required: [authorizationUrl]
          properties:
            authorizationUrl:
              type: string
              format: uri
        error:
          type: 'null'
    DisconnectResult:
      type: object
      required: [data, error]
      properties:
        data:
          type: object
          required: [disconnected]
          properties:
            disconnected:
              type: boolean
        error:
          type: 'null'
    WebhookEventName:
      type: string
      enum:
        - connection.connected
        - connection.disconnected
        - connection.expired
        - connection.refreshed
        - connection.error
    WebhookEvent:
      type: object
      required: [id, type, createdAt, data]
      properties:
        id:
          type: string
          format: uuid
        type:
          $ref: '#/components/schemas/WebhookEventName'
        createdAt:
          type: string
          format: date-time
        data:
          $ref: '#/components/schemas/WebhookEventData'
    WebhookEventData:
      type: object
      required: [externalUserId, serviceId]
      properties:
        externalUserId:
          type: string
        serviceId:
          type: string
        connectionId:
          type: string
          format: uuid
        expiresAt:
          type: [string, 'null']
          format: date-time
        errorCode:
          type: string
  examples:
    CatalogResponseExample:
      summary: Tenant-enabled services
      value:
        data:
          - id: github
            name: GitHub
            authType: oauth2
            enabled: true
            config: {}
          - id: slack
            name: Slack
            authType: oauth2
            enabled: true
            config: {}
        error: null
    ConnectionsResponseExample:
      summary: Effective connection states for one external user
      value:
        data:
          - serviceId: github
            status: connected
            connected: true
            expiresAt: '2026-08-01T12:00:00.000Z'
            connectedAt: '2026-07-18T10:00:00.000Z'
            lastCheckedAt: null
            errorCode: null
          - serviceId: slack
            status: disconnected
            connected: false
            expiresAt: null
            connectedAt: null
            lastCheckedAt: null
            errorCode: null
        error: null
    CapabilitiesResponseExample:
      summary: MCP capabilities for one external user
      value:
        data:
          externalUserId: user_123
          format: mcp
          version: cafebabe
          services:
            - serviceId: github
              status: connected
              connected: true
              expiresAt: '2026-08-01T12:00:00.000Z'
              tools:
                - name: github_create_issue
                  description: Create an issue in a GitHub repository.
                  inputSchema:
                    type: object
                    properties:
                      owner:
                        type: string
                      repo:
                        type: string
                      title:
                        type: string
                    required: [owner, repo, title]
        error: null
    ToolsResponseExample:
      summary: MCP tool definitions
      value:
        data:
          tools:
            - name: github_create_issue
              description: Create an issue in a GitHub repository.
              inputSchema:
                type: object
                properties:
                  owner:
                    type: string
                  repo:
                    type: string
                  title:
                    type: string
                required: [owner, repo, title]
          version: cafebabe
        error: null
    OpenAIToolsResponseExample:
      summary: OpenAI function definitions
      value:
        data:
          functions:
            - name: github_create_issue
              description: Create an issue in a GitHub repository.
              parameters:
                type: object
                properties:
                  owner:
                    type: string
                  repo:
                    type: string
                  title:
                    type: string
                required: [owner, repo, title]
          version: cafebabe
        error: null
    CredentialLeaseResponseExample:
      summary: Access-only OAuth credential lease
      value:
        data:
          type: oauth2
          leaseId: 550e8400-e29b-41d4-a716-446655440000
          accessToken: provider-access-token-redacted
          tokenType: Bearer
          scopes: [repo, user:email]
          expiresAt: '2026-07-18T12:05:00.000Z'
        error: null
    CreateConnectSessionRequestExample:
      summary: Origin-bound session with all currently enabled services
      value:
        externalUserId: user_123
        allowedServices: []
        allowedOrigin: https://app.example.com
        expiresInSeconds: 600
    CreateConnectSessionResponseExample:
      summary: One-time hosted connect handoff
      value:
        data:
          id: 550e8400-e29b-41d4-a716-446655440000
          token: acs_redacted
          url: https://app.authlane.io/connect?origin=https%3A%2F%2Fapp.example.com#session=acs_redacted
          expiresAt: '2026-07-18T12:10:00.000Z'
        error: null
    HostedConnectSessionRequestExample:
      summary: Resolve the session for its exact parent origin
      value:
        parentOrigin: https://app.example.com
    HostedConnectSessionResponseExample:
      summary: Services visible in the hosted connect UI
      value:
        data:
          externalUserId: user_123
          expiresAt: '2026-07-18T12:10:00.000Z'
          services:
            - id: github
              name: GitHub
              authType: oauth2
              status: connected
            - id: slack
              name: Slack
              authType: oauth2
              status: disconnected
        error: null
    AuthorizeRequestExample:
      summary: Bind authorization to the parent origin
      value:
        parentOrigin: https://app.example.com
    AuthorizeResponseExample:
      summary: Provider authorization redirect URL
      value:
        data:
          authorizationUrl: https://github.com/login/oauth/authorize?client_id=redacted&state=redacted&code_challenge=redacted&code_challenge_method=S256
        error: null
    DisconnectRequestExample:
      summary: Bind disconnect to the parent origin
      value:
        parentOrigin: https://app.example.com
    DisconnectResponseExample:
      summary: Existing connection removed
      value:
        data:
          disconnected: true
        error: null
    ErrorResponseExample:
      summary: Supabase-style request error
      value:
        data: null
        error:
          message: Invalid external user ID
          code: VALIDATION_ERROR
          hint: Use the external user ID from your authenticated SaaS session.
          docUrl: https://authlane.io/docs/api-reference
          statusCode: 400
    RateLimitedResponseExample:
      summary: Rate limit exceeded
      value:
        data: null
        error:
          message: Rate limit exceeded
          code: RATE_LIMIT_EXCEEDED
          hint: Maximum 100 requests per 60 seconds
          docUrl: https://authlane.io/docs/api-reference/authentication
          statusCode: 429
    ConnectionWebhookExample:
      summary: Signed connection lifecycle delivery
      value:
        id: 550e8400-e29b-41d4-a716-446655440000
        type: connection.connected
        createdAt: '2026-07-18T12:00:00.000Z'
        data:
          externalUserId: user_123
          serviceId: github
          connectionId: 6ba7b810-9dad-41d1-80b4-00c04fd430c8
