{
  "openapi": "3.1.0",
  "info": {
    "title": "Atlas ESIGN API",
    "version": "2.0.0",
    "description": "The ESIGN API built for autonomous agents. Pass any PDF or DOCX. Atlas detects signing fields, emails the signer, and returns a SHA-256 audit trail. Saved templates pin field layout for repeat sends.",
    "contact": {
      "name": "Atlas Support",
      "url": "https://atlaswork.ai"
    },
    "license": {
      "name": "Proprietary"
    }
  },
  "servers": [
    { "url": "https://atlaswork.ai", "description": "Production" },
    { "url": "http://localhost:3000", "description": "Local development" }
  ],
  "components": {
    "securitySchemes": {
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "API key obtained from the Atlas Dashboard, or OAuth access token."
      }
    },
    "schemas": {
      "Party": {
        "type": "object",
        "description": "Canonical signing party. Single source of truth for who signs and in what order.",
        "properties": {
          "role":      { "type": "string", "description": "Free-form role label (e.g. 'Tenant', 'ACME Inc.'). Used to match detected fields to parties." },
          "role_type": { "type": "string", "description": "Canonical role type (e.g. 'tenant', 'landlord'). Also accepts 'cc' to mark a carbon-copy recipient." },
          "email":     { "type": "string", "format": "email", "description": "Required for signing parties." },
          "name":      { "type": "string" },
          "order":     { "type": "integer", "minimum": 1, "description": "Signing sequence. Defaults to position in the array." },
          "company":   { "type": "string", "description": "Optional. Used for field matching on bilateral contracts (e.g. 'ACME Inc.' matches 'ACME Inc. Signature')." },
          "status":    { "type": "string", "enum": ["pending", "waiting", "signed", "declined"], "readOnly": true }
        },
        "required": ["email"]
      },
      "Field": {
        "type": "object",
        "description": "Top-left origin, fractions of page (0.0 to 1.0).",
        "properties": {
          "label":         { "type": "string" },
          "type":          { "type": "string", "enum": ["signature", "initials", "date", "text", "number", "checkbox"] },
          "page":          { "type": "integer", "minimum": 1 },
          "x_pct":         { "type": "number", "minimum": 0, "maximum": 1 },
          "y_pct":         { "type": "number", "minimum": 0, "maximum": 1 },
          "w_pct":         { "type": "number", "minimum": 0, "maximum": 1 },
          "h_pct":         { "type": "number", "minimum": 0, "maximum": 1 },
          "party":         { "type": "string", "description": "Role label matching one of the parties." },
          "party_order":   { "type": "integer", "description": "Resolved signing order for this field's owning party." },
          "initial_value": { "type": "string", "description": "Pre-filled value, editable by the signer." }
        },
        "required": ["label", "type", "page", "x_pct", "y_pct", "w_pct", "h_pct"]
      },
      "EnvelopeSummary": {
        "type": "object",
        "properties": {
          "id":             { "type": "string", "format": "uuid" },
          "status":         { "type": "string", "enum": ["draft", "pending", "signed", "declined", "voided"] },
          "parties":        { "type": "array", "items": { "$ref": "#/components/schemas/Party" }, "nullable": true },
          "agent_identity": { "type": "string", "nullable": true },
          "created_at":     { "type": "string", "format": "date-time" },
          "expires_at":     { "type": "string", "format": "date-time", "nullable": true },
          "document_hash":  { "type": "string", "nullable": true },
          "signed_at":      { "type": "string", "format": "date-time", "nullable": true },
          "decline_reason": { "type": "string", "nullable": true }
        }
      },
      "EnvelopeDetail": {
        "allOf": [
          { "$ref": "#/components/schemas/EnvelopeSummary" },
          {
            "type": "object",
            "properties": {
              "webhook_url":   { "type": "string", "nullable": true },
              "variables":     { "type": "object", "additionalProperties": { "type": "string" } },
              "fields":        { "type": "array", "items": { "$ref": "#/components/schemas/Field" } },
              "fields_status": { "type": "string", "enum": ["pending", "ready", "failed", "recovered_empty"] },
              "parties":       { "type": "array", "items": { "$ref": "#/components/schemas/Party" } }
            }
          }
        ]
      },
      "Error": {
        "type": "object",
        "properties": {
          "success": { "type": "boolean", "enum": [false] },
          "error":   { "type": "string" }
        },
        "required": ["success", "error"]
      },
      "TemplateSummary": {
        "type": "object",
        "properties": {
          "id":            { "type": "string", "format": "uuid" },
          "name":          { "type": "string" },
          "status":        { "type": "string", "enum": ["needs_template_signature", "active", "archived"] },
          "status_label":  { "type": "string", "description": "Human label: Ready, Setup, or Deleted." },
          "subtitle":      { "type": "string" },
          "ready":         { "type": "boolean", "description": "True when send_contract_from_template is allowed." },
          "use_count":     { "type": "integer" },
          "last_used_at":  { "type": "string", "format": "date-time", "nullable": true },
          "created_at":    { "type": "string", "format": "date-time" },
          "updated_at":    { "type": "string", "format": "date-time" }
        }
      },
      "TemplateSendParty": {
        "type": "object",
        "properties": {
          "role_name": { "type": "string", "description": "Must match parties_for_send from GET /api/templates/{id}." },
          "email":     { "type": "string", "format": "email" },
          "name":      { "type": "string" },
          "company":   { "type": "string" },
          "order":     { "type": "integer", "minimum": 1 }
        },
        "required": ["role_name", "email"]
      }
    }
  },
  "security": [{ "BearerAuth": [] }],
  "paths": {
    "/api/envelope": {
      "post": {
        "operationId": "createEnvelope",
        "summary": "Create a signing envelope",
        "description": "Single endpoint, two transports. The route dispatches on Content-Type:\n- application/json with `document` (base64), `document_url`, or `document_id`\n- multipart/form-data with a `file` part plus form params\n\nRuns the AI field detection pipeline. Returns review_url by default.\n\nWith `document` and no `parties`, the envelope is created in `draft` status. Call POST /api/envelope/{id}/send to activate.\n\n`document_id` references a row from POST /api/documents (same account). Branded URLs like `https://atlaswork.ai/d/{id}` are accepted as `document_url` and resolved server-side.\n\n`signer_email` on create returns 400. Use `parties[]` instead.",
        "security": [{ "BearerAuth": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "document":         { "type": "string", "format": "byte", "description": "Base64-encoded PDF or DOCX. For files larger than ~3MB prefer `document_url` or `document_id` to avoid request-body limits." },
                  "document_url":     { "type": "string", "format": "uri", "description": "Public HTTPS URL to a PDF or DOCX, or Atlas-branded `https://atlaswork.ai/d/{document_id}` from POST /api/documents." },
                  "document_id":      { "type": "string", "format": "uuid", "description": "UUID from POST /api/documents or the upload page. Loads staged bytes for this API key owner." },
                  "document_filename": { "type": "string", "description": "Optional filename hint for detection and display." },
                  "signer_name":      { "type": "string", "description": "Deprecated. Use parties[].name." },
                  "sender_name":      { "type": "string", "description": "Display name shown in the signing email and on the sign page." },
                  "parties":          { "type": "array", "items": { "$ref": "#/components/schemas/Party" }, "description": "Multi-party signing model. Canonical source of truth for who signs." },
                  "fields":           { "type": "array", "items": { "$ref": "#/components/schemas/Field" }, "description": "Explicit field placement. Skips AI inference entirely." },
                  "prefill":          { "type": "object", "additionalProperties": { "type": "string" }, "description": "Exact-match prefill. Field label to value." },
                  "prefill_map":      { "type": "object", "additionalProperties": { "type": "string" }, "description": "Fuzzy-match prefill. Resolves synonyms and substrings. See llms.txt for the full match strategy." },
                  "webhook_url":      { "type": "string", "format": "uri", "description": "URL to POST on status changes." },
                  "agent_identity":   { "type": "string", "description": "Identifier of the AI agent initiating this request. Recorded in the ESIGN Certificate." },
                  "auto_send":        { "type": "boolean", "description": "Skip review and email the first signer immediately." },
                  "email_subject":    { "type": "string" },
                  "email_body":       { "type": "string" },
                  "access_code":      { "type": "string", "description": "Optional access code the signer must enter to view the document." },
                  "metadata":         { "type": "object", "additionalProperties": true, "description": "Arbitrary metadata stored on the envelope. Filtered against an allowlist." },
                  "idempotency_key":  { "type": "string", "description": "Body-level idempotency key." }
                }
              },
              "example": {
                "document_url": "https://example.com/lease.pdf",
                "parties": [{ "email": "tenant@example.com", "name": "John Smith", "order": 1 }],
                "auto_send":    false,
                "idempotency_key": "lease-2026-q2-7741"
              }
            },
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "file":               { "type": "string", "format": "binary", "description": "PDF or DOCX file." },
                  "signer_name":        { "type": "string", "description": "Deprecated. Use parties JSON." },
                  "sender_name":        { "type": "string" },
                  "parties":            { "type": "string", "description": "JSON-encoded array of Party objects." },
                  "fields":             { "type": "string", "description": "JSON-encoded array of Field objects." },
                  "prefill_map":        { "type": "string", "description": "JSON-encoded object." },
                  "prefill":            { "type": "string", "description": "JSON-encoded object." },
                  "webhook_url":        { "type": "string", "format": "uri" },
                  "agent_identity":     { "type": "string" },
                  "auto_send":          { "type": "boolean" }
                },
                "required": ["file"]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Envelope created or draft awaiting confirmation.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "description": "Envelope created and active (auto_send=true).",
                      "properties": {
                        "success":         { "type": "boolean", "enum": [true] },
                        "envelope_id":     { "type": "string", "format": "uuid" },
                        "sign_url":        { "type": "string", "description": "Full URL to the signing page." },
                        "detected_fields": { "type": "array", "items": { "$ref": "#/components/schemas/Field" } }
                      }
                    },
                    {
                      "type": "object",
                      "description": "Review-first (default). Caller activates via POST /api/envelope/{id}/send.",
                      "properties": {
                        "success":       { "type": "boolean", "enum": [true] },
                        "envelope_id":   { "type": "string", "format": "uuid" },
                        "review_url":    { "type": "string", "description": "URL to review field placement before sending." },
                        "fields_status": { "type": "string", "enum": ["pending", "ready"] }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": { "description": "Bad request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "401": { "description": "Unauthorized", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "413": { "description": "Payload too large", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "422": { "description": "Unprocessable. Sender fields not satisfied by prefill, or DOCX conversion error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "429": { "description": "Rate limit exceeded" }
        }
      }
    },
    "/api/envelope/{id}": {
      "get": {
        "operationId": "getEnvelope",
        "summary": "Get envelope by ID",
        "description": "Drafts created without a signer have `status: draft`. Once activated via /send, status transitions to `pending`.",
        "security": [],
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }
        ],
        "responses": {
          "200": {
            "description": "Envelope data",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "envelope": { "$ref": "#/components/schemas/EnvelopeDetail" }
                  }
                }
              }
            }
          },
          "404": { "description": "Not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      },
      "patch": {
        "operationId": "patchEnvelope",
        "summary": "Submit signature, reassign a signer, or update envelope state",
        "description": "Dispatches on the `action` field:\n- `reassign`: hand off signing to another recipient. Updates the party, resets status, issues a new token, and emails the new recipient.\n- Signature submission (called by the sign page browser): writes signatures, marks the signer done, embeds the signed PDF when all parties have signed, and fires webhooks.",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "type": "object",
                    "description": "Reassign a signer.",
                    "properties": {
                      "action":           { "type": "string", "enum": ["reassign"] },
                      "new_signer_email": { "type": "string", "format": "email" },
                      "new_signer_name":  { "type": "string" },
                      "reason":           { "type": "string" },
                      "signer_token":     { "type": "string", "description": "Optional. If omitted, the first pending party is reassigned." }
                    },
                    "required": ["action", "new_signer_email"]
                  },
                  {
                    "type": "object",
                    "description": "Signature submission. Called by the signer's browser.",
                    "properties": {
                      "signatures_by_label": { "type": "object", "additionalProperties": { "type": "string" } },
                      "filled_fields":       { "type": "object", "additionalProperties": { "type": "string" } },
                      "signer_token":        { "type": "string" }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Updated" },
          "400": { "description": "Bad request" },
          "404": { "description": "Not found" }
        }
      }
    },
    "/api/envelope/{id}/send": {
      "post": {
        "operationId": "sendEnvelope",
        "summary": "Activate a draft envelope",
        "description": "Transitions a `draft` envelope to `pending` by supplying `parties[]` and confirming field placement. Supports `Idempotency-Key` header.",
        "security": [{ "BearerAuth": [] }],
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } },
          { "name": "Idempotency-Key", "in": "header", "required": false, "schema": { "type": "string" } }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "parties":           { "type": "array", "items": { "$ref": "#/components/schemas/Party" }, "description": "Required, at least one entry." },
                  "prefill":           { "type": "object", "additionalProperties": { "type": "string" } },
                  "prefill_map":       { "type": "object", "additionalProperties": { "type": "string" } },
                  "sender_name":       { "type": "string" },
                  "email_subject":     { "type": "string" },
                  "email_body":        { "type": "string" },
                  "scheduled_send_at": { "type": "string", "format": "date-time", "description": "Optional. Defer email dispatch until this time." }
                }
              },
              "example": {
                "parties": [{ "email": "alice@example.com", "name": "Alice" }],
                "prefill": { "Client Name": "Acme Corp", "Effective Date": "April 1, 2026" }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Envelope activated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success":     { "type": "boolean", "enum": [true] },
                    "envelope_id": { "type": "string", "format": "uuid" },
                    "sign_url":    { "type": "string" }
                  }
                }
              }
            }
          },
          "400": { "description": "Bad request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "404": { "description": "Envelope not found" },
          "409": { "description": "Envelope is not a draft (already pending, signed, etc.) or detection is still pending" },
          "422": { "description": "Validation error" },
          "429": { "description": "Rate limit exceeded" }
        }
      }
    },
    "/api/envelope/{id}/resend": {
      "post": {
        "operationId": "resendEnvelope",
        "summary": "Resend the signing email",
        "description": "Re-dispatches the signing email to the next unsigned party. Optional custom message.",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "message": { "type": "string", "description": "Optional message included in the reminder email." }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Reminder sent" },
          "404": { "description": "Not found" },
          "409": { "description": "Envelope not in pending status" }
        }
      }
    },
    "/api/envelope/{id}/void": {
      "post": {
        "operationId": "voidEnvelope",
        "summary": "Void an envelope",
        "description": "Cancels a pending envelope. Only the owner can void. Signed envelopes cannot be voided.",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "reason": { "type": "string" }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Voided successfully" },
          "403": { "description": "Forbidden" },
          "404": { "description": "Not found" },
          "409": { "description": "Conflict — envelope already signed" }
        }
      }
    },
    "/api/envelope/{id}/decline": {
      "post": {
        "operationId": "declineEnvelope",
        "summary": "Decline to sign",
        "description": "Called by the signer's browser when they choose to decline. Fires envelope.declined webhook if configured.",
        "security": [],
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "reason": { "type": "string" }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Declined successfully" },
          "404": { "description": "Not found" },
          "409": { "description": "Conflict — envelope not in pending status" }
        }
      }
    },
    "/api/envelope/{id}/redetect": {
      "post": {
        "operationId": "redetectEnvelope",
        "summary": "Re-run field detection",
        "description": "Wipes the field-detection caches and re-runs the pipeline against the original document. Only owner-callable. Use when a previous detection produced poor results.",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }
        ],
        "responses": {
          "200": { "description": "Detection re-run scheduled" },
          "404": { "description": "Not found" }
        }
      }
    },
    "/api/envelope/{id}/autofill-suggestions": {
      "get": {
        "operationId": "getEnvelopeAutofillSuggestions",
        "summary": "Counterparty autofill suggestions",
        "description": "Returns per-detected-party autofill suggestions sourced from the caller's own past envelopes. High-confidence matches (display_name or role_hint == a past party's role / name / company) may be applied to party rows without user action; medium-confidence matches (email-domain SLD) surface as suggestion chips. Owner-scoped; never leaks emails across users.",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }
        ],
        "responses": {
          "200": {
            "description": "Suggestions list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "suggestions": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "detected_party_index": { "type": "integer" },
                          "email":      { "type": "string", "format": "email" },
                          "name":       { "type": "string", "nullable": true },
                          "company":    { "type": "string", "nullable": true },
                          "last_seen":  { "type": "string", "format": "date-time" },
                          "confidence": { "type": "string", "enum": ["high", "medium"] }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": { "description": "Unauthorized" },
          "404": { "description": "Not found" },
          "429": { "description": "Rate limited" }
        }
      }
    },
    "/api/envelope/{id}/pdf": {
      "get": {
        "operationId": "getEnvelopePdf",
        "summary": "Download the (signed or partial) PDF",
        "description": "Returns the PDF bytes. For signed envelopes the response includes all signatures and the ESIGN certificate appended. For pending multi-signer envelopes the PDF is regenerated on the fly with signatures collected so far.",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }
        ],
        "responses": {
          "200": {
            "description": "PDF bytes",
            "content": { "application/pdf": { "schema": { "type": "string", "format": "binary" } } }
          },
          "404": { "description": "Not found" }
        }
      }
    },
    "/api/envelope/{id}/audit-cert": {
      "get": {
        "operationId": "getEnvelopeAuditCert",
        "summary": "Download the ESIGN certificate PDF",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }
        ],
        "responses": {
          "200": {
            "description": "Certificate PDF",
            "content": { "application/pdf": { "schema": { "type": "string", "format": "binary" } } }
          },
          "404": { "description": "Not found" }
        }
      }
    },
    "/api/envelope/{id}/events": {
      "get": {
        "operationId": "envelopeEvents",
        "summary": "Stream envelope status (SSE)",
        "description": "Server-Sent Events stream. Emits `data: {status, envelope}` every ~2 s. Closes automatically when `signed`, `declined`, or `voided` is reached, or after 10 minutes.",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }
        ],
        "responses": {
          "200": {
            "description": "SSE stream",
            "content": { "text/event-stream": { "schema": { "type": "string" } } }
          }
        }
      }
    },
    "/api/envelopes": {
      "get": {
        "operationId": "listEnvelopes",
        "summary": "List envelopes",
        "description": "Returns envelopes belonging to the authenticated user, newest first.",
        "parameters": [
          { "name": "status", "in": "query", "schema": { "type": "string", "enum": ["draft", "pending", "signed", "declined", "voided"] } },
          { "name": "party_email", "in": "query", "schema": { "type": "string", "format": "email" }, "description": "Filter envelopes where any signing party email contains this string (case-insensitive)." },
          { "name": "signer_email", "in": "query", "schema": { "type": "string", "format": "email", "deprecated": true, "description": "Deprecated alias for party_email." } },
          { "name": "limit",  "in": "query", "schema": { "type": "integer", "default": 20, "maximum": 100 } },
          { "name": "offset", "in": "query", "schema": { "type": "integer", "default": 0 } }
        ],
        "responses": {
          "200": {
            "description": "List of envelopes",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success":   { "type": "boolean" },
                    "envelopes": { "type": "array", "items": { "$ref": "#/components/schemas/EnvelopeSummary" } },
                    "limit":     { "type": "integer" },
                    "offset":    { "type": "integer" }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/documents": {
      "post": {
        "operationId": "uploadDocument",
        "summary": "Stage a document and get a hosted URL",
        "description": "Uploads a PDF or DOCX to Atlas storage and returns document_url and document_id for POST /api/envelope or send_contract_for_review.\n\nJSON body: document_url or document_base64 (+ optional document_filename).\n\nMultipart: file part (PDF or DOCX). Auth via Bearer API key, OAuth Bearer with scope documents:write, or x-upload-token from request_document_upload (upload page).\n\nMCP agents normally use request_document_upload on send_contract_for_review instead of calling this directly.",
        "parameters": [
          {
            "name": "x-upload-token",
            "in": "header",
            "required": false,
            "schema": { "type": "string" },
            "description": "HMAC upload session token from send_contract_for_review needs_document flow. Alternative to Bearer API key on the upload page."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "document_url":      { "type": "string", "format": "uri" },
                  "document_base64":   { "type": "string", "format": "byte" },
                  "document_filename": { "type": "string" }
                }
              }
            },
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": ["file"],
                "properties": {
                  "file": { "type": "string", "format": "binary", "description": "PDF or DOCX file." }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Hosted document URL",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success":      { "type": "boolean" },
                    "document_id":  { "type": "string", "format": "uuid" },
                    "document_url": { "type": "string", "format": "uri" },
                    "filename":     { "type": "string" },
                    "next_action":  { "type": "string" }
                  }
                }
              }
            }
          },
          "400": { "description": "Bad request" }
        }
      }
    },
    "/api/templates": {
      "get": {
        "operationId": "listTemplates",
        "summary": "List saved templates",
        "description": "Returns templates with ready, status_label, and subtitle. Send only when ready is true. Templates are immutable after save.",
        "responses": {
          "200": {
            "description": "Template list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success":   { "type": "boolean" },
                    "templates": { "type": "array", "items": { "$ref": "#/components/schemas/TemplateSummary" } }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/templates/{id}": {
      "get": {
        "operationId": "getTemplate",
        "summary": "Get template contract",
        "description": "Full agent contract: ready, parties_for_send, stored_signatures, template_roles (prefill_keys, ceremony_fields, template_defaults), do_not_include_in_parties, example_create, send_path, preview_path. PATCH returns 403 (immutable).",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }
        ],
        "responses": {
          "200": { "description": "Template detail with contract fields" },
          "404": { "description": "Not found" }
        }
      }
    },
    "/api/templates/{id}/send": {
      "post": {
        "operationId": "sendFromTemplate",
        "summary": "Send an envelope from a pinned template",
        "description": "Skips field detection. Replays stored template signatures. parties[] must use role_name values from parties_for_send only.",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "parties":     { "type": "array", "items": { "$ref": "#/components/schemas/TemplateSendParty" } },
                  "prefill_map": { "type": "object", "additionalProperties": { "type": "string" } },
                  "auto_send":   { "type": "boolean", "description": "Default true. Emails the first signer immediately." },
                  "document_url": { "type": "string", "format": "uri", "description": "Optional hash gate against pinned PDF." }
                },
                "required": ["parties"]
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Envelope created; sign_url when auto_send is true" },
          "409": { "description": "Party mismatch or template_hash_mismatch" }
        }
      }
    },
    "/api/templates/{id}/invite-template-signature": {
      "post": {
        "operationId": "inviteTemplateSignature",
        "summary": "Invite one-time template signature setup",
        "description": "Required when status is needs_template_signature. Returns sign_url for the authorized signer.",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": { "type": "string", "format": "email" },
                  "name":  { "type": "string", "description": "Printed name for stored signature replay." }
                },
                "required": ["email", "name"]
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Invite sent with sign_url" }
        }
      }
    },
    "/api/mcp": {
      "post": {
        "operationId": "mcpRpc",
        "summary": "MCP JSON-RPC 2.0 endpoint",
        "description": "JSON-RPC 2.0 router for the Atlas MCP server. Accepts an API key bearer token or an OAuth access token. Tool list and schemas are returned via the standard `tools/list` MCP method.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "jsonrpc": { "type": "string", "enum": ["2.0"] },
                  "id":      {},
                  "method":  { "type": "string" },
                  "params":  { "type": "object", "additionalProperties": true }
                },
                "required": ["jsonrpc", "method"]
              }
            }
          }
        },
        "responses": {
          "200": { "description": "JSON-RPC response" },
          "401": { "description": "Unauthorized" }
        }
      }
    },
    "/api/oauth/.well-known/oauth-authorization-server": {
      "get": {
        "operationId": "oauthMetadata",
        "summary": "OAuth 2.0 authorization server metadata (RFC 8414)",
        "security": [],
        "responses": {
          "200": { "description": "Metadata document", "content": { "application/json": { "schema": { "type": "object", "additionalProperties": true } } } }
        }
      }
    },
    "/api/oauth/authorize": {
      "get": {
        "operationId": "oauthAuthorize",
        "summary": "OAuth authorization endpoint",
        "description": "Redirects to the Atlas consent UI page.",
        "security": [],
        "responses": { "302": { "description": "Redirect to consent page" } }
      }
    },
    "/api/oauth/token": {
      "post": {
        "operationId": "oauthToken",
        "summary": "OAuth token endpoint",
        "description": "Issues and rotates access/refresh tokens. Supports `authorization_code` and `refresh_token` grants.",
        "security": [],
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "grant_type":    { "type": "string", "enum": ["authorization_code", "refresh_token"] },
                  "code":          { "type": "string" },
                  "refresh_token": { "type": "string" },
                  "client_id":     { "type": "string" },
                  "client_secret": { "type": "string" },
                  "code_verifier": { "type": "string" },
                  "redirect_uri":  { "type": "string", "format": "uri" }
                },
                "required": ["grant_type"]
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Token response", "content": { "application/json": { "schema": { "type": "object", "additionalProperties": true } } } },
          "400": { "description": "Invalid grant" }
        }
      }
    },
    "/api/oauth/userinfo": {
      "get": {
        "operationId": "oauthUserInfo",
        "summary": "Return sub and email for a valid access token",
        "responses": {
          "200": { "description": "User info" },
          "401": { "description": "Invalid token" }
        }
      }
    },
    "/api/oauth/revoke": {
      "post": {
        "operationId": "oauthRevoke",
        "summary": "Revoke an access or refresh token",
        "responses": { "200": { "description": "Revoked" } }
      }
    },
    "/api/stripe/checkout": {
      "post": {
        "operationId": "stripeCheckout",
        "summary": "Create a Stripe Checkout session for credit packs",
        "responses": {
          "200": { "description": "Checkout session URL" }
        }
      }
    },
    "/api/billing/portal": {
      "post": {
        "operationId": "billingPortal",
        "summary": "Open the Stripe Customer Portal",
        "responses": {
          "200": { "description": "Portal URL" },
          "404": { "description": "No Stripe customer for the authenticated user" }
        }
      }
    }
  },
  "webhooks": {
    "envelope.created": {
      "post": {
        "summary": "envelope.created",
        "description": "Fired when a new envelope row is written. For document path, this fires before detection completes.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event":         { "type": "string", "enum": ["envelope.created"] },
                  "envelope_id":   { "type": "string", "format": "uuid" },
                  "document_type": { "type": "string", "enum": ["pdf", "docx"] },
                  "created_at":    { "type": "string", "format": "date-time" }
                }
              }
            }
          }
        },
        "responses": { "200": { "description": "Acknowledged" } }
      }
    },
    "envelope.sent": {
      "post": {
        "summary": "envelope.sent",
        "description": "Fired when the first signing email is dispatched.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event":        { "type": "string", "enum": ["envelope.sent"] },
                  "envelope_id":  { "type": "string", "format": "uuid" },
                  "sign_url":     { "type": "string" },
                  "party_email":  { "type": "string", "format": "email" },
                  "sent_at":      { "type": "string", "format": "date-time" }
                }
              }
            }
          }
        },
        "responses": { "200": { "description": "Acknowledged" } }
      }
    },
    "envelope.viewed": {
      "post": {
        "summary": "envelope.viewed",
        "description": "Fired the first time a signer opens the sign page for an envelope.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event":       { "type": "string", "enum": ["envelope.viewed"] },
                  "envelope_id": { "type": "string", "format": "uuid" },
                  "viewed_at":   { "type": "string", "format": "date-time" }
                }
              }
            }
          }
        },
        "responses": { "200": { "description": "Acknowledged" } }
      }
    },
    "envelope.signed": {
      "post": {
        "summary": "envelope.signed",
        "description": "Fired each time a signer completes signing. In multi-party envelopes, fires for each signer plus once when all parties have signed.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event":          { "type": "string", "enum": ["envelope.signed"] },
                  "envelope_id":    { "type": "string", "format": "uuid" },
                  "status":         { "type": "string" },
                  "signed_at":      { "type": "string", "format": "date-time" },
                  "parties":        { "type": "array", "items": { "$ref": "#/components/schemas/Party" } },
                  "filled_fields":  { "type": "object", "additionalProperties": { "type": "string" } },
                  "document_hash":  { "type": "string" },
                  "signed_pdf_url": { "type": "string", "format": "uri" }
                }
              }
            }
          }
        },
        "responses": { "200": { "description": "Acknowledged" } }
      }
    },
    "envelope.declined": {
      "post": {
        "summary": "envelope.declined",
        "description": "Fired when any signer declines.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event":         { "type": "string", "enum": ["envelope.declined"] },
                  "envelope_id":   { "type": "string", "format": "uuid" },
                  "party_email":   { "type": "string", "format": "email" },
                  "party_name":    { "type": "string" },
                  "reason":        { "type": "string" },
                  "declined_at":   { "type": "string", "format": "date-time" }
                }
              }
            }
          }
        },
        "responses": { "200": { "description": "Acknowledged" } }
      }
    },
    "envelope.voided": {
      "post": {
        "summary": "envelope.voided",
        "description": "Fired when the sender voids a pending envelope.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event":       { "type": "string", "enum": ["envelope.voided"] },
                  "envelope_id": { "type": "string", "format": "uuid" },
                  "reason":      { "type": "string" },
                  "voided_at":   { "type": "string", "format": "date-time" }
                }
              }
            }
          }
        },
        "responses": { "200": { "description": "Acknowledged" } }
      }
    },
    "contract.extracted": {
      "post": {
        "summary": "contract.extracted",
        "description": "Fired asynchronously after envelope.signed when structured data extraction completes. Includes the full contract_data object.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event":           { "type": "string", "enum": ["contract.extracted"] },
                  "envelope_id":     { "type": "string", "format": "uuid" },
                  "extracted_at":    { "type": "string", "format": "date-time" },
                  "extracted_terms": { "type": "object", "additionalProperties": true }
                }
              }
            }
          }
        },
        "responses": { "200": { "description": "Acknowledged" } }
      }
    }
  }
}
