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

# Hooks API

> Manage hooks (curl, scripts, app launch args) programmatically

These endpoints let you create, read, update, and delete [hooks](/hooks) — the setup, teardown, and runtime scripts that prepare app state and bridge your backend with the Autosana agent.

All requests require an API key in the `X-API-Key` header. See [API Reference](/api-reference) for authentication and the standard `401` / `403` / `429` / `500` error shapes shared by every endpoint on this page.

<Tip>
  This API covers hook CRUD and test execution. To attach a hook as a setup/teardown step on a flow or suite, use the [dashboard](https://autosana.ai/hooks) or the Autosana MCP. Runtime hooks need no attachment — reference them from flow instructions as `${hooks:Hook Name}`.
</Tip>

***

## List Hooks

<Note>
  **GET** `/api/v1/hooks` — Returns `200 OK`
</Note>

### Query Parameters

<ParamField query="flow_id" type="string">
  Optional. Filter to setup/teardown hooks attached to this flow. Runtime hooks (embedded in flow instructions as `${hooks:NAME}`) are NOT returned.
</ParamField>

### Example Request

```bash theme={null}
curl -X GET https://backend.autosana.ai/api/v1/hooks \
  -H "X-API-Key: YOUR_API_KEY"

# Filter to a flow's setup/teardown hooks
curl -X GET "https://backend.autosana.ai/api/v1/hooks?flow_id=FLOW_UUID" \
  -H "X-API-Key: YOUR_API_KEY"
```

### Response Fields

<ResponseField name="hooks" type="array">
  List of hook objects.

  <Expandable title="Hook object">
    <ResponseField name="id" type="string">
      Unique identifier (UUID).
    </ResponseField>

    <ResponseField name="name" type="string">
      Hook name. Referenced from flow instructions as `${hooks:NAME}` for runtime hooks.
    </ResponseField>

    <ResponseField name="script" type="string">
      Full hook script (curl command, Python source, etc.).
    </ResponseField>

    <ResponseField name="script_type" type="string">
      One of `curl`, `python`, `javascript`, `typescript`, `bash`, `launch_args`.
    </ResponseField>

    <ResponseField name="description" type="string | null">
      Optional human-readable note.
    </ResponseField>

    <ResponseField name="created_at" type="string">
      ISO 8601 timestamp.
    </ResponseField>

    <ResponseField name="updated_at" type="string">
      ISO 8601 timestamp of the last modification. Useful for sync tooling that polls for drift.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="count" type="integer">
  Total number of hooks returned.
</ResponseField>

#### Example Response

```json theme={null}
{
  "hooks": [
    {
      "id": "660e8400-e29b-41d4-a716-446655440001",
      "name": "Create Test User",
      "script": "curl -X POST https://api.example.com/users -d '{...}'",
      "script_type": "curl",
      "description": "Provisions a fresh test user before each flow",
      "created_at": "2026-01-15T10:31:00Z"
    }
  ],
  "count": 1
}
```

***

## Create Hook

Create a new hook. Reference it in flow instructions as `${hooks:NAME}` for runtime use, or attach it as a setup/teardown hook from the dashboard.

<Note>
  **POST** `/api/v1/hooks` — Returns `201 Created`
</Note>

### Request Body

<ParamField body="name" type="string" required>
  Name of the hook (1–255 characters). Used as the `${hooks:NAME}` reference in flow instructions.
</ParamField>

<ParamField body="script" type="string" required>
  The script content (curl command, Python source, etc.).
</ParamField>

<ParamField body="script_type" type="string" required>
  One of `curl`, `python`, `javascript`, `typescript`, `bash`, `launch_args`. See [Hook Types](/hooks#hook-types) for what each one runs.
</ParamField>

<ParamField body="description" type="string">
  Optional description of what the hook does.
</ParamField>

### Example Request

```bash theme={null}
curl -X POST https://backend.autosana.ai/api/v1/hooks \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Create Test User",
    "script": "curl -X POST https://api.example.com/users -H \"Content-Type: application/json\" -d \"{\\\"email\\\":\\\"test@example.com\\\"}\"",
    "script_type": "curl",
    "description": "Provisions a fresh test user"
  }'
```

#### Example Response

```json theme={null}
{
  "id": "660e8400-e29b-41d4-a716-446655440001",
  "name": "Create Test User",
  "script": "curl -X POST https://api.example.com/users ...",
  "script_type": "curl",
  "description": "Provisions a fresh test user",
  "created_at": "2026-01-15T10:31:00Z"
}
```

***

## Get Hook

<Note>
  **GET** `/api/v1/hooks/{hook_id}` — Returns `200 OK` with the same shape as items in [List Hooks](#list-hooks), or `404` if the hook does not exist.
</Note>

<ParamField path="hook_id" type="string" required>
  UUID of the hook.
</ParamField>

```bash theme={null}
curl -X GET https://backend.autosana.ai/api/v1/hooks/HOOK_UUID \
  -H "X-API-Key: YOUR_API_KEY"
```

***

## Update Hook

Update a hook's `script`, `script_type`, and/or `description`. Omitted fields are left unchanged.

<Warning>
  **Hook names cannot be updated.** Runtime hooks reference hooks by name in flow instructions (`${hooks:NAME}`), so renaming would silently break those references. To rename safely: create a new hook, update every flow that references the old name to use the new one, then delete the old hook. There is no atomic rename.
</Warning>

<Note>
  **PATCH** `/api/v1/hooks/{hook_id}` — Returns `200 OK`. `400` if no recognized fields are provided. `404` if the hook does not exist.
</Note>

<ParamField path="hook_id" type="string" required>
  UUID of the hook.
</ParamField>

<ParamField body="script" type="string">
  New script content.
</ParamField>

<ParamField body="script_type" type="string">
  New script type (`curl`, `python`, `javascript`, `typescript`, `bash`, `launch_args`).
</ParamField>

<ParamField body="description" type="string | null">
  New description. Pass `""` or `null` to clear.
</ParamField>

```bash theme={null}
curl -X PATCH https://backend.autosana.ai/api/v1/hooks/HOOK_UUID \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "script": "curl -X POST https://api.example.com/v2/users" }'
```

***

## Delete Hook

Deletes the hook. Past run history (the rows that referenced this hook when they ran) is preserved.

If the hook is currently attached to one or more flows or suites, the call returns `409 Conflict` with the names of the affected resources. Pass `?force=true` to detach and delete anyway — the setup/teardown attachments are deactivated as part of the delete.

<Note>
  **DELETE** `/api/v1/hooks/{hook_id}` — Returns `204 No Content`. `409` if the hook is in use and `force=false`. `404` if the hook does not exist.
</Note>

<ParamField path="hook_id" type="string" required>
  UUID of the hook.
</ParamField>

<ParamField query="force" type="boolean">
  Defaults to `false`. When `true`, deletes the hook even if it's attached to flows or suites — those attachments are deactivated.
</ParamField>

<Warning>
  `force=true` detaches the hook from **every** flow and suite at once. There's no per-attachment opt-out, no dry-run, and no undo. Inspect the `409` conflict response (without `force`) first to see exactly which flows/suites will be affected.
</Warning>

```bash theme={null}
# Refuses to delete if attached anywhere
curl -X DELETE https://backend.autosana.ai/api/v1/hooks/HOOK_UUID \
  -H "X-API-Key: YOUR_API_KEY"

# Force delete + detach
curl -X DELETE "https://backend.autosana.ai/api/v1/hooks/HOOK_UUID?force=true" \
  -H "X-API-Key: YOUR_API_KEY"
```

#### Example 409 Response

```json theme={null}
{
  "detail": "Hook 'Create Test User' is used in flow(s): Login Flow. Deleting will detach it from these flows/suites. Pass ?force=true to delete anyway."
}
```

***

## Test Hook

Execute the hook in isolation against a chosen environment and return the result. Uses a shorter 60-second timeout for fast feedback.

<Note>
  **POST** `/api/v1/hooks/{hook_id}/test` — Returns `200 OK`. `404` if the hook does not exist.
</Note>

### Request Body

<ParamField body="environment_id" type="string" required>
  UUID of the environment whose env vars to inject.
</ParamField>

<ParamField body="script_override" type="string">
  Optional. Test a modified script without saving the changes — useful for CI to dry-run a proposed update before persisting it via `PATCH /api/v1/hooks/{id}`.
</ParamField>

### Example Request

```bash theme={null}
# Test the saved script
curl -X POST https://backend.autosana.ai/api/v1/hooks/HOOK_UUID/test \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "environment_id": "ENV_UUID" }'

# Test a proposed script change without saving
curl -X POST https://backend.autosana.ai/api/v1/hooks/HOOK_UUID/test \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "environment_id": "ENV_UUID",
    "script_override": "curl -X POST https://api.example.com/v2/users -d \"{...}\""
  }'
```

### Response Fields

<ResponseField name="success" type="boolean">
  Whether the hook executed successfully.
</ResponseField>

<ResponseField name="status_code" type="integer | null">
  Exit code (for scripts) or HTTP status (for `curl`).
</ResponseField>

<ResponseField name="output" type="string">
  Captured stdout/stderr from the sandbox.
</ResponseField>

<ResponseField name="execution_time_ms" type="integer">
  Wall-clock duration in milliseconds.
</ResponseField>

<ResponseField name="error" type="string | null">
  Category of failure when `success=false` (e.g. `Execution timed out`, `Missing environment variable`).
</ResponseField>
