Skip to main content

HTTP Request Step

The HTTP Request step calls external APIs and web services. Use it to fetch data, send notifications, update external systems, or integrate with any service that has a REST API. Unlike agent tools (which agents use intelligently), HTTP Request gives you explicit control over API calls.
When to use HTTP Request vs. Agent Tools: Use HTTP Request when you need explicit control over the API call (specific endpoint, exact parameters, precise error handling). Use Agent Tools when you want the agent to intelligently decide when and how to call APIs.

How HTTP Requests Work

HTTP Requests send data to external endpoints and return responses that subsequent steps can use:
Trigger: User requests data

HTTP Request: GET https://api.example.com/users/123

Response: User data

Agent: Format response for user

Configuration

Request Settings

method
enum
required
HTTP methodOptions:
  • GET - Retrieve data (most common)
  • POST - Create new resource or submit data
  • PUT - Update existing resource (replace)
  • PATCH - Update existing resource (modify)
  • DELETE - Remove resource
  • HEAD - Get headers only (no body)
  • OPTIONS - Check allowed methods
Examples:
  • GET: Fetch user profile, list orders
  • POST: Create customer, send notification
  • PUT: Update entire record
  • PATCH: Update specific fields
  • DELETE: Remove item, cancel subscription
url
string
required
API endpoint URLCan include variables from previous steps:
https://api.example.com/users/${trigger.user_id}
https://api.crm.com/contacts/${agent.email}
Best practices:
  • Always use HTTPS (not HTTP)
  • Include API version in URL if available
  • Verify URL is correct before deploying
headers
object
Request headersCommon headers:
{
  "Content-Type": "application/json",
  "Authorization": "Bearer ${api_key}",
  "User-Agent": "quiva.ai-Flow/1.0",
  "Accept": "application/json"
}
Can include variables:
{
  "X-Customer-ID": "${customer.id}",
  "X-Request-ID": "${flow.execution_id}"
}
body
object
Request body (for POST, PUT, PATCH)JSON body:
{
  "name": "${form.name}",
  "email": "${form.email}",
  "data": {
    "source": "quiva",
    "timestamp": "${now}"
  }
}
Can reference entire objects:
${agent.output}
Note: Body is automatically JSON-encoded. For form-data or other formats, use appropriate Content-Type header.
queryParams
object
URL query parametersExample:
{
  "page": "1",
  "limit": "50",
  "filter": "active",
  "sort": "created_desc"
}
Automatically appended to URL:
https://api.example.com/items?page=1&limit=50&filter=active&sort=created_desc
Can use variables:
{
  "customer_id": "${customer.id}",
  "date_from": "${start_date}"
}

Authentication

authentication
object
Authentication configurationTypes:
Most common for modern APIs
{
  "type": "bearer",
  "token": "${secrets.api_key}"
}
Adds header: Authorization: Bearer <token>
Security: Store API keys and secrets in Secrets Manager, not directly in flows. Reference them with ${secrets.key_name}.

Response Handling

Response Structure

HTTP Request returns:
{
  "status": 200,
  "statusText": "OK",
  "headers": {
    "content-type": "application/json",
    "x-rate-limit-remaining": "99"
  },
  "body": {
    // Parsed response body
  },
  "rawBody": "...", // Raw response text
  "duration": 145 // Request duration in ms
}

Accessing Response Data

Reference response in subsequent steps:
// Status code
${http_request.status}

// Response body (entire object)
${http_request.body}

// Specific fields
${http_request.body.data.id}
${http_request.body.results[0].name}

// Headers
${http_request.headers.x-rate-limit-remaining}

// Request duration
${http_request.duration}

Error Handling

retryConfig
object
Automatic retry configuration
{
  "enabled": true,
  "maxRetries": 3,
  "retryDelay": 1000,
  "retryOn": [500, 502, 503, 504],
  "backoffMultiplier": 2
}
Settings:
  • maxRetries: Number of retry attempts
  • retryDelay: Initial delay between retries (ms)
  • retryOn: Status codes that trigger retry
  • backoffMultiplier: Increase delay each retry (exponential backoff)
Example: First retry after 1s, second after 2s, third after 4s
timeout
number
default:"30000"
Request timeout in millisecondsRequest fails if no response within timeout period.Recommendations:
  • Fast APIs: 5000ms (5 seconds)
  • Standard APIs: 30000ms (30 seconds)
  • Slow APIs: 60000ms (60 seconds)
  • Long operations: 120000ms+ (2+ minutes)
failOn
array
Status codes that should be treated as failures
[400, 401, 403, 404, 500, 502, 503]
By default, only 5xx codes fail. Use this to also fail on specific 4xx codes.

Error Response

When request fails:
{
  "success": false,
  "error": {
    "message": "Request failed with status 404",
    "status": 404,
    "statusText": "Not Found",
    "body": {
      // Error response from API
    }
  }
}
Access in conditions:
${http_request.success} == false
${http_request.error.status} == 404

Common Patterns

Get external data before agent processes
Trigger: User inquiry

HTTP Request: GET customer data from CRM
  URL: https://api.crm.com/customers/${trigger.customer_id}

Agent: Personalize response using customer data
  Context: ${http_request.body}
Use when: Agent needs context from external systems
Agent generates content, HTTP sends to external system
Agent: Generate article content

HTTP Request: POST to CMS
  URL: https://api.cms.com/articles
  Body: ${agent.output}

Response: Published article URL
Use when: Agent creates content for external systems
Chain multiple API calls using previous responses
HTTP Request 1: GET user ID by email

HTTP Request 2: GET user orders
  URL: https://api.shop.com/users/${http_1.body.id}/orders

HTTP Request 3: GET order details
  URL: https://api.shop.com/orders/${http_2.body.orders[0].id}
Use when: Need data from multiple endpoints
Call multiple APIs simultaneously (not sequential)
Trigger

├─ HTTP Request 1: Get user profile
├─ HTTP Request 2: Get order history  
└─ HTTP Request 3: Get preferences

Agent: Combine all data and respond
Use when: Need data from multiple sources, order doesn’t matterNote: Configure parallel execution in flow settings
Respond to webhook with HTTP call
Webhook Trigger: Payment received

HTTP Request: Acknowledge to payment provider
  URL: ${trigger.body.callback_url}
  Body: {"status": "received"}

Agent: Process payment data
Use when: Webhook requires acknowledgment
Retry failed requests with backoff
HTTP Request: Call external API
  Retry: 3 attempts, exponential backoff

Condition: Check success
├─ If success → Continue
└─ If failed → Notify admin + Alternative flow
Use when: External APIs may have temporary failures
Check and respect rate limits
HTTP Request: Call API

Condition: Check rate limit header
├─ If ${http.headers.x-rate-limit-remaining} < 10
│   ↓
│   Delay: Wait 60 seconds
└─ Else → Continue normally
Use when: API has rate limits you need to respect
Iterate through paginated results
HTTP Request 1: Get page 1

Map: Process page 1 items

Condition: Has next page?
├─ If ${http_1.body.next_page} exists
│   ↓
│   HTTP Request 2: Get next page
│   ↓
│   Map: Process page 2 items
│   ↓
│   (Repeat as needed)
└─ Else → Complete
Use when: API returns paginated results

Real-World Examples

Example 1: CRM Contact Creation

Scenario: Create or update contact in CRM after agent qualifies lead
Agent: Qualify lead
  Output: {qualified: true, contact: {...}}

Condition: Is qualified?
↓ (true)
HTTP Request: POST to CRM
  URL: https://api.crm.com/contacts
  Headers: {
    "Authorization": "Bearer ${secrets.crm_api_key}",
    "Content-Type": "application/json"
  }
  Body: {
    "name": "${agent.output.contact.name}",
    "email": "${agent.output.contact.email}",
    "company": "${agent.output.contact.company}",
    "score": ${agent.output.contact.score},
    "source": "quiva",
    "custom_fields": {
      "qualification_notes": "${agent.output.notes}"
    }
  }

Response: Contact created with ID

Agent: Send confirmation email

Example 2: Slack Notification

Scenario: Send notification to Slack when high-value order placed
Trigger: Order received

Condition: Amount > $10,000?
↓ (true)
HTTP Request: POST to Slack
  URL: https://hooks.slack.com/services/${secrets.slack_webhook}
  Body: {
    "text": "🎉 High-value order received!",
    "blocks": [
      {
        "type": "section",
        "text": {
          "type": "mrkdwn",
          "text": "*Order Details*\nAmount: $${trigger.amount}\nCustomer: ${trigger.customer_name}\nOrder ID: ${trigger.order_id}"
        }
      }
    ]
  }

Example 3: Email via SendGrid

Scenario: Send personalized email after agent generates content
Agent: Generate welcome email
  Output: {subject: "...", body: "..."}

HTTP Request: POST to SendGrid
  URL: https://api.sendgrid.com/v3/mail/send
  Headers: {
    "Authorization": "Bearer ${secrets.sendgrid_api_key}",
    "Content-Type": "application/json"
  }
  Body: {
    "personalizations": [
      {
        "to": [{"email": "${customer.email}"}],
        "subject": "${agent.output.subject}"
      }
    ],
    "from": {"email": "[email protected]"},
    "content": [
      {
        "type": "text/html",
        "value": "${agent.output.body}"
      }
    ]
  }

Example 4: Database Query via API

Scenario: Query database for customer order history
Trigger: Customer inquiry

HTTP Request: GET order history
  URL: https://api.company.com/orders
  Query Params: {
    "customer_id": "${trigger.customer_id}",
    "limit": "10",
    "sort": "created_desc"
  }
  Headers: {
    "Authorization": "Bearer ${secrets.api_key}"
  }

Agent: Answer customer question using order data
  Context: "Customer orders: ${http_request.body.orders}"

Example 5: Payment Processing

Scenario: Process payment through Stripe
Agent: Validate payment details
  Output: {valid: true, amount: 99.99}

Condition: Payment valid?
↓ (true)
HTTP Request: POST to Stripe
  URL: https://api.stripe.com/v1/payment_intents
  Headers: {
    "Authorization": "Bearer ${secrets.stripe_secret_key}",
    "Content-Type": "application/x-www-form-urlencoded"
  }
  Body: {
    "amount": ${agent.output.amount * 100},
    "currency": "usd",
    "customer": "${customer.stripe_id}",
    "metadata": {
      "order_id": "${trigger.order_id}"
    }
  }

Condition: Payment successful?
├─ If status == 200 → Confirmation email
└─ If failed → Retry or notify customer

Best Practices

Use HTTPS

Always use HTTPS endpoints (not HTTP) for security. API keys and data are encrypted in transit.

Store Secrets Securely

Never hardcode API keys. Use Secrets Manager and reference with ${secrets.key_name}.

Handle Errors

Always add error handling with Conditions. Check status codes and have fallback flows.

Set Appropriate Timeouts

Set timeouts based on expected API response time. Don’t leave default if API is slow.

Use Retry for Transient Failures

Enable retry for 5xx errors and network issues. Use exponential backoff to avoid overwhelming APIs.

Validate Responses

Check that response has expected structure before using data. Use Conditions to verify.

Respect Rate Limits

Check rate limit headers. Add delays if approaching limits.

Log for Debugging

Monitor HTTP requests in flow execution logs. Review failures to improve error handling.

Troubleshooting

Causes:
  • API key missing or incorrect
  • Token expired
  • Wrong authentication type
Solutions:
  • Verify API key in Secrets Manager
  • Check authentication configuration
  • Regenerate API key if needed
  • For OAuth, check token expiration
Causes:
  • Wrong URL or endpoint
  • Resource doesn’t exist
  • Variable in URL not populated
Solutions:
  • Double-check URL spelling
  • Verify endpoint in API docs
  • Check variable references: ${trigger.id} not ${id}
  • Test URL manually in browser/Postman
Causes:
  • API is down
  • Invalid request body
  • Server-side bug
Solutions:
  • Check API status page
  • Verify request body structure
  • Enable retries for transient failures
  • Contact API provider if persistent
Causes:
  • API too slow
  • Timeout too short
  • Network issues
Solutions:
  • Increase timeout setting
  • Check API performance/status
  • Optimize API call (reduce data)
  • Use async/background processing if possible
Causes:
  • Wrong variable path
  • Response not JSON
  • API returned error
Solutions:
  • Check execution logs for actual response
  • Verify response is JSON (Content-Type: application/json)
  • Check for error response instead of success
  • Try ${http.rawBody} to see exact response
Note: CORS errors don’t apply to QuivaWorks flows (server-side). If you see CORS errors:
  • You’re likely testing from browser console
  • Flows run server-side and don’t have CORS restrictions
  • The API might not allow your testing origin
Solution: CORS won’t affect production flows. Ignore when testing server-side.

Security Best Practices

Do:
  • Store keys in Secrets Manager
  • Rotate keys regularly (every 90 days)
  • Use separate keys for dev/staging/prod
  • Revoke immediately if compromised
  • Monitor key usage
Don’t:
  • Hardcode keys in flows
  • Share keys in chat or email
  • Use same key across environments
  • Commit keys to version control
Always validate and sanitize user input before including in API calls:
Agent: Validate input
  - Check data types
  - Sanitize strings
  - Validate email format
  - Check value ranges

Condition: Valid?
↓ (true)
HTTP Request: Use validated data
Never pass raw user input directly to APIs without validation.
Implement your own rate limiting:
  • Track API calls per user/session
  • Add delays if approaching limits
  • Cache responses when possible
  • Use webhooks instead of polling
Don’t expose sensitive information in errors:Bad:
"Error: API key abc123xyz is invalid"
Good:
"Error: Authentication failed"

Next Steps