Updated September 2026 · Technical Guide & Agent Patterns

How to Send a Signing Packet from an Agent Without Webhooks

AI agents operating in IDEs, local development environments, and ephemeral serverless workers do not possess public IP ingress. Here is the complete engineering architecture to dispatch legally binding agreements and verify signature completion with pure REST requests and zero webhook overhead.

Michael Beckett
Michael Beckett

Founder, Signbee

TL;DR

Autonomous agents and command-line LLM runners often assume they need inbound webhook listeners, reverse proxy tunnels, and ingress infrastructure to orchestrate e-signatures. Signbee supports webhooks on Pro and Business plans, but they are completely optional: an agent dispatches an agreement with POST /api/v1/send, Signbee manages recipient email delivery and certificate generation, and the agent polls or inspects the live status whenever needed via GET /api/v1/documents/{id}. This guide explains how to build reliable, webhook-free agent signing workflows.

The Webhook Tax on Autonomous AI Agents

Most traditional e-signature platforms (DocuSign, Adobe Acrobat Sign, Dropbox Sign) were architected around asynchronous webhook relays. In that legacy model, your application must host an internet-accessible HTTP endpoint, verify cryptographic HMAC payload signatures, handle connection dropouts, manage firewall ingress rules, and maintain database retry tables.

For autonomous AI agents running inside developer IDEs (like Cursor or Windsurf), local desktop runtimes (like Claude Desktop), or ephemeral serverless containers (like AWS Lambda, Modal, or Cloudflare Workers), this architecture breaks down immediately. An agent running on a developer's laptop or within a sandboxed execution container simply does not have an inbound public URL to accept push webhooks.

Forcing an agent to stand up tunnel proxies (such as ngrok or Cloudflare Tunnels) creates fragile network dependencies, requires separate authentication tokens, and introduces security vulnerabilities into local environments. The cleaner architectural solution is straightforward: let the agent dispatch the signing packet directly over HTTP and check the state on demand.

Agent Environment Matrix: Webhook vs. REST Polling

The table below contrasts why synchronous REST dispatch and on-demand polling is the superior architecture across modern AI agent execution contexts:

Agent Execution RuntimeWebhook FeasibilityREST Polling FeasibilityRecommended Pattern
Desktop IDE (Cursor / Windsurf)Impossible without ngrok proxyNative (Direct outbound fetch)Single-step send + on-demand GET
Local CLI / Terminal ToolsRequires local port binding & router NATNative (HTTP client)Exponential backoff loop
Serverless (AWS Lambda / Modal)Requires separate API Gateway + DB routeNative (Stateless invocations)Step Functions / Scheduled poll
Multi-Agent Swarm (LangGraph)Complex distributed routingZero state synchronizationAgent node state inspection

Latency & Infrastructure Cost Analysis

Engineers often wonder whether polling increases cloud compute costs. Because human signers review contracts over minutes or hours rather than milliseconds, an intelligent exponential backoff algorithm uses fewer than 15 total HTTP requests over a 24-hour window:

Ingress ArchitectureInfrastructure RequiredMonthly Maintenance CostAgent Complexity
Inbound Webhook ServerPublic ALB, TLS cert, DDOS protection$35 – $120 / monthHigh (Ingress routing & secrets)
Reverse Proxy TunnelNgrok Pro / Cloudflare Named Tunnel$20 – $40 / monthModerate (Process lifecycle management)
Pure REST GET PollingZero (Outbound HTTPS only)$0.00 / monthZero (1 simple fetch call)

The Live API Pattern: Send and Check

The entire document workflow requires only two core endpoints: POST /api/v1/send to create and dispatch the signing packet, and GET /api/v1/documents/{id} to inspect its progress.

1. Dispatching the Signing Packet

The agent submits markdown (or a PDF URL) plus recipient_name and recipient_email. With an API key, the sender is pre-verified and does not need to be supplied in the body:

POST /api/v1/send
curl -X POST https://signb.ee/api/v1/send \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{
    "markdown": "# Mutual NDA\n\nThis agreement between...",
    "recipient_name": "Bob Smith",
    "recipient_email": "bob@acme.com"
  }'

Signbee immediately returns the document_id, status pending_recipient, sender, recipient, and expires_at:

Response 200 OK
{
  "document_id": "cmm7890123456",
  "status": "pending_recipient",
  "sender": "Alice Chen",
  "recipient": "Bob Smith",
  "expires_at": "2026-04-19T12:00:00.000Z"
}

2. Checking Status On Demand

Whenever the agent resumes execution or needs to confirm completion before continuing a workflow, it queries the document endpoint:

GET /api/v1/documents/{id}
curl https://signb.ee/api/v1/documents/cmm7890123456 \
  -H "Authorization: Bearer YOUR_API_KEY"

When both parties have signed, the status transitions to signed and signed_pdf_url contains the certified PDF. The cryptographic SHA-256 certificate is permanently sealed into that PDF:

Response (Signed)
{
  "document_id": "cmm7890123456",
  "status": "signed",
  "title": "Mutual NDA",
  "sender": { "name": "Alice Chen", "signed_at": "2026-08-30T12:04:12.000Z" },
  "recipient": { "name": "Bob Smith", "signed_at": "2026-08-30T12:04:12.000Z" },
  "original_pdf_url": "https://signb.ee/storage/original.pdf",
  "signed_pdf_url": "https://signb.ee/storage/signed_sealed.pdf",
  "signing_url": null,
  "expires_at": "2026-09-29T12:00:00.000Z",
  "created_at": "2026-08-30T12:00:00.000Z"
}

TypeScript Implementation: Exponential Backoff with Jitter

To prevent hammering the API when waiting for human counterparty signatures, agents should implement an exponential backoff loop with randomized jitter:

TypeScript — poll-contract.ts
interface ContractStatus {
  document_id: string;
  status: "pending_recipient" | "signed" | "expired" | "declined";
  signed_pdf_url?: string;
}

export async function waitForContractSignature(
  documentId: string,
  apiKey: string,
  maxWaitMinutes: number = 60
): Promise<ContractStatus> {
  const startTime = Date.now();
  const maxWaitMs = maxWaitMinutes * 60 * 1000;
  let delay = 10000; // Start at 10 seconds

  while (Date.now() - startTime < maxWaitMs) {
    const res = await fetch(`https://signb.ee/api/v1/documents/${documentId}`, {
      headers: { Authorization: `Bearer ${apiKey}` },
    });

    if (res.ok) {
      const data: ContractStatus = await res.json();
      if (data.status === "signed") {
        return data;
      }
      if (data.status === "declined" || data.status === "expired") {
        throw new Error(`Contract failed with status: ${data.status}`);
      }
    }

    // Exponential backoff capped at 2 minutes with 20% randomized jitter
    const jitter = delay * (0.8 + Math.random() * 0.4);
    await new Promise((resolve) => setTimeout(resolve, jitter));
    delay = Math.min(delay * 1.5, 120000);
  }

  throw new Error("Signature wait window timed out");
}

Python Implementation: Async Httpx Polling

For Python AI agents built on LangGraph, AutoGen, or CrewAI, here is the asynchronous equivalent using httpx:

Python 3 — async_poller.py
import asyncio
import random
import httpx

async def poll_until_signed(document_id: str, api_key: str, timeout_seconds: int = 3600):
    url = f"https://signb.ee/api/v1/documents/{document_id}"
    headers = {"Authorization": f"Bearer {api_key}"}
    delay = 15.0

    async with httpx.AsyncClient() as client:
        start_time = asyncio.get_event_loop().time()
        while asyncio.get_event_loop().time() - start_time < timeout_seconds:
            resp = await client.get(url, headers=headers)
            if resp.status_code == 200:
                data = resp.json()
                if data.get("status") == "signed":
                    return data["signed_pdf_url"]
                if data.get("status") in ["declined", "expired"]:
                    raise RuntimeError(f"Contract {document_id} ended: {data.get('status')}")

            # Sleep with decorrelated jitter
            jittered_sleep = delay * (0.85 + random.random() * 0.3)
            await asyncio.sleep(jittered_sleep)
            delay = min(delay * 1.6, 180.0)

    raise TimeoutError(f"Contract {document_id} was not signed within timeout period")

State Persistence Across Agent Context Wipes

A common challenge in LLM application design is memory context truncation. When an agent converses with a user over hours, the context window can be pruned, losing in-memory variables like the document_id.

In webhook-free architectures, solving this is trivial: the agent commits a single structured JSON record to local disk:

JSON — .agent/state/active_contracts.json
{
  "contract_id": "cmm7890123456",
  "type": "Contractor Agreement",
  "recipient_email": "contractor@example.com",
  "dispatched_at": "2026-09-04T10:00:00Z",
  "status": "pending_recipient"
}

When the user later prompts: "Did Sarah sign the contract yet?", the agent reads the local file, executes a single GET /api/v1/documents/cmm7890123456, and answers authoritatively without any background servers or webhooks running.

MCP Tools: Native Agent Integration

If your agent connects via the Model Context Protocol (MCP) or uses standard agent skill packages, you do not need to write custom REST wrappers.

To install the MCP server or the agent skill, run:

Terminal
# Install and run the MCP Server
npx -y signbee-mcp

# Or install the Agent Skill
npx skills add signbee/skill

The Signbee MCP server exposes two dedicated tools designed specifically for LLM tool-calling:

  • send_document: Takes dynamic markdown agreement text, sender information, and recipient details to generate and dispatch a signing packet.
  • send_document_pdf: Takes a URL to an existing pre-compiled PDF document and dispatches it through the signing ceremony.
mcp.json / claude_desktop_config.json
{
  "mcpServers": {
    "signbee": {
      "command": "npx",
      "args": ["-y", "signbee-mcp"],
      "env": {
        "SIGNBEE_API_KEY": "your_api_key_here"
      }
    }
  }
}

Frequently Asked Questions

Why do AI agents prefer polling over webhooks in local or serverless environments?

AI agents running in desktop environments (like Cursor, Claude Desktop, or Windsurf), local command-line interfaces, or ephemeral serverless functions (such as AWS Lambda or modal containers) lack a persistent public IP address or ingress listener. Configuring reverse proxy tunnels like ngrok or Cloudflare Tunnels adds fragile dependencies and security holes. By utilizing idempotent GET polling against Signbee's REST API, the agent remains completely stateless, avoids opening inbound network ports, and queries agreement state only when relevant to its reasoning loop.

How should an AI agent implement polling without hitting API rate limits?

Agents should avoid naive tight loops (such as polling every 1 second). Instead, an agent should implement randomized exponential backoff with decorrelated jitter (for example, starting at 15 seconds, scaling to 30, 60, and 120 seconds up to a maximum 5-minute interval). Signbee permits 100 requests per minute per API key on standard tiers, which easily supports hundreds of concurrent documents when proper exponential backoff algorithms are applied.

How does an agent persist document state across session context wipes?

When an agent operates in a conversational interface or long-running multi-agent workflow, its working context window can be cleared or truncated. To preserve signing workflows, the agent should record the returned document_id in a local persistent store (such as SQLite, a JSON file, or an agent memory scratchpad). When invoked hours or days later with "check contract status", the agent reads the stored ID and performs a single GET request to determine if the signed PDF is available.

Which MCP tools does Signbee provide for webhook-free AI agents?

The Signbee MCP server provides two dedicated tools: send_document (which takes dynamic markdown contract content, sender information, and recipient email) and send_document_pdf (which accepts an existing PDF URL). Both tools return the document_id and immediate dispatch metadata, enabling the agent to either complete its turn immediately or check completion state subsequently without managing webhook listeners.

How much compute and latency overhead does on-demand polling add compared to push webhooks?

On-demand polling adds negligible compute overhead (typically less than 20 milliseconds per HTTP request) because Signbee's document status endpoint is served from low-latency edge caches. In terms of latency, because legal document signing ceremonies involve human contemplation taking minutes to days, checking status on natural workflow boundaries or via a 30-second backoff loop introduces zero perceptible delay compared to complex push webhook pipelines.

Zero friction e-signatures for agents and developers.

Last updated: September 2026 · Michael Beckett is the founder of Signbee and B2bee Ltd.

Related resources