# Proxy Hooks

**Page:** api/proxy-hooks

[Download Raw Markdown](./api/proxy-hooks.md)

---

{/* AUTO-GENERATED — Do not edit manually. Regenerate with: npm run docs:api:generate */}



Proxy Hooks let you attach MITM-style intercept scripts to specific paths on a container service. Hooks are evaluated per-service in `position` order, first-match-wins. All mutating operations are ETag-gated via the `If-Match: file:v` header to prevent lost updates.

## List all hooks for a container

`GET /api/v1/containers/{id}/proxy/hooks`

Returns every hook for the container grouped by service, alongside the current `file_version` and ETag.

### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `id` | path | string | Yes | Container ID |

### Response



```json
{
  "statusCode": 200,
  "message": "Proxy hooks listed successfully",
  "data": {
    "hooks": {
      "auth": [
        {
          "id": "01hz8x9k2b3c4d5e6f7g8h9j0k",
          "position": 0,
          "match": {
            "method": "POST",
            "path": "/v1/login",
            "headers": {
              "x-tenant": "acme"
            }
          },
          "script": {
            "subdomain": "hooks",
            "execId": "exec_01hz8x9k2b3c4d5e6f7g8h9j0k",
            "path": "/hooks/login-trace.js"
          },
          "timeout": 5000,
          "applies_to": {
            "groups": ["admins", "sre"]
          }
        }
      ],
      "billing": []
    },
    "file_version": 42,
    "etag": "file:v42"
  }
}
```


```json
{
  "statusCode": 404,
  "error": "Not Found",
  "message": "Resource not found"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `NOT_FOUND` | Hook or service not found | The hook id, service, or container does not exist, or the service is reject-listed | Verify the service name is not reject-listed (logs, proxy, workspaces) and that the hook id exists |
| `VALIDATION_ERROR` | Validation error | Request body violates hook schema, caps, or referential integrity | Check error details and correct the body shape; ensure applies_to.groups references defined groups |
| `PRECONDITION_REQUIRED` | If-Match required | Destructive writes require an If-Match: file:v&lt;N&gt; header | Fetch the resource first to obtain the ETag and resend with If-Match |
| `PRECONDITION_FAILED` | ETag mismatch | The If-Match header does not match the current file_version | Re-fetch the resource to get the current ETag and retry |



### SDK

```ts
const { data } = await client.api.proxyHooks.listContainerProxyHooks({
  id: "container_01hz8x9k2b3c4d5e6f7g8h9j0k",
});
```

---

## List hooks for a service

`GET /api/v1/containers/{id}/proxy/hooks/{service}`

Returns the ordered hook array for a single service. Within the list, evaluation is first-match-wins by `position` order.

### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `id` | path | string | Yes | Container ID |
| `service` | path | string | Yes | Service name |

### Response



```json
{
  "statusCode": 200,
  "message": "Service hooks listed successfully",
  "data": {
    "service": "auth",
    "hooks": [
      {
        "id": "01hz8x9k2b3c4d5e6f7g8h9j0k",
        "position": 0,
        "match": {
          "method": ["POST", "PUT"],
          "path": "/v1/login",
          "headers": {
            "x-tenant": "acme"
          }
        },
        "script": {
          "subdomain": "hooks",
          "execId": "exec_01hz8x9k2b3c4d5e6f7g8h9j0k",
          "path": "/hooks/login-trace.js"
        },
        "timeout": 5000,
        "applies_to": {
          "groups": ["admins", "sre"]
        }
      }
    ],
    "file_version": 42,
    "etag": "file:v42"
  }
}
```


```json
{
  "statusCode": 404,
  "error": "Not Found",
  "message": "Resource not found"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `NOT_FOUND` | Hook or service not found | The hook id, service, or container does not exist, or the service is reject-listed | Verify the service name is not reject-listed (logs, proxy, workspaces) and that the hook id exists |
| `VALIDATION_ERROR` | Validation error | Request body violates hook schema, caps, or referential integrity | Check error details and correct the body shape; ensure applies_to.groups references defined groups |
| `PRECONDITION_REQUIRED` | If-Match required | Destructive writes require an If-Match: file:v&lt;N&gt; header | Fetch the resource first to obtain the ETag and resend with If-Match |
| `PRECONDITION_FAILED` | ETag mismatch | The If-Match header does not match the current file_version | Re-fetch the resource to get the current ETag and retry |



### SDK

```ts
const { data } = await client.api.proxyHooks.listContainerProxyServiceHooks({
  id: "container_01hz8x9k2b3c4d5e6f7g8h9j0k",
  service: "auth",
});
```

---

## Get a single hook

`GET /api/v1/containers/{id}/proxy/hooks/{service}/{hookId}`

Returns the hook identified by `hookId` under the given service.

### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `id` | path | string | Yes | Container ID |
| `service` | path | string | Yes | Service name |
| `hookId` | path | string | Yes | 26-char Crockford base32 ULID (lowercase) |

### Response



```json
{
  "statusCode": 200,
  "message": "Hook retrieved successfully",
  "data": {
    "hook": {
      "id": "01hz8x9k2b3c4d5e6f7g8h9j0k",
      "position": 0,
      "match": {
        "method": "*",
        "path": "/v1/login",
        "headers": {
          "x-tenant": "acme"
        }
      },
      "script": {
        "subdomain": "hooks",
        "execId": "exec_01hz8x9k2b3c4d5e6f7g8h9j0k",
        "path": "/hooks/login-trace.js"
      },
      "timeout": 5000,
      "applies_to": {
        "groups": ["admins"]
      }
    },
    "file_version": 42,
    "etag": "file:v42"
  }
}
```


```json
{
  "statusCode": 404,
  "error": "Not Found",
  "message": "Resource not found"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `NOT_FOUND` | Hook or service not found | The hook id, service, or container does not exist, or the service is reject-listed | Verify the service name is not reject-listed (logs, proxy, workspaces) and that the hook id exists |
| `VALIDATION_ERROR` | Validation error | Request body violates hook schema, caps, or referential integrity | Check error details and correct the body shape; ensure applies_to.groups references defined groups |
| `PRECONDITION_REQUIRED` | If-Match required | Destructive writes require an If-Match: file:v&lt;N&gt; header | Fetch the resource first to obtain the ETag and resend with If-Match |
| `PRECONDITION_FAILED` | ETag mismatch | The If-Match header does not match the current file_version | Re-fetch the resource to get the current ETag and retry |



### SDK

```ts
const { data } = await client.api.proxyHooks.getContainerProxyHook({
  id: "container_01hz8x9k2b3c4d5e6f7g8h9j0k",
  service: "auth",
  hookId: "01hz8x9k2b3c4d5e6f7g8h9j0k",
});
```

---

## Append or insert a new hook

`POST /api/v1/containers/{id}/proxy/hooks/{service}`

Creates a new hook under the given service. Omit `position` to append; supply a 0-indexed `position` to insert. Requires `If-Match: file:v`.

### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `id` | path | string | Yes | Container ID |
| `service` | path | string | Yes | Service name |
| `if-match` | header | string | No | file:v&lt;N&gt; ETag precondition |

### Request Body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `match` | object | Yes | Request matcher. Contains `method` (string or array of strings from `*`, `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `HEAD`, `OPTIONS`), `path` (string, max 257 chars), and `headers` (object of string values). |
| `script` | object | Yes | Script reference. `path` is required; optionally `subdomain` and `execId`. |
| `timeout` | integer | No | Execution timeout in ms (1–30000). |
| `applies_to` | object | No | Restriction object. `groups` is an array of group name strings (min 1). |
| `position` | integer | No | 0-indexed insertion position (POST only). |

```json
{
  "match": {
    "method": "POST",
    "path": "/v1/login",
    "headers": {
      "x-tenant": "acme"
    }
  },
  "script": {
    "subdomain": "hooks",
    "execId": "exec_01hz8x9k2b3c4d5e6f7g8h9j0k",
    "path": "/hooks/login-trace.js"
  },
  "timeout": 5000,
  "applies_to": {
    "groups": ["admins"]
  }
}
```

### Response



```json
{
  "statusCode": 201,
  "message": "Hook created successfully",
  "data": {
    "hook": {
      "id": "01hz8x9k2b3c4d5e6f7g8h9j0k",
      "position": 0,
      "match": {
        "method": "POST",
        "path": "/v1/login",
        "headers": {
          "x-tenant": "acme"
        }
      },
      "script": {
        "subdomain": "hooks",
        "execId": "exec_01hz8x9k2b3c4d5e6f7g8h9j0k",
        "path": "/hooks/login-trace.js"
      },
      "timeout": 5000,
      "applies_to": {
        "groups": ["admins"]
      }
    },
    "file_version": 43,
    "etag": "file:v43"
  }
}
```


```json
{
  "statusCode": 404,
  "error": "Not Found",
  "message": "Resource not found"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `NOT_FOUND` | Hook or service not found | The hook id, service, or container does not exist, or the service is reject-listed | Verify the service name is not reject-listed (logs, proxy, workspaces) and that the hook id exists |
| `VALIDATION_ERROR` | Validation error | Request body violates hook schema, caps, or referential integrity | Check error details and correct the body shape; ensure applies_to.groups references defined groups |
| `PRECONDITION_REQUIRED` | If-Match required | Destructive writes require an If-Match: file:v&lt;N&gt; header | Fetch the resource first to obtain the ETag and resend with If-Match |
| `PRECONDITION_FAILED` | ETag mismatch | The If-Match header does not match the current file_version | Re-fetch the resource to get the current ETag and retry |


```json
{
  "statusCode": 412,
  "error": "Precondition Failed",
  "message": "etag_mismatch"
}
```



```json
{
  "statusCode": 422,
  "error": "Validation Error",
  "message": "Invalid hook"
}
```



```json
{
  "statusCode": 428,
  "error": "Precondition Required",
  "message": "If-Match header required for this operation"
}
```




### SDK

```ts
const { data } = await client.api.proxyHooks.addContainerProxyHook({
  id: "container_01hz8x9k2b3c4d5e6f7g8h9j0k",
  service: "auth",
  "if-match": "file:v42",
  data: {
    match: { method: "POST", path: "/v1/login" },
    script: { path: "/hooks/login-trace.js" },
    timeout: 5000,
  },
});
```

---

## Replace a hook in place

`PATCH /api/v1/containers/{id}/proxy/hooks/{service}/{hookId}`

Full-replaces the hook at the given id, preserving its `id` and `position`. Requires `If-Match`.

### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `id` | path | string | Yes | Container ID |
| `service` | path | string | Yes | Service name |
| `hookId` | path | string | Yes | 26-char Crockford base32 ULID (lowercase) |
| `if-match` | header | string | No | file:v&lt;N&gt; ETag precondition |

### Request Body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `match` | object | Yes | Request matcher. Contains `method` (string or array of strings from `*`, `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `HEAD`, `OPTIONS`), `path` (string, max 257 chars), and `headers` (object of string values). |
| `script` | object | Yes | Script reference. `path` is required; optionally `subdomain` and `execId`. |
| `timeout` | integer | No | Execution timeout in ms (1–30000). |
| `applies_to` | object | No | Restriction object. `groups` is an array of group name strings (min 1). |
| `position` | integer | No | 0-indexed insertion position (POST only). |

```json
{
  "match": {
    "method": "POST",
    "path": "/v1/login",
    "headers": {
      "x-tenant": "acme"
    }
  },
  "script": {
    "subdomain": "hooks",
    "execId": "exec_01hz8x9k2b3c4d5e6f7g8h9j0k",
    "path": "/hooks/login-trace.js"
  },
  "timeout": 5000,
  "applies_to": {
    "groups": ["admins"]
  }
}
```

### Response



```json
{
  "statusCode": 200,
  "message": "Hook updated successfully",
  "data": {
    "hook": {
      "id": "01hz8x9k2b3c4d5e6f7g8h9j0k",
      "position": 0,
      "match": {
        "method": "POST",
        "path": "/v1/login",
        "headers": {
          "x-tenant": "acme"
        }
      },
      "script": {
        "subdomain": "hooks",
        "execId": "exec_01hz8x9k2b3c4d5e6f7g8h9j0k",
        "path": "/hooks/login-trace.js"
      },
      "timeout": 5000,
      "applies_to": {
        "groups": ["admins"]
      }
    },
    "file_version": 43,
    "etag": "file:v43"
  }
}
```


```json
{
  "statusCode": 404,
  "error": "Not Found",
  "message": "Resource not found"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `NOT_FOUND` | Hook or service not found | The hook id, service, or container does not exist, or the service is reject-listed | Verify the service name is not reject-listed (logs, proxy, workspaces) and that the hook id exists |
| `VALIDATION_ERROR` | Validation error | Request body violates hook schema, caps, or referential integrity | Check error details and correct the body shape; ensure applies_to.groups references defined groups |
| `PRECONDITION_REQUIRED` | If-Match required | Destructive writes require an If-Match: file:v&lt;N&gt; header | Fetch the resource first to obtain the ETag and resend with If-Match |
| `PRECONDITION_FAILED` | ETag mismatch | The If-Match header does not match the current file_version | Re-fetch the resource to get the current ETag and retry |


```json
{
  "statusCode": 412,
  "error": "Precondition Failed",
  "message": "etag_mismatch"
}
```



```json
{
  "statusCode": 422,
  "error": "Validation Error",
  "message": "Invalid hook"
}
```



```json
{
  "statusCode": 428,
  "error": "Precondition Required",
  "message": "If-Match header required for this operation"
}
```




### SDK

```ts
const { data } = await client.api.proxyHooks.updateContainerProxyHook({
  id: "container_01hz8x9k2b3c4d5e6f7g8h9j0k",
  service: "auth",
  hookId: "01hz8x9k2b3c4d5e6f7g8h9j0k",
  "if-match": "file:v42",
  data: {
    match: { method: "POST", path: "/v1/login" },
    script: { path: "/hooks/login-trace.js" },
    timeout: 5000,
  },
});
```

---

## Move a hook to a new position

`PATCH /api/v1/containers/{id}/proxy/hooks/{service}/{hookId}/position`

Atomically reorders a single hook. Body must contain the target `position`. Requires `If-Match`.

### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `id` | path | string | Yes | Container ID |
| `service` | path | string | Yes | Service name |
| `hookId` | path | string | Yes | 26-char Crockford base32 ULID (lowercase) |
| `if-match` | header | string | No | file:v&lt;N&gt; ETag precondition |

### Request Body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `position` | integer | Yes | 0-indexed target position. |

```json
{
  "position": 2
}
```

### Response



```json
{
  "statusCode": 200,
  "message": "Hook moved successfully",
  "data": {
    "hook": {
      "id": "01hz8x9k2b3c4d5e6f7g8h9j0k",
      "position": 2,
      "match": {
        "method": "POST",
        "path": "/v1/login",
        "headers": {
          "x-tenant": "acme"
        }
      },
      "script": {
        "subdomain": "hooks",
        "execId": "exec_01hz8x9k2b3c4d5e6f7g8h9j0k",
        "path": "/hooks/login-trace.js"
      },
      "timeout": 5000,
      "applies_to": {
        "groups": ["admins", "sre"]
      }
    },
    "file_version": 43,
    "etag": "file:v43"
  }
}
```


```json
{
  "statusCode": 404,
  "error": "Not Found",
  "message": "Resource not found"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `NOT_FOUND` | Hook or service not found | The hook id, service, or container does not exist, or the service is reject-listed | Verify the service name is not reject-listed (logs, proxy, workspaces) and that the hook id exists |
| `VALIDATION_ERROR` | Validation error | Request body violates hook schema, caps, or referential integrity | Check error details and correct the body shape; ensure applies_to.groups references defined groups |
| `PRECONDITION_REQUIRED` | If-Match required | Destructive writes require an If-Match: file:v&lt;N&gt; header | Fetch the resource first to obtain the ETag and resend with If-Match |
| `PRECONDITION_FAILED` | ETag mismatch | The If-Match header does not match the current file_version | Re-fetch the resource to get the current ETag and retry |


```json
{
  "statusCode": 412,
  "error": "Precondition Failed",
  "message": "etag_mismatch"
}
```



```json
{
  "statusCode": 428,
  "error": "Precondition Required",
  "message": "If-Match header required for this operation"
}
```




### SDK

```ts
const { data } = await client.api.proxyHooks.moveContainerProxyHook({
  id: "container_01hz8x9k2b3c4d5e6f7g8h9j0k",
  service: "auth",
  hookId: "01hz8x9k2b3c4d5e6f7g8h9j0k",
  "if-match": "file:v42",
  data: { position: 2 },
});
```

---

## Clear all hooks for a service

`DELETE /api/v1/containers/{id}/proxy/hooks/{service}`

Removes every hook under the given service. Requires `If-Match`.

### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `id` | path | string | Yes | Container ID |
| `service` | path | string | Yes | Service name |
| `if-match` | header | string | No | file:v&lt;N&gt; ETag precondition |

### Response



```json
{
  "statusCode": 200,
  "message": "Service hooks cleared",
  "data": {
    "removed": 3,
    "file_version": 44,
    "etag": "file:v44"
  }
}
```


```json
{
  "statusCode": 404,
  "error": "Not Found",
  "message": "Resource not found"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `NOT_FOUND` | Hook or service not found | The hook id, service, or container does not exist, or the service is reject-listed | Verify the service name is not reject-listed (logs, proxy, workspaces) and that the hook id exists |
| `VALIDATION_ERROR` | Validation error | Request body violates hook schema, caps, or referential integrity | Check error details and correct the body shape; ensure applies_to.groups references defined groups |
| `PRECONDITION_REQUIRED` | If-Match required | Destructive writes require an If-Match: file:v&lt;N&gt; header | Fetch the resource first to obtain the ETag and resend with If-Match |
| `PRECONDITION_FAILED` | ETag mismatch | The If-Match header does not match the current file_version | Re-fetch the resource to get the current ETag and retry |


```json
{
  "statusCode": 412,
  "error": "Precondition Failed",
  "message": "etag_mismatch"
}
```



```json
{
  "statusCode": 428,
  "error": "Precondition Required",
  "message": "If-Match header required for this operation"
}
```




### SDK

```ts
const { data } = await client.api.proxyHooks.clearContainerProxyServiceHooks({
  id: "container_01hz8x9k2b3c4d5e6f7g8h9j0k",
  service: "auth",
  "if-match": "file:v42",
});
```

---

## Remove a single hook

`DELETE /api/v1/containers/{id}/proxy/hooks/{service}/{hookId}`

Deletes a single hook by id. Requires `If-Match`.

### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `id` | path | string | Yes | Container ID |
| `service` | path | string | Yes | Service name |
| `hookId` | path | string | Yes | 26-char Crockford base32 ULID (lowercase) |
| `if-match` | header | string | No | file:v&lt;N&gt; ETag precondition |

### Response



```json
{
  "statusCode": 200,
  "message": "Hook removed successfully",
  "data": {
    "file_version": 44,
    "etag": "file:v44"
  }
}
```


```json
{
  "statusCode": 404,
  "error": "Not Found",
  "message": "Resource not found"
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `NOT_FOUND` | Hook or service not found | The hook id, service, or container does not exist, or the service is reject-listed | Verify the service name is not reject-listed (logs, proxy, workspaces) and that the hook id exists |
| `VALIDATION_ERROR` | Validation error | Request body violates hook schema, caps, or referential integrity | Check error details and correct the body shape; ensure applies_to.groups references defined groups |
| `PRECONDITION_REQUIRED` | If-Match required | Destructive writes require an If-Match: file:v&lt;N&gt; header | Fetch the resource first to obtain the ETag and resend with If-Match |
| `PRECONDITION_FAILED` | ETag mismatch | The If-Match header does not match the current file_version | Re-fetch the resource to get the current ETag and retry |


```json
{
  "statusCode": 412,
  "error": "Precondition Failed",
  "message": "etag_mismatch"
}
```



```json
{
  "statusCode": 428,
  "error": "Precondition Required",
  "message": "If-Match header required for this operation"
}
```




### SDK

```ts
await client.api.proxyHooks.removeContainerProxyHook({
  id: "container_01hz8x9k2b3c4d5e6f7g8h9j0k",
  service: "auth",
  hookId: "01hz8x9k2b3c4d5e6f7g8h9j0k",
  "if-match": "file:v42",
});
```