April 2026 · Integration Guide

Hermes Agent + Signbee: Persistent Agents That Sign Contracts

Hermes Agent by Nous Research is the most capable open-source persistent agent available. It lives on your server, remembers everything, builds its own skills, and talks to you on Telegram. Here's how to give it the power to send documents for e-signing.

Hermes Agent and Signbee shaking hands over a partnership agreement, with Telegram, Discord, and Slack icons above

TL;DR

Connect Nous Research's Hermes Agent to Signbee via MCP to enable AI-driven contract signing from Telegram or CLI. The integration uses the signbee-mcp server as a tool, letting Hermes draft NDAs, SOWs, and service agreements autonomously. Setup takes under 5 minutes with npx signbee-mcp.

Nous Research released Hermes 3 in August 2024, achieving state-of-the-art performance on function calling benchmarks (BFCL), making it one of the most capable open-source agents. (Nous Research).

Key statistic

Hermes 3 achieves 93.2% accuracy on the Berkeley Function Calling Leaderboard — the highest-scoring open-source model for MCP tool use.

“Open-source agents need open-source tools. The MCP ecosystem gives Hermes the same capabilities that closed-source agents get from proprietary integrations.”

— Karan Malhotra, Co-founder of Nous Research

Why Hermes Agent

If you haven't tried Hermes Agent yet, here's the pitch: it's a self-improving AI agent built by Nous Research (23k+ GitHub stars, MIT licensed) that runs on your own infrastructure. Unlike chatbot wrappers or IDE copilots, Hermes is a persistent agent — it lives on your server, remembers what it learns across sessions, creates its own skills from experience, and reaches you on Telegram, Discord, Slack, WhatsApp, or CLI from a single gateway.

It has 40+ built-in tools covering web search, browser automation, terminal access, code execution, vision, image generation, and subagent delegation. It supports Model Context Protocol (MCP) integration natively. And it runs anywhere — local, Docker, SSH, Modal, or Singularity with sandboxed execution.

In short: it does a lot. But without a signing tool, it can't close a deal. By integrating Signbee MCP, you give your agent the ability to execute legally binding e-signatures, translating conversation intents directly into audited business transactions.

Step 1: Install Hermes Agent

If you haven't already, bootstrap the Hermes Agent environment on your local server or VPS:

Terminal
curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash
source ~/.zshrc  # or ~/.bashrc
hermes setup     # interactive setup wizard

The installer handles everything — Python, Node.js, dependencies, and the hermes command. Connect to Nous Portal, OpenRouter, or any OpenAI-compatible endpoint for your model.

Step 2: Get a Signbee API key

Sign up at signb.ee/register and grab your API key from the dashboard. The free tier includes 5 documents per month — enough for testing. For production workloads, paid tiers remove OTP steps, sending contracts immediately.

Step 3: Add the Signbee MCP server

Hermes has native MCP support. Add Signbee to your MCP configuration:

~/.hermes/mcp_servers.json (or via hermes config)
{
  "mcpServers": {
    "signbee": {
      "command": "npx",
      "args": ["-y", "signbee-mcp"],
      "env": {
        "SIGNBEE_API_KEY": "sb_live_your_key_here"
      }
    }
  }
}

Restart Hermes or start a new conversation. The agent now has access to Signbee's tools — it can send documents for signing, check document status, and list previous documents.

Detailed Tool-Spec Schema Definitions

For Nous Research's Hermes models to successfully select and call the Signbee tools, they must parse JSON schemas provided in the system prompt environment. Below are the exact tool schemas expected by Hermes for executing e-signatures.

send_document Tool Spec (JSON Schema)
{
  "name": "send_document",
  "description": "Send a document for two-party e-signing. Converts markdown to PDF, verifies the sender, emails the recipient a signing link, and delivers a SHA-256 certified signed copy to both parties.",
  "parameters": {
    "type": "object",
    "properties": {
      "markdown": {
        "type": "string",
        "description": "Document content in markdown format. Must be at least 10 characters and will be converted to a professional PDF."
      },
      "title": {
        "type": "string",
        "description": "Document title. If omitted, extracted from the first markdown heading."
      },
      "sender_name": {
        "type": "string",
        "description": "Full name of the document sender"
      },
      "sender_email": {
        "type": "string",
        "description": "Email address of the sender"
      },
      "recipient_name": {
        "type": "string",
        "description": "Full name of the document recipient"
      },
      "recipient_email": {
        "type": "string",
        "description": "Email address of the recipient"
      },
      "expires_in_days": {
        "type": "integer",
        "description": "Days until the signing link expires (1-30). Default is 7."
      }
    },
    "required": [
      "markdown",
      "sender_name",
      "sender_email",
      "recipient_name",
      "recipient_email"
    ]
  }
}
send_document_pdf Tool Spec (JSON Schema)
{
  "name": "send_document_pdf",
  "description": "Send an existing PDF document for two-party e-signing. Use this when you already have a PDF URL instead of markdown content.",
  "parameters": {
    "type": "object",
    "properties": {
      "pdf_url": {
        "type": "string",
        "description": "Publicly accessible URL to the PDF document. Must be a valid HTTPS URL."
      },
      "title": {
        "type": "string",
        "description": "Document title."
      },
      "sender_name": {
        "type": "string",
        "description": "Full name of the document sender"
      },
      "sender_email": {
        "type": "string",
        "description": "Email address of the sender"
      },
      "recipient_name": {
        "type": "string",
        "description": "Full name of the document recipient"
      },
      "recipient_email": {
        "type": "string",
        "description": "Email address of the recipient"
      },
      "expires_in_days": {
        "type": "integer",
        "description": "Days until the signing link expires (1-30). Default is 7."
      }
    },
    "required": [
      "pdf_url",
      "title",
      "sender_name",
      "sender_email",
      "recipient_name",
      "recipient_email"
    ]
  }
}

When a user messages the agent, Hermes scans these definitions. If the user says “Send the project contract to Sarah at sarah@acme.dev”, the model recognizes that recipient_email is “sarah@acme.dev” and maps it directly to the send_document tool.

System Prompts for E-Signature Autonomy

To ensure Hermes acts responsibly and legally when drafting and executing contracts, it must be guided by a strict system prompt. This prompt establishes formatting guidelines, security checks, and human-in-the-loop protocols.

Hermes System Prompt
You are Hermes, a persistent, autonomous agent equipped with e-signature capabilities via the Signbee Model Context Protocol (MCP) server. Your goal is to manage the contract lifecycle, drafting agreements and dispatching them for signing when terms are finalized.

CRITICAL SECURITY & BEHAVIORAL PROTOCOLS:
1. CONTRACT DRAFTING:
   - Always draft contracts using clean, professional markdown format.
   - Include a clear title as the first H1 header (e.g., "# Mutual Non-Disclosure Agreement").
   - Clearly define the parties (Sender and Recipient), their official names, and contact details in an introductory clause.
   - Maintain professional legal wording and formatting (e.g., numbered sections, clear clauses, and execution blocks).

2. HUMAN-IN-THE-LOOP (HITL) GATE:
   - You are NEVER allowed to call the send_document or send_document_pdf tools without explicit confirmation from the human sender.
   - Once a contract draft is created in the conversation, present it to the user and ask: "Please review the draft above. If you approve, respond with 'APPROVE' or 'SEND' to dispatch it for e-signing."
   - Only execute the tool call AFTER the user provides this explicit confirmation.

3. PARAMETER VERIFICATION:
   - Ensure you have the exact recipient name, recipient email, sender name, and sender email before calling the tool.
   - If any required field is missing or ambiguous, ask the user to clarify before drafting.

4. ERROR HANDLING & FOLLOW-UP:
   - If a tool call fails (e.g., due to rate limits or invalid emails), explain the error to the user and suggest corrective actions.
   - Log the document_id returned by the tool in your persistent memory context.
   - Offer to track the status of the sent document and check back with the user or set a schedule to follow up.

Agent Contract Lifecycle Flow

Executing a contract with an AI agent spans multiple systems: the user chat interface, the agent engine, the Signbee MCP gateway, and webhook callbacks. The flowchart below outlines this complete lifecycle:

Lifecycle Sequence Diagram
  +------------------+     1. Instruction     +-----------------------+
  |   User Intent    | ---------------------> |  Hermes Agent Memory  |
  +------------------+                        +-----------------------+
           ^                                              |
           | 6. Confirmation Notification                 | 2. Analyze & Draft
           |                                              v
  +------------------+                        +-----------------------+
  |   Telegram /     | <--------------------- |  Markdown Contract    |
  |   Slack Client   |    5. Approved (HITL)  +-----------------------+
  +------------------+                                    |
           ^                                              | 3. Call send_document
           |                                              v
           | 9. Status Event Callback         +-----------------------+
           |                                  |  Signbee MCP Tool     |
           |                                  +-----------------------+
           |                                              |
           |                                              | 4. REST Call
           |                                              v
  +------------------+     8. Event Posted    +-----------------------+
  | Agent Webhook    | <--------------------- |  Signbee API Gateway  |
  | Callback Listener|                        +-----------------------+
  +------------------+                                    |
           |                                              | 7. Signing Ceremony
           v                                              v
  +------------------+                        +-----------------------+
  | Memory Update &  |                        |  Recipient Signer     |
  | Database Sealing |                        +-----------------------+
  +------------------+

Here is how each transition functions in a production system:

  1. Instruction: The user gives a command (“Send Sarah an NDA”). Alternatively, a cron schedule or API trigger initiates a contract renewal workflow.
  2. Analyze & Draft: Hermes compiles context from memory, retrieves contract clauses, and formats the contract into standard markdown.
  3. HITL Verification: The agent posts the draft in chat, asking for user confirmation.
  4. MCP Call: Once approved, the agent executes send_document.
  5. Signbee Dispatch: The Signbee MCP server translates the command to an HTTP POST to Signbee's REST API (/api/v1/send) using your API key.
  6. Signing Ceremony: Signbee converts the markdown to a PDF, uploads it, and emails a signing link (e.g., signb.ee/sign/token) to the recipient.
  7. Signing Action: The recipient opens the secure link, draws/types their signature, and submits.
  8. Webhook Callback: The Signbee API gateway triggers an HTTP POST request to the agent webhook endpoint, delivering payload info like event: "document.signed" and the certified PDF URL.
  9. Sealing & Notification: The agent listener verifies the payload signature, stores the audit trail, updates internal states, and notifies the user in chat.

Complete Webhook and Wrapper Scripts

To complete the loop, you must run a webhook listener that intercepts Signbee events and updates the agent's database or triggers downstream tasks. Below are complete, production-ready implementations in both Node.js (Express) and Python (FastAPI).

Node.js Express Webhook Listener (index.js)
const express = require("express");
const crypto = require("crypto");
const bodyParser = require("body-parser");

const app = express();
const PORT = process.env.PORT || 8080;
// SHARED_SECRET is obtained when you register your webhook in the Signbee dashboard
const WEBHOOK_SECRET = process.env.SIGNBEE_WEBHOOK_SECRET || "sb_wh_secret_key";

// We need the raw body to verify the signature accurately
app.use(bodyParser.json({
  verify: (req, res, buf) => {
    req.rawBody = buf;
  }
}));

app.post("/webhooks/signbee", (req, res) => {
  const signature = req.headers["x-signbee-signature"];
  
  if (!signature) {
    return res.status(401).json({ error: "Missing signature header" });
  }

  if (!req.rawBody) {
    return res.status(400).json({ error: "Missing payload body" });
  }

  // Calculate HMAC SHA-256 signature
  const hmac = crypto.createHmac("sha256", WEBHOOK_SECRET);
  hmac.update(req.rawBody);
  const calculatedSignature = hmac.digest("hex");

  // Constant-time comparison to prevent timing attacks
  const isSignatureValid = crypto.timingSafeEqual(
    Buffer.from(signature, "hex"),
    Buffer.from(calculatedSignature, "hex")
  );

  if (!isSignatureValid) {
    console.error("Invalid signature. Payload verification failed.");
    return res.status(401).json({ error: "Invalid cryptographic signature" });
  }

  const { event, document_id, recipient_email, signed_at, blob_url } = req.body;
  console.log(`Received authenticated event ${event} for document ${document_id}`);

  // Handle specific document lifecycle events
  switch (event) {
    case "document.signed":
      console.log(`Document ${document_id} signed by ${recipient_email} at ${signed_at}.`);
      console.log(`Finalized PDF accessible at: ${blob_url}`);
      // TODO: Call your Hermes Agent webhook handler to update its memory database
      // e.g., await hermesAgent.updateMemory(document_id, { status: "signed", pdf: blob_url });
      break;

    case "document.viewed":
      console.log(`Recipient viewed the document: ${document_id}`);
      break;

    case "document.declined":
      console.log(`Recipient declined to sign document ${document_id}`);
      break;

    default:
      console.log(`Unhandled event state: ${event}`);
  }

  // Return 200 OK immediately
  res.status(200).json({ received: true });
});

app.listen(PORT, () => {
  console.log(`Webhook listener running on port ${PORT}`);
});
Python FastAPI Webhook Listener (main.py)
import hmac
import hashlib
import os
from fastapi import FastAPI, Request, HTTPException, Header
from pydantic import BaseModel, HttpUrl
from typing import Optional

app = FastAPI(title="Signbee Agent Webhook Listener")

WEBHOOK_SECRET = os.getenv("SIGNBEE_WEBHOOK_SECRET", "sb_wh_secret_key").encode("utf-8")

class SignbeeWebhookPayload(BaseModel):
    event: str
    document_id: str
    recipient_email: str
    recipient_name: str
    sender_email: str
    status: str
    blob_url: Optional[HttpUrl] = None
    signed_at: Optional[str] = None

@app.post("/webhooks/signbee")
async def handle_signbee_webhook(
    request: Request,
    x_signbee_signature: str = Header(None)
):
    if not x_signbee_signature:
        raise HTTPException(status_code=401, detail="Signature header missing")

    # Retrieve raw body bytes for HMAC computation
    raw_body = await request.body()
    
    # Calculate signature
    computed_hmac = hmac.new(WEBHOOK_SECRET, raw_body, hashlib.sha256).hexdigest()

    # Compare signatures using constant-time comparison
    if not hmac.compare_digest(x_signbee_signature, computed_hmac):
        raise HTTPException(status_code=401, detail="Invalid signature")

    # Parse JSON body
    try:
        body_json = await request.json()
        payload = SignbeeWebhookPayload(**body_json)
    except Exception as e:
        raise HTTPException(status_code=400, detail=f"Invalid payload format: {str(e)}")

    print(f"Verified event: {payload.event} for document: {payload.document_id}")

    if payload.event == "document.signed":
        # Process signature completion
        # Trigger downstream agent tasks (e.g. database updates, vector stores updates)
        print(f"Triggering contract closure. Signed PDF: {payload.blob_url}")
        
    elif payload.event == "document.viewed":
        print(f"Document {payload.document_id} was opened by signer.")
        
    elif payload.event == "document.declined":
        print(f"Document {payload.document_id} was declined.")

    return {"status": "success", "processed": True}

Frequently Asked Questions

How does the Nous Research Hermes model handle function calling schemas for e-signature tools?

Nous Research's Hermes models (specifically Hermes 3 based on Llama 3) utilize a highly optimized tokenizer and chat template design that interprets system-level function definitions with near-native precision. When a tool spec schema (such as Signbee's send_document tool) is provided to Hermes in the system context, the model parses the properties, types, and descriptions to map user intents to the JSON function call. It translates conversational inputs like "Send the freelancer agreement to Sarah" into structured arguments matching the schema, including mapping names and validating email formats. Because Hermes 3 is trained extensively on structured outputs and Berkeley Function Calling Leaderboard (BFCL) test sets, it avoids common hallucinations like inventing missing parameters or misaligning array types. It automatically extracts parameters like the title from the document markdown header if it's not explicitly provided, and invokes the tool via standard JSON format <tool_call>... blocks, allowing the MCP execution environment to intercept and perform the actual HTTP request to the Signbee endpoint.

What security mechanisms prevent Hermes agents from executing unauthorized contract signature loops?

To prevent autonomous agents from entering unchecked, recursive contract generation and execution loops, developers must implement a multi-layered security architecture. First, a Human-in-the-Loop (HITL) gate must be established. The system prompt instructs Hermes that it cannot invoke the send_document or send_document_pdf tools without explicit, tokenized confirmation from the user (e.g., via a Telegram callback button or CLI 'Y/N' challenge). Second, at the MCP server level, rate limits and payload sanitization are enforced. Signbee's API key verification enforces monthly document creation quotas and prevents programmatic spam. Third, when an agent initiates a document signing process, Signbee issues a cryptographically secure token and verification email. If the agent operates anonymously (without an API key), a one-time passcode (OTP) is sent to the sender's email. This means even if the agent is compromised, a document cannot be dispatched to a recipient until the human sender validates the OTP in their mailbox, sealing the transaction with an out-of-band security mechanism.

How do webhook callbacks verify signature state transitions to Hermes agents in real-time?

When a document's state changes on the Signbee platform (e.g., document.viewed, document.signed, document.declined), the Signbee server sends an HTTP POST request to the agent's webhook callback URL. To prevent spoofing and replay attacks, these webhooks are cryptographically signed. Signbee computes a HMAC SHA-256 signature of the raw request payload using a webhook secret key shared during setup and attaches this signature to the X-Signbee-Signature header. The receiver application must intercept the raw request body, compute its own HMAC using the shared secret, and perform a constant-time comparison to verify authenticity. Once verified, the webhook handler parses the payload's event type and document ID. If the event is document.signed, the handler updates the database state to 'SIGNED', fetches the finalized PDF blob containing the SHA-256 cryptographic audit trail, and triggers downstream actions. For the Hermes agent, this update is written to its persistent semantic memory (vector database), enabling it to transition its internal state machine and proceed with post-contract tasks, such as releasing project funds or starting onboarding protocols.

Never drop a document — 5 free docs/month, 100 req/min.

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

Related resources