{
  "schemaVersion": "1.0",
  "item": {
    "slug": "saysigned",
    "name": "SaySigned - Agreement infrastructure for AI agents",
    "source": "tencent",
    "type": "skill",
    "category": "开发工具",
    "sourceUrl": "https://clawhub.ai/klsv/saysigned",
    "canonicalUrl": "https://clawhub.ai/klsv/saysigned",
    "targetPlatform": "OpenClaw"
  },
  "install": {
    "downloadMode": "redirect",
    "downloadUrl": "/downloads/saysigned",
    "sourceDownloadUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=saysigned",
    "sourcePlatform": "tencent",
    "targetPlatform": "OpenClaw",
    "installMethod": "Manual import",
    "extraction": "Extract archive",
    "prerequisites": [
      "OpenClaw"
    ],
    "packageFormat": "ZIP package",
    "includedAssets": [
      "SKILL.md"
    ],
    "primaryDoc": "SKILL.md",
    "quickSetup": [
      "Download the package from Yavira.",
      "Extract the archive and review SKILL.md first.",
      "Import or place the package into your OpenClaw setup."
    ],
    "agentAssist": {
      "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
      "steps": [
        "Download the package from Yavira.",
        "Extract it into a folder your agent can access.",
        "Paste one of the prompts below and point your agent at the extracted folder."
      ],
      "prompts": [
        {
          "label": "New install",
          "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete."
        },
        {
          "label": "Upgrade existing",
          "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run."
        }
      ]
    },
    "sourceHealth": {
      "source": "tencent",
      "status": "healthy",
      "reason": "direct_download_ok",
      "recommendedAction": "download",
      "checkedAt": "2026-05-07T17:22:31.273Z",
      "expiresAt": "2026-05-14T17:22:31.273Z",
      "httpStatus": 200,
      "finalUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=afrexai-annual-report",
      "contentType": "application/zip",
      "probeMethod": "head",
      "details": {
        "probeUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=afrexai-annual-report",
        "contentDisposition": "attachment; filename=\"afrexai-annual-report-1.0.0.zip\"",
        "redirectLocation": null,
        "bodySnippet": null
      },
      "scope": "source",
      "summary": "Source download looks usable.",
      "detail": "Yavira can redirect you to the upstream package for this source.",
      "primaryActionLabel": "Download for OpenClaw",
      "primaryActionHref": "/downloads/saysigned"
    },
    "validation": {
      "installChecklist": [
        "Use the Yavira download entry.",
        "Review SKILL.md after the package is downloaded.",
        "Confirm the extracted package contains the expected setup assets."
      ],
      "postInstallChecks": [
        "Confirm the extracted package includes the expected docs or setup files.",
        "Validate the skill or prompts are available in your target agent workspace.",
        "Capture any manual follow-up steps the agent could not complete."
      ]
    },
    "downloadPageUrl": "https://openagent3.xyz/downloads/saysigned",
    "agentPageUrl": "https://openagent3.xyz/skills/saysigned/agent",
    "manifestUrl": "https://openagent3.xyz/skills/saysigned/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/saysigned/agent.md"
  },
  "agentAssist": {
    "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
    "steps": [
      "Download the package from Yavira.",
      "Extract it into a folder your agent can access.",
      "Paste one of the prompts below and point your agent at the extracted folder."
    ],
    "prompts": [
      {
        "label": "New install",
        "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete."
      },
      {
        "label": "Upgrade existing",
        "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run."
      }
    ]
  },
  "documentation": {
    "source": "clawhub",
    "primaryDoc": "SKILL.md",
    "sections": [
      {
        "title": "SaySigned — AI Agent E-Signature Skill",
        "body": "E-signatures for AI agents. Legally binding under ESIGN Act & UETA Section 14.\nTwo delivery modes: API (agent-to-agent) and Email (agent-to-human). Register, sign contracts, verify — all via REST or consent UI.\n\nPublisher: SaySigned, Inc.\nHomepage: https://www.saysigned.com\nDocumentation: https://www.saysigned.com/docs\nBase URL: https://api.saysigned.com\nMCP Endpoint: https://mcp.saysigned.com\nTLS Required: All endpoints enforce HTTPS. Verify certificates for *.saysigned.com before connecting.\n\nAuthentication: Pass your API key in the X-API-Key header. Keys start with ss_live_. Public endpoints (register, verify, templates) don't require authentication."
      },
      {
        "title": "MCP Server (Recommended for AI Agents)",
        "body": "Connect directly to the remote MCP endpoint — no local install, no HTTP requests to craft.\n\nAuth note: MCP clients send Authorization: Bearer <api_key>. The MCP server extracts your key and forwards it as X-API-Key internally. For direct REST calls, use the X-API-Key header."
      },
      {
        "title": "Setup",
        "body": "Human approval required. Steps 1, 4, and 5 modify your MCP client configuration. Always show the user what you're about to change and ask for their confirmation before writing to config files or restarting the MCP client. Never modify config silently.\n\nStep 1. Show the user the following MCP config and ask for confirmation before adding it:\n\n{\n  \"mcpServers\": {\n    \"saysigned\": {\n      \"type\": \"streamable-http\",\n      \"url\": \"https://mcp.saysigned.com\"\n    }\n  }\n}\n\nAfter the user confirms, add it to the MCP config. No API key is needed yet — public tools work immediately.\n\nStep 2. Restart your MCP client so it loads the new server.\n\nStep 3. Call the saysigned_register tool (no auth required) with the agent's name and email. The response contains an api_key — save it immediately, it is shown once and cannot be retrieved later. Keys start with ss_live_.\n\nStep 4. Show the user the updated config with the auth header and ask for confirmation before writing it:\n\n{\n  \"mcpServers\": {\n    \"saysigned\": {\n      \"type\": \"streamable-http\",\n      \"url\": \"https://mcp.saysigned.com\",\n      \"headers\": {\n        \"Authorization\": \"Bearer ss_live_PASTE_YOUR_ACTUAL_KEY_HERE\"\n      }\n    }\n  }\n}\n\nStep 5. After the user confirms, restart your MCP client so it picks up the auth header. All authenticated tools now work.\n\nHuman-in-the-loop for config changes. The user must confirm before you (1) add or modify MCP config entries, (2) store API keys in config files, or (3) restart the MCP client. You may handle registration, envelope creation, signing, and verification autonomously — those are normal API operations that don't modify the agent's configuration."
      },
      {
        "title": "Already have a key?",
        "body": "If you already have an API key (starts with ss_live_), skip to Step 4 — show the user the config with headers, get their confirmation, and restart."
      },
      {
        "title": "MCP Workflow (signing flow)",
        "body": "Prepare the contract — two options:\n\nUse a template: call saysigned_list_templates → saysigned_get_template to discover required variables, then pass template_id + variables in the contract field.\nBring your own text: pass your own contract content in the clauses array. Each clause has id, title, and body. You can put an entire document into a single clause or split it into sections. Use this when you have your own NDA, agreement, or any custom document.\n\n\nCreate envelope — saysigned_create_envelope with title, contract, and recipients array.\nSend envelope — saysigned_send_envelope with the envelope_id. Response includes each recipient's id and access_token. Save these — they are needed for signing.\nSign — For API recipients: saysigned_sign for each recipient, passing their envelope_id, recipient_id, access_token, and signature_data (at minimum full_name). For email recipients: the human reviews and gives consent at doc.saysigned.com — no action needed from the agent.\nVerify — saysigned_verify with the envelope_id. No auth needed."
      },
      {
        "title": "Critical Notes for AI Agents",
        "body": "You don't have to use a template. If you have your own contract text (an NDA, agreement, policy — any document), pass it directly via clauses. Templates are a convenience, not a requirement.\nAlways call saysigned_get_template before creating an envelope with a template. The required variables differ per template and the API will reject unknown or missing variables.\nsaysigned_send_envelope returns access tokens. You must capture and use these tokens for signing. They are 128-character hex strings.\nSigning does not use your API key. The saysigned_sign and saysigned_decline tools authenticate via the access_token parameter, not the API key header.\nThe envelope auto-completes when the last recipient signs. You don't need to call a separate \"complete\" endpoint.\nAuth header is forwarded automatically. Authenticated tools (create_envelope, send_envelope, get_envelope, get_profile, etc.) use the API key from your MCP client's Authorization header. Public tools (register, verify, list_templates, get_template) work without it.\ndelivery_method: \"email\" sends a signing link to a real human. The human reviews the contract at doc.saysigned.com and gives consent via browser. Email recipients do NOT get an access_token in the send response — they authenticate via a URL token in their email link. Use delivery_method: \"api\" (default) for agent-to-agent signing.\nPoll or use webhooks for email recipients. Since humans sign asynchronously, use saysigned_get_envelope to poll for status changes or configure a webhook_url to receive recipient.viewed and recipient.signed events."
      },
      {
        "title": "All 14 MCP Tools",
        "body": "ToolAuthDescriptionsaysigned_registerNoneRegister agent, get API keysaysigned_create_envelopeAPI keyCreate draft envelopesaysigned_send_envelopeAPI keySend for signing, get access tokenssaysigned_get_envelopeAPI keyGet envelope details + statussaysigned_void_envelopeAPI keyCancel a sent (not completed) envelopesaysigned_signAccess tokenSign as a recipientsaysigned_declineAccess tokenDecline to signsaysigned_verifyNoneVerify cryptographic integritysaysigned_get_audit_trailAPI keyGet hash-chain audit trailsaysigned_list_templatesNoneList available contract templatessaysigned_get_templateNoneGet template details + required variablessaysigned_billing_setupAPI keyUpgrade to paid plansaysigned_get_usageAPI keyCurrent billing period usagesaysigned_get_profileAPI keyAgent profile + plan info"
      },
      {
        "title": "Complete End-to-End Example",
        "body": "This is a full working flow: register, create an NDA, send it, both parties sign, then verify. Every response shown is from the real production API."
      },
      {
        "title": "Step 1 — Register (no auth needed)",
        "body": "curl -s -X POST https://api.saysigned.com/agents/register \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"name\": \"My AI Agent\", \"email\": \"agent@example.com\"}'\n\n{\n  \"agent_id\": \"7773b4af-44d1-44fc-8db7-05d9bc95b541\",\n  \"api_key\": \"ss_live_203bff0e53ba167462aa2cdcbd8e189e2909d37cf76c31da675cb1dba7dc0026\",\n  \"plan\": \"free\",\n  \"free_envelopes_remaining\": 5\n}\n\nSave the api_key — it is shown once and cannot be retrieved later."
      },
      {
        "title": "Step 2 — Create an envelope",
        "body": "Use the api_key from step 1 in the X-API-Key header. Use template_id for standard contracts or provide custom clauses.\n\ncurl -s -X POST https://api.saysigned.com/envelopes \\\n  -H \"Content-Type: application/json\" \\\n  -H \"X-API-Key: ss_live_203bff0e53ba167462aa2cdcbd8e189e2909d37cf76c31da675cb1dba7dc0026\" \\\n  -d '{\n    \"title\": \"Mutual NDA — Acme & Beta\",\n    \"contract\": {\n      \"template_id\": \"nda_mutual_v1\",\n      \"variables\": {\n        \"disclosing_party_name\": \"Acme Corp\",\n        \"receiving_party_name\": \"Beta Inc\",\n        \"effective_date\": \"2026-02-09\",\n        \"purpose\": \"Evaluating a potential partnership\",\n        \"governing_law_state\": \"California\"\n      }\n    },\n    \"recipients\": [\n      {\"name\": \"Alice\", \"email\": \"alice@acme.com\", \"role\": \"signer\"},\n      {\"name\": \"Bob\", \"email\": \"bob@beta.com\", \"role\": \"signer\"}\n    ]\n  }'\n\n{\n  \"id\": \"2c272d15-cd4b-4c0d-96c0-bd7438b66699\",\n  \"status\": \"draft\",\n  \"title\": \"Mutual NDA — Acme & Beta\",\n  \"recipients\": [\n    {\"id\": \"62f73ba5-05a0-4223-8f55-e880047a7b3e\", \"name\": \"Alice\", \"status\": \"pending\"},\n    {\"id\": \"cc0aeb32-eb9d-420d-ab93-1d42400f85b9\", \"name\": \"Bob\", \"status\": \"pending\"}\n  ]\n}\n\nSave the envelope id and each recipient's id."
      },
      {
        "title": "Step 3 — Send the envelope",
        "body": "This consumes 1 envelope from your quota (free tier) or reports 1 metered event (paid plan). Recipients get access_token values — these are how they authenticate when signing.\n\ncurl -s -X POST https://api.saysigned.com/envelopes/2c272d15-cd4b-4c0d-96c0-bd7438b66699/send \\\n  -H \"X-API-Key: ss_live_203bff0e53ba167462aa2cdcbd8e189e2909d37cf76c31da675cb1dba7dc0026\"\n\n{\n  \"id\": \"2c272d15-cd4b-4c0d-96c0-bd7438b66699\",\n  \"status\": \"sent\",\n  \"recipients\": [\n    {\"id\": \"62f73ba5-05a0-4223-8f55-e880047a7b3e\", \"name\": \"Alice\", \"status\": \"sent\", \"access_token\": \"600b9b4297b2921396a1db138a80801c7dfb5101...\"},\n    {\"id\": \"cc0aeb32-eb9d-420d-ab93-1d42400f85b9\", \"name\": \"Bob\", \"status\": \"sent\", \"access_token\": \"952340a770b96740e2696c306ad490b28556952e...\"}\n  ]\n}\n\nSave each recipient's access_token. Distribute tokens to the respective signers."
      },
      {
        "title": "Step 4 — Recipients sign",
        "body": "Each recipient signs using their access_token via the X-Access-Token header (recommended) or ?access_token= query parameter. No API key needed — the token is the auth.\n\n# Alice signs\ncurl -s -X POST \"https://api.saysigned.com/envelopes/2c272d15-cd4b-4c0d-96c0-bd7438b66699/recipients/62f73ba5-05a0-4223-8f55-e880047a7b3e/sign\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"X-Access-Token: 600b9b4297b2921396a1db138a80801c7dfb5101...\" \\\n  -d '{\"signature_data\": {\"full_name\": \"Alice Chen\", \"title\": \"CEO\"}}'\n\n{\n  \"signed\": true,\n  \"recipient_id\": \"62f73ba5-05a0-4223-8f55-e880047a7b3e\",\n  \"envelope_id\": \"2c272d15-cd4b-4c0d-96c0-bd7438b66699\",\n  \"signed_at\": \"2026-02-09T04:09:24.111Z\"\n}\n\n# Bob signs (last signer → envelope automatically completes with RFC 3161 timestamp)\ncurl -s -X POST \"https://api.saysigned.com/envelopes/2c272d15-cd4b-4c0d-96c0-bd7438b66699/recipients/cc0aeb32-eb9d-420d-ab93-1d42400f85b9/sign\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"X-Access-Token: 952340a770b96740e2696c306ad490b28556952e...\" \\\n  -d '{\"signature_data\": {\"full_name\": \"Bob Smith\", \"title\": \"CTO\"}}'\n\n{\n  \"signed\": true,\n  \"recipient_id\": \"cc0aeb32-eb9d-420d-ab93-1d42400f85b9\",\n  \"envelope_id\": \"2c272d15-cd4b-4c0d-96c0-bd7438b66699\",\n  \"signed_at\": \"2026-02-09T04:09:25.190Z\"\n}"
      },
      {
        "title": "Step 5 — Verify (public, no auth needed)",
        "body": "curl -s -X POST https://api.saysigned.com/verify \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"envelope_id\": \"2c272d15-cd4b-4c0d-96c0-bd7438b66699\"}'\n\n{\n  \"envelope_id\": \"2c272d15-cd4b-4c0d-96c0-bd7438b66699\",\n  \"status\": \"completed\",\n  \"chain_verification\": {\"valid\": true, \"entries_checked\": 5},\n  \"timestamp_verification\": {\n    \"valid\": true,\n    \"gen_time\": \"2026-02-09T04:09:25+00:00\",\n    \"tsa_name\": \"DigiCert SHA256 RSA4096 Timestamp Responder 2025 1\"\n  },\n  \"recipients\": [\n    {\"name\": \"Alice\", \"email\": \"alice@acme.com\", \"role\": \"signer\", \"status\": \"signed\", \"signed_at\": \"2026-02-09T04:09:24.111Z\"},\n    {\"name\": \"Bob\", \"email\": \"bob@beta.com\", \"role\": \"signer\", \"status\": \"signed\", \"signed_at\": \"2026-02-09T04:09:25.190Z\"}\n  ],\n  \"completed_at\": \"2026-02-09T04:09:25.039Z\"\n}"
      },
      {
        "title": "Bring Your Own Contract",
        "body": "You don't need a template. If you have your own document text — an NDA your user provided, a custom agreement, any markdown or plain text — pass it directly via clauses. Each clause needs an id, title, and body.\n\nSingle-clause approach (simplest — put the entire document in one clause):\n\ncurl -s -X POST https://api.saysigned.com/envelopes \\\n  -H \"Content-Type: application/json\" \\\n  -H \"X-API-Key: YOUR_API_KEY\" \\\n  -d '{\n    \"title\": \"NDA — Acme & Beta\",\n    \"contract\": {\n      \"clauses\": [\n        {\n          \"id\": \"nda\",\n          \"title\": \"Mutual Non-Disclosure Agreement\",\n          \"body\": \"This Mutual Non-Disclosure Agreement is entered into by Acme Corp (\\\"Party A\\\") and Beta Inc (\\\"Party B\\\")...\\n\\n1. Definition of Confidential Information...\\n\\n2. Obligations of Receiving Party...\\n\\n3. Term. This Agreement shall remain in effect for two (2) years...\"\n        }\n      ]\n    },\n    \"recipients\": [\n      {\"name\": \"Alice\", \"email\": \"alice@acme.com\", \"role\": \"signer\"},\n      {\"name\": \"Bob\", \"email\": \"bob@beta.com\", \"role\": \"signer\"}\n    ]\n  }'\n\nMulti-clause approach (split into sections for cleaner presentation):\n\ncurl -s -X POST https://api.saysigned.com/envelopes \\\n  -H \"Content-Type: application/json\" \\\n  -H \"X-API-Key: YOUR_API_KEY\" \\\n  -d '{\n    \"title\": \"NDA — Acme & Beta\",\n    \"contract\": {\n      \"clauses\": [\n        {\"id\": \"1\", \"title\": \"1. Definition of Confidential Information\", \"body\": \"\\\"Confidential Information\\\" means any non-public information disclosed by either party...\"},\n        {\"id\": \"2\", \"title\": \"2. Obligations of Receiving Party\", \"body\": \"The Receiving Party shall hold all Confidential Information in strict confidence...\"},\n        {\"id\": \"3\", \"title\": \"3. Term and Termination\", \"body\": \"This Agreement shall remain in effect for two (2) years from the Effective Date...\"},\n        {\"id\": \"4\", \"title\": \"4. Governing Law\", \"body\": \"This Agreement shall be governed by the laws of the State of California...\"}\n      ]\n    },\n    \"recipients\": [\n      {\"name\": \"Alice\", \"email\": \"alice@acme.com\", \"role\": \"signer\"},\n      {\"name\": \"Bob\", \"email\": \"bob@beta.com\", \"role\": \"signer\"}\n    ]\n  }'\n\nMCP equivalent — same structure, just pass it to saysigned_create_envelope:\n\n{\n  \"title\": \"NDA — Acme & Beta\",\n  \"contract\": {\n    \"clauses\": [\n      {\"id\": \"nda\", \"title\": \"Mutual Non-Disclosure Agreement\", \"body\": \"Full text of the NDA here...\"}\n    ]\n  },\n  \"recipients\": [\n    {\"name\": \"Alice\", \"email\": \"alice@acme.com\", \"role\": \"signer\"},\n    {\"name\": \"Bob\", \"email\": \"bob@beta.com\", \"role\": \"signer\"}\n  ]\n}\n\nWhen to use which: Use templates when you want a standard contract and just need to fill in the blanks (party names, dates, etc.). Use custom clauses when you already have the contract text — from a file, a user-provided document, or your own drafting."
      },
      {
        "title": "Key Concepts",
        "body": "IDs are UUIDs — all envelope, recipient, and agent IDs are standard UUIDs (e.g., 2c272d15-cd4b-4c0d-96c0-bd7438b66699)\nAccess tokens are hex strings — 128-character hex, passed via X-Access-Token header (recommended) or ?access_token= query parameter\nAPI keys start with ss_live_ — REST: pass in X-API-Key header. MCP: pass as Authorization: Bearer <key> (auto-translated)\nTemplates — use GET /templates to discover available templates and GET /templates/:id to see required variables\nSigning order — all recipients sign in parallel (no enforced order)\nCompletion — when the last signer signs, the envelope auto-completes with an RFC 3161 timestamp from DigiCert\n\nFree tier: 5 envelopes/month, no credit card required. Upgrade: POST /billing/setup with {\"plan\": \"payg\"}.\n\nDelivery methods — \"api\" (default) returns access tokens for programmatic signing; \"email\" sends an invitation email to a human who reviews and gives consent at doc.saysigned.com\nEmail recipients — human recipients authenticate via a URL token in their email link, not an access token. They review the contract in a browser and click \"Give Consent\" or \"Decline\""
      },
      {
        "title": "MCP Tool Definitions",
        "body": "These tool definitions are compatible with MCP (Model Context Protocol) runtimes. Each tool maps to a REST API endpoint."
      },
      {
        "title": "saysigned_register",
        "body": "Create an agent account and get your API key.\n\n{\n  \"name\": \"saysigned_register\",\n  \"description\": \"Register a new AI agent account on SaySigned. Returns an API key (shown once — save it). Free tier: 5 envelopes/month.\",\n  \"inputSchema\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"name\": { \"type\": \"string\", \"description\": \"Agent or organization name\" },\n      \"email\": { \"type\": \"string\", \"format\": \"email\", \"description\": \"Contact email (must be unique)\" },\n      \"company_name\": { \"type\": \"string\", \"description\": \"Company name (optional)\" },\n      \"webhook_url\": { \"type\": \"string\", \"format\": \"uri\", \"description\": \"URL to receive webhook notifications (optional)\" }\n    },\n    \"required\": [\"name\", \"email\"]\n  }\n}\n\nREST: POST /agents/register — no auth required."
      },
      {
        "title": "saysigned_create_envelope",
        "body": "Create a draft envelope with a contract and recipients.\n\n{\n  \"name\": \"saysigned_create_envelope\",\n  \"description\": \"Create a draft envelope with contract content and recipients. Two ways to provide a contract: (1) Use template_id + variables for standard contracts (call saysigned_get_template first), or (2) provide your own contract text via the clauses array — each clause has an id, title, and body. You can put an entire document into a single clause or split it into sections. Use option 2 when you have your own NDA, agreement, or any custom document text. Set delivery_method: 'email' on a recipient to send them a consent link via email instead of returning an access token. Does not send — call saysigned_send_envelope to dispatch.\",\n  \"inputSchema\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"title\": { \"type\": \"string\", \"description\": \"Envelope title\" },\n      \"description\": { \"type\": \"string\", \"description\": \"Brief description\" },\n      \"contract\": {\n        \"type\": \"object\",\n        \"description\": \"Contract content. Use template_id + variables for templates, or provide raw clauses.\",\n        \"properties\": {\n          \"template_id\": { \"type\": \"string\", \"description\": \"Template ID (e.g., 'nda_mutual_v1', 'service_agreement_v1')\" },\n          \"variables\": { \"type\": \"object\", \"description\": \"Template variables (see GET /templates/:id for schema)\" },\n          \"clauses\": {\n            \"type\": \"array\",\n            \"description\": \"Custom clauses (when not using a template)\",\n            \"items\": {\n              \"type\": \"object\",\n              \"properties\": {\n                \"id\": { \"type\": \"string\" },\n                \"title\": { \"type\": \"string\" },\n                \"body\": { \"type\": \"string\" }\n              },\n              \"required\": [\"id\", \"title\", \"body\"]\n            }\n          },\n          \"signature_block\": {\n            \"type\": \"object\",\n            \"description\": \"Custom signature block (when not using a template)\"\n          }\n        }\n      },\n      \"recipients\": {\n        \"type\": \"array\",\n        \"items\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"name\": { \"type\": \"string\" },\n            \"email\": { \"type\": \"string\", \"format\": \"email\" },\n            \"role\": { \"type\": \"string\", \"enum\": [\"signer\", \"cc\", \"approver\", \"witness\"], \"default\": \"signer\" },\n            \"delivery_method\": { \"type\": \"string\", \"enum\": [\"api\", \"email\"], \"default\": \"api\", \"description\": \"'api' (default) for AI agent signing, 'email' to send a consent link to a human\" }\n          },\n          \"required\": [\"name\", \"email\"]\n        }\n      },\n      \"expires_at\": { \"type\": \"string\", \"format\": \"date-time\", \"description\": \"Expiration date (optional)\" },\n      \"metadata\": { \"type\": \"object\", \"description\": \"Custom key-value metadata (optional)\" }\n    },\n    \"required\": [\"title\", \"contract\", \"recipients\"]\n  }\n}\n\nREST: POST /envelopes — requires X-API-Key header.\n\nExample — Custom contract (no template):\n\ncurl -s -X POST https://api.saysigned.com/envelopes \\\n  -H \"Content-Type: application/json\" \\\n  -H \"X-API-Key: YOUR_API_KEY\" \\\n  -d '{\"title\": \"Data Processing Addendum\", \"contract\": {\"clauses\": [{\"id\": \"scope\", \"title\": \"1. Scope\", \"body\": \"This addendum governs the processing of personal data...\"}], \"signature_block\": {\"preamble\": \"Agreed and accepted:\", \"signers\": [{\"role\": \"Data Controller\", \"fields\": [\"signature\", \"printed_name\", \"date\"]}]}}, \"recipients\": [{\"name\": \"Controller Corp\", \"email\": \"legal@controller.com\", \"role\": \"signer\"}]}'"
      },
      {
        "title": "saysigned_send_envelope",
        "body": "Send a draft envelope for signing. Consumes 1 envelope from your quota.\n\n{\n  \"name\": \"saysigned_send_envelope\",\n  \"description\": \"Send a draft envelope for signing. For API recipients, generates access tokens. For email recipients, sends an invitation email with a consent link to doc.saysigned.com (no access_token in response). Consumes 1 envelope (free tier) or reports 1 metered event (paid). Returns 402 if free tier exhausted.\",\n  \"inputSchema\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"envelope_id\": { \"type\": \"string\", \"format\": \"uuid\", \"description\": \"ID of the draft envelope to send\" }\n    },\n    \"required\": [\"envelope_id\"]\n  }\n}\n\nREST: POST /envelopes/:id/send — requires X-API-Key header.\n\n402 response (free tier exhausted):\n\n{\"error\": \"Free tier envelope limit exhausted. Upgrade to a paid plan.\"}"
      },
      {
        "title": "saysigned_sign",
        "body": "Sign an envelope as a recipient.\n\n{\n  \"name\": \"saysigned_sign\",\n  \"description\": \"Sign an envelope as a recipient. Provide the access_token from the send response as a query parameter. No API key needed — the token is the auth. Records IP, user-agent, and timestamp in the audit trail.\",\n  \"inputSchema\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"envelope_id\": { \"type\": \"string\", \"format\": \"uuid\" },\n      \"recipient_id\": { \"type\": \"string\", \"format\": \"uuid\" },\n      \"access_token\": { \"type\": \"string\", \"description\": \"128-char hex token from the send response\" },\n      \"signature_data\": {\n        \"type\": \"object\",\n        \"description\": \"Signature metadata\",\n        \"properties\": {\n          \"full_name\": { \"type\": \"string\" },\n          \"title\": { \"type\": \"string\" },\n          \"company\": { \"type\": \"string\" }\n        },\n        \"required\": [\"full_name\"]\n      }\n    },\n    \"required\": [\"envelope_id\", \"recipient_id\", \"access_token\", \"signature_data\"]\n  }\n}\n\nREST: POST /envelopes/:id/recipients/:rid/sign with X-Access-Token: TOKEN header (recommended) or ?access_token=TOKEN query parameter. No API key needed."
      },
      {
        "title": "saysigned_decline",
        "body": "Decline to sign an envelope.\n\n{\n  \"name\": \"saysigned_decline\",\n  \"description\": \"Decline to sign an envelope as a recipient. Optionally provide a reason.\",\n  \"inputSchema\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"envelope_id\": { \"type\": \"string\", \"format\": \"uuid\" },\n      \"recipient_id\": { \"type\": \"string\", \"format\": \"uuid\" },\n      \"access_token\": { \"type\": \"string\" },\n      \"reason\": { \"type\": \"string\", \"description\": \"Reason for declining (optional)\" }\n    },\n    \"required\": [\"envelope_id\", \"recipient_id\", \"access_token\"]\n  }\n}\n\nREST: POST /envelopes/:id/recipients/:rid/decline with X-Access-Token: TOKEN header (recommended) or ?access_token=TOKEN query parameter. No API key needed."
      },
      {
        "title": "saysigned_get_envelope",
        "body": "Get envelope details and recipient status.\n\n{\n  \"name\": \"saysigned_get_envelope\",\n  \"description\": \"Get full details of an envelope including status, contract content, and recipient statuses.\",\n  \"inputSchema\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"envelope_id\": { \"type\": \"string\", \"format\": \"uuid\" }\n    },\n    \"required\": [\"envelope_id\"]\n  }\n}\n\nREST: GET /envelopes/:id — requires X-API-Key header."
      },
      {
        "title": "saysigned_void_envelope",
        "body": "Void (cancel) an envelope.\n\n{\n  \"name\": \"saysigned_void_envelope\",\n  \"description\": \"Void an envelope that has been sent but not yet completed. Cannot void completed envelopes.\",\n  \"inputSchema\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"envelope_id\": { \"type\": \"string\", \"format\": \"uuid\" },\n      \"reason\": { \"type\": \"string\", \"description\": \"Reason for voiding\" }\n    },\n    \"required\": [\"envelope_id\"]\n  }\n}\n\nREST: POST /envelopes/:id/void — requires X-API-Key header."
      },
      {
        "title": "saysigned_verify",
        "body": "Cryptographically verify an envelope. Public — no authentication required.\n\n{\n  \"name\": \"saysigned_verify\",\n  \"description\": \"Verify the cryptographic integrity of an envelope. Checks the hash chain audit trail and RFC 3161 timestamp. Public endpoint — no API key needed.\",\n  \"inputSchema\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"envelope_id\": { \"type\": \"string\", \"format\": \"uuid\" }\n    },\n    \"required\": [\"envelope_id\"]\n  }\n}\n\nREST: POST /verify\n\nSee the end-to-end example above for the real response format."
      },
      {
        "title": "saysigned_get_audit_trail",
        "body": "Get the full hash-chain audit trail for an envelope.\n\n{\n  \"name\": \"saysigned_get_audit_trail\",\n  \"description\": \"Get the complete cryptographic audit trail for an envelope, including hash chain verification status.\",\n  \"inputSchema\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"envelope_id\": { \"type\": \"string\", \"format\": \"uuid\" }\n    },\n    \"required\": [\"envelope_id\"]\n  }\n}\n\nREST: GET /envelopes/:id/audit-trail — requires X-API-Key header."
      },
      {
        "title": "saysigned_get_profile",
        "body": "Get your agent profile and current usage.\n\n{\n  \"name\": \"saysigned_get_profile\",\n  \"description\": \"Get your agent profile, plan details, and current usage statistics.\",\n  \"inputSchema\": { \"type\": \"object\", \"properties\": {} }\n}\n\nREST: GET /agents/me — requires X-API-Key header."
      },
      {
        "title": "saysigned_billing_setup",
        "body": "Upgrade from free tier to a paid plan. IMPORTANT: Always show the returned checkout_url to the human user so they can complete payment in their browser. Never silently consume the URL.\n\n{\n  \"name\": \"saysigned_billing_setup\",\n  \"description\": \"Upgrade to a paid plan. Creates a Stripe customer and returns a checkout URL. IMPORTANT: Always show the checkout_url to the human user so they can complete payment in their browser.\",\n  \"inputSchema\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"plan\": {\n        \"type\": \"string\",\n        \"enum\": [\"payg\", \"commit_500\", \"commit_2000\"],\n        \"description\": \"Plan to upgrade to. payg = $0.65/env, commit_500 (Pro) = $225/mo for 500 env, commit_2000 (Business) = $600/mo for 2000 env.\"\n      }\n    },\n    \"required\": [\"plan\"]\n  }\n}\n\nREST: POST /billing/setup — requires X-API-Key header."
      },
      {
        "title": "saysigned_get_usage",
        "body": "Get current billing period usage.\n\n{\n  \"name\": \"saysigned_get_usage\",\n  \"description\": \"Get envelope usage for the current billing period, including count, cost, and plan details.\",\n  \"inputSchema\": { \"type\": \"object\", \"properties\": {} }\n}\n\nREST: GET /billing/usage — requires X-API-Key header."
      },
      {
        "title": "Available Templates",
        "body": "Discover templates programmatically:\n\n# List all templates\ncurl https://api.saysigned.com/templates\n\n# Get template details with variable schema\ncurl https://api.saysigned.com/templates/nda_mutual_v1"
      },
      {
        "title": "nda_mutual_v1 — Mutual Non-Disclosure Agreement",
        "body": "Standard mutual NDA. Required variables: disclosing_party_name, receiving_party_name, effective_date, purpose, governing_law_state. Optional: term_years (default: 2), non_solicitation (default: false), etc."
      },
      {
        "title": "service_agreement_v1 — Professional Services Agreement",
        "body": "Professional services contract. Required variables: client_name, provider_name, effective_date, scope_of_services, compensation_model, governing_law_state. Supports 4 compensation models: fixed, hourly, milestone, retainer."
      },
      {
        "title": "Webhook Events",
        "body": "Configure your webhook_url during registration to receive real-time notifications.\n\nEventDescriptionrecipient.deliveredEnvelope sent, recipient notified (API recipients include access_token; email recipients do not)recipient.email_sentInvitation email dispatched to human recipientrecipient.viewedHuman recipient opened the consent pagerecipient.signedRecipient completed signingrecipient.declinedRecipient declined to signenvelope.completedAll signers done, timestamp anchoredenvelope.declinedA recipient declinedenvelope.voidedAgent voided the envelope"
      },
      {
        "title": "Webhook Payload",
        "body": "{\n  \"event\": \"recipient.signed\",\n  \"envelope_id\": \"env_...\",\n  \"timestamp\": \"2026-02-01T12:00:00Z\",\n  \"data\": {\n    \"recipient_id\": \"rec_...\",\n    \"recipient_name\": \"Alice Chen\",\n    \"recipient_email\": \"alice@acme.com\"\n  }\n}"
      },
      {
        "title": "Verifying Webhook Signatures",
        "body": "Every webhook includes an X-Signature-256 header containing sha256=<HMAC>. Verify it:\n\nimport hmac, hashlib\n\ndef verify_webhook(body: bytes, secret: str, signature_header: str) -> bool:\n    expected = \"sha256=\" + hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()\n    return hmac.compare_digest(expected, signature_header)"
      },
      {
        "title": "Two-Party NDA",
        "body": "1. POST /envelopes (template_id: \"nda_mutual_v1\", 2 signer recipients)\n2. POST /envelopes/:id/send\n3. Party A signs: POST /envelopes/:id/recipients/:r1/sign (X-Access-Token header)\n4. Party B signs: POST /envelopes/:id/recipients/:r2/sign (X-Access-Token header)\n5. → envelope.completed webhook fires, RFC 3161 timestamp anchored\n6. POST /verify to confirm integrity"
      },
      {
        "title": "Service Agreement with CC",
        "body": "1. POST /envelopes (template_id: \"service_agreement_v1\", 2 signers + 1 CC)\n2. POST /envelopes/:id/send\n3. Both signers sign (in any order)\n4. CC recipient receives notifications but doesn't sign\n5. → envelope.completed on last signature"
      },
      {
        "title": "Handling Declines",
        "body": "1. Recipient declines: POST /envelopes/:id/recipients/:rid/decline\n2. → recipient.declined webhook fires\n3. → envelope.declined webhook fires (envelope status → declined)\n4. Create a new envelope with revised terms if needed"
      },
      {
        "title": "Human Recipient Flow",
        "body": "1. POST /envelopes (set delivery_method: \"email\" on human recipients)\n2. POST /envelopes/:id/send → invitation email sent to human\n3. Human opens email → clicks \"Review & Give Consent\" link\n4. Human reviews contract at doc.saysigned.com → gives consent or declines\n5. → recipient.signed or recipient.declined webhook fires\n6. Envelope auto-completes when all signers consent\n7. Human receives confirmation email with 14-day document viewing link"
      },
      {
        "title": "Polling for Completion",
        "body": "If you don't use webhooks, poll for envelope completion:\n\n1. Call GET /envelopes/:id (or saysigned_get_envelope) to check status\n2. Poll every 5-10 seconds\n3. Terminal states: completed, declined, voided\n4. Max recommended wait: 24 hours"
      },
      {
        "title": "Error Handling",
        "body": "StatusMeaningWhat to Do400Bad request / validation errorCheck request body against schema401Invalid or missing API keyVerify X-API-Key header402Free tier exhaustedCall POST /billing/setup and show the checkout_url to the human user404Resource not foundCheck envelope/recipient IDs409Invalid state transitionCheck envelope/recipient status first429Rate limitedBack off and retry500Server errorRetry with exponential backoff"
      },
      {
        "title": "Security Notes",
        "body": "API keys are shown once at creation. Store securely. Rotate or delete keys that are no longer needed.\nConfig file security — API keys stored in MCP config files should have appropriate file permissions. Limit access to the config directory.\nAccess tokens are per-recipient, per-envelope. Do not reuse. Tokens are 128-character hex strings with no expiration — treat them as secrets for the duration of the signing flow.\nWebhook secrets are per-agent. Verify every webhook signature.\nAll connections require TLS (HTTPS). Verify TLS certificates for api.saysigned.com, mcp.saysigned.com, and doc.saysigned.com.\nAPI keys are stored as SHA-256 hashes — SaySigned never stores plaintext keys.\nURL tokens (email recipients) are 32-char HMAC-derived tokens, one-time use, cleared after consent.\nCSRF protection on consent/decline forms — tokens bound to recipient ID, expire after 4 hours.\nRate limiting on consent pages — 10 URL attempts per 15 min per IP, 3 form submissions per 5 min per recipient.\nSandboxing recommended — If running in an untrusted environment, enable logging/monitoring of config changes and outbound connections."
      },
      {
        "title": "Legal Context",
        "body": "ESIGN Act (15 U.S.C. § 7001) — Electronic signatures have the same legal standing as handwritten signatures.\nUETA Section 14 — Explicitly authorizes electronic agents to form contracts and conduct transactions.\nAttribution chain — Every signature records: who (name, email), when (timestamp), how (API, IP address, user-agent), and what (contract hash).\nRFC 3161 timestamp — Independent proof from DigiCert that the envelope was completed at a specific time.\nHash chain — Tamper-evident audit trail where each entry links to the previous via SHA-256.\nESIGN mandatory disclosures — four pre-consent disclosures displayed to human recipients: right to paper copies, right to withdraw consent, scope of consent (single document), and hardware/software requirements.\nAI-generated contract disclosure — if the contract was created from a template, a notice is displayed: \"This document was prepared using automated tools based on the sender's inputs.\""
      },
      {
        "title": "Pricing",
        "body": "PlanPriceEnvelopesFree$0/mo5/monthPay-as-you-go$0.65/envelopeUnlimitedPro$225/mo500 included, overage $0.45/envBusiness$600/mo2,000 included, overage $0.30/envEnterpriseCustom10,000+/month\n\nUpgrade anytime: POST /billing/setup with {\"plan\": \"payg\"}, {\"plan\": \"commit_500\"}, or {\"plan\": \"commit_2000\"}. The response includes a checkout_url — always present this link to the human user so they can complete payment in their browser."
      }
    ],
    "body": "SaySigned — AI Agent E-Signature Skill\n\nE-signatures for AI agents. Legally binding under ESIGN Act & UETA Section 14. Two delivery modes: API (agent-to-agent) and Email (agent-to-human). Register, sign contracts, verify — all via REST or consent UI.\n\nPublisher: SaySigned, Inc. Homepage: https://www.saysigned.com Documentation: https://www.saysigned.com/docs Base URL: https://api.saysigned.com MCP Endpoint: https://mcp.saysigned.com TLS Required: All endpoints enforce HTTPS. Verify certificates for *.saysigned.com before connecting.\n\nAuthentication: Pass your API key in the X-API-Key header. Keys start with ss_live_. Public endpoints (register, verify, templates) don't require authentication.\n\nMCP Server (Recommended for AI Agents)\n\nConnect directly to the remote MCP endpoint — no local install, no HTTP requests to craft.\n\nAuth note: MCP clients send Authorization: Bearer <api_key>. The MCP server extracts your key and forwards it as X-API-Key internally. For direct REST calls, use the X-API-Key header.\n\nSetup\n\nHuman approval required. Steps 1, 4, and 5 modify your MCP client configuration. Always show the user what you're about to change and ask for their confirmation before writing to config files or restarting the MCP client. Never modify config silently.\n\nStep 1. Show the user the following MCP config and ask for confirmation before adding it:\n\n{\n  \"mcpServers\": {\n    \"saysigned\": {\n      \"type\": \"streamable-http\",\n      \"url\": \"https://mcp.saysigned.com\"\n    }\n  }\n}\n\n\nAfter the user confirms, add it to the MCP config. No API key is needed yet — public tools work immediately.\n\nStep 2. Restart your MCP client so it loads the new server.\n\nStep 3. Call the saysigned_register tool (no auth required) with the agent's name and email. The response contains an api_key — save it immediately, it is shown once and cannot be retrieved later. Keys start with ss_live_.\n\nStep 4. Show the user the updated config with the auth header and ask for confirmation before writing it:\n\n{\n  \"mcpServers\": {\n    \"saysigned\": {\n      \"type\": \"streamable-http\",\n      \"url\": \"https://mcp.saysigned.com\",\n      \"headers\": {\n        \"Authorization\": \"Bearer ss_live_PASTE_YOUR_ACTUAL_KEY_HERE\"\n      }\n    }\n  }\n}\n\n\nStep 5. After the user confirms, restart your MCP client so it picks up the auth header. All authenticated tools now work.\n\nHuman-in-the-loop for config changes. The user must confirm before you (1) add or modify MCP config entries, (2) store API keys in config files, or (3) restart the MCP client. You may handle registration, envelope creation, signing, and verification autonomously — those are normal API operations that don't modify the agent's configuration.\n\nAlready have a key?\n\nIf you already have an API key (starts with ss_live_), skip to Step 4 — show the user the config with headers, get their confirmation, and restart.\n\nMCP Workflow (signing flow)\nPrepare the contract — two options:\nUse a template: call saysigned_list_templates → saysigned_get_template to discover required variables, then pass template_id + variables in the contract field.\nBring your own text: pass your own contract content in the clauses array. Each clause has id, title, and body. You can put an entire document into a single clause or split it into sections. Use this when you have your own NDA, agreement, or any custom document.\nCreate envelope — saysigned_create_envelope with title, contract, and recipients array.\nSend envelope — saysigned_send_envelope with the envelope_id. Response includes each recipient's id and access_token. Save these — they are needed for signing.\nSign — For API recipients: saysigned_sign for each recipient, passing their envelope_id, recipient_id, access_token, and signature_data (at minimum full_name). For email recipients: the human reviews and gives consent at doc.saysigned.com — no action needed from the agent.\nVerify — saysigned_verify with the envelope_id. No auth needed.\nCritical Notes for AI Agents\nYou don't have to use a template. If you have your own contract text (an NDA, agreement, policy — any document), pass it directly via clauses. Templates are a convenience, not a requirement.\nAlways call saysigned_get_template before creating an envelope with a template. The required variables differ per template and the API will reject unknown or missing variables.\nsaysigned_send_envelope returns access tokens. You must capture and use these tokens for signing. They are 128-character hex strings.\nSigning does not use your API key. The saysigned_sign and saysigned_decline tools authenticate via the access_token parameter, not the API key header.\nThe envelope auto-completes when the last recipient signs. You don't need to call a separate \"complete\" endpoint.\nAuth header is forwarded automatically. Authenticated tools (create_envelope, send_envelope, get_envelope, get_profile, etc.) use the API key from your MCP client's Authorization header. Public tools (register, verify, list_templates, get_template) work without it.\ndelivery_method: \"email\" sends a signing link to a real human. The human reviews the contract at doc.saysigned.com and gives consent via browser. Email recipients do NOT get an access_token in the send response — they authenticate via a URL token in their email link. Use delivery_method: \"api\" (default) for agent-to-agent signing.\nPoll or use webhooks for email recipients. Since humans sign asynchronously, use saysigned_get_envelope to poll for status changes or configure a webhook_url to receive recipient.viewed and recipient.signed events.\nAll 14 MCP Tools\nTool\tAuth\tDescription\nsaysigned_register\tNone\tRegister agent, get API key\nsaysigned_create_envelope\tAPI key\tCreate draft envelope\nsaysigned_send_envelope\tAPI key\tSend for signing, get access tokens\nsaysigned_get_envelope\tAPI key\tGet envelope details + status\nsaysigned_void_envelope\tAPI key\tCancel a sent (not completed) envelope\nsaysigned_sign\tAccess token\tSign as a recipient\nsaysigned_decline\tAccess token\tDecline to sign\nsaysigned_verify\tNone\tVerify cryptographic integrity\nsaysigned_get_audit_trail\tAPI key\tGet hash-chain audit trail\nsaysigned_list_templates\tNone\tList available contract templates\nsaysigned_get_template\tNone\tGet template details + required variables\nsaysigned_billing_setup\tAPI key\tUpgrade to paid plan\nsaysigned_get_usage\tAPI key\tCurrent billing period usage\nsaysigned_get_profile\tAPI key\tAgent profile + plan info\nComplete End-to-End Example\n\nThis is a full working flow: register, create an NDA, send it, both parties sign, then verify. Every response shown is from the real production API.\n\nStep 1 — Register (no auth needed)\ncurl -s -X POST https://api.saysigned.com/agents/register \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"name\": \"My AI Agent\", \"email\": \"agent@example.com\"}'\n\n{\n  \"agent_id\": \"7773b4af-44d1-44fc-8db7-05d9bc95b541\",\n  \"api_key\": \"ss_live_203bff0e53ba167462aa2cdcbd8e189e2909d37cf76c31da675cb1dba7dc0026\",\n  \"plan\": \"free\",\n  \"free_envelopes_remaining\": 5\n}\n\n\nSave the api_key — it is shown once and cannot be retrieved later.\n\nStep 2 — Create an envelope\n\nUse the api_key from step 1 in the X-API-Key header. Use template_id for standard contracts or provide custom clauses.\n\ncurl -s -X POST https://api.saysigned.com/envelopes \\\n  -H \"Content-Type: application/json\" \\\n  -H \"X-API-Key: ss_live_203bff0e53ba167462aa2cdcbd8e189e2909d37cf76c31da675cb1dba7dc0026\" \\\n  -d '{\n    \"title\": \"Mutual NDA — Acme & Beta\",\n    \"contract\": {\n      \"template_id\": \"nda_mutual_v1\",\n      \"variables\": {\n        \"disclosing_party_name\": \"Acme Corp\",\n        \"receiving_party_name\": \"Beta Inc\",\n        \"effective_date\": \"2026-02-09\",\n        \"purpose\": \"Evaluating a potential partnership\",\n        \"governing_law_state\": \"California\"\n      }\n    },\n    \"recipients\": [\n      {\"name\": \"Alice\", \"email\": \"alice@acme.com\", \"role\": \"signer\"},\n      {\"name\": \"Bob\", \"email\": \"bob@beta.com\", \"role\": \"signer\"}\n    ]\n  }'\n\n{\n  \"id\": \"2c272d15-cd4b-4c0d-96c0-bd7438b66699\",\n  \"status\": \"draft\",\n  \"title\": \"Mutual NDA — Acme & Beta\",\n  \"recipients\": [\n    {\"id\": \"62f73ba5-05a0-4223-8f55-e880047a7b3e\", \"name\": \"Alice\", \"status\": \"pending\"},\n    {\"id\": \"cc0aeb32-eb9d-420d-ab93-1d42400f85b9\", \"name\": \"Bob\", \"status\": \"pending\"}\n  ]\n}\n\n\nSave the envelope id and each recipient's id.\n\nStep 3 — Send the envelope\n\nThis consumes 1 envelope from your quota (free tier) or reports 1 metered event (paid plan). Recipients get access_token values — these are how they authenticate when signing.\n\ncurl -s -X POST https://api.saysigned.com/envelopes/2c272d15-cd4b-4c0d-96c0-bd7438b66699/send \\\n  -H \"X-API-Key: ss_live_203bff0e53ba167462aa2cdcbd8e189e2909d37cf76c31da675cb1dba7dc0026\"\n\n{\n  \"id\": \"2c272d15-cd4b-4c0d-96c0-bd7438b66699\",\n  \"status\": \"sent\",\n  \"recipients\": [\n    {\"id\": \"62f73ba5-05a0-4223-8f55-e880047a7b3e\", \"name\": \"Alice\", \"status\": \"sent\", \"access_token\": \"600b9b4297b2921396a1db138a80801c7dfb5101...\"},\n    {\"id\": \"cc0aeb32-eb9d-420d-ab93-1d42400f85b9\", \"name\": \"Bob\", \"status\": \"sent\", \"access_token\": \"952340a770b96740e2696c306ad490b28556952e...\"}\n  ]\n}\n\n\nSave each recipient's access_token. Distribute tokens to the respective signers.\n\nStep 4 — Recipients sign\n\nEach recipient signs using their access_token via the X-Access-Token header (recommended) or ?access_token= query parameter. No API key needed — the token is the auth.\n\n# Alice signs\ncurl -s -X POST \"https://api.saysigned.com/envelopes/2c272d15-cd4b-4c0d-96c0-bd7438b66699/recipients/62f73ba5-05a0-4223-8f55-e880047a7b3e/sign\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"X-Access-Token: 600b9b4297b2921396a1db138a80801c7dfb5101...\" \\\n  -d '{\"signature_data\": {\"full_name\": \"Alice Chen\", \"title\": \"CEO\"}}'\n\n{\n  \"signed\": true,\n  \"recipient_id\": \"62f73ba5-05a0-4223-8f55-e880047a7b3e\",\n  \"envelope_id\": \"2c272d15-cd4b-4c0d-96c0-bd7438b66699\",\n  \"signed_at\": \"2026-02-09T04:09:24.111Z\"\n}\n\n# Bob signs (last signer → envelope automatically completes with RFC 3161 timestamp)\ncurl -s -X POST \"https://api.saysigned.com/envelopes/2c272d15-cd4b-4c0d-96c0-bd7438b66699/recipients/cc0aeb32-eb9d-420d-ab93-1d42400f85b9/sign\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"X-Access-Token: 952340a770b96740e2696c306ad490b28556952e...\" \\\n  -d '{\"signature_data\": {\"full_name\": \"Bob Smith\", \"title\": \"CTO\"}}'\n\n{\n  \"signed\": true,\n  \"recipient_id\": \"cc0aeb32-eb9d-420d-ab93-1d42400f85b9\",\n  \"envelope_id\": \"2c272d15-cd4b-4c0d-96c0-bd7438b66699\",\n  \"signed_at\": \"2026-02-09T04:09:25.190Z\"\n}\n\nStep 5 — Verify (public, no auth needed)\ncurl -s -X POST https://api.saysigned.com/verify \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"envelope_id\": \"2c272d15-cd4b-4c0d-96c0-bd7438b66699\"}'\n\n{\n  \"envelope_id\": \"2c272d15-cd4b-4c0d-96c0-bd7438b66699\",\n  \"status\": \"completed\",\n  \"chain_verification\": {\"valid\": true, \"entries_checked\": 5},\n  \"timestamp_verification\": {\n    \"valid\": true,\n    \"gen_time\": \"2026-02-09T04:09:25+00:00\",\n    \"tsa_name\": \"DigiCert SHA256 RSA4096 Timestamp Responder 2025 1\"\n  },\n  \"recipients\": [\n    {\"name\": \"Alice\", \"email\": \"alice@acme.com\", \"role\": \"signer\", \"status\": \"signed\", \"signed_at\": \"2026-02-09T04:09:24.111Z\"},\n    {\"name\": \"Bob\", \"email\": \"bob@beta.com\", \"role\": \"signer\", \"status\": \"signed\", \"signed_at\": \"2026-02-09T04:09:25.190Z\"}\n  ],\n  \"completed_at\": \"2026-02-09T04:09:25.039Z\"\n}\n\nBring Your Own Contract\n\nYou don't need a template. If you have your own document text — an NDA your user provided, a custom agreement, any markdown or plain text — pass it directly via clauses. Each clause needs an id, title, and body.\n\nSingle-clause approach (simplest — put the entire document in one clause):\n\ncurl -s -X POST https://api.saysigned.com/envelopes \\\n  -H \"Content-Type: application/json\" \\\n  -H \"X-API-Key: YOUR_API_KEY\" \\\n  -d '{\n    \"title\": \"NDA — Acme & Beta\",\n    \"contract\": {\n      \"clauses\": [\n        {\n          \"id\": \"nda\",\n          \"title\": \"Mutual Non-Disclosure Agreement\",\n          \"body\": \"This Mutual Non-Disclosure Agreement is entered into by Acme Corp (\\\"Party A\\\") and Beta Inc (\\\"Party B\\\")...\\n\\n1. Definition of Confidential Information...\\n\\n2. Obligations of Receiving Party...\\n\\n3. Term. This Agreement shall remain in effect for two (2) years...\"\n        }\n      ]\n    },\n    \"recipients\": [\n      {\"name\": \"Alice\", \"email\": \"alice@acme.com\", \"role\": \"signer\"},\n      {\"name\": \"Bob\", \"email\": \"bob@beta.com\", \"role\": \"signer\"}\n    ]\n  }'\n\n\nMulti-clause approach (split into sections for cleaner presentation):\n\ncurl -s -X POST https://api.saysigned.com/envelopes \\\n  -H \"Content-Type: application/json\" \\\n  -H \"X-API-Key: YOUR_API_KEY\" \\\n  -d '{\n    \"title\": \"NDA — Acme & Beta\",\n    \"contract\": {\n      \"clauses\": [\n        {\"id\": \"1\", \"title\": \"1. Definition of Confidential Information\", \"body\": \"\\\"Confidential Information\\\" means any non-public information disclosed by either party...\"},\n        {\"id\": \"2\", \"title\": \"2. Obligations of Receiving Party\", \"body\": \"The Receiving Party shall hold all Confidential Information in strict confidence...\"},\n        {\"id\": \"3\", \"title\": \"3. Term and Termination\", \"body\": \"This Agreement shall remain in effect for two (2) years from the Effective Date...\"},\n        {\"id\": \"4\", \"title\": \"4. Governing Law\", \"body\": \"This Agreement shall be governed by the laws of the State of California...\"}\n      ]\n    },\n    \"recipients\": [\n      {\"name\": \"Alice\", \"email\": \"alice@acme.com\", \"role\": \"signer\"},\n      {\"name\": \"Bob\", \"email\": \"bob@beta.com\", \"role\": \"signer\"}\n    ]\n  }'\n\n\nMCP equivalent — same structure, just pass it to saysigned_create_envelope:\n\n{\n  \"title\": \"NDA — Acme & Beta\",\n  \"contract\": {\n    \"clauses\": [\n      {\"id\": \"nda\", \"title\": \"Mutual Non-Disclosure Agreement\", \"body\": \"Full text of the NDA here...\"}\n    ]\n  },\n  \"recipients\": [\n    {\"name\": \"Alice\", \"email\": \"alice@acme.com\", \"role\": \"signer\"},\n    {\"name\": \"Bob\", \"email\": \"bob@beta.com\", \"role\": \"signer\"}\n  ]\n}\n\n\nWhen to use which: Use templates when you want a standard contract and just need to fill in the blanks (party names, dates, etc.). Use custom clauses when you already have the contract text — from a file, a user-provided document, or your own drafting.\n\nKey Concepts\nIDs are UUIDs — all envelope, recipient, and agent IDs are standard UUIDs (e.g., 2c272d15-cd4b-4c0d-96c0-bd7438b66699)\nAccess tokens are hex strings — 128-character hex, passed via X-Access-Token header (recommended) or ?access_token= query parameter\nAPI keys start with ss_live_ — REST: pass in X-API-Key header. MCP: pass as Authorization: Bearer <key> (auto-translated)\nTemplates — use GET /templates to discover available templates and GET /templates/:id to see required variables\nSigning order — all recipients sign in parallel (no enforced order)\nCompletion — when the last signer signs, the envelope auto-completes with an RFC 3161 timestamp from DigiCert\n\nFree tier: 5 envelopes/month, no credit card required. Upgrade: POST /billing/setup with {\"plan\": \"payg\"}.\n\nDelivery methods — \"api\" (default) returns access tokens for programmatic signing; \"email\" sends an invitation email to a human who reviews and gives consent at doc.saysigned.com\nEmail recipients — human recipients authenticate via a URL token in their email link, not an access token. They review the contract in a browser and click \"Give Consent\" or \"Decline\"\nMCP Tool Definitions\n\nThese tool definitions are compatible with MCP (Model Context Protocol) runtimes. Each tool maps to a REST API endpoint.\n\nsaysigned_register\n\nCreate an agent account and get your API key.\n\n{\n  \"name\": \"saysigned_register\",\n  \"description\": \"Register a new AI agent account on SaySigned. Returns an API key (shown once — save it). Free tier: 5 envelopes/month.\",\n  \"inputSchema\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"name\": { \"type\": \"string\", \"description\": \"Agent or organization name\" },\n      \"email\": { \"type\": \"string\", \"format\": \"email\", \"description\": \"Contact email (must be unique)\" },\n      \"company_name\": { \"type\": \"string\", \"description\": \"Company name (optional)\" },\n      \"webhook_url\": { \"type\": \"string\", \"format\": \"uri\", \"description\": \"URL to receive webhook notifications (optional)\" }\n    },\n    \"required\": [\"name\", \"email\"]\n  }\n}\n\n\nREST: POST /agents/register — no auth required.\n\nsaysigned_create_envelope\n\nCreate a draft envelope with a contract and recipients.\n\n{\n  \"name\": \"saysigned_create_envelope\",\n  \"description\": \"Create a draft envelope with contract content and recipients. Two ways to provide a contract: (1) Use template_id + variables for standard contracts (call saysigned_get_template first), or (2) provide your own contract text via the clauses array — each clause has an id, title, and body. You can put an entire document into a single clause or split it into sections. Use option 2 when you have your own NDA, agreement, or any custom document text. Set delivery_method: 'email' on a recipient to send them a consent link via email instead of returning an access token. Does not send — call saysigned_send_envelope to dispatch.\",\n  \"inputSchema\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"title\": { \"type\": \"string\", \"description\": \"Envelope title\" },\n      \"description\": { \"type\": \"string\", \"description\": \"Brief description\" },\n      \"contract\": {\n        \"type\": \"object\",\n        \"description\": \"Contract content. Use template_id + variables for templates, or provide raw clauses.\",\n        \"properties\": {\n          \"template_id\": { \"type\": \"string\", \"description\": \"Template ID (e.g., 'nda_mutual_v1', 'service_agreement_v1')\" },\n          \"variables\": { \"type\": \"object\", \"description\": \"Template variables (see GET /templates/:id for schema)\" },\n          \"clauses\": {\n            \"type\": \"array\",\n            \"description\": \"Custom clauses (when not using a template)\",\n            \"items\": {\n              \"type\": \"object\",\n              \"properties\": {\n                \"id\": { \"type\": \"string\" },\n                \"title\": { \"type\": \"string\" },\n                \"body\": { \"type\": \"string\" }\n              },\n              \"required\": [\"id\", \"title\", \"body\"]\n            }\n          },\n          \"signature_block\": {\n            \"type\": \"object\",\n            \"description\": \"Custom signature block (when not using a template)\"\n          }\n        }\n      },\n      \"recipients\": {\n        \"type\": \"array\",\n        \"items\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"name\": { \"type\": \"string\" },\n            \"email\": { \"type\": \"string\", \"format\": \"email\" },\n            \"role\": { \"type\": \"string\", \"enum\": [\"signer\", \"cc\", \"approver\", \"witness\"], \"default\": \"signer\" },\n            \"delivery_method\": { \"type\": \"string\", \"enum\": [\"api\", \"email\"], \"default\": \"api\", \"description\": \"'api' (default) for AI agent signing, 'email' to send a consent link to a human\" }\n          },\n          \"required\": [\"name\", \"email\"]\n        }\n      },\n      \"expires_at\": { \"type\": \"string\", \"format\": \"date-time\", \"description\": \"Expiration date (optional)\" },\n      \"metadata\": { \"type\": \"object\", \"description\": \"Custom key-value metadata (optional)\" }\n    },\n    \"required\": [\"title\", \"contract\", \"recipients\"]\n  }\n}\n\n\nREST: POST /envelopes — requires X-API-Key header.\n\nExample — Custom contract (no template):\n\ncurl -s -X POST https://api.saysigned.com/envelopes \\\n  -H \"Content-Type: application/json\" \\\n  -H \"X-API-Key: YOUR_API_KEY\" \\\n  -d '{\"title\": \"Data Processing Addendum\", \"contract\": {\"clauses\": [{\"id\": \"scope\", \"title\": \"1. Scope\", \"body\": \"This addendum governs the processing of personal data...\"}], \"signature_block\": {\"preamble\": \"Agreed and accepted:\", \"signers\": [{\"role\": \"Data Controller\", \"fields\": [\"signature\", \"printed_name\", \"date\"]}]}}, \"recipients\": [{\"name\": \"Controller Corp\", \"email\": \"legal@controller.com\", \"role\": \"signer\"}]}'\n\nsaysigned_send_envelope\n\nSend a draft envelope for signing. Consumes 1 envelope from your quota.\n\n{\n  \"name\": \"saysigned_send_envelope\",\n  \"description\": \"Send a draft envelope for signing. For API recipients, generates access tokens. For email recipients, sends an invitation email with a consent link to doc.saysigned.com (no access_token in response). Consumes 1 envelope (free tier) or reports 1 metered event (paid). Returns 402 if free tier exhausted.\",\n  \"inputSchema\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"envelope_id\": { \"type\": \"string\", \"format\": \"uuid\", \"description\": \"ID of the draft envelope to send\" }\n    },\n    \"required\": [\"envelope_id\"]\n  }\n}\n\n\nREST: POST /envelopes/:id/send — requires X-API-Key header.\n\n402 response (free tier exhausted):\n\n{\"error\": \"Free tier envelope limit exhausted. Upgrade to a paid plan.\"}\n\nsaysigned_sign\n\nSign an envelope as a recipient.\n\n{\n  \"name\": \"saysigned_sign\",\n  \"description\": \"Sign an envelope as a recipient. Provide the access_token from the send response as a query parameter. No API key needed — the token is the auth. Records IP, user-agent, and timestamp in the audit trail.\",\n  \"inputSchema\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"envelope_id\": { \"type\": \"string\", \"format\": \"uuid\" },\n      \"recipient_id\": { \"type\": \"string\", \"format\": \"uuid\" },\n      \"access_token\": { \"type\": \"string\", \"description\": \"128-char hex token from the send response\" },\n      \"signature_data\": {\n        \"type\": \"object\",\n        \"description\": \"Signature metadata\",\n        \"properties\": {\n          \"full_name\": { \"type\": \"string\" },\n          \"title\": { \"type\": \"string\" },\n          \"company\": { \"type\": \"string\" }\n        },\n        \"required\": [\"full_name\"]\n      }\n    },\n    \"required\": [\"envelope_id\", \"recipient_id\", \"access_token\", \"signature_data\"]\n  }\n}\n\n\nREST: POST /envelopes/:id/recipients/:rid/sign with X-Access-Token: TOKEN header (recommended) or ?access_token=TOKEN query parameter. No API key needed.\n\nsaysigned_decline\n\nDecline to sign an envelope.\n\n{\n  \"name\": \"saysigned_decline\",\n  \"description\": \"Decline to sign an envelope as a recipient. Optionally provide a reason.\",\n  \"inputSchema\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"envelope_id\": { \"type\": \"string\", \"format\": \"uuid\" },\n      \"recipient_id\": { \"type\": \"string\", \"format\": \"uuid\" },\n      \"access_token\": { \"type\": \"string\" },\n      \"reason\": { \"type\": \"string\", \"description\": \"Reason for declining (optional)\" }\n    },\n    \"required\": [\"envelope_id\", \"recipient_id\", \"access_token\"]\n  }\n}\n\n\nREST: POST /envelopes/:id/recipients/:rid/decline with X-Access-Token: TOKEN header (recommended) or ?access_token=TOKEN query parameter. No API key needed.\n\nsaysigned_get_envelope\n\nGet envelope details and recipient status.\n\n{\n  \"name\": \"saysigned_get_envelope\",\n  \"description\": \"Get full details of an envelope including status, contract content, and recipient statuses.\",\n  \"inputSchema\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"envelope_id\": { \"type\": \"string\", \"format\": \"uuid\" }\n    },\n    \"required\": [\"envelope_id\"]\n  }\n}\n\n\nREST: GET /envelopes/:id — requires X-API-Key header.\n\nsaysigned_void_envelope\n\nVoid (cancel) an envelope.\n\n{\n  \"name\": \"saysigned_void_envelope\",\n  \"description\": \"Void an envelope that has been sent but not yet completed. Cannot void completed envelopes.\",\n  \"inputSchema\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"envelope_id\": { \"type\": \"string\", \"format\": \"uuid\" },\n      \"reason\": { \"type\": \"string\", \"description\": \"Reason for voiding\" }\n    },\n    \"required\": [\"envelope_id\"]\n  }\n}\n\n\nREST: POST /envelopes/:id/void — requires X-API-Key header.\n\nsaysigned_verify\n\nCryptographically verify an envelope. Public — no authentication required.\n\n{\n  \"name\": \"saysigned_verify\",\n  \"description\": \"Verify the cryptographic integrity of an envelope. Checks the hash chain audit trail and RFC 3161 timestamp. Public endpoint — no API key needed.\",\n  \"inputSchema\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"envelope_id\": { \"type\": \"string\", \"format\": \"uuid\" }\n    },\n    \"required\": [\"envelope_id\"]\n  }\n}\n\n\nREST: POST /verify\n\nSee the end-to-end example above for the real response format.\n\nsaysigned_get_audit_trail\n\nGet the full hash-chain audit trail for an envelope.\n\n{\n  \"name\": \"saysigned_get_audit_trail\",\n  \"description\": \"Get the complete cryptographic audit trail for an envelope, including hash chain verification status.\",\n  \"inputSchema\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"envelope_id\": { \"type\": \"string\", \"format\": \"uuid\" }\n    },\n    \"required\": [\"envelope_id\"]\n  }\n}\n\n\nREST: GET /envelopes/:id/audit-trail — requires X-API-Key header.\n\nsaysigned_get_profile\n\nGet your agent profile and current usage.\n\n{\n  \"name\": \"saysigned_get_profile\",\n  \"description\": \"Get your agent profile, plan details, and current usage statistics.\",\n  \"inputSchema\": { \"type\": \"object\", \"properties\": {} }\n}\n\n\nREST: GET /agents/me — requires X-API-Key header.\n\nsaysigned_billing_setup\n\nUpgrade from free tier to a paid plan. IMPORTANT: Always show the returned checkout_url to the human user so they can complete payment in their browser. Never silently consume the URL.\n\n{\n  \"name\": \"saysigned_billing_setup\",\n  \"description\": \"Upgrade to a paid plan. Creates a Stripe customer and returns a checkout URL. IMPORTANT: Always show the checkout_url to the human user so they can complete payment in their browser.\",\n  \"inputSchema\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"plan\": {\n        \"type\": \"string\",\n        \"enum\": [\"payg\", \"commit_500\", \"commit_2000\"],\n        \"description\": \"Plan to upgrade to. payg = $0.65/env, commit_500 (Pro) = $225/mo for 500 env, commit_2000 (Business) = $600/mo for 2000 env.\"\n      }\n    },\n    \"required\": [\"plan\"]\n  }\n}\n\n\nREST: POST /billing/setup — requires X-API-Key header.\n\nsaysigned_get_usage\n\nGet current billing period usage.\n\n{\n  \"name\": \"saysigned_get_usage\",\n  \"description\": \"Get envelope usage for the current billing period, including count, cost, and plan details.\",\n  \"inputSchema\": { \"type\": \"object\", \"properties\": {} }\n}\n\n\nREST: GET /billing/usage — requires X-API-Key header.\n\nAvailable Templates\n\nDiscover templates programmatically:\n\n# List all templates\ncurl https://api.saysigned.com/templates\n\n# Get template details with variable schema\ncurl https://api.saysigned.com/templates/nda_mutual_v1\n\nnda_mutual_v1 — Mutual Non-Disclosure Agreement\n\nStandard mutual NDA. Required variables: disclosing_party_name, receiving_party_name, effective_date, purpose, governing_law_state. Optional: term_years (default: 2), non_solicitation (default: false), etc.\n\nservice_agreement_v1 — Professional Services Agreement\n\nProfessional services contract. Required variables: client_name, provider_name, effective_date, scope_of_services, compensation_model, governing_law_state. Supports 4 compensation models: fixed, hourly, milestone, retainer.\n\nWebhook Events\n\nConfigure your webhook_url during registration to receive real-time notifications.\n\nEvent\tDescription\nrecipient.delivered\tEnvelope sent, recipient notified (API recipients include access_token; email recipients do not)\nrecipient.email_sent\tInvitation email dispatched to human recipient\nrecipient.viewed\tHuman recipient opened the consent page\nrecipient.signed\tRecipient completed signing\nrecipient.declined\tRecipient declined to sign\nenvelope.completed\tAll signers done, timestamp anchored\nenvelope.declined\tA recipient declined\nenvelope.voided\tAgent voided the envelope\nWebhook Payload\n{\n  \"event\": \"recipient.signed\",\n  \"envelope_id\": \"env_...\",\n  \"timestamp\": \"2026-02-01T12:00:00Z\",\n  \"data\": {\n    \"recipient_id\": \"rec_...\",\n    \"recipient_name\": \"Alice Chen\",\n    \"recipient_email\": \"alice@acme.com\"\n  }\n}\n\nVerifying Webhook Signatures\n\nEvery webhook includes an X-Signature-256 header containing sha256=<HMAC>. Verify it:\n\nimport hmac, hashlib\n\ndef verify_webhook(body: bytes, secret: str, signature_header: str) -> bool:\n    expected = \"sha256=\" + hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()\n    return hmac.compare_digest(expected, signature_header)\n\nCommon Workflows\nTwo-Party NDA\n1. POST /envelopes (template_id: \"nda_mutual_v1\", 2 signer recipients)\n2. POST /envelopes/:id/send\n3. Party A signs: POST /envelopes/:id/recipients/:r1/sign (X-Access-Token header)\n4. Party B signs: POST /envelopes/:id/recipients/:r2/sign (X-Access-Token header)\n5. → envelope.completed webhook fires, RFC 3161 timestamp anchored\n6. POST /verify to confirm integrity\n\nService Agreement with CC\n1. POST /envelopes (template_id: \"service_agreement_v1\", 2 signers + 1 CC)\n2. POST /envelopes/:id/send\n3. Both signers sign (in any order)\n4. CC recipient receives notifications but doesn't sign\n5. → envelope.completed on last signature\n\nHandling Declines\n1. Recipient declines: POST /envelopes/:id/recipients/:rid/decline\n2. → recipient.declined webhook fires\n3. → envelope.declined webhook fires (envelope status → declined)\n4. Create a new envelope with revised terms if needed\n\nHuman Recipient Flow\n1. POST /envelopes (set delivery_method: \"email\" on human recipients)\n2. POST /envelopes/:id/send → invitation email sent to human\n3. Human opens email → clicks \"Review & Give Consent\" link\n4. Human reviews contract at doc.saysigned.com → gives consent or declines\n5. → recipient.signed or recipient.declined webhook fires\n6. Envelope auto-completes when all signers consent\n7. Human receives confirmation email with 14-day document viewing link\n\nPolling for Completion\n\nIf you don't use webhooks, poll for envelope completion:\n\n1. Call GET /envelopes/:id (or saysigned_get_envelope) to check status\n2. Poll every 5-10 seconds\n3. Terminal states: completed, declined, voided\n4. Max recommended wait: 24 hours\n\nError Handling\nStatus\tMeaning\tWhat to Do\n400\tBad request / validation error\tCheck request body against schema\n401\tInvalid or missing API key\tVerify X-API-Key header\n402\tFree tier exhausted\tCall POST /billing/setup and show the checkout_url to the human user\n404\tResource not found\tCheck envelope/recipient IDs\n409\tInvalid state transition\tCheck envelope/recipient status first\n429\tRate limited\tBack off and retry\n500\tServer error\tRetry with exponential backoff\nSecurity Notes\nAPI keys are shown once at creation. Store securely. Rotate or delete keys that are no longer needed.\nConfig file security — API keys stored in MCP config files should have appropriate file permissions. Limit access to the config directory.\nAccess tokens are per-recipient, per-envelope. Do not reuse. Tokens are 128-character hex strings with no expiration — treat them as secrets for the duration of the signing flow.\nWebhook secrets are per-agent. Verify every webhook signature.\nAll connections require TLS (HTTPS). Verify TLS certificates for api.saysigned.com, mcp.saysigned.com, and doc.saysigned.com.\nAPI keys are stored as SHA-256 hashes — SaySigned never stores plaintext keys.\nURL tokens (email recipients) are 32-char HMAC-derived tokens, one-time use, cleared after consent.\nCSRF protection on consent/decline forms — tokens bound to recipient ID, expire after 4 hours.\nRate limiting on consent pages — 10 URL attempts per 15 min per IP, 3 form submissions per 5 min per recipient.\nSandboxing recommended — If running in an untrusted environment, enable logging/monitoring of config changes and outbound connections.\nLegal Context\nESIGN Act (15 U.S.C. § 7001) — Electronic signatures have the same legal standing as handwritten signatures.\nUETA Section 14 — Explicitly authorizes electronic agents to form contracts and conduct transactions.\nAttribution chain — Every signature records: who (name, email), when (timestamp), how (API, IP address, user-agent), and what (contract hash).\nRFC 3161 timestamp — Independent proof from DigiCert that the envelope was completed at a specific time.\nHash chain — Tamper-evident audit trail where each entry links to the previous via SHA-256.\nESIGN mandatory disclosures — four pre-consent disclosures displayed to human recipients: right to paper copies, right to withdraw consent, scope of consent (single document), and hardware/software requirements.\nAI-generated contract disclosure — if the contract was created from a template, a notice is displayed: \"This document was prepared using automated tools based on the sender's inputs.\"\nPricing\nPlan\tPrice\tEnvelopes\nFree\t$0/mo\t5/month\nPay-as-you-go\t$0.65/envelope\tUnlimited\nPro\t$225/mo\t500 included, overage $0.45/env\nBusiness\t$600/mo\t2,000 included, overage $0.30/env\nEnterprise\tCustom\t10,000+/month\n\nUpgrade anytime: POST /billing/setup with {\"plan\": \"payg\"}, {\"plan\": \"commit_500\"}, or {\"plan\": \"commit_2000\"}. The response includes a checkout_url — always present this link to the human user so they can complete payment in their browser."
  },
  "trust": {
    "sourceLabel": "tencent",
    "provenanceUrl": "https://clawhub.ai/klsv/saysigned",
    "publisherUrl": "https://clawhub.ai/klsv/saysigned",
    "owner": "klsv",
    "version": "1.0.1",
    "license": null,
    "verificationStatus": "Indexed source record"
  },
  "links": {
    "detailUrl": "https://openagent3.xyz/skills/saysigned",
    "downloadUrl": "https://openagent3.xyz/downloads/saysigned",
    "agentUrl": "https://openagent3.xyz/skills/saysigned/agent",
    "manifestUrl": "https://openagent3.xyz/skills/saysigned/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/saysigned/agent.md"
  }
}