> ## 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.

# Triggers

> Register triggers to invoke functions based on events

Triggers automatically invoke functions when specific events occur, such as HTTP requests, scheduled times, or custom events.

## registerTrigger()

Register a trigger to invoke a function when an event occurs.

```typescript theme={null}
const trigger = iii.registerTrigger(config)
```

<ParamField path="config" type="RegisterTriggerInput" required>
  Trigger configuration

  <Expandable title="RegisterTriggerInput properties">
    <ParamField path="type" type="string" required>
      Trigger type (e.g., `'http'`, `'cron'`, `'functions_available'`)
    </ParamField>

    <ParamField path="function_id" type="string" required>
      ID of the function to invoke
    </ParamField>

    <ParamField path="config" type="unknown" required>
      Type-specific trigger configuration
    </ParamField>
  </Expandable>
</ParamField>

<ResponseField name="trigger" type="Trigger">
  Trigger reference with unregister method

  <Expandable title="Trigger properties">
    <ResponseField name="unregister" type="() => void">
      Function to unregister this trigger
    </ResponseField>
  </Expandable>
</ResponseField>

## HTTP Triggers

Expose functions as HTTP endpoints.

### Basic HTTP Endpoint

```typescript theme={null}
import { init, type HttpRequest, type ApiResponse } from 'iii-sdk'

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

// Register function
iii.registerFunction(
  { id: 'api::hello' },
  async (req: HttpRequest): Promise<ApiResponse> => {
    return {
      status_code: 200,
      body: { message: 'Hello, World!' }
    }
  }
)

// Register HTTP trigger
const trigger = iii.registerTrigger({
  type: 'http',
  function_id: 'api::hello',
  config: {
    api_path: 'hello',
    http_method: 'GET'
  }
})
```

Endpoint available at: `http://localhost:3199/hello`

### HTTP Request Types

<ParamField path="HttpRequest" type="object">
  HTTP request object passed to function handlers

  <Expandable title="HttpRequest properties">
    <ParamField path="path_params" type="Record<string, string>">
      Path parameters extracted from route (e.g., `:id`)
    </ParamField>

    <ParamField path="query_params" type="Record<string, string | string[]>">
      Query string parameters
    </ParamField>

    <ParamField path="body" type="unknown">
      Parsed request body (JSON for `application/json`)
    </ParamField>

    <ParamField path="headers" type="Record<string, string | string[]>">
      HTTP request headers
    </ParamField>

    <ParamField path="method" type="string">
      HTTP method (GET, POST, etc.)
    </ParamField>

    <ParamField path="request_body" type="ChannelReader">
      Streaming request body reader
    </ParamField>
  </Expandable>
</ParamField>

<ParamField path="ApiResponse" type="object">
  JSON response format

  <Expandable title="ApiResponse properties">
    <ParamField path="status_code" type="number" required>
      HTTP status code (e.g., 200, 404, 500)
    </ParamField>

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

    <ParamField path="body" type="string | Buffer | Record<string, unknown>">
      Response body (automatically JSON-stringified for objects)
    </ParamField>
  </Expandable>
</ParamField>

### Path Parameters

```typescript theme={null}
iii.registerFunction(
  { id: 'api::get_user' },
  async (req: HttpRequest): Promise<ApiResponse> => {
    const userId = req.path_params.id
    
    return {
      status_code: 200,
      body: { id: userId, name: 'John Doe' }
    }
  }
)

iii.registerTrigger({
  type: 'http',
  function_id: 'api::get_user',
  config: {
    api_path: 'users/:id',
    http_method: 'GET'
  }
})

// GET /users/123 → { id: "123", name: "John Doe" }
```

### Query Parameters

```typescript theme={null}
iii.registerFunction(
  { id: 'api::search' },
  async (req: HttpRequest): Promise<ApiResponse> => {
    const query = req.query_params.q as string
    const limit = parseInt(req.query_params.limit as string || '10')
    
    return {
      status_code: 200,
      body: { query, limit, results: [] }
    }
  }
)

iii.registerTrigger({
  type: 'http',
  function_id: 'api::search',
  config: {
    api_path: 'search',
    http_method: 'GET'
  }
})

// GET /search?q=hello&limit=20
```

### Request Body

```typescript theme={null}
iii.registerFunction(
  { id: 'api::create_post' },
  async (req: HttpRequest): Promise<ApiResponse> => {
    const body = req.body as { title: string; content: string }
    
    const post = {
      id: crypto.randomUUID(),
      title: body.title,
      content: body.content,
      created_at: new Date().toISOString()
    }
    
    return {
      status_code: 201,
      body: post
    }
  }
)

iii.registerTrigger({
  type: 'http',
  function_id: 'api::create_post',
  config: {
    api_path: 'posts',
    http_method: 'POST'
  }
})
```

## Streaming HTTP Responses

For streaming responses (SSE, file downloads, etc.), use the `http()` helper:

```typescript theme={null}
import { init, http, type HttpRequest, type HttpResponse } from 'iii-sdk'
import * as fs from 'node:fs'
import { pipeline } from 'node:stream/promises'

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

// File download
iii.registerFunction(
  { id: 'api::download' },
  http(async (req: HttpRequest, response: HttpResponse) => {
    const fileStream = fs.createReadStream('./report.pdf')
    
    response.status(200)
    response.headers({
      'Content-Type': 'application/pdf',
      'Content-Disposition': 'attachment; filename="report.pdf"'
    })
    
    await pipeline(fileStream, response.stream)
  })
)

iii.registerTrigger({
  type: 'http',
  function_id: 'api::download',
  config: {
    api_path: 'download/report',
    http_method: 'GET'
  }
})
```

### Server-Sent Events (SSE)

```typescript theme={null}
iii.registerFunction(
  { id: 'api::events' },
  http(async (req: HttpRequest, response: HttpResponse) => {
    response.status(200)
    response.headers({
      'Content-Type': 'text/event-stream',
      'Cache-Control': 'no-cache',
      'Connection': 'keep-alive'
    })
    
    // Send events
    for (let i = 0; i < 10; i++) {
      const event = `data: ${JSON.stringify({ count: i })}\n\n`
      response.stream.write(Buffer.from(event))
      await new Promise(resolve => setTimeout(resolve, 1000))
    }
    
    response.stream.end()
  })
)

iii.registerTrigger({
  type: 'http',
  function_id: 'api::events',
  config: {
    api_path: 'events',
    http_method: 'GET'
  }
})
```

### Streaming Request Body

```typescript theme={null}
iii.registerFunction(
  { id: 'api::upload' },
  http(async (req: HttpRequest, response: HttpResponse) => {
    const chunks: Buffer[] = []
    
    // Read streaming request body
    for await (const chunk of req.request_body.stream) {
      chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk))
    }
    
    const totalSize = chunks.reduce((sum, buf) => sum + buf.length, 0)
    
    response.status(200)
    response.headers({ 'Content-Type': 'application/json' })
    response.stream.end(
      Buffer.from(JSON.stringify({ uploaded_bytes: totalSize }))
    )
  })
)

iii.registerTrigger({
  type: 'http',
  function_id: 'api::upload',
  config: {
    api_path: 'upload',
    http_method: 'POST'
  }
})
```

## Custom Trigger Types

Create custom trigger types for your own event sources.

### registerTriggerType()

```typescript theme={null}
iii.registerTriggerType<TConfig>(triggerType, handler)
```

<ParamField path="triggerType" type="RegisterTriggerTypeMessage" required>
  Trigger type definition

  <Expandable title="Properties">
    <ParamField path="id" type="string" required>
      Unique trigger type ID
    </ParamField>

    <ParamField path="description" type="string" required>
      Human-readable description
    </ParamField>
  </Expandable>
</ParamField>

<ParamField path="handler" type="TriggerHandler<TConfig>" required>
  Handler for registering/unregistering triggers

  ```typescript theme={null}
  interface TriggerHandler<TConfig> {
    registerTrigger(config: {
      id: string
      function_id: string
      config: TConfig
    }): Promise<void>
    
    unregisterTrigger(config: {
      id: string
      function_id: string
      config: TConfig
    }): Promise<void>
  }
  ```
</ParamField>

### Example: Custom Webhook Trigger

```typescript theme={null}
interface WebhookConfig {
  url: string
  secret: string
}

const webhooks = new Map<string, WebhookConfig>()

iii.registerTriggerType<WebhookConfig>(
  {
    id: 'webhook',
    description: 'Trigger function via webhook'
  },
  {
    async registerTrigger({ id, function_id, config }) {
      webhooks.set(id, config)
      
      // Subscribe to webhook service
      await fetch(config.url, {
        method: 'POST',
        headers: { 'X-Secret': config.secret },
        body: JSON.stringify({
          action: 'subscribe',
          callback: `http://engine:3199/webhooks/${id}`
        })
      })
    },
    
    async unregisterTrigger({ id, config }) {
      const webhook = webhooks.get(id)
      if (webhook) {
        // Unsubscribe from webhook service
        await fetch(config.url, {
          method: 'POST',
          headers: { 'X-Secret': config.secret },
          body: JSON.stringify({ action: 'unsubscribe' })
        })
        
        webhooks.delete(id)
      }
    }
  }
)

// Use the custom trigger type
iii.registerTrigger({
  type: 'webhook',
  function_id: 'handlers::webhook',
  config: {
    url: 'https://webhook-service.com/subscribe',
    secret: process.env.WEBHOOK_SECRET!
  }
})
```

### unregisterTriggerType()

Remove a custom trigger type:

```typescript theme={null}
iii.unregisterTriggerType({
  id: 'webhook',
  description: 'Webhook trigger'
})
```

## Engine Built-in Triggers

The III Engine provides built-in trigger types:

### functions\_available

Triggers when new functions are registered or unregistered.

```typescript theme={null}
iii.onFunctionsAvailable((functions) => {
  console.log('Functions updated:', functions.length)
  functions.forEach(fn => {
    console.log(`- ${fn.function_id}`)
  })
})
```

See [onFunctionsAvailable](#onfunctionsavailable) for details.

## Best Practices

<AccordionGroup>
  <Accordion title="Use descriptive API paths">
    ```typescript theme={null}
    // Good
    api_path: 'users/:id/orders'
    api_path: 'products/search'

    // Avoid
    api_path: 'u/:id/o'
    api_path: 'psearch'
    ```
  </Accordion>

  <Accordion title="Return appropriate status codes">
    ```typescript theme={null}
    // 200 OK - Successful GET/PUT/PATCH
    // 201 Created - Successful POST
    // 204 No Content - Successful DELETE
    // 400 Bad Request - Invalid input
    // 404 Not Found - Resource not found
    // 500 Internal Server Error - Server error

    return {
      status_code: 404,
      body: { error: 'User not found' }
    }
    ```
  </Accordion>

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

    triggers.push(iii.registerTrigger({ /* ... */ }))

    process.on('SIGTERM', () => {
      triggers.forEach(t => t.unregister())
    })
    ```
  </Accordion>
</AccordionGroup>
