{
  "openapi": "3.1.0",
  "info": {
    "title": "Nex-Sign API",
    "version": "1.0.0",
    "description": "Embeddable e-signature API. Upload a DOCX containing placeholder anchor tokens (e.g. `$signature`, `$date_signed`); Nex-Sign converts it, emails each signer a magic link, captures the signature, and returns a sealed PDF + signed DOCX, then notifies your webhook. Every response uses the uniform envelope `{ success, data, error, meta }` (the download URL and this document are the exceptions — they are returned raw). Declared anchor tokens are replaced/hidden in the output; unmatched tokens are left as-is. Documents are stored in AWS London (eu-west-2)."
  },
  "servers": [{ "url": "https://nex-sign.com" }],
  "security": [{ "bearerAuth": [] }],
  "tags": [
    { "name": "Envelopes", "description": "Create, track, download and cancel signing envelopes." },
    { "name": "Business", "description": "Per-business configuration (webhooks, branding)." }
  ],
  "paths": {
    "/v1/envelopes": {
      "post": {
        "tags": ["Envelopes"],
        "summary": "Create & send an envelope",
        "description": "Multipart request: one or more `files` plus a JSON `payload`. Returns 202; conversion, delivery and sealing run asynchronously. A field targets its document by `documentIndex` (0-based upload position, preferred) or `documentName` (a repeated filename is rejected).",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": ["files", "payload"],
                "properties": {
                  "files": {
                    "type": "array",
                    "items": { "type": "string", "format": "binary" },
                    "description": "One or more DOCX files."
                  },
                  "payload": {
                    "type": "string",
                    "description": "JSON-encoded CreateEnvelope (see schema)."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted — envelope created and dispatched.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/EnvelopeEnvelope" }
              }
            }
          },
          "402": { "$ref": "#/components/responses/Error" },
          "409": { "$ref": "#/components/responses/Error" },
          "422": { "$ref": "#/components/responses/Error" }
        }
      },
      "get": {
        "tags": ["Envelopes"],
        "summary": "List envelopes",
        "parameters": [
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 20 } },
          { "name": "cursor", "in": "query", "schema": { "type": "string" } },
          { "name": "status", "in": "query", "schema": { "$ref": "#/components/schemas/EnvelopeStatus" } }
        ],
        "responses": {
          "200": { "description": "A page of envelopes.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EnvelopeListEnvelope" } } } }
        }
      }
    },
    "/v1/envelopes/{id}": {
      "get": {
        "tags": ["Envelopes"],
        "summary": "Get envelope detail (recipients + documents)",
        "parameters": [{ "$ref": "#/components/parameters/EnvelopeId" }],
        "responses": {
          "200": { "description": "Envelope with recipients and documents.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EnvelopeDetailEnvelope" } } } },
          "404": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/envelopes/{id}/audit": {
      "get": {
        "tags": ["Envelopes"],
        "summary": "Get the envelope audit trail",
        "parameters": [{ "$ref": "#/components/parameters/EnvelopeId" }],
        "responses": { "200": { "description": "Ordered audit events (created, sent, viewed, signed, completed, voided…)." } }
      }
    },
    "/v1/envelopes/{id}/documents/{docId}/download": {
      "get": {
        "tags": ["Envelopes"],
        "summary": "Get a short-lived download URL",
        "description": "Returns JSON `{ url, expiresInSeconds }` (not a redirect). The URL is a pre-signed S3 link valid for 900 seconds (15 min). Fetch the `docId` from GET /v1/envelopes/{id} first.",
        "parameters": [
          { "$ref": "#/components/parameters/EnvelopeId" },
          { "name": "docId", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } },
          { "name": "type", "in": "query", "required": true, "schema": { "type": "string", "enum": ["source", "rendered", "signed_docx", "signed_pdf"] }, "description": "source = original DOCX · rendered = signer preview PDF · signed_docx = signed working copy · signed_pdf = sealed legal record (with audit certificate)." }
        ],
        "responses": {
          "200": { "description": "A time-limited download link.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DownloadEnvelope" } } } },
          "404": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/envelopes/{id}/remind": {
      "post": {
        "tags": ["Envelopes"],
        "summary": "Re-send the invite to recipients who haven't signed",
        "parameters": [{ "$ref": "#/components/parameters/EnvelopeId" }],
        "responses": { "202": { "description": "Reminder queued.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EnvelopeEnvelope" } } } } }
      }
    },
    "/v1/envelopes/{id}/void": {
      "post": {
        "tags": ["Envelopes"],
        "summary": "Void (cancel) an envelope",
        "description": "Cancels signing. The recipients' magic links are blocked immediately — a canceled document can no longer be signed — and an `envelope.voided` webhook fires.",
        "parameters": [{ "$ref": "#/components/parameters/EnvelopeId" }],
        "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { "reason": { "type": "string" } } } } } },
        "responses": { "202": { "description": "Envelope voided.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EnvelopeEnvelope" } } } } }
      }
    },
    "/v1/envelopes/{id}/recipients/{recipientId}/signing-link": {
      "post": {
        "tags": ["Envelopes"],
        "summary": "Generate a signing link for a recipient",
        "description": "Issues a fresh magic signing link and returns its URL, so you can deliver it via SMS, chat, or your own portal instead of email. The link is a single-use bearer credential that expires (30 days). Works only while the envelope is open (`sent`/`in_progress`) and the recipient hasn't signed.",
        "parameters": [
          { "$ref": "#/components/parameters/EnvelopeId" },
          { "name": "recipientId", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }
        ],
        "responses": {
          "200": { "description": "A fresh signing link.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SigningLinkEnvelope" } } } },
          "404": { "$ref": "#/components/responses/Error" },
          "409": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/business/{id}/webhook": {
      "patch": {
        "tags": ["Business"],
        "summary": "Set the webhook URL & secret",
        "description": "Configure where events are POSTed and the secret used to sign them. You choose your own `webhookSecret`; it is HMAC-SHA256'd over the raw request body into the `X-NexSign-Signature` header on every delivery.",
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }],
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "type": "object", "properties": { "webhookUrl": { "type": "string", "format": "uri", "nullable": true }, "webhookSecret": { "type": "string", "nullable": true } } } } }
        },
        "responses": { "200": { "description": "Updated business." } }
      }
    }
  },
  "webhooks": {
    "envelope.sent": { "post": { "summary": "Envelope dispatched to signers", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookEnvelopeSent" } } } }, "responses": { "2XX": { "description": "Acknowledged." } } } },
    "recipient.viewed": { "post": { "summary": "A signer opened the document (first time)", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookRecipient" } } } }, "responses": { "2XX": { "description": "Acknowledged." } } } },
    "recipient.signed": { "post": { "summary": "A recipient signed", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookRecipient" } } } }, "responses": { "2XX": { "description": "Acknowledged." } } } },
    "recipient.bounced": { "post": { "summary": "An invite email could not be delivered", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookRecipient" } } } }, "responses": { "2XX": { "description": "Acknowledged." } } } },
    "envelope.voided": { "post": { "summary": "Envelope canceled", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookEnvelopeVoided" } } } }, "responses": { "2XX": { "description": "Acknowledged." } } } },
    "envelope.completed": { "post": { "summary": "Every recipient has signed", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookEnvelopeCompleted" } } } }, "responses": { "2XX": { "description": "Acknowledged." } } } }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": { "type": "http", "scheme": "bearer", "description": "Your API token. The business is inferred from the token." }
    },
    "parameters": {
      "EnvelopeId": { "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }
    },
    "responses": {
      "Error": { "description": "Uniform error envelope.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } }
    },
    "schemas": {
      "EnvelopeStatus": { "type": "string", "enum": ["draft", "sent", "in_progress", "completed", "declined", "voided", "expired"] },
      "FieldType": { "type": "string", "enum": ["signature", "date_signed"] },
      "AnchorType": { "type": "string", "enum": ["text", "bookmark"], "default": "text" },
      "ErrorCode": { "type": "string", "enum": ["bad_request", "validation_failed", "unauthorized", "forbidden", "not_found", "conflict", "payment_required", "rate_limited", "email_not_verified", "internal_error"] },
      "CreateEnvelope": {
        "type": "object",
        "required": ["subject", "recipients", "fields"],
        "properties": {
          "subject": { "type": "string" },
          "message": { "type": "string" },
          "externalId": { "type": "string", "description": "Your own reference, echoed on the envelope and every webhook." },
          "sequential": { "type": "boolean", "default": false, "description": "true = chain (each signer emailed after the previous signs); false = parallel." },
          "recipients": { "type": "array", "items": { "$ref": "#/components/schemas/Recipient" } },
          "fields": { "type": "array", "items": { "$ref": "#/components/schemas/Field" } }
        }
      },
      "Recipient": {
        "type": "object",
        "required": ["email", "fullName"],
        "properties": {
          "email": { "type": "string", "format": "email" },
          "fullName": { "type": "string" },
          "routingOrder": { "type": "integer", "minimum": 1, "description": "Signing order in sequential mode." }
        }
      },
      "Field": {
        "type": "object",
        "required": ["recipientEmail", "type", "anchorKeyword"],
        "description": "Targets a document by documentIndex (preferred) or documentName. If the same anchorKeyword appears multiple times in a document it is placed at every occurrence; the signer adopts once to fill them all.",
        "properties": {
          "documentIndex": { "type": "integer", "minimum": 0, "description": "0-based position in the uploaded files. Preferred; collision-proof." },
          "documentName": { "type": "string", "description": "Original filename. Rejected if two uploaded files share this name." },
          "recipientEmail": { "type": "string", "format": "email" },
          "type": { "$ref": "#/components/schemas/FieldType" },
          "anchorKeyword": { "type": "string", "description": "Text token in the DOCX (e.g. $signature) or, with anchorType=bookmark, a Word bookmark name." },
          "anchorType": { "$ref": "#/components/schemas/AnchorType" },
          "required": { "type": "boolean", "default": true }
        }
      },
      "Envelope": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "externalId": { "type": "string", "nullable": true },
          "subject": { "type": "string" },
          "message": { "type": "string", "nullable": true },
          "status": { "$ref": "#/components/schemas/EnvelopeStatus" },
          "sequential": { "type": "boolean" },
          "expiresAt": { "type": "string", "format": "date-time", "nullable": true },
          "sentAt": { "type": "string", "format": "date-time", "nullable": true },
          "completedAt": { "type": "string", "format": "date-time", "nullable": true },
          "createdAt": { "type": "string", "format": "date-time" }
        }
      },
      "Download": {
        "type": "object",
        "properties": {
          "url": { "type": "string", "format": "uri", "description": "Pre-signed S3 URL." },
          "expiresInSeconds": { "type": "integer", "example": 900 }
        }
      },
      "SigningLink": {
        "type": "object",
        "properties": {
          "recipientEmail": { "type": "string", "format": "email" },
          "recipientFullName": { "type": "string" },
          "url": { "type": "string", "format": "uri", "description": "The magic signing link to deliver via any channel." },
          "expiresAt": { "type": "string", "format": "date-time" }
        }
      },
      "SigningLinkEnvelope": { "allOf": [{ "$ref": "#/components/schemas/SuccessEnvelope" }, { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/SigningLink" } } }] },
      "ApiError": {
        "type": "object",
        "properties": {
          "code": { "$ref": "#/components/schemas/ErrorCode" },
          "message": { "type": "string" },
          "details": {}
        }
      },
      "EnvelopeEnvelope": { "allOf": [{ "$ref": "#/components/schemas/SuccessEnvelope" }, { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/Envelope" } } }] },
      "PaginationMeta": { "type": "object", "properties": { "limit": { "type": "integer" }, "nextCursor": { "type": "string", "nullable": true }, "hasMore": { "type": "boolean" } } },
      "EnvelopeListEnvelope": { "allOf": [{ "$ref": "#/components/schemas/SuccessEnvelope" }, { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/Envelope" } }, "meta": { "type": "object", "properties": { "pagination": { "$ref": "#/components/schemas/PaginationMeta" } } } } }] },
      "EnvelopeDetailEnvelope": { "allOf": [{ "$ref": "#/components/schemas/SuccessEnvelope" }, { "type": "object", "properties": { "data": { "type": "object", "description": "Envelope plus recipients[] and documents[]." } } }] },
      "DownloadEnvelope": { "allOf": [{ "$ref": "#/components/schemas/SuccessEnvelope" }, { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/Download" } } }] },
      "SuccessEnvelope": { "type": "object", "properties": { "success": { "type": "boolean", "example": true }, "data": {}, "error": { "type": "null" }, "meta": { "type": ["object", "null"] } } },
      "ErrorEnvelope": { "type": "object", "properties": { "success": { "type": "boolean", "example": false }, "data": { "type": "null" }, "error": { "$ref": "#/components/schemas/ApiError" }, "meta": { "type": "null" } } },
      "WebhookEnvelopeSent": { "type": "object", "properties": { "event": { "const": "envelope.sent" }, "envelopeId": { "type": "string" }, "externalId": { "type": "string", "nullable": true }, "subject": { "type": "string" }, "sentAt": { "type": "string", "format": "date-time", "nullable": true } } },
      "WebhookEnvelopeVoided": { "type": "object", "properties": { "event": { "const": "envelope.voided" }, "envelopeId": { "type": "string" }, "externalId": { "type": "string", "nullable": true }, "reason": { "type": "string", "nullable": true } } },
      "WebhookEnvelopeCompleted": { "type": "object", "properties": { "event": { "const": "envelope.completed" }, "envelopeId": { "type": "string" }, "externalId": { "type": "string", "nullable": true }, "subject": { "type": "string" }, "completedAt": { "type": "string", "format": "date-time", "nullable": true }, "documents": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" } } } } } },
      "WebhookRecipient": { "type": "object", "properties": { "event": { "type": "string", "enum": ["recipient.viewed", "recipient.signed", "recipient.bounced"] }, "envelopeId": { "type": "string" }, "externalId": { "type": "string", "nullable": true }, "recipient": { "type": "object", "properties": { "email": { "type": "string" }, "fullName": { "type": "string" }, "routingOrder": { "type": "integer" } } }, "envelopeStatus": { "$ref": "#/components/schemas/EnvelopeStatus" } } }
    }
  }
}
