Integration Patterns

When integrating external services with Julep, following consistent patterns helps ensure security, reliability, and maintainability. This guide covers common integration patterns with a focus on using secrets effectively.

Authentication Patterns

API Key Authentication with Secrets

For services that use API keys for authentication, store them as secrets:

steps:
  - kind: tool_call
    tool: external_api
    operation: fetch_data
    arguments:
      url: "https://api.example.com/data"
      headers:
        Authorization: "$ f'Bearer {secrets.api_key}'"
        X-API-Key: "$ secrets.api_key"

OAuth Authentication with Secrets

For OAuth flows, keep client credentials in secrets:

steps:
  - kind: tool_call
    tool: oauth_service
    operation: get_token
    arguments:
      client_id: "$ secrets.oauth_client_id"
      client_secret: "$ secrets.oauth_client_secret"
      scope: "read write"
    output: token
  
  - kind: tool_call
    tool: api_service
    operation: call_api
    arguments:
      url: "https://api.example.com/data"
      headers:
        Authorization: "Bearer {{ token }}"

Basic Authentication with Secrets

For services using basic authentication:

steps:
  - kind: tool_call
    tool: api_service
    operation: call_api
    arguments:
      url: "https://api.example.com/data"
      auth:
        username: "$ secrets.api_username"
        password: "$ secrets.api_password"

Integration Configuration Patterns

Database Connection with Secrets

When connecting to databases, use secrets for connection parameters:

steps:
  - kind: tool_call
    tool: database
    operation: query
    arguments:
      query: "SELECT * FROM users LIMIT 10"
      connection:
        host: "$ secrets.db_host"
        port: "$ secrets.db_port"
        user: "$ secrets.db_username"
        password: "$ secrets.db_password"
        database: "$ secrets.db_name"

Service Configuration with Secrets

For configuring service endpoints and parameters:

steps:
  - kind: tool_call
    tool: email
    operation: send
    arguments:
      to: "recipient@example.com"
      subject: "Important update"
      body: "This is an important message."
      smtp:
        host: "$ secrets.smtp_host"
        port: "$ secrets.smtp_port"
        username: "$ secrets.smtp_username"
        password: "$ secrets.smtp_password"
        tls: true

Advanced Integration Patterns

Hybrid Secret and Expression Pattern

Combine secrets with expressions for dynamic configurations:

steps:
  - kind: transform
    expression: "$ f'https://{secrets.api_domain}/v1/{input.resource}?api_key={secrets.api_key}'"
    input:
      resource: "users"
    output: api_url

  - kind: tool_call
    tool: http
    operation: get
    arguments:
      url: "{{ api_url }}"

Multi-tenant Service Integration

For handling multiple tenant configurations with secrets:

steps:
  - kind: transform
    expression: "$ f'tenant_{input.tenant_id}'"
    input:
      tenant_id: "123"
    output: tenant_key

  - kind: transform 
    expression: "$ f'{secrets[tenant_key + \"_api_key\"]}'"
    output: api_key

  - kind: tool_call
    tool: external_api
    operation: fetch_data
    arguments:
      url: "https://api.example.com/data"
      headers:
        Authorization: "Bearer {{ api_key }}"

Service Discovery Pattern

For dynamically selecting services based on configuration:

steps:
  - kind: transform
    expression: "$ secrets.preferred_service"
    output: service_name

  - kind: if_else
    if: "$ service_name == 'service_a'"
    then:
      - kind: tool_call
        tool: service_a
        operation: process
        arguments:
          input: "{{ input }}"
          api_key: "$ secrets.service_a_api_key"
    else:
      - kind: tool_call
        tool: service_b
        operation: process
        arguments:
          data: "{{ input }}"
          auth_token: "$ secrets.service_b_auth_token"

Best Practices

Secret Naming Conventions

  • Use descriptive names: stripe_api_key instead of just api_key
  • Use service prefixes: aws_access_key, aws_secret_key
  • For multiple environments: dev_api_key, prod_api_key

Secret Rotation

Implement regular secret rotation without service disruption:

# Python example of rotating a secret
from julep import Julep
import uuid

client = Julep(api_key="your_api_key")

# Generate temporary name
temp_name = f"stripe_key_rotation_{uuid.uuid4().hex[:8]}"

# Create new secret with temp name
client.secrets.create(
    name=temp_name,
    value="sk_new_value...",
    description="New Stripe API key (rotation)"
)

# Test the new key works
# ...

# If valid, delete old secret and rename new one
client.secrets.delete(name="stripe_api_key")
client.secrets.update(
    name=temp_name,
    new_name="stripe_api_key",
    description="Stripe API key"
)

Error Handling

For graceful handling of authentication and configuration errors:

steps:
  - kind: try_catch
    try:
      - kind: tool_call
        tool: external_api
        operation: fetch_data
        arguments:
          url: "https://api.example.com/data"
          headers:
            Authorization: "Bearer $ secrets.api_key"
    catch:
      - kind: if_else
        if: "$ error.type == 'AuthenticationError'"
        then:
          - kind: prompt
            model: gpt-4
            prompt: "API key authentication failed. Please suggest troubleshooting steps."
        else:
          - kind: prompt
            model: gpt-4
            prompt: "An error occurred: {{ error.message }}"

Next Steps