# Third-Party API Integration

Entaura's third-party API is available under `/api/v1`. In Docker Compose mode,
third-party requests use short-lived OAuth 2.0 bearer tokens issued by
Keycloak. The browser UI uses Entaura's existing session login separately.

## Start Keycloak

Development Compose starts Entaura, Keycloak, and Keycloak's PostgreSQL database.
The default development Keycloak URL is `http://127.0.0.1:8086`. If that port
is already in use, set `KEYCLOAK_PORT` and `KEYCLOAK_PUBLIC_URL` to the same
available host port in `.env.development`.

Start only the identity services:

```bash
docker compose --env-file .env.development -f docker-compose.dev.yml \
  up -d keycloak-db keycloak
```

Start the complete development stack:

```bash
docker compose --env-file .env.development -f docker-compose.dev.yml \
  up -d --build
```

Open the Keycloak administration console at:

```text
http://127.0.0.1:8086/admin/
```

The administrator username and password are `KEYCLOAK_ADMIN_USERNAME` and
`KEYCLOAK_ADMIN_PASSWORD`. Never commit `.env.development` or production
credentials.

Production should expose Keycloak through a separate HTTPS hostname, such as
`https://auth.example.com`, and set `KEYCLOAK_PUBLIC_URL` and

## Create an integration

### HTTPS reverse proxy setup

For the hosted development environment, set this in the server's
`.env.development` (production uses `.env.production`):

```env
KEYCLOAK_PUBLIC_URL=https://auth.dev.entaura.ai
```

Both Compose stacks pass this full URL to Keycloak as `KC_HOSTNAME`. This
keeps the advertised issuer, login, cookie-check, and token URLs on HTTPS.
Entaura continues using `http://keycloak:8080` for internal requests.

In the existing Nginx HTTPS server block for `auth.dev.entaura.ai`, configure
the proxy location below. Match the upstream port to `KEYCLOAK_PORT` on that
server; use `8085` if that is the existing published port.

```nginx
location / {
    proxy_pass http://127.0.0.1:8085;
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-Port $server_port;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
```

After deploying the Compose changes and updating the environment, recreate
Keycloak and Entaura without recreating the databases:

```bash
docker compose --env-file .env.development -f docker-compose.dev.yml \
  up -d --no-deps --force-recreate keycloak app
sudo nginx -t && sudo systemctl reload nginx
```

Verify the public issuer and endpoints:

```bash
curl -fsS https://auth.dev.entaura.ai/realms/master/.well-known/openid-configuration \
  | jq '{issuer, authorization_endpoint, token_endpoint, jwks_uri}'
```

All four URLs must start with `https://auth.dev.entaura.ai`. Repeat for the
`entaura` realm. If a realm's **Frontend URL** explicitly contains HTTP,
clear that override or change it to the correct HTTPS URL. Reopen the admin
console over HTTPS and request new integration tokens after changing the issuer.

Workspace owners and administrators can use **Developer integrations** in the
Entaura UI. The screen creates a Keycloak confidential client, assigns Entaura
workspace permissions, rotates its secret, and enables or disables access.

The browser management endpoints are also available. They require an Entaura
browser session, not a Keycloak access token:

```text
POST   /workspaces/{workspace_id}/integrations
GET    /workspaces/{workspace_id}/integrations
PATCH  /workspaces/{workspace_id}/integrations/{integration_id}
POST   /workspaces/{workspace_id}/integrations/{integration_id}/rotate-secret
DELETE /workspaces/{workspace_id}/integrations/{integration_id}
```

Example creation request:

```bash
curl -b cookies.txt -X POST "$ENTAURA_URL/workspaces/$WORKSPACE_ID/integrations" \
  -H 'Content-Type: application/json' \
  -d '{"name":"ERP integration","permissions":["read","monitors:write","channels:write"]}'
```

The response contains `client_id`, `client_secret`, and `token_url`. The
client secret is returned only when the integration is created or rotated.
Store it in a secret manager. Entaura stores the Keycloak client ID, workspace
mapping, and permissions; Keycloak stores the client credential.

To disable or re-enable an integration:

```bash
curl -X PATCH "$ENTAURA_URL/workspaces/$WORKSPACE_ID/integrations/$INTEGRATION_ID" \
  -b cookies.txt -H 'Content-Type: application/json' \
  -d '{"enabled":false}'
```

## Get an access token

Use the integration's client credentials with the Keycloak client-credentials
grant. `KEYCLOAK_URL` must be the public Keycloak URL reachable by the client.

```bash
export ENTAURA_URL=http://127.0.0.1:8000
export KEYCLOAK_URL=http://127.0.0.1:8086
export KEYCLOAK_REALM=entaura
export CLIENT_ID=replace-me
export CLIENT_SECRET=replace-me

TOKEN_RESPONSE=$(curl -sS -X POST \
  "$KEYCLOAK_URL/realms/$KEYCLOAK_REALM/protocol/openid-connect/token" \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  --data-urlencode 'grant_type=client_credentials' \
  --data-urlencode "client_id=$CLIENT_ID" \
  --data-urlencode "client_secret=$CLIENT_SECRET")
export ACCESS_TOKEN=$(printf '%s' "$TOKEN_RESPONSE" | jq -r .access_token)
```

Send the token to Entaura:

```http
Authorization: Bearer <access_token>
```

Access tokens expire. The integration must request a new token when necessary;
it must not ask users to paste an access token manually.

## Permissions

Supported integration permissions are:

```text
read, write
connectors:read, connectors:write
datasets:read, datasets:write
monitors:read, monitors:write
alerts:read, alerts:write
channels:read, channels:write
```

`read` grants read access across resources. `write` grants all API operations.
A resource `:write` permission also permits reading that resource. Every API
request is restricted to the workspace assigned to the Keycloak integration.
Disabled integrations and invalid, expired, or incorrectly issued tokens are
rejected.

## API resources

| Method | Endpoint | Permission |
| --- | --- | --- |
| GET | `/api/v1/workspace` | `read` |
| GET, POST | `/api/v1/connectors` | `connectors:read/write` |
| POST | `/api/v1/connectors/{id}/streams` | `connectors:write` |
| POST | `/api/v1/connectors/{id}/sync` | `connectors:write` |
| GET | `/api/v1/connectors/{id}/jobs/{job_id}` | `connectors:read` |
| GET, POST | `/api/v1/datasets` | `datasets:read/write` |
| GET, POST | `/api/v1/monitors` | `monitors:read/write` |
| GET | `/api/v1/monitors/{id}/runs?limit=50` | `monitors:read` |
| GET | `/api/v1/alerts?limit=50` | `alerts:read` |
| GET, POST | `/api/v1/channels` | `channels:read/write` |

Connector creation creates a workspace-owned draft. Stream configuration and
sync use the configured Airbyte connection. Connector creation does not expose
raw credentials in its response. Notification-channel credentials are stored
encrypted and are never returned by the API.

## Direct monitor creation

Use direct creation when the external application already has a complete rule:

```bash
curl -X POST "$ENTAURA_URL/api/v1/monitors" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"name":"Invoice alert","record_key":"invoices","instruction":"Notify me when overdue_count is greater than zero.","interval_seconds":300}'
```

Monitor execution is performed by the Entaura scheduler. The first run creates
a baseline; later qualifying changes create alerts.

## Conversational monitor building

Use the monitor-building API when the external application needs the same
question-and-answer flow as the Entaura UI. The session is persisted and bound
to the creating Keycloak integration and workspace.

### Start a session

```bash
SESSION_RESPONSE=$(curl -sS -X POST \
  "$ENTAURA_URL/api/v1/monitor-building/sessions" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"connector_id":12,"name":"High-value orders","instruction":"Alert me when a high-value order is added","interval_seconds":300}')
export SESSION_ID=$(printf '%s' "$SESSION_RESPONSE" | jq -r .session_id)
```

If information is missing, the response has:

```json
{
  "status": "needs_clarification",
  "question": "What order amount should count as high-value?",
  "session_id": "..."
}
```

Send the answer:

```bash
curl -X POST "$ENTAURA_URL/api/v1/monitor-building/sessions/$SESSION_ID/messages" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"message":"Orders above 10000"}'
```

Continue until the response has `status: "ready"`. The response includes a
`rule` preview. Show that preview to the user, then explicitly activate it:

```bash
curl -X POST "$ENTAURA_URL/api/v1/monitor-building/sessions/$SESSION_ID/activate" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

Activation returns the created `monitor_id`. Activation is idempotent after a
monitor has been created. A pending clarification cannot be activated, and
another integration cannot read or modify the session.

## Errors and operational guidance

- `400`: malformed request or invalid input.
- `401`: missing, invalid, expired, or disabled authentication.
- `403`: valid integration without the required permission or workspace access.
- `404`: resource does not exist in the integration's workspace.
- `409`: conflicting operation, such as answering an already-ready session.
- `422`: valid JSON with a business validation error.
- `502`: Keycloak, Airbyte, or AI provider dependency failure.

Use one Keycloak client per integration and grant the smallest permission set.
Rotate secrets from **Developer integrations**, deploy the replacement, then
discard the old secret. Never put client secrets or access tokens in source
control or browser JavaScript.

The importable collection at
`postman/entaura-watch-api.json` includes the token request, resource requests,
and conversational monitor-building requests. Set its `keycloak_url` to the
actual Keycloak public port when it differs from the development default.
