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

# Custom APIs

Custom API endpoints let you add your own HTTP endpoints to the Orchestrator. Use custom APIs to build health checks, webhook receivers, token exchange endpoints, or any custom endpoint that needs access to sessions, caches, and other Orchestrator services.

Custom APIs are managed through the **Service Extensions** area in the Console -- they are not an application type.

## Console UI Workflow

**Console UI**

**1. Open Service Extensions**

Navigate to **Service Extensions** in the Console sidebar and select **API**.

**2. Create the API**

Click **Create** and provide a **name** for the API endpoint and a **function name** that matches the Go function in your extension code (e.g., `ServeAPI`).

**3. Write the extension code**

Use the built-in code editor to write your `serveSE` handler, or upload a `.go` source file. The editor provides syntax highlighting and access to the service extension SDK.

**4. Attach to a deployment**

Go to the deployment's **Settings** page. In the **APIs** section, attach the API you created. This includes the API in the deployment's configuration bundle.

**5. Publish**

Publish the deployment to push the updated configuration bundle to your Orchestrator instances.

**Configuration**

Custom API endpoints are defined under the `apis` top-level key in the YAML configuration. See the [Configuration](#configuration) section below.

## Request Lifecycle

Unlike other hooks that run during request processing, `serveSE` runs once when the Orchestrator starts up. Your extension registers HTTP route handlers that then execute whenever a matching request arrives.

```mermaid theme={null}
%%{init: {'theme': 'neutral', 'themeVariables': {'edgeWidth': 4}}}%%
flowchart TD
    START[Orchestrator starts] --> PARSE[Load API configuration]
    PARSE --> SE[serveSE — register custom endpoints]
    SE -- error --> FAIL[Log startup error]
    SE -- success --> READY[Endpoints ready to receive traffic]
    READY --> REQ[Request arrives at custom endpoint]
    REQ --> HANDLER[Your handler processes the request]
    HANDLER --> RESP[Return response]

    classDef hook stroke:#6A3EC8,stroke-width:6px
    class SE hook
```

## Hooks

### `serveSE`

Register custom HTTP endpoints on the Orchestrator. This hook runs at startup and gives you access to the router to define your own API routes. Use this to implement custom REST endpoints, webhook receivers, or internal service APIs.

**Signature:**

```go theme={null}
func ServeAPI(api orchestrator.Orchestrator) error
```

**App types:** Top-level `apis[]`

**Config location:** `apis[].serveSE`

**Parameters:**

| Parameter | Type | Description |
| - | - | - |
| `api` | `orchestrator.Orchestrator` | Access to sessions, caches, secrets, logging, router, and other Orchestrator services |

**Returns:** `error` -- return `nil` on success, or an error if the API handler setup fails.

**Examples:**

> **Forward incoming webhooks to an external service**
>
> When you need to receive webhooks at the Orchestrator and relay them to an
> external system — such as a SIEM, logging platform, or notification
> service — this extension registers a `/webhooks/forward` endpoint that
> accepts incoming requests and forwards them using a reusable HTTP client.
>
> **Extension**
>
> ```go webhook-forwarder.go theme={null}
> package main
>
> import (
>     "fmt"
>     "io"
>     "net/http"
>     "time"
>
>     "github.com/strata-io/service-extension/orchestrator"
> )
>
> func ServeAPI(api orchestrator.Orchestrator) error {
>     logger := api.Logger()
>
>     // Create a reusable HTTP client at startup. Reusing clients pools
>     // TCP connections and prevents resource exhaustion under load.
>     apiHTTP := api.HTTP()
>     err := apiHTTP.SetClient("forwarder", &http.Client{
>         Timeout: 10 * time.Second,
>     })
>     if err != nil {
>         return fmt.Errorf("failed to create HTTP client: %w", err)
>     }
>
>     // Read the destination URL from SE metadata.
>     metadata := api.Metadata()
>     targetURL := metadata["targetURL"].(string)
>
>     // Read the auth token from the secret provider.
>     sp := api.SecretProvider()
>     authToken, err := sp.GetString("webhook-auth-token")
>     if err != nil {
>         return fmt.Errorf("failed to get auth token: %w", err)
>     }
>
>     router := api.Router()
>     router.HandleFunc("/webhooks/forward", func(rw http.ResponseWriter, req *http.Request) {
>         client, _ := apiHTTP.GetClient("forwarder")
>
>         fwd, err := http.NewRequest(req.Method, targetURL, req.Body)
>         if err != nil {
>             logger.Error("se", "failed to create forward request", "error", err.Error())
>             http.Error(rw, "internal error", http.StatusInternalServerError)
>             return
>         }
>         fwd.Header.Set("Content-Type", req.Header.Get("Content-Type"))
>         fwd.Header.Set("Authorization", "Bearer "+authToken)
>
>         resp, err := client.Do(fwd)
>         if err != nil {
>             logger.Error("se", "failed to forward webhook", "error", err.Error())
>             http.Error(rw, "forwarding failed", http.StatusBadGateway)
>             return
>         }
>         defer resp.Body.Close()
>
>         rw.WriteHeader(resp.StatusCode)
>         io.Copy(rw, resp.Body)
>     })
>
>     logger.Info("se", "registered /webhooks/forward endpoint")
>     return nil
> }
> ```
>
> **Configuration**
>
> ```yaml maverics.yaml theme={null}
> apis:
>   - name: webhook-forwarder
>     serveSE:
>       funcName: ServeAPI
>       file: /etc/maverics/extensions/webhook-forwarder.go
>       metadata:
>         targetURL: https://siem.example.com/api/events
> ```

## Configuration

Custom API endpoints are defined under the `apis` top-level key in the deployment configuration.

```yaml maverics.yaml theme={null}
apis:
  - name: my-custom-endpoint
    serveSE:
      funcName: ServeAPI
      file: /path/to/api-handler.go
```

### Field Reference

| Key | Type | Default | Required | Description |
| - | - | - | - | - |
| `apis[].name` | string | -- | Yes | Unique name for the API endpoint |
| `apis[].serveSE` | ServiceExtension | -- | Yes | Service extension that handles requests to this endpoint |

The `serveSE` field accepts the standard [ServiceExtension](/reference/orchestrator/service-extensions#field-reference) object (`funcName`, `code`/`file`, `metadata`, `goPath`, `allowedProtectedPackages`).

### Validation Rules

* Each `name` must be unique across all `apis[]` entries
* The `serveSE` field is required -- an API endpoint without a handler is invalid

### Example

A custom health check endpoint and a user info endpoint:

```yaml maverics.yaml theme={null}
apis:
  - name: custom-health
    serveSE:
      funcName: HealthCheck
      file: /etc/maverics/extensions/health-check.go
      metadata:
        backendURL: "https://backend.example.com/health"

  - name: user-info
    serveSE:
      funcName: GetUserInfo
      file: /etc/maverics/extensions/user-info.go
```

Multiple custom APIs can be defined, each with its own service extension handler.

## Related Pages

> **[Service Extensions Overview](/reference/orchestrator/service-extensions)**
>
> Configuration, SDK reference, and best practices

> **[Applications](/reference/orchestrator/applications)**
>
> Application configuration for proxy, OIDC, and SAML apps
