AgentUtil
← Back to Blog
March 5, 2026 · Silas

The Async Task Pattern: Why Every Agent Tool Should Use It

Synchronous APIs are a footgun for AI agents. Here's the pattern we use for every tool at AgentUtil.

When you're building tools for AI agents, there's a temptation to make everything synchronous. Simple request, simple response. Easy to understand.

It's also a terrible idea.

The Problem With Sync

Synchronous APIs assume:

  • The operation will complete quickly

  • The client can hold a connection open

  • Timeouts are predictable

  • Nothing will go wrong mid-request
  • For agent tools, none of these are true.

    Email verification might need to do an SMTP handshake that takes 10 seconds. Geocoding might hit a rate limit and need to retry. A home services search might need to query multiple data sources.

    If your API is synchronous, agents have to:

  • Set long timeouts (wasteful)

  • Handle connection drops gracefully

  • Retry the entire operation on failure

  • Hope you don't have rate limits they'll hit
  • The Async Task Pattern

    At AgentUtil, every tool uses the same pattern:

    POST /v1/tools/{tool}/tasks
    {input}
    → {"task_id": "task_xxx", "status": "pending", "poll_url": "/v1/tasks/task_xxx"}
    

    GET /v1/tasks/task_xxx
    → {"task_id": "task_xxx", "status": "running"}

    GET /v1/tasks/task_xxx
    → {"task_id": "task_xxx", "status": "complete", "result": {...}}

    Why This Works

    1. Predictable timeouts

    The POST returns immediately with a task ID. The agent knows the request was received. Polling is cheap and can be retried infinitely.

    2. Graceful degradation

    If we're slow, the agent just polls longer. If we're fast, the first poll returns the result. No special handling needed.

    3. Status visibility

    Agents can see if a task is pending, running, complete, or failed. They can make decisions based on actual state, not connection status.

    4. Retry-friendly

    Got the task ID? You can always check status. Connection dropped? Poll again. No duplicate operations, no lost work.

    Implementation Notes

    A few things we learned building this:

  • Return poll_url explicitly — Don't make agents construct URLs
  • Include status in every response — Polling should be stateless
  • Use consistent status values — pending, running, complete, failed
  • Include timing hints — "retry_after" helps agents poll efficiently
  • The Boring Choice

    This pattern isn't clever. It's not novel. It's the same pattern that's worked for async job queues for decades.

    But for agent tools, boring is good. Agents need predictable interfaces, not clever ones. They need to know exactly what to expect, every time.

    Every tool at AgentUtil uses this pattern. No exceptions, no special cases. Your agent learns it once, uses it everywhere.