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

# Edge Functions Documentation

> Version: 1.2.0 Last Updated: 2026-03-23 Total Functions: 294 (deployable folders under supabase/functions/, excluding _shared. Refresh count: npm run docs:edge…

**Version:** 1.2.0
**Last Updated:** 2026-03-23
**Total Functions:** 294 (deployable folders under `supabase/functions/`, excluding `_shared`. Refresh count: `npm run docs:edge-functions-count` from repo root.)

Reference for Supabase Edge Functions in this repo: deployment, shared code, env/secrets, and patterns. Functions cover integrations, async work, and backend processing.

***

## Table of Contents

1. [Overview](#overview)
2. [Architecture](#architecture)
3. [Shared Code](#shared-code)
4. [Deployment](#deployment)
5. [Environment Variables & Secrets](#environment-variables--secrets)
6. [Error Handling](#error-handling)
7. [Testing](#testing)
8. [Monitoring & Logging](#monitoring--logging)
9. [Function Reference](#function-reference)
   * [Platform Foundation Functions](#platform-foundation-functions)
   * [Forms & Workflow Functions](#forms--workflow-functions)
   * [HR Functions](#hr-functions)
   * [Finance Functions](#finance-functions)
   * [Recovery Housing Functions](#recovery-housing-functions)
   * [Facilities Functions](#facilities-functions)
   * [Testing Functions](#testing-functions)
   * [Reporting Functions](#reporting-functions)
   * [System Functions](#system-functions)

***

## Overview

Edge functions are Deno-based serverless functions deployed to Supabase that handle:

* Complex business logic processing
* External API integrations (email, SMS, AI)
* Async workflows and automation
* Scheduled task execution
* Data transformation and aggregation
* Testing and development utilities

**Key Characteristics:**

* Deno runtime (not Node.js)
* Serverless execution
* Automatic scaling
* CORS support
* JWT verification (configurable per function)

***

## Architecture

### Function Structure

```
supabase/functions/
├── _shared/                    # Shared utilities
│   ├── cors.ts                 # CORS headers
│   ├── errors.ts               # Error response utilities
│   ├── logger.ts               # Structured logging
│   ├── expressionEvaluator.ts  # Expression evaluation
│   └── executionHelpers.ts     # Execution utilities
├── {function-name}/
│   └── index.ts                # Function entry point
```

### Calling Patterns

**1. Direct HTTP Invocation:**

```typescript theme={null}
const response = await fetch(
  `${supabaseUrl}/functions/v1/{function-name}`,
  {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${anonKey}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(payload),
  }
);
```

**2. Database Trigger → Edge Function:**

```sql theme={null}
-- Trigger calls edge function via pg_net
CREATE OR REPLACE FUNCTION {core}_trigger_edge_function()
RETURNS TRIGGER LANGUAGE plpgsql SECURITY DEFINER AS $$
BEGIN
  PERFORM net.http_post(
    url := '{supabase_url}/functions/v1/{function-name}',
    headers := jsonb_build_object(
      'Authorization', 'Bearer ' || service_key,
      'Content-Type', 'application/json'
    ),
    body := jsonb_build_object(
      'record_id', NEW.id,
      'organization_id', NEW.organization_id,
      'action', TG_OP
    )
  );
  RETURN NEW;
END;
$$;
```

**3. Scheduled Task (pg\_cron):**

```sql theme={null}
SELECT cron.schedule(
  '{core}-{task-name}',
  '0 6 * * *',  -- 6 AM daily
  $$SELECT net.http_post(
    url:='{supabase_url}/functions/v1/{function-name}',
    headers:='{"Authorization": "Bearer {service_key}"}'::jsonb,
    body:='{"task": "{task-name}"}'::jsonb
  )$$
);
```

***

## Shared Code

Shared utilities are located in `supabase/functions/_shared/` to avoid code duplication.

### CORS Headers (`_shared/cors.ts`)

Standard CORS headers for all functions:

```typescript theme={null}
import { corsHeaders } from '../_shared/cors.ts';

// Use in responses
return new Response(JSON.stringify(data), {
  headers: { ...corsHeaders, 'Content-Type': 'application/json' }
});
```

### Error Handling (`_shared/errors.ts`)

Standardized error responses with correlation IDs:

```typescript theme={null}
import { createErrorResponse, ErrorCategory, createValidationError } from '../_shared/errors.ts';

// Standard error
return createErrorResponse(
  'Configuration error',
  ErrorCategory.CONFIG,
  correlationId,
  400
);

// Validation error (400)
return createValidationError('Invalid input', correlationId, { field: 'email' });
```

**Error Categories:**

* `CONFIG` - Configuration errors
* `VALIDATION` - Input validation errors
* `AUTHORIZATION` - Permission errors
* `NOT_FOUND` - Resource not found
* `RUNTIME` - Runtime errors
* `TIMEOUT` - Operation timeout
* `EXTERNAL_SERVICE` - External API errors

### Logging (`_shared/logger.ts`)

Structured logging with PHI protection:

```typescript theme={null}
import { createLogger } from '../_shared/logger.ts';

const logger = createLogger('function-name');
const correlationId = crypto.randomUUID();

logger.info('Request received', { correlationId, userId: 'uuid' });
logger.error('Operation failed', { correlationId, error: err.message });
```

**Never logs PHI/PII** - only stable IDs (user\_id, org\_id, etc.)

### Expression Evaluator (`_shared/expressionEvaluator.ts`)

Expression evaluation for workflow automation:

```typescript theme={null}
import { evaluateExpression, resolveTemplate } from '../_shared/expressionEvaluator.ts';

// Evaluate condition
const result = evaluateExpression('{{field1}} > 100', context);

// Resolve template
const message = resolveTemplate('Hello {{name}}', context);
```

***

## Deployment

### Local Development

```bash theme={null}
# Start Supabase locally
supabase start

# Serve all functions locally (omit function name to serve all; hot-reload is default)
supabase functions serve

# Or serve a single function
supabase functions serve {function-name}

# Test function (anon key from npx supabase status when using local)
curl -i --location --request POST 'http://localhost:54321/functions/v1/{function-name}' \
  --header 'Authorization: Bearer {anon-key}' \
  --header 'Content-Type: application/json' \
  --data '{"test": "data"}'
```

**Full workflow, debugging with breakpoints (inspect mode), and Deno unit tests:** See [EDGE\_AND\_API\_TESTING.md](../testing/EDGE_AND_API_TESTING.md).

### Production Deployment

```bash theme={null}
# Deploy single function
supabase functions deploy {function-name}

# Deploy all functions
supabase functions deploy

# Deploy with secrets
supabase secrets set GMAIL_SERVICE_ACCOUNT_JSON='...'
supabase functions deploy send-email-notification
```

### JWT Verification

Configure JWT verification in `supabase/config.toml`:

```toml theme={null}
[functions.{function-name}]
verify_jwt = true  # Require valid JWT
# or
verify_jwt = false  # No JWT required (for scheduled tasks, webhooks)
```

**Functions with `verify_jwt = true`:**

* `execute-report`
* `process-entity-mapping`
* `ai-assistant`
* `workflow-debug-control`
* `sandbox-execute`
* `test-*` functions
* `ai-document-analyze`
* `workflow-metrics-aggregate`
* `workflow-path-aggregate`
* `workflow-version-compare`

**Functions with `verify_jwt = false`:**

* Scheduled tasks (cron jobs)
* Database trigger invocations
* Batch processing functions

***

## Environment Variables & Secrets

### Required Secrets

**Supabase (Auto-provided):**

* `SUPABASE_URL` - Project URL
* `SUPABASE_SERVICE_ROLE_KEY` - Service role key

**Email (Entra ID or Gmail):**

* `ENTRA_CLIENT_SECRET` - For Entra/Graph API email (optional)
* `GMAIL_SERVICE_ACCOUNT_JSON` - For Gmail API org-level email (optional)
* Per-org config in Settings → Integrations; `send-email-notification` and others use shared `email-provider`

**SMS (Twilio):**

* `TWILIO_ACCOUNT_SID` - Twilio account SID (for `send-sms-notification`)
* `TWILIO_AUTH_TOKEN` - Twilio auth token
* `TWILIO_PHONE_NUMBER` - Twilio phone number

**AI (OpenRouter) - PF-59:**

* `OPENROUTER_API_KEY` - OpenRouter API key (for `ai-assistant`, `ai-document-analyze`, `generate-report-narrative`)
* `APP_URL` - Application URL for OpenRouter HTTP-Referer attribution (defaults to `https://encoreos.io`)

### Setting Secrets

```bash theme={null}
# Set secrets (email: Entra and/or Gmail)
supabase secrets set ENTRA_CLIENT_SECRET=xxxxx
supabase secrets set GMAIL_SERVICE_ACCOUNT_JSON='{"type":"service_account",...}'

# List secrets
supabase secrets list

# Unset secret
supabase secrets unset GMAIL_SERVICE_ACCOUNT_JSON
```

**Never commit secrets to version control!**

***

## Error Handling

All functions should:

1. Handle CORS preflight requests
2. Use standardized error responses
3. Include correlation IDs for tracing
4. Never expose PHI/PII in errors
5. Return appropriate HTTP status codes

**Standard Error Response:**

```json theme={null}
{
  "success": false,
  "error": "User-friendly error message",
  "category": "validation",
  "correlationId": "uuid",
  "details": {
    "field": "email",
    "expected": "valid email"
  }
}
```

**Success Response:**

```json theme={null}
{
  "success": true,
  "data": { ... },
  "correlationId": "uuid"
}
```

***

## Testing

### Unit Testing Edge Functions

Tests are co-located with functions, e.g. `supabase/functions/<name>/index.test.ts` or `<name>_test.ts`. Examples: [generate-templated-pdf/index.test.ts](../../supabase/functions/generate-templated-pdf/index.test.ts), [hr-encrypt-bank-account/index\_test.ts](../../supabase/functions/hr-encrypt-bank-account/index_test.ts).

**Environment:** Set `VITE_SUPABASE_URL` or `SUPABASE_URL` to `http://localhost:54321` and the anon key (from `npx supabase status`) when testing against local. Some tests also use `VITE_SUPABASE_PUBLISHABLE_KEY` or `TEST_AUTH_TOKEN`.

**Run from repo root:** `npm run test:functions` (runs `deno test` for all Edge Function tests). Or run `deno test --allow-all` from a function directory.

```typescript theme={null}
// test_edge_function.ts
import { assertEquals } from 'https://deno.land/std@0.168.0/testing/asserts.ts';

Deno.test('function handles valid input', async () => {
  const response = await fetch('http://localhost:54321/functions/v1/{function-name}', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${Deno.env.get('VITE_SUPABASE_PUBLISHABLE_KEY')}` },
    body: JSON.stringify({ test: 'data' }),
  });

  const data = await response.json();
  assertEquals(data.success, true);
});
```

**Debugging:** Run `npm run supabase:functions:serve:inspect`, then open Chrome → `chrome://inspect` → Configure `127.0.0.1:8083` → Open dedicated DevTools for Node. Trigger a function to pause on first line and use breakpoints. See [EDGE\_AND\_API\_TESTING.md](../testing/EDGE_AND_API_TESTING.md).

### Integration Testing

Test functions with real database:

* Use test database with seed data
* Verify RLS policies are enforced
* Test error scenarios
* Verify logging output

### Troubleshooting (Edge Functions)

* **401/403 locally:** Missing or invalid `Authorization` header; confirm anon key or user JWT; check `verify_jwt` if used.
* **Import errors:** Use `npm:` or `jsr:` specifiers and pin versions.
* **File writes:** Only under `/tmp`.
* **Hanging requests:** Unawaited Promises; offload background work with `EdgeRuntime.waitUntil(promise)`.

Full checklist: [EDGE\_AND\_API\_TESTING.md](../testing/EDGE_AND_API_TESTING.md#troubleshooting-checklist).

***

## Monitoring & Logging

### Logging Best Practices

1. **Use structured logging:**
   ```typescript theme={null}
   logger.info('Processing request', { correlationId, userId, orgId });
   ```

2. **Never log PHI/PII:**
   * ❌ `logger.info('Processing user', { name: 'John Doe', ssn: '123-45-6789' });`
   * ✅ `logger.info('Processing user', { userId: 'uuid', orgId: 'uuid' });`

3. **Include correlation IDs:**
   * Generate UUID for each request
   * Include in all log entries
   * Return in response headers

4. **Log levels:**
   * `debug` - Development debugging
   * `info` - Normal operations
   * `warn` - Warning conditions
   * `error` - Error conditions

### Monitoring

* **Supabase Dashboard:** View function logs and metrics
* **Error Tracking:** Integrate with Sentry/LogRocket
* **Performance:** Monitor execution time and memory usage
* **Alerts:** Set up alerts for error rates > 1%

***

## Function Reference

### Platform Foundation Functions

#### `admin-reset-password`

**Purpose:** Admin-initiated password reset for users

**Input:**

```json theme={null}
{
  "userId": "uuid",
  "newPassword": "string",
  "resetBy": "uuid"
}
```

**Output:**

```json theme={null}
{
  "success": true,
  "message": "Password reset successfully"
}
```

**Environment Variables:** None (uses Supabase Auth)

**JWT Verification:** `true` (admin only)

**Related Tables:** `auth.users`, `pf_profiles`

**Error Codes:**

* `400` - Invalid input
* `403` - Insufficient permissions
* `404` - User not found
* `500` - Reset failed

***

#### `ai-assistant`

**Purpose:** AI chat integration via OpenRouter (PF-59)

**Model Selection:** Automatic based on `moduleContext.module`:

* `gr`, `hr` → `anthropic/claude-3.5-sonnet` (compliance/policy reasoning)
* `fa`, `fw`, `rh`, `pf`, `ce` → `openai/gpt-4o` (general analysis)
* `lo`, `fm` → `openai/gpt-4o-mini` (fast summaries)

**Input:**

```json theme={null}
{
  "messages": [
    { "role": "user", "content": "..." }
  ],
  "moduleContext": {
    "module": "hr",
    "feature": "credentialing"
  },
  "stream": true
}
```

**Output (Streaming):**
SSE stream with `Content-Type: text/event-stream`

**Output (Non-Streaming):**

```json theme={null}
{
  "content": "AI response text",
  "usage": {
    "prompt_tokens": 100,
    "completion_tokens": 200,
    "total_tokens": 300,
    "cost": 0.003
  },
  "model": "openai/gpt-4o"
}
```

**Environment Variables:**

* `OPENROUTER_API_KEY` (required)
* `APP_URL` (optional, defaults to `https://encoreos.io`)

**JWT Verification:** `true`

**PHI Protection:** Automatically detects and logs PHI patterns in prompts

**Rate Limiting:** Exponential backoff retry on 429 (1s, 2s, 4s, max 30s)

**Error Codes:**

* `401` - Authentication required
* `402` - AI credits depleted
* `403` - User not in organization
* `429` - Rate limited (auto-retry)
* `500` - Service error

**Shared Utilities:** `supabase/functions/_shared/ai-utils.ts`

**Related Documentation:**

* `specs/pf/PF-27-platform-ai.md`
* `specs/pf/PF-59-ai-provider-migration.md`
* `src/platform/ai/README.md`

***

#### `ai-document-analyze`

**Purpose:** Analyze documents using AI via OpenRouter (PF-59)

**Model:** `openai/gpt-4o` (optimized for structured output with tool calling)

**Input:**

```json theme={null}
{
  "fileBase64": "base64-encoded-content",
  "fileName": "document.pdf",
  "fileType": "application/pdf",
  "analysisPrompt": "Extract key information from this document",
  "toolSchema": {
    "name": "extract_document_info",
    "description": "Extract key information",
    "parameters": {
      "type": "object",
      "properties": { ... },
      "required": [ ... ],
      "additionalProperties": false
    }
  },
  "moduleContext": {
    "module": "hr",
    "entityType": "credential",
    "entityId": "uuid"
  }
}
```

**Output:**

```json theme={null}
{
  "result": { ... },
  "model": "openai/gpt-4o"
}
```

**Environment Variables:**

* `OPENROUTER_API_KEY` (required)
* `APP_URL` (optional)

**JWT Verification:** `true`

**Multimodal Support:** Images (base64), PDFs, text documents

**Rate Limiting:** Exponential backoff retry on 429

**Error Codes:**

* `400` - Missing required fields
* `402` - AI credits exhausted
* `429` - Rate limited
* `500` - Service error

**Related Tables:** `pf_documents`, `pf_ai_usage_logs`

**Related Documentation:** `specs/pf/PF-59-ai-provider-migration.md`

***

#### `event-consumer`

**Purpose:** Route domain events to appropriate handlers

**Input:**

```json theme={null}
{
  "event_type": "leave_approved" | "credential_expired" | ...,
  "payload": {
    "organizationId": "uuid",
    "entityId": "uuid",
    "timestamp": "ISO8601",
    ...
  }
}
```

**Output:**

```json theme={null}
{
  "success": true,
  "handled": true,
  "handler": "leave_approved_handler"
}
```

**JWT Verification:** `false` (called by database triggers)

**Event Handlers:**

* `leave_approved` → Updates leave balances
* `leave_cancelled` → Reverses leave balance changes
* Future: `credential_expired`, `onboarding_completed`

**Related Documentation:** `docs/architecture/integrations/EVENT_CONTRACTS.md`

***

#### `execute-report`

**Purpose:** Execute report definitions and return results

**Input:**

```json theme={null}
{
  "reportId": "uuid",
  "organizationId": "uuid",
  "parameters": {
    "startDate": "2025-01-01",
    "endDate": "2025-01-31",
    ...
  }
}
```

**Output:**

```json theme={null}
{
  "success": true,
  "data": [ ... ],
  "metadata": {
    "rowCount": 100,
    "executionTime": 1.23
  }
}
```

**JWT Verification:** `true`

**Related Tables:** `pf_report_definitions`, `pf_report_history`

***

#### `send-email-notification`

**Purpose:** Send email notifications via org-configured provider (Entra or Gmail)

**Input:**

```json theme={null}
{
  "notificationId": "uuid",
  "recipientEmail": "user@example.com",
  "recipientName": "John Doe",
  "subject": "Notification Subject",
  "body": "Email body with {{variables}}",
  "variables": {
    "name": "John",
    "action": "approved"
  }
}
```

**Output:**

```json theme={null}
{
  "success": true,
  "messageId": "entra-|gmail-<id>",
  "sentAt": "2025-01-07T10:00:00Z"
}
```

**Environment Variables:**

* `ENTRA_CLIENT_SECRET` or `GMAIL_SERVICE_ACCOUNT_JSON` (per org config; see EMAIL\_SMS\_SETUP.md)

**JWT Verification:** `false` (called by notification system)

**Error Codes:**

* `400` - Invalid email or template
* `500` - Email provider (Entra/Gmail) API error

**Related Tables:** `pf_notifications`

***

#### `send-sms-notification` (deprecated)

**Status:** Deprecated. Forwards to `sms-send`. Use `sms-send` directly with body `{ organizationId, toNumber, body, mergeData? }`.

**Purpose:** Legacy SMS notifications (Twilio-only). Now a thin wrapper that invokes `sms-send` and updates `pf_notifications` delivery status.

**Input:** `notificationId`, `phoneNumber`, `message`, `variables?`, `organizationId?` (optional; if omitted, looked up from `pf_notifications` by `notificationId`).

**Output:** Same shape as before: `{ success, messageId, notificationId, status }` for backward compatibility.

**Migration:** Call `sms-send` with `organizationId` (from notification or context), `toNumber` = phoneNumber, `body` = message after variable substitution, `mergeData` = variables. After 30 days with no callers, this function will be removed.

***

#### `send-pending-notifications`

**Purpose:** Batch process pending notifications (email and SMS)

**Input:**

```json theme={null}
{
  "organizationId": "uuid",
  "limit": 100
}
```

**Output:**

```json theme={null}
{
  "success": true,
  "processed": 50,
  "succeeded": 48,
  "failed": 2,
  "results": [ ... ]
}
```

**JWT Verification:** `false` (scheduled task)

**Scheduled:** Daily via `pg_cron`

**Related Tables:** `pf_notifications`

***

### Forms & Workflow Functions

#### `aggregate-form-analytics`

**Purpose:** Aggregate form submission analytics

**Input:**

```json theme={null}
{
  "formId": "uuid",
  "organizationId": "uuid",
  "dateRange": {
    "start": "2025-01-01",
    "end": "2025-01-31"
  }
}
```

**Output:**

```json theme={null}
{
  "success": true,
  "analytics": {
    "totalSubmissions": 100,
    "completionRate": 0.85,
    "averageTime": 120,
    "fieldAnalytics": { ... }
  }
}
```

**JWT Verification:** `false`

**Related Tables:** `fw_form_submissions`, `fw_form_analytics`

***

#### `automation-executor`

**Purpose:** Execute automation rules and workflows (FW-03, FW-06, FW-18)

**Input:**

```json theme={null}
{
  "trigger_type": "form_submitted" | "form_updated" | "schedule" | "webhook",
  "submission_id": "uuid",
  "form_id": "uuid",
  "organization_id": "uuid",
  "submission_data": { ... }
}
```

**Output:**

```json theme={null}
{
  "success": true,
  "executed": true,
  "rulesExecuted": 3,
  "actionsExecuted": 5
}
```

**JWT Verification:** `false` (called by database triggers)

**Features:**

* Condition evaluation
* Action execution (email, notification, update, webhook)
* Workflow graph execution (FW-06)
* Variable management (FW-18)
* Retry logic
* Circuit breaker

**Idempotency (R7):** When invoked with `execution_id` or `event_id`, the executor MUST check if that id has already been processed (e.g. in `fw_workflow_executions` or a processed-events store) and skip duplicate execution. Use exponential backoff (1s, 2s, 4s) on transient failures; handlers must be safe when invoked more than once.

**Related Tables:** `fw_automation_rules`, `fw_workflows`, `fw_automation_logs`

**Related Documentation:** `specs/fw/specs/FW-03-automation-engine.md`, `specs/fw/specs/FW-06-visual-workflows.md`

***

#### `process-entity-mapping`

**Purpose:** Create/update entity records from form submissions (FW-03/PF-16)

**Input:**

```json theme={null}
{
  "submissionData": {
    "field1": "value1",
    "field2": "value2"
  },
  "actionConfig": {
    "targetEntity": "hr_employees",
    "operation": "create" | "update" | "upsert",
    "lookupField": "email",
    "fieldMappings": [
      {
        "formField": "email",
        "entityField": "email",
        "isCustomField": false
      },
      {
        "formField": "badge_number",
        "entityField": "custom_fields.badge_number",
        "isCustomField": true
      }
    ]
  },
  "organizationId": "uuid",
  "userId": "uuid"
}
```

**Output:**

```json theme={null}
{
  "success": true,
  "entityId": "uuid",
  "operation": "created",
  "mappedFields": 5
}
```

**JWT Verification:** `true`

**Features:**

* Maps form fields to entity columns
* Supports `custom_fields` JSONB mapping
* Handles create/update/upsert operations
* Template variable resolution

**Related Tables:** All entity tables with `custom_fields`

**Related Documentation:** `specs/fw/specs/FW-03-automation-engine.md`

***

#### `validate-form-submission`

**Purpose:** Validate form submissions before processing

**Input:**

```json theme={null}
{
  "formId": "uuid",
  "submissionData": { ... },
  "organizationId": "uuid"
}
```

**Output:**

```json theme={null}
{
  "success": true,
  "valid": true,
  "errors": []
}
```

**JWT Verification:** `false`

**Related Tables:** `fw_forms`, `fw_form_submissions`

***

#### `workflow-debug-control`

**Purpose:** Debug workflow execution (enable/disable, step through)

**Input:**

```json theme={null}
{
  "action": "enable" | "disable" | "step",
  "workflowId": "uuid",
  "organizationId": "uuid"
}
```

**Output:**

```json theme={null}
{
  "success": true,
  "debugMode": "enabled",
  "currentStep": "node-123"
}
```

**JWT Verification:** `true` (admin only)

**Related Tables:** `fw_workflows`, `fw_workflow_runs`

***

#### `workflow-metrics-aggregate`

**Purpose:** Aggregate workflow performance metrics

**Input:**

```json theme={null}
{
  "workflowId": "uuid",
  "organizationId": "uuid",
  "dateRange": {
    "start": "2025-01-01",
    "end": "2025-01-31"
  }
}
```

**Output:**

```json theme={null}
{
  "success": true,
  "metrics": {
    "totalRuns": 100,
    "averageDuration": 2.5,
    "successRate": 0.95,
    "errorRate": 0.05
  }
}
```

**JWT Verification:** `true`

**Related Tables:** `fw_workflow_runs`, `fw_workflow_metrics`

***

#### `workflow-notification-trigger`

**Purpose:** Trigger notifications from workflow nodes

**Input:**

```json theme={null}
{
  "workflowRunId": "uuid",
  "nodeId": "uuid",
  "notificationConfig": {
    "type": "email" | "sms" | "in_app",
    "recipient": "uuid",
    "template": "..."
  }
}
```

**Output:**

```json theme={null}
{
  "success": true,
  "notificationId": "uuid"
}
```

**JWT Verification:** `false` (called by workflow executor)

**Related Tables:** `fw_workflow_runs`, `pf_notifications`

***

#### `workflow-path-aggregate`

**Purpose:** Aggregate workflow execution paths for analytics

**Input:**

```json theme={null}
{
  "workflowId": "uuid",
  "organizationId": "uuid"
}
```

**Output:**

```json theme={null}
{
  "success": true,
  "paths": [
    {
      "path": ["start", "condition", "action"],
      "count": 50,
      "averageDuration": 2.3
    }
  ]
}
```

**JWT Verification:** `true`

**Related Tables:** `fw_workflow_runs`

***

#### `workflow-version-compare`

**Purpose:** Compare workflow versions for changes

**Input:**

```json theme={null}
{
  "workflowId": "uuid",
  "version1": 1,
  "version2": 2,
  "organizationId": "uuid"
}
```

**Output:**

```json theme={null}
{
  "success": true,
  "changes": {
    "nodesAdded": 2,
    "nodesRemoved": 1,
    "nodesModified": 3,
    "edgesChanged": 5
  }
}
```

**JWT Verification:** `true`

**Related Tables:** `fw_workflows`, `fw_workflow_versions`

***

### HR Functions

#### `export-employee-list`

**Purpose:** Export employee list to CSV/Excel

**Input:**

```json theme={null}
{
  "organizationId": "uuid",
  "filters": {
    "departmentId": "uuid",
    "status": "active"
  },
  "format": "csv" | "xlsx"
}
```

**Output:**

```json theme={null}
{
  "success": true,
  "fileUrl": "https://storage.supabase.co/...",
  "fileName": "employees_2025-01-07.csv",
  "rowCount": 150
}
```

**JWT Verification:** `true`

**Related Tables:** `hr_employees`, `pf_departments`

***

#### `export-payroll-data`

**Purpose:** Export payroll data for external payroll systems

**Input:**

```json theme={null}
{
  "organizationId": "uuid",
  "payPeriod": {
    "start": "2025-01-01",
    "end": "2025-01-15"
  },
  "format": "csv" | "adp" | "paychex"
}
```

**Output:**

```json theme={null}
{
  "success": true,
  "fileUrl": "https://storage.supabase.co/...",
  "fileName": "payroll_2025-01-07.csv",
  "employeeCount": 50
}
```

**JWT Verification:** `true` (admin only)

**Related Tables:** `hr_timesheets`, `hr_employees`, `hr_payroll_exports`

***

#### `generate-schedule`

**Purpose:** Generate shift schedules based on templates and availability

**Input:**

```json theme={null}
{
  "organizationId": "uuid",
  "siteId": "uuid",
  "startDate": "2025-01-07",
  "endDate": "2025-01-21",
  "templateId": "uuid"
}
```

**Output:**

```json theme={null}
{
  "success": true,
  "shiftsCreated": 84,
  "coverage": {
    "totalHours": 672,
    "requiredHours": 700,
    "coveragePercent": 96
  }
}
```

**JWT Verification:** `false` (scheduled task)

**Related Tables:** `hr_shift_templates`, `hr_shifts`, `hr_employee_availability`

***

#### `generate-timesheets`

**Purpose:** Generate timesheets for pay period

**Input:**

```json theme={null}
{
  "organizationId": "uuid",
  "payPeriod": {
    "start": "2025-01-01",
    "end": "2025-01-15"
  },
  "employeeIds": ["uuid"] // Optional: specific employees
}
```

**Output:**

```json theme={null}
{
  "success": true,
  "timesheetsCreated": 50,
  "totalHours": 3200,
  "overtimeHours": 150
}
```

**JWT Verification:** `false` (scheduled task)

**Scheduled:** Weekly (Sunday) via `pg_cron`

**Related Tables:** `hr_timesheets`, `hr_time_entries`, `hr_employees`

**Database Function:** `calculate_timesheet_hours()`

***

#### `process-leave-accruals`

**Purpose:** Process leave accruals for all employees

**Input:**

```json theme={null}
{
  "organizationId": "uuid",
  "accrualDate": "2025-01-01"
}
```

**Output:**

```json theme={null}
{
  "success": true,
  "employeesProcessed": 100,
  "accrualsApplied": 150,
  "totalHoursAccrued": 300
}
```

**JWT Verification:** `false` (scheduled task)

**Scheduled:** Monthly (1st of month) via `pg_cron`

**Related Tables:** `hr_leave_balances`, `hr_leave_policies`, `hr_employees`

***

#### `process-swap-request`

**Purpose:** Process shift swap requests and approvals

**Input:**

```json theme={null}
{
  "swapRequestId": "uuid",
  "action": "approve" | "reject" | "auto_approve",
  "approvedBy": "uuid"
}
```

**Output:**

```json theme={null}
{
  "success": true,
  "status": "approved",
  "shiftsSwapped": 2
}
```

**JWT Verification:** `false` (called by application)

**Related Tables:** `hr_shift_swaps`, `hr_shifts`

***

#### `send-credential-alerts`

**Purpose:** Send alerts for expiring credentials

**Input:**

```json theme={null}
{
  "organizationId": "uuid",
  "alertDays": [90, 60, 30, 14, 7]
}
```

**Output:**

```json theme={null}
{
  "success": true,
  "alertsSent": 25,
  "expiringCredentials": [
    {
      "credentialId": "uuid",
      "employeeId": "uuid",
      "daysUntilExpiry": 30
    }
  ]
}
```

**JWT Verification:** `false` (scheduled task)

**Scheduled:** Daily via `pg_cron`

**Related Tables:** `hr_employee_credentials`, `hr_employees`, `pf_notifications`

***

#### `validate-shift-assignment`

**Purpose:** Validate shift assignments (credentials, availability, conflicts)

**Input:**

```json theme={null}
{
  "shiftId": "uuid",
  "employeeId": "uuid",
  "organizationId": "uuid"
}
```

**Output:**

```json theme={null}
{
  "success": true,
  "valid": true,
  "warnings": [],
  "errors": []
}
```

**JWT Verification:** `false` (called by application)

**Validation Checks:**

* Employee has required credentials
* Employee is available (no conflicts)
* Shift doesn't exceed max hours
* Site access permissions

**Related Tables:** `hr_shifts`, `hr_employees`, `hr_employee_credentials`

***

### Finance Functions

#### `check-budget-alerts`

**Purpose:** Check budgets and create alerts for threshold breaches

**Input:**

```json theme={null}
{
  "organizationId": "uuid",
  "thresholdPercent": 10.0
}
```

**Output:**

```json theme={null}
{
  "success": true,
  "totalAlerts": 5,
  "organizationsChecked": 10,
  "results": [
    {
      "org_id": "uuid",
      "success": true,
      "alerts_created": 2
    }
  ]
}
```

**JWT Verification:** `false` (scheduled task)

**Scheduled:** Daily via `pg_cron`

**Database Function:** `fa_check_budget_alerts()`

**Related Tables:** `fa_budgets`, `fa_budget_alerts`, `pf_organizations`

***

#### FA scheduled reports (via `execute-scheduled-reports`)

**Purpose:** Execute scheduled Finance & Accounting reports. Use the unified `execute-scheduled-reports` function with body `{ module: 'fa' }`.

**Input:** Same as `execute-scheduled-reports` with `module: 'fa'` in the request body. Cron callers should invoke `execute-scheduled-reports` with `{ "module": "fa" }`.

**Related Tables:** `fa_report_schedules`, `fa_report_runs`, `fa_get_due_scheduled_reports` (RPC)

***

#### `generate-fa-report-export`

**Purpose:** Generate Finance report exports (PDF, Excel, CSV)

**Input:**

```json theme={null}
{
  "reportId": "uuid",
  "format": "pdf" | "xlsx" | "csv",
  "organizationId": "uuid"
}
```

**Output:**

```json theme={null}
{
  "success": true,
  "fileUrl": "https://storage.supabase.co/...",
  "fileName": "trial_balance_2025-01-07.pdf"
}
```

**JWT Verification:** `false`

**Related Tables:** `fa_reports`, `pf_documents`

***

#### `parse-bank-statement`

**Purpose:** Parse bank statement files (CSV, OFX, QIF) and extract transactions

**Input:**

```json theme={null}
{
  "fileUrl": "https://storage.supabase.co/...",
  "organizationId": "uuid",
  "accountId": "uuid",
  "format": "csv" | "ofx" | "qif"
}
```

**Output:**

```json theme={null}
{
  "success": true,
  "transactionsParsed": 150,
  "transactionsCreated": 145,
  "duplicatesSkipped": 5,
  "transactions": [ ... ]
}
```

**JWT Verification:** `false`

**Related Tables:** `fa_bank_accounts`, `fa_transactions`

***

#### `query-payment-status`

**Purpose:** Query payment status from payment processors

**Input:**

```json theme={null}
{
  "paymentId": "uuid",
  "organizationId": "uuid",
  "processor": "stripe" | "paypal" | "ach"
}
```

**Output:**

```json theme={null}
{
  "success": true,
  "status": "completed" | "pending" | "failed",
  "processorResponse": { ... }
}
```

**JWT Verification:** `false`

**Related Tables:** `fa_payments`, `fa_invoices`

***

#### `trigger-invoice-creation`

**Purpose:** Trigger invoice creation from resident charges or other events

**Input:**

```json theme={null}
{
  "chargeId": "uuid",
  "organizationId": "uuid",
  "residentId": "uuid"
}
```

**Output:**

```json theme={null}
{
  "success": true,
  "invoiceId": "uuid",
  "invoiceNumber": "INV-2025-001"
}
```

**JWT Verification:** `false` (called by database triggers)

**Related Tables:** `fa_invoices`, `rh_resident_charges`, `rh_residents`

***

### Recovery Housing Functions

#### `rh-charge-invoice`

**Purpose:** Create invoices from resident charges

**Input:**

```json theme={null}
{
  "residentId": "uuid",
  "organizationId": "uuid",
  "chargeIds": ["uuid"],
  "invoiceDate": "2025-01-07"
}
```

**Output:**

```json theme={null}
{
  "success": true,
  "invoiceId": "uuid",
  "invoiceNumber": "INV-2025-001",
  "totalAmount": 1500.00
}
```

**JWT Verification:** `false`

**Related Tables:** `fa_invoices`, `rh_resident_charges`, `rh_residents`

***

### Facilities Functions

#### `check-overdue-pms`

**Purpose:** Check for overdue preventive maintenance tasks

**Input:**

```json theme={null}
{
  "organizationId": "uuid",
  "siteId": "uuid" // Optional
}
```

**Output:**

```json theme={null}
{
  "success": true,
  "overdueCount": 5,
  "overdueTasks": [
    {
      "pmScheduleId": "uuid",
      "dueDate": "2025-01-01",
      "daysOverdue": 6
    }
  ]
}
```

**JWT Verification:** `false` (scheduled task)

**Scheduled:** Daily via `pg_cron`

**Related Tables:** `fm_pm_schedules`, `fm_work_orders`

***

#### `check-vendor-certifications`

**Purpose:** Check vendor certifications for expiration

**Input:**

```json theme={null}
{
  "organizationId": "uuid",
  "alertDays": [90, 60, 30]
}
```

**Output:**

```json theme={null}
{
  "success": true,
  "expiringCertifications": [
    {
      "vendorId": "uuid",
      "certificationId": "uuid",
      "daysUntilExpiry": 30
    }
  ],
  "alertsCreated": 3
}
```

**JWT Verification:** `false` (scheduled task)

**Scheduled:** Daily via `pg_cron`

**Related Tables:** `fm_vendors`, `fm_vendor_certifications`, `pf_notifications`

***

#### `generate-pm-work-orders`

**Purpose:** Generate work orders from PM schedules

**Input:**

```json theme={null}
{
  "organizationId": "uuid",
  "pmScheduleId": "uuid", // Optional: specific schedule
  "dueDate": "2025-01-07"
}
```

**Output:**

```json theme={null}
{
  "success": true,
  "workOrdersCreated": 10,
  "workOrderIds": ["uuid"]
}
```

**JWT Verification:** `false` (scheduled task)

**Scheduled:** Daily via `pg_cron`

**Related Tables:** `fm_pm_schedules`, `fm_work_orders`

***

### Testing Functions

#### `calculate-test-coverage`

**Purpose:** Calculate test coverage metrics

**Input:**

```json theme={null}
{
  "organizationId": "uuid",
  "testSuite": "unit" | "integration" | "rls" | "all"
}
```

**Output:**

```json theme={null}
{
  "success": true,
  "coverage": {
    "unit": 0.85,
    "integration": 0.72,
    "rls": 0.95,
    "overall": 0.84
  }
}
```

**JWT Verification:** `true` (admin only)

**Related Tables:** Test results database

***

#### `sandbox-execute`

**Purpose:** Execute code in sandboxed environment for testing

**Input:**

```json theme={null}
{
  "code": "function test() { return 1 + 1; }",
  "language": "typescript" | "javascript"
}
```

**Output:**

```json theme={null}
{
  "success": true,
  "result": 2,
  "executionTime": 0.05
}
```

**JWT Verification:** `true` (admin only)

**Security:** Sandboxed execution, no file system access

***

#### `test-cases-run`

**Purpose:** Run test cases and return results

**Input:**

```json theme={null}
{
  "testSuite": "unit" | "integration" | "rls",
  "testFiles": ["test1.ts", "test2.ts"] // Optional
}
```

**Output:**

```json theme={null}
{
  "success": true,
  "results": {
    "passed": 50,
    "failed": 2,
    "skipped": 1,
    "duration": 12.5
  },
  "failures": [ ... ]
}
```

**JWT Verification:** `true` (admin only)

***

#### `test-datasets-import`

**Purpose:** Import test datasets for testing

**Input:**

```json theme={null}
{
  "dataset": "employees" | "residents" | "transactions",
  "organizationId": "uuid",
  "count": 100
}
```

**Output:**

```json theme={null}
{
  "success": true,
  "recordsCreated": 100,
  "dataset": "employees"
}
```

**JWT Verification:** `true` (admin only)

**Note:** Only works in development/staging environments

***

### Reporting Functions

#### `execute-scheduled-reports`

**Purpose:** Execute scheduled reports for Platform (PF) or Finance (FA). Single function with request body `module` to route.

**Input:**

```json theme={null}
{
  "module": "pf" | "fa",  // default "pf"
  "scheduleId": "uuid",   // optional, for PF: run specific schedule
  "manual": true,         // optional
  "source": "pg_cron"     // optional
}
```

For PF: queries `pf_report_schedules`, runs reports, emails via `send-email-notification`. For FA: calls `fa_get_due_scheduled_reports`, creates `fa_report_runs`, updates `fa_report_schedules`.

**Output:**

```json theme={null}
{
  "success": true,
  "reportsExecuted": 5,
  "results": [ ... ]
}
```

**JWT Verification:** `false` (scheduled task)

**Scheduled:** Daily/Weekly/Monthly via `pg_cron`

**Related Tables:** `pf_report_definitions`, `pf_report_history`

***

#### `generate-compliance-report`

**Purpose:** Generate compliance reports (HR credentials, RH incidents, etc.)

**Input:**

```json theme={null}
{
  "organizationId": "uuid",
  "reportType": "credential_compliance" | "incident_compliance",
  "dateRange": {
    "start": "2025-01-01",
    "end": "2025-01-31"
  }
}
```

**Output:**

```json theme={null}
{
  "success": true,
  "reportId": "uuid",
  "fileUrl": "https://storage.supabase.co/...",
  "findings": [ ... ]
}
```

**JWT Verification:** `false`

**Related Tables:** `hr_employee_credentials`, `rh_significant_events`, `pf_reports`

***

#### `generate-report-narrative`

**Purpose:** Generate AI-powered narrative summaries for reports via OpenRouter (PF-59)

**Model:** `openai/gpt-4o` (for detailed analysis and recommendations)

**Input:**

```json theme={null}
{
  "reportTitle": "Monthly Financial Summary",
  "reportType": "financial",
  "data": {
    "totalRevenue": 125000,
    "totalExpenses": 98000,
    "metrics": [ ... ]
  },
  "dateRange": {
    "start": "2025-01-01",
    "end": "2025-01-31"
  },
  "options": {
    "includeInsights": true,
    "includeRecommendations": true,
    "tone": "formal",
    "maxLength": 500
  }
}
```

**Output:**

```json theme={null}
{
  "narrative": "Executive summary text with insights and recommendations...",
  "model": "openai/gpt-4o"
}
```

**Environment Variables:**

* `OPENROUTER_API_KEY` (required)
* `APP_URL` (optional)

**JWT Verification:** `false` (called by scheduled reports)

**Rate Limiting:** Exponential backoff retry on 429

**Error Codes:**

* `400` - Missing required fields
* `402` - AI credits exhausted
* `429` - Rate limited
* `500` - Service error

**Related Tables:** `pf_reports`, `pf_report_narratives`, `pf_ai_usage_logs`

**Related Documentation:** `specs/pf/PF-59-ai-provider-migration.md`

***

### System Functions

#### `process-alert-escalations`

**Purpose:** Process alert escalations based on time and severity

**Input:**

```json theme={null}
{
  "organizationId": "uuid",
  "alertType": "all" | "credential" | "budget" | "compliance"
}
```

**Output:**

```json theme={null}
{
  "success": true,
  "escalationsProcessed": 10,
  "escalatedAlerts": [ ... ]
}
```

**JWT Verification:** `false` (scheduled task)

**Scheduled:** Hourly via `pg_cron`

**Related Tables:** `pf_alerts`, `pf_notifications`

***

## Function Categories Summary

| Category            | Count  | Functions                                                                                                                                                                                                                             |
| ------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Platform Foundation | 8      | admin-reset-password, ai-assistant, ai-document-analyze, event-consumer, execute-report, send-email-notification, send-sms-notification, send-pending-notifications                                                                   |
| Forms & Workflow    | 9      | aggregate-form-analytics, automation-executor, process-entity-mapping, validate-form-submission, workflow-debug-control, workflow-metrics-aggregate, workflow-notification-trigger, workflow-path-aggregate, workflow-version-compare |
| HR                  | 8      | export-employee-list, export-payroll-data, generate-schedule, generate-timesheets, process-leave-accruals, process-swap-request, send-credential-alerts, validate-shift-assignment                                                    |
| Finance             | 6      | check-budget-alerts, execute-scheduled-reports (module=fa), generate-fa-report-export, parse-bank-statement, query-payment-status, trigger-invoice-creation                                                                           |
| Recovery Housing    | 1      | rh-charge-invoice                                                                                                                                                                                                                     |
| Facilities          | 3      | check-overdue-pms, check-vendor-certifications, generate-pm-work-orders                                                                                                                                                               |
| Testing             | 4      | calculate-test-coverage, sandbox-execute, test-cases-run, test-datasets-import                                                                                                                                                        |
| Reporting           | 3      | execute-scheduled-reports, generate-compliance-report, generate-report-narrative                                                                                                                                                      |
| System              | 1      | process-alert-escalations                                                                                                                                                                                                             |
| **Total**           | **43** |                                                                                                                                                                                                                                       |

**Note:** Some functions may serve multiple purposes. Count reflects primary categorization.

**AI Functions (PF-59):** The functions `ai-assistant`, `ai-document-analyze`, and `generate-report-narrative` use OpenRouter for AI capabilities with module-specific model selection. See `supabase/functions/_shared/ai-utils.ts` for model configuration.

***

## Best Practices

### 1. Error Handling

* Always use `_shared/errors.ts` for consistent error responses
* Include correlation IDs for tracing
* Never expose PHI/PII in error messages
* Return appropriate HTTP status codes

### 2. Logging

* Use `_shared/logger.ts` for structured logging
* Include correlation IDs in all log entries
* Never log PHI/PII (only stable IDs)
* Use appropriate log levels

### 3. Security

* Validate all inputs
* Enforce JWT verification where appropriate
* Use service role key only when necessary
* Never expose secrets in responses

### 4. Performance

* Keep execution time under 60 seconds (Supabase limit)
* Use database functions for complex queries
* Batch operations when possible
* Handle timeouts gracefully

### 5. Testing

* Test with real database (staging)
* Verify RLS policies are enforced
* Test error scenarios
* Verify logging output

***

## Troubleshooting

### Common Issues

**1. Function Timeout**

* **Symptom:** Function returns 504 after 60 seconds
* **Solution:** Break into smaller functions, use database functions for heavy processing

**2. CORS Errors**

* **Symptom:** Browser blocks requests
* **Solution:** Ensure CORS headers are included in all responses

**3. JWT Verification Failures**

* **Symptom:** 401 Unauthorized
* **Solution:** Check `verify_jwt` setting in `config.toml`, ensure valid JWT in request

**4. Missing Environment Variables**

* **Symptom:** Function fails with "not configured" error
* **Solution:** Set secrets via `supabase secrets set KEY=value`

**5. Database Connection Issues**

* **Symptom:** Function can't connect to database
* **Solution:** Verify `SUPABASE_URL` and `SUPABASE_SERVICE_ROLE_KEY` are set

***

## Related Documentation

* **Constitution:** `constitution.md` §5.8-5.9 (Edge Function Patterns)
* **AI Guide:** `AI_GUIDE.md` (Edge Function Patterns)
* **Integration Contracts:** `docs/architecture/integrations/EVENT_CONTRACTS.md`
* **Supabase Setup:** `docs/integrations/SUPABASE_SETUP.md` (when created)

***

**Document Owner:** Platform Team\
**Review Frequency:** Quarterly\
**Last Updated:** 2026-02-01
