Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

http_call

Outbound HTTP with templated url/body and optional HMAC signing.

rflow_version: 1
name: trade-hooks
 
config:
  port: 3940
  db_connection: ${DATABASE_URL}
 
networks:
  - name: ethereum
    chain_id: 1
    rpc: ${ETH_RPC}
 
contracts:
  Pool:
    abi: ./abis/uniswap-v3-pool.json
    network: ethereum
    address: "0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640"
 
secrets:
  API_KEY: ${BACKEND_API_KEY}
  HOOK_KEY: ${BACKEND_HMAC_KEY}
 
workflows:
  trade-hook:
    trigger:
      event:
        contract: Pool
        name: Swap
        network: ethereum
    steps:
      - id: notify-backend
        http_call: 
          url: ${BACKEND_URL}/hooks/trade
          method: POST
          headers: 
            x-api-key: "${{ secrets.API_KEY }}"
          hmac: "${{ secrets.HOOK_KEY }}"
          body: 
            trader: "${{ trigger.args.sender }}"
            amount: "${{ trigger.args.amount0 }}"
            src: "${{ trigger.tx_hash }}"

Fields

FieldRequiredDescription
url✅Templated request url
methodDefaults to POST when a body is set, GET otherwise
headersMap of headers; values are templates
bodyA YAML mapping rendered recursively (every string leaf is a template), then sent as JSON
hmacSecret used to sign the request (see below)

HMAC signing

When hmac: is set, rflow computes HMAC-SHA256 over the raw request body bytes (the empty byte string for body-less requests) and sends the lowercase hex digest as the x-rflow-signature header. Your backend verifies with the shared secret, typically one from secrets: so it never appears in logs.

# receiver side (python)
expected = hmac.new(key, request_body_bytes, hashlib.sha256).hexdigest()
assert hmac.compare_digest(expected, request.headers["x-rflow-signature"])

Idempotency and recovery

An HTTP request can reach the receiver before rflow records its outcome. A crash or retry in that window can send it again. HMAC proves authenticity; it does not prevent duplicate effects.

For a consequential request, pass a stable key that the receiver atomically deduplicates with its own write. For one request step in a run, for example:

headers:
  x-idempotency-key: "${{ run.id }}:notify-backend"

Use different keys for different steps and foreach items. If the same business operation must remain deduplicated across newly created runs, live replays or a database restore, use its stable business identity instead of a run UUID. Match the receiver's key-retention period to your retry and recovery requirements.

Dry-run logs the prepared native request without sending it. This does not sandbox a separate command: step that makes its own HTTP calls.

Output and failure mapping

A 2xx response succeeds with output:

{ "status": 200, "body": { ... } }

body is parsed as JSON when possible, kept as a string otherwise, so ${{ steps.notify-backend.output.body.some_field }} works against JSON APIs.

Non-2xx responses map into the failure taxonomy: 5xx and 429 are retryable (rate_limited / retryable kinds) under a retry: block; 4xx are terminal. Timeouts are rpc_timeout-class and retryable.

rflow_version: 1
name: trade-hooks
 
config:
  port: 3940
  db_connection: ${DATABASE_URL}
 
workflows:
  poll-backend:
    trigger:
      cron: { expression: "*/5 * * * *" }
    steps:
      - id: notify-backend
        http_call:
          url: ${BACKEND_URL}/hooks/trade
        retry: 
          max_attempts: 3
          backoff: 10s