{"openapi":"3.0.0","paths":{"/v1/health":{"get":{"description":"Returns the API health status and current timestamp","operationId":"Health_check","parameters":[],"responses":{"200":{"description":"API is healthy and operational","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"ok","description":"Health status"},"timestamp":{"type":"string","format":"date-time","description":"Current server timestamp in ISO format"}}}}}}},"summary":"Health check","tags":["Health"]}},"/v1/workspaces":{"get":{"description":"Retrieves all workspaces the user has access to. Automatically provisions personal workspace if needed.","operationId":"Workspaces_list","parameters":[],"responses":{"200":{"description":"List of workspaces retrieved successfully"},"401":{"description":"Unauthorized - Invalid or missing JWT token"}},"security":[{"clerk-jwt":[]}],"summary":"List workspaces","tags":["Workspaces"]},"post":{"description":"Creates a team workspace for collaborative document management","operationId":"Workspaces_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWorkspaceDto"}}}},"responses":{"201":{"description":"Workspace created successfully"},"400":{"description":"Invalid input data"},"401":{"description":"Unauthorized - Invalid or missing JWT token"}},"security":[{"clerk-jwt":[]}],"summary":"Create a new workspace","tags":["Workspaces"]}},"/v1/workspaces/{id}":{"get":{"description":"Retrieves detailed information about a specific workspace","operationId":"Workspaces_findOne","parameters":[{"name":"id","required":true,"in":"path","description":"Workspace UUID","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"Workspace retrieved successfully"},"401":{"description":"Unauthorized - Invalid or missing JWT token"},"403":{"description":"Forbidden - Not a member of this workspace"},"404":{"description":"Workspace not found"}},"security":[{"clerk-jwt":[]}],"summary":"Get workspace by ID","tags":["Workspaces"]},"patch":{"description":"Updates workspace name (owner/admin only)","operationId":"Workspaces_update","parameters":[{"name":"id","required":true,"in":"path","description":"Workspace UUID","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateWorkspaceDto"}}}},"responses":{"200":{"description":"Workspace updated successfully"},"400":{"description":"Invalid input data"},"401":{"description":"Unauthorized - Invalid or missing JWT token"},"403":{"description":"Forbidden - Insufficient permissions"},"404":{"description":"Workspace not found"}},"security":[{"clerk-jwt":[]}],"summary":"Update workspace","tags":["Workspaces"]},"delete":{"description":"Permanently deletes a workspace (owner only)","operationId":"Workspaces_remove","parameters":[{"name":"id","required":true,"in":"path","description":"Workspace UUID","schema":{"format":"uuid","type":"string"}}],"responses":{"204":{"description":"Workspace deleted successfully"},"401":{"description":"Unauthorized - Invalid or missing JWT token"},"403":{"description":"Forbidden - Only owner can delete workspace"},"404":{"description":"Workspace not found"}},"security":[{"clerk-jwt":[]}],"summary":"Delete workspace","tags":["Workspaces"]}},"/v1/workspaces/{id}/members":{"get":{"description":"Retrieves all active members of a workspace","operationId":"Workspaces_listMembers","parameters":[{"name":"id","required":true,"in":"path","description":"Workspace UUID","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"List of members retrieved successfully"},"401":{"description":"Unauthorized - Invalid or missing JWT token"},"403":{"description":"Forbidden - Not a member of this workspace"},"404":{"description":"Workspace not found"}},"security":[{"clerk-jwt":[]}],"summary":"List workspace members","tags":["Workspaces"]},"post":{"description":"Sends an invitation to join the workspace (owner/admin only)","operationId":"Workspaces_invite","parameters":[{"name":"id","required":true,"in":"path","description":"Workspace UUID","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteMemberDto"}}}},"responses":{"201":{"description":"Invitation sent successfully"},"400":{"description":"Invalid email or user already a member"},"401":{"description":"Unauthorized - Invalid or missing JWT token"},"403":{"description":"Forbidden - Insufficient permissions"},"404":{"description":"Workspace not found"}},"security":[{"clerk-jwt":[]}],"summary":"Invite member to workspace","tags":["Workspaces"]}},"/v1/workspaces/{id}/invitations":{"get":{"description":"Retrieves all pending invitations for a workspace","operationId":"Workspaces_listInvitations","parameters":[{"name":"id","required":true,"in":"path","description":"Workspace UUID","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"List of invitations retrieved successfully"},"401":{"description":"Unauthorized - Invalid or missing JWT token"},"403":{"description":"Forbidden - Not a member of this workspace"},"404":{"description":"Workspace not found"}},"security":[{"clerk-jwt":[]}],"summary":"List pending invitations","tags":["Workspaces"]}},"/v1/workspaces/{id}/members/{userId}":{"patch":{"description":"Changes a member's role in the workspace (owner/admin only)","operationId":"Workspaces_updateMember","parameters":[{"name":"id","required":true,"in":"path","description":"Workspace UUID","schema":{"format":"uuid","type":"string"}},{"name":"userId","required":true,"in":"path","description":"User ID of the member to update","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateMemberDto"}}}},"responses":{"200":{"description":"Member role updated successfully"},"400":{"description":"Invalid role or cannot modify owner"},"401":{"description":"Unauthorized - Invalid or missing JWT token"},"403":{"description":"Forbidden - Insufficient permissions"},"404":{"description":"Workspace or member not found"}},"security":[{"clerk-jwt":[]}],"summary":"Update member role","tags":["Workspaces"]},"delete":{"description":"Removes a member from the workspace (owner/admin only)","operationId":"Workspaces_removeMember","parameters":[{"name":"id","required":true,"in":"path","description":"Workspace UUID","schema":{"format":"uuid","type":"string"}},{"name":"userId","required":true,"in":"path","description":"User ID of the member to remove","schema":{"type":"string"}}],"responses":{"204":{"description":"Member removed successfully"},"400":{"description":"Cannot remove workspace owner"},"401":{"description":"Unauthorized - Invalid or missing JWT token"},"403":{"description":"Forbidden - Insufficient permissions"},"404":{"description":"Workspace or member not found"}},"security":[{"clerk-jwt":[]}],"summary":"Remove member from workspace","tags":["Workspaces"]}},"/v1/workspaces/{id}/invitations/{invitationId}":{"delete":{"description":"Cancels a pending workspace invitation (owner/admin only)","operationId":"Workspaces_revokeInvitation","parameters":[{"name":"id","required":true,"in":"path","description":"Workspace UUID","schema":{"format":"uuid","type":"string"}},{"name":"invitationId","required":true,"in":"path","description":"Invitation UUID","schema":{"format":"uuid","type":"string"}}],"responses":{"204":{"description":"Invitation revoked successfully"},"401":{"description":"Unauthorized - Invalid or missing JWT token"},"403":{"description":"Forbidden - Insufficient permissions"},"404":{"description":"Workspace or invitation not found"}},"security":[{"clerk-jwt":[]}],"summary":"Revoke invitation","tags":["Workspaces"]}},"/v1/billing":{"get":{"description":"Current plan, entitlements, seat and document usage, and the available upgrade prices. Readable by any workspace member so the UI can show why an action was blocked; only the owner can change anything.","operationId":"Billing_summary","parameters":[],"responses":{"200":{"description":"Billing summary","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingSummaryDto"}}}}},"security":[{"workspace-id":[]},{"clerk-jwt":[]}],"summary":"Get billing summary","tags":["Billing"]}},"/v1/billing/checkout":{"post":{"description":"Creates a Stripe-hosted Checkout Session for the chosen plan and returns its URL. Redirect the browser to it with a full-page navigation. Pass `seats` to buy more than the plan bundles in the same transaction. Seats already in use beyond what the plan bundles are included in the first invoice, so nobody loses access on upgrade.","operationId":"Billing_checkout","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SelectPlanDto"}}}},"responses":{"200":{"description":"Checkout URL","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingRedirectDto"}}}},"403":{"description":"Not the workspace owner"}},"security":[{"workspace-id":[]},{"clerk-jwt":[]}],"summary":"Start a checkout","tags":["Billing"]}},"/v1/billing/portal":{"post":{"description":"Creates a Stripe-hosted Customer Portal session for managing the payment method, downloading invoices, and cancelling. Plan and seat changes are deliberately not available there — use this API, which checks changes against actual workspace membership.","operationId":"Billing_portal","parameters":[],"responses":{"200":{"description":"Portal URL","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingRedirectDto"}}}},"403":{"description":"Not the workspace owner"},"404":{"description":"Account has no billing history yet"}},"security":[{"workspace-id":[]},{"clerk-jwt":[]}],"summary":"Open the customer portal","tags":["Billing"]}},"/v1/billing/plan":{"patch":{"description":"Moves an existing subscription to another plan or billing interval. Upgrades are prorated and take effect at once. Downgrades issue no credit and the lower price applies from the next renewal. Seats already paid for carry over; pass `seats` to change that number at the same time.","operationId":"Billing_changePlan","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SelectPlanDto"}}}},"responses":{"200":{"description":"Updated billing summary","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingSummaryDto"}}}},"403":{"description":"Not the workspace owner"},"404":{"description":"No subscription to change"}},"security":[{"workspace-id":[]},{"clerk-jwt":[]}],"summary":"Change plan or interval","tags":["Billing"]}},"/v1/billing/seats":{"patch":{"description":"Sets how many seats the account pays for. Never called implicitly by inviting someone — adding a teammate returns 402 with `SEAT_LIMIT_REACHED` so the owner confirms the charge first.","operationId":"Billing_updateSeats","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSeatsDto"}}}},"responses":{"200":{"description":"Updated billing summary","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingSummaryDto"}}}},"403":{"description":"Not the workspace owner"},"404":{"description":"No subscription to add seats to"}},"security":[{"workspace-id":[]},{"clerk-jwt":[]}],"summary":"Set the seat count","tags":["Billing"]}},"/v1/billing/public/checkout":{"post":{"description":"Creates a Stripe-hosted Checkout Session for a visitor who has not signed up, and returns its URL for a full-page redirect. Stripe collects the email; the resulting subscription is attached to an account provisioned for that address when the webhook arrives, and the buyer proves ownership of it by signing in afterwards. Seats are charged as requested, floored at what the plan bundles.","operationId":"PublicCheckout_checkout","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnonymousCheckoutDto"}}}},"responses":{"200":{"description":"Checkout URL","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingRedirectDto"}}}}},"summary":"Start a checkout without an account","tags":["Billing"]}},"/v1/billing/public/checkout/{sessionId}":{"get":{"description":"Returns the plan, seat count, and email for a checkout started without an account, so the post-payment page can confirm what was bought and point the buyer at the right sign-in address. Sessions that were not started anonymously are reported as not found.","operationId":"PublicCheckout_status","parameters":[{"name":"sessionId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Checkout summary","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnonymousCheckoutStatusDto"}}}},"404":{"description":"Unknown checkout session"}},"summary":"Describe an anonymous checkout","tags":["Billing"]}},"/v1/templates/{templateId}/draft":{"get":{"description":"Returns the current draft version's content, or 200 with `null` if none exists.","operationId":"TemplateVersions_getDraft","parameters":[{"name":"templateId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Draft retrieved (or null)."}},"security":[{"workspace-id":[]},{"clerk-jwt":[]}],"summary":"Get current draft version","tags":["Template Versions"]},"patch":{"description":"Writes the full version content. Always allowed; returns engine diagnostics for the editor to render (warning-tolerant).","operationId":"TemplateVersions_upsertDraft","parameters":[{"name":"templateId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"workspace-id":[]},{"clerk-jwt":[]}],"summary":"Upsert draft content","tags":["Template Versions"]},"delete":{"operationId":"TemplateVersions_discardDraft","parameters":[{"name":"templateId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""}},"security":[{"workspace-id":[]},{"clerk-jwt":[]}],"summary":"Discard the current draft","tags":["Template Versions"]}},"/v1/templates/{templateId}/draft/ensure":{"post":{"operationId":"TemplateVersions_ensureDraft","parameters":[{"name":"templateId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"security":[{"workspace-id":[]},{"clerk-jwt":[]}],"summary":"Lazily create a draft from the published version (or empty)","tags":["Template Versions"]}},"/v1/templates/{templateId}/draft/publish":{"post":{"description":"Strict engine validation gate. Returns the published version; or throws RuleEngineException with structured diagnostics.","operationId":"TemplateVersions_publish","parameters":[{"name":"templateId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"security":[{"workspace-id":[]},{"clerk-jwt":[]}],"summary":"Publish the draft","tags":["Template Versions"]}},"/v1/templates/{templateId}/versions":{"get":{"operationId":"TemplateVersions_listVersions","parameters":[{"name":"templateId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"workspace-id":[]},{"clerk-jwt":[]}],"summary":"List all published + draft versions","tags":["Template Versions"]}},"/v1/templates/{templateId}/versions/{versionId}":{"get":{"operationId":"TemplateVersions_findOne","parameters":[{"name":"templateId","required":true,"in":"path","schema":{"type":"string"}},{"name":"versionId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"workspace-id":[]},{"clerk-jwt":[]}],"summary":"Get a specific version (read-only)","tags":["Template Versions"]}},"/v1/templates":{"post":{"description":"Reserves a template id and the initial draft version. For a PDF source (the default) the response also carries a presigned URL the browser uploads the file to. Pass a `content` body with `sourceKind: \"document\"` to author the template as rich text instead; there is then no file to upload, so no `uploadUrl` is returned.","operationId":"Templates_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTemplateDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTemplateResponseDto"}}}}},"security":[{"workspace-id":[]},{"clerk-jwt":[]},{"api-key":[]}],"summary":"Create a new template","tags":["Templates"]},"get":{"operationId":"Templates_list","parameters":[{"name":"status","required":false,"in":"query","description":"Filter templates by status","schema":{"example":"active","type":"string","enum":["active","archived","all"]}},{"name":"kind","required":false,"in":"query","description":"Which kind of template to list. `reusable` (the default) is the templates library. `oneoff` returns only the single-use templates behind an unfinished 'upload and send' — the ones still being prepared, with no document created from them yet.","schema":{"example":"reusable","type":"string","enum":["reusable","oneoff"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TemplateWithVersionsDto"}}}}}},"security":[{"workspace-id":[]},{"clerk-jwt":[]},{"api-key":[]}],"summary":"List templates in the workspace","tags":["Templates"]}},"/v1/templates/{id}":{"get":{"description":"Returns the lightweight template row bundled with its current draft + published version (when each exists).","operationId":"Templates_findOne","parameters":[{"name":"id","required":true,"in":"path","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateWithVersionsDto"}}}}},"security":[{"workspace-id":[]},{"clerk-jwt":[]},{"api-key":[]}],"summary":"Get template by id","tags":["Templates"]},"patch":{"operationId":"Templates_update","parameters":[{"name":"id","required":true,"in":"path","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTemplateDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateWithVersionsDto"}}}}},"security":[{"workspace-id":[]},{"clerk-jwt":[]},{"api-key":[]}],"summary":"Update template metadata (name only)","tags":["Templates"]},"delete":{"operationId":"Templates_remove","parameters":[{"name":"id","required":true,"in":"path","schema":{"format":"uuid","type":"string"}}],"responses":{"204":{"description":""}},"security":[{"workspace-id":[]},{"clerk-jwt":[]},{"api-key":[]}],"summary":"Delete template","tags":["Templates"]}},"/v1/templates/{id}/pdf":{"get":{"operationId":"Templates_getPdfUrl","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateUrlResponseDto"}}}}},"security":[{"workspace-id":[]},{"clerk-jwt":[]},{"api-key":[]}],"summary":"Get a presigned PDF URL for the latest version","tags":["Templates"]}},"/v1/templates/{id}/archive":{"post":{"operationId":"Templates_archive","parameters":[{"name":"id","required":true,"in":"path","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateWithVersionsDto"}}}}},"security":[{"workspace-id":[]},{"clerk-jwt":[]},{"api-key":[]}],"summary":"Archive template","tags":["Templates"]}},"/v1/templates/{id}/unarchive":{"post":{"operationId":"Templates_unarchive","parameters":[{"name":"id","required":true,"in":"path","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateWithVersionsDto"}}}}},"security":[{"workspace-id":[]},{"clerk-jwt":[]},{"api-key":[]}],"summary":"Unarchive template","tags":["Templates"]}},"/v1/documents":{"post":{"description":"Creates a document from a template with recipients for signing. Reusable templates must already be published. One-off templates — the single-use containers behind the 'upload and send' flow — are published implicitly here, so their draft must pass rules-engine validation.","operationId":"Documents_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDocumentDto"}}}},"responses":{"201":{"description":"Document created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentDto"}}}},"400":{"description":"Invalid input data, an unknown role key, an archived template, a reusable template with no published version (`NO_PUBLISHED_VERSION`), or a one-off template whose draft has blocking rule diagnostics."},"401":{"description":"Unauthorized - Invalid or missing JWT token"},"404":{"description":"Template not found"}},"security":[{"workspace-id":[]},{"clerk-jwt":[]},{"api-key":[]}],"summary":"Create a new document","tags":["Documents"]},"get":{"description":"Retrieves all documents in the workspace with optional filtering by status, template, or search query","operationId":"Documents_list","parameters":[{"name":"status","required":false,"in":"query","description":"Filter by document status","schema":{"example":"sent","type":"string","enum":["draft","sent","in_progress","completed","declined","voided","expired"]}},{"name":"templateId","required":false,"in":"query","description":"Filter by template UUID","schema":{"format":"uuid","example":"550e8400-e29b-41d4-a716-446655440000","type":"string"}},{"name":"q","required":false,"in":"query","description":"Search query for document name, recipient name, or email","schema":{"example":"john","type":"string"}}],"responses":{"200":{"description":"List of documents retrieved successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/DocumentDto"}}}}}},"security":[{"workspace-id":[]},{"clerk-jwt":[]},{"api-key":[]}],"summary":"List documents","tags":["Documents"]}},"/v1/documents/{id}":{"get":{"description":"Retrieves detailed information about a specific document","operationId":"Documents_findOne","parameters":[{"name":"id","required":true,"in":"path","description":"Document UUID","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentDto"}}}},"403":{"description":"Forbidden - Not the document owner"},"404":{"description":"Document not found"}},"security":[{"workspace-id":[]},{"clerk-jwt":[]},{"api-key":[]}],"summary":"Get document by ID","tags":["Documents"]},"patch":{"description":"Updates document details (only allowed in draft state)","operationId":"Documents_update","parameters":[{"name":"id","required":true,"in":"path","description":"Document UUID","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateDocumentDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentDto"}}}},"400":{"description":"Invalid input or document not in draft state"},"403":{"description":"Forbidden - Not the document owner"},"404":{"description":"Document not found"}},"security":[{"workspace-id":[]},{"clerk-jwt":[]},{"api-key":[]}],"summary":"Update document","tags":["Documents"]},"delete":{"description":"Permanently deletes a document","operationId":"Documents_remove","parameters":[{"name":"id","required":true,"in":"path","description":"Document UUID","schema":{"format":"uuid","type":"string"}}],"responses":{"204":{"description":"Document deleted"},"403":{"description":"Forbidden - Not the document owner"},"404":{"description":"Document not found"}},"security":[{"workspace-id":[]},{"clerk-jwt":[]},{"api-key":[]}],"summary":"Delete document","tags":["Documents"]}},"/v1/documents/{id}/send":{"post":{"description":"Sends the document to recipients and transitions it to 'sent' state","operationId":"Documents_send","parameters":[{"name":"id","required":true,"in":"path","description":"Document UUID","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentDto"}}}},"400":{"description":"Document not in valid state for sending"},"403":{"description":"Forbidden - Not the document owner"},"404":{"description":"Document not found"}},"security":[{"workspace-id":[]},{"clerk-jwt":[]},{"api-key":[]}],"summary":"Send document for signing","tags":["Documents"]}},"/v1/documents/{id}/remind":{"post":{"description":"Immediately emails a reminder to every pending recipient with an active signing link. Does not consume the automatic reminder budget.","operationId":"Documents_remind","parameters":[{"name":"id","required":true,"in":"path","description":"Document UUID","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentDto"}}}},"400":{"description":"No pending recipients, or document not in a remindable state"},"403":{"description":"Forbidden - Not the document owner"},"404":{"description":"Document not found"}},"security":[{"workspace-id":[]},{"clerk-jwt":[]},{"api-key":[]}],"summary":"Send a manual reminder","tags":["Documents"]}},"/v1/documents/{id}/recipients/{recipientId}/remind":{"post":{"description":"Immediately emails a reminder to a single pending recipient with an active signing link. Does not consume the automatic reminder budget.","operationId":"Documents_remindRecipient","parameters":[{"name":"recipientId","required":true,"in":"path","description":"Recipient UUID","schema":{"format":"uuid","type":"string"}},{"name":"id","required":true,"in":"path","description":"Document UUID","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentDto"}}}},"400":{"description":"Recipient not pending or does not have an active signing link"},"403":{"description":"Forbidden - Not the document owner"},"404":{"description":"Document or recipient not found"}},"security":[{"workspace-id":[]},{"clerk-jwt":[]},{"api-key":[]}],"summary":"Send a reminder to a specific recipient","tags":["Documents"]}},"/v1/documents/{id}/void":{"post":{"description":"Cancels the document and prevents further signing","operationId":"Documents_voidDoc","parameters":[{"name":"id","required":true,"in":"path","description":"Document UUID","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentDto"}}}},"400":{"description":"Document cannot be voided in current state"},"403":{"description":"Forbidden - Not the document owner"},"404":{"description":"Document not found"}},"security":[{"workspace-id":[]},{"clerk-jwt":[]},{"api-key":[]}],"summary":"Void document","tags":["Documents"]}},"/v1/documents/{id}/signed-pdf":{"get":{"description":"Retrieves a presigned URL for the completed signed PDF. By default the URL renders inline (for previewing in a browser); pass `download=true` to force an attachment download with a friendly filename.","operationId":"Documents_signedPdf","parameters":[{"name":"download","required":false,"in":"query","description":"Force an attachment download instead of inline rendering","schema":{"type":"boolean"}},{"name":"id","required":true,"in":"path","description":"Document UUID","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UrlResponseDto"}}}},"403":{"description":"Forbidden - Not the document owner"},"404":{"description":"Document not found or not completed"}},"security":[{"workspace-id":[]},{"clerk-jwt":[]},{"api-key":[]}],"summary":"Get signed PDF URL","tags":["Documents"]}},"/v1/documents/{id}/certificate":{"get":{"description":"Retrieves a presigned URL to download the standalone certificate of completion","operationId":"Documents_certificate","parameters":[{"name":"id","required":true,"in":"path","description":"Document UUID","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UrlResponseDto"}}}},"403":{"description":"Forbidden - Not the document owner"},"404":{"description":"Document or certificate not found"}},"security":[{"workspace-id":[]},{"clerk-jwt":[]},{"api-key":[]}],"summary":"Get certificate of completion URL","tags":["Documents"]}},"/v1/documents/{id}/audit":{"get":{"description":"Returns the ordered chain-of-custody events (sent, viewed, signed, declined, etc.) for the document","operationId":"Documents_audit","parameters":[{"name":"id","required":true,"in":"path","description":"Document UUID","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AuditEventDto"}}}}},"403":{"description":"Forbidden - Not the document owner"},"404":{"description":"Document not found"}},"security":[{"workspace-id":[]},{"clerk-jwt":[]},{"api-key":[]}],"summary":"Get document audit trail","tags":["Documents"]}},"/v1/submissions":{"get":{"description":"Retrieves all recipient submissions for a specific document including their signature data","operationId":"Submissions_list","parameters":[{"name":"documentId","required":true,"in":"query","description":"Document UUID to get submissions for","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"List of submissions retrieved successfully"},"400":{"description":"Missing or invalid documentId"},"401":{"description":"Unauthorized - Invalid or missing JWT token"},"403":{"description":"Forbidden - Not authorized to view this document"},"404":{"description":"Document not found"}},"security":[{"workspace-id":[]},{"clerk-jwt":[]},{"api-key":[]}],"summary":"List submissions for a document","tags":["Submissions"]}},"/v1/sign/{token}":{"get":{"description":"Retrieves document and field information for a recipient using their signing token","operationId":"Signing_getContext","parameters":[{"name":"token","required":true,"in":"path","description":"Unique signing token sent to recipient","schema":{"type":"string"}}],"responses":{"200":{"description":"Signing context retrieved successfully"},"401":{"description":"Invalid or expired signing token"},"404":{"description":"Document or recipient not found"}},"summary":"Get signing context","tags":["Signing"]}},"/v1/sign/{token}/draft":{"get":{"description":"Retrieves previously saved draft values for the recipient","operationId":"Signing_getDraft","parameters":[{"name":"token","required":true,"in":"path","description":"Unique signing token sent to recipient","schema":{"type":"string"}}],"responses":{"200":{"description":"Draft retrieved successfully"},"204":{"description":"No draft exists"},"401":{"description":"Invalid or expired signing token"}},"summary":"Get saved draft","tags":["Signing"]},"post":{"description":"Saves recipient's progress without submitting the document","operationId":"Signing_saveDraft","parameters":[{"name":"token","required":true,"in":"path","description":"Unique signing token sent to recipient","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SaveDraftDto"}}}},"responses":{"204":{"description":"Draft saved successfully"},"400":{"description":"Invalid field values"},"401":{"description":"Invalid or expired signing token"}},"summary":"Save draft","tags":["Signing"]}},"/v1/sign/{token}/document":{"get":{"description":"Presigned URL for the document as it stands for this recipient. A CC or viewer placed mid-order receives the snapshot as of their step; once the document is complete, everyone gets the certified final PDF.","operationId":"Signing_documentUrl","parameters":[{"name":"token","required":true,"in":"path","description":"Unique signing token sent to recipient","schema":{"type":"string"}}],"responses":{"200":{"description":"Download URL issued"},"401":{"description":"Invalid or expired signing token"},"409":{"description":"No rendered PDF exists for this document yet"}},"summary":"Get a download link for the document","tags":["Signing"]}},"/v1/sign/{token}/submit":{"post":{"description":"Submits the completed document with recipient's signatures and data","operationId":"Signing_submit","parameters":[{"name":"token","required":true,"in":"path","description":"Unique signing token sent to recipient","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitSigningDto"}}}},"responses":{"201":{"description":"Document submitted successfully"},"400":{"description":"Invalid or incomplete field values"},"401":{"description":"Invalid or expired signing token"},"403":{"description":"Recipient's role does not permit signing (a CC recipient holds a read-only copy link)"},"409":{"description":"Document already submitted or in invalid state"}},"summary":"Submit signature","tags":["Signing"]}},"/v1/sign/{token}/decline":{"post":{"description":"Allows recipient to decline signing the document with a reason","operationId":"Signing_decline","parameters":[{"name":"token","required":true,"in":"path","description":"Unique signing token sent to recipient","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeclineDto"}}}},"responses":{"204":{"description":"Document declined successfully"},"400":{"description":"Invalid decline reason or document already processed"},"401":{"description":"Invalid or expired signing token"},"403":{"description":"Recipient's role does not permit declining"}},"summary":"Decline to sign","tags":["Signing"]}},"/v1/template-gallery":{"get":{"description":"Returns the curated gallery catalog. Optionally filter by category. Card-sized: includes recipients and a field count, but not `fields`, `body`, or a preview URL — fetch a single template for those.","operationId":"TemplateGallery_list","parameters":[{"name":"category","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/GalleryTemplateDto"}}}}}},"security":[{"workspace-id":[]},{"clerk-jwt":[]}],"summary":"List gallery templates","tags":["Template Gallery"]}},"/v1/template-gallery/{slug}":{"get":{"description":"Includes the full normalized `content` so the client can render the template itself, plus a short-lived presigned PDF preview URL for `sourceKind: 'pdf'` templates only.","operationId":"TemplateGallery_findOne","parameters":[{"name":"slug","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GalleryTemplateDto"}}}},"404":{"description":"Not found."}},"security":[{"workspace-id":[]},{"clerk-jwt":[]}],"summary":"Get a single gallery template","tags":["Template Gallery"]}},"/v1/template-gallery/{slug}/instantiate":{"post":{"description":"Clones the gallery template (PDF + content) into a new workspace template. Use mode=\"customize\" to open the editor, or mode=\"send\" to auto-publish and send immediately.","operationId":"TemplateGallery_instantiate","parameters":[{"name":"slug","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InstantiateGalleryTemplateDto"}}}},"responses":{"201":{"description":"New workspace template created."},"403":{"description":"Plan tier does not allow this template."},"404":{"description":"Gallery template not found."}},"security":[{"workspace-id":[]},{"clerk-jwt":[]}],"summary":"Instantiate a gallery template into the workspace","tags":["Template Gallery"]}},"/v1/agent/sessions":{"post":{"operationId":"Agent_createSession","parameters":[],"responses":{"201":{"description":""}},"security":[{"workspace-id":[]},{"clerk-jwt":[]}],"summary":"Create a new agent conversation","tags":["Agent"]},"get":{"operationId":"Agent_listSessions","parameters":[{"name":"templateId","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"workspace-id":[]},{"clerk-jwt":[]}],"summary":"List agent conversations (optionally by template)","tags":["Agent"]}},"/v1/agent/sessions/{id}":{"get":{"operationId":"Agent_getSession","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"workspace-id":[]},{"clerk-jwt":[]}],"summary":"Load a conversation with rehydrated messages","tags":["Agent"]},"delete":{"operationId":"Agent_deleteSession","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"workspace-id":[]},{"clerk-jwt":[]}],"summary":"Delete a conversation","tags":["Agent"]}},"/v1/agent/chat":{"post":{"operationId":"Agent_chat","parameters":[],"responses":{"201":{"description":""}},"security":[{"workspace-id":[]},{"clerk-jwt":[]}],"summary":"Stream a chat turn (Server-Sent Events)","tags":["Agent"]}},"/v1/agent/generate":{"post":{"operationId":"Agent_generate","parameters":[],"responses":{"201":{"description":""}},"security":[{"workspace-id":[]},{"clerk-jwt":[]}],"summary":"Generate a new template draft from a prompt","tags":["Agent"]}},"/v1/agent/detect-fields":{"post":{"operationId":"Agent_detectFields","parameters":[],"responses":{"201":{"description":""}},"security":[{"workspace-id":[]},{"clerk-jwt":[]}],"summary":"Detect fields on a draft PDF (AcroForm + text)","tags":["Agent"]}},"/v1/webhooks":{"post":{"description":"Register a new HTTPS endpoint. The signing secret is returned **once** in this response and will not be shown again — store it securely.","operationId":"Webhooks_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWebhookDto"}}}},"responses":{"201":{"description":"Endpoint created — includes signing secret (shown once)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointWithSecretDto"}}}}},"security":[{"workspace-id":[]},{"clerk-jwt":[]},{"api-key":[]}],"summary":"Create a webhook endpoint","tags":["Webhooks"]},"get":{"operationId":"Webhooks_list","parameters":[],"responses":{"200":{"description":"List of endpoints (secrets omitted)","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEndpointDto"}}}}}},"security":[{"workspace-id":[]},{"clerk-jwt":[]},{"api-key":[]}],"summary":"List webhook endpoints for the workspace","tags":["Webhooks"]}},"/v1/webhooks/{id}":{"get":{"operationId":"Webhooks_get","parameters":[{"name":"id","required":true,"in":"path","description":"Webhook endpoint ID","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointDto"}}}},"404":{"description":"Not found"}},"security":[{"workspace-id":[]},{"clerk-jwt":[]},{"api-key":[]}],"summary":"Get a single webhook endpoint","tags":["Webhooks"]},"patch":{"operationId":"Webhooks_update","parameters":[{"name":"id","required":true,"in":"path","description":"Webhook endpoint ID","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateWebhookDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointDto"}}}}},"security":[{"workspace-id":[]},{"clerk-jwt":[]},{"api-key":[]}],"summary":"Update a webhook endpoint","tags":["Webhooks"]},"delete":{"operationId":"Webhooks_remove","parameters":[{"name":"id","required":true,"in":"path","description":"Webhook endpoint ID","schema":{"type":"string"}}],"responses":{"204":{"description":"Deleted"}},"security":[{"workspace-id":[]},{"clerk-jwt":[]},{"api-key":[]}],"summary":"Delete a webhook endpoint","tags":["Webhooks"]}},"/v1/webhooks/{id}/ping":{"post":{"description":"Sends a `webhook.test` event to verify the endpoint is reachable. Returns the delivery result.","operationId":"Webhooks_ping","parameters":[{"name":"id","required":true,"in":"path","description":"Webhook endpoint ID","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryDto"}}}}},"security":[{"workspace-id":[]},{"clerk-jwt":[]},{"api-key":[]}],"summary":"Send a test event","tags":["Webhooks"]}},"/v1/webhooks/{id}/rotate-secret":{"post":{"description":"Issues a new HMAC secret for the endpoint. The new secret is returned **once** — update your server immediately. All future deliveries will use the new secret.","operationId":"Webhooks_rotateSecret","parameters":[{"name":"id","required":true,"in":"path","description":"Webhook endpoint ID","schema":{"type":"string"}}],"responses":{"200":{"description":"New secret (shown once)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointWithSecretDto"}}}}},"security":[{"workspace-id":[]},{"clerk-jwt":[]},{"api-key":[]}],"summary":"Rotate the signing secret","tags":["Webhooks"]}},"/v1/webhooks/{id}/deliveries":{"get":{"description":"Returns the 50 most recent delivery attempts.","operationId":"Webhooks_listDeliveries","parameters":[{"name":"id","required":true,"in":"path","description":"Webhook endpoint ID","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/WebhookDeliveryDto"}}}}}},"security":[{"workspace-id":[]},{"clerk-jwt":[]},{"api-key":[]}],"summary":"List recent deliveries for an endpoint","tags":["Webhooks"]}},"/v1/webhooks/{id}/deliveries/{deliveryId}/retry":{"post":{"description":"Resets the delivery to `pending` and immediately re-attempts it. The original payload and `Signvoy-Event-Id` are preserved so receivers can deduplicate.","operationId":"Webhooks_retryDelivery","parameters":[{"name":"id","required":true,"in":"path","description":"Webhook endpoint ID","schema":{"type":"string"}},{"name":"deliveryId","required":true,"in":"path","description":"Delivery ID","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryDto"}}}}},"security":[{"workspace-id":[]},{"clerk-jwt":[]},{"api-key":[]}],"summary":"Manually retry a failed delivery","tags":["Webhooks"]}},"/v1/api-keys":{"post":{"description":"Creates a new secret API key scoped to this workspace. The plaintext key is returned **once** in this response and cannot be retrieved again — store it securely.","operationId":"ApiKeys_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateApiKeyDto"}}}},"responses":{"201":{"description":"Key created (includes the plaintext secret, shown once)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedApiKeyDto"}}}},"403":{"description":"Not a workspace owner or admin"}},"security":[{"workspace-id":[]},{"clerk-jwt":[]}],"summary":"Create an API key","tags":["API Keys"]},"get":{"description":"Returns all API keys for the workspace. Secrets are never included — only the prefix and last 4 characters.","operationId":"ApiKeys_list","parameters":[],"responses":{"200":{"description":"List of API keys","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ApiKeyDto"}}}}}},"security":[{"workspace-id":[]},{"clerk-jwt":[]}],"summary":"List API keys","tags":["API Keys"]}},"/v1/api-keys/{id}":{"delete":{"description":"Immediately and permanently revokes the key. Any request using it will start failing at once. This cannot be undone.","operationId":"ApiKeys_revoke","parameters":[{"name":"id","required":true,"in":"path","description":"API key ID","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"The revoked key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyDto"}}}},"403":{"description":"Not a workspace owner or admin"},"404":{"description":"API key not found"}},"security":[{"workspace-id":[]},{"clerk-jwt":[]}],"summary":"Revoke an API key","tags":["API Keys"]}},"/v1/embed/session":{"post":{"description":"Mints a scoped, short-lived signing URL for a specific document recipient. Pass the returned `url` to an `<iframe src={url}>` or to the `@signvoy/react` `<SignvoyEmbed url={url} />` component. The document must be in `sent` state and the recipient must be `pending`.","operationId":"Embed_createSession","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateEmbedSessionDto"}}}},"responses":{"201":{"description":"Embed session created — includes the signing URL","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmbedSessionResponseDto"}}}},"400":{"description":"Document not in 'sent' state or recipient already signed"},"403":{"description":"Document does not belong to this workspace"},"404":{"description":"Document or recipient not found"}},"security":[{"workspace-id":[]},{"api-key":[]}],"summary":"Create an embed session","tags":["Embed"]}},"/v1/notifications":{"get":{"operationId":"Notifications_list","parameters":[{"name":"unread","required":false,"in":"query","description":"When true, return only unread notifications","schema":{"default":false,"type":"boolean"}},{"name":"limit","required":false,"in":"query","description":"Maximum number of notifications to return","schema":{"minimum":1,"maximum":100,"default":50,"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/NotificationDto"}}}}}},"security":[{"workspace-id":[]},{"clerk-jwt":[]}],"summary":"List notifications for the current user","tags":["Notifications"]}},"/v1/notifications/unread-count":{"get":{"operationId":"Notifications_unreadCount","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnreadCountDto"}}}}},"security":[{"workspace-id":[]},{"clerk-jwt":[]}],"summary":"Get the unread notification count","tags":["Notifications"]}},"/v1/notifications/{id}/read":{"patch":{"operationId":"Notifications_markRead","parameters":[{"name":"id","required":true,"in":"path","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationDto"}}}}},"security":[{"workspace-id":[]},{"clerk-jwt":[]}],"summary":"Mark a notification as read","tags":["Notifications"]}},"/v1/notifications/read-all":{"post":{"operationId":"Notifications_markAllRead","parameters":[],"responses":{"204":{"description":""}},"security":[{"workspace-id":[]},{"clerk-jwt":[]}],"summary":"Mark all notifications as read","tags":["Notifications"]}},"/v1/users/me":{"get":{"operationId":"Users_me","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserProfileDto"}}}}},"security":[{"clerk-jwt":[]}],"summary":"Get the current user's profile, creating it on first call","tags":["Users"]},"patch":{"operationId":"Users_updateMe","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateUserDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserProfileDto"}}}}},"security":[{"clerk-jwt":[]}],"summary":"Update the current user's profile and onboarding progress","tags":["Users"]}},"/v1/users/me/signatures":{"get":{"description":"Default first, then newest — the order the signing picker uses.","operationId":"UserSignatures_list","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UserSignatureDto"}}}}}},"security":[{"clerk-jwt":[]}],"summary":"List the current user's saved signatures","tags":["Signatures"]},"post":{"description":"Stores a signature for reuse. Up to 10 per user; the first one saved becomes the default.","operationId":"UserSignatures_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateUserSignatureDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserSignatureDto"}}}},"409":{"description":"The per-user signature limit is reached"}},"security":[{"clerk-jwt":[]}],"summary":"Save a signature","tags":["Signatures"]}},"/v1/users/me/signatures/{id}":{"patch":{"description":"Promotes it to default, replaces its image, or both. `isDefault: false` is a no-op — a user who owns signatures always has exactly one default, so the way to move it is to promote another. Replacing the image leaves already-signed documents untouched, since the image is copied into each submission at signing time.","operationId":"UserSignatures_update","parameters":[{"name":"id","required":true,"in":"path","schema":{"format":"uuid","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateUserSignatureDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserSignatureDto"}}}},"404":{"description":"Signature not found"}},"security":[{"clerk-jwt":[]}],"summary":"Update a signature","tags":["Signatures"]},"delete":{"description":"Deleting the default promotes the next most recent signature. Documents already signed with it are unaffected — the image is copied into each submission at signing time.","operationId":"UserSignatures_remove","parameters":[{"name":"id","required":true,"in":"path","schema":{"format":"uuid","type":"string"}}],"responses":{"204":{"description":"Signature deleted"},"404":{"description":"Signature not found"}},"security":[{"clerk-jwt":[]}],"summary":"Delete a signature","tags":["Signatures"]}},"/v1/mcp-grants":{"post":{"description":"Creates or un-revokes a grant so the specified Clerk OAuth client can access the MCP endpoint on behalf of the authenticated user. The clerkClientId is the client_id returned by Clerk's Dynamic Client Registration endpoint when an MCP client connects for the first time. A grant carries no workspace — each tool call names the workspace it acts on and is authorised against the user's membership of it.","operationId":"McpGrants_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateMcpGrantDto"}}}},"responses":{"201":{"description":""}},"summary":"Authorise an OAuth client to call the MCP endpoint","tags":["MCP"]},"get":{"operationId":"McpGrants_list","parameters":[],"responses":{"200":{"description":""}},"summary":"List active MCP grants for the current user","tags":["MCP"]}},"/v1/mcp-grants/{id}":{"delete":{"operationId":"McpGrants_revoke","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""}},"summary":"Revoke an MCP grant","tags":["MCP"]}}},"info":{"title":"Signvoy API","description":"API for managing PDF document signing workflows, templates, and workspaces","version":"1.0","contact":{}},"tags":[{"name":"Health","description":"Health check endpoints"},{"name":"Workspaces","description":"Workspace management and member operations"},{"name":"Templates","description":"PDF template management"},{"name":"Template Versions","description":"Draft and published template version management"},{"name":"Template Gallery","description":"Curated starter templates"},{"name":"Documents","description":"Document creation and lifecycle management"},{"name":"Submissions","description":"Document submission and completion tracking"},{"name":"Signing","description":"Public signing endpoints for recipients"},{"name":"Agent","description":"AI agent sessions for template generation, field detection, and conversational editing"},{"name":"Webhooks","description":"Webhook endpoint management — register URLs, manage subscriptions, view delivery history"},{"name":"API Keys","description":"Create and revoke workspace-scoped API keys for programmatic access"},{"name":"Embed","description":"Mint scoped signing-session URLs for embedding the signing experience in your own UI"},{"name":"Notifications","description":"In-app notifications — list, unread counts, and read-state management"},{"name":"MCP","description":"Model Context Protocol grants — authorise and revoke AI agent access per workspace"},{"name":"Billing","description":"Plan, seats, and usage — read entitlements, start a checkout, open the customer portal"},{"name":"Users","description":"The signed-in user's own profile and onboarding progress — scoped to the user, not a workspace"},{"name":"Signatures","description":"The signed-in user's reusable signature library — scoped to the user, not a workspace, so the same signatures are available wherever they sign"}],"servers":[{"url":"https://api.signvoy.com","description":"Signvoy API"}],"components":{"securitySchemes":{"api-key":{"scheme":"bearer","bearerFormat":"API key","type":"http","description":"**Recommended.** Signvoy API key. Create one in Workspace Settings → API Keys and pass it as `Authorization: Bearer sk_live_...`. The key implies its workspace, so `X-Workspace-Id` is optional when using an API key."},"clerk-jwt":{"scheme":"bearer","bearerFormat":"Clerk JWT","type":"http","description":"Clerk session JWT — used internally by the Signvoy dashboard. Not recommended for external integrations; use an API key instead."},"workspace-id":{"type":"apiKey","in":"header","name":"X-Workspace-Id","description":"Workspace ID for multi-tenancy. Optional when authenticating with an API key (the key already implies its workspace)."}},"schemas":{"CreateWorkspaceDto":{"type":"object","properties":{"name":{"type":"string","description":"Workspace name","example":"Acme Corporation","minLength":2,"maxLength":60},"slug":{"type":"string","description":"URL-friendly slug (auto-generated from name if omitted). Must be lowercase alphanumeric with optional hyphens.","example":"acme-corp","minLength":2,"maxLength":40}},"required":["name"]},"UpdateWorkspaceDto":{"type":"object","properties":{"name":{"type":"string","description":"Updated workspace name","example":"Updated Corporation Name","minLength":2,"maxLength":60},"slug":{"type":"string","description":"Updated URL-friendly slug. Must be lowercase alphanumeric with optional hyphens.","example":"updated-corp","minLength":2,"maxLength":40}}},"InviteMemberDto":{"type":"object","properties":{"email":{"type":"string","description":"Email address of the person to invite","example":"newmember@example.com"},"role":{"type":"string","description":"Role to assign (defaults to 'member')","enum":["admin","member"],"example":"member"}},"required":["email"]},"UpdateMemberDto":{"type":"object","properties":{"role":{"type":"string","description":"New role for the member. Note: Owner role cannot be assigned via this endpoint.","enum":["admin","member"],"example":"admin"}},"required":["role"]},"BillingSeatsDto":{"type":"object","properties":{"included":{"type":"number","description":"Seats bundled with the current plan","example":3},"purchased":{"type":"number","description":"Extra seats bought on top","example":1},"allowed":{"type":"number","description":"Seats the account may fill (included + purchased)","example":4},"used":{"type":"number","description":"Seats in use: distinct members across every workspace on the account, plus pending invitations to addresses that are not already members","example":4}},"required":["included","purchased","allowed","used"]},"BillingDocumentsDto":{"type":"object","properties":{"sent":{"type":"number","description":"Documents sent in the current billing period","example":7},"limit":{"type":"number","nullable":true,"description":"Documents included per period; null means unlimited","example":10}},"required":["sent"]},"BillingWorkspacesDto":{"type":"object","properties":{"count":{"type":"number","description":"Workspaces on this account","example":1},"limit":{"type":"number","nullable":true,"description":"Workspaces allowed; null means unlimited","example":1}},"required":["count"]},"BillingFeaturesDto":{"type":"object","properties":{"customBranding":{"type":"boolean","description":"Custom logo and colours; Signvoy branding removed"},"customEmailSender":{"type":"boolean","description":"Custom from / reply-to address on signing emails"},"sharedTemplates":{"type":"boolean","description":"Templates shared with the whole workspace"},"workspaceWideVisibility":{"type":"boolean","description":"Members can see every document in the workspace"}},"required":["customBranding","customEmailSender","sharedTemplates","workspaceWideVisibility"]},"BillingPlanOptionDto":{"type":"object","properties":{"plan":{"type":"string","enum":["pro","team"],"example":"pro"},"monthlyUsd":{"type":"number","description":"Monthly price in USD","example":10},"annualMonthlyUsd":{"type":"number","description":"Effective monthly price when billed annually, in USD","example":8},"includedSeats":{"type":"number","description":"Seats bundled with this plan","example":1}},"required":["plan","monthlyUsd","annualMonthlyUsd","includedSeats"]},"BillingSummaryDto":{"type":"object","properties":{"plan":{"type":"string","description":"Plan on the subscription, i.e. what the account pays for","enum":["free","pro","team","enterprise"],"example":"pro"},"effectivePlan":{"type":"string","description":"Plan actually in force. Differs from `plan` only when the subscription has lapsed, in which case entitlements fall back to Free.","enum":["free","pro","team","enterprise"],"example":"pro"},"status":{"type":"string","nullable":true,"description":"Stripe subscription status; null on an account that never paid","example":"active"},"interval":{"type":"string","nullable":true,"enum":["month","year"],"description":"Billing interval; null on Free","example":"month"},"cancelAtPeriodEnd":{"type":"boolean","description":"Whether the subscription stops at the end of the period"},"currentPeriodEnd":{"type":"string","format":"date-time","nullable":true,"description":"End of the current billing period; null on Free"},"hasBillingHistory":{"type":"boolean","description":"Whether this account has ever reached checkout, and therefore whether the Customer Portal has invoices and a payment method to show"},"seats":{"$ref":"#/components/schemas/BillingSeatsDto"},"documents":{"$ref":"#/components/schemas/BillingDocumentsDto"},"workspaces":{"$ref":"#/components/schemas/BillingWorkspacesDto"},"features":{"$ref":"#/components/schemas/BillingFeaturesDto"},"extraSeatMonthlyUsd":{"type":"number","description":"Price per extra seat per month, in USD","example":8},"extraSeatAnnualUsd":{"type":"number","description":"Price per extra seat per month when billed annually, in USD","example":6},"availablePlans":{"type":"array","items":{"$ref":"#/components/schemas/BillingPlanOptionDto"}}},"required":["plan","effectivePlan","cancelAtPeriodEnd","hasBillingHistory","seats","documents","workspaces","features","extraSeatMonthlyUsd","extraSeatAnnualUsd","availablePlans"]},"SelectPlanDto":{"type":"object","properties":{"plan":{"type":"string","description":"Plan to subscribe to or move to","enum":["pro","team"],"example":"pro"},"interval":{"type":"string","description":"Billing interval. Annual is billed once for twelve months at a 20% discount.","enum":["month","year"],"example":"month"},"seats":{"type":"number","description":"Total seats to buy, including the ones the plan already bundles — the same number the dashboard displays. Omit it to buy exactly what the plan bundles, or what the account already occupies if that is more. Values below the seats currently in use are raised to that floor.","example":10,"minimum":1,"maximum":500}},"required":["plan","interval"]},"BillingRedirectDto":{"type":"object","properties":{"url":{"type":"string","description":"Stripe-hosted URL to send the browser to","example":"https://checkout.stripe.com/c/pay/cs_test_..."}},"required":["url"]},"UpdateSeatsDto":{"type":"object","properties":{"seats":{"type":"number","description":"Total seats the account should have, including the ones the plan already bundles — the same number the dashboard displays. Requests below the seats currently in use are raised to that floor rather than rejected, so members are never billed out from under.","example":4,"minimum":1,"maximum":500}},"required":["seats"]},"AnonymousCheckoutDto":{"type":"object","properties":{"plan":{"type":"string","description":"Plan to buy. Free needs no payment and Enterprise is a sales conversation.","enum":["pro","team"],"example":"team"},"interval":{"type":"string","description":"Billing interval. Annual is billed once for twelve months at a 20% discount.","enum":["month","year"],"example":"month"},"seats":{"type":"number","description":"Total seats to buy, including the ones the plan bundles. Omit it to buy exactly what the plan bundles. Values below that are raised to it.","example":10,"minimum":1,"maximum":500}},"required":["plan","interval"]},"AnonymousCheckoutStatusDto":{"type":"object","properties":{"plan":{"type":"string","nullable":true,"description":"Plan bought","example":"team"},"interval":{"type":"string","nullable":true,"description":"Billing interval bought","example":"month"},"seats":{"type":"number","nullable":true,"description":"Total seats bought, including those the plan bundles","example":10},"email":{"type":"string","nullable":true,"description":"Email Stripe collected. The subscription is attached to an account for this address, so it is the address the buyer must sign in with.","example":"alex@example.com"},"paid":{"type":"boolean","description":"Whether the payment has settled. Delayed payment methods can complete a session while the charge is still pending.","example":true}},"required":["paid"]},"CreateTemplateDto":{"type":"object","properties":{"name":{"type":"string","description":"Template name","example":"NDA Template","maxLength":255},"fileName":{"type":"string","description":"Original PDF filename. Required for a pdf source; omit it for a document source, which has no uploaded file.","example":"nda.pdf"},"pageCount":{"type":"number","description":"Number of pages. Required for a pdf source; omit it for a document source, whose length is only known once the body is rendered.","example":3,"minimum":1},"content":{"type":"object","description":"Optional initial draft content (recipients/fields/rules, plus the `sourceFiles` manifest when several PDFs were merged on upload). Pass a body with `sourceKind: \"document\"` to author the template as rich text instead of uploading a PDF."},"kind":{"type":"string","description":"Whether this is a reusable template or the single-use container behind a one-off document. One-off templates are hidden from the templates list and are published implicitly when a document is created from them.","enum":["reusable","oneoff"],"default":"reusable"}},"required":["name"]},"CreateTemplateResponseDto":{"type":"object","properties":{"templateId":{"type":"string","format":"uuid","description":"Newly created template ID"},"draftVersionId":{"type":"string","format":"uuid","description":"ID of the initial draft version"},"uploadUrl":{"type":"string","format":"uri","description":"Presigned PUT URL for uploading the source PDF directly to storage. Upload the PDF bytes to this URL with Content-Type: application/pdf. Absent for a document-source template, which has no file to upload — its PDF is rendered from the body at signing time."}},"required":["templateId","draftVersionId"]},"TemplateVersionDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"templateId":{"type":"string","format":"uuid"},"version":{"type":"integer","minimum":1},"status":{"type":"string","enum":["draft","published"]},"fileName":{"type":"string","description":"Original PDF file name"},"pageCount":{"type":"integer","minimum":1},"createdAt":{"type":"integer","description":"Creation timestamp (Unix ms)"},"publishedAt":{"type":"integer","description":"Unix ms when published"},"publishedBy":{"type":"string","description":"Clerk user ID of the publisher"},"pdfUrl":{"type":"string","format":"uri","description":"Presigned PDF URL (included when explicitly requested)"}},"required":["id","templateId","version","status","fileName","pageCount","createdAt"]},"TemplateWithVersionsDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"workspaceId":{"type":"string","format":"uuid"},"ownerId":{"type":"string","description":"Clerk user ID of the owner"},"name":{"type":"string"},"kind":{"type":"string","description":"Reusable templates appear in the templates list. One-off templates back a single 'upload and send' document and are hidden from every listing.","enum":["reusable","oneoff"]},"archivedAt":{"type":"integer","description":"Unix ms when archived (null if active)"},"createdAt":{"type":"integer"},"updatedAt":{"type":"integer"},"currentDraftVersionId":{"type":"string","format":"uuid","nullable":true},"currentPublishedVersionId":{"type":"string","format":"uuid","nullable":true},"draftVersion":{"nullable":true,"description":"Current draft version (null if none exists)","type":"object","allOf":[{"$ref":"#/components/schemas/TemplateVersionDto"}]},"publishedVersion":{"nullable":true,"description":"Current published version (null if never published)","type":"object","allOf":[{"$ref":"#/components/schemas/TemplateVersionDto"}]}},"required":["id","workspaceId","ownerId","name","kind","createdAt","updatedAt"]},"TemplateUrlResponseDto":{"type":"object","properties":{"url":{"type":"string","format":"uri","description":"Presigned PDF URL"}},"required":["url"]},"UpdateTemplateDto":{"type":"object","properties":{"name":{"type":"string","description":"Updated template name","maxLength":255}}},"ReminderSettingsDto":{"type":"object","properties":{"enabled":{"type":"boolean","description":"Whether automatic reminders are enabled for this document","default":true},"intervalDays":{"type":"number","description":"Days between automatic reminder emails","minimum":1,"maximum":30,"default":3},"maxReminders":{"type":"number","description":"Maximum number of automatic reminders before giving up","minimum":0,"maximum":10,"default":3}}},"ExpirySettingsDto":{"type":"object","properties":{"days":{"type":"number","description":"Days after sending that this document can still be signed. The absolute deadline is derived from this when the document is sent.","minimum":1,"maximum":365,"default":30}}},"CreateDocumentRecipientDto":{"type":"object","properties":{"roleKey":{"type":"string","description":"Template role key this recipient fills (matches `version.content.recipients[].key`). Required for `signer`; ignored for `cc` and `viewer`, which hold no fields and are assigned a synthetic key.","example":"buyer"},"name":{"type":"string","description":"Recipient's full name","example":"John Doe","maxLength":255},"email":{"type":"string","description":"Recipient's email address for notifications","example":"john.doe@example.com","maxLength":254},"type":{"type":"string","description":"What this recipient is asked to do. `signer` (default) fills in fields and signs. `cc` receives a copy and never holds up the signing order. `viewer` must open and confirm they reviewed the document, and blocks the order until they do.","enum":["signer","cc","viewer"],"default":"signer"},"order":{"type":"number","description":"Position in the signing order. Signers default to the order set on their template role. For a `cc` this is also *when* the copy is sent: a position after every signer gets the certified final document, while a mid-order position gets the document as it stands at that step.","minimum":1,"example":2}},"required":["roleKey","name"]},"CreateDocumentDto":{"type":"object","properties":{"templateId":{"type":"string","description":"Template UUID to create document from","format":"uuid","example":"550e8400-e29b-41d4-a716-446655440000"},"name":{"type":"string","description":"Custom name for the document","example":"Q4 2025 Contract Agreement","maxLength":255},"message":{"type":"string","description":"Message to include with the signing request","example":"Please review and sign this document at your earliest convenience.","maxLength":5000},"emailSubject":{"type":"string","description":"Replaces the generated subject line on the signing request, the awaiting-your-turn heads-up, and reminders (which stay prefixed with \"Reminder: \"). Completion, decline, void and expiry notices always use their standard wording. Leave unset for the generated subject.","example":"Please sign: Acme MSA","maxLength":120},"routingMode":{"type":"string","description":"Signing-order mode. parallel (default): all recipients are invited at once. sequential: recipients are activated group-by-group by ascending order value — the next group is only activated once the current group has fully signed.","enum":["parallel","sequential"],"default":"parallel"},"reminders":{"description":"Automatic reminder cadence for this document. When omitted, the template's reminder default (or the product default) is used.","allOf":[{"$ref":"#/components/schemas/ReminderSettingsDto"}]},"expiry":{"description":"Signing deadline for this document. When omitted, the template's deadline default (or the product default) is used.","allOf":[{"$ref":"#/components/schemas/ExpirySettingsDto"}]},"recipients":{"description":"Everyone involved with the document, in signing order — those who sign, those who must view and confirm, and those who just receive a copy. At least one must sign or view.","minItems":1,"type":"array","items":{"$ref":"#/components/schemas/CreateDocumentRecipientDto"}}},"required":["templateId","recipients"]},"DocumentRecipientDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"roleKey":{"type":"string","description":"Matches template version recipient key"},"roleName":{"type":"string","description":"Role display name snapshot at send time"},"type":{"type":"string","enum":["signer","cc","viewer"],"description":"What this recipient is asked to do. `signer` fills in fields and signs. `cc` receives a copy and never holds up the signing order. `viewer` must open and confirm they reviewed the document."},"name":{"type":"string"},"email":{"type":"string","description":"Recipient email address"},"color":{"type":"string","description":"Hex colour assigned to this recipient"},"order":{"type":"integer","description":"Signing order (0-based)"},"status":{"type":"string","enum":["pending","signed","acknowledged","copy_sent","declined"],"description":"Each role reaches its own terminal status: a signer `signed`, a viewer `acknowledged`, a CC `copy_sent`. They are never interchangeable."},"signedAt":{"type":"integer","description":"Unix ms timestamp when recipient signed"},"declinedAt":{"type":"integer","description":"Unix ms timestamp when recipient declined"},"declineReason":{"type":"string","description":"Reason for declining"},"signUrl":{"type":"string","description":"URL where this recipient can complete signing"},"lastRemindedAt":{"type":"integer","description":"Unix ms timestamp when recipient was last reminded (manual or automatic)"},"hasActiveSigningLink":{"type":"boolean","description":"Whether this recipient has an active signing link (can currently sign)"}},"required":["id","roleKey","roleName","type","name","color","order","status"]},"DocumentDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"workspaceId":{"type":"string","format":"uuid"},"templateId":{"type":"string","format":"uuid"},"templateVersionId":{"type":"string","format":"uuid"},"ownerId":{"type":"string","description":"Clerk user ID of the document owner"},"name":{"type":"string","description":"Human-readable document name"},"status":{"type":"string","enum":["draft","sent","in_progress","completed","declined","voided","expired"],"description":"Current document lifecycle state"},"routingMode":{"type":"string","enum":["parallel","sequential"],"description":"Signing-order mode"},"message":{"type":"string","description":"Custom message sent with the signing request"},"emailSubject":{"type":"string","description":"Sender's replacement subject for the signing request and reminders. Absent means the generated subject is used."},"createdAt":{"type":"integer","description":"Creation timestamp (Unix ms)"},"sentAt":{"type":"integer","description":"Unix ms when sent"},"completedAt":{"type":"integer","description":"Unix ms when all signed"},"voidedAt":{"type":"integer","description":"Unix ms when voided"},"expiresAt":{"type":"integer","description":"Unix ms when expires/expired"},"documentHash":{"type":"string","description":"SHA-256 fingerprint of the certified final PDF"},"certifiedAt":{"type":"integer","description":"Unix ms when certified"},"reminderEnabled":{"type":"boolean","description":"Whether automatic reminders are enabled"},"reminderIntervalDays":{"type":"integer","description":"Days between automatic reminders"},"reminderMaxCount":{"type":"integer","description":"Max automatic reminders"},"reminderCount":{"type":"integer","description":"Automatic reminders sent so far"},"lastReminderAt":{"type":"integer","description":"Unix ms of the most recent reminder (auto or manual)"},"recipients":{"type":"array","items":{"$ref":"#/components/schemas/DocumentRecipientDto"}},"sendBlockers":{"type":"array","description":"Reasons a draft cannot yet be sent. Empty array when the document is ready to send or is not in draft state.","items":{"type":"object","properties":{"code":{"type":"string","enum":["NO_RECIPIENTS","RECIPIENT_EMAIL_MISSING","CC_EMAIL_MISSING"]},"message":{"type":"string"},"recipientIds":{"type":"array","items":{"type":"string"}}},"required":["code","message"]}}},"required":["id","workspaceId","templateId","templateVersionId","ownerId","name","status","routingMode","createdAt","reminderEnabled","reminderIntervalDays","reminderMaxCount","reminderCount","recipients","sendBlockers"]},"UpdateDocumentDto":{"type":"object","properties":{"name":{"type":"string","description":"Custom name for the document","example":"Q4 2025 Contract Agreement","maxLength":255},"message":{"type":"string","description":"Message to include with the signing request","example":"Please review and sign this document at your earliest convenience.","maxLength":5000},"emailSubject":{"type":"string","description":"Replaces the generated subject line on the signing request, the awaiting-your-turn heads-up, and reminders (which stay prefixed with \"Reminder: \"). Completion, decline, void and expiry notices always use their standard wording. Leave unset for the generated subject.","example":"Please sign: Acme MSA","maxLength":120},"routingMode":{"type":"string","description":"Signing-order mode. parallel (default): all recipients are invited at once. sequential: recipients are activated group-by-group by ascending order value — the next group is only activated once the current group has fully signed.","enum":["parallel","sequential"],"default":"parallel"},"reminders":{"description":"Automatic reminder cadence for this document. When omitted, the template's reminder default (or the product default) is used.","allOf":[{"$ref":"#/components/schemas/ReminderSettingsDto"}]},"expiry":{"description":"Signing deadline for this document. When omitted, the template's deadline default (or the product default) is used.","allOf":[{"$ref":"#/components/schemas/ExpirySettingsDto"}]},"recipients":{"description":"Everyone involved with the document, in signing order — those who sign, those who must view and confirm, and those who just receive a copy. At least one must sign or view.","minItems":1,"type":"array","items":{"$ref":"#/components/schemas/CreateDocumentRecipientDto"}}}},"UrlResponseDto":{"type":"object","properties":{"url":{"type":"string","description":"Presigned download URL","format":"uri"}},"required":["url"]},"AuditEventDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"documentId":{"type":"string","format":"uuid"},"documentRecipientId":{"type":"string","format":"uuid","nullable":true},"type":{"type":"string","description":"Event type","example":"signed"},"actorName":{"type":"string","nullable":true},"actorEmail":{"type":"string","nullable":true},"apiKeyId":{"type":"string","format":"uuid","nullable":true,"description":"API key that performed the action (when applicable)"},"apiKeyName":{"type":"string","nullable":true,"description":"Human-readable name of the API key used (when applicable)"},"ipAddress":{"type":"string","nullable":true},"userAgent":{"type":"string","nullable":true},"metadata":{"type":"object","additionalProperties":true,"nullable":true},"createdAt":{"format":"date-time","type":"string"}},"required":["id","documentId","type","createdAt"]},"SaveDraftDto":{"type":"object","properties":{"values":{"type":"object","description":"Field values as key-value pairs (fieldId -> value)","example":{"field-1":"John Doe","field-2":"john@example.com","field-3":true}}},"required":["values"]},"SubmitSigningDto":{"type":"object","properties":{"values":{"type":"object","description":"Completed field values as key-value pairs (fieldId -> value)","example":{"field-1":"data:image/png;base64,iVBORw0KGgoAAAANS...","field-2":"John Doe","field-3":"2025-05-13"}},"signerName":{"type":"string","description":"Name of the person signing","example":"John Doe"},"signerEmail":{"type":"string","description":"Email of the person signing","example":"john.doe@example.com"},"consentAcceptedAt":{"type":"number","description":"Unix timestamp (milliseconds) when consent was accepted","example":1715607600000}},"required":["values","signerName","consentAcceptedAt"]},"DeclineDto":{"type":"object","properties":{"reason":{"type":"string","description":"Optional reason for declining to sign","example":"I need more time to review"}}},"GalleryRecipientDto":{"type":"object","properties":{"key":{"type":"string","description":"Stable key referenced by `field.recipientKey`"},"name":{"type":"string"},"color":{"type":"string","description":"Hex colour, e.g. `#2563EB`"},"order":{"type":"integer","description":"Signing order (0-based)"}},"required":["key","name","color","order"]},"GalleryTemplateDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"slug":{"type":"string","description":"Stable URL-safe identifier"},"name":{"type":"string"},"description":{"type":"string"},"category":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"fileName":{"type":"string"},"pageCount":{"type":"integer","description":"Page count of the seed PDF. Meaningful only for `sourceKind: 'pdf'` — a document-source template has no fixed page count until it is frozen at publish."},"planTier":{"type":"string"},"sortOrder":{"type":"integer"},"sourceKind":{"type":"string","enum":["pdf","document"],"description":"`document` for content-native rich-text templates, `pdf` for templates built on an uploaded file."},"recipients":{"description":"Signers, with the colours their fields are drawn in.","type":"array","items":{"$ref":"#/components/schemas/GalleryRecipientDto"}},"fieldCount":{"type":"integer","description":"Total fields across all pages"},"previewUrl":{"type":"string","description":"Short-lived presigned URL for the seed PDF. Only present on the detail response and only for `sourceKind: 'pdf'` — document templates are rendered client-side from `content.body`."},"content":{"type":"object","description":"Full normalized template content: recipients, fields, rules, and (for document source) the rich-text body. Detail response only."}},"required":["id","slug","name","description","category","tags","fileName","pageCount","planTier","sortOrder","sourceKind","recipients","fieldCount"]},"InstantiateGalleryTemplateDto":{"type":"object","properties":{}},"CreateWebhookDto":{"type":"object","properties":{"url":{"type":"string","description":"HTTPS URL that Signvoy will POST events to","example":"https://example.com/webhooks/signvoy"},"description":{"type":"string","description":"Human-readable label for this endpoint (shown in the dashboard)","example":"Production webhook"},"enabledEvents":{"description":"Events to subscribe to. Pass an empty array to subscribe to all events.","example":["document.completed","document.declined"],"type":"array","items":{"type":"string"}}},"required":["url","enabledEvents"]},"WebhookEndpointWithSecretDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"workspaceId":{"type":"string","format":"uuid"},"url":{"type":"string","format":"uri","description":"HTTPS URL Signvoy will POST events to"},"description":{"type":"object","description":"Human-readable label"},"enabledEvents":{"description":"Subscribed events. Empty array = all events.","example":["document.completed","document.declined"],"type":"array","items":{"type":"string"}},"status":{"type":"string","enum":["enabled","disabled"]},"consecutiveFailures":{"type":"integer","description":"Count of consecutive delivery failures"},"disabledAt":{"type":"string","format":"date-time","nullable":true},"successRate":{"type":"integer","nullable":true,"description":"Percentage of successful deliveries (null if no deliveries yet)"},"createdAt":{"format":"date-time","type":"string"},"updatedAt":{"format":"date-time","type":"string"},"secret":{"type":"string","description":"HMAC-SHA256 signing secret. Shown only once — store it securely. Use it to verify the `Signvoy-Signature` header on inbound events.","example":"whsec_abc123..."}},"required":["id","workspaceId","url","enabledEvents","status","consecutiveFailures","createdAt","updatedAt","secret"]},"WebhookEndpointDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"workspaceId":{"type":"string","format":"uuid"},"url":{"type":"string","format":"uri","description":"HTTPS URL Signvoy will POST events to"},"description":{"type":"object","description":"Human-readable label"},"enabledEvents":{"description":"Subscribed events. Empty array = all events.","example":["document.completed","document.declined"],"type":"array","items":{"type":"string"}},"status":{"type":"string","enum":["enabled","disabled"]},"consecutiveFailures":{"type":"integer","description":"Count of consecutive delivery failures"},"disabledAt":{"type":"string","format":"date-time","nullable":true},"successRate":{"type":"integer","nullable":true,"description":"Percentage of successful deliveries (null if no deliveries yet)"},"createdAt":{"format":"date-time","type":"string"},"updatedAt":{"format":"date-time","type":"string"}},"required":["id","workspaceId","url","enabledEvents","status","consecutiveFailures","createdAt","updatedAt"]},"UpdateWebhookDto":{"type":"object","properties":{"url":{"type":"string","description":"New HTTPS URL for this endpoint","example":"https://example.com/webhooks/signvoy"},"description":{"type":"string","description":"Human-readable label","example":"Production webhook"},"enabledEvents":{"description":"Events to subscribe to","type":"array","items":{"type":"string"}},"status":{"type":"string","description":"Enable or disable this endpoint","enum":["active","disabled"]}}},"WebhookDeliveryDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"endpointId":{"type":"string","format":"uuid"},"eventType":{"type":"string","description":"Event type","example":"document.completed"},"status":{"type":"string","enum":["pending","success","failed"]},"responseStatus":{"type":"integer","description":"HTTP status code received"},"responseBody":{"type":"object","description":"First 500 chars of response body"},"attempts":{"type":"integer","description":"Delivery attempt count"},"lastAttemptAt":{"format":"date-time","type":"string","description":"Last attempt timestamp"},"createdAt":{"format":"date-time","type":"string"}},"required":["id","endpointId","eventType","status","attempts","lastAttemptAt","createdAt"]},"CreateApiKeyDto":{"type":"object","properties":{"name":{"type":"string","description":"Human-readable label to identify this key in the dashboard","example":"Production server","maxLength":100},"expiresInDays":{"type":"number","description":"Optional expiry, in days from now. Omit for a key that never expires.","example":90,"minimum":1,"maximum":3650}},"required":["name"]},"CreatedApiKeyDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique API key identifier"},"name":{"type":"string","description":"Human-readable label"},"prefix":{"type":"string","description":"Non-secret leading segment of the key","example":"sk_live_x7Kf9aB2"},"last4":{"type":"string","description":"Last 4 characters of the key","example":"a1b2"},"lastUsedAt":{"type":"string","format":"date-time","nullable":true,"description":"When the key was last used to authenticate a request"},"expiresAt":{"type":"string","format":"date-time","nullable":true,"description":"When the key expires (null = never)"},"revoked":{"type":"boolean","description":"Whether the key has been revoked"},"createdAt":{"type":"string","format":"date-time","description":"Creation timestamp"},"key":{"type":"string","description":"The full secret key. Shown only once, at creation — store it securely. Use it as a Bearer token: `Authorization: Bearer sk_live_...`","example":"sk_live_EXAMPLE_only_not_a_real_key"}},"required":["id","name","prefix","last4","revoked","createdAt","key"]},"ApiKeyDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique API key identifier"},"name":{"type":"string","description":"Human-readable label"},"prefix":{"type":"string","description":"Non-secret leading segment of the key","example":"sk_live_x7Kf9aB2"},"last4":{"type":"string","description":"Last 4 characters of the key","example":"a1b2"},"lastUsedAt":{"type":"string","format":"date-time","nullable":true,"description":"When the key was last used to authenticate a request"},"expiresAt":{"type":"string","format":"date-time","nullable":true,"description":"When the key expires (null = never)"},"revoked":{"type":"boolean","description":"Whether the key has been revoked"},"createdAt":{"type":"string","format":"date-time","description":"Creation timestamp"}},"required":["id","name","prefix","last4","revoked","createdAt"]},"CreateEmbedSessionDto":{"type":"object","properties":{"documentId":{"type":"string","format":"uuid","description":"UUID of the document whose signing session to embed"},"documentRecipientId":{"type":"string","format":"uuid","description":"UUID of the specific recipient whose signing view to embed"},"expiresInSeconds":{"type":"integer","minimum":60,"maximum":86400,"default":3600,"description":"How long (seconds) the returned signing URL should remain valid. Defaults to 1 hour. Maximum is 24 hours."}},"required":["documentId","documentRecipientId"]},"EmbedSessionResponseDto":{"type":"object","properties":{"url":{"type":"string","format":"uri","description":"Scoped signing URL for the specified recipient. Embed this in an <iframe> or pass it to the @signvoy/react <SignvoyEmbed /> component. The URL is single-use and expires after the requested window.","example":"https://signvoy.com/sign/abc123?embed=1"},"expiresAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp when this session URL expires"},"documentId":{"type":"string","format":"uuid","description":"Document ID this session belongs to"},"documentRecipientId":{"type":"string","format":"uuid","description":"Recipient ID this session was issued for"}},"required":["url","expiresAt","documentId","documentRecipientId"]},"NotificationDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"workspaceId":{"type":"string","format":"uuid"},"userId":{"type":"string"},"type":{"type":"string","enum":["recipient_signed","document_completed","document_declined","document_expired"]},"title":{"type":"string"},"body":{"type":"string"},"documentId":{"type":"string","format":"uuid","nullable":true},"data":{"type":"object","additionalProperties":true,"nullable":true},"readAt":{"type":"string","format":"date-time","nullable":true},"createdAt":{"format":"date-time","type":"string"}},"required":["id","workspaceId","userId","type","title","body","documentId","data","readAt","createdAt"]},"UnreadCountDto":{"type":"object","properties":{"count":{"type":"number"}},"required":["count"]},"UserDemographicsDto":{"type":"object","properties":{"role":{"type":"string","description":"Professional role"},"useCases":{"description":"Document types the user plans to send","type":"array","items":{"type":"string"}},"monthlyVolume":{"type":"string","enum":["1-5","6-20","21-100","100+"]},"teamSize":{"type":"string","enum":["just_me","2-10","11-50","51-200","200+"]},"referralSource":{"type":"string","description":"How the user heard about Signvoy"},"previousTool":{"type":"string","description":"Tool the user signs documents with today"}}},"UserProfileDto":{"type":"object","properties":{"id":{"type":"string","description":"Clerk user id"},"email":{"type":"string","nullable":true},"name":{"type":"string","nullable":true,"description":"Convenience join of firstName and lastName (\"first last\"), null when both are unset. Not stored — update the parts, not this.","readOnly":true},"firstName":{"type":"string","nullable":true},"lastName":{"type":"string","nullable":true},"imageUrl":{"type":"string","nullable":true,"description":"Clerk avatar URL"},"phone":{"type":"string","nullable":true},"demographics":{"nullable":true,"type":"object","allOf":[{"$ref":"#/components/schemas/UserDemographicsDto"}]},"onboardingStep":{"type":"number","description":"Furthest onboarding step reached"},"onboardingCompletedAt":{"type":"string","format":"date-time","nullable":true},"createdAt":{"format":"date-time","type":"string"},"updatedAt":{"format":"date-time","type":"string"}},"required":["id","email","name","firstName","lastName","imageUrl","phone","demographics","onboardingStep","onboardingCompletedAt","createdAt","updatedAt"]},"UpdateUserDto":{"type":"object","properties":{"demographics":{"$ref":"#/components/schemas/UserDemographicsDto"},"phone":{"type":"string","description":"Contact phone number (optional)"},"onboardingStep":{"type":"number","description":"Furthest onboarding step the user has reached. Only ever moves forward.","minimum":0,"maximum":20},"onboardingCompleted":{"type":"boolean","description":"Set true to stamp onboarding as finished"}}},"UserSignatureDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"source":{"type":"string","enum":["draw","type"],"description":"How the signature was produced"},"dataUrl":{"type":"string","description":"The signature image as a base64 PNG data URL","example":"data:image/png;base64,iVBORw0KGgo..."},"typedName":{"type":"string","nullable":true,"description":"The name that was typed. Only set when source is 'type'."},"fontKey":{"type":"string","nullable":true,"description":"Font the name was rendered in, keyed to SIGNATURE_FONTS. Only set when source is 'type'."},"isDefault":{"type":"boolean","description":"Offered first when signing. At most one per user."},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","source","dataUrl","typedName","fontKey","isDefault","createdAt","updatedAt"]},"CreateUserSignatureDto":{"type":"object","properties":{"source":{"type":"string","enum":["draw","type"],"description":"How the signature was produced"},"dataUrl":{"type":"string","description":"Base64 PNG data URL, up to 256 KB decoded","example":"data:image/png;base64,iVBORw0KGgo..."},"typedName":{"type":"string","description":"The name that was typed. Required when source is 'type'."},"fontKey":{"type":"string","enum":["caveat","dancing-script","sacramento","great-vibes","homemade-apple","allura","parisienne"],"description":"Font the name was rendered in. Required when source is 'type'."},"isDefault":{"type":"boolean","description":"Make this the default. The first signature a user saves becomes the default regardless."}},"required":["source","dataUrl"]},"UpdateUserSignatureDto":{"type":"object","properties":{"isDefault":{"type":"boolean","description":"Pass true to make this the default. Passing false is a no-op: a user always has a default while they have any signatures."},"source":{"type":"string","enum":["draw","type"],"description":"How the replacement image was produced. Required with dataUrl."},"dataUrl":{"type":"string","description":"Replacement image as a base64 PNG data URL. Required with source."},"typedName":{"type":"string","description":"The name that was typed. Required when source is 'type'."},"fontKey":{"type":"string","enum":["caveat","dancing-script","sacramento","great-vibes","homemade-apple","allura","parisienne"],"description":"Font the name was rendered in. Required when source is 'type'."}}},"CreateMcpGrantDto":{"type":"object","properties":{}},"ErrorResponseDto":{"type":"object","properties":{"statusCode":{"type":"number","description":"HTTP status code","example":400},"timestamp":{"type":"string","format":"date-time","description":"When the error occurred (ISO 8601)","example":"2026-07-03T12:34:56.000Z"},"path":{"type":"string","description":"Request path that produced the error","example":"/v1/documents"},"message":{"description":"Human-readable error message. For validation failures this may be an array of field-level messages.","oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"example":"Template not found"}},"required":["statusCode","timestamp","path","message"]}}}}