Overview

The Consultant API provides endpoints for managing client relationships, portfolio analytics, white-label branding, and bulk operations. All endpoints require authentication with a consultant account.
Base URL: https://your-domain.com/apiAuthentication: Session-based (better-auth)Rate Limits: 100 requests/minute per consultant

Authentication

All consultant API endpoints require an active consultant session.

Session Headers

Cookie: better-auth.session_token=<session-token>

Error Responses

// 401 Unauthorized
{
  "error": "Unauthorized"
}

// 403 Forbidden (not a consultant)
{
  "error": "Consultant access required"
}

Client Management

Search Organizations

Search for organizations to invite as clients.
GET /api/organizations/search?q=acme
Query Parameters:
  • q (required): Search query (minimum 2 characters)
Response (200 OK):
{
  "organizations": [
    {
      "id": "org_123",
      "name": "Acme Corporation",
      "slug": "acme-corp"
    }
  ],
  "query": "acme"
}
Error Responses:
  • 400 Bad Request: Query too short (< 2 characters)
  • 401 Unauthorized: Not authenticated
  • 500 Internal Server Error: Search failed

Send Client Invitation

Invite an organization to become your client.
POST /api/invitations/send
Content-Type: application/json

{
  "organizationId": "org_123",
  "message": "Join my consultant portfolio"
}
Request Body:
{
  organizationId: string; // Organization ID to invite
  message?: string;        // Optional invitation message
}
Response (200 OK):
{
  "success": true,
  "data": {
    "id": "invite_456",
    "organizationId": "org_123",
    "consultantId": "consultant_789",
    "status": "pending",
    "createdAt": "2025-01-18T12:00:00Z"
  }
}
Error Responses:
  • 400 Bad Request: Invalid request body
  • 401 Unauthorized: Not authenticated
  • 409 Conflict: Client relationship already exists
  • 500 Internal Server Error: Invitation failed

Accept Client Invitation

Accept an invitation to join a consultant’s portfolio (client side).
POST /api/invitations/accept
Content-Type: application/json

{
  "invitationId": "invite_456"
}
Request Body:
{
  invitationId: string; // Invitation ID to accept
}
Response (200 OK):
{
  "success": true,
  "data": {
    "id": "client_rel_789",
    "consultantId": "consultant_789",
    "organizationId": "org_123",
    "status": "active",
    "acceptedAt": "2025-01-18T12:30:00Z"
  }
}

White-Label & Theming

Save Theme Configuration

Save custom theme for a client organization.
POST /api/theme/save
Content-Type: application/json

{
  "organizationId": "org_123",
  "theme": {
    "light": {
      "primary": "0.318 0.132 275.0",
      "background": "0.980 0.000 0.0"
    },
    "dark": {
      "primary": "0.450 0.150 275.0",
      "background": "0.150 0.010 255.0"
    }
  }
}
Request Body:
{
  organizationId: string;
  theme: {
    light: Record<string, string>; // 31 OKLCH color values
    dark: Record<string, string>;  // 31 OKLCH color values
  };
}
Response (200 OK):
{
  "success": true,
  "message": "Theme saved successfully",
  "organizationId": "org_123"
}
Error Responses:
  • 400 Bad Request: Invalid theme format
  • 401 Unauthorized: Not authenticated
  • 403 Forbidden: Not authorized for this organization
  • 500 Internal Server Error: Save failed

Generate Theme with AI

Generate a complete theme using AI from brand colors.
POST /api/theme/generate
Content-Type: application/json

{
  "primaryColor": "#0d1594",
  "secondaryColor": "#26dbd9",
  "context": "Professional healthcare organization"
}
Request Body:
{
  primaryColor: string;    // Hex color (e.g., "#0d1594")
  secondaryColor?: string; // Optional secondary color
  accentColor?: string;    // Optional accent color
  context?: string;        // Industry/brand context for AI
}
Response (200 OK):
{
  "success": true,
  "theme": {
    "light": {
      "primary": "0.318 0.132 275.0",
      "secondary": "0.700 0.100 200.0",
      "background": "0.980 0.000 0.0",
      // ... 28 more colors
    },
    "dark": {
      "primary": "0.450 0.150 275.0",
      "secondary": "0.750 0.120 200.0",
      "background": "0.150 0.010 255.0",
      // ... 28 more colors
    }
  }
}
Error Responses:
  • 400 Bad Request: Invalid color format
  • 401 Unauthorized: Not authenticated
  • 500 Internal Server Error: AI generation failed
  • 503 Service Unavailable: OpenAI API unavailable
AI Generation requires OpenAI API key configuration. Contact your administrator if this endpoint returns 503.

Bulk Operations

Bulk Invite Clients

Invite multiple clients via CSV upload.
POST /api/bulk/invite
Content-Type: multipart/form-data

--boundary
Content-Disposition: form-data; name="file"; filename="clients.csv"
Content-Type: text/csv

organizationId,message
org_123,Join my portfolio
org_456,Let's work together
--boundary--
CSV Format:
organizationId,message
org_123,Join my portfolio
org_456,Let's work together
org_789,Become my client
Response (200 OK):
{
  "success": true,
  "invited": 3,
  "failed": 0,
  "results": [
    {
      "organizationId": "org_123",
      "status": "success",
      "invitationId": "invite_001"
    },
    {
      "organizationId": "org_456",
      "status": "success",
      "invitationId": "invite_002"
    },
    {
      "organizationId": "org_789",
      "status": "success",
      "invitationId": "invite_003"
    }
  ]
}
Error Responses:
  • 400 Bad Request: Invalid CSV format
  • 401 Unauthorized: Not authenticated
  • 413 Payload Too Large: CSV exceeds 1MB
  • 500 Internal Server Error: Processing failed
CSV Limits: Maximum 500 rows per upload, 1MB file size

Export Portfolio Data

Export portfolio analytics to CSV.
GET /api/bulk/export?format=csv&period=month
Query Parameters:
  • format (required): Export format (csv or json)
  • period (optional): Time period (week, month, quarter, year)
Response (200 OK):
Client,Kindness Acts,Volunteer Hours,Donations,Compliance Rate
Acme Corp,234,1200,45000,92
TechStart,45,300,12000,67
GreenCo,67,450,18000,73
Error Responses:
  • 400 Bad Request: Invalid format
  • 401 Unauthorized: Not authenticated
  • 500 Internal Server Error: Export failed

Health Check

API Health Status

Check API availability and consultant service status.
GET /api/health
Response (200 OK):
{
  "status": "ok",
  "timestamp": "2025-01-18T12:00:00Z",
  "service": "consultant-api",
  "version": "1.0.0"
}

Rate Limiting

All consultant API endpoints are rate-limited to prevent abuse. Limits:
  • Authenticated: 100 requests / minute
  • Bulk operations: 10 requests / minute
  • AI generation: 5 requests / minute
Rate Limit Headers:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1642514400
429 Too Many Requests:
{
  "error": "Rate limit exceeded",
  "retryAfter": 60
}
Best Practice: Implement exponential backoff when you receive 429 responses.

Error Handling

Standard Error Format

All API errors follow a consistent JSON format:
{
  "error": "Human-readable error message",
  "details": {
    // Optional detailed error information
  },
  "code": "ERROR_CODE"
}

Common HTTP Status Codes

CodeMeaningDescription
200OKRequest succeeded
400Bad RequestInvalid request body or parameters
401UnauthorizedNot authenticated (missing session)
403ForbiddenAuthenticated but not authorized
404Not FoundResource doesn’t exist
409ConflictResource already exists
413Payload Too LargeRequest body exceeds limits
429Too Many RequestsRate limit exceeded
500Internal Server ErrorServer-side error
503Service UnavailableTemporary service outage

SDK Integration

Use the @repo/consultant package for type-safe API calls:
import { inviteClient, getPortfolioOverview } from '@repo/consultant';

// Invite a client
const invitation = await inviteClient(consultantId, {
  organizationId: 'org_123',
  message: 'Join my portfolio',
});

// Get portfolio analytics
const overview = await getPortfolioOverview(consultantId);

JavaScript Fetch

For external integrations without the SDK:
async function inviteClient(organizationId, message) {
  const response = await fetch('/api/invitations/send', {
    method: 'POST',
    credentials: 'include',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      organizationId,
      message,
    }),
  });

  if (!response.ok) {
    const error = await response.json();
    throw new Error(error.error);
  }

  return response.json();
}

Webhooks (Coming Soon)

Planned Feature: Webhook support for real-time client events will be available in Phase 2.
Planned webhook events:
  • client.invited - Client invitation sent
  • client.accepted - Client accepted invitation
  • client.cancelled - Client relationship ended
  • theme.updated - Client theme changed
  • portfolio.milestone - Portfolio metrics threshold reached

Additional Resources

Consultant Guide

User guide for consultant features

Theme Editor Guide

Theme editor for branding customization

Authentication

API Support: Contact api@ripplecore.co.uk for integration assistance or to report API issues.