> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/iii-hq/sdk/llms.txt
> Use this file to discover all available pages before exploring further.

# Functions

> Register and handle III functions

Functions are the core building blocks of III applications. They can be called by other services, triggered by events, or exposed as HTTP endpoints.

## registerFunction()

Register a function with a handler that executes when the function is invoked.

```typescript theme={null}
const functionRef = iii.registerFunction(message, handler)
```

<ParamField path="message" type="RegisterFunctionMessage" required>
  Function registration metadata

  <Expandable title="RegisterFunctionMessage properties">
    <ParamField path="id" type="string" required>
      Function ID/path (use `::` for namespacing, e.g., `service::function_name`)
    </ParamField>

    <ParamField path="description" type="string">
      Human-readable description of the function
    </ParamField>

    <ParamField path="request_format" type="RegisterFunctionFormat">
      JSON schema for input validation and documentation
    </ParamField>

    <ParamField path="response_format" type="RegisterFunctionFormat">
      JSON schema for output validation and documentation
    </ParamField>

    <ParamField path="metadata" type="Record<string, unknown>">
      Custom metadata attached to the function
    </ParamField>
  </Expandable>
</ParamField>

<ParamField path="handler" type="RemoteFunctionHandler<TInput, TOutput>" required>
  Async function that processes the input and returns output

  ```typescript theme={null}
  type RemoteFunctionHandler<TInput, TOutput> = (data: TInput) => Promise<TOutput>
  ```
</ParamField>

<ResponseField name="functionRef" type="FunctionRef">
  Reference to the registered function

  <Expandable title="FunctionRef properties">
    <ResponseField name="id" type="string">
      The function ID
    </ResponseField>

    <ResponseField name="unregister" type="() => void">
      Function to unregister this function
    </ResponseField>
  </Expandable>
</ResponseField>

### Example: Basic Function

```typescript theme={null}
import { init, getContext } from 'iii-sdk'

const iii = init('ws://localhost:49199')

const echoFn = iii.registerFunction(
  { 
    id: 'examples::echo',
    description: 'Echo back the input message'
  },
  async (data: { message: string }) => {
    const { logger } = getContext()
    logger.info('Echo called', { message: data.message })
    
    return { echoed: data.message }
  }
)

// Later: unregister
echoFn.unregister()
```

### Example: Typed Function with Validation

```typescript theme={null}
interface CreateUserInput {
  email: string
  name: string
  age?: number
}

interface CreateUserOutput {
  id: string
  email: string
  name: string
  created_at: string
}

iii.registerFunction<CreateUserInput, CreateUserOutput>(
  {
    id: 'users::create',
    description: 'Create a new user',
    request_format: {
      name: 'CreateUserInput',
      type: 'object',
      body: [
        { name: 'email', type: 'string', required: true },
        { name: 'name', type: 'string', required: true },
        { name: 'age', type: 'number', required: false }
      ]
    },
    response_format: {
      name: 'CreateUserOutput',
      type: 'object',
      body: [
        { name: 'id', type: 'string', required: true },
        { name: 'email', type: 'string', required: true },
        { name: 'name', type: 'string', required: true },
        { name: 'created_at', type: 'string', required: true }
      ]
    }
  },
  async (input) => {
    const { logger, trace } = getContext()
    
    // Validate input
    if (!input.email.includes('@')) {
      throw new Error('Invalid email address')
    }
    
    const user = {
      id: crypto.randomUUID(),
      email: input.email,
      name: input.name,
      created_at: new Date().toISOString()
    }
    
    logger.info('User created', { userId: user.id })
    trace?.setAttribute('user.id', user.id)
    
    return user
  }
)
```

## registerHttpFunction()

Register an external HTTP function (Lambda, Cloudflare Worker, etc.) that the engine invokes via HTTP.

```typescript theme={null}
const functionRef = iii.registerHttpFunction(id, config)
```

<ParamField path="id" type="string" required>
  Function ID/path
</ParamField>

<ParamField path="config" type="HttpInvocationConfig" required>
  HTTP endpoint configuration

  <Expandable title="HttpInvocationConfig properties">
    <ParamField path="url" type="string" required>
      HTTP endpoint URL
    </ParamField>

    <ParamField path="method" type="'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'" default="POST">
      HTTP method
    </ParamField>

    <ParamField path="timeout_ms" type="number">
      Request timeout in milliseconds
    </ParamField>

    <ParamField path="headers" type="Record<string, string>">
      Custom HTTP headers
    </ParamField>

    <ParamField path="auth" type="HttpAuthConfig">
      Authentication configuration

      <Expandable title="Auth types">
        <ParamField path="type" type="'hmac' | 'bearer' | 'api_key'" required>
          Authentication type
        </ParamField>

        **HMAC Auth:**

        ```typescript theme={null}
        { type: 'hmac', secret_key: string }
        ```

        **Bearer Token:**

        ```typescript theme={null}
        { type: 'bearer', token_key: string }
        ```

        **API Key:**

        ```typescript theme={null}
        { type: 'api_key', header: string, value_key: string }
        ```
      </Expandable>
    </ParamField>
  </Expandable>
</ParamField>

<ResponseField name="functionRef" type="FunctionRef">
  Reference to the registered HTTP function
</ResponseField>

### Example: Lambda Function

```typescript theme={null}
iii.registerHttpFunction(
  'external::my_lambda',
  {
    url: 'https://abc123.lambda-url.us-east-1.on.aws/',
    method: 'POST',
    timeout_ms: 30000,
    headers: {
      'Content-Type': 'application/json'
    },
    auth: {
      type: 'bearer',
      token_key: process.env.LAMBDA_TOKEN!
    }
  }
)

// Call the Lambda function like any other III function
const result = await iii.call('external::my_lambda', { data: 'test' })
```

### Example: Cloudflare Worker

```typescript theme={null}
iii.registerHttpFunction(
  'external::cf_worker',
  {
    url: 'https://my-worker.username.workers.dev/process',
    method: 'POST',
    headers: {
      'X-Custom-Header': 'value'
    },
    auth: {
      type: 'api_key',
      header: 'X-API-Key',
      value_key: process.env.CF_API_KEY!
    }
  }
)
```

## Function Handler Context

All function handlers have access to a context object via `getContext()`:

```typescript theme={null}
import { getContext } from 'iii-sdk'

iii.registerFunction(
  { id: 'example::handler' },
  async (data) => {
    const { logger, trace } = getContext()
    
    // Use the logger
    logger.info('Processing request', { data })
    
    // Add trace attributes
    trace?.setAttribute('custom.attribute', 'value')
    trace?.addEvent('Processing started')
    
    return { success: true }
  }
)
```

See [Context API](/nodejs/api/context) for full documentation.

## Error Handling

Throw errors in function handlers to propagate them to callers:

```typescript theme={null}
iii.registerFunction(
  { id: 'users::get' },
  async (data: { id: string }) => {
    const { logger } = getContext()
    
    const user = await db.users.findUnique({ where: { id: data.id } })
    
    if (!user) {
      logger.warn('User not found', { userId: data.id })
      throw new Error(`User not found: ${data.id}`)
    }
    
    return user
  }
)

// Caller receives the error
try {
  await iii.call('users::get', { id: 'invalid' })
} catch (error) {
  console.error('Error:', error.message) // "User not found: invalid"
}
```

## Unregistering Functions

Unregister functions when they're no longer needed:

```typescript theme={null}
const fn = iii.registerFunction(
  { id: 'temp::function' },
  async (data) => ({ result: 'ok' })
)

// Later: unregister
fn.unregister()

// Or call by function ID
iii.sendMessage(MessageType.UnregisterFunction, { id: 'temp::function' })
```

## Best Practices

<AccordionGroup>
  <Accordion title="Use namespacing for organization">
    Group related functions using `::` separator:

    ```typescript theme={null}
    // Good
    iii.registerFunction({ id: 'users::create' }, handler)
    iii.registerFunction({ id: 'users::update' }, handler)
    iii.registerFunction({ id: 'users::delete' }, handler)

    // Avoid
    iii.registerFunction({ id: 'createUser' }, handler)
    iii.registerFunction({ id: 'updateUser' }, handler)
    ```
  </Accordion>

  <Accordion title="Add descriptions for discoverability">
    ```typescript theme={null}
    iii.registerFunction(
      {
        id: 'orders::process_payment',
        description: 'Process payment for an order using Stripe'
      },
      handler
    )
    ```
  </Accordion>

  <Accordion title="Use TypeScript generics for type safety">
    ```typescript theme={null}
    interface Input { /* ... */ }
    interface Output { /* ... */ }

    iii.registerFunction<Input, Output>(
      { id: 'my::function' },
      async (data) => {
        // data is typed as Input
        // return type must match Output
      }
    )
    ```
  </Accordion>

  <Accordion title="Clean up on shutdown">
    ```typescript theme={null}
    const functions: FunctionRef[] = []

    functions.push(iii.registerFunction({ id: 'fn1' }, handler1))
    functions.push(iii.registerFunction({ id: 'fn2' }, handler2))

    process.on('SIGTERM', async () => {
      // Unregister all functions
      functions.forEach(fn => fn.unregister())
      await iii.shutdown()
    })
    ```
  </Accordion>
</AccordionGroup>
