Skip to main content

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 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.
const trigger = iii.registerTrigger(config)
config
RegisterTriggerInput
required
Trigger configuration
trigger
Trigger
Trigger reference with unregister method

HTTP Triggers

Expose functions as HTTP endpoints.

Basic HTTP Endpoint

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

HttpRequest
object
HTTP request object passed to function handlers
ApiResponse
object
JSON response format

Path Parameters

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

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

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:
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)

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

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()

iii.registerTriggerType<TConfig>(triggerType, handler)
triggerType
RegisterTriggerTypeMessage
required
Trigger type definition
handler
TriggerHandler<TConfig>
required
Handler for registering/unregistering triggers
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>
}

Example: Custom Webhook Trigger

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:
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.
iii.onFunctionsAvailable((functions) => {
  console.log('Functions updated:', functions.length)
  functions.forEach(fn => {
    console.log(`- ${fn.function_id}`)
  })
})
See onFunctionsAvailable for details.

Best Practices

// Good
api_path: 'users/:id/orders'
api_path: 'products/search'

// Avoid
api_path: 'u/:id/o'
api_path: 'psearch'
// 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' }
}
const triggers: Trigger[] = []

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

process.on('SIGTERM', () => {
  triggers.forEach(t => t.unregister())
})

Build docs developers (and LLMs) love