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

<Tabs>
  <Tab title="Console UI">
    <Steps>
      <Step title="Open Service Extensions">
        Navigate to **Service Extensions** in the Console sidebar and select **API**.
      </Step>

      <Step title="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`).
      </Step>

      <Step title="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.
      </Step>

      <Step title="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.
      </Step>

      <Step title="Publish">
        Publish the deployment to push the updated configuration bundle to your Orchestrator instances.
      </Step>
    </Steps>
  </Tab>

  <Tab title="Configuration">
    Custom API endpoints are defined under the `apis` top-level key in the YAML configuration. See the [Configuration](#configuration) section below.
  </Tab>
</Tabs>

## 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:**

<AccordionGroup>
  <Accordion title="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.

    <Tabs>
      <Tab title="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
        }
        ```
      </Tab>

      <Tab title="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
        ```
      </Tab>
    </Tabs>
  </Accordion>
</AccordionGroup>

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

<CardGroup cols={2}>
  <Card title="Service Extensions Overview" icon="code" href="/reference/orchestrator/service-extensions">
    Configuration, SDK reference, and best practices
  </Card>

  <Card title="Applications" icon="browser" href="/reference/orchestrator/applications">
    Application configuration for proxy, OIDC, and SAML apps
  </Card>
</CardGroup>
