Back to skills
extension
Category: Development & EngineeringNo API key required

connection-schema-design

Connection profile and state schema design rules and core profile selection

personAuthor: jakexiaohubgithub

Connection Profile & State Design Rules

Rules for designing connectionProfile.yml and connectionState.yml schemas

🚨 CRITICAL RULES

Rule 1: Always Extend Core Profiles

NEVER create custom profiles from scratch - ALWAYS extend core profiles

# ❌ WRONG - Custom profile
type: object
properties:
  apiKey:
    type: string

# ✅ CORRECT - Extend core profile
$ref: './node_modules/@zerobias-org/types-core/schema/tokenProfile.yml'

Rule 2: connectionState MUST Extend baseConnectionState

🚨 MANDATORY: connectionState.yml MUST extend baseConnectionState.yml for expiresIn

# ✅ CORRECT - Extends base with expiresIn
allOf:
  - $ref: './node_modules/@zerobias-org/types-core/schema/baseConnectionState.yml'
  - type: object
    properties:
      accessToken:
        type: string

# ❌ WRONG - Missing baseConnectionState
type: object
properties:
  accessToken:
    type: string
  # Missing expiresIn!

WHY: Server uses expiresIn to schedule automatic token refresh cronjobs

Rule 3: expiresIn Must Be in Seconds

# ✅ CORRECT
expiresIn:
  type: integer
  description: Token expiry time in SECONDS from now

# ❌ WRONG
expiresAt:
  type: string  # Don't use expiresAt, convert to expiresIn

Calculation: expiresIn = Math.floor((expiresAt - now) / 1000)

Core Profile Selection

Available Core Profiles

| Profile | When to Use | Contains | |---------|-------------|----------| | tokenProfile.yml | API Key, Bearer Token, PAT | token | | oauthClientProfile.yml | OAuth2 Client Credentials | clientId, clientSecret | | oauthTokenProfile.yml | OAuth2 with Refresh | clientId, clientSecret | | basicConnection.yml | Username/Password, Email/Password | username, password |

Path: ./node_modules/@zerobias-org/types-core/schema/{profileName}.yml

Selection Decision Tree

@credential-manager provides authMethodType:

IF authMethodType == "bearer-token" OR "api-key"
  → EXTEND tokenProfile.yml

IF authMethodType == "oauth2-client-credentials"
  → EXTEND oauthClientProfile.yml

IF authMethodType == "oauth2-authorization-code"
  → EXTEND oauthTokenProfile.yml

IF authMethodType == "basic-auth"
  → EXTEND basicConnection.yml

Available Core States

| State | When to Use | Contains | |-------|-------------|----------| | tokenConnectionState.yml | Simple token auth | accessToken + expiresIn | | oauthTokenState.yml | OAuth with refresh | accessToken + refreshToken + expiresIn |

State Selection

IF requiresRefresh == false
  → EXTEND tokenConnectionState.yml

IF requiresRefresh == true
  → EXTEND oauthTokenState.yml

Common Patterns

Pattern 1: Simple API Token

# connectionProfile.yml
$ref: './node_modules/@zerobias-org/types-core/schema/tokenProfile.yml'

# connectionState.yml
$ref: './node_modules/@zerobias-org/types-core/schema/tokenConnectionState.yml'

Pattern 2: OAuth Client Credentials

# connectionProfile.yml
$ref: './node_modules/@zerobias-org/types-core/schema/oauthClientProfile.yml'

# connectionState.yml (no refresh)
$ref: './node_modules/@zerobias-org/types-core/schema/tokenConnectionState.yml'

Pattern 3: OAuth with Refresh Token

# connectionProfile.yml
$ref: './node_modules/@zerobias-org/types-core/schema/oauthTokenProfile.yml'

# connectionState.yml
$ref: './node_modules/@zerobias-org/types-core/schema/oauthTokenState.yml'

Pattern 4: Custom Fields (Extend Core)

# connectionProfile.yml - Add organizationId
allOf:
  - $ref: './node_modules/@zerobias-org/types-core/schema/tokenProfile.yml'
  - type: object
    required: [organizationId]
    properties:
      organizationId:
        type: string
        description: Organization identifier for API access

# connectionState.yml - Add extra state
allOf:
  - $ref: './node_modules/@zerobias-org/types-core/schema/tokenConnectionState.yml'
  - type: object
    properties:
      organizationName:
        type: string
        description: Cached organization name

Connection Profile Scope

What Belongs in connectionProfile

ONLY connection parameters - minimal set needed to CONNECT

# ✅ GOOD - Connection parameters
- token / apiKey / clientId / clientSecret
- baseUrl (if API has multiple environments)
- organizationId (if required to authenticate)

# ❌ BAD - Operation parameters
- limit (pagination parameter)
- projectId (operation scope, not connection scope)
- fields (query parameter)

Rule: If you can connect WITHOUT it, it's an operation parameter (not profile)

Check for Additional Optional Parameters

Always review API docs for environment-specific optional parameters:

# Example: Service has optional region parameter
allOf:
  - $ref: './node_modules/@zerobias-org/types-core/schema/tokenProfile.yml'
  - type: object
    properties:
      region:
        type: string
        description: Optional region for multi-region deployments
        enum: [us-east-1, eu-west-1, ap-southeast-1]
    # region is optional, but include it if API docs mention it

Don't omit parameters that some environments might need!

🚨 CRITICAL: Check Parent Schema First

Before adding ANY property, check what the parent schema provides:

# Check what tokenProfile.yml provides
cat node_modules/@zerobias-org/types-core/schema/tokenProfile.yml

# Check what basicConnection.yml provides
cat node_modules/@zerobias-org/types-core/schema/basicConnection.yml

# Check what baseConnectionState.yml provides
cat node_modules/@zerobias-org/types-core/schema/baseConnectionState.yml

Avoid Semantic Duplication

# ❌ WRONG - basicConnection already has uri
allOf:
  - $ref: './node_modules/@zerobias-org/types-core/schema/basicConnection.yml'
  - type: object
    properties:
      url:    # Duplicate! basicConnection has uri
        type: string

# ✅ CORRECT - Use parent's uri, or extend if truly different
allOf:
  - $ref: './node_modules/@zerobias-org/types-core/schema/basicConnection.yml'
  # uri is already provided by basicConnection

Semantic duplicates to avoid:

  • url / uri / baseUrl
  • token / accessToken
  • username / user / userName
  • password / pass / pwd

🚨 CRITICAL: ALWAYS Check Product Documentation

Before finalizing connectionProfile, check product docs for ALL auth parameters:

# Example: Missing mfaCode from product docs
allOf:
  - $ref: './node_modules/@zerobias-org/types-core/schema/basicConnection.yml'
  - type: object
    properties:
      mfaCode:    # Found in product docs!
        type: string
        description: Multi-factor authentication code (optional)

Don't assume - verify:

  1. Read product documentation authentication section
  2. Look for optional parameters (region, environment, mfaCode, etc.)
  3. Include ALL mentioned parameters (even if optional)
  4. Don't omit parameters some environments might need

🚨 CRITICAL: Connection Scope MUST NOT Limit Operations

NEVER add organization/project/workspace IDs to connection:

# ❌ WRONG - Limits connection to single organization
connectionState:
  allOf:
    - $ref: './node_modules/@zerobias-org/types-core/schema/baseConnectionState.yml'
    - type: object
      properties:
        accessToken:
          type: string
        organizationId:    # NO! Limits scope
          type: string

# ✅ CORRECT - Connection works across all organizations
connectionState:
  allOf:
    - $ref: './node_modules/@zerobias-org/types-core/schema/baseConnectionState.yml'
    - type: object
      properties:
        accessToken:
          type: string
  # organizationId is operation parameter, NOT connection parameter

WHY: Connection should be reusable across all scopes. Use operation parameters for scope.

Scope identifiers belong in operation parameters, NOT connection:

  • ❌ organizationId in connection
  • ❌ workspaceId in connection
  • ❌ projectId in connection
  • ❌ teamId in connection
  • ✅ These are operation parameters (use in API paths)

🚨 CRITICAL: Only Add Used Fields (for connectionState)

For connectionState - don't add fields "just in case" - only add what's actually USED:

# ❌ WRONG - identityId not used anywhere
connectionState:
  properties:
    accessToken: string
    identityId: string    # Where is this used? Nowhere!

# ✅ CORRECT - Only fields that are actually used
connectionState:
  properties:
    accessToken: string
  # If connect() doesn't return identityId, don't add it

Ask:

  1. Does connect() method return this field?
  2. Do operations use this field?
  3. Is it needed for refresh/reconnect? If NO to all → don't add it

For connectionProfile - include environment-optional fields:

# ✅ CORRECT - Include optional fields that some environments need
allOf:
  - $ref: './node_modules/@zerobias-org/types-core/schema/tokenProfile.yml'
  - type: object
    properties:
      region:
        type: string
        description: Optional region (needed in some deployments)
      mfaCode:
        type: string
        description: Multi-factor authentication code (optional)
    # These aren't required, but include them if product docs mention them

connectionProfile vs connectionState:

  • connectionProfile: Include optional fields that might be needed in some environments (region, mfaCode, etc.)
  • connectionState: Only fields actually used by connect() or operations

Validation Checklist

Before finalizing connection schemas:

  • [ ] Checked parent schema - no semantic duplicates (url/uri, token/accessToken)
  • [ ] Checked product docs thoroughly - all auth parameters included (mfaCode, region, etc.)
  • [ ] connectionProfile extends a core profile (NOT custom)
  • [ ] connectionState extends baseConnectionState (includes expiresIn)
  • [ ] expiresIn is in seconds (integer), not expiresAt timestamp
  • [ ] If refresh capability: state extends oauthTokenState
  • [ ] If simple token: state extends tokenConnectionState
  • [ ] Custom fields use allOf to extend core
  • [ ] All required fields marked as required
  • [ ] Checked API docs for optional connection parameters (region, environment, etc.)
  • [ ] Profile contains ONLY connection parameters (not operation parameters)
  • [ ] NO scope limitation - no organizationId/projectId/workspaceId in connection
  • [ ] Only used fields - every state field is actually used by connect() or operations
  • [ ] Minimal set needed to connect (don't add operation scope parameters)

Common Mistakes

❌ Creating Custom Profile

# DON'T DO THIS
type: object
properties:
  apiKey: string
  baseUrl: string

Fix: Extend tokenProfile.yml

❌ Missing baseConnectionState

# DON'T DO THIS
type: object
properties:
  accessToken: string

Fix: Extend baseConnectionState or tokenConnectionState

❌ Using expiresAt Instead of expiresIn

# DON'T DO THIS
expiresAt:
  type: string
  format: date-time

Fix: Convert to expiresIn (seconds) in connect() method

❌ No expiresIn for Refresh Tokens

# DON'T DO THIS
properties:
  accessToken: string
  refreshToken: string
  # Missing expiresIn!

Fix: MUST extend baseConnectionState for expiresIn

Remember

  1. Always extend core profiles - Never create custom from scratch
  2. Always extend baseConnectionState - Server needs expiresIn for cronjobs
  3. expiresIn in seconds - Not timestamps, not milliseconds
  4. Use allOf for extensions - Add custom fields via composition
  5. Receive data from @credential-manager - Don't guess auth method

Connection schemas are YAML schemas - design them like api.yml schemas!