Hooks allow you to configure your test environment in three ways:
- Scripts: Python, JavaScript, TypeScript, or Bash scripts that run server-side
- cURL Requests: Backend API calls for simple HTTP requests
- App Launch Configuration: Configure how your mobile app launches (feature flags, environment settings) - mobile only
Perfect for creating test users, resetting databases, configuring backend settings, or controlling app behavior during tests.
Hook Types
Scripts
Server-side scripts that execute in a secure sandbox environment. Choose from Python, JavaScript, TypeScript, or Bash depending on your needs.
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).
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
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).
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.
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
- Navigate to Hooks in the sidebar
- Click Create Hook
- Enter a hook name (e.g., “Create Test User” or “Generate Auth Token”)
- 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)
- Enter your script or configuration:
For Python:
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:
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:
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:
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:
curl -X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]","password":"TestPass123"}'
For App Launch Configuration:
{
"testEnvironment": "staging",
"featureFlags": {
"newCheckoutFlow": "enabled",
"darkMode": true
},
"apiTimeout": 30
}
- Click Create Hook
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!
Testing Hooks
Before attaching hooks to flows, you can test them to verify they work correctly.
- Open the hook you want to test
- Click the Test Hook button
- The hook will execute with a 1-minute timeout
- 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
Always test hooks independently before attaching them to flows. This helps isolate issues and speeds up debugging.
Using Hooks in Flows
Attaching Hooks
When creating a flow:
- Expand the Hooks section
- Select a Setup Hook (runs before flow)
- Optionally select a Teardown Hook (runs after flow)
When editing a flow:
- Click Edit on a flow
- Select hooks from the dropdowns
- Save the flow
Execution Order
1. Setup Hook executes (if configured)
2. Flow runs on mobile app
3. Teardown Hook executes (if configured)
Running Hooks Mid-Flow
You can execute hooks as actions during flow execution by using the syntax ${hooks:Hook Name} in any action description.
Example:
Tap the login button, then ${hooks:Create Test User}, then verify the user appears
When the agent encounters ${hooks:Hook Name}, it will:
- Look up the hook by name
- Execute the hook script
- Continue with the rest of the action
This is useful for:
- Creating data mid-flow (e.g., “add 5 items to cart, then
${hooks:Apply Discount Code}”)
- Triggering backend events at specific points
- Resetting state between actions
Mid-flow hooks execute synchronously - the flow waits for the hook to complete before continuing.
Environment Variables in Hooks
For cURL Requests
Reference environment variables using ${env:VARIABLE_NAME} syntax:
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 = [email protected]
TEST_PASSWORD = SecurePass123
Executed command:
curl -X POST https://staging-api.example.com/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]","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:
import os
api_url = os.environ.get('API_URL')
test_email = os.getenv('TEST_EMAIL')
JavaScript / TypeScript:
const apiUrl = process.env.API_URL;
const testEmail = process.env.TEST_EMAIL;
Bash:
echo $API_URL
echo ${TEST_EMAIL}
Learn more about environment variables →
Click Insert Environment Variable when creating/editing a hook to browse and select from your available environment variables.
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 mid-flow 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
- In your hook, write values to
/tmp/autosana.env in KEY=VALUE format
- Subsequent hooks can access these values as environment variables
- 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:
import urllib.request
import json
import os
data = json.dumps({
"email": "[email protected]",
"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}")
Mid-Flow Hook (cURL) - Uses the token:
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:
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)
Hook Examples
cURL Examples
Create User Account
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
curl -X POST https://api.example.com/test/reset \
-H "Authorization: Bearer ${env:ADMIN_TOKEN}" \
-d '{"scope": "test_data"}'
Generate Auth Token
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)
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
import urllib.request
import urllib.error
import json
import os
import random
import string
# Generate random email for test user
random_suffix = ''.join(random.choices(string.ascii_lowercase, k=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
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
# 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):
// 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):
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:
import { LaunchArguments } from "react-native-launch-arguments";
const args = LaunchArguments.value();
// Simple values
console.log(args.testEnvironment); // "staging"
console.log(args.apiTimeout); // "30" (string on iOS, number on Android)
// Nested objects come as JSON strings - parse them
if (args.featureFlags) {
const flags = JSON.parse(args.featureFlags);
console.log(flags.darkMode); // true
console.log(flags.newCheckoutFlow); // "enabled"
}
Launch Configuration Examples
Override Feature Flags
{
"featureFlags": {
"newCheckoutFlow": "treatment_a",
"paymentMethodV2": "enabled",
"showPromotion": false
}
}
Set Environment and Timeouts
{
"testEnvironment": "staging",
"apiBaseUrl": "https://staging-api.example.com",
"requestTimeout": 30,
"retryAttempts": 3
}
A/B Testing Configuration
{
"experiments": {
"checkoutExperiment": "variant_b",
"pricingExperiment": "control"
},
"userId": "test_user_123"
}
Debug Mode Settings
{
"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:
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, teardown, mid-flow) | 5 minutes |
If a hook exceeds its timeout, it will be terminated and marked as failed.
Keep hooks focused and fast. If you have long-running operations, consider breaking them into multiple hooks or optimizing your API calls.
Best Practices
Use Descriptive NamesName hooks after what they do: “Create Premium Test User” instead of “Hook 1”
Store Secrets in Environment VariablesNever hardcode API keys or passwords in hooks. Use environment variables like ${env:API_KEY}.
Test Hooks IndependentlyUse the Test button to verify your hooks work correctly before attaching them to flows.
Use Teardown Hooks for CleanupAlways clean up test data created by setup hooks to avoid polluting your backend.
Keep Hooks SimpleEach hook should do one thing. Create separate hooks for different setup tasks.
Use Scripts for Complex LogicWhen you need conditionals, loops, error handling, or data processing, use Python/JavaScript instead of complex cURL commands.
Export Only What You NeedWhen sharing data between hooks, only export the values that subsequent hooks actually need.
Add Helpful Print StatementsOutput from your hooks is passed to the agent. Add print statements to provide context about what happened.
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
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 ✗
Next Steps