Anatomy of a REST Tool

# tools/stripe.yaml
tools:
  stripe_list_customers:
    type: rest
    description: "List Stripe customers, optionally filtered by email"
    url: "https://api.stripe.com/v1/customers"
    method: GET
    auth:
      type: bearer
      token: "${STRIPE_SECRET_KEY}"
    params:
      email: {type: string, required: false}
      limit: {type: integer, default: 10, max: 100}
    output_path: "$.data"    # JSONPath to extract from response
    
  stripe_create_payment_link:
    type: rest
    description: "Create a Stripe payment link for a product"
    url: "https://api.stripe.com/v1/payment_links"
    method: POST
    auth:
      type: bearer
      token: "${STRIPE_SECRET_KEY}"
    body:
      line_items:
        - price: "{{ price_id }}"
          quantity: "{{ quantity | default(1) }}"

CRM Tools (HubSpot)

# tools/hubspot.yaml
tools:
  hubspot_create_contact:
    type: rest
    url: "https://api.hubapi.com/crm/v3/objects/contacts"
    method: POST
    auth:
      type: bearer
      token: "${HUBSPOT_API_KEY}"
    body:
      properties:
        email: "{{ email }}"
        firstname: "{{ first_name }}"
        lastname: "{{ last_name }}"
        company: "{{ company | default('') }}"
        
  hubspot_update_deal_stage:
    type: rest
    url: "https://api.hubapi.com/crm/v3/objects/deals/{{ deal_id }}"
    method: PATCH
    auth:
      type: bearer
      token: "${HUBSPOT_API_KEY}"
    body:
      properties:
        dealstage: "{{ stage }}"

The Agent

# revenue-ops-agent.yaml
name: revenue-ops
description: "Revenue operations automation across Stripe, HubSpot, SendGrid"

llm:
  model: gpt-4o
  temperature: 0

system_prompt: |
  You are a revenue operations agent. Automate tasks across payment,
  CRM, and email systems. Always confirm before creating or deleting data.
  For financial operations, log every action with amounts and IDs.

tools:
  - stripe_list_customers
  - stripe_create_payment_link
  - hubspot_create_contact
  - hubspot_update_deal_stage
  - sendgrid_send_transactional

approval_required:
  - stripe_create_payment_link   # Must confirm before creating payment links
  - sendgrid_send_transactional  # Must confirm before sending emails

Example Tasks

# Natural language tasks the agent can handle:
openclaw run --config revenue-ops-agent.yaml \
  "Find all Stripe customers who signed up in January 2025 and create HubSpot contacts for each"

openclaw run --config revenue-ops-agent.yaml \
  "Create a payment link for our Pro plan ($99/mo) and send it to alice@example.com"

openclaw run --config revenue-ops-agent.yaml \
  "Move all HubSpot deals in 'Proposal Sent' stage that are older than 30 days to 'Needs Follow-Up'"

OAuth 2.0 Support

# For OAuth-protected APIs (e.g., Google APIs)
tools:
  google_calendar_list_events:
    type: rest
    url: "https://www.googleapis.com/calendar/v3/calendars/primary/events"
    method: GET
    auth:
      type: oauth2
      client_id: "${GOOGLE_CLIENT_ID}"
      client_secret: "${GOOGLE_CLIENT_SECRET}"
      token_url: "https://oauth2.googleapis.com/token"
      scopes: ["https://www.googleapis.com/auth/calendar.readonly"]
      token_cache: ~/.openclaw/tokens/google.json   # Cached refresh token

What This Agent Does

This tutorial builds an agent that integrates with external APIs — reading data from REST endpoints, posting updates, polling for status changes, and composing results from multiple APIs into a unified view. The example connects to a project management API (GitHub Issues) and a notification API (Slack-compatible webhook), but the pattern applies to any REST API combination your workflow needs.

Step 1 — Agent Configuration

Create api-integration-agent.yaml:

agent:
  name: api-integration
  description: Integrates data from multiple external APIs
  system_prompt: |
    You are an API integration specialist. You can call external APIs
    using the http_request tool. When working with APIs:
    - Always check response status codes — treat 4xx as user errors, 5xx as API errors
    - Respect rate limits: if you receive a 429, wait the time specified in
      Retry-After header before retrying (default 60s if header absent)
    - Never log or display raw credentials from API responses
    - For paginated endpoints, retrieve all pages before processing unless
      explicitly told to limit results

llm:
  model: claude-3-5-sonnet-20241022
  temperature: 0.1
  max_tokens: 4096

tools:
  - name: http_request
    enabled: true
    config:
      allowed_domains:
        - api.github.com
        - hooks.slack.com
      timeout: 30
      max_response_size: 1048576  # 1MB
  - name: file_write
    enabled: true
    config:
      allowed_dirs:
        - ./output

prompt_vars:
  github_token: "${GITHUB_TOKEN}"
  slack_webhook: "${SLACK_WEBHOOK_URL}"

Step 2 — Fetch Data from GitHub

openclaw run --config api-integration-agent.yaml   --task "Fetch all open issues from the repository 'owner/repo' using the GitHub API.
  Use the Authorization header: 'Bearer {{github_token}}'.
  Filter to issues labeled 'bug' created in the last 7 days.
  Sort by creation date, newest first.
  Save the results to output/recent-bugs.json"

Step 3 — Post a Digest to Slack

openclaw run --config api-integration-agent.yaml   --task "Fetch recent bugs from GitHub (same as before) and post a summary
  to the Slack webhook at {{slack_webhook}}.
  Format the Slack message as:
  *Daily Bug Report — [date]*
  Total new bugs: N
  - [issue title] - [assignee or Unassigned] - [URL]
  (list top 5, then 'and N more' if there are more than 5)
  Send the POST request with Content-Type: application/json"

Step 4 — Cross-API Data Joining

One of the most powerful use cases for an API integration agent is joining data across systems that don't natively integrate. For example, fetch a list of tickets from your support system, look up the assigned engineer's details from your HR API, and join them to produce a workload report by person. The agent handles the API calls, parsing, and joining logic using natural language instructions rather than requiring custom code for each integration.

Step 5 — Polling and Change Detection

For monitoring workflows, run the agent on a schedule to detect changes. On each run, the agent fetches the current API state, compares it to a saved snapshot from the previous run, identifies what changed, and triggers notifications if changes match alert criteria. Save the state to a file after each successful run. The agent performs the comparison and decision automatically — no custom change detection code required.

Troubleshooting

If the agent cannot access an API, verify the domain is in the allowed_domains list — the agent will refuse to contact domains not explicitly permitted. If you see authentication errors, confirm your environment variables are set: echo $GITHUB_TOKEN. For APIs requiring OAuth flows, generate a personal access token or service account token offline and inject it as an environment variable rather than implementing OAuth in the agent itself.

Handling API Versioning and Breaking Changes

APIs evolve, and version changes can silently break your integration. Defensively specify the API version in every request — use the Accept: application/vnd.api+json;version=2 header or the version-specific URL path rather than relying on the default. When an API deprecates a version, you'll receive a deprecation warning in the response headers before it breaks — add a check to your agent's instructions: "If you see a Deprecation header in any response, include it in your output as a warning that requires attention."

Test your integration with the API's sandbox or staging environment before pointing it at production. Most API providers offer sandbox environments with the same interface but simulated data. Run your full integration workflow in the sandbox first, verify the output is correct, then switch the base URL to production. This practice prevents discovering integration bugs by causing real data mutations in production systems.

Idempotent Operations

When an API call fails partway through a multi-step operation, you need to know whether to retry or continue. Design your agent's API operations to be idempotent where possible — the same operation performed twice should have the same effect as performing it once. For APIs that support idempotency keys (many payment and messaging APIs do), include them in create operations. Instruct the agent: "Before creating a resource, check if a resource with the same identifier already exists — if it does, use the existing one rather than creating a duplicate."

Building a Local API Mock for Testing

For testing your integration agent without making real API calls, create a simple local mock server that returns pre-defined responses for your expected API calls. Configure the agent to point at localhost:8080 during testing by setting a test-specific config file with the mock server URLs in the allowed_domains list. Testing against a mock lets you verify the agent's data processing and decision logic without rate limit concerns, authentication issues, or costs from real API calls. Once the agent behaves correctly against the mock, test against the real API's sandbox for final validation.

Building Reusable API Connector Modules

Once you've built and tested an API integration for a specific service, package the agent configuration and task templates as a reusable connector module. Store the domain-specific configuration — allowed domains, authentication pattern, known endpoint paths — in a dedicated YAML file. Anyone who needs to build a workflow involving that API starts from your connector rather than from scratch. Over time, your team builds a library of reliable, tested API connectors that dramatically accelerates new integration development. Document each connector's capabilities, limitations, and any known rate limiting constraints so users can choose the right approach for their use case.

Webhook-Triggered Integration

Rather than polling APIs on a schedule, configure webhooks to trigger your integration agent reactively. When a new GitHub issue is created, a webhook fires — your integration agent receives the payload, enriches it with data from other APIs (for example, adding team member context from your HR system or adding priority score from your product roadmap), and posts the enriched notification to the relevant Slack channel. Reactive webhook-driven integrations are more efficient than polling (no unnecessary API calls when nothing has changed) and are more timely (the action happens within seconds of the triggering event rather than waiting for the next scheduled poll).