{"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. Signature des webhooks. Chaque livraison porte l'en-tete X-JuriSign-Signature, dont la valeur est le prefixe litteral \"sha256=\" suivi de HMAC-SHA256 en hexadecimal minuscule, calcule avec le secret de l'endpoint sur le corps HTTP EXACTEMENT tel qu'il est transmis. Verifiez toujours la signature sur le corps brut : re-serialiser le JSON decode change les espaces ou l'ordre des cles et produit une signature differente. Comparez en temps constant (hash_equals). Trois autres en-tetes accompagnent la livraison : X-JuriSign-Event (le type d'evenement), X-JuriSign-Timestamp (epoch en secondes, hors signature) et X-JuriSign-Delivery (identifiant unique de la livraison, utile pour l'idempotence en cas de reprise). L'agent est JuriSign-Webhook/1.0. Exemple PHP : hash_equals('sha256='.hash_hmac('sha256', $corpsBrut, $secret), $_SERVER['HTTP_X_JURISIGN_SIGNATURE']). Verification d'identite du signataire (option) : evenements signer.identity_verified (identite verifiee) et signer.identity_failed (refusee, abandonnee ou expiree) ; leur charge utile reprend data.signer.identity_verification (status, attempts_remaining, name_match, document_type, document_country, decided_at). Expiration : l'evenement sign_request.expired est envoye quand une demande expire avant que tous les signataires aient signe ; les signatures deja recueillies restent facturees, les autres sont rendues.","version":"3.46.10","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. Les livraisons sont signees : voir la description de l'API pour le format de X-JuriSign-Signature."}],"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. To complete a sandbox signature, the test signer opens the signing link and enters the fixed sandbox OTP code 123456 (displayed on the signing page); the signed PDF and its proof file can then be downloaded as in production. 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. When the identity verification option is available to your organization, `identity_credits` gives the balance of identity verification credits.","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},"identity_credits":{"type":"object","description":"Present only when the identity verification option is available to your organization. One credit is consumed per signer whose verification succeeds; credits are bought in Settings > Billing.","properties":{"balance":{"type":"integer","example":20,"description":"Credits owned."},"reserved":{"type":"integer","example":3,"description":"Signers of pending requests with the option whose identity is not verified yet."},"available":{"type":"integer","example":17,"description":"balance - reserved: what a new request can use. Creating a request with the option is refused with 422 when it does not cover every signer."}}}}}}}}}},"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"}}},"post":{"summary":"Create a template","description":"Creates a reusable request template: subject, message, options, signers (which may be left to complete at use) and zones, optionally with its own copy of a PDF. Use it with POST /templates/{id}/sign-requests. Requires the `sign-requests:write` scope.","operationId":"createTemplate","tags":["Templates"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":255},"description":{"type":"string","maxLength":1000,"nullable":true},"subject":{"type":"string","maxLength":255},"message":{"type":"string","maxLength":2000,"nullable":true},"signing_order_type":{"type":"integer","enum":[0,1],"description":"0 = parallel, 1 = sequential."},"expiry_hours":{"type":"integer","minimum":1,"maximum":720},"require_selfie":{"type":"boolean"},"requires_paraph":{"type":"boolean"},"require_geolocation":{"type":"boolean"},"document_id":{"type":"string","format":"uuid","nullable":true,"description":"A document of your organization (POST /documents): the template keeps its own copy of the PDF. Without it, each use provides its own document."},"signers":{"type":"array","minItems":1,"maxItems":10,"items":{"type":"object","properties":{"prenom":{"type":"string","maxLength":255,"description":"Optional: leave empty to provide the signer when the template is used."},"nom":{"type":"string","maxLength":255},"email":{"type":"string","format":"email","description":"Optional: a template whose signers have no email must receive `signers` on POST /templates/{id}/sign-requests."},"otp_channel":{"type":"string","enum":["sms","email"],"default":"email"},"telephone":{"type":"string","maxLength":20},"attachments_requested":{"type":"array","maxItems":5,"description":"Documents requested from this signer (labels). In a template they are always required.","items":{"oneOf":[{"type":"string"},{"type":"object","properties":{"label":{"type":"string"}}}]}},"identity_verification":{"type":"boolean","nullable":true,"description":"Per-signer identity verification (when `identity_verification` is enabled on the request or template). `false` exempts this signer (e.g. the sender signing their own document): no Didit check, no credit reserved, and the proof file says so. Omitted or `true`: verified. If no signer is left to verify, the option is not applied."}}},"description":"Replaced as a whole on update."},"zones":{"type":"array","minItems":1,"items":{"type":"object","required":["signer_index","page","x","y","width","height"],"properties":{"signer_index":{"type":"integer","minimum":0,"description":"Rank of the signer (0-based) in `signers`."},"page":{"type":"integer","minimum":1},"x":{"type":"number","minimum":0,"maximum":100,"description":"Percent of the page width."},"y":{"type":"number","minimum":0,"maximum":100,"description":"Percent of the page height."},"width":{"type":"number","minimum":1,"maximum":100},"height":{"type":"number","minimum":1,"maximum":100},"type":{"type":"string","enum":["signature","text","date","checkbox"],"default":"signature"},"label":{"type":"string","maxLength":255,"description":"Required for `text` and `checkbox`."},"required":{"type":"boolean","default":true}}},"description":"Every signer needs at least one `signature` zone. Replaced as a whole on update."},"identity_verification":{"type":"boolean","default":false,"description":"Signer identity verification required by the template (paid option, same rules as on POST /sign-requests). Inherited by requests created from the template (the call can override it with its own `identity_verification`) and by public forms built on it: each respondent verifies their identity, one identity credit is used per successful verification, and a submission is refused when the publishing organization has no available credit. The signature remains a simple electronic signature (SES), neither advanced nor qualified."}},"required":["name","subject","signers","zones"]}}}},"responses":{"201":{"description":"Template created","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/TemplateDetailed"}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"422":{"$ref":"#/components/responses/ValidationError"}}}},"/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"}}},"put":{"summary":"Update a template","description":"Partial update: only the fields sent change; `signers` and `zones` are replaced as a whole. Requires the `sign-requests:write` scope.","operationId":"updateTemplate","tags":["Templates"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":255},"description":{"type":"string","maxLength":1000,"nullable":true},"subject":{"type":"string","maxLength":255},"message":{"type":"string","maxLength":2000,"nullable":true},"signing_order_type":{"type":"integer","enum":[0,1],"description":"0 = parallel, 1 = sequential."},"expiry_hours":{"type":"integer","minimum":1,"maximum":720},"require_selfie":{"type":"boolean"},"requires_paraph":{"type":"boolean"},"require_geolocation":{"type":"boolean"},"document_id":{"type":"string","format":"uuid","nullable":true,"description":"A document of your organization (POST /documents): the template keeps its own copy of the PDF. Without it, each use provides its own document. `null` removes the template's copy."},"signers":{"type":"array","minItems":1,"maxItems":10,"items":{"type":"object","properties":{"prenom":{"type":"string","maxLength":255,"description":"Optional: leave empty to provide the signer when the template is used."},"nom":{"type":"string","maxLength":255},"email":{"type":"string","format":"email","description":"Optional: a template whose signers have no email must receive `signers` on POST /templates/{id}/sign-requests."},"otp_channel":{"type":"string","enum":["sms","email"],"default":"email"},"telephone":{"type":"string","maxLength":20},"attachments_requested":{"type":"array","maxItems":5,"description":"Documents requested from this signer (labels). In a template they are always required.","items":{"oneOf":[{"type":"string"},{"type":"object","properties":{"label":{"type":"string"}}}]}},"identity_verification":{"type":"boolean","nullable":true,"description":"Per-signer identity verification (when `identity_verification` is enabled on the request or template). `false` exempts this signer (e.g. the sender signing their own document): no Didit check, no credit reserved, and the proof file says so. Omitted or `true`: verified. If no signer is left to verify, the option is not applied."}}},"description":"Replaced as a whole on update."},"zones":{"type":"array","minItems":1,"items":{"type":"object","required":["signer_index","page","x","y","width","height"],"properties":{"signer_index":{"type":"integer","minimum":0,"description":"Rank of the signer (0-based) in `signers`."},"page":{"type":"integer","minimum":1},"x":{"type":"number","minimum":0,"maximum":100,"description":"Percent of the page width."},"y":{"type":"number","minimum":0,"maximum":100,"description":"Percent of the page height."},"width":{"type":"number","minimum":1,"maximum":100},"height":{"type":"number","minimum":1,"maximum":100},"type":{"type":"string","enum":["signature","text","date","checkbox"],"default":"signature"},"label":{"type":"string","maxLength":255,"description":"Required for `text` and `checkbox`."},"required":{"type":"boolean","default":true}}},"description":"Every signer needs at least one `signature` zone. Replaced as a whole on update."},"identity_verification":{"type":"boolean","default":false,"description":"Signer identity verification required by the template (paid option, same rules as on POST /sign-requests). Inherited by requests created from the template (the call can override it with its own `identity_verification`) and by public forms built on it: each respondent verifies their identity, one identity credit is used per successful verification, and a submission is refused when the publishing organization has no available credit. The signature remains a simple electronic signature (SES), neither advanced nor qualified."}}}}}},"responses":{"200":{"description":"Template details","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/TemplateDetailed"}}}}}},"404":{"$ref":"#/components/responses/NotFound"},"401":{"$ref":"#/components/responses/Unauthenticated"},"422":{"$ref":"#/components/responses/ValidationError"}}}},"/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.\n\n**Since 3.30.0** — two optional parameters:\n- `signers`: the actual recipients, replacing the template's placeholders. Same count and order as the template (422 otherwise). The template's phone number is never inherited: provide `telephone` for an SMS signer.\n- `fields`: prefill text and checkbox zones, designated by `label` or by `zone_index` (position in the template's `zones`, 0-based). With `read_only: true` the signer sees the value but cannot change it — enforced server-side. Date and signature zones cannot be prefilled. The prefilled values are recorded in the audit trail (event `sign_request.fields_prefilled`).","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"},"signers":{"type":"array","minItems":1,"maxItems":10,"description":"Actual recipients, in the template's order. Omit to keep the template's signers.","items":{"type":"object","required":["prenom","nom","email"],"properties":{"prenom":{"type":"string","maxLength":100},"nom":{"type":"string","maxLength":100},"email":{"type":"string","format":"email"},"telephone":{"type":"string","maxLength":20,"nullable":true,"description":"Required if otp_channel is sms. Never inherited from the template."},"otp_channel":{"type":"string","enum":["email","sms"],"description":"Defaults to the template's channel."},"identity_verification":{"type":"boolean","nullable":true,"description":"Per-signer identity verification (when `identity_verification` is enabled on the request or template). `false` exempts this signer (e.g. the sender signing their own document): no Didit check, no credit reserved, and the proof file says so. Omitted or `true`: verified. If no signer is left to verify, the option is not applied."},"birth_name":{"type":"string","nullable":true,"maxLength":100,"description":"Birth name of this signer, if it is the name shown on their ID document (e.g. maiden name). Used only with identity verification: the name check then accepts either the last name or the birth name (first name always required). Returned as `signers[].birth_name`; mentioned in the proof file."}}}},"fields":{"type":"array","maxItems":100,"description":"Values for text and checkbox zones. Each item targets one zone, by label (must be unique among text/checkbox zones) or by zone_index.","items":{"type":"object","required":["value"],"properties":{"label":{"type":"string"},"zone_index":{"type":"integer","minimum":0},"value":{"description":"Text: non-empty string, max 1000 characters. Checkbox: true/false.","oneOf":[{"type":"string"},{"type":"boolean"}]},"read_only":{"type":"boolean","default":false,"description":"The signer cannot change the value. A required checkbox cannot be locked unchecked."}}},"example":[{"label":"Montant","value":"1 250,00 EUR","read_only":true},{"zone_index":3,"value":true}]},"reminders":{"type":"boolean","default":true,"description":"Automatic e-mail reminders to signers (D+1, D+3, D+5 and the day before expiry). Set to false to disable them for this request, e.g. when your system sends its own reminders (see POST /sign-requests/{id}/remind)."},"sender_name":{"type":"string","maxLength":100,"nullable":true,"description":"Name shown to signers as the sender for this request, e.g. your own customer when you sign on their behalf. Displayed as \"<sender_name>, via <your organization name>\" in the invitation e-mail and on the signing page, and recorded in the evidence file. No URL or HTML. Omit it to keep the default sender name."},"identity_verification":{"type":"boolean","default":false,"description":"Paid option \"Signer identity verification\", same rules as on `POST /sign-requests`: ignored when the option is not available to your organization; outside the sandbox, refused with **422** (`errors.identity_verification`) when your available identity credits do not cover every signer. The signature remains a simple electronic signature (SES): the verification strengthens the evidence file, it is neither an advanced nor a qualified signature. When omitted, the template's own `identity_verification` setting applies."}}}}}},"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."}},"identity_verification":{"type":"boolean","default":false,"description":"Paid option \"Signer identity verification\", same rules as on `POST /sign-requests`: ignored when the option is not available to your organization; outside the sandbox, refused with **422** (`errors.identity_verification`) when your available identity credits do not cover every recipient (one signer each). The signature remains a simple electronic signature (SES): the verification strengthens the evidence file, it is neither an advanced nor a qualified signature."},"sender_name":{"type":"string","maxLength":100,"nullable":true,"description":"Name shown to signers as the sender for this request, e.g. your own customer when you sign on their behalf. Displayed as \"<sender_name>, via <your organization name>\" in the invitation e-mail and on the signing page, and recorded in the evidence file. No URL or HTML. Omit it to keep the default sender name. Applied to every request of the campaign."},"reminders":{"type":"boolean","default":true,"description":"Automatic e-mail reminders to signers (D+1, D+3, D+5 and the day before expiry). Set to false to disable them for this request, e.g. when your system sends its own reminders (see POST /sign-requests/{id}/remind). Applied to every request of the campaign."},"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. Why a declared domain: the signer trusts the jurisign.fr link; accepting any URL would let anyone bounce your signers to a lookalike phishing site (open redirect). Your subdomains are accepted automatically; do not declare an IP, localhost or a shared hosting domain. Omit redirect_url while testing. See https://www.jurisign.fr/api/guide#redirection Applied to every request of the campaign."},"requires_paraph":{"type":"boolean","default":false,"description":"Initials on every page: each signer draws initials distinct from the signature, stamped at the bottom of every page (the full signature goes in the signature zones). Not restricted by plan on the API. Applied to every request of the campaign."},"require_geolocation":{"type":"boolean","default":false,"description":"Record where each signer signs: the position declared by the signer's device (with its accuracy) and a fingerprint of the device, written to the proof (the signer consents first). Never rejected: included from the Professionnel plan; otherwise billed 1.25 EUR excl. VAT per request on the monthly invoice (or one prepaid 'lieu de signature' credit is used). Never billed in the sandbox. Applied to every request of the campaign."}}}}}},"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"},"otp_channel":{"type":"string","enum":["sms","email"],"nullable":true,"description":"Verification channel imposed on every respondent. When absent, each signer chooses."}}}}}},"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},"otp_channel":{"type":"string","enum":["sms","email"],"nullable":true,"description":"Verification channel imposed on every respondent. When absent, each signer chooses."}}}}}},"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 size per file: 3 MB, 10 MB on the Professionnel and Entreprise plans (HTTP 422 beyond).","operationId":"uploadDocument","tags":["Documents"],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary","description":"Document to upload: PDF, JPG, PNG, WEBP, DOC or DOCX. Required unless `files[]` is sent. Maximum size depends on your plan (a 422 error states the limit)."},"files[]":{"type":"array","maxItems":10,"items":{"type":"string","format":"binary"},"description":"Several documents merged into a single PDF, in the order sent (PDF, JPG, PNG, WEBP, DOC, DOCX). Use instead of `file`. Not restricted by plan on the API."},"title":{"type":"string","maxLength":255,"description":"Document title (defaults to filename if omitted)"}},"description":"Send either `file` (one document) or `files[]` (2 to 10 documents merged into ONE PDF, in the order sent)."}}}},"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}/remind":{"post":{"summary":"Remind Signers","description":"Sends an e-mail reminder to the signers whose turn it is (all pending signers in parallel mode, the next one in sequential mode), or only to `signer_id`. The request must be sent (`pending` or `partially_signed`) and not expired. At most one reminder per signer per 24 hours (1 minute in the sandbox), automatic reminders included: otherwise 429 with a Retry-After header. In sandbox mode nothing is sent.","operationId":"remindSigners","tags":["Sign Requests"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"signer_id":{"type":"string","description":"UUID of a single signer to remind. Omit to remind every signer whose turn it is."}}}}}},"responses":{"200":{"description":"Signers reminded","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"},"data":{"type":"object","properties":{"reminded":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"reminded_at":{"type":"string","format":"date-time"}}}}}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Error"},"429":{"description":"A reminder was already sent to this signer less than 24 hours ago (1 minute in the sandbox; see Retry-After)."}}}},"/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","sign_request.expired","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","signer.identity_verified","signer.identity_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","sign_request.expired","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","signer.identity_verified","signer.identity_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","sign_request.expired","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","signer.identity_verified","signer.identity_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"}}}},"/sign-requests/{id}/attachments/{attachment_id}":{"get":{"summary":"Telecharger un document depose par un signataire","tags":["Sign Requests"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"attachment_id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Le fichier depose (PDF, JPG ou PNG)."},"404":{"description":"Document non depose ou introuvable."}}}}},"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","partially_signed","pending_approval"],"description":"`draft`: created, not sent. `pending`: sent, waiting for signatures. `partially_signed`: at least one signer has signed, others have not yet. `pending_approval`: waiting for internal approval (web only — requests sent through the API are never held for approval). `completed`: everyone signed. `cancelled`: cancelled by the sender. `expired`: expired before every signer signed (webhook `sign_request.expired`)."},"signing_order_type":{"type":"string","enum":["parallel","sequential"]},"requires_paraph":{"type":"boolean","description":"Initials required on every page."},"require_selfie":{"type":"boolean","description":"A photo of each signer is required."},"require_geolocation":{"type":"boolean","description":"true if the signer's location will be recorded for this request."},"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). Why a declared domain: the signer trusts the jurisign.fr link; accepting any URL would let anyone bounce your signers to a lookalike phishing site (open redirect). Your subdomains are accepted automatically; do not declare an IP, localhost or a shared hosting domain. Omit redirect_url while testing. See https://www.jurisign.fr/api/guide#redirection"},"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"]}}}},"identity_verification":{"type":"boolean","description":"Returned only when the identity verification option is enabled for your organization, or was used on this request: `true` if the option was requested."},"reminders":{"type":"boolean","description":"Whether automatic e-mail reminders are enabled for this request."},"sender_name":{"type":"string","nullable":true,"description":"Name shown to signers as the sender for this request (see sender_name on creation), or null."},"metadata":{"type":"object","nullable":true,"additionalProperties":true,"description":"The `metadata` object you sent when creating the request, returned as is (same content as in webhook payloads). JuriSign adds `source` (`api`) and, for requests created from a template, `template_id`.","example":{"ref":"D-2026-042","source":"api"}}}},"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"}},"attachments":{"type":"array","description":"Present uniquement si des documents ont ete demandes a ce signataire.","items":{"type":"object","properties":{"id":{"type":"integer"},"label":{"type":"string"},"required":{"type":"boolean"},"uploaded_at":{"type":"string","format":"date-time","nullable":true},"filename":{"type":"string","nullable":true},"size":{"type":"integer","nullable":true},"sha256":{"type":"string","nullable":true},"download_url":{"type":"string","nullable":true,"description":"GET /sign-requests/{id}/attachments/{attachment_id} (scope sign-requests:read)"}}}},"identity_verification":{"type":"object","description":"Present only on requests created with `identity_verification: true`. Result of the identity verification of this signer. Never contains images, the document number or raw scores.","properties":{"status":{"type":"string","enum":["not_required","not_started","pending","approved","declined","in_review","abandoned","expired"],"description":"`not_required`: this signer was exempted by the sender (`signers[].identity_verification: false`). `not_started`: the signer has not started yet. `pending`: in progress. `approved`: identity verified, the signer can sign. `declined`: document, liveness or face match refused, or name mismatch. `in_review`: under manual review by the provider (no webhook until a decision). `abandoned` / `expired`: the session was left unfinished. A signer can retry while `attempts_remaining` > 0."},"attempts_remaining":{"type":"integer","example":2,"description":"Verification attempts left for this signer (3 at most)."},"name_match":{"type":"boolean","nullable":true,"description":"Whether the name read on the identity document matches the `prenom`/`nom` you provided."},"document_type":{"type":"string","nullable":true,"example":"Identity Card"},"document_country":{"type":"string","nullable":true,"example":"FRA","description":"Issuing country (ISO 3166-1 alpha-3)."},"decided_at":{"type":"string","format":"date-time","nullable":true}}},"evidence":{"type":"object","description":"Present only when the request uses selfie, initials or signing location. Same elements as in the evidence file (PDF); never the selfie image nor raw device details.","properties":{"selfie":{"type":"object","description":"When `require_selfie` is true.","properties":{"taken":{"type":"boolean"},"consent_at":{"type":"string","format":"date-time","nullable":true},"sha256":{"type":"string","nullable":true,"description":"Fingerprint of the photo kept in the evidence file."}}},"initials":{"type":"object","description":"When `requires_paraph` is true.","properties":{"done":{"type":"boolean"},"consent_at":{"type":"string","format":"date-time","nullable":true}}},"location":{"type":"object","description":"When `require_geolocation` is true.","properties":{"source":{"type":"string","nullable":true,"description":"`device`: position declared by the signer's device, with consent; `none`: no position (refused, unavailable or unsupported — see `unavailable_reason`)."},"latitude":{"type":"number","nullable":true},"longitude":{"type":"number","nullable":true},"accuracy_m":{"type":"integer","nullable":true},"unavailable_reason":{"type":"string","nullable":true},"consent_at":{"type":"string","format":"date-time","nullable":true},"device_fingerprint":{"type":"string","nullable":true}}}}}}}},"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. Why a declared domain: the signer trusts the jurisign.fr link; accepting any URL would let anyone bounce your signers to a lookalike phishing site (open redirect). Your subdomains are accepted automatically; do not declare an IP, localhost or a shared hosting domain. Omit redirect_url while testing. See https://www.jurisign.fr/api/guide#redirection"},"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'"},"attachments_requested":{"type":"array","maxItems":5,"description":"Documents que ce signataire doit deposer avant de signer (RIB, piece d'identite...). Chaque element est un libelle (obligatoire) ou un objet {label, required}. Formats acceptes cote signataire : PDF, JPG, PNG, 10 Mo au plus.","items":{"oneOf":[{"type":"string","maxLength":100},{"type":"object","required":["label"],"properties":{"label":{"type":"string","maxLength":100},"required":{"type":"boolean","default":true}}}]},"example":["RIB",{"label":"Justificatif de domicile","required":false}]},"identity_verification":{"type":"boolean","nullable":true,"description":"Per-signer identity verification (when `identity_verification` is enabled on the request or template). `false` exempts this signer (e.g. the sender signing their own document): no Didit check, no credit reserved, and the proof file says so. Omitted or `true`: verified. If no signer is left to verify, the option is not applied."},"birth_name":{"type":"string","nullable":true,"maxLength":100,"description":"Birth name of this signer, if it is the name shown on their ID document (e.g. maiden name). Used only with identity verification: the name check then accepts either the last name or the birth name (first name always required). Returned as `signers[].birth_name`; mentioned in the proof file."}}}},"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"},"require_geolocation":{"type":"boolean","default":false,"description":"Record where each signer signs: the position declared by the signer's device (with its accuracy) and a fingerprint of the device, written to the proof (the signer consents first). Never rejected: included from the Professionnel plan; otherwise billed 1.25 EUR excl. VAT per request on the monthly invoice (or one prepaid 'lieu de signature' credit is used). Never billed in the sandbox."},"requires_paraph":{"type":"boolean","default":false,"description":"Initials on every page: each signer draws initials distinct from the signature, stamped at the bottom of every page (the full signature goes in the signature zones). Not restricted by plan on the API."},"auto_send":{"type":"boolean","default":false,"description":"Send the invitations immediately after creation, so that a single call replaces POST /sign-requests followed by POST /sign-requests/{id}/send."},"identity_verification":{"type":"boolean","default":false,"description":"Paid option \"Signer identity verification\". Before signing, each signer of the request verifies their identity: photo of an identity document, liveness check, face match against the document photo, and match between the name on the document and the `prenom`/`nom` you provided. The check is performed by our provider Didit; only the result (status, document type and country, masked number, scores) is kept in the evidence file. The signature remains a simple electronic signature (SES): the verification strengthens the evidence file, it is neither an advanced nor a qualified signature. The per-signer result is returned in `signers[].identity_verification` (GET /sign-requests/{id}), and the webhooks `signer.identity_verified` / `signer.identity_failed` notify each decision; the balance of identity credits is in `GET /account/credits`.\n\nAvailable once the option is enabled for your organization; otherwise the field is ignored and the request is created without it.\n\nBilling: one prepaid credit per signer whose verification succeeds (credits are bought in Settings > Billing). A signer who fails or never completes the check consumes no credit. Creation is refused with **422** (`errors.identity_verification`) when your available credits (balance minus signers already reserved by pending requests) do not cover every signer.\n\nSandbox: the verification is simulated, and no credit is needed or consumed.","example":true},"reminders":{"type":"boolean","default":true,"description":"Automatic e-mail reminders to signers (D+1, D+3, D+5 and the day before expiry). Set to false to disable them for this request, e.g. when your system sends its own reminders (see POST /sign-requests/{id}/remind)."},"sender_name":{"type":"string","maxLength":100,"nullable":true,"description":"Name shown to signers as the sender for this request, e.g. your own customer when you sign on their behalf. Displayed as \"<sender_name>, via <your organization name>\" in the invitation e-mail and on the signing page, and recorded in the evidence file. No URL or HTML. Omit it to keep the default sender name."}}},"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"},"require_geolocation":{"type":"boolean","description":"Les demandes créées depuis ce modèle consignent la position et l'appareil du signataire (avec son accord), si l'option est active sur l'organisation."},"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"}}},"identity_verification":{"type":"boolean","description":"Present when the identity verification option is available to your organization, or set on the template."}}},"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"]},"identity_verification":{"type":"boolean","nullable":true,"description":"Per-signer identity verification (when `identity_verification` is enabled on the request or template). `false` exempts this signer (e.g. the sender signing their own document): no Didit check, no credit reserved, and the proof file says so. Omitted or `true`: verified. If no signer is left to verify, the option is not applied."}}}},"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"},"otp_channel":{"type":"string","enum":["sms","email"],"nullable":true,"description":"Verification channel imposed on every respondent. When absent, each signer chooses."}}},"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"}}}}}}}}}}}