Best Practices & Examples
Real-world flow examples and strategies for building effective, maintainable flows.
Example: Authentication Flow
A 3-step flow that logs in, fetches the user profile, and updates it. Demonstrates token extraction and reuse across steps.
URL: {{base_url}}/api/auth/login
Body:
Extractions (post-response script):
| Name | Source | Expression |
|---|---|---|
| token | JSON Path | data.accessToken |
| refreshToken | JSON Path | data.refreshToken |
| userId | JSON Path | data.user.id |
URL: {{base_url}}/api/users/{{flow.userId}}
Headers:
Assertions (Processors tab):
✓ Status code equals 200
✓ Body JSON data.id equals {{flow.userId}}
Extractions:
| Name | Source | Expression |
|---|---|---|
| username | JSON Path | data.username |
URL: {{base_url}}/api/users/{{flow.userId}}
Headers:
Body:
Assertions:
✓ Status code equals 200
✓ Body JSON data.displayName equals Updated Name
Key takeaway: {{base_url}}, {{email}}, and {{password}} come from the group environment (static, reusable). {{flow.token}}, {{flow.userId}}, and {{flow.username}} come from response extractions (dynamic, per-run).
Example: Order Management Flow
A 4-step CRUD flow that creates an order, retrieves it, updates the quantity, and finally cancels it. Demonstrates the Setup → Test → Teardown pattern.
URL: {{base_url}}/api/orders
Headers: Authorization: Bearer {{flow.token}}
Body:
Extractions:
| Name | Source | Expression |
|---|---|---|
| orderId | JSON Path | data.id |
| orderStatus | JSON Path | data.status |
Assertions: Status code equals 201
URL: {{base_url}}/api/orders/{{flow.orderId}}
Headers: Authorization: Bearer {{flow.token}}
Assertions:
✓ Status code equals 200
✓ Body JSON data.id equals {{flow.orderId}}
✓ Body JSON data.status equals pending
✓ Body JSON data.quantity equals 2
URL: {{base_url}}/api/orders/{{flow.orderId}}
Headers: Authorization: Bearer {{flow.token}}
Body:
Assertions:
✓ Status code equals 200
✓ Body JSON data.quantity equals 5
URL: {{base_url}}/api/orders/{{flow.orderId}}
Headers: Authorization: Bearer {{flow.token}}
Assertions: Status code equals 200 or 204
Key takeaway: The {{flow.token}} here is assumed to come from a previous Auth flow run, or from a Login step you add at the beginning. In a real setup, you'd either prepend a Login step or use at.flow.set() in a pre-script to set the token. The last step (Cancel) acts as teardown to keep the database clean for re-runs.
Example: Payment Processing Flow
A 3-step flow that creates a payment intent, confirms the payment, and verifies the final state. Shows how multiple extractions chain through the flow.
URL: {{base_url}}/api/payments/intent
Headers: Authorization: Bearer {{flow.token}}
Body:
Extractions:
| Name | Source | Expression |
|---|---|---|
| paymentId | JSON Path | data.id |
| clientSecret | JSON Path | data.clientSecret |
Assertions: Status code equals 201, Body JSON data.status equals requires_confirmation
URL: {{base_url}}/api/payments/{{flow.paymentId}}/confirm
Headers: Authorization: Bearer {{flow.token}}
Body:
Extractions:
| Name | Source | Expression |
|---|---|---|
| transactionId | JSON Path | data.transactionId |
Assertions: Status code equals 200, Body JSON data.status equals succeeded
URL: {{base_url}}/api/payments/{{flow.paymentId}}
Headers: Authorization: Bearer {{flow.token}}
Assertions:
✓ Status code equals 200
✓ Body JSON data.status equals succeeded
✓ Body JSON data.amount equals 9999
✓ Body JSON data.transactionId equals {{flow.transactionId}}
Key takeaway: Notice how data chains through: Step 1 extracts paymentId and clientSecret, Step 2 uses both and extracts transactionId, Step 3 verifies using paymentId from Step 1 and transactionId from Step 2. Each step builds on the data from previous steps.
Flow Organization
- •Group by domain: Create flow groups per API or service (e.g., "Auth API", "Payment API", "User API")
- •Name flows descriptively: Use action-oriented names like "User Registration Flow" or "Order Checkout Sequence" instead of generic names
- •Name steps clearly: Each step name should describe its purpose (e.g., "Create User" instead of "Step 1")
- •Keep flows focused: A flow should test one scenario or workflow. Prefer multiple small flows over one large flow.
Variable Strategy
- •Use {{flow.varName}} for dynamic data: Tokens, IDs, timestamps, and anything that comes from a response should use the flow prefix
- •Use {{varName}} for config: Base URLs, API keys, and static configuration belong in group environments
- •Extract early: Set up extractions in the first steps that produce values needed later (e.g., auth tokens, resource IDs)
- •Use meaningful names: Name variables based on their purpose — authToken instead of var1
- •Prefer JSON Path: For structured JSON responses, JSON Path extraction is the most reliable and readable
- •Use regex sparingly: Reserve regex extraction for non-JSON responses or complex patterns that JSON Path can't handle
Environment Usage
- •Use environments for base URLs: Store {{base_url}} in environments so the same flow works against dev, staging, and production
- •Mark secrets as secret: Always use the secret flag for API keys, passwords, and tokens to prevent accidental exposure
- •Store test credentials: Keep test user credentials (email, password) in environments so flows are rerunnable without editing steps
Lifecycle Script Patterns
Pick the right hook for each kind of work. Lifecycle scripts (Before All / After All) run once per flow run; step pre/post scripts run for every individual request.
| Goal | Hook | Why |
|---|---|---|
| One-time auth setup | Before All | Fetch a token once and store with at.flow.set("token", ...) so every step reuses it via {{flow.token}} |
| Seed test data | Before All | Create fixtures or warm up caches before the actual flow steps execute |
| Reporting / notifications | After All | POST at.export.json() to a dashboard, send Slack/Discord webhook, archive HTML report |
| Cleanup | After All | Delete temporary resources or revoke session tokens after the flow finishes (success or failure) |
| Per-request dynamic header | Step Pre-Script | Use at.request.setHeader(...) to inject a fresh timestamp, signature, or correlation id on each request |
| Cross-step extraction | Step Post-Script | Parse the response with at.response.json() and stash values via at.flow.set for later steps |
Execution Tips
- •Test steps individually first: Use the Send button on each step to verify it works before running the entire flow
- •Use "Stop on Failure" during development: This helps you find and fix the first broken step without noise from subsequent failures
- •Disable "Stop on Failure" for regression: When running regression tests, let all steps execute to get the full picture of what's passing and failing
- •Use iterations for stability: Run 5-10 iterations to catch intermittent failures or performance regressions
- •Add delays for rate-limited APIs: If an API has rate limits, use the delay option to space out requests
Step Design
- •One action per step: Each step should do one thing. Don't try to combine multiple operations into a single request.
- •Add assertions: Use the Processors tab to add assertions that validate response status, body content, and headers. This turns your flows into automated tests.
- •Configure retries for flaky endpoints: If a step calls an unreliable service, set up retry behavior instead of re-running the entire flow.
- •Disable non-essential steps: Use the enable/disable toggle to temporarily skip steps during debugging without deleting them.