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

> Set the state of the app before, during, and after your flows run via scripts, API calls, and app launch configurations

Hooks serve as a bridge between your backend or server-side scripts and the Autosana testing environment. They allow you to communicate data, set the state of the app, set the state of a user, etc.

They are short programs that run in sandboxed environments during the testing process. They can be added to suites and flows, and any output from them (print statements, console.log, echo) is visible to the agent.

A few common examples:

1. Resetting a user's onboarding in the backend via JS code
2. Generating a random email to be used by the Autosana agent via Python code
3. Returning a magic link or OTP code from your server for Autosana to use in the flow via a cURL request
4. Mocking audio or video input by hitting an endpoint on your server via TypeScript code

Possibilities are endless and usually the more hooks the better, as it adds a level of determinism to the state of the test!

**Execution Positions**

There are three hook execution positions:

1. **Setup Hooks**: Run before suites or flows start. Great for providing context.
2. **Runtime Hooks**: Run as an action during the flow — added directly into flow instructions, allows real-time backend interaction during testing
3. **Teardown Hooks**: Run at the very end of suites and flows. Helpful to clean up testing states

## Hook Types

Hooks allow you to configure your test environment in three ways:

1. **Scripts**: Python, JavaScript, TypeScript, or Bash scripts that run server-side
2. **cURL Requests**: Backend API calls for simple HTTP requests
3. **App Launch Configuration**: Configure how your mobile app launches (feature flags, environment settings) - *mobile only*

### Scripts

Server-side scripts that execute in a secure sandbox environment. Choose from Python, JavaScript, TypeScript, or Bash depending on your needs.

<Note>
  If your hook calls a firewalled API, see [Network allowlist](/network-allowlist) for the IPs to allow.
</Note>

<Warning>
  Scripts run in a sandboxed environment and **cannot use external libraries**. Use only built-in/standard libraries (e.g., `urllib` instead of `requests` for Python, built-in `fetch` for JavaScript/TypeScript).
</Warning>

<Warning>
  Because hook sandboxes are created from snapshots, pseudo-random generators can repeat values across runs. Avoid `Math.random()` in JavaScript/TypeScript and unseeded `random` in Python when generating emails, usernames, IDs, or other unique test data.
</Warning>

**Common use cases:**

* Complex multi-step API operations
* Data processing and transformation
* Conditional logic based on API responses
* Generating dynamic test data
* Chaining multiple API calls with error handling

#### Generating Random Values

Use cryptographically secure randomness from the language standard library when generating unique test data.

**JavaScript / TypeScript:**

```javascript theme={null}
const crypto = require('crypto');

const suffix = crypto.randomInt(1, 100000001);
const email = `test-${suffix}@example.com`;
const uniqueId = crypto.randomUUID();

console.log(email);
```

**Python:**

```python theme={null}
import secrets
import string
import uuid

suffix = ''.join(secrets.choice(string.ascii_lowercase) for _ in range(8))
email = f"test-{suffix}@example.com"
unique_id = str(uuid.uuid4())

print(email)
```

If you must use Python's `random` module, seed it from OS entropy at the top of your script:

```python theme={null}
import os
import random

random.seed(os.urandom(32))
```

### cURL Requests

Backend API calls that execute before (setup) or after (teardown) a flow runs. Best for simple, single HTTP requests.

**Common use cases:**

* Create test user accounts via your API
* Reset database state between flows
* Generate auth tokens or session data
* Configure backend feature flags
* Clean up test data after flows complete

### App Launch Configuration (Mobile Only)

Configure values that are passed to your mobile app when it launches. These values are available to your app code as environment variables (iOS) or intent extras (Android).

<Note>
  App Launch Configuration is only available for mobile apps (iOS and Android). For web testing, use Scripts or cURL Requests to configure your test environment.
</Note>

**Common use cases:**

* Override feature flag values for specific test scenarios
* Set test environment parameters (staging vs. production mode)
* Configure API timeouts or retry behavior
* Enable debug modes or verbose logging
* Set experiment variants for A/B testing

## Creating a Hook

1. Navigate to **[Hooks](https://autosana.ai/hooks)** in the sidebar
2. Click **Create Hook**
3. Enter a hook name (e.g., "Create Test User" or "Generate Auth Token")
4. Select hook type:
   * **Python**: For complex logic and data processing
   * **JavaScript**: For Node.js-based operations
   * **TypeScript**: For type-safe Node.js operations
   * **Bash**: For shell script operations
   * **cURL Request**: For simple HTTP API calls
   * **App Launch Configuration**: For mobile app settings (mobile only)
5. Enter your script or configuration:

### For Python:

```python theme={null}
import urllib.request
import json
import os

data = json.dumps({
    "email": os.environ.get('TEST_EMAIL'),
    "password": os.environ.get('TEST_PASSWORD')
}).encode('utf-8')

req = urllib.request.Request(
    f"{os.environ.get('API_URL')}/auth/login",
    data=data,
    headers={'Content-Type': 'application/json'}
)

with urllib.request.urlopen(req) as response:
    result = json.loads(response.read().decode('utf-8'))

print(f"Logged in as user: {result['userId']}")

# Export token for subsequent hooks
with open('/tmp/autosana.env', 'w') as f:
    f.write(f"AUTH_TOKEN={result['token']}\n")
```

### For JavaScript:

```javascript theme={null}
const response = await fetch(`${process.env.API_URL}/auth/login`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    email: process.env.TEST_EMAIL,
    password: process.env.TEST_PASSWORD
  })
});

const data = await response.json();
console.log(`Logged in as user: ${data.userId}`);

// Export token for subsequent hooks
const fs = require('fs');
fs.writeFileSync('/tmp/autosana.env', `AUTH_TOKEN=${data.token}\n`);
```

### For TypeScript:

```typescript theme={null}
const response = await fetch(`${process.env.API_URL}/auth/login`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    email: process.env.TEST_EMAIL,
    password: process.env.TEST_PASSWORD
  })
});

interface LoginResponse {
  token: string;
  userId: string;
}

const data: LoginResponse = await response.json();
console.log(`Logged in as user: ${data.userId}`);

// Export token for subsequent hooks
const fs = require('fs');
fs.writeFileSync('/tmp/autosana.env', `AUTH_TOKEN=${data.token}\n`);
```

### For Bash:

```bash theme={null}
RESPONSE=$(curl -s -X POST "${API_URL}/auth/login" \
  -H "Content-Type: application/json" \
  -d "{\"email\":\"${TEST_EMAIL}\",\"password\":\"${TEST_PASSWORD}\"}")

TOKEN=$(echo $RESPONSE | jq -r '.token')
USER_ID=$(echo $RESPONSE | jq -r '.userId')

echo "Logged in as user: $USER_ID"

# Export token for subsequent hooks (append with >>)
echo "AUTH_TOKEN=$TOKEN" >> /tmp/autosana.env
```

### For cURL Requests:

```bash theme={null}
curl -X POST https://api.example.com/users \
  -H "Content-Type: application/json" \
  -d '{"email":"test@example.com","password":"TestPass123"}'
```

### For App Launch Configuration:

```json theme={null}
{
  "testEnvironment": "staging",
  "featureFlags": {
    "newCheckoutFlow": "enabled",
    "darkMode": true
  },
  "apiTimeout": 30
}
```

6. Click **Create Hook**

<Tip>
  Any output from your hooks (print statements, console.log, echo) is passed to the agent as additional context. Use this to provide helpful information about what the hook did!
</Tip>

## Testing Hooks

Before attaching hooks to flows, you can test them to verify they work correctly.

1. Open the hook you want to test
2. Click the **Test Hook** button
3. The hook will execute with a **1-minute timeout**
4. View the output to verify success or debug issues

**What to check:**

* Script executes without errors
* API calls return expected responses
* Environment variables are being read correctly
* Exported values (if any) are formatted correctly

<Tip>
  Always test hooks independently before attaching them to flows. This helps isolate issues and speeds up debugging.
</Tip>

## Using Hooks

<iframe src="https://www.veed.io/embed/720a1987-f583-42c1-99df-1dd04b9f13d7?watermark=0&color=default&sharing=0&title=1" width="744" height="504" frameBorder="0" title="Adding Hooks to Flows" allow="fullscreen" allowFullScreen />

### Setup & Teardown Hooks

**Attaching Setup & Teardown Hooks**

1. Create a new suite or flow or click **Edit** on an existing one
2. Expand the **Advanced** section
3. Expand the **Setup Hooks** or **Teardown Hooks** section
4. Click the **Add a setup hook...** or **Add a teardown hook...** button
5. Select the hook you want to attach

### Runtime Hooks

You can execute hooks as actions during flow execution by using the syntax `${hooks:Hook Name}` in the flow instructions.

**Example:**

```
Tap the login button, then ${hooks:Create Test User}, then verify the user appears
```

When the agent encounters `${hooks:Hook Name}`, it will:

1. Look up the hook by name
2. Execute the hook script
3. Continue with the rest of the action

This is useful for:

* Creating data mid-flow (e.g., "add 5 items to cart, then `${hooks:Create Dummy Discount Code}`")
* Triggering backend events at specific points
* Resetting state between actions

<Tip>
  **Runtime Hooks** execute synchronously - the flow waits for the hook to complete before continuing.
</Tip>

### Execution Order

1. Setup Hook executes (if configured)
2. Flow runs on app start
3. Agent runs actions and any Runtime Hooks (if configured)
4. Flow run stops
5. Teardown Hook executes (if configured)

## Environment Variables in Hooks

### For cURL Requests

Reference environment variables using `${env:VARIABLE_NAME}` syntax:

```bash theme={null}
curl -X POST ${env:API_URL}/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"${env:TEST_EMAIL}","password":"${env:TEST_PASSWORD}"}'
```

**Environment variables (in Settings → Environments):**

* `API_URL` = `https://staging-api.example.com`
* `TEST_EMAIL` = `test@staging.com`
* `TEST_PASSWORD` = `SecurePass123`

**Executed command:**

```bash theme={null}
curl -X POST https://staging-api.example.com/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"test@staging.com","password":"SecurePass123"}'
```

### For Scripts (Python, JavaScript, TypeScript, Bash)

Environment variables are automatically injected into the script environment. Access them using your language's standard method:

**Python:**

```python theme={null}
import os

api_url = os.environ.get('API_URL')
test_email = os.getenv('TEST_EMAIL')
```

**JavaScript / TypeScript:**

```javascript theme={null}
const apiUrl = process.env.API_URL;
const testEmail = process.env.TEST_EMAIL;
```

**Bash:**

```bash theme={null}
echo $API_URL
echo ${TEST_EMAIL}
```

[Learn more about environment variables →](/environments)

<Tip>
  Click **Insert Environment Variable** when creating/editing a hook to browse and select from your available environment variables.
</Tip>

## Sharing Data Between Hooks

Hooks can pass data to subsequent hooks in the same suite. This is useful for scenarios like:

* Generating an auth token in a setup hook and using it in a runtime hook
* Creating a test user and passing the user ID to a teardown hook for cleanup
* Sharing credentials across multiple flows in a suite

### How It Works

1. In your hook, write values to `/tmp/autosana.env` in `KEY=VALUE` format
2. Subsequent hooks can access these values as environment variables
3. Values persist for all subsequent hooks in the suite (across all flows)

### Key Naming Rules

When exporting values, keys must follow these rules:

| Rule                           | Valid                              | Invalid                |
| ------------------------------ | ---------------------------------- | ---------------------- |
| Alphanumeric + underscore only | `AUTH_TOKEN`, `API_KEY`, `user_id` | `API-KEY`, `user.id`   |
| Cannot start with a number     | `TOKEN_123`, `_INTERNAL`           | `123_TOKEN`, `1ST_KEY` |

### Example: Passing a Token Between Hooks

**Setup Hook (Python) - Creates token:**

```python theme={null}
import urllib.request
import json
import os

data = json.dumps({
    "email": "test@example.com",
    "password": "secret"
}).encode('utf-8')

req = urllib.request.Request(
    f"{os.environ.get('API_URL')}/auth/login",
    data=data,
    headers={'Content-Type': 'application/json'}
)

with urllib.request.urlopen(req) as response:
    result = json.loads(response.read().decode('utf-8'))

token = result['token']
user_id = result['userId']

# Export for subsequent hooks
with open('/tmp/autosana.env', 'w') as f:
    f.write(f"AUTH_TOKEN={token}\n")
    f.write(f"TEST_USER_ID={user_id}\n")

print(f"Created session for user {user_id}")
```

**Runtime Hook (cURL) - Uses the token:**

```bash theme={null}
curl -X POST ${env:API_URL}/cart/add \
  -H "Authorization: Bearer ${env:AUTH_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{"productId": "12345", "quantity": 1}'
```

**Teardown Hook (Python) - Cleans up:**

```python theme={null}
import urllib.request
import os

# Access values exported by setup hook
token = os.environ.get('AUTH_TOKEN')
user_id = os.environ.get('TEST_USER_ID')

# Clean up test user
req = urllib.request.Request(
    f"{os.environ.get('API_URL')}/users/{user_id}",
    method='DELETE',
    headers={'Authorization': f'Bearer {token}'}
)

with urllib.request.urlopen(req) as response:
    pass  # Just need to execute the request

print(f"Cleaned up user {user_id}")
```

### Important Notes

* **Scripts can read and write** exported values
* **cURL hooks can only read** exported values (they cannot write to `/tmp/autosana.env`)
* If multiple hooks export the same key, the **latest value wins**
* Exported values are available to all subsequent hooks in the suite (across all flows)
* Exported values are also available to **subsequent flows** in the suite via `${env:KEY}`
* The agent can also retrieve exported values directly by name using **Get Variable**

<Tip>
  Hook exports are part of Autosana's runtime variable system. For a complete overview of how variables work across hooks, flows, and suites, see [Variables](/variables).
</Tip>

## Hook Examples

### cURL Examples

#### Create User Account

```bash theme={null}
curl -X POST https://api.example.com/users \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ${env:ADMIN_TOKEN}" \
  -d '{
    "email": "${env:TEST_EMAIL}",
    "password": "${env:TEST_PASSWORD}",
    "role": "tester"
  }'
```

#### Reset Database

```bash theme={null}
curl -X POST https://api.example.com/test/reset \
  -H "Authorization: Bearer ${env:ADMIN_TOKEN}" \
  -d '{"scope": "test_data"}'
```

#### Generate Auth Token

```bash theme={null}
curl -X POST https://api.example.com/auth/token \
  -H "Content-Type: application/json" \
  -d '{
    "client_id": "${env:CLIENT_ID}",
    "client_secret": "${env:CLIENT_SECRET}"
  }'
```

#### Delete Test Data (Teardown)

```bash theme={null}
curl -X DELETE https://api.example.com/users/${env:TEST_USER_ID} \
  -H "Authorization: Bearer ${env:ADMIN_TOKEN}"
```

### Script Examples

#### Python: Create User and Export Credentials

```python theme={null}
import urllib.request
import urllib.error
import json
import os
import secrets
import string

# Generate random email for test user
random_suffix = ''.join(secrets.choice(string.ascii_lowercase) for _ in range(8))
test_email = f"test_{random_suffix}@example.com"

data = json.dumps({
    "email": test_email,
    "password": "TestPass123",
    "role": "tester"
}).encode('utf-8')

req = urllib.request.Request(
    f"{os.environ.get('API_URL')}/users",
    data=data,
    headers={
        'Content-Type': 'application/json',
        'Authorization': f"Bearer {os.environ.get('ADMIN_TOKEN')}"
    }
)

try:
    with urllib.request.urlopen(req) as response:
        user_data = json.loads(response.read().decode('utf-8'))
        print(f"Created test user: {test_email}")

        # Export for use in flow and teardown
        with open('/tmp/autosana.env', 'w') as f:
            f.write(f"TEST_USER_EMAIL={test_email}\n")
            f.write(f"TEST_USER_ID={user_data['id']}\n")
except urllib.error.HTTPError as e:
    raise Exception(f"Failed to create user: {e.read().decode('utf-8')}")
```

#### JavaScript: Fetch and Process Data

```javascript theme={null}
const response = await fetch(`${process.env.API_URL}/products`, {
  headers: { 'Authorization': `Bearer ${process.env.API_TOKEN}` }
});

const products = await response.json();

// Find a product that's in stock
const availableProduct = products.find(p => p.inStock && p.price < 100);

if (availableProduct) {
  console.log(`Found product: ${availableProduct.name} ($${availableProduct.price})`);

  // Export for use in flow
  const fs = require('fs');
  fs.writeFileSync('/tmp/autosana.env', `TEST_PRODUCT_ID=${availableProduct.id}\n`);
} else {
  throw new Error('No suitable test product found');
}
```

#### Bash: Quick API Check

```bash theme={null}
# Check if API is healthy before running tests
HEALTH=$(curl -s -o /dev/null -w "%{http_code}" "${API_URL}/health")

if [ "$HEALTH" != "200" ]; then
  echo "API health check failed with status: $HEALTH"
  exit 1
fi

echo "API is healthy, proceeding with tests"
```

## App Launch Configuration (Mobile Only)

### How It Works

App Launch Configuration hooks pass values to your mobile app when it launches. These values are set **at app startup** and are available throughout your test. This feature is only available for iOS and Android apps.

**Important:** Launch configuration is applied when the app starts. Within a suite, the launch configuration from the first flow will be used for all flows in that suite.

### Accessing Values in Your App

**iOS (Swift):**

```swift theme={null}
// Access simple values (converted to strings)
let environment = ProcessInfo.processInfo.environment
let testEnv = environment["testEnvironment"] // "staging"
let apiTimeout = environment["apiTimeout"] // "30" (as string)

// Nested objects are JSON strings - parse them
if let flagsJSON = environment["featureFlags"],
   let flagsData = flagsJSON.data(using: .utf8) {
    do {
        if let flags = try JSONSerialization.jsonObject(with: flagsData) as? [String: Any] {
            let darkMode = flags["darkMode"] as? Bool // true
            let checkoutFlow = flags["newCheckoutFlow"] as? String // "enabled"
        }
    } catch {
        print("Failed to parse feature flags: \(error)")
    }
}
```

**Android (Kotlin):**

```kotlin theme={null}
import org.json.JSONObject

// Access simple values (type-aware)
val testEnv = intent.extras?.getString("testEnvironment") // "staging"
val apiTimeout = intent.extras?.getInt("apiTimeout") // 30 (as int)

// Nested objects are JSON strings - parse them
val flagsJSON = intent.extras?.getString("featureFlags")
if (flagsJSON != null) {
    try {
        val flags = JSONObject(flagsJSON)
        val darkMode = flags.getBoolean("darkMode") // true
        val checkoutFlow = flags.getString("newCheckoutFlow") // "enabled"
    } catch (e: Exception) {
        Log.e("LaunchArgs", "Failed to parse feature flags", e)
    }
}
```

**React Native:**

```javascript theme={null}
import { NativeModules, Platform } from "react-native";

// iOS: values are set as environment variables (available via native bridge)
// Android: values are passed as intent extras
const launchArgs = Platform.OS === "ios"
  ? NativeModules.ProcessInfo?.environment
  : NativeModules.IntentExtras?.getExtras();

// Simple values
console.log(launchArgs.testEnvironment); // "staging"
console.log(launchArgs.apiTimeout); // "30" (string on iOS, number on Android)

// Nested objects come as JSON strings - parse them
if (launchArgs.featureFlags) {
    const flags = JSON.parse(launchArgs.featureFlags);
    console.log(flags.darkMode); // true
    console.log(flags.newCheckoutFlow); // "enabled"
}
```

<Note>
  On iOS, launch configuration values are set as **environment variables** and accessed via `ProcessInfo.processInfo.environment`. On Android, they are passed as **intent extras** and accessed via `intent.extras`. See the platform-specific examples above for details.
</Note>

### Launch Configuration Examples

#### Override Feature Flags

```json theme={null}
{
  "featureFlags": {
    "newCheckoutFlow": "treatment_a",
    "paymentMethodV2": "enabled",
    "showPromotion": false
  }
}
```

#### Set Environment and Timeouts

```json theme={null}
{
  "testEnvironment": "staging",
  "apiBaseUrl": "https://staging-api.example.com",
  "requestTimeout": 30,
  "retryAttempts": 3
}
```

#### A/B Testing Configuration

```json theme={null}
{
  "experiments": {
    "checkoutExperiment": "variant_b",
    "pricingExperiment": "control"
  },
  "userId": "test_user_123"
}
```

#### Debug Mode Settings

```json theme={null}
{
  "enableDebugMode": true,
  "logLevel": "verbose",
  "showPerformanceMetrics": true,
  "mockPaymentProvider": true
}
```

### When to Use Each Hook Type

| Use Case                          | Recommended Hook Type             |
| --------------------------------- | --------------------------------- |
| Simple single API call            | cURL                              |
| Multiple API calls with logic     | Python, JavaScript, or TypeScript |
| Shell operations, piping commands | Bash                              |
| Configure app at startup          | App Launch Configuration          |
| Need error handling and retries   | Python, JavaScript, or TypeScript |
| Parse and transform API responses | Python, JavaScript, or TypeScript |

## Setup Hooks & Agent Context

**Pro Tip:** When a hook produces output (via print statements, console.log, or echo), the agent automatically receives this data as additional context and can use it during flow execution.

**Example setup hook:**

```python theme={null}
import urllib.request
import json
import os

data = json.dumps({
    "email": os.environ.get('TEST_EMAIL'),
    "password": os.environ.get('TEST_PASSWORD')
}).encode('utf-8')

req = urllib.request.Request(
    f"{os.environ.get('API_URL')}/auth/login",
    data=data,
    headers={'Content-Type': 'application/json'}
)

with urllib.request.urlopen(req) as response:
    result = json.loads(response.read().decode('utf-8'))

# This output is passed to the agent
print(f"Logged in as: {result['email']}")
print(f"User role: {result['role']}")
print(f"Account created: {result['createdAt']}")
```

The agent receives this output and can reference it if needed during the flow execution.

## Timeouts

Hooks have different timeout limits depending on the context:

| Context                                       | Timeout   |
| --------------------------------------------- | --------- |
| **Testing hooks** (via Test button)           | 1 minute  |
| **Flow execution** (setup, runtime, teardown) | 5 minutes |

If a hook exceeds its timeout, it will be terminated and marked as failed.

<Tip>
  Keep hooks focused and fast. If you have long-running operations, consider breaking them into multiple hooks or optimizing your API calls.
</Tip>

## Best Practices

<Tip>
  **Use Descriptive Names**

  Name hooks after what they do: "Create Premium Test User" instead of "Hook 1"
</Tip>

<Tip>
  **Store Secrets in Environment Variables**

  Never hardcode API keys or passwords in hooks. Use environment variables like `${env:API_KEY}`.
</Tip>

<Tip>
  **Test Hooks Independently**

  Use the Test button to verify your hooks work correctly before attaching them to flows.
</Tip>

<Tip>
  **Use Teardown Hooks for Cleanup**

  Always clean up test data created by setup hooks to avoid polluting your backend.
</Tip>

<Tip>
  **Keep Hooks Simple**

  Each hook should do one thing. Create separate hooks for different setup tasks.
</Tip>

<Tip>
  **Use Scripts for Complex Logic**

  When you need conditionals, loops, error handling, or data processing, use Python/JavaScript instead of complex cURL commands.
</Tip>

<Tip>
  **Export Only What You Need**

  When sharing data between hooks, only export the values that subsequent hooks actually need.
</Tip>

<Tip>
  **Add Helpful Print Statements**

  Output from your hooks is passed to the agent. Add print statements to provide context about what happened.
</Tip>

## Hooks vs Suite Auth Instructions

| Feature         | Hooks                         | Suite Auth Instructions        |
| --------------- | ----------------------------- | ------------------------------ |
| **Type**        | Scripts, cURL, or Launch args | Natural language instructions  |
| **Purpose**     | Setup/cleanup backend state   | Navigate app to starting point |
| **Execution**   | Before/after each flow        | Once at suite start            |
| **Reusability** | Can be used across many flows | Specific to one suite          |

**Use hooks when:** You need to configure backend state, create test data, or call APIs

**Use suite auth instructions when:** You need to start the suite from a specific state (e.g., logged in with a certain test account)

## Troubleshooting

### Script Issues

**Script times out**

* Hooks have a 5-minute timeout during flow execution (1 minute when testing)
* Break long operations into multiple hooks
* Check for infinite loops or slow API endpoints

**Environment variable not found**

* Verify the variable exists in Settings → Environments
* Check spelling and case (variable names are case-sensitive)
* For scripts, use the correct access method for your language

**Random values are the same every run**

* Hook sandboxes are created from snapshots, so pseudo-random generators may repeat their seed across runs
* In JavaScript/TypeScript, use Node's built-in `crypto` module (`crypto.randomInt`, `crypto.randomUUID`) instead of `Math.random()`
* In Python, prefer `secrets` or `uuid`; if you must use `random`, call `random.seed(os.urandom(32))` before generating values

**Exported values not available in next hook**

* Verify you wrote to `/tmp/autosana.env` (exact path)
* Check key naming rules (alphanumeric + underscore, can't start with number)
* Ensure the format is `KEY=VALUE` with one per line

### cURL Request Issues

**Hook fails to execute**

* Verify the curl command works in your terminal first
* Check that environment variables are defined
* Ensure API endpoints are accessible from Autosana's infrastructure

**Environment variables not replacing**

* Syntax must be `${env:VARIABLE_NAME}` (not `{{VARIABLE_NAME}}` or `$VARIABLE_NAME`)
* Variable names are case-sensitive
* Variables must exist in Settings → Environments

### App Launch Configuration Issues

**Configuration not appearing in app**

* Verify JSON is valid (use a JSON validator)
* Check you're accessing values correctly for your platform (ProcessInfo for iOS, intent.extras for Android)
* Ensure the hook is attached as a **setup hook** (launch configuration doesn't work in teardown)

**Values have wrong type**

* On iOS, all values become strings (including numbers and booleans)
* On Android, primitives keep their types (int, float, boolean, string)
* Nested objects are JSON-serialized as strings on both platforms
* Use the examples in this guide to parse nested structures correctly

### Data Sharing Issues

**Hook can't read value from previous hook**

* Ensure the previous hook successfully wrote to `/tmp/autosana.env`
* Check that the previous hook completed without errors
* Verify the key name matches exactly (case-sensitive)

**Invalid key name warning**

* Keys must contain only letters, numbers, and underscores
* Keys cannot start with a number
* Examples: `AUTH_TOKEN` ✓, `123_KEY` ✗, `API-KEY` ✗

## Managing Hooks via API

Hooks can be created, updated, and deleted programmatically via the [Hooks API](/api-hooks). Useful for syncing hook definitions from your repo, bulk-importing from another system, or wiring up custom tooling.

## Next Steps

* [Learn about environment variables →](/environments)
* [Organize flows with suites →](/suites)
* [Create your first flow →](/flows)
* [Manage hooks via API →](/api-hooks)
