# OpenClaw Adapter Modes

This document describes how `@paperclipai/adapter-openclaw` selects request shape and endpoint behavior.

## Transport Modes

The adapter has two transport modes:

- `sse` (default)
- `webhook`

Configured via `adapterConfig.streamTransport` (or legacy `adapterConfig.transport`).

## Mode Matrix

| streamTransport | configured URL path | behavior |
| --- | --- | --- |
| `sse` | `/v1/responses` | Sends OpenResponses request with `stream: true`, expects `text/event-stream` response until terminal event. |
| `sse` | `/hooks/*` | Rejected (`openclaw_sse_incompatible_endpoint`). Hooks are not stream-capable. |
| `sse` | other endpoint | Sends generic streaming payload (`stream: true`, `text`, `paperclip`) and expects SSE response. |
| `webhook` | `/hooks/wake` | Sends wake payload `{ text, mode }`. |
| `webhook` | `/hooks/agent` | Sends agent payload `{ message, ...hook fields }`. |
| `webhook` | `/v1/responses` | Compatibility flow: tries `/hooks/agent` first, then falls back to original `/v1/responses` if hook endpoint returns `404`. |
| `webhook` | other endpoint | Sends legacy generic webhook payload (`stream: false`, `text`, `paperclip`). |

## Webhook Payload Shapes

### 1) Hook Wake (`/hooks/wake`)

Payload:

```json
{
  "text": "Paperclip wake event ...",
  "mode": "now"
}
```

### 2) Hook Agent (`/hooks/agent`)

Payload:

```json
{
  "message": "Paperclip wake event ...",
  "name": "Optional hook name",
  "agentId": "Optional OpenClaw agent id",
  "wakeMode": "now",
  "deliver": true,
  "channel": "last",
  "to": "Optional channel recipient",
  "model": "Optional model override",
  "thinking": "Optional thinking override",
  "timeoutSeconds": 120
}
```

Notes:

- `message` is always used (not `text`) for `/hooks/agent`.
- `sessionKey` is **not** sent by default for `/hooks/agent`.
- To include derived session keys in `/hooks/agent`, set:
  - `hookIncludeSessionKey: true`

### 3) OpenResponses (`/v1/responses`)

When used directly (SSE mode or webhook fallback), payload uses OpenResponses shape:

```json
{
  "stream": false,
  "model": "openclaw",
  "input": "...",
  "metadata": {
    "paperclip_session_key": "paperclip:issue:ISSUE_ID"
  }
}
```

## Auth Header Behavior

You can provide auth either explicitly or via token headers:

- Explicit auth header:
  - `webhookAuthHeader: "Bearer ..."`
- Token headers (adapter derives `Authorization` automatically when missing):
  - `headers["x-openclaw-token"]` (preferred)
  - `headers["x-openclaw-auth"]` (legacy compatibility)

## Session Key Behavior

Session keys are resolved from:

- `sessionKeyStrategy`: `issue` (default), `fixed`, `run`
- `sessionKey`: used when strategy is `fixed` (default value `paperclip`)

Where session keys are applied:

- `/v1/responses`: sent via `x-openclaw-session-key` header + metadata.
- `/hooks/wake`: not sent as a dedicated field.
- `/hooks/agent`: only sent if `hookIncludeSessionKey=true`.
- Generic webhook fallback: sent as `sessionKey` field.

## Recommended Config Examples

### SSE (streaming endpoint)

```json
{
  "url": "http://127.0.0.1:18789/v1/responses",
  "streamTransport": "sse",
  "method": "POST",
  "headers": {
    "x-openclaw-token": "replace-me"
  }
}
```

### Webhook (hooks endpoint)

```json
{
  "url": "http://127.0.0.1:18789/hooks/agent",
  "streamTransport": "webhook",
  "method": "POST",
  "headers": {
    "x-openclaw-token": "replace-me"
  }
}
```

### Webhook with legacy URL retained

If URL is still `/v1/responses` and `streamTransport=webhook`, the adapter will:

1. try `.../hooks/agent`
2. fallback to original `.../v1/responses` when hook endpoint returns `404`

This lets older OpenClaw setups continue working while migrating to hooks.
