{
  "openapi": "3.0.3",
  "info": {
    "title": "JuriSign API",
    "description": "Electronic signature API (eIDAS SES level) with OTP verification (email or SMS). Upload documents, create signature requests with multiple signers, and track the signing process via webhooks. Rate limits, per minute and per authenticated user: 60 regular calls, 20 document uploads, 30 send/cancel/relaunch actions, 30 webhook management calls. Authentication is limited to 10 attempts per minute for the targeted account, plus 60 per minute for the calling IP. Exceeding a limit returns 429 with a Retry-After header — wait for it rather than retrying immediately.",
    "version": "3.24.1",
    "contact": {
      "name": "JuriSign Support",
      "email": "support@jurisign.fr",
      "url": "https://www.jurisign.fr"
    },
    "license": {
      "name": "Proprietary"
    }
  },
  "servers": [
    {
      "url": "https://www.jurisign.fr/api/v1",
      "description": "Production (also serves sandbox requests — see POST /auth/sandbox-token)"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Authentication",
      "description": "Obtain and revoke API tokens"
    },
    {
      "name": "Documents",
      "description": "Upload and manage PDF documents"
    },
    {
      "name": "Sign Requests",
      "description": "Create, send, and track signature requests"
    },
    {
      "name": "Templates",
      "description": "Reusable sign request templates"
    },
    {
      "name": "Bulk",
      "description": "Merge templates and bulk campaigns (one individualised request per recipient)"
    },
    {
      "name": "Public Forms",
      "description": "Shareable links: each respondent signs their own generated request, no account needed"
    },
    {
      "name": "Account",
      "description": "Credit balance and plan"
    },
    {
      "name": "Webhooks",
      "description": "Manage webhook endpoints for event notifications"
    }
  ],
  "paths": {
    "/": {
      "get": {
        "summary": "API Status",
        "description": "Returns the current API status and version information.",
        "operationId": "getStatus",
        "security": [],
        "tags": [
          "Authentication"
        ],
        "responses": {
          "200": {
            "description": "API is operational",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string",
                      "example": "JuriSign API"
                    },
                    "version": {
                      "type": "string",
                      "example": "3.2"
                    },
                    "status": {
                      "type": "string",
                      "example": "ok"
                    },
                    "docs": {
                      "type": "string",
                      "example": "https://www.jurisign.fr/api/docs"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/auth/token": {
      "post": {
        "summary": "Create API Token",
        "description": "Authenticate with email/password to obtain a Bearer token, scoped to your organization. By default the token is granted all 5 available scopes (documents:read, documents:write, sign-requests:read, sign-requests:write, webhooks:manage). Pass `scopes` to request a restricted subset instead — every endpoint checks the token's abilities and returns 403 if the required scope is missing. Rate limited to 10 attempts per minute for the targeted account, and 60 per minute for the calling IP.",
        "operationId": "createToken",
        "security": [],
        "tags": [
          "Authentication"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "password"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "example": "user@example.com",
                    "description": "Your account email address"
                  },
                  "password": {
                    "type": "string",
                    "format": "password",
                    "example": "your-password",
                    "description": "Your account password"
                  },
                  "device_name": {
                    "type": "string",
                    "maxLength": 100,
                    "example": "my-app",
                    "description": "Optional device identifier. Tokens with the same device name will be revoked and replaced."
                  },
                  "scopes": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "documents:read",
                        "documents:write",
                        "sign-requests:read",
                        "sign-requests:write",
                        "webhooks:manage"
                      ]
                    },
                    "example": [
                      "documents:read",
                      "sign-requests:write"
                    ],
                    "description": "Optional. Restricts the token to the given scopes. `sign-requests:read` / `sign-requests:write` also cover the templates and bulk-* endpoints. Omit to receive all 5 scopes (default, unchanged behavior for existing integrations)."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Token created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "token": {
                      "type": "string",
                      "example": "1|abc123..."
                    },
                    "token_type": {
                      "type": "string",
                      "example": "Bearer"
                    },
                    "user": {
                      "$ref": "#/components/schemas/AuthUser"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Account not in an organization or organization suspended",
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "description": "Invalid credentials or validation error",
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "description": "Rate limit exceeded (10 attempts/minute for the targeted account, 60/minute for the calling IP) — see the Retry-After header",
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "delete": {
        "summary": "Revoke Current Token",
        "description": "Revoke the Bearer token used in this request.",
        "operationId": "revokeToken",
        "tags": [
          "Authentication"
        ],
        "responses": {
          "200": {
            "description": "Token revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          }
        }
      }
    },
    "/auth/sandbox-token": {
      "post": {
        "summary": "Create Sandbox API Token",
        "description": "Authenticate with the same email/password as POST /auth/token, but obtain a sandbox token instead (prefixed sandbox_...). Requests made with this token never send real SMS or emails (SMS are logged, emails go through the log mailer) and never deduct signature credits. Documents and signature requests created in sandbox mode are isolated from your organization's real data: they never appear in a normal (non-sandbox) request, and vice versa. A sandbox token can only be used with the sandbox_ prefix, and a normal token can never be used with it — mixing the two returns 401. Rate limited to 10 attempts per minute for the targeted account, and 60 per minute for the calling IP.",
        "operationId": "createSandboxToken",
        "security": [],
        "tags": [
          "Authentication"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "password"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "example": "user@example.com",
                    "description": "Your account email address"
                  },
                  "password": {
                    "type": "string",
                    "format": "password",
                    "example": "your-password",
                    "description": "Your account password"
                  },
                  "device_name": {
                    "type": "string",
                    "maxLength": 100,
                    "example": "my-app-sandbox",
                    "description": "Optional device identifier. Tokens with the same device name will be revoked and replaced."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Sandbox token created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "token": {
                      "type": "string",
                      "example": "sandbox_1|abc123..."
                    },
                    "token_type": {
                      "type": "string",
                      "example": "Bearer"
                    },
                    "sandbox": {
                      "type": "boolean",
                      "example": true
                    },
                    "user": {
                      "$ref": "#/components/schemas/AuthUser"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Account not in an organization or organization suspended",
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "description": "Invalid credentials or validation error",
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "description": "Rate limit exceeded (10 attempts/minute for the targeted account, 60/minute for the calling IP) — see the Retry-After header",
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/account/credits": {
      "get": {
        "summary": "Get Credit Balance",
        "description": "Retrieve your organization's real-time signature credit consumption. Scoped strictly to the organization of the authenticated token.",
        "operationId": "getAccountCredits",
        "tags": [
          "Account"
        ],
        "responses": {
          "200": {
            "description": "Credit balance",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "plan": {
                          "type": "string",
                          "example": "equipe"
                        },
                        "credits_total": {
                          "type": "integer",
                          "example": 100
                        },
                        "credits_used": {
                          "type": "integer",
                          "example": 41
                        },
                        "credits_remaining": {
                          "type": "integer",
                          "example": 59,
                          "description": "Never negative, even if usage exceeds the plan."
                        },
                        "trial_ends_at": {
                          "type": "string",
                          "format": "date-time",
                          "nullable": true
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          }
        }
      }
    },
    "/templates": {
      "get": {
        "summary": "List Sign Request Templates",
        "description": "Retrieve reusable sign request templates for your organization.",
        "operationId": "listTemplates",
        "tags": [
          "Templates"
        ],
        "parameters": [
          {
            "name": "per_page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 20
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Templates list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Template"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          }
        }
      }
    },
    "/templates/{id}": {
      "get": {
        "summary": "Get Template",
        "description": "Retrieve full details of a template, including signers and signature zones.",
        "operationId": "getTemplate",
        "tags": [
          "Templates"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ResourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Template details",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/TemplateDetailed"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "summary": "Delete Template",
        "description": "Delete a template and its own copy of the PDF, if any.",
        "operationId": "deleteTemplate",
        "tags": [
          "Templates"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ResourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Template deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/templates/{id}/sign-requests": {
      "post": {
        "summary": "Create Sign Request From Template",
        "description": "Create a signature request from a template, reusing its signers, signature zones, and (if present) its own copy of the document. Subject, message, expiry and redirect_url can be overridden. If the template has no document of its own, `document_id` is required.",
        "operationId": "createSignRequestFromTemplate",
        "tags": [
          "Templates"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ResourceId"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "subject": {
                    "type": "string",
                    "maxLength": 255
                  },
                  "message": {
                    "type": "string",
                    "maxLength": 2000,
                    "nullable": true
                  },
                  "metadata": {
                    "type": "object",
                    "nullable": true
                  },
                  "expiry_hours": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 720
                  },
                  "auto_send": {
                    "type": "boolean"
                  },
                  "redirect_url": {
                    "type": "string",
                    "format": "uri",
                    "maxLength": 2048
                  },
                  "document_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Required only if the template has no document of its own"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Sign request created from template",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/SignRequest"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      }
    },
    "/bulk-templates": {
      "get": {
        "summary": "List Bulk Templates",
        "description": "Retrieve reusable merge-field HTML templates used by bulk campaigns.",
        "operationId": "listBulkTemplates",
        "tags": [
          "Bulk"
        ],
        "responses": {
          "200": {
            "description": "Bulk templates list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/BulkTemplate"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          }
        }
      },
      "post": {
        "summary": "Create Bulk Template",
        "description": "Create a merge-field HTML template (e.g. `<p>Bonjour {{prenom}} {{nom}}</p>`). Merge fields are auto-extracted from the content.",
        "operationId": "createBulkTemplate",
        "tags": [
          "Bulk"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "html_content"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 255
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 1000,
                    "nullable": true
                  },
                  "html_content": {
                    "type": "string",
                    "description": "HTML with {{field}} placeholders"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Bulk template created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/BulkTemplate"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      }
    },
    "/bulk-templates/{id}": {
      "get": {
        "summary": "Get Bulk Template",
        "operationId": "getBulkTemplate",
        "tags": [
          "Bulk"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ResourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Bulk template details",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/BulkTemplate"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "put": {
        "summary": "Update Bulk Template",
        "operationId": "updateBulkTemplate",
        "tags": [
          "Bulk"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ResourceId"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "description": {
                    "type": "string",
                    "nullable": true
                  },
                  "html_content": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Bulk template updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/BulkTemplate"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "summary": "Delete Bulk Template",
        "description": "Fails with 422 if the template is still referenced by a campaign.",
        "operationId": "deleteBulkTemplate",
        "tags": [
          "Bulk"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ResourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Bulk template deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/bulk-campaigns": {
      "get": {
        "summary": "List Bulk Campaigns",
        "operationId": "listBulkCampaigns",
        "tags": [
          "Bulk"
        ],
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 20
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Bulk campaigns list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/BulkCampaign"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          }
        }
      },
      "post": {
        "summary": "Create Bulk Campaign",
        "description": "Create a bulk campaign with recipients provided directly as JSON (no Excel upload needed for API clients). Each recipient generates one individualized sign request, merged from the bulk template. Invalid rows (missing fields, invalid email, duplicate email) are skipped and reported in `skipped_recipients`, not fatal.",
        "operationId": "createBulkCampaign",
        "tags": [
          "Bulk"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "bulk_template_id",
                  "expiry_hours",
                  "otp_channel",
                  "recipients"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 255
                  },
                  "bulk_template_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "expiry_hours": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 720
                  },
                  "otp_channel": {
                    "type": "string",
                    "enum": [
                      "sms",
                      "email"
                    ]
                  },
                  "message": {
                    "type": "string",
                    "maxLength": 2000,
                    "nullable": true
                  },
                  "require_selfie": {
                    "type": "boolean"
                  },
                  "recipients": {
                    "type": "array",
                    "minItems": 1,
                    "items": {
                      "type": "object",
                      "required": [
                        "nom",
                        "prenom",
                        "email"
                      ],
                      "properties": {
                        "nom": {
                          "type": "string"
                        },
                        "prenom": {
                          "type": "string"
                        },
                        "email": {
                          "type": "string",
                          "format": "email"
                        },
                        "telephone": {
                          "type": "string",
                          "nullable": true
                        },
                        "adresse": {
                          "type": "string",
                          "nullable": true
                        }
                      },
                      "description": "Extra keys are accepted and become available as {{merge fields}} in the bulk template."
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Campaign created (draft, not yet launched)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/BulkCampaign"
                    },
                    "skipped_recipients": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      }
    },
    "/bulk-campaigns/{id}": {
      "get": {
        "summary": "Get Bulk Campaign",
        "operationId": "getBulkCampaign",
        "tags": [
          "Bulk"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ResourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Bulk campaign details",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/BulkCampaign"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "summary": "Delete Bulk Campaign",
        "description": "Fails with 422 if the campaign is currently processing.",
        "operationId": "deleteBulkCampaign",
        "tags": [
          "Bulk"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ResourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Bulk campaign deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/bulk-campaigns/{id}/launch": {
      "post": {
        "summary": "Launch Bulk Campaign",
        "description": "Dispatches an asynchronous job that generates each recipient's document, creates their sign request, and sends it. Fails with 422 if the organization does not have enough remaining credits.",
        "operationId": "launchBulkCampaign",
        "tags": [
          "Bulk"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ResourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Campaign launched (status: processing)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/BulkCampaign"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/bulk-campaigns/{id}/cancel": {
      "post": {
        "summary": "Cancel Bulk Campaign",
        "operationId": "cancelBulkCampaign",
        "tags": [
          "Bulk"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ResourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Campaign cancelled",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/BulkCampaign"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/bulk-campaigns/{id}/retry": {
      "post": {
        "summary": "Retry Failed Recipients",
        "description": "Resets recipients in error back to pending so the campaign can be launched again.",
        "operationId": "retryBulkCampaign",
        "tags": [
          "Bulk"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ResourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Failed recipients reset",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/BulkCampaign"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/bulk-campaigns/{id}/export": {
      "get": {
        "summary": "Export Campaign Results",
        "description": "Per-recipient status, send/sign timestamps, and error messages.",
        "operationId": "exportBulkCampaignResults",
        "tags": [
          "Bulk"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ResourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Recipient results",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "nom": {
                            "type": "string"
                          },
                          "prenom": {
                            "type": "string"
                          },
                          "email": {
                            "type": "string"
                          },
                          "telephone": {
                            "type": "string",
                            "nullable": true
                          },
                          "status": {
                            "type": "string"
                          },
                          "sent_at": {
                            "type": "string",
                            "format": "date-time",
                            "nullable": true
                          },
                          "signed_at": {
                            "type": "string",
                            "format": "date-time",
                            "nullable": true
                          },
                          "error_message": {
                            "type": "string",
                            "nullable": true
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/public-forms": {
      "get": {
        "summary": "List Public Forms",
        "description": "Retrieve the shareable public forms (PowerForms) of your organization.",
        "operationId": "listPublicForms",
        "tags": [
          "Public Forms"
        ],
        "parameters": [
          {
            "name": "per_page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 20
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Public forms list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PublicForm"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          }
        }
      },
      "post": {
        "summary": "Create Public Form",
        "description": "Publish a shareable link backed by one of your sign request templates. Anyone opening the link fills in their details and becomes the signer of their own signature request, generated on the fly. The template MUST have exactly one signer and its own copy of the PDF.",
        "operationId": "createPublicForm",
        "tags": [
          "Public Forms"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "sign_request_template_id"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 255
                  },
                  "sign_request_template_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Template with exactly 1 signer and its own document"
                  },
                  "expires_at": {
                    "type": "string",
                    "format": "date-time",
                    "nullable": true
                  },
                  "daily_submission_limit": {
                    "type": "integer",
                    "minimum": 1,
                    "nullable": true,
                    "description": "Anti-abuse cap; clamped to the platform maximum"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Public form created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PublicForm"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      }
    },
    "/public-forms/{id}": {
      "get": {
        "summary": "Get Public Form",
        "operationId": "getPublicForm",
        "tags": [
          "Public Forms"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ResourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Public form details",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PublicForm"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "put": {
        "summary": "Update Public Form",
        "description": "Rename, pause/reactivate, change the expiry or the daily cap.",
        "operationId": "updatePublicForm",
        "tags": [
          "Public Forms"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ResourceId"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "active",
                      "paused"
                    ]
                  },
                  "expires_at": {
                    "type": "string",
                    "format": "date-time",
                    "nullable": true
                  },
                  "daily_submission_limit": {
                    "type": "integer",
                    "nullable": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Public form updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PublicForm"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "summary": "Delete Public Form",
        "operationId": "deletePublicForm",
        "tags": [
          "Public Forms"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ResourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Public form deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/public-forms/{id}/rotate-token": {
      "post": {
        "summary": "Rotate Public Form Token",
        "description": "Generate a new public token. The previous link stops working immediately — use this if a link leaked or was shared too widely.",
        "operationId": "rotatePublicFormToken",
        "tags": [
          "Public Forms"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ResourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "New token generated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PublicForm"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/documents": {
      "get": {
        "summary": "List Documents",
        "description": "Retrieve a paginated list of documents belonging to your organization.",
        "operationId": "listDocuments",
        "tags": [
          "Documents"
        ],
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "description": "Filter by document status",
            "schema": {
              "type": "string",
              "enum": [
                "draft",
                "pending",
                "completed",
                "cancelled"
              ]
            }
          },
          {
            "name": "search",
            "in": "query",
            "description": "Search by document title",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "description": "Results per page (default: 20, max: 100)",
            "schema": {
              "type": "integer",
              "default": 20
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "Page number",
            "schema": {
              "type": "integer",
              "default": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Documents list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Document"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          }
        }
      },
      "post": {
        "summary": "Upload Document",
        "description": "Upload a PDF document. Maximum file size is 20 MB.",
        "operationId": "uploadDocument",
        "tags": [
          "Documents"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "PDF file to upload (max 20 MB)"
                  },
                  "title": {
                    "type": "string",
                    "maxLength": 255,
                    "description": "Document title (defaults to filename if omitted)"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Document uploaded",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Document"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      }
    },
    "/documents/{id}": {
      "get": {
        "summary": "Get Document",
        "description": "Retrieve details of a specific document.",
        "operationId": "getDocument",
        "tags": [
          "Documents"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ResourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Document details",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Document"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "summary": "Delete Document",
        "description": "Delete a document. Only documents in `draft` status can be deleted.",
        "operationId": "deleteDocument",
        "tags": [
          "Documents"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ResourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Document deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/documents/{id}/download": {
      "get": {
        "summary": "Download Document",
        "description": "Download the original PDF file.",
        "operationId": "downloadDocument",
        "tags": [
          "Documents"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ResourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "PDF file download",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/sign-requests": {
      "get": {
        "summary": "List Sign Requests",
        "description": "Retrieve a paginated list of signature requests.",
        "operationId": "listSignRequests",
        "tags": [
          "Sign Requests"
        ],
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "description": "Filter by status",
            "schema": {
              "type": "string",
              "enum": [
                "draft",
                "pending",
                "completed",
                "cancelled",
                "expired"
              ]
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 20
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Sign requests list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SignRequest"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          }
        }
      },
      "post": {
        "summary": "Create Sign Request",
        "description": "Create a new signature request with one or more signers. The request is created in `draft` status and must be sent explicitly via the send endpoint.",
        "operationId": "createSignRequest",
        "tags": [
          "Sign Requests"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateSignRequestInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Sign request created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/SignRequest"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "409": {
            "description": "The Idempotency-Key was already used with a different request body. Use a new key, or replay the original body unchanged."
          }
        },
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. Send a unique key (for example your own order reference) to make this call safely repeatable. If the network drops after your request reaches us but before you get the response, replay the exact same call with the same key: you receive the original response — same body, same status, with an `Idempotent-Replayed: true` header — and no second signature request is created, so no second SMS is billed. Reusing a key with a DIFFERENT body returns 409: that is an integration bug, not a retry. Keys are scoped to your organization and honoured for 24 hours. Failed responses never consume a key.",
            "schema": {
              "type": "string",
              "maxLength": 255
            },
            "example": "order-4821"
          }
        ]
      }
    },
    "/sign-requests/{id}": {
      "get": {
        "summary": "Get Sign Request",
        "description": "Retrieve full details of a signature request, including signers, signature zones, and download links.",
        "operationId": "getSignRequest",
        "tags": [
          "Sign Requests"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ResourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Sign request details",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/SignRequestDetailed"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/sign-requests/{id}/send": {
      "post": {
        "summary": "Send Sign Request",
        "description": "Send the signature request to all signers. This changes the status from `draft` to `pending` and sends invitation emails.",
        "operationId": "sendSignRequest",
        "tags": [
          "Sign Requests"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ResourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Request sent to signers",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/SignRequest"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/sign-requests/{id}/cancel": {
      "post": {
        "summary": "Cancel Sign Request",
        "description": "Cancel a pending signature request. Only requests in `pending` status can be cancelled.",
        "operationId": "cancelSignRequest",
        "tags": [
          "Sign Requests"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ResourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Request cancelled",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/SignRequest"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/sign-requests/{id}/download": {
      "get": {
        "summary": "Download Signed PDF",
        "description": "Download the signed PDF document. Only available after all signers have completed signing.",
        "operationId": "downloadSignedPdf",
        "tags": [
          "Sign Requests"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ResourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Signed PDF file",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/sign-requests/{id}/proof": {
      "get": {
        "summary": "Download Audit Proof",
        "description": "Download the audit proof PDF, containing a detailed log of all signing actions with timestamps and IP addresses.",
        "operationId": "downloadProof",
        "tags": [
          "Sign Requests"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ResourceId"
          }
        ],
        "responses": {
          "200": {
            "description": "Audit proof PDF",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/webhooks": {
      "get": {
        "summary": "List Webhook Endpoints",
        "description": "List all webhook endpoints for your organization.",
        "operationId": "listWebhooks",
        "tags": [
          "Webhooks"
        ],
        "responses": {
          "200": {
            "description": "Webhook endpoints",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WebhookEndpoint"
                      }
                    },
                    "available_events": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "example": [
                        "sign_request.sent",
                        "sign_request.completed",
                        "sign_request.cancelled",
                        "signer.signed",
                        "signer.declined",
                        "sign_request.relaunched",
                        "public_form.submitted",
                        "sign_request.submitted_for_approval",
                        "sign_request.approved",
                        "sign_request.rejected",
                        "payment.completed",
                        "payment.failed"
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          }
        }
      },
      "post": {
        "summary": "Create Webhook Endpoint",
        "description": "Register a new webhook endpoint. The response includes the signing secret which is only shown once.",
        "operationId": "createWebhook",
        "tags": [
          "Webhooks"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "url",
                  "events"
                ],
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "maxLength": 500,
                    "example": "https://your-app.com/webhooks/jurisign",
                    "description": "The HTTPS URL to receive webhook payloads"
                  },
                  "events": {
                    "type": "array",
                    "minItems": 1,
                    "items": {
                      "type": "string",
                      "enum": [
                        "sign_request.sent",
                        "sign_request.completed",
                        "sign_request.cancelled",
                        "signer.signed",
                        "signer.declined",
                        "sign_request.relaunched",
                        "public_form.submitted",
                        "sign_request.submitted_for_approval",
                        "sign_request.approved",
                        "sign_request.rejected",
                        "payment.completed",
                        "payment.failed"
                      ]
                    },
                    "description": "Events to subscribe to"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Webhook created (secret shown only once)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/WebhookEndpoint"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      }
    },
    "/webhooks/{id}": {
      "put": {
        "summary": "Update Webhook Endpoint",
        "description": "Update a webhook endpoint's URL, events, or active status.",
        "operationId": "updateWebhook",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "events": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "sign_request.sent",
                        "sign_request.completed",
                        "sign_request.cancelled",
                        "signer.signed",
                        "signer.declined",
                        "sign_request.relaunched",
                        "public_form.submitted",
                        "sign_request.submitted_for_approval",
                        "sign_request.approved",
                        "sign_request.rejected",
                        "payment.completed",
                        "payment.failed"
                      ]
                    }
                  },
                  "is_active": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webhook updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/WebhookEndpoint"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "summary": "Delete Webhook Endpoint",
        "description": "Delete a webhook endpoint and all its delivery logs.",
        "operationId": "deleteWebhook",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Webhook deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/webhooks/{id}/logs": {
      "get": {
        "summary": "Webhook Delivery Logs",
        "description": "View delivery logs for a specific webhook endpoint.",
        "operationId": "getWebhookLogs",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 20
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Delivery logs",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WebhookLog"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/webhooks/{id}/regenerate-secret": {
      "post": {
        "summary": "Regenerate Webhook Secret",
        "description": "Generate a new signing secret for the webhook endpoint. The old secret will no longer be valid.",
        "operationId": "regenerateWebhookSecret",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "New secret generated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "secret": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Obtain a token via POST /auth/token, then pass it as: Authorization: Bearer {token}"
      }
    },
    "parameters": {
      "ResourceId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "Resource UUID",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      }
    },
    "schemas": {
      "AuthUser": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "example": "Jean Dupont"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "role": {
            "type": "string",
            "example": "admin"
          },
          "organization": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "name": {
                "type": "string",
                "example": "My Company"
              }
            }
          }
        }
      },
      "Document": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "title": {
            "type": "string",
            "example": "Contract 2026"
          },
          "original_filename": {
            "type": "string",
            "example": "contract.pdf"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "pending",
              "completed",
              "cancelled"
            ]
          },
          "page_count": {
            "type": "integer",
            "example": 3
          },
          "file_size": {
            "type": "integer",
            "example": 245760,
            "description": "Size in bytes"
          },
          "file_hash": {
            "type": "string",
            "description": "SHA-256 hash of the original file"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_by": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "name": {
                "type": "string"
              }
            }
          }
        }
      },
      "SignRequest": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "subject": {
            "type": "string",
            "example": "Please sign the NDA"
          },
          "message": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "pending",
              "completed",
              "cancelled",
              "expired"
            ]
          },
          "signing_order_type": {
            "type": "string",
            "enum": [
              "parallel",
              "sequential"
            ]
          },
          "extended_retention": {
            "type": "boolean",
            "description": "True if your organization's commercial contract includes the extended archiving option (10 years). Informational only — reflects the billed option, not a distinct technical purge policy."
          },
          "progress": {
            "type": "object",
            "properties": {
              "signed": {
                "type": "integer"
              },
              "total": {
                "type": "integer"
              }
            }
          },
          "redirect_url": {
            "type": "string",
            "format": "uri",
            "nullable": true,
            "example": "https://app.your-company.com/signed",
            "description": "URL shown to the signer as an explicit \"return to <domain>\" button on the success page. Must use https and match the domain declared for your organization (contact support to register it)."
          },
          "payment": {
            "type": "object",
            "nullable": true,
            "description": "Present only when a payment was requested on this sign request (see the `payment` field on creation). The signer is invited to pay only after signing (\"sign then pay\") — `checkout_url` is null until the sign request is fully signed.",
            "properties": {
              "status": {
                "type": "string",
                "enum": [
                  "pending",
                  "processing",
                  "paid",
                  "failed",
                  "canceled",
                  "expired"
                ]
              },
              "amount_cents": {
                "type": "integer",
                "example": 5000
              },
              "currency": {
                "type": "string",
                "example": "eur"
              },
              "description": {
                "type": "string",
                "nullable": true
              },
              "checkout_url": {
                "type": "string",
                "format": "uri",
                "nullable": true,
                "description": "Stripe Checkout link, only present while status is processing (i.e. the sign request has been fully signed and the signer has not yet paid)."
              },
              "paid_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              }
            }
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "sent_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "completed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "document": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "title": {
                "type": "string"
              },
              "page_count": {
                "type": "integer"
              }
            }
          },
          "created_by": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "name": {
                "type": "string"
              }
            }
          },
          "signers": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid"
                },
                "name": {
                  "type": "string"
                },
                "email": {
                  "type": "string",
                  "format": "email"
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "pending",
                    "notified",
                    "signed",
                    "declined"
                  ]
                }
              }
            }
          }
        }
      },
      "SignRequestDetailed": {
        "allOf": [
          {
            "$ref": "#/components/schemas/SignRequest"
          },
          {
            "type": "object",
            "properties": {
              "signers": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "name": {
                      "type": "string"
                    },
                    "email": {
                      "type": "string",
                      "format": "email"
                    },
                    "otp_channel": {
                      "type": "string",
                      "enum": [
                        "sms",
                        "email"
                      ]
                    },
                    "status": {
                      "type": "string"
                    },
                    "signing_order": {
                      "type": "integer"
                    },
                    "signing_link": {
                      "type": "string",
                      "format": "uri"
                    },
                    "notified_at": {
                      "type": "string",
                      "format": "date-time",
                      "nullable": true
                    },
                    "viewed_at": {
                      "type": "string",
                      "format": "date-time",
                      "nullable": true
                    },
                    "signed_at": {
                      "type": "string",
                      "format": "date-time",
                      "nullable": true
                    },
                    "declined_at": {
                      "type": "string",
                      "format": "date-time",
                      "nullable": true
                    },
                    "decline_reason": {
                      "type": "string",
                      "nullable": true
                    },
                    "zones": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SignatureZone"
                      }
                    }
                  }
                }
              },
              "downloads": {
                "type": "object",
                "properties": {
                  "signed_pdf": {
                    "type": "string",
                    "format": "uri",
                    "nullable": true
                  },
                  "proof": {
                    "type": "string",
                    "format": "uri",
                    "nullable": true
                  }
                }
              }
            }
          }
        ]
      },
      "CreateSignRequestInput": {
        "type": "object",
        "required": [
          "document_id",
          "subject",
          "signing_order_type",
          "expiry_hours",
          "signers"
        ],
        "properties": {
          "document_id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of a previously uploaded document"
          },
          "subject": {
            "type": "string",
            "maxLength": 255,
            "example": "Please sign the NDA",
            "description": "Email subject line for signers"
          },
          "message": {
            "type": "string",
            "maxLength": 2000,
            "nullable": true,
            "description": "Optional message included in the invitation email"
          },
          "metadata": {
            "type": "object",
            "nullable": true,
            "description": "Arbitrary key-value metadata for your records"
          },
          "redirect_url": {
            "type": "string",
            "format": "uri",
            "nullable": true,
            "maxLength": 2048,
            "example": "https://app.your-company.com/signed",
            "description": "Must use https and match the domain declared for your organization, or the request is rejected with a 422. Contact support to register your domain."
          },
          "payment": {
            "type": "object",
            "nullable": true,
            "description": "Request a payment from the signer once they've signed (\"sign then pay\" — the signature is never blocked by payment). Requires an active Stripe Connect account for your organization (see Settings > Paiement à la signature), or the request is rejected with a 422. JuriSign never holds the funds: they go directly to your own Stripe account, minus a fixed 0.90€ platform fee.",
            "required": [
              "amount_cents"
            ],
            "properties": {
              "amount_cents": {
                "type": "integer",
                "minimum": 50,
                "example": 5000,
                "description": "Amount to collect from the signer, in cents."
              },
              "currency": {
                "type": "string",
                "default": "eur",
                "example": "eur"
              },
              "description": {
                "type": "string",
                "maxLength": 255,
                "nullable": true,
                "example": "Acompte devis n°2026-042"
              }
            }
          },
          "signing_order_type": {
            "type": "integer",
            "enum": [
              0,
              1
            ],
            "description": "0 = parallel (all signers at once), 1 = sequential (one after another)"
          },
          "expiry_hours": {
            "type": "integer",
            "minimum": 1,
            "maximum": 720,
            "example": 72,
            "description": "Hours until the request expires (max 30 days)"
          },
          "signers": {
            "type": "array",
            "minItems": 1,
            "maxItems": 10,
            "items": {
              "type": "object",
              "required": [
                "prenom",
                "nom",
                "email",
                "otp_channel"
              ],
              "properties": {
                "prenom": {
                  "type": "string",
                  "maxLength": 255,
                  "description": "First name"
                },
                "nom": {
                  "type": "string",
                  "maxLength": 255,
                  "description": "Last name"
                },
                "email": {
                  "type": "string",
                  "format": "email",
                  "description": "Email address"
                },
                "otp_channel": {
                  "type": "string",
                  "enum": [
                    "sms",
                    "email"
                  ],
                  "description": "OTP verification channel"
                },
                "telephone": {
                  "type": "string",
                  "maxLength": 20,
                  "description": "Required when otp_channel is 'sms'"
                }
              }
            }
          },
          "zones": {
            "type": "array",
            "nullable": true,
            "description": "Signature zone positions. Auto-generated at bottom of last page if omitted.",
            "items": {
              "type": "object",
              "required": [
                "signer_index",
                "page",
                "x",
                "y",
                "width",
                "height"
              ],
              "properties": {
                "signer_index": {
                  "type": "integer",
                  "minimum": 0,
                  "description": "Index in the signers array (0-based)"
                },
                "page": {
                  "type": "integer",
                  "minimum": 1,
                  "description": "Page number"
                },
                "x": {
                  "type": "number",
                  "minimum": 0,
                  "maximum": 100,
                  "description": "X position (% from left)"
                },
                "y": {
                  "type": "number",
                  "minimum": 0,
                  "maximum": 100,
                  "description": "Y position (% from top)"
                },
                "width": {
                  "type": "number",
                  "minimum": 1,
                  "maximum": 100,
                  "description": "Width (% of page)"
                },
                "height": {
                  "type": "number",
                  "minimum": 1,
                  "maximum": 100,
                  "description": "Height (% of page)"
                },
                "type": {
                  "type": "string",
                  "enum": [
                    "signature",
                    "text",
                    "date",
                    "checkbox"
                  ],
                  "default": "signature",
                  "description": "Field type. 'date' is always auto-filled at signing time, never editable by the signer."
                },
                "label": {
                  "type": "string",
                  "maxLength": 255,
                  "description": "Instruction shown to the signer. Required when type is 'text' or 'checkbox'."
                },
                "required": {
                  "type": "boolean",
                  "default": true,
                  "description": "For 'text'/'checkbox' zones only: blocks signing until filled. Ignored for 'signature'/'date'."
                }
              }
            }
          },
          "require_selfie": {
            "type": "boolean",
            "default": false,
            "description": "Require signers to take a selfie for identity verification"
          }
        }
      },
      "SignatureZone": {
        "type": "object",
        "properties": {
          "page": {
            "type": "integer"
          },
          "x": {
            "type": "number"
          },
          "y": {
            "type": "number"
          },
          "width": {
            "type": "number"
          },
          "height": {
            "type": "number"
          },
          "type": {
            "type": "string",
            "enum": [
              "signature",
              "text",
              "date",
              "checkbox"
            ],
            "default": "signature"
          },
          "label": {
            "type": "string",
            "nullable": true,
            "description": "Instruction shown to the signer for text/checkbox fields"
          },
          "required": {
            "type": "boolean",
            "default": true,
            "description": "Whether a text/checkbox field must be filled before the signer can sign"
          },
          "value": {
            "type": "string",
            "nullable": true,
            "description": "Value entered by the signer (text/checkbox) or auto-filled date. Populated only once the sign request is signed."
          }
        }
      },
      "Template": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "subject": {
            "type": "string"
          },
          "signing_order_type": {
            "type": "string",
            "enum": [
              "parallel",
              "sequential"
            ]
          },
          "expiry_hours": {
            "type": "integer"
          },
          "require_selfie": {
            "type": "boolean"
          },
          "requires_paraph": {
            "type": "boolean"
          },
          "signer_count": {
            "type": "integer"
          },
          "has_document": {
            "type": "boolean"
          },
          "usage_count": {
            "type": "integer"
          },
          "last_used_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_by": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "name": {
                "type": "string"
              }
            }
          }
        }
      },
      "TemplateDetailed": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Template"
          },
          {
            "type": "object",
            "properties": {
              "message": {
                "type": "string",
                "nullable": true
              },
              "signers": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "prenom": {
                      "type": "string"
                    },
                    "nom": {
                      "type": "string"
                    },
                    "email": {
                      "type": "string"
                    },
                    "telephone": {
                      "type": "string",
                      "nullable": true
                    },
                    "otp_channel": {
                      "type": "string",
                      "enum": [
                        "sms",
                        "email"
                      ]
                    }
                  }
                }
              },
              "zones": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/SignatureZone"
                }
              },
              "document": {
                "type": "object",
                "nullable": true,
                "properties": {
                  "title": {
                    "type": "string"
                  },
                  "original_filename": {
                    "type": "string"
                  },
                  "page_count": {
                    "type": "integer"
                  }
                }
              }
            }
          }
        ]
      },
      "BulkTemplate": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "merge_fields": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "example": [
              "nom",
              "prenom",
              "lot"
            ]
          },
          "html_content": {
            "type": "string",
            "description": "Only present on show/create/update responses"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_by": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "name": {
                "type": "string"
              }
            }
          }
        }
      },
      "PublicForm": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "paused",
              "expired"
            ]
          },
          "public_url": {
            "type": "string",
            "format": "uri",
            "description": "The shareable link to publish",
            "example": "https://www.jurisign.fr/formulaires/AbCd…"
          },
          "template": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "name": {
                "type": "string"
              }
            }
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "daily_submission_limit": {
            "type": "integer",
            "nullable": true
          },
          "submissions_count": {
            "type": "integer"
          },
          "last_submitted_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "BulkCampaign": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "processing",
              "sending",
              "completed",
              "partially_sent",
              "failed",
              "cancelled"
            ]
          },
          "template": {
            "type": "object",
            "nullable": true,
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "name": {
                "type": "string"
              }
            }
          },
          "progress": {
            "type": "integer",
            "description": "Percentage 0-100"
          },
          "stats": {
            "type": "object",
            "properties": {
              "total": {
                "type": "integer"
              },
              "processed": {
                "type": "integer"
              },
              "sent": {
                "type": "integer"
              },
              "signed": {
                "type": "integer"
              },
              "errors": {
                "type": "integer"
              },
              "pending": {
                "type": "integer"
              }
            }
          },
          "started_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "completed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "cancelled_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_by": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "name": {
                "type": "string"
              }
            }
          }
        }
      },
      "WebhookEndpoint": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "secret": {
            "type": "string",
            "description": "Full secret only shown on creation"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "is_active": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "stats": {
            "type": "object",
            "properties": {
              "success": {
                "type": "integer"
              },
              "failed": {
                "type": "integer"
              },
              "pending": {
                "type": "integer"
              }
            }
          }
        }
      },
      "WebhookLog": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "event": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "success",
              "pending",
              "failed"
            ]
          },
          "response_status": {
            "type": "integer",
            "nullable": true
          },
          "attempts": {
            "type": "integer"
          },
          "next_retry_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Pagination": {
        "type": "object",
        "properties": {
          "current_page": {
            "type": "integer"
          },
          "last_page": {
            "type": "integer"
          },
          "per_page": {
            "type": "integer"
          },
          "total": {
            "type": "integer"
          }
        }
      },
      "MessageResponse": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string"
          }
        }
      }
    },
    "responses": {
      "Unauthenticated": {
        "description": "Missing or invalid Bearer token",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "message": {
                  "type": "string",
                  "example": "Unauthenticated."
                }
              }
            }
          }
        }
      },
      "Forbidden": {
        "description": "Insufficient permissions",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "message": {
                  "type": "string",
                  "example": "Forbidden."
                }
              }
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "message": {
                  "type": "string",
                  "example": "Not found."
                }
              }
            }
          }
        }
      },
      "Error": {
        "description": "Error response",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "message": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "ValidationError": {
        "description": "Validation error",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "message": {
                  "type": "string",
                  "example": "The given data was invalid."
                },
                "errors": {
                  "type": "object",
                  "additionalProperties": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}
