Skip to main content
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
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

  1. Navigate to 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:

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
}
  1. 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.
  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
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:
  1. Expand the Hooks section
  2. Select a Setup Hook (runs before flow)
  3. Optionally select a Teardown Hook (runs after flow)
When editing a flow:
  1. Click Edit on a flow
  2. Select hooks from the dropdowns
  3. 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:
  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: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

  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:
RuleValidInvalid
Alphanumeric + underscore onlyAUTH_TOKEN, API_KEY, user_idAPI-KEY, user.id
Cannot start with a numberTOKEN_123, _INTERNAL123_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 CaseRecommended Hook Type
Simple single API callcURL
Multiple API calls with logicPython, JavaScript, or TypeScript
Shell operations, piping commandsBash
Configure app at startupApp Launch Configuration
Need error handling and retriesPython, JavaScript, or TypeScript
Parse and transform API responsesPython, 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:
ContextTimeout
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

FeatureHooksSuite Auth Instructions
TypeScripts, cURL, or Launch argsNatural language instructions
PurposeSetup/cleanup backend stateNavigate app to starting point
ExecutionBefore/after each flowOnce at suite start
ReusabilityCan be used across many flowsSpecific 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