{
  "openapi": "3.1.0",
  "info": {
    "title": "Portant agent signing API",
    "version": "1.0.0",
    "summary": "Send a document for signature and get the signed PDF back.",
    "description": "The same operations as the Portant MCP server at https://mcp.portant.co/mcp. A person always signs, in a browser; no endpoint applies a signature. Free plan: 30 documents a month. Set up and terms: https://www.portant.co/agents/",
    "contact": {
      "name": "Portant",
      "url": "https://www.portant.co/agents/"
    }
  },
  "servers": [
    {
      "url": "https://mcp.portant.co"
    }
  ],
  "security": [
    {
      "apiKey": []
    },
    {}
  ],
  "paths": {
    "/v1/requests": {
      "get": {
        "operationId": "findSignatureRequests",
        "summary": "Find signature requests",
        "description": "Needs an API key. Finds the account's own requests by the signer's email address, a part of the title, or both, newest first, up to 10. With no filter, returns the 10 most recent. Free accounts search their 30 most recent requests; Plus and Scale search the last 1,000 and also get the signer's email and how often the signer opened the signing page. To download a signed PDF you still need the request_token from the create response.",
        "parameters": [
          {
            "name": "signer_email",
            "in": "query",
            "required": false,
            "description": "The signer's email address. Must match exactly (not case sensitive).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "title",
            "in": "query",
            "required": false,
            "description": "A word or part of the document title. Not case sensitive.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The matching requests.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FindResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "post": {
        "operationId": "sendForSignature",
        "summary": "Send a document for signature",
        "description": "Get a document signed by one person, or by up to five people in a set order (signers), and optionally by the user first. When only the user signs, pass sender_signs: true and leave out signer and signers. Give the document as markdown text you wrote (use {{signature:signer}} where the signature goes, or leave it out and Portant adds a signature block), a PDF (upload_id from get_upload_url for a PDF file, including one you made with a file skill; pdf_url; or pdf_base64 for a small PDF), or a Google Doc link shared with \"anyone with the link\". Portant emails the signer a private link; the signer signs in their browser. This tool never signs for anyone. When the user wants to check the document or sign it first, call this tool at once with review: true or sender_signs: true, not after showing the document as text: Portant shows the finished PDF on a card, and nothing reaches the signer until the user clicks Send or signs. Before a send without review or sender_signs, confirm each signer's name and email, and the signing order, with the user. With signers, Portant emails each person after the one before has signed, and everyone gets the signed PDF by email at the end. Returns request_id, request_token (keep it to download the signed PDF or send a reminder) and next_step (tell it to the user). With review: true, nothing goes to the signer until the user opens review_link, checks the document and clicks Send. Limits: five signers, PDFs up to 5 MB and 50 pages, links expire after 14 days. Simple electronic signature.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "document"
                ],
                "properties": {
                  "document": {
                    "type": "object",
                    "description": "Exactly one of these, except that template_id can come with values and markdown.",
                    "properties": {
                      "markdown": {
                        "type": "string",
                        "description": "The document as markdown. Put {{signature:signer}}, {{name:signer}}, {{date:signer}}, {{text:signer:Label}}, {{signature:sender}}, {{name:sender}} or {{date:sender}} where each field goes, or leave them out and Portant adds a signature block at the end. When you place the fields yourself, give each person a {{name:...}} beside their signature: Portant fills it with the full name the person types when they sign, as it fills the date, so do not add a text field for the name. With several signers, signer is the first and signer2 to signer5 the next ones, for example {{signature:signer2}}. With template_id: the template text after you changed it for this send (get it with get_template, put the values in, change only what the user asked for); the request then waits for the user to review the changes. Text still to fill in, such as [Plan name] or {{var:Plan}}, is refused."
                      },
                      "pdf_base64": {
                        "type": "string",
                        "description": "A small PDF as base64. For a PDF file, including one you made with a file skill, or any PDF over 1 MB, use upload_id (get_upload_url) or pdf_url."
                      },
                      "pdf_url": {
                        "type": "string",
                        "description": "A public https link to a PDF."
                      },
                      "google_doc_url": {
                        "type": "string",
                        "description": "A Google Docs link shared as \"Anyone with the link can view\"."
                      },
                      "upload_id": {
                        "type": "string",
                        "description": "From get_upload_url, after the PDF was uploaded."
                      },
                      "template_id": {
                        "type": "string",
                        "description": "A saved template, from list_templates. Portant adds the new signer's name to its signature block."
                      },
                      "values": {
                        "type": "object",
                        "additionalProperties": {
                          "type": "string"
                        },
                        "description": "With template_id: a value for each named field of the template ({{var:Name}} or [Name] in its text; get_template lists them in named_fields), for example {\"Plan\": \"Team\", \"Deal amount\": \"$12,000\"}. Names match without regard to case, spaces or underscores. Each value is put in as plain text."
                      }
                    }
                  },
                  "title": {
                    "type": "string",
                    "description": "The document title. Required with markdown; it is the heading and the email subject."
                  },
                  "signer": {
                    "type": "object",
                    "properties": {
                      "name": {
                        "type": "string",
                        "description": "The signer's full name."
                      },
                      "email": {
                        "type": "string",
                        "description": "The signer's email address."
                      }
                    },
                    "required": [
                      "email"
                    ],
                    "description": "The person who signs. Give signer or signers, not both. Leave both out when only the user signs (sender_signs: true)."
                  },
                  "signers": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 5,
                    "description": "Two to five people who sign one after another, in this order. Use it instead of signer only when the user names more than one signer.",
                    "items": {
                      "type": "object",
                      "properties": {
                        "name": {
                          "type": "string",
                          "description": "The signer's full name."
                        },
                        "email": {
                          "type": "string",
                          "description": "The signer's email address."
                        }
                      },
                      "required": [
                        "email"
                      ]
                    }
                  },
                  "sender": {
                    "type": "object",
                    "properties": {
                      "name": {
                        "type": "string",
                        "description": "The user (the sender)'s full name."
                      },
                      "email": {
                        "type": "string",
                        "description": "The user (the sender)'s email address."
                      }
                    },
                    "description": "The user who is sending. Required only when Portant is connected without sign-in; Portant then emails the user to confirm before anything reaches the signer."
                  },
                  "sender_signs": {
                    "type": "boolean",
                    "description": "True when the user signs too. The user signs first, then the signer. With no signer and no signers, only the user signs: the user signs on the card or at sender_link, the request is then complete, and Portant emails the user the signed PDF. That needs Portant connected with sign-in."
                  },
                  "message": {
                    "type": "string",
                    "description": "A short plain-text note to the signer, up to 500 characters. Links are removed."
                  },
                  "language": {
                    "type": "string",
                    "enum": [
                      "en",
                      "es",
                      "de",
                      "fr",
                      "ja",
                      "it",
                      "nl",
                      "pt-br"
                    ],
                    "description": "Language of the emails and the signing page. Default en."
                  },
                  "fields": {
                    "type": "array",
                    "description": "Only for PDF or Google Doc input, when you know where the fields go. Positions are fractions of the page from the top-left corner. Without fields, Portant looks for where the PDF asks for signatures (a signature form field, \"Signature: ____\", or a line over the word Signature) and puts the boxes there when it can tell whose line is whose; otherwise it adds a signature page. So leave fields out unless the user gives the places.",
                    "items": {
                      "type": "object",
                      "required": [
                        "kind",
                        "page",
                        "x",
                        "y",
                        "width"
                      ],
                      "properties": {
                        "kind": {
                          "type": "string",
                          "enum": [
                            "signature",
                            "date",
                            "text",
                            "name"
                          ]
                        },
                        "party": {
                          "type": "string",
                          "enum": [
                            "signer",
                            "signer2",
                            "signer3",
                            "signer4",
                            "signer5",
                            "sender"
                          ],
                          "description": "Default signer, the first signer. signer2 to signer5 are the next signers in signers."
                        },
                        "page": {
                          "type": "integer",
                          "minimum": 1
                        },
                        "x": {
                          "type": "number",
                          "minimum": 0,
                          "maximum": 1
                        },
                        "y": {
                          "type": "number",
                          "minimum": 0,
                          "maximum": 1
                        },
                        "width": {
                          "type": "number",
                          "minimum": 0,
                          "maximum": 1
                        },
                        "height": {
                          "type": "number",
                          "minimum": 0,
                          "maximum": 1
                        },
                        "label": {
                          "type": "string"
                        },
                        "required": {
                          "type": "boolean"
                        }
                      }
                    }
                  },
                  "delivery": {
                    "type": "string",
                    "enum": [
                      "email",
                      "link"
                    ],
                    "description": "email (default): Portant emails the signer. link: no email; you get the signing link to share."
                  },
                  "review": {
                    "type": "boolean",
                    "description": "True to let the user see the finished document before anything goes to the signer. You get review_link for the user, and Portant also emails it to them. Use it when the user wants to check the document first, for example a document you wrote. Not needed when sender_signs is true (the user sees the document when signing) or when Portant is connected without sign-in (Portant then asks the user to confirm by email)."
                  },
                  "signature_page": {
                    "type": "boolean",
                    "description": "PDF or Google Doc input with no fields: true to skip looking for signature lines in the document and add Portant's signature page instead. Use it only when the user asks for a separate signature page."
                  },
                  "certificate": {
                    "type": "boolean",
                    "description": "Append a signing certificate page. Default true."
                  },
                  "save_as_template": {
                    "type": "boolean",
                    "description": "True to also save this document as a template the user can send again from any chat. Use it only when the user asks to send the document now and also keep it. To save a template without sending anything, use save_template instead. Portant saves it before any names go in. A Google Doc is saved as its link, so each later send uses the Doc as it is then."
                  },
                  "template_name": {
                    "type": "string",
                    "description": "A short name for the saved template, with no signer name in it, for example \"Mutual NDA\". A later send from the template uses it as the title. Default: the title."
                  },
                  "allow_placeholders": {
                    "type": "boolean",
                    "description": "Portant refuses markdown that still has text to fill in, such as [Plan name] or {{var:Plan}}. Pass true only when the user confirms the square brackets are meant to be in the document."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The request was created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "502": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/requests/{request_id}": {
      "get": {
        "operationId": "getSignatureStatus",
        "summary": "Check signature status",
        "description": "Do not check more than once a minute. The sender also gets an email with the signed PDF when it is signed.",
        "parameters": [
          {
            "name": "request_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-f0-9]{32}$"
            }
          },
          {
            "name": "X-Request-Token",
            "in": "header",
            "required": false,
            "description": "The request_token from the create response. Needed for a request made without an API key; with the API key of the account that made it, it can be left out (download always needs it).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Status"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/requests/{request_id}/signed-pdf": {
      "get": {
        "operationId": "downloadSignedPdf",
        "summary": "Download the signed PDF",
        "description": "After the status is signed. Returns the signed PDF with its signing certificate page. Send the request_token, or, with an API key, nothing more for a signed copy Portant stores for the account (Plus and Scale store signed copies until you delete them).",
        "parameters": [
          {
            "name": "request_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-f0-9]{32}$"
            }
          },
          {
            "name": "X-Request-Token",
            "in": "header",
            "required": false,
            "description": "The request_token from the create response. Not needed with an API key when the account has a stored copy of this signed PDF.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The signed PDF.",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "410": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "delete": {
        "operationId": "deleteStoredCopy",
        "summary": "Delete the stored signed PDF",
        "description": "Deletes every copy of the signed PDF that Portant stores for this request. The request record stays, with stored false. Needs an API key for the account that sent it.",
        "parameters": [
          {
            "name": "request_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-f0-9]{32}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "request_id": {
                      "type": "string"
                    },
                    "stored": {
                      "const": false
                    },
                    "stored_copy_deleted_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/requests/{request_id}/cancel": {
      "post": {
        "operationId": "cancelSignatureRequest",
        "summary": "Cancel a signature request",
        "description": "Cancel a request that is not signed yet. The signing link stops working. A cancel does not give back the monthly count.",
        "parameters": [
          {
            "name": "request_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-f0-9]{32}$"
            }
          },
          {
            "name": "X-Request-Token",
            "in": "header",
            "required": false,
            "description": "The request_token from the create response. Needed for a request made without an API key; with the API key of the account that made it, it can be left out (download always needs it).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Cancelled.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "request_id": {
                      "type": "string"
                    },
                    "status": {
                      "const": "cancelled"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/requests/{request_id}/remind": {
      "post": {
        "operationId": "sendReminder",
        "summary": "Remind the signer",
        "description": "Email the signer again with the same private signing link, for a request with the status \"pending\" (waiting for the signer). Use it only when the user asks. Needs the request_token from send_for_signature. Portant sends up to 3 reminders for a request, at least an hour after the last email to the signer. A request sent with delivery \"link\" got no email, so give the user the signing link to share again instead.",
        "parameters": [
          {
            "name": "request_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-f0-9]{32}$"
            }
          },
          {
            "name": "X-Request-Token",
            "in": "header",
            "required": true,
            "description": "The request_token from the create response. Always needed here: the reminder carries the signing link, and Portant does not store the key it holds.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "message": {
                    "type": "string",
                    "description": "A short plain-text note to the signer, up to 500 characters. Links are removed."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The reminder was sent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReminderResult"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "410": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "502": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/uploads": {
      "post": {
        "operationId": "getUploadUrl",
        "summary": "Get a PDF upload URL",
        "description": "For PDFs too large to send as base64. PUT the raw PDF bytes to upload_url (with the same Authorization header, if any), then pass upload_id as document.upload_id.",
        "responses": {
          "201": {
            "description": "The upload URL.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "upload_id": {
                      "type": "string"
                    },
                    "upload_url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "method": {
                      "const": "PUT"
                    },
                    "content_type": {
                      "const": "application/pdf"
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "next_step": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/uploads/{upload_id}": {
      "put": {
        "operationId": "uploadPdf",
        "summary": "Upload the PDF",
        "parameters": [
          {
            "name": "upload_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/pdf": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Uploaded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "upload_id": {
                      "type": "string"
                    },
                    "bytes": {
                      "type": "integer"
                    },
                    "next_step": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "413": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/webhook": {
      "get": {
        "operationId": "getWebhook",
        "summary": "Get the webhook",
        "description": "Plus and Scale only. The webhook URL and the result of the last delivery. The secret is never shown again.",
        "responses": {
          "200": {
            "description": "The webhook.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Webhook"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "put": {
        "operationId": "setWebhook",
        "summary": "Set the webhook URL",
        "description": "Plus and Scale only. Portant POSTs an event to this https URL when one of the account's documents is signed. The first save, and a save with rotate_secret, returns the signing secret once.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "url"
                ],
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "description": "An https URL with a public host name, 500 characters or fewer."
                  },
                  "rotate_secret": {
                    "type": "boolean",
                    "description": "Make a new signing secret."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Saved.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": "string"
                    },
                    "secret": {
                      "type": "string",
                      "description": "Only when a secret was made. Store it: Portant shows it once."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "delete": {
        "operationId": "removeWebhook",
        "summary": "Remove the webhook",
        "description": "Portant stops sending events and deletes the URL and the secret.",
        "responses": {
          "200": {
            "description": "Removed."
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/webhook/test": {
      "post": {
        "operationId": "testWebhook",
        "summary": "Send a test event",
        "description": "Plus and Scale only. Sends an event of type \"test\" to the webhook URL, signed like every event.",
        "responses": {
          "200": {
            "description": "The result.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "delivered": {
                      "type": "boolean"
                    },
                    "result": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/templates": {
      "get": {
        "operationId": "listTemplates",
        "summary": "List saved templates",
        "description": "Saved templates are a Plus and Scale feature. Lists the documents the account saved (with POST /v1/templates, or with save_as_template on a send), newest first; send one again with document.template_id in POST /v1/requests. A Free account still sees, and can delete, the templates it saved on a paid plan. With q, lists only the templates whose name fits those words (for example q=mutual NDA), the same search as the find_templates tool; a word many names share, such as \"agreement\", does not match alone.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "A few words for the document, for example \"mutual NDA\". Returns only the templates whose name fits.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The saved templates.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "templates": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TemplateSummary"
                      }
                    },
                    "next_step": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "post": {
        "operationId": "saveTemplate",
        "summary": "Save a template",
        "description": "Saves a document as a template and sends nothing. Portant checks the document the way a send would. Plus and Scale.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "document"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "A short name for the template, with no signer name in it, for example \"Sales agreement\". A send from the template uses it as the title."
                  },
                  "document": {
                    "type": "object",
                    "description": "Exactly one of these, except that template_id can come with values and markdown.",
                    "properties": {
                      "markdown": {
                        "type": "string",
                        "description": "The document as markdown. Put {{signature:signer}}, {{date:signer}}, {{text:signer:Label}}, {{signature:sender}} or {{date:sender}} where each field goes, or leave them out and Portant adds a signature block at the end. With several signers, signer is the first and signer2 to signer5 the next ones, for example {{signature:signer2}}. With template_id: the template text after you changed it for this send (get it with get_template, put the values in, change only what the user asked for); the request then waits for the user to review the changes. Text still to fill in, such as [Plan name] or {{var:Plan}}, is refused."
                      },
                      "pdf_base64": {
                        "type": "string",
                        "description": "A small PDF as base64. For a PDF file, including one you made with a file skill, or any PDF over 1 MB, use upload_id (get_upload_url) or pdf_url."
                      },
                      "pdf_url": {
                        "type": "string",
                        "description": "A public https link to a PDF."
                      },
                      "google_doc_url": {
                        "type": "string",
                        "description": "A Google Docs link shared as \"Anyone with the link can view\"."
                      },
                      "upload_id": {
                        "type": "string",
                        "description": "From get_upload_url, after the PDF was uploaded."
                      }
                    }
                  },
                  "fields": {
                    "type": "array",
                    "description": "Only for a PDF or Google Doc, when you know where the fields go. The same as fields in send_for_signature. Without fields, each send looks for the signature lines in the document, and adds a signature page when it cannot place them.",
                    "items": {
                      "type": "object",
                      "required": [
                        "kind",
                        "page",
                        "x",
                        "y",
                        "width"
                      ],
                      "properties": {
                        "kind": {
                          "type": "string",
                          "enum": [
                            "signature",
                            "date",
                            "text"
                          ]
                        },
                        "party": {
                          "type": "string",
                          "enum": [
                            "signer",
                            "signer2",
                            "signer3",
                            "signer4",
                            "signer5",
                            "sender"
                          ]
                        },
                        "page": {
                          "type": "integer",
                          "minimum": 1
                        },
                        "x": {
                          "type": "number",
                          "minimum": 0,
                          "maximum": 1
                        },
                        "y": {
                          "type": "number",
                          "minimum": 0,
                          "maximum": 1
                        },
                        "width": {
                          "type": "number",
                          "minimum": 0,
                          "maximum": 1
                        },
                        "height": {
                          "type": "number",
                          "minimum": 0,
                          "maximum": 1
                        },
                        "label": {
                          "type": "string"
                        },
                        "required": {
                          "type": "boolean"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The saved template.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "template_id": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "kind": {
                      "type": "string",
                      "enum": [
                        "markdown",
                        "pdf",
                        "google_doc"
                      ]
                    },
                    "sent": {
                      "type": "boolean"
                    },
                    "next_step": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "413": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/templates/{template_id}": {
      "get": {
        "operationId": "getTemplate",
        "summary": "Read a saved template",
        "description": "Plus and Scale only. A markdown template returns its text (up to 80,000 characters), so it can be changed and sent as document.markdown, or saved with PATCH. A Google Doc template returns its link: each send exports the Doc again. A PDF template returns its name and field count.",
        "parameters": [
          {
            "name": "template_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^tpl_[a-f0-9]{24}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The template.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Template"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "patch": {
        "operationId": "updateTemplate",
        "summary": "Change a saved template",
        "description": "Plus and Scale only. Rename a template, replace the whole text of a markdown template, or point a Google Doc template at another Doc. The old version cannot be restored; requests already sent do not change. A PDF template can only be renamed (409 template_kind for other changes). New markdown is checked as a send would check it, and a new google_doc_url is checked with Google. Saved field positions are removed when a Google Doc template points at another Doc.",
        "parameters": [
          {
            "name": "template_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^tpl_[a-f0-9]{24}$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 80,
                    "description": "A new short name, with no signer name in it."
                  },
                  "markdown": {
                    "type": "string",
                    "description": "The whole new text of a markdown template, with the same field tokens as POST /v1/requests."
                  },
                  "google_doc_url": {
                    "type": "string",
                    "description": "Another Google Docs link, shared as \"Anyone with the link can view\", for a Google Doc template."
                  }
                },
                "minProperties": 1
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Saved.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TemplateUpdateResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "413": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "delete": {
        "operationId": "deleteTemplate",
        "summary": "Delete a saved template",
        "description": "Requests already sent from the template are not changed. Works on every plan, so a Free account can delete templates it saved on a paid plan.",
        "parameters": [
          {
            "name": "template_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^tpl_[a-f0-9]{24}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "template_id": {
                      "type": "string"
                    },
                    "deleted": {
                      "const": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "An API key from https://mcp.portant.co/keys. Without it, a create request needs sender.email, and Portant emails the sender to confirm before the signer gets anything."
      }
    },
    "schemas": {
      "CreateResult": {
        "type": "object",
        "properties": {
          "request_id": {
            "type": "string"
          },
          "request_token": {
            "type": "string",
            "description": "Keep it: it is the only way to download the signed PDF. Portant does not store it."
          },
          "status": {
            "type": "string",
            "enum": [
              "awaiting_sender_confirmation",
              "awaiting_sender_signature",
              "pending"
            ]
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "next_step": {
            "type": "string",
            "description": "A sentence to tell the user."
          },
          "signer_link": {
            "type": "string",
            "format": "uri",
            "description": "Only with delivery \"link\" when the sender does not sign: the private signing link to give the signer."
          },
          "sender_link": {
            "type": "string",
            "format": "uri",
            "description": "Only with sender_signs, for a request made with an API key: the link for the sender to sign first. With delivery \"email\" Portant also emails it to the sender."
          },
          "review_link": {
            "type": "string",
            "format": "uri",
            "description": "Only with review, for a request made with an API key when the sender does not sign: the link for the sender to check the document and click Send. Nothing goes to the signer before that. With delivery \"email\" Portant also emails it to the sender."
          },
          "status_link": {
            "type": "string",
            "format": "uri"
          },
          "usage": {
            "type": "object",
            "properties": {
              "used_this_month": {
                "type": "integer"
              },
              "monthly_limit": {
                "type": "integer"
              }
            }
          },
          "template": {
            "type": "object",
            "description": "Only with save_as_template: the saved template.",
            "properties": {
              "template_id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              }
            }
          },
          "template_error": {
            "type": "string",
            "description": "Only when the document was sent but could not be saved as a template."
          }
        }
      },
      "Status": {
        "type": "object",
        "properties": {
          "request_id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "awaiting_sender_confirmation",
              "awaiting_sender_signature",
              "pending",
              "signed",
              "cancelled",
              "expired"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "signed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "stored": {
            "type": "boolean",
            "description": "Plus and Scale: true while Portant stores the signed PDF for the account. It stays until you delete it, and you can download it from the account page or with an API key. Without stored, the signed copy is deleted 14 days after signing."
          },
          "stored_copy_deleted_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the user deleted the stored signed PDF."
          },
          "next_step": {
            "type": "string"
          },
          "views": {
            "type": "integer",
            "description": "Plus and Scale only: how many times the signer opened the signing page. A reload in the same browser tab does not count again."
          },
          "last_viewed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Plus and Scale only: when the signer last opened the signing page."
          },
          "signers": {
            "type": "array",
            "description": "Only for a request with several signers (signers in the request): each one in signing order. email is shown to a Plus or Scale owner.",
            "items": {
              "type": "object",
              "properties": {
                "position": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 5
                },
                "name": {
                  "type": "string"
                },
                "email": {
                  "type": "string"
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "signed",
                    "signing",
                    "waiting"
                  ],
                  "description": "signing: it is this signer's turn."
                },
                "signed_at": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                }
              }
            }
          },
          "signer_link": {
            "type": "string",
            "format": "uri",
            "description": "Only with delivery \"link\", several signers and the X-Request-Token header: the signing link of the signer whose turn it is, to pass on."
          }
        }
      },
      "FindResult": {
        "type": "object",
        "properties": {
          "requests": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "request_id": {
                  "type": "string"
                },
                "title": {
                  "type": "string"
                },
                "signer": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "email": {
                      "type": "string",
                      "description": "Plus and Scale only."
                    }
                  }
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "awaiting_sender_confirmation",
                    "awaiting_sender_signature",
                    "pending",
                    "signed",
                    "cancelled",
                    "expired"
                  ]
                },
                "sent_at": {
                  "type": "string",
                  "format": "date-time"
                },
                "signed_at": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                },
                "expires_at": {
                  "type": "string",
                  "format": "date-time"
                },
                "views": {
                  "type": "integer",
                  "description": "Plus and Scale only: how many times the signer opened the signing page. A reload in the same browser tab does not count again."
                },
                "last_viewed_at": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time",
                  "description": "Plus and Scale only: when the signer last opened the signing page."
                }
              }
            }
          },
          "total_matches": {
            "type": "integer",
            "description": "All matches in the searched requests; at most 10 are returned."
          },
          "next_step": {
            "type": "string",
            "description": "A sentence to tell the user."
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "error",
          "message"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "A stable code, for example bad_request, monthly_limit, rate_limited, not_found, bad_token."
          },
          "message": {
            "type": "string",
            "description": "A sentence that says what to do."
          },
          "resets_at": {
            "type": "string",
            "format": "date-time"
          },
          "retryAfter": {
            "type": "integer"
          }
        }
      },
      "ReminderResult": {
        "type": "object",
        "properties": {
          "request_id": {
            "type": "string"
          },
          "status": {
            "const": "pending"
          },
          "reminders_sent": {
            "type": "integer",
            "description": "Reminders sent for this request so far, this one included."
          },
          "reminders_left": {
            "type": "integer"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "next_step": {
            "type": "string",
            "description": "A sentence to tell the user."
          }
        }
      },
      "Webhook": {
        "type": "object",
        "properties": {
          "url": {
            "type": [
              "string",
              "null"
            ]
          },
          "last_delivery": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "at": {
                "type": "string",
                "format": "date-time"
              },
              "result": {
                "type": "string",
                "description": "The HTTP status of the last try, or \"timeout\" or \"no response\"."
              }
            }
          }
        }
      },
      "TemplateSummary": {
        "type": "object",
        "properties": {
          "template_id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "markdown",
              "pdf",
              "google_doc"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_used_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "uses": {
            "type": "integer"
          }
        }
      },
      "Template": {
        "type": "object",
        "properties": {
          "template_id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "markdown",
              "pdf",
              "google_doc"
            ]
          },
          "title": {
            "type": "string"
          },
          "markdown": {
            "type": "string",
            "description": "Markdown templates of 80,000 characters or fewer."
          },
          "google_doc_url": {
            "type": "string",
            "description": "Google Doc templates: the Doc that each send exports again, so the signer gets the Doc as it is at that time."
          },
          "field_count": {
            "type": "integer",
            "description": "PDF and Google Doc templates: the saved field positions. 0 means each send looks for the signature lines in the document, and adds a signature page when it cannot place them."
          },
          "next_step": {
            "type": "string"
          }
        }
      },
      "TemplateUpdateResult": {
        "type": "object",
        "properties": {
          "template_id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "markdown",
              "pdf",
              "google_doc"
            ]
          },
          "changed": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "name",
                "markdown",
                "google_doc_url"
              ]
            },
            "description": "What changed. Empty when the template already had these values."
          },
          "next_step": {
            "type": "string"
          }
        }
      }
    },
    "responses": {
      "Error": {
        "description": "An error.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    }
  },
  "webhooks": {
    "requestSigned": {
      "post": {
        "summary": "A document was signed",
        "description": "Sent to the webhook URL of a Plus or Scale account when one of its documents is signed, and type \"test\" for a test event. A request with several signers also sends \"request.signer_signed\" each time a signer who is not the last one signs. Portant tries three times within about 10 seconds and stops at the first 2xx answer. The header Portant-Signature is \"t=<unix seconds>,v1=<hex>\", where hex is the HMAC-SHA256 of \"<t>.<raw body>\" with the signing secret. Check it, and refuse a t older than 5 minutes. The event holds no document, title or signer: get those with the request_token.",
        "parameters": [
          {
            "name": "Portant-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Portant-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "request.signed",
                "request.signer_signed",
                "test"
              ]
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "evt_<hex>, unique per event."
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "request.signed",
                      "request.signer_signed",
                      "test"
                    ]
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "request_id": {
                        "type": "string"
                      },
                      "status": {
                        "type": "string",
                        "enum": [
                          "signed"
                        ],
                        "description": "request.signed only."
                      },
                      "signed_at": {
                        "type": "string",
                        "format": "date-time"
                      },
                      "stored": {
                        "type": "boolean",
                        "description": "request.signed only: whether Portant stores the signed PDF for the account."
                      },
                      "signer": {
                        "type": "integer",
                        "description": "request.signer_signed only: the position of the signer who signed, from 1."
                      },
                      "signers": {
                        "type": "integer",
                        "description": "request.signer_signed only: how many signers the request has."
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx answer stops the tries."
          }
        }
      }
    }
  }
}
