> ## Documentation Index
> Fetch the complete documentation index at: https://beta-docs.nevermind.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Integration Guide

> A comprehensive guide to integrating Nevermined into your AI services.

This guide provides a deep dive into integrating Nevermined. We'll cover the core components you'll interact with and the different patterns you can use to add a payment layer to your AI agents.

## Core Integration Concepts

Before diving into integration patterns, it's important to understand the fundamental components of the Nevermined SDK.

### 1. The Payments Client

The Payments Client is your primary interface for all interactions with the Nevermined protocol. It's a singleton instance that abstracts away the complexities of blockchain interactions.

**Key Responsibilities:**

* **Agent & Plan Management**: Handles the registration and configuration of your agents and their payment plans.
* **Access Validation**: Verifies subscriber permissions through bearer token validation.
* **Credit Redemption**: Manages the lifecycle of credits, from issuance to consumption.
* **Query Routing**: Facilitates communication between subscribers and agents.

**Modular Architecture:**

```typescript theme={null}
const payments = Payments.getInstance({ nvmApiKey, environment })

payments.agents     // Agent registration and management
payments.plans      // Plan creation and ordering
payments.requests   // Request validation and tracking
payments.a2a        // Agent-to-Agent communication
```

### 2. AI Agents

In Nevermined, an AI Agent is your monetizable service. It's defined by its metadata, its API endpoints, and the payment plans associated with it.

**Agent Structure:**

```typescript theme={null}
interface Agent {
  agentId: string;
  metadata: {
    name: string;
    description?: string;
    tags: string[];
    dateCreated: Date;
    image?: string;
  };
  api: {
    endpoints: Array<{ [method: string]: string }>; // Protected endpoints
    openEndpoints?: string[]; // Public, non-monetized endpoints
  };
  plans: PaymentPlan[];
}
```

### 3. The Credits System

Credits are the unit of consumption in Nevermined, providing a flexible way to bill for AI services.

**Credit Lifecycle:**

1. **Purchase**: A subscriber buys a plan and receives credits.
2. **Validation**: On each API call, the system checks if the subscriber has sufficient credits.
3. **Consumption**: After a successful request, the specified number of credits is redeemed (burned).
4. **Tracking**: The subscriber's balance is updated in real-time.
5. **Expiration**: For time-based plans, access expires after the duration ends.

### 4. Access Control

Nevermined uses bearer token authentication to secure your agent's endpoints, ensuring only authorized subscribers can access them.

**How it Works:**

<Steps>
  <Step title="Plan Purchase">
    A subscriber purchases a plan through the SDK or Web App.
  </Step>

  <Step title="Token Generation">
    Upon purchase, the subscriber receives a bearer token via `getAgentAccessToken()`.
  </Step>

  <Step title="Request Authentication">
    The subscriber includes the token in the Authorization header of their requests.
  </Step>

  <Step title="Validation">
    The Nevermined Proxy or your agent validates the token, checks credit balance, and authorizes access.
  </Step>

  <Step title="Service Delivery">
    If validated, your agent processes the request and credits are automatically deducted.
  </Step>
</Steps>

***

## Integration Patterns

Choose the integration pattern that best fits your application's architecture.

<CardGroup cols={3}>
  <Card title="Direct Integration" icon="code" href="#direct-integration">
    Integrate the SDK directly into your agent's code for full control.
  </Card>

  <Card title="Proxy Integration" icon="shield" href="#proxy-integration">
    No code changes - Nevermined Proxy handles payments.
  </Card>

  <Card title="Middleware Pattern" icon="layer-group" href="#middleware-pattern">
    Create a reusable authentication layer for Node.js applications.
  </Card>
</CardGroup>

### Direct Integration

This pattern is best for new services or when you want maximum control over the request lifecycle.

**Example: Protecting an Express.js Endpoint**

```typescript theme={null}
app.post('/api/query', async (req, res) => {
  try {
    // Extract bearer token from Authorization header
    const authHeader = req.headers['authorization'];
    if (!authHeader?.startsWith('Bearer ')) {
      return res.status(401).json({ error: 'Unauthorized' });
    }
    
    const token = authHeader.substring(7);
    
    // 1. Validate access token
    const validation = await payments.requests.isValidRequest(
      token,
      req.body
    );
    
    if (!validation.isValid) {
      // Return available payment plans
      return res.status(402).json({
        error: 'Payment Required',
        plans: validation.plans
      });
    }

    // 2. Process the AI request
    const result = await processAIRequest(req.body.prompt);

    // 3. Credits are automatically redeemed by the proxy
    // For direct integration without proxy, you can manually track:
    // await payments.requests.logRequest(token, { usage: 1 });

    // 4. Return response
    res.json(result);

  } catch (error) {
    console.error('Request failed:', error);
    res.status(500).json({ error: 'Internal server error' });
  }
});
```

### Proxy Integration

For existing services that can't be modified, use the Nevermined Proxy. The proxy handles all payment validation automatically.

**How It Works:**

1. Register your existing API endpoints with Nevermined
2. Subscribers access your service through the proxy URL
3. The proxy validates tokens and manages credits
4. Your service receives forwarded requests without modification

**Example Registration:**

```typescript theme={null}
const { agentId, planId } = await payments.agents.registerAgentAndPlan(
  {
    name: 'My Existing Service',
    tags: ['ai', 'proxy']
  },
  {
    endpoints: [
      { POST: 'https://my-api.com/process' },
      { GET: 'https://my-api.com/status' }
    ],
    openEndpoints: ['https://my-api.com/health']
  },
  planMetadata,
  priceConfig,
  creditsConfig
);

// Your service is now accessible at:
// https://proxy.nevermined.app/api/v1/agents/{agentId}/invoke
```

### Middleware Pattern

For Node.js applications, create reusable middleware to protect multiple routes.

**Example: Express.js Auth Middleware**

```typescript theme={null}
export function createNeverminedAuth(payments: Payments) {
  return async (req: Request, res: Response, next: NextFunction) => {
    try {
      const authHeader = req.headers['authorization'];
      
      if (!authHeader?.startsWith('Bearer ')) {
        return res.status(401).json({ error: 'Missing bearer token' });
      }
      
      const token = authHeader.substring(7);
      const validation = await payments.requests.isValidRequest(token, req.body);
      
      if (!validation.isValid) {
        return res.status(402).json({
          error: 'Payment required',
          plans: validation.plans
        });
      }

      // Attach validation info to request
      (req as any).nevermined = {
        token,
        planId: validation.planId,
        subscriberAddress: validation.subscriberAddress
      };
      
      next();
    } catch (error) {
      console.error('Auth error:', error);
      res.status(500).json({ error: 'Authentication failed' });
    }
  };
}

// Usage
const auth = createNeverminedAuth(payments);

app.post('/api/query', auth, handleQuery);
app.post('/api/generate', auth, handleGenerate);
```

## Direct Integration

Best for new services or when you want full control over the payment flow.

### Step 1: Install Dependencies

```bash theme={null}
npm install @nevermined-io/payments express
npm install --save-dev @types/express typescript
```

### Step 2: Initialize Nevermined

```typescript theme={null}
import { Payments } from '@nevermined-io/payments'
import express from 'express'

const app = express()
app.use(express.json())

const payments = Payments.getInstance({
  nvmApiKey: process.env.NVM_API_KEY,
  environment: 'production'
})
```

### Step 3: Create Protected Endpoint

```typescript theme={null}
app.post('/api/query', async (req, res) => {
  try {
    // Extract bearer token
    const authHeader = req.headers['authorization']
    if (!authHeader?.startsWith('Bearer ')) {
      return res.status(401).json({ error: 'Unauthorized - Bearer token required' })
    }
    
    const token = authHeader.substring(7)
    
    // Validate access
    const validation = await payments.requests.isValidRequest(token, req.body)
    
    if (!validation.isValid) {
      return res.status(402).json({
        error: 'Payment required',
        message: 'Please purchase a plan to access this service',
        plans: validation.plans // Available payment plans
      })
    }

    // Process the AI request
    const result = await processAIRequest(req.body.prompt)

    // For direct integration, optionally track usage:
    // await payments.requests.logRequest(token, { 
    //   creditsUsed: 1,
    //   requestData: { prompt: req.body.prompt.substring(0, 100) }
    // })
    
    // Return response
    res.json({
      result,
      usage: {
        creditsUsed: 1,
        planId: validation.planId
      }
    })

  } catch (error) {
    console.error('Request failed:', error)
    res.status(500).json({ error: 'Internal server error' })
  }
})

async function processAIRequest(prompt: string) {
  // Your AI logic here
  return { response: `Processed: ${prompt}` }
}
```

### Step 4: Add Health Check

```typescript theme={null}
app.get('/health', (req, res) => {
  res.json({ 
    status: 'healthy',
    service: 'ai-agent',
    version: '1.0.0'
  })
})
```

### Step 5: Register Your Agent

```typescript theme={null}
import { 
  getERC20PriceConfig, 
  getFixedCreditsConfig 
} from '@nevermined-io/payments'

async function setupAgent() {
  const agentMetadata = {
    name: 'My AI Service',
    tags: ['ai', 'nlp'],
    dateCreated: new Date(),
    description: 'Advanced AI processing service'
  }

  const agentApi = {
    endpoints: [
      { POST: 'https://api.myservice.com/api/query' }
    ],
    openEndpoints: [
      'https://api.myservice.com/health'
    ]
  }
  
  const planMetadata = {
    name: 'Basic Plan',
    description: '100 queries with advanced AI processing',
    dateCreated: new Date()
  }

  const priceConfig = getERC20PriceConfig(
    10_000_000n, // 10 USDC (6 decimals)
    USDC_ADDRESS,
    process.env.BUILDER_ADDRESS
  )

  const creditsConfig = getFixedCreditsConfig(100n, 1n)

  const { agentId, planId } = await payments.agents.registerAgentAndPlan(
    agentMetadata,
    agentApi,
    planMetadata,
    priceConfig,
    creditsConfig
  )

  console.log('Agent registered:', agentId)
  console.log('Plan created:', planId)
  console.log('Access via proxy:', `https://proxy.nevermined.app/api/v1/agents/${agentId}`)
}
```

## Middleware Pattern

Create reusable authentication middleware for Express/Node.js applications.

### Create Auth Middleware

```typescript theme={null}
import { Request, Response, NextFunction } from 'express'
import { Payments } from '@nevermined-io/payments'

interface NeverminedRequest extends Request {
  nevermined?: {
    planId: string
    agentId: string
    subscriberAddress: string
  }
}

export function createNeverminedAuth(payments: Payments) {
  return (agentId: string) => {
    return async (
      req: NeverminedRequest, 
      res: Response, 
      next: NextFunction
    ) => {
      try {
        const { planId, subscriberAddress } = req.body
        const signature = req.headers['x-nvm-query-signature'] as string

        if (!planId || !subscriberAddress || !signature) {
          return res.status(400).json({ 
            error: 'Missing required payment parameters' 
          })
        }

        const isValid = await payments.isValidRequest(
          planId,
          agentId,
          subscriberAddress,
          signature
        )

        if (!isValid) {
          const paymentCard = await payments.getAgentPaymentCard(agentId)
          return res.status(402).json({
            error: 'Payment required',
            paymentCard
          })
        }

        // Attach payment info to request
        req.nevermined = { planId, agentId, subscriberAddress }
        next()

      } catch (error) {
        console.error('Auth middleware error:', error)
        res.status(500).json({ error: 'Authentication failed' })
      }
    }
  }
}
```

### Use Middleware

```typescript theme={null}
const app = express()
const payments = Payments.getInstance({
  nvmApiKey: process.env.NVM_API_KEY,
  environment: 'testing'
})

const auth = createNeverminedAuth(payments)

// Apply to all protected routes
app.use('/api/*', auth)

// Or apply to specific routes
app.post('/api/query', auth, async (req, res) => {
  // Access validation info from req.nevermined
  const { token, planId } = req.nevermined
  
  // Process request...
  const result = await processQuery(req.body)
  
  // Optionally log usage for direct integration
  // await payments.requests.logRequest(token, { creditsUsed: 1 })
  
  res.json(result)
})
```

### Advanced Middleware Features

```typescript theme={null}
export function createAdvancedAuth(payments: Payments, options?: {
  checkBalance?: boolean
  minCredits?: bigint
  rateLimitPerMinute?: number
}) {
  const requestCounts = new Map<string, number[]>()

  return async (req: NeverminedRequest, res: Response, next: NextFunction) => {
    try {
      // Extract and validate token
      const authHeader = req.headers['authorization']
      if (!authHeader?.startsWith('Bearer ')) {
        return res.status(401).json({ error: 'Unauthorized' })
      }
      
      const token = authHeader.substring(7)
      const validation = await payments.requests.isValidRequest(token, req.body)
      
      if (!validation.isValid) {
        return res.status(402).json({ 
          error: 'Payment required',
          plans: validation.plans 
        })
      }

        // Check balance if requested
        if (options?.checkBalance) {
          const balance = await payments.getPlanBalance(
            req.body.planId,
            req.body.subscriberAddress
          )

          if (balance.credits < (options.minCredits || 1n)) {
            return res.status(402).json({
              error: 'Insufficient credits',
              required: options.minCredits?.toString() || '1',
              available: balance.credits.toString()
            })
          }

          res.setHeader('X-Credits-Remaining', balance.credits.toString())
        }

        // Rate limiting per subscriber
        if (options?.rateLimitPerMinute) {
          const now = Date.now()
          const minute = 60 * 1000
          const key = req.body.subscriberAddress
          
          const requests = requestCounts.get(key) || []
          const recentRequests = requests.filter(t => now - t < minute)
          
          if (recentRequests.length >= options.rateLimitPerMinute) {
            return res.status(429).json({
              error: 'Rate limit exceeded',
              retryAfter: minute - (now - recentRequests[0])
            })
          }
          
          recentRequests.push(now)
          requestCounts.set(key, recentRequests)
        }

        req.nevermined = {
          planId: req.body.planId,
          agentId,
          subscriberAddress: req.body.subscriberAddress
        }

        next()

      } catch (error) {
        console.error('Auth error:', error)
        res.status(500).json({ error: 'Authentication failed' })
      }
    }
  }
}

// Usage
const advancedAuth = createAdvancedAuth(payments, {
  checkBalance: true,
  minCredits: 5n,
  rateLimitPerMinute: 60
})

app.post('/api/advanced-query', advancedAuth(agentId), handler)
```

## Proxy Integration

For existing services that can't be modified, use the Nevermined Proxy.

### How It Works

<Steps>
  <Step title="Register Your Service">
    Register your existing endpoints with Nevermined
  </Step>

  <Step title="Proxy Handles Payments">
    All payment validation happens at the proxy layer
  </Step>

  <Step title="No Code Changes">
    Your service continues to work as before
  </Step>
</Steps>

### Setup Process

```typescript theme={null}
async function setupProxyIntegration() {
  const payments = Payments.getInstance({
    nvmApiKey: process.env.NVM_API_KEY,
    environment: 'production'
  })

  // Register your existing service
  const metadata = {
    name: 'Legacy AI Service',
    tags: ['ai', 'legacy', 'proxy'],
    dateCreated: new Date(),
    description: 'Existing AI service with payment layer'
  }

  const api = {
    endpoints: [
      // Your actual service endpoints
      { POST: 'https://legacy-api.example.com/process' },
      { GET: 'https://legacy-api.example.com/status' }
    ],
    openEndpoints: [
      // Public endpoints (no payment required)
      'https://legacy-api.example.com/health'
    ]
  }

  const price = getERC20PriceConfig(15_000_000n, USDC_ADDRESS, builderAddress)
  const credits = getFixedCreditsConfig(100n, 1n)

  const { agentId, planId } = await payments.registerAgentAndPlan(
    metadata,
    api,
    price,
    credits
  )

  console.log(`
    Proxy Integration Complete!
    
    Your service is now accessible at:
    https://proxy.nevermined.app/agent/${agentId}
    
    The proxy handles:
    - Payment validation
    - Credit redemption  
    - Access control
    - Usage tracking
  `)
}
```

### Client Usage

Subscribers access your proxied service:

```typescript theme={null}
// For subscribers
async function useProxiedService() {
  const payments = Payments.getInstance({
    nvmApiKey: process.env.NVM_API_KEY,
    environment: 'production'
  })

  // Purchase plan
  await payments.orderPlan(planId)

  // Get access credentials
  const options = await payments.getAgentHTTPOptions(planId, agentId)

  // Make requests through proxy
  const response = await fetch(options.neverminedProxyUri, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${options.accessToken}`
    },
    body: JSON.stringify({
      // Your original API payload
      data: 'process this'
    })
  })

  const result = await response.json()
  console.log(result)
}
```

## Error Handling

Implement comprehensive error handling for all integration patterns:

```typescript theme={null}
import { PaymentsError } from '@nevermined-io/payments'

// Express error handler
app.use((error: Error, req: Request, res: Response, next: NextFunction) => {
  if (error instanceof PaymentsError) {
    const statusMap = {
      'INVALID_API_KEY': 401,
      'INSUFFICIENT_BALANCE': 402,
      'INVALID_PLAN_CONFIG': 400,
      'NETWORK_ERROR': 503,
      'VALIDATION_ERROR': 400
    }

    const status = statusMap[error.code] || 500
    
    return res.status(status).json({
      error: error.message,
      code: error.code,
      details: error.details
    })
  }

  // Handle other errors
  console.error('Unexpected error:', error)
  res.status(500).json({ 
    error: 'Internal server error',
    message: process.env.NODE_ENV === 'development' ? error.message : undefined
  })
})
```

## Request Tracking

Track and manage the lifecycle of subscriber requests for analytics, debugging, and compliance purposes.

### Request Lifecycle Management

The SDK provides methods to track requests from initialization through completion:

```typescript theme={null}
// Initialize a new request when subscriber starts
const requestId = await payments.requests.initializeSubscriberRequest(
  planId,
  agentId,
  subscriberAddress,
  {
    method: 'POST',
    endpoint: '/api/query',
    timestamp: new Date(),
    metadata: {
      feature: 'advanced-analysis',
      version: '1.0'
    }
  }
)

// Update request progress (optional)
await payments.requests.updateSubscriberRequest(
  requestId,
  {
    status: 'processing',
    progress: 50,
    checkpoint: 'Model inference started'
  }
)

// Finish request and record results
await payments.requests.finishSubscriberRequest(
  requestId,
  {
    status: 'completed',
    creditsUsed: 5n,
    responseTime: 1250, // milliseconds
    metadata: {
      tokensGenerated: 450,
      modelUsed: 'gpt-4'
    }
  }
)
```

### Integration with Request Handler

Add request tracking to your API endpoints:

```typescript theme={null}
app.post('/api/query', authenticate(agentId), async (req, res) => {
  let requestId: string | null = null
  
  try {
    // Initialize request tracking
    requestId = await payments.requests.initializeSubscriberRequest(
      req.nevermined.planId,
      req.nevermined.agentId,
      req.nevermined.subscriberAddress,
      {
        method: req.method,
        endpoint: req.path,
        timestamp: new Date(),
        metadata: {
          prompt: req.body.prompt,
          parameters: req.body.parameters
        }
      }
    )
    
    // Update progress during processing
    await payments.requests.updateSubscriberRequest(requestId, {
      status: 'processing',
      progress: 25,
      checkpoint: 'Request validated'
    })
    
    // Process AI request
    const startTime = Date.now()
    const result = await processAIRequest(req.body.prompt)
    const responseTime = Date.now() - startTime
    
    // Finish request tracking
    await payments.requests.finishSubscriberRequest(requestId, {
      status: 'completed',
      creditsUsed: 1n,
      responseTime,
      metadata: {
        outputLength: result.response.length,
        success: true
      }
    })
    
    res.json({
      result,
      requestId,
      usage: {
        creditsUsed: 1,
        responseTime
      }
    })
    
  } catch (error) {
    // Track failed requests
    if (requestId) {
      await payments.requests.finishSubscriberRequest(requestId, {
        status: 'failed',
        creditsUsed: 0n,
        error: error.message,
        metadata: {
          errorType: error.constructor.name
        }
      })
    }
    
    res.status(500).json({ error: 'Processing failed' })
  }
})
```

### Analytics and Monitoring

Use request tracking data for insights:

```typescript theme={null}
// Get request history for a subscriber
async function getSubscriberUsageAnalytics(
  subscriberAddress: string,
  planId: string
) {
  // This would integrate with your analytics backend
  // The request tracking provides the raw data
  
  return {
    totalRequests: 156,
    successRate: 0.98,
    averageResponseTime: 850,
    creditsConsumed: 312,
    popularFeatures: {
      'basic-query': 89,
      'advanced-analysis': 45,
      'batch-processing': 22
    },
    timeDistribution: {
      // Requests by hour of day
    }
  }
}
```

### Best Practices for Request Tracking

<AccordionGroup>
  <Accordion title="Performance Considerations">
    * Use async operations to avoid blocking request processing
    * Consider batching updates for high-volume scenarios
    * Implement request sampling for very high traffic
    * Store detailed metadata only when necessary
  </Accordion>

  <Accordion title="Privacy and Compliance">
    * Don't store sensitive user data in request metadata
    * Implement data retention policies
    * Allow users to request deletion of their tracking data
    * Comply with GDPR and other privacy regulations
  </Accordion>

  <Accordion title="Error Handling">
    * Always finish requests even if they fail
    * Track different types of failures separately
    * Use request IDs for debugging and support
    * Implement retry logic for tracking failures
  </Accordion>
</AccordionGroup>

### Request Tracking Middleware

Create reusable middleware for automatic request tracking:

```typescript theme={null}
export function createRequestTracker(payments: Payments) {
  return (agentId: string) => {
    return async (req: NeverminedRequest, res: Response, next: NextFunction) => {
      if (!req.nevermined) {
        return next()
      }
      
      const requestId = await payments.requests.initializeSubscriberRequest(
        req.nevermined.planId,
        agentId,
        req.nevermined.subscriberAddress,
        {
          method: req.method,
          endpoint: req.path,
          timestamp: new Date()
        }
      )
      
      // Store request ID for later use
      req.nevermined.requestId = requestId
      
      // Override res.json to track completion
      const originalJson = res.json.bind(res)
      res.json = function(data: any) {
        payments.requests.finishSubscriberRequest(requestId, {
          status: 'completed',
          creditsUsed: req.nevermined.creditsUsed || 1n,
          responseTime: Date.now() - req.nevermined.startTime
        }).catch(console.error)
        
        return originalJson(data)
      }
      
      // Track request start time
      req.nevermined.startTime = Date.now()
      
      next()
    }
  }
}

// Usage
const requestTracker = createRequestTracker(payments)
app.post('/api/query', 
  authenticate(agentId), 
  requestTracker(agentId),
  handler
)
```

## Testing Your Integration

### Unit Tests

```typescript theme={null}
import { Payments } from '@nevermined-io/payments'

describe('Payment Integration', () => {
  let payments: Payments
  
  beforeEach(() => {
    payments = Payments.getInstance({
      nvmApiKey: process.env.NVM_TEST_API_KEY,
      environment: 'testing'
    })
  })

  test('should validate authorized requests', async () => {
    const isValid = await payments.isValidRequest(
      testPlanId,
      testAgentId,
      testSubscriber,
      validSignature
    )
    
    expect(isValid).toBe(true)
  })

  test('should reject unauthorized requests', async () => {
    const isValid = await payments.isValidRequest(
      testPlanId,
      testAgentId,
      'wrong-address',
      'invalid-signature'
    )
    
    expect(isValid).toBe(false)
  })
})
```

### Integration Tests

```typescript theme={null}
import request from 'supertest'
import { app } from './app'

describe('API Integration', () => {
  test('should return 402 without payment', async () => {
    const response = await request(app)
      .post('/api/query')
      .send({ prompt: 'test' })
    
    expect(response.status).toBe(402)
    expect(response.body).toHaveProperty('paymentCard')
  })

  test('should process request with valid payment', async () => {
    const response = await request(app)
      .post('/api/query')
      .set('x-nvm-query-signature', validSignature)
      .send({
        planId: testPlanId,
        agentId: testAgentId,
        subscriberAddress: testSubscriber,
        prompt: 'test query'
      })
    
    expect(response.status).toBe(200)
    expect(response.body).toHaveProperty('result')
    expect(response.body.usage).toHaveProperty('creditsRemaining')
  })
})
```

## Production Checklist

Before going live:

* [ ] Environment variables configured
* [ ] Error handling implemented
* [ ] Rate limiting in place
* [ ] Logging and monitoring set up
* [ ] Security headers configured
* [ ] SSL/TLS enabled
* [ ] Health check endpoint working
* [ ] Payment flow tested end-to-end
* [ ] Documentation updated

## Next Steps

<CardGroup cols={2}>
  <Card title="Best Practices" icon="check" href="/introduction/best-practices">
    Security and performance recommendations
  </Card>

  <Card title="Go Live" icon="rocket" href="/introduction/go-live-checklist">
    Deploy your integrated service
  </Card>

  <Card title="Examples" icon="code" href="/introduction/example-apps">
    See complete implementations
  </Card>

  <Card title="Troubleshooting" icon="wrench" href="/introduction/troubleshooting">
    Common issues and solutions
  </Card>
</CardGroup>
