- Developers
- Reference
API reference
Every endpoint, generated from the same contract the API serves, so it cannot fall behind.
Last updated
Generated from the CecurSign API contract on : 243 operations in 33 groups, from version 1.0 of the contract. For the machine-readable document, call GET /v1/openapi.json with a key that has read access.
Before you read this
Every path below already carries the /v1 prefix, so a request goes to https://cecursign.io/api plus the path. The full base URL is https://cecursign.io/api/v1.
Most endpoints accept an API key as a bearer token. Some are reachable from a signed-in session in the app, and a few need no credential at all: the signing ceremony and public proposal acceptance are opened by the link the recipient was sent. Each entry states which applies. For how to obtain and send a key, read Authentication and API keys.
X-Acting-User
Accepted on every endpoint, so it is stated once here rather than repeated on every one of the 243 below. On an API-key request, the person at the firm whose authority this call carries: a CecurSign user id, or the email address of a user in the same account. Verified against a live user before the request runs. An unknown or cross-account value is rejected with 400, never silently ignored. When present, the audit trail and the certificate of completion name that person alongside the integration; when absent, the act is recorded against the API key alone. Required on keys created with `requireActingUser`, and required by POST /v1/envelopes, which must record an author.
Groups
Analytics
GET/v1/activityTenant activity feed
Reverse-chronological, cursor-paginated, merged from the real signing audit trail and the proposal event log. Deliberately returns NO total: a count over a cursor-paginated union is either a second expensive query or a lie, and the page this replaces displayed a lie (a count over fifty envelopes, labelled "Total Events"). High-frequency per-field and per-email audit rows are filtered out of the feed but remain in the envelope audit trail.
Opens with: API key
Parameters
cursorstringqueryOpaque cursor from the previous page. Encodes the last row timestamp and id, so a feed that gains rows while being read does not skip or repeat.limitnumberquerysubjectsigning | proposalsqueryRestrict to one subject. Omitted means both — and for a caller without proposals, both means signing, because the proposal half is never queried for them.
Responses
200ActivityFeedResponseDto
GET/v1/analytics/exportExport analytics data
Exports analytics data as CSV or JSON for the specified period.
Opens with: API key
Parameters
startDatestringqueryStart date (ISO 8601). Defaults to 30 days ago.endDatestringqueryEnd date (ISO 8601). Defaults to now.formatcsv | jsonqueryExport format: csv or json. Defaults to csv.
Responses
200Exported analytics data403Feature not available on current plan
GET/v1/analytics/funnelGet conversion funnel
Returns the conversion funnel: Sent -> Viewed -> Signed -> Completed with drop-off percentages.
Opens with: API key
Parameters
startDatestringqueryStart date (ISO 8601). Defaults to 30 days ago.endDatestringqueryEnd date (ISO 8601). Defaults to now.
Responses
200FunnelResponseDtoConversion funnel data403Feature not available on current plan
GET/v1/analytics/peak-timesGet peak signing times
Returns signing activity grouped by day-of-week and hour-of-day.
Opens with: API key
Parameters
startDatestringqueryStart date (ISO 8601). Defaults to 30 days ago.endDatestringqueryEnd date (ISO 8601). Defaults to now.tzOffsetMinutesnumberqueryThe caller's Date#getTimezoneOffset() in minutes (UTC+1 sends -60). Defaults to 0 (UTC).
Responses
200PeakTimesResponseDtoPeak signing times data403Feature not available on current plan
GET/v1/analytics/summaryGet analytics summary
Returns high-level KPIs including total sent, completion rate, average time-to-sign, and more.
Opens with: API key
Parameters
startDatestringqueryStart date (ISO 8601). Defaults to 30 days ago.endDatestringqueryEnd date (ISO 8601). Defaults to now.
Responses
200SummaryResponseDtoAnalytics summary403Feature not available on current plan
GET/v1/analytics/team-performanceGet team performance metrics
Returns per-user completion rates, volume, and average time-to-sign. Requires an OWNER or ADMIN session, matching GET /v1/team/members which returns the same roster.
Opens with: API key
Parameters
startDatestringqueryStart date (ISO 8601). Defaults to 30 days ago.endDatestringqueryEnd date (ISO 8601). Defaults to now.
Responses
200TeamPerformanceResponseDtoTeam performance data403Insufficient role, or feature not on current plan
GET/v1/analytics/time-to-signGet time-to-sign distribution
Returns time-to-sign distribution in buckets with percentiles (median, average, p90).
Opens with: API key
Parameters
startDatestringqueryStart date (ISO 8601). Defaults to 30 days ago.endDatestringqueryEnd date (ISO 8601). Defaults to now.
Responses
200TimeToSignResponseDtoTime-to-sign distribution403Feature not available on current plan
GET/v1/analytics/trendsGet envelope trends over time
Returns daily, weekly, or monthly envelope counts (sent and completed) over the specified period.
Opens with: API key
Parameters
startDatestringqueryStart date (ISO 8601). Defaults to 30 days ago.endDatestringqueryEnd date (ISO 8601). Defaults to now.granularityday | week | monthqueryGranularity: day, week, or month. Defaults to day.
Responses
200TrendsResponseDtoTrends data403Feature not available on current plan
API Keys
GET/v1/auth/api-keysList all API keys
Requires an OWNER or ADMIN session.
Opens with: API key
Responses
200ApiKeyListResponseDtoList of API keys401Not authenticated403An API key (a key cannot manage keys), or a role below ADMIN
POST/v1/auth/api-keysCreate a new API key
Requires an OWNER or ADMIN session. A key is tenant-wide and unrestricted by department, so minting one is an escalation for anyone who is not already company-wide.
Opens with: API key
Request body: CreateApiKeyDto
expiresAtstringExpiration date for the API key (optional)namestringRequiredName for the API key (for identification)permissionsREAD | WRITE[]Permissions for the API keyrequireActingUserbooleanRefuse any write on this key that does not name the person it is acting for, via the `X-Acting-User` header (a CecurSign user id or the email address of a user in this account). Recommended for partner integrations that act on behalf of named staff: without it, a machine-driven send is recorded against the key alone, and the audit record is append-only, so the attribution cannot be added afterwards. Defaults to false, which keeps existing integrations working unchanged.
Responses
201CreatedApiKeyResponseDtoAPI key created successfully400Invalid input401Not authenticated403An API key (a key cannot mint a key), a role below ADMIN, or API access not available on the current plan
GET/v1/auth/api-keys/{id}Get API key details
Requires an OWNER or ADMIN session.
Opens with: API key
Parameters
idstringpathRequired
Responses
200ApiKeyResponseDtoAPI key details401Not authenticated403An API key (a key cannot manage keys), or a role below ADMIN404API key not found
DELETE/v1/auth/api-keys/{id}Revoke an API key
Requires an OWNER or ADMIN session. Revoking is a denial of service against every integration the company runs off that key.
Opens with: API key
Parameters
idstringpathRequired
Responses
200MessageResponseDtoAPI key revoked401Not authenticated403An API key (a key cannot manage keys), or a role below ADMIN404API key not found
Authentication
POST/v1/auth/forgot-passwordRequest password reset
Opens with: No credential.
Request body: ForgotPasswordDto
emailstringRequiredUser email address
Responses
200MessageResponseDtoReset email sent if account exists429Too many password reset requests
POST/v1/auth/loginLogin with email and password
Opens with: No credential.
Request body: LoginDto
emailstringRequiredUser email addresspasswordstringRequiredUser password
Responses
200LoginResponseDtoLogin successful401Invalid credentials429Too many login attempts
POST/v1/auth/logoutLogout current session
Opens with: Signed-in session
Responses
200MessageResponseDtoLogged out successfully
GET/v1/auth/meGet current user session, or why there is none
Opens with: Signed-in session
Responses
200Returns current user, or authenticated:false with a reason
POST/v1/auth/me/resend-verificationResend the signed-in user's email-verification link
Opens with: Signed-in session
Responses
200Accepted
POST/v1/auth/registerSelf-registration (deprecated, sign up at cecur.io)
Opens with: No credential.
Responses
410Self-registration is no longer available
POST/v1/auth/resend-verificationResend email verification
Opens with: No credential.
Request body: ResendVerificationDto
emailstringRequiredUser email address
Responses
200MessageResponseDtoVerification email sent if applicable429Too many verification email requests
POST/v1/auth/reset-passwordReset password with token
Opens with: No credential.
Request body: ResetPasswordDto
passwordstringRequiredNew password (min 8 chars, must include uppercase, number, and special character)tokenstringRequiredPassword reset token from email
Responses
200MessageResponseDtoPassword reset successful400Invalid or expired token429Too many password reset attempts
POST/v1/auth/verify-emailVerify email with token
Opens with: No credential.
Request body: VerifyEmailDto
tokenstringRequiredEmail verification token
Responses
200VerifyEmailResponseDtoEmail verified400Invalid or expired token
Billing
GET/v1/billing/addonsVolume packs: what this tenant holds, and what their tier can hold
A pack is capacity bought once and spent only after the plan's own monthly allowance runs out, in whatever month that happens. The figures are ADDITIONS to the plan, not replacement caps.
Opens with: API key
Responses
200AddOnsResponseDtoBalance, offers and grants
POST/v1/billing/addons/checkoutStart Stripe Checkout for a booster pack, via Cecur Home
Opens with: API key
Request body: StartAddOnCheckoutDto
addOnCodestringRequiredBooster code from CecurSign's cataloguequantitynumberHow many packs. Defaults to 1.
Responses
200AddOnCheckoutSessionResponseDtoCheckout session created; redirect the browser to `url`403The booster is not sold on this account's plan503Cecur Home could not be reached
POST/v1/billing/checkoutStart Stripe Checkout for a plan change, via Cecur Home
Opens with: API key
Request body: StartCheckoutDto
billingCyclemonthly | annualRequiredBilling cadenceconfirmedImmediateChargebooleanSet when the customer has been shown the amount quoted by POST /billing/preview and confirmed it.planINDIVIDUAL | PROFESSIONAL | BUSINESS | ENTERPRISERequiredPlan to move to
Responses
200CheckoutSessionResponseDtoCheckout session created; redirect the browser to `url`503Cecur Home could not be reached
GET/v1/billing/entitlementsWhat this tenant can actually use, and why they might want a different plan
Opens with: API key
Responses
200EntitlementsResponseDtoEntitlements
GET/v1/billing/invoicesList this account's Cecur invoices, via Cecur Home
Opens with: API key
Responses
200BillingInvoicesResponseDto
GET/v1/billing/plansGet available plans with pricing (public)
Opens with: No credential.
Responses
200PlansResponseDtoAvailable plans
POST/v1/billing/previewQuote what a plan change costs today, before making it
Opens with: API key
Request body: PreviewPlanChangeDto
billingCyclemonthly | annualRequiredBilling cadence being consideredplanINDIVIDUAL | PROFESSIONAL | BUSINESS | ENTERPRISERequiredPlan being considered
Responses
200PlanChangePreviewDtoWhat the change would do, and what it would cost including VAT503Cecur Home could not be reached
GET/v1/billing/subscriptionGet current plan and subscription details
Opens with: API key
Responses
200SubscriptionDetailsDtoSubscription details
GET/v1/billing/usageGet current usage stats vs plan limits
Opens with: API key
Responses
200UsageStatsDtoUsage statistics
Branding (Public)
GET/v1/book/public/branding/{tenantId}/logoPublic tenant logo (no authentication required)
Opens with: No credential.
Parameters
tenantIduuidpathRequired
Responses
200Logo bytes with image content-type404Tenant has no logo
GET/v1/branding/{tenantId}/logoPublic tenant logo (no authentication required)
Opens with: No credential.
Parameters
tenantIduuidpathRequired
Responses
200Logo bytes with image content-type404Tenant has no logo
Bulk Send
GET/v1/bulk-sendList bulk sends
Get all bulk send operations for your account
Opens with: API key
Responses
200BulkSendListResponseDtoList of bulk sends
POST/v1/bulk-sendCreate a bulk send
Create and start a bulk send operation from a template
Opens with: API key
Request body: CreateBulkSendDto
csvRoleIdstringTemplate role ID that the uploaded recipients fill (the "variable" signer). Defaults to the first SIGNER role on the template when omitted.namestringRequiredName for this bulk send batchrecipientsBulkSendRecipientInput[]RequiredList of recipients (max 500). Each one becomes its own envelope, mapped to the template role identified by csvRoleId.roleAssignmentsBulkSendRoleAssignmentDto[]How the template's OTHER roles are filled on every envelope (fixed recipient or self-sign). Required for any template role that is not csvRoleId.templateIdstringRequiredTemplate ID to use for envelopes
Responses
201BulkSendResponseDtoBulk send created and queued400Invalid request404Template not found
GET/v1/bulk-send/{id}Get bulk send details
Get detailed status of a bulk send operation including recipients
Opens with: API key
Parameters
idstringpathRequired
Responses
200BulkSendDetailResponseDtoBulk send details404Bulk send not found
POST/v1/bulk-send/{id}/cancelCancel a bulk send
Cancel a pending or in-progress bulk send operation
Opens with: API key
Parameters
idstringpathRequired
Responses
200BulkSendResponseDtoBulk send cancelled400Cannot cancel404Bulk send not found
Contact
POST/v1/public/contactSend an enquiry from the public contact form
Emails the enquiry to the address that answers them and stores nothing. Public and unauthenticated: rate limited to 3 per 10 minutes per caller, with a honeypot field and a time-on-form check. Sends no acknowledgement to the enquirer, deliberately, so the endpoint cannot be used to mail an arbitrary address.
Opens with: No credential.
Request body: CreateContactEnquiryDto
companystringThe company they are enquiring forelapsedMsnumberClient-measured time on the form, in millisecondsemailstringRequiredWhere a reply should goenquiryTypeGENERAL | SALES | SECURITY | EXISTING_CUSTOMERWhat the enquiry is about. Steers the subject line; grants nothing.messagestringRequiredThe enquiry itselfnamestringRequiredThe name the enquirer gavewebsitestringAnti-spam honeypot. Must be empty. A filled value is accepted and discarded.
Responses
202ContactEnquiryResponseDtoAccepted for delivery. Also returned for a submission the anti-abuse checks discarded, so an automated caller learns nothing about which check it failed.400Validation failure429Rate limited: try again shortly503The notification system is not configured or was unreachable. The message was NOT sent and the caller is told so.
Contacts
GET/v1/contactsList contacts
Get a paginated list of contacts with optional filtering
Opens with: API key
Parameters
pagenumberquerypageSizenumberquerysearchstringquerycategorystringqueryfavoritesOnlybooleanqueryincludeArchivedbooleanqueryInclude archived contacts (excluded by default)engagementLIVE | ENDING_SOON | LAPSED | WITH_CLIENT | UNATTENDEDqueryNarrow to one derived engagement bucket. Each bucket carries its own ordering — ENDING_SOON soonest-first, LAPSED/UNATTENDED most-recently-lapsed first, WITH_CLIENT longest-held first — because that is the only ordering that makes each one actionable and it is the one key every row in the bucket is guaranteed to have.
Responses
200Returns paginated contacts list
POST/v1/contactsCreate contact
Create a new contact
Opens with: API key
Request body: CreateContactDto
addressstringPostal address (resolves the {{clientAddress}} proposal merge token)categoriesstring[]Category tags for the contactcompanystringCompany nameemailstringRequiredContact email addressisFavoritebooleanMark as favoritenamestringContact namenotesstringNotes about the contactphonestringContact phone number
Responses
201Contact created409Contact with this email already exists
GET/v1/contacts/{id}Get contact details
Get a single contact with detailed stats
Opens with: API key
Parameters
idstringpathRequiredContact ID
Responses
200Returns contact details404Contact not found
PATCH/v1/contacts/{id}Update contact
Update an existing contact
Opens with: API key
Parameters
idstringpathRequiredContact ID
Request body: UpdateContactDto
addressstringPostal address (resolves the {{clientAddress}} proposal merge token)categoriesstring[]Category tags for the contactcompanystringCompany nameemailstringContact email addressisFavoritebooleanMark as favoritenamestringContact namenotesstringNotes about the contactphonestringContact phone number
Responses
200Contact updated404Contact not found409Contact with this email already exists
DELETE/v1/contacts/{id}Delete or archive a contact
Deletes the contact outright when it has no proposal or envelope history. When it does, the contact is ARCHIVED instead — hidden from the default list and restorable — because Proposal.contactId is a required Restrict foreign key. Check `outcome` on the response to tell which happened.
Opens with: API key
Parameters
idstringpathRequiredContact ID
Responses
200DeleteContactResponse404Contact not found
GET/v1/contacts/{id}/documentsGet every document this client has been sent
Derived from the envelopes the contact is a recipient of and the proposals whose engagement letter was rendered for them — there is no manual document-to-client link to go stale. This is where rendered engagement letters live now that they are excluded from the company-wide document library.
Opens with: API key
Parameters
idstringpathRequiredContact IDpagenumberquerypageSizenumberquery
Responses
200Returns the client’s documents, newest first404Contact not found
GET/v1/contacts/{id}/envelopesGet contact envelope history
Get all envelopes associated with a contact
Opens with: API key
Parameters
idstringpathRequiredContact IDpagenumberquerypageSizenumberquery
Responses
200Returns envelope history404Contact not found
POST/v1/contacts/{id}/favoriteToggle favorite
Toggle the favorite status of a contact
Opens with: API key
Parameters
idstringpathRequiredContact ID
Responses
200Favorite status toggled404Contact not found
POST/v1/contacts/{id}/restoreRestore an archived contact
Clears the archived flag, returning the contact to the default list. No-op if it is not archived.
Opens with: API key
Parameters
idstringpathRequiredContact ID
Responses
201ContactResponse404Contact not found
GET/v1/contacts/categoriesGet all categories
Get all unique categories used across contacts
Opens with: API key
Responses
200Returns list of category names
GET/v1/contacts/companiesGet all companies
Get every distinct company name in use, de-duplicated case-insensitively. Backs the company autocomplete on the contact form.
Opens with: API key
Responses
200Returns list of company names
GET/v1/contacts/favoritesGet favorite contacts
Get all contacts marked as favorites
Opens with: API key
Responses
200Returns favorite contacts
POST/v1/contacts/importImport contacts from a parsed CSV
Accepts rows already parsed by the client (same contract shape as bulk-send). Send `dryRun: true` for the preview step — it reports per-row validation and whether each row creates or updates, without writing. Committing upserts by (tenant, email) rather than failing on duplicates.
Opens with: API key
Request body: ImportContactsDto
dryRunbooleanWhen true, validate and report what WOULD happen without writing anything. Powers the preview step.rowsImportContactRowDto[]RequiredParsed CSV rows, in file order
Responses
201ImportContactsResponse
GET/v1/contacts/out-of-contractClients out of contract with nothing in flight
Every accepted engagement has ended and no proposal is open. The renewal nudge already exists as an overnight email from the proposal sweeps; this is the same fact, in the app, on the day somebody can act on it. Most recently lapsed first. Archived clients are excluded, because archiving is how a company says the relationship is over.
Opens with: API key
Parameters
limitnumberqueryMax rows (default 5, cap 50)
Responses
200OutOfContractResponse
GET/v1/contacts/recentGet recent recipients
Get recently used recipient emails from envelope history
Opens with: API key
Parameters
limitnumberqueryMax results (default 10)
Responses
200Returns recent recipients
GET/v1/contacts/searchSearch contacts
Search contacts and recent recipients for autocomplete. Returns both saved contacts and recipients from envelope history.
Opens with: API key
Parameters
qstringqueryRequiredSearch query (min 2 chars)limitnumberqueryMax results (default 10)
Responses
200Returns matching contacts and recipients
GET/v1/contacts/statsEngagement counts for the Clients list tile row
Live / Ending soon / Lapsed / With the client, derived from proposals and never stored. Honours search, category, favourites and archived so the tiles caption the same population the list shows; ignores the engagement filter itself.
Opens with: API key
Parameters
searchstringquerycategorystringqueryfavoritesOnlybooleanqueryincludeArchivedbooleanquery
Responses
200ContactStatsResponse
Dashboard
GET/v1/dashboard/layoutGet the current user's Home layout
Returns the widget list in render order. A user who has never customised Home gets a default generated from their tenant's current entitlements, with `customised: false` and nothing written to the database.
Opens with: API key
Responses
200DashboardLayoutResponseDto
PUT/v1/dashboard/layoutReplace the current user's Home layout
Takes the complete widget list in render order and replaces what was stored. Creates the row on first save. Unknown widget ids are rejected by validation; duplicates are dropped rather than rejected, so a client bug cannot cost the user the rest of an arrangement they just built.
Opens with: API key
Request body: UpdateDashboardLayoutDto
showQuickStartbooleanWhether the quick start row above the grid is shown. Optional so a client built before the flag existed keeps saving layouts; absent means shown.widgetsHomeWidgetPlacementDto[]RequiredThe complete widget list, IN RENDER ORDER. A full replacement, not a patch: the client holds the whole arrangement while the user drags it around, and a partial update would need an ordering protocol to express "this moved above that". An EMPTY array is legal — a user is allowed to clear their Home, and the picker is how they put it back.
Responses
200DashboardLayoutResponseDto400Unknown widget id, or too many widgets
DELETE/v1/dashboard/layoutReset the current user's Home layout to the product default
Deletes the saved arrangement and returns the freshly generated default. The user goes back to tracking entitlement changes automatically, exactly as before they customised.
Opens with: API key
Responses
200DashboardLayoutResponseDto
GET/v1/dashboard/summaryHome dashboard summary
Counts and the attention list that Home renders above its recent lists. Deliberately NOT gated on the analyticsAccess plan feature: these are operational counts of the tenant's own work, not reporting, and Home is the first screen every user sees on every plan. The proposals block is omitted entirely — not zero-filled — for callers who cannot use proposals.
Opens with: API key
Parameters
tzOffsetMinutesnumberqueryThe caller's Date#getTimezoneOffset() in minutes (UTC+1 sends -60). Used only to decide which local day each timestamp falls in for the weekly chart. Defaults to 0 (UTC).
Responses
200DashboardSummaryResponseDto
Departments
GET/v1/departmentsList the company’s departments
Active only by default. Open to any authenticated user: the web labels with these.
Opens with: API key
Parameters
includeInactivestringqueryRequired
Responses
200DepartmentListResponse
POST/v1/departmentsCreate a department
Opens with: API key
Request body: CreateDepartmentDto
descriptionstringnamestringRequired
Responses
201DepartmentResponse409A department with that name already exists
PATCH/v1/departments/{id}Rename, re-describe, or retire a department
Opens with: API key
Parameters
idstringpathRequired
Request body: UpdateDepartmentDto
descriptionstringisActivebooleanSet false to retire a department without deleting its history.namestring
Responses
200DepartmentResponse400The default department cannot be deactivated404Department not found
DELETE/v1/departments/{id}Retire a department
Soft — sets isActive=false and keeps membership, so reactivating restores the previous state. A department that has issued work cannot be hard-deleted without deciding what happens to that work, which is not a decision a delete button should take.
Opens with: API key
Parameters
idstringpathRequired
Responses
200DepartmentResponse400The default department cannot be deactivated404Department not found
GET/v1/departments/assignableDepartments the caller may file new work into
What the create-time picker offers. Narrower than GET /departments, which is every department in the company for labelling: this answers where THIS caller may put a new envelope or proposal, and is resolved by the same function the create endpoints validate against, so the picker cannot offer a choice the API would reject. When singleChoice is true the picker should not be rendered — there is no decision to make.
Opens with: API key
Responses
200AssignableDepartmentsResponse
GET/v1/departments/meThe caller’s own visibility scope and departments
Returns the EFFECTIVE scope, not the stored column: OWNER and ADMIN always read TENANT. An API-key caller has no user and is tenant-wide, so it reports TENANT with no departments.
Opens with: API key
Responses
200MyScopeResponse
GET/v1/departments/rosterEvery user in the company with their scope and departments
Deleted users are excluded; suspended users are not.
Opens with: API key
Responses
200DepartmentRosterResponse
PUT/v1/departments/roster/{userId}Set one user’s visibility scope and department membership
Membership is replaced wholesale by departmentIds, so the stored state always matches what the admin had on screen. DEPARTMENT scope with no departments is rejected — it would produce a user who sees nothing, with no explanation.
Opens with: API key
Parameters
userIdstringpathRequiredThe user whose scope is being set
Request body: SetUserScopeDto
departmentIdsstring[]Replaces this user’s membership wholesale. Required and non-empty when visibilityScope is DEPARTMENT; ignored otherwise but still applied, so switching a user between DEPARTMENT and TENANT does not lose where they sit.visibilityScopeOWN | DEPARTMENT | TENANTRequiredTENANT = the whole company (today’s behaviour for everyone). DEPARTMENT = only the departments listed below. OWN = only what this user created.
Responses
200DepartmentRosterMemberResponse400Empty department list, or a department not in this company404No such user in this tenant
Document Categories
GET/v1/document-categoriesList document categories
The company-wide shelves for the document library. `documentCount` is scoped to what the caller can see and is intended for the management drawer on the Documents screen. Filter chips carry no count.
Opens with: API key
Responses
200DocumentCategoryResponseDto[]
POST/v1/document-categoriesCreate a document category
Opens with: API key
Request body: CreateDocumentCategoryDto
colorstringHex colour for the chip. Omit for the neutral tone.namestringRequiredCategory name, unique within the companysortOrdernumberDisplay order, ascending
Responses
201DocumentCategoryResponseDto409A category with this name already exists
PATCH/v1/document-categories/{id}Rename, recolour or reorder a category
Opens with: API key
Parameters
idstringpathRequired
Request body: UpdateDocumentCategoryDto
colorstringNew hex colournamestringNew namesortOrdernumberNew display order
Responses
200DocumentCategoryResponseDto404Category not found409A category with this name already exists
DELETE/v1/document-categories/{id}Delete a category
Documents in it are NOT deleted — they return to "Uncategorised". No in-use guard, because nothing is lost by deleting a shelf.
Opens with: API key
Parameters
idstringpathRequired
Responses
204Deleted404Category not found
Document copies
GET/v1/copies/{token}What a copy link points at, and record that it was opened
Returns only what the reader needs in order to decide whether to open the document: the subject, who sent it, the firm, their note and when the link expires. No envelope id, no recipient list and no signer names: the reader was not a party to the document. The FIRST open of a link writes one audit event; later opens move a counter on the row.
Opens with: No credential.
Parameters
tokenstringpathRequiredThe token from the emailed link
Responses
200PublicCopyResponseDto404The link is not valid410The link was withdrawn, has expired, or the document is no longer available
GET/v1/copies/{token}/documentDownload the signed document behind a copy link
Served through our API and re-verified against the hash recorded for the document, like every other route that hands over a signed PDF. A mismatch is refused rather than served, because nobody can tell from here whether the file or the record is wrong.
Opens with: No credential.
Parameters
tokenstringpathRequiredThe token from the emailed link
Responses
200The signed PDF404The link is not valid410The link was withdrawn or has expired
Document packs
GET/v1/document-packsList the firm's document packs
A pack is a named set of supporting items a template can carry, so a firm sending its onboarding bundle picks one thing instead of five. Firm-wide, like the templates that point at them.
Opens with: API key
Responses
200DocumentPackListResponseDto
POST/v1/document-packsCreate a document pack
Items follow exactly the same rules as a supporting document: a library document or an https link to the firm's own site, never both. A library document is verified against the caller's tenant and department scope.
Opens with: API key
Request body: CreateDocumentPackDto
descriptionstringA note to the colleague choosing between packs. Never shown to a recipient.itemsDocumentPackItemInputDto[]The items, in the order the recipient will read them. May be empty.namestringRequiredWhat the firm calls this pack. Unique within the firm.
Responses
201DocumentPackResponseDto400An item gave both or neither source, or a non-https link404One of the documents was not found409A pack with this name already exists
GET/v1/document-packs/{packId}Read one document pack and its items
Opens with: API key
Parameters
packIdstringpathRequiredDocument pack id
Responses
200DocumentPackResponseDto404Document pack not found
PATCH/v1/document-packs/{packId}Rename a pack, or replace its items
Sending `items` replaces the whole list; omitting it leaves the items alone. Editing a pack never changes an envelope or a proposal already created from it: the items were copied at instantiation, not referenced.
Opens with: API key
Parameters
packIdstringpathRequiredDocument pack id
Request body: UpdateDocumentPackDto
descriptionstringA note to the colleague choosing between packsitemsDocumentPackItemInputDto[]The complete new item list, in order. Sent, it REPLACES every item on the pack; omitted, the items are left alone. Editing a pack never reaches an envelope or a proposal that has already been created from it: materialisation copies, it does not reference.namestringWhat the firm calls this pack
Responses
200DocumentPackResponseDto400An item gave both or neither source, or a non-https link404Document pack, or one of the documents, not found409A pack with this name already exists
DELETE/v1/document-packs/{packId}Delete a document pack
Templates pointing at it simply stop carrying a pack, and every envelope ever built from one keeps the copy it was given. No document is deleted.
Opens with: API key
Parameters
packIdstringpathRequiredDocument pack id
Responses
204Deleted404Document pack not found
POST/v1/document-packs/{packId}/itemsAdd one item to a document pack
Opens with: API key
Parameters
packIdstringpathRequiredDocument pack id
Request body: AddDocumentPackItemDto
descriptionstringOne line of context, shown under the labeldocumentIdstringA document from this firm's library. Verified against the caller's tenant AND their department scope before it is accepted. Mutually exclusive with externalUrl.externalUrlstringAn https URL the firm already publishes on its own site. Mutually exclusive with documentId. An external link is never tracked: the request goes to the firm's own server and never reaches us.labelstringRequiredWhat the recipient sees. Not the filename: "Our privacy policy" reads better to a client than "privacy-policy-v4-FINAL.pdf".ordernumberPosition in the list. Omit and it is appended to the end.
Responses
201DocumentPackResponseDto400Both or neither source given, or a non-https link404Document pack or document not found
DELETE/v1/document-packs/{packId}/items/{itemId}Remove one item from a document pack
Opens with: API key
Parameters
packIdstringpathRequiredDocument pack iditemIdstringpathRequiredPack item id
Responses
200DocumentPackResponseDto404Document pack or item not found
PUT/v1/document-packs/{packId}/items/orderReorder a pack's items
Takes every item id on the pack, in the order they should appear. A partial list is refused rather than applied.
Opens with: API key
Parameters
packIdstringpathRequiredDocument pack id
Request body: ReorderDocumentPackItemsDto
idsstring[]RequiredEvery item id on this pack, in the order they should appear. Ids that do not belong here are refused rather than ignored.
Responses
200DocumentPackResponseDto400The list is incomplete, or names something else404Document pack not found
Documents
GET/v1/documentsList all documents
Opens with: API key
Parameters
pagenumberqueryPage number (1-indexed)pageSizenumberqueryPage sizestatusPROCESSING | READY | ERROR | DELETEDqueryFilter by statussearchstringquerySearch by namesortrecent | newest | oldest | name-asc | name-descqueryResult ordering. Exists because the pickers sort, and they now paginate server-side: sorting a single page in the browser reorders ten rows out of a hundred and calls it sorted. `recent` puts the documents that last went out first (most recent send first), then the never-sent ones newest first. It is what a picker wants at the top: the letter that goes out weekly, not whatever was uploaded last.includeArchivedbooleanqueryInclude archived documents. Off by default — archiving exists to get superseded editions out of the way, so they have to be out of the way unless asked for.categoryIdstringqueryFilter to one category. The literal string `none` returns UNCATEGORISED documents — a real chip in the UI, and not expressible as a UUID.
Responses
200DocumentListResponseDtoList of documents401Unauthorized
POST/v1/documentsUpload a new document
Opens with: API key
Request body
filebinaryRequiredDocument file (PDF, DOCX, PNG, JPEG)namestringCustom document name (optional)
Responses
201UploadResponseDtoDocument uploaded successfully400Invalid file type or size401Unauthorized
GET/v1/documents/{id}Get document by ID
Opens with: API key
Parameters
idstringpathRequiredDocument ID
Responses
200DocumentResponseDtoDocument details404Document not found
PATCH/v1/documents/{id}Rename, archive or restore a document
Archiving hides a document from the library and both pickers. It does NOT detach it from envelopes or templates that already carry it, and a draft that already references it still sends.
Opens with: API key
Parameters
idstringpathRequiredDocument ID
Request body: UpdateDocumentDto
archivedbooleanArchive (true) or restore (false). Archiving hides the document from the library and both pickers; it does not detach it from envelopes or templates that already carry it.categoryIdobjectMove to a category. Pass `null` to return it to Uncategorised. Omit to leave unchanged.namestringNew display name
Responses
200DocumentResponseDtoUpdated document400Empty name404Document not found
DELETE/v1/documents/{id}Delete a document
Opens with: API key
Parameters
idstringpathRequiredDocument ID
Responses
204Document deleted403Document is in use404Document not found
GET/v1/documents/{id}/contentGet document content for viewing (inline)
Opens with: API key
Parameters
idstringpathRequiredDocument ID
Responses
200Document file stream for inline viewing404Document not found
GET/v1/documents/{id}/detailsGet document with pages and fields
Opens with: API key
Parameters
idstringpathRequiredDocument ID
Responses
200DocumentWithPagesDtoDocument with pages and fields404Document not found
GET/v1/documents/{id}/downloadDownload document content
Opens with: API key
Parameters
idstringpathRequiredDocument ID
Responses
200Document file stream404Document not found
GET/v1/documents/{id}/fieldsGet fields for a document scoped to a template or envelope.
Opens with: API key
Parameters
idstringpathRequiredDocument ID
Responses
200FieldResponseDto[]Scoped fields400Missing or conflicting scope404Document not found
POST/v1/documents/{id}/fieldsAdd a field to document (scoped to a template or envelope)
Opens with: API key
Parameters
idstringpathRequiredDocument ID
Request body: CreateFieldDto
gridGroupIdstringGrid group this field belongs to (client-supplied GridGroup id).groupKeystringGroup key for RADIO fields. All options in the same radio group share this key.heightnumberRequiredHeight (0-1 normalized)labelstringField labellinkKeystringLink key for Field Linking. Fields sharing a non-null linkKey propagate values to one another during signing. Distinct from groupKey (RADIO grouping).multilinebooleanFor TEXT fields: allow multiple lines. Signers get a textarea and the completed PDF word-wraps the value instead of shrinking it onto one line.optionLabelstringOption label/value for RADIO fields (the text shown next to the option).pageNumbernumberRequiredPage number (1-indexed)placeholderstringPlaceholder textrecipientRolestringRecipient role this field is assigned torequiredbooleanIs field requiredtypeSIGNATURE | INITIALS | DATE_SIGNED | TEXT | CHECKBOX | DROPDOWN | ATTACHMENT | RADIO | DATERequiredField typevalidationobjectValidation rules (JSON)widthnumberRequiredWidth (0-1 normalized)xnumberRequiredX position (0-1 normalized)ynumberRequiredY position (0-1 normalized)
Responses
201FieldResponseDtoField created400Invalid field data or missing/conflicting scope404Document not found
PUT/v1/documents/{id}/fields/{fieldId}Update a field within a template or envelope scope
Opens with: API key
Parameters
idstringpathRequiredDocument IDfieldIdstringpathRequiredField ID
Request body: UpdateFieldDto
groupKeystringGroup key for RADIO fieldsheightnumberHeight (0-1 normalized)labelstringField labelmultilinebooleanFor TEXT fields: allow multiple lines (textarea + word-wrapped PDF output).optionLabelstringOption label for RADIO fieldsplaceholderstringPlaceholder textrecipientRolestringRecipient rolerequiredbooleanIs field requiredwidthnumberWidth (0-1 normalized)xnumberX position (0-1 normalized)ynumberY position (0-1 normalized)
Responses
200FieldResponseDtoField updated404Document or field not found
DELETE/v1/documents/{id}/fields/{fieldId}Delete a field within a template or envelope scope
Opens with: API key
Parameters
idstringpathRequiredDocument IDfieldIdstringpathRequiredField ID
Responses
204Field deleted404Document or field not found
POST/v1/documents/{id}/fields/bulkAdd multiple fields to a document (scoped to a template or envelope)
Opens with: API key
Parameters
idstringpathRequiredDocument ID
Request body: BulkCreateFieldsDto
fieldsCreateFieldDto[]RequiredArray of fields to create
Responses
201FieldResponseDto[]Fields created400Invalid field data or missing/conflicting scope404Document not found
POST/v1/documents/{id}/fields/replaceAtomically replace fields for a (document, owner) pair. Editors call this with the full desired field set.
Opens with: API key
Parameters
idstringpathRequiredDocument ID
Request body: ReplaceFieldsDto
envelopeIdstringEnvelope that owns these field placementsfieldsCreateFieldDto[]RequiredFull set of fields to persist for this (document, owner) pair. Replaces existing.gridGroupsGridGroupDto[]Full set of grid groups to persist for this (document, owner) pair. Replaces existing.templateIdstringTemplate that owns these field placements
Responses
200FieldResponseDto[]Fields after replacement400Invalid scope or field data404Document/template/envelope not found
GET/v1/documents/{id}/grid-groupsGet grid groups for a document scoped to a template or envelope.
Opens with: API key
Parameters
idstringpathRequiredDocument ID
Responses
200GridGroupResponseDto[]Scoped grid groups400Missing or conflicting scope404Document not found
GET/v1/documents/{id}/pages/{pageNumber}Get page thumbnail URL
Opens with: API key
Parameters
idstringpathRequiredDocument IDpageNumbernumberpathRequiredPage number (1-indexed)
Responses
200objectPage thumbnail URL400Invalid page number404Document not found
POST/v1/documents/bulk-archiveArchive or restore several documents
The same as `archived` on PATCH /documents/:id, for a set. Archiving hides a document from the library and both pickers and detaches it from nothing. Not all or nothing: each id gets an outcome (ARCHIVED, RESTORED, UNCHANGED or NOT_FOUND) and the rest go through.
Opens with: API key
Request body: BulkArchiveDocumentsDto
archivedbooleanRequired`true` archives (hides from the library and both pickers), `false` restores.documentIdsstring[]RequiredDocuments to archive or restore, at most 100.
Responses
200BulkArchiveDocumentsResponseDto400An invalid list
POST/v1/documents/bulk-categoriseFile several documents under one category
Moves every listed document to the category, or to Uncategorised with `categoryId: null`. All or nothing: if any document is not found or not editable by the caller, nothing moves.
Opens with: API key
Request body: BulkCategoriseDocumentsDto
categoryIdstringRequiredThe category to file them under, or `null` for Uncategorised.documentIdsstring[]RequiredDocuments to move, at most 100. All or nothing: one id the caller cannot edit refuses the whole call.
Responses
200BulkCategoriseDocumentsResponseDto400Category not found, or an invalid list404One or more documents not found
GET/v1/documents/recentRecently used documents
The documents this caller reached for most recently, newest first — the ranking signal a picker actually wants. READY uploads only; drafts do not count as use.
Opens with: API key
Responses
200DocumentResponseDto[]Recently used documents, most recent first401Unauthorized
Envelopes
GET/v1/envelopesList envelopes
Opens with: API key
Parameters
cursorstringqueryCursor for pagination (use nextCursor from previous response)limitnumberqueryNumber of items to return (max 100)pagenumberqueryDeprecatedPage number (1-indexed) - DEPRECATED: Use cursor pagination insteadpageSizenumberqueryDeprecatedPage size - DEPRECATED: Use limit insteadstatusDRAFT | SENT | VIEWED | PARTIALLY_SIGNED | COMPLETED | DECLINED | VOIDED | EXPIRED[]queryFilter by status. Repeat the parameter for several — `?status=SENT&status=VIEWED`. Matches any of the given statuses.searchstringquerySearch by subject or recipient emailfolderIdstringqueryFilter by folder ID. Use "null" to get envelopes not in any folder.tagstringqueryFilter by tag. Use "null" for envelopes with no tag.createdFromdate-timequeryOnly envelopes created on or after this instant.createdTodate-timequeryOnly envelopes created on or before this instant.contactEmailstringqueryFilter to envelopes with a recipient at this email address (case-insensitive). This is how a client is matched — recipients carry no contact ID.sourceDIRECT | PROPOSAL_ACCEPTANCE | SELF_SIGNqueryFilter by provenance. `DIRECT` for envelopes composed in the app, `PROPOSAL_ACCEPTANCE` for those spawned by a client accepting a proposal, `SELF_SIGN` for documents the sender signed alone. Omit for `DIRECT` only: `SELF_SIGN` was never sent to anybody, and `PROPOSAL_ACCEPTANCE` belongs to its proposal, so both are excluded unless named.includeAllbooleanqueryInclude every source, hidden ones too. Ignored when `source` is given. Omit for the default, which is `DIRECT` only.mineOnlybooleanqueryOnly envelopes created by the calling user. Narrows the caller's existing scope and never widens it.
Responses
200EnvelopeListResponseDtoReturns list of envelopes
POST/v1/envelopesCreate a new envelope
Opens with: API key
Parameters
Idempotency-KeystringheaderUnique key for idempotent requests
Request body: CreateEnvelopeDto
departmentIdstringWhich department this envelope belongs to. Omit and it resolves from the creator: their single department, or the company-wide default when they belong to none or several. Must be one the caller may file into (GET /v1/departments/assignable) — anything else is a 400, never a silent fallback.documentIdsstring[]RequiredDocument IDs to include in the envelopeexpiresAtdate-timeA fixed expiration date. Give at most one of expiresAt, expiryDays and neverExpires; give none for the standard 30 days after sending.expiryDaysnumberExpire this many days after the envelope is SENT, not after it is created.folderIdstringFolder ID to assign the envelope toidempotencyKeystringIdempotency key for this request. Can also be provided via the Idempotency-Key header.messagestringMessage to recipientsmetadataobjectCustom metadataneverExpiresbooleanThe envelope never expires. It stays open until completed or voided; signing links still lapse after 30 days and a reminder issues a fresh one.recipientsCreateRecipientDto[]RequiredRecipients of the envelopesigningOrderPARALLEL | SEQUENTIALSigning order typesubjectstringRequiredEnvelope subject (email subject line)templateIdstringTemplate ID if creating from a template
Responses
201EnvelopeDetailResponseDtoEnvelope created successfully400Invalid input, or an API-key request that does not name the person it is acting for (`ACTING_USER_REQUIRED`) — see the X-Acting-User header404Document(s) not found
GET/v1/envelopes/{id}Get envelope by ID
Opens with: API key
Parameters
idstringpathRequired
Responses
200EnvelopeDetailResponseDtoReturns envelope details404Envelope not found
PATCH/v1/envelopes/{id}Edit a draft envelope
Change a DRAFT in place: subject, message, signing order, expiry, folder, and the complete set of documents and recipients (each replaces the current set when present; an omitted field is unchanged). A recipient row that names the id it replaces and omits its access code keeps the stored one. Field placements are edited through the document field routes with envelopeId. Only a DRAFT accepts this; a sent envelope is voided or resent, and a proposal acceptance or self-sign envelope is refused.
Opens with: API key
Parameters
idstringpathRequired
Request body: UpdateEnvelopeDto
documentIdsstring[]The complete, ordered set of document IDs. Replaces the current set.expiresAtobjectA fixed expiration date. Naming any of expiresAt, expiryDays or neverExpires replaces the whole expiry choice; omit all three to leave it unchanged. Null clears.expiryDaysnumberExpire this many days after sending. Null clears.folderIdobjectFolder to file the envelope into. Null removes it from its folder.messagestringMessage to recipients. An empty string clears it.neverExpiresbooleanThe envelope never expires.recipientsUpdateRecipientDto[]The complete set of recipients. Replaces the current set.signingOrderPARALLEL | SEQUENTIALSigning order typesubjectstringEnvelope subject (email subject line)
Responses
200EnvelopeDetailResponseDtoDraft updated400Invalid input, or a document is not ready404Envelope, document or folder not found409The envelope is not a draft, or cannot be edited
DELETE/v1/envelopes/{id}Delete a draft envelope
Opens with: API key
Parameters
idstringpathRequired
Responses
204Envelope deleted successfully404Envelope not found409Only draft envelopes can be deleted
GET/v1/envelopes/{id}/auditGet envelope audit trail
Opens with: API key
Parameters
idstringpathRequired
Responses
200Returns chronological audit events with hash chain verification404Envelope not found
GET/v1/envelopes/{id}/audit/anchorsList and verify timestamp anchors
Every batch this envelope is included in, the providers that timestamped each batch, and a re-verification of the inclusion proof against the root those providers signed. An envelope with no anchors returns anchored:false and verified:true — absence of an anchor is an absent claim, not a failed check, and every envelope completed before 2026-08-06 has none.
Opens with: API key
Parameters
idstringpathRequired
Responses
200Anchor verification results404Envelope not found
GET/v1/envelopes/{id}/audit/verifyVerify audit chain integrity
Verifies the hash chain, cross-checks any timestamp anchor against the chain as it stands now, and reconciles the trail against the envelope and recipient statuses recorded outside it. Returns the breakpoint on a hash failure and a typed finding for each contradiction. An unanchored envelope is verified:true with anchored:false — absence of an anchor is an absent claim, not a failed check.
Opens with: API key
Parameters
idstringpathRequired
Responses
200Chain verification result404Envelope not found
GET/v1/envelopes/{id}/certificateGet certificate of completion
Opens with: API key
Parameters
idstringpathRequired
Responses
200Returns PDF certificate404Envelope not found
GET/v1/envelopes/{id}/copiesEvery copy sent for this envelope
Who it went to, when, whether it has been opened and whether it is still live. This is the half of the feature a PDF on a shared drive can never give you.
Opens with: API key
Parameters
idstringpathRequired
Responses
200EnvelopeCopyListResponseDto404Envelope not found
POST/v1/envelopes/{id}/copiesEmail a copy of a completed document to one address
Mints a scoped, expiring, revocable link and emails it. The document is never attached and the link is never a storage URL: every read is served through our own API, recorded against the link, and refused once the link is withdrawn or the envelope is voided. Session callers only.
Opens with: API key
Parameters
idstringpathRequired
Request body: SendEnvelopeCopyDto
emailstringRequiredThe one address this copy is for. One per send, deliberately: a send is an act with a named recipient, and a list would make withdrawing it all-or-nothing.notestringAn optional line from the sender, shown in the email as an attributed quote under their name. It is NOT an email body: URLs are stripped and the text is capped before it is stored, because the email already carries exactly one destination and a second one inside a sender's words is indistinguishable from ours to the reader.
Responses
201EnvelopeCopyResponseDto400Invalid address, or this firm has reached its hourly limit for copies403An API key cannot send a copy404Envelope not found409The envelope is not completed, or its signed document is not ready yet
POST/v1/envelopes/{id}/copies/{copyId}/revokeWithdraw a copy link
The link stops working immediately. Withdrawing one that is already withdrawn is not an error, so pressing the button twice on a stale list does not report a failure over a link that is in exactly the state the caller asked for.
Opens with: API key
Parameters
idstringpathRequiredcopyIdstringpathRequired
Responses
200The link is withdrawn404Envelope or copy link not found
GET/v1/envelopes/{id}/documents/{documentId}/downloadDownload envelope document content
Opens with: API key
Parameters
idstringpathRequireddocumentIdstringpathRequired
Responses
200Document file stream404Envelope or document not found
GET/v1/envelopes/{id}/download/signed-mergedDownload all signed documents merged into a single PDF
Returns the envelope-level merged signed PDF (all documents combined).
Opens with: API key
Parameters
idstringpathRequired
Responses
200Merged signed PDF stream404Envelope not completed yet
POST/v1/envelopes/{id}/duplicateDuplicate an envelope
Create a copy of an envelope as a new draft with the same documents and recipients
Opens with: API key
Parameters
idstringpathRequired
Responses
201DuplicateEnvelopeResponseDtoEnvelope duplicated successfully400Invalid input, or an API-key request that does not name the person it is acting for (`ACTING_USER_REQUIRED`) — see the X-Acting-User header404Envelope not found
GET/v1/envelopes/{id}/evidenceGet evidence package (ZIP)
Opens with: API key
Parameters
idstringpathRequired
Responses
200Returns ZIP file404Envelope not found
POST/v1/envelopes/{id}/folderUpdate envelope folder
Move an envelope to a folder or remove from folder
Opens with: API key
Parameters
idstringpathRequired
Request body: UpdateEnvelopeFolderDto
folderIdobjectRequiredFolder ID to move envelope to (null to remove from folder)
Responses
200Folder updated404Envelope or folder not found
POST/v1/envelopes/{id}/quick-signQuick Sign: apply your saved signature to all your fields
Auto-applies the current user's saved signature to every signature/initials/date field assigned to them on this envelope. Only allowed when the user is a pending recipient and (for sequential envelopes) is the active signer. Fails if the user has any required TEXT/CHECKBOX/DROPDOWN/RADIO fields that need manual input.
Opens with: API key
Parameters
idstringpathRequired
Request body: QuickSignEnvelopeDto
consentbooleanRequiredConsent to sign electronically. Must be true.consentVersionstringId of the consent statement the client displayed, e.g. "quick-sign/v1". The wording is resolved server-side from that id and never taken from the request; this only says which of our own published statements was on screen. Omit and the current one is recorded.initialsIdstringSaved initials ID to use (when the recipient has INITIALS fields). Defaults to the user's default saved initials.signatureIdstringSaved signature ID to use. Defaults to the user's default saved signature.
Responses
200QuickSignResponseDtoSigned400Cannot quick-sign (validation failed)404Envelope not found or not awaiting you
POST/v1/envelopes/{id}/recipients/{recipientId}/embedGenerate embedded signing URL
Generate a short-lived URL for embedding signing in an iframe. The URL expires in 5 minutes and can only be used once.
Opens with: API key
Parameters
idstringpathRequiredrecipientIdstringpathRequired
Responses
200Returns embeddable signing URL403Feature not available on current plan404Envelope or recipient not found409Recipient already signed or envelope not in signable state
PATCH/v1/envelopes/{id}/recipients/{recipientId}/mark-notifiedMark recipient as manually notified
Mark a LINK_ONLY recipient as having been notified manually (e.g., after copying and sharing the signing link).
Opens with: API key
Parameters
idstringpathRequiredrecipientIdstringpathRequired
Responses
200Recipient marked as notified404Envelope or recipient not found
GET/v1/envelopes/{id}/recipients/{recipientId}/signing-linkGet signing link for a recipient
Get the signing URL for a specific recipient. Useful for LINK_ONLY recipients where you need to manually share the link. Requires a WRITE-scoped API key: the link confers the ability to sign.
Opens with: API key
Parameters
idstringpathRequiredrecipientIdstringpathRequired
Responses
200RecipientSigningLinkDtoReturns signing link details404Envelope, recipient, or signing session not found
POST/v1/envelopes/{id}/remindSend reminder to pending recipients
Opens with: API key
Parameters
idstringpathRequired
Responses
200Reminders sent404Envelope not found409Cannot send reminders for envelope in current state, or the envelope is a self-sign (source SELF_SIGN), whose only signer is its author
POST/v1/envelopes/{id}/resend-with-answersResend a finished envelope pre-filled with previous answers
For a completed or declined envelope, creates and sends a fresh envelope pre-filled with the data signers entered last time (text, dates, checkboxes, radios). Signatures, initials, signing dates and attachments are left blank so signers review and re-sign. The original envelope is preserved as the record.
Opens with: API key
Parameters
idstringpathRequired
Request body: ResendWithAnswersDto
messagestringOptional note to the signers explaining what to correct. Becomes the message on the resent envelope and is shown to recipients. Falls back to the original message if omitted.
Responses
201ResendWithAnswersResponseDtoEnvelope resent successfully400Invalid input, or an API-key request that does not name the person it is acting for (`ACTING_USER_REQUIRED`) — see the X-Acting-User header404Envelope not found409Envelope is not completed or declined
POST/v1/envelopes/{id}/save-as-templateSave an envelope as a template
Creates a template from this envelope: its documents in order, its recipients as numbered roles (signer_1, viewer_1, ...), its field placements, its signing order and its message. No recipient email or expiry is carried across. Refused for a proposal acceptance envelope.
Opens with: API key
Parameters
idstringpathRequired
Request body: CreateTemplateFromEnvelopeDto
categorystringTemplate category for groupingdescriptionstringTemplate descriptionnamestringRequiredTemplate name
Responses
201TemplateDetailResponseDtoTemplate created400The envelope is a proposal acceptance, has no documents, or a document is gone404Envelope not found
POST/v1/envelopes/{id}/sendSend envelope to recipients
Opens with: API key
Parameters
idstringpathRequired
Request body: SendEnvelopeDto
expiresAtdate-timeOverride expiration date
Responses
200SendEnvelopeResponseDtoEnvelope sent successfully400Invalid envelope state or missing fields404Envelope not found409Envelope already sent
PUT/v1/envelopes/{id}/tagSet or clear the tag on an envelope
Files an envelope under a single optional label, at any status. FILING, NOT EVIDENCE: it writes no audit event, never reaches the signed PDF or the certificate, and changing it changes nothing a counterparty relies on. An empty or whitespace-only value clears the tag, as does null. A self-sign envelope may only be tagged by the person who created it, which is the same privacy rule `mineOnly` applies to the list.
Opens with: API key
Parameters
idstringpathRequired
Request body: SetEnvelopeTagDto
tagobjectRequiredThe tag, or null to remove it. Trimmed; an empty or whitespace-only string is treated as null, so clearing the box in the UI and sending null mean the same thing.
Responses
200EnvelopeResponseDtoTag set404Envelope not found
POST/v1/envelopes/{id}/voidVoid an envelope
Opens with: API key
Parameters
idstringpathRequired
Request body: VoidEnvelopeDto
reasonstringRequiredReason for voiding the envelope
Responses
200VoidEnvelopeResponseDtoEnvelope voided successfully404Envelope not found409Cannot void envelope in current state
GET/v1/envelopes/awaiting-meList envelopes awaiting the current user's signature
Returns envelopes where the current logged-in user is a pending signer. For sequential envelopes, only the actively-pending wave is returned.
Opens with: API key
Responses
200AwaitingMeListResponseDtoList of envelopes awaiting current user
POST/v1/envelopes/bulk-moveBulk move envelopes to folder
Move multiple envelopes to a folder or remove from folder
Opens with: API key
Request body: BulkMoveEnvelopesDto
envelopeIdsstring[]RequiredEnvelope IDs to movefolderIdobjectRequiredFolder ID to move envelopes to (null to remove from folder)
Responses
200Envelopes moved404Folder not found
POST/v1/envelopes/preview-emailRender the covering email for an envelope being composed
Opens with: API key
Request body: EmailPreviewRequestDto
expiresAtobjectISO date, or null.messagestringThe covering note, plain text, possibly carrying references.recipientEmailstringRequiredWhich recipient this preview is for. Everyone gets their own links.recipientNamestringsubjectstringRequiredThe envelope subject, which becomes the email subject line.supportingEmailPreviewSupportingDto[]
Responses
200EmailPreviewResponseDto
POST/v1/envelopes/self-signCreate a self-sign envelope
Create an envelope where you are the sole signer. Returns a signing URL to sign immediately.
Opens with: API key
Request body: CreateSelfSignEnvelopeDto
consentbooleanSet true to sign the document in the same call. Every placement carrying a `value` is filled and the envelope is completed. Consent is required to sign and is recorded in full on the audit trail. Omit to create an unsigned envelope and sign it separately.consentVersionstringId of the consent statement the interface displayed, e.g. "quick-sign/v1". The wording is resolved server-side from this id and never read from the request. Omit and the current statement is recorded.departmentIdstringWhich department this envelope belongs to. Omit and it resolves from the creator: their single department, or the company-wide default when they belong to none or several. Must be one the caller may file into (GET /v1/departments/assignable) — anything else is a 400, never a silent fallback.documentIdsstring[]RequiredDocument IDs to include in the envelopefieldsSelfSignFieldDto[]Field placements for this envelope, in fractional (0-1) page geometry. Fields cannot be added after creation — a self-sign envelope is created already SENT and placements are frozen from that point. Omit to create an envelope with no fields.gridGroupsSelfSignGridGroupDto[]Grid tables referenced by the `gridGroupId` on any grid cell in `fields`.metadataobjectCustom metadatasubjectstringRequiredDocument name/subject for the self-signed document
Responses
201SelfSignEnvelopeResponseDtoSelf-sign envelope created. Use signingUrl to sign.400Invalid input, documents not ready, or an API-key request that does not name the person it is acting for (`ACTING_USER_REQUIRED`) — see the X-Acting-User header404Document(s) not found
GET/v1/envelopes/statsEnvelope status counts for the list’s tile row
Counts of drafts, envelopes out for signature, completed and expired, plus the overall total — over the SAME filters and the SAME visibility scope as `GET /envelopes`. Takes every list filter except `status`, which it strips, so the tiles describe the filtered set independently of which status is selected and each one remains clickable. The four buckets deliberately do not sum to `total`: DECLINED and VOIDED are counted in `total` and in no bucket. NOTE: nothing in this codebase writes the EXPIRED status — there is no envelope expiry sweep — so `expired` reflects only seeded or out-of-band rows.
Opens with: API key
Parameters
searchstringquerySearch by subject or recipient email, identical to the list endpoint’sfolderIdstringqueryFilter by folder ID. Use "null" for envelopes not in any folder.contactEmailstringqueryFilter to one client by recipient email, identical to the list endpoint'ssourceDIRECT | PROPOSAL_ACCEPTANCE | SELF_SIGNqueryFilter by provenance, identical to the list endpoint'sincludeAllbooleanqueryInclude every source, hidden ones too. Ignored when `source` is given. Omit for the default, which is `DIRECT` only.
Responses
200EnvelopeStatsResponseDto400Invalid query params
Field Linking
POST/v1/fields/{id}/unlinkClear a field’s linkKey (unlink it from its group)
Opens with: API key
Parameters
idstringpathRequiredField ID
Responses
200FieldResponseDto
POST/v1/fields/linkLink fields under a shared linkKey (value propagation)
Opens with: API key
Request body: LinkFieldsDto
fieldIdsstring[]RequiredIDs of the fields to link (at least two, same envelope or same template)linkKeystringShared link key. If omitted, a unique key is generated (or an existing key already on one of the fields is reused).
Responses
200LinkFieldsResponseDto400Fewer than two fields, or mixed scope
Field Suggestions
GET/v1/documents/{id}/suggestionsList PENDING smart field suggestions for a document
Opens with: API key
Parameters
idstringpathRequiredDocument ID
Responses
200FieldSuggestionResponseDto[]
POST/v1/documents/{id}/suggestions/accept-allAccept all PENDING suggestions for a document
Opens with: API key
Parameters
idstringpathRequiredDocument ID
Request body: SuggestionScopeDto
envelopeIdstringEnvelope ID (mutually exclusive with templateId)templateIdstringTemplate ID (mutually exclusive with envelopeId)
Responses
200AcceptSuggestionResponseDto
POST/v1/documents/{id}/suggestions/detectRun smart-field detection on demand and (re)write PENDING suggestions
Synchronous re-run of the upload-time detection job. Idempotent: existing PENDING suggestions are cleared and re-created; ACCEPTED/REJECTED rows are preserved. A detector load/parse failure surfaces as a 500 rather than a false empty result.
Opens with: API key
Parameters
idstringpathRequiredDocument ID
Request body: DetectSuggestionsRequestDto
forcebooleanReserved for future use; has no effect on current behaviour
Responses
200DetectSuggestionsResponseDto404Document not found500Detection failed (could not analyse the document)
POST/v1/documents/{id}/suggestions/reject-allReject all PENDING suggestions for a document
Opens with: API key
Parameters
idstringpathRequiredDocument ID
Responses
200SuggestionActionResultDto
POST/v1/suggestions/{id}/acceptAccept a suggestion (promotes to a real Field; link group accepts together)
Opens with: API key
Parameters
idstringpathRequiredSuggestion ID
Request body: SuggestionScopeDto
envelopeIdstringEnvelope ID (mutually exclusive with templateId)templateIdstringTemplate ID (mutually exclusive with envelopeId)
Responses
200AcceptSuggestionResponseDto400Already actioned or invalid scope
POST/v1/suggestions/{id}/rejectReject a suggestion
Opens with: API key
Parameters
idstringpathRequiredSuggestion ID
Responses
200FieldSuggestionResponseDto
Folders
GET/v1/foldersGet folder tree
Get the complete folder tree with envelope counts
Opens with: API key
Responses
200Returns folder tree
POST/v1/foldersCreate folder
Create a new folder
Opens with: API key
Request body: CreateFolderDto
colorstringHex color for folder icon (e.g., #3B82F6)namestringRequiredFolder nameparentIdstringParent folder ID for nesting
Responses
201Folder created404Parent folder not found409Folder with this name already exists
GET/v1/folders/{id}Get folder
Get a single folder by ID
Opens with: API key
Parameters
idstringpathRequiredFolder ID
Responses
200Returns folder404Folder not found
PATCH/v1/folders/{id}Update folder
Update a folder (name, parent, color)
Opens with: API key
Parameters
idstringpathRequiredFolder ID
Request body: UpdateFolderDto
colorstringHex color for folder iconnamestringFolder nameparentIdobjectParent folder ID (null to move to root)
Responses
200Folder updated404Folder not found409Folder with this name already exists
DELETE/v1/folders/{id}Delete folder
Delete a folder. Child folders and envelopes are moved to the parent.
Opens with: API key
Parameters
idstringpathRequiredFolder ID
Responses
200Folder deleted404Folder not found
POST/v1/folders/{id}/moveMove folder
Move a folder to a new parent (or root)
Opens with: API key
Parameters
idstringpathRequiredFolder ID
Request body: MoveFolderDto
newParentIdobjectNew parent folder ID (null to move to root)
Responses
200Folder moved400Invalid move (circular reference)404Folder not found
GET/v1/folders/{id}/pathGet folder path
Get the breadcrumb path from root to this folder
Opens with: API key
Parameters
idstringpathRequiredFolder ID
Responses
200Returns folder path404Folder not found
GET/v1/folders/listList all folders
Get all folders as a flat list
Opens with: API key
Responses
200Returns flat folder list
Health
GET/v1/healthHealth check endpoint
Opens with: No credential.
Responses
200Service is healthy
Notifications
GET/v1/notificationsGet notifications
Get all notifications for the current user
Opens with: API key
Parameters
limitnumberqueryMaximum number of notifications to return (default: 20)offsetnumberqueryNumber of notifications to skip (default: 0)unreadOnlybooleanqueryOnly return unread notifications (default: false)
Responses
200List of notifications
DELETE/v1/notificationsDelete all notifications
Delete all notifications for the current user
Opens with: API key
Responses
200All notifications deleted
DELETE/v1/notifications/{id}Delete a notification
Delete a specific notification
Opens with: API key
Parameters
idstringpathRequired
Responses
204Notification deleted
POST/v1/notifications/{id}/readMark notification as read
Mark a specific notification as read
Opens with: API key
Parameters
idstringpathRequired
Responses
200Notification marked as read404Notification not found
GET/v1/notifications/countGet unread count
Get the number of unread notifications
Opens with: API key
Responses
200Unread notification count
POST/v1/notifications/read-allMark all notifications as read
Mark all notifications for the current user as read
Opens with: API key
Responses
200All notifications marked as read
Onboarding
GET/v1/onboarding/preferencesGet the current user's onboarding tour preferences
Returns whether the user has permanently opted out of automatic walkthroughs. Also included in GET /onboarding/tours; exposed separately for UI that only needs the toggle.
Opens with: API key
Responses
200OnboardingPreferencesResponseDtoTour preferences401Unauthorized
PUT/v1/onboarding/preferencesTurn automatic onboarding tours off (or back on) for the current user
Sets the account-wide opt-out behind the "Don't show tours again" checkbox. When toursDisabled is true, no tour auto-runs again for this user — including tours whose version is later bumped, which is the point: a version bump must never override an explicit opt-out. Tours stay replayable on demand from the Help menu. Idempotent.
Opens with: API key
Request body: UpdateOnboardingPreferencesDto
toursDisabledbooleanRequiredTrue to stop showing walkthroughs automatically, false to re-enable them. Idempotent — re-sending the same value keeps the original opt-out timestamp.
Responses
200OnboardingPreferencesResponseDtoUpdated tour preferences401Unauthorized
GET/v1/onboarding/setupFirst-run setup checklist for the current account
Every item is computed from live data on each request rather than stored, so it cannot disagree with the account. `required` marks the items that BLOCK something (company identity, and the per-user proposals grant on an entitled plan); the rest are encouragement. Proposals items appear only for a tenant whose plan includes the module.
Opens with: API key
Responses
200SetupChecklistResponseDtoChecklist401Unauthorized
GET/v1/onboarding/toursList onboarding tour state for the current user
Returns one row per tour the user has completed or dismissed, plus the account-wide tour preferences. The web client compares completedVersion against its TOUR_VERSIONS constants to decide whether to re-trigger a tour — unless preferences.toursDisabled is true, which suppresses all automatic tours regardless of version.
Opens with: API key
Responses
200TourStateListResponseDtoTour state list401Unauthorized
DELETE/v1/onboarding/tours/{key}Reset an onboarding tour for the current user
Deletes the user's state for this tour key, causing it to re-trigger on next visit. Used by the "Restart tour" action.
Opens with: API key
Parameters
keystringpathRequiredTour key
Responses
204Tour state cleared401Unauthorized
POST/v1/onboarding/tours/{key}/completeMark an onboarding tour as completed
Records that the user has finished the tour at the given version. Upserts on (userId, tourKey).
Opens with: API key
Parameters
keystringpathRequiredTour key (e.g. "sidebar", "dashboard")
Request body: CompleteTourDto
versionnumberRequiredThe version of the tour the user just completed. Used to detect when a tour has been updated and should re-trigger.
Responses
200TourStateResponseDtoTour state401Unauthorized
POST/v1/onboarding/tours/{key}/dismissMark an onboarding tour as dismissed
Records that the user dismissed/skipped the tour at the given version. A skip earns a one-version grace in the web client (see TOUR_SKIP_GRACE in apps/web/lib/tours) — the next bump does not re-ask, the one after that does.
Opens with: API key
Parameters
keystringpathRequiredTour key
Request body: DismissTourDto
versionnumberRequiredThe version of the tour the user dismissed. Recorded so the same version does not re-trigger.
Responses
200TourStateResponseDtoTour state401Unauthorized
Recipients
GET/v1/recipientsList aggregated recipients
Get a list of unique recipients aggregated from all envelopes. Useful for autocomplete and recipient management.
Opens with: API key
Parameters
pagenumberquerypageSizenumberquerysearchstringqueryroleSIGNER | VIEWER | CCquery
Responses
200Returns list of aggregated recipients
GET/v1/recipients/{email}Get recipient envelopes
Get all envelopes associated with a specific recipient email.
Opens with: API key
Parameters
emailstringpathRequiredpagestringqueryRequiredpageSizestringqueryRequired
Responses
200Returns recipient details with envelope history
GET/v1/recipients/suggestionsGet recipient suggestions
Get autocomplete suggestions for recipient email/name. Requires at least 2 characters.
Opens with: API key
Parameters
qstringqueryRequiredSearch query (min 2 chars)limitnumberqueryMax results (default 10)
Responses
200Returns list of recipient suggestions
Signatures
GET/v1/signaturesList saved signatures
Get all saved signatures for the current user.
Opens with: API key
Responses
200SavedSignatureListResponseDtoList of saved signatures401Unauthorized
POST/v1/signaturesCreate a saved signature
Save a new signature to the user's signature library for reuse.
Opens with: API key
Request body: CreateSavedSignatureDto
imageDatastringRequiredSignature image data (base64 for DRAWN/UPLOADED, text for TYPED)isDefaultbooleanSet as default signatureisInitialsbooleanIs this an initials signaturenamestringRequiredInternal label for this saved signature (shown in pickers)signerEmailstringOptional delivery email. When this signature is applied for a self-sign role, the completion notification + signed PDF are routed to this address instead of the logged-in account email.signerNamestringOptional legal name to attribute to this signature on audit certificates and recipient rows when used for self-sign. Useful when an admin holds signatures for multiple staff (CEO, secretary, etc.). Falls back to the logged-in user's account name when omitted.typeDRAWN | TYPED | UPLOADEDRequiredSignature type
Responses
201SavedSignatureResponseDtoSignature saved successfully400Invalid data or limit reached401Unauthorized
GET/v1/signatures/{id}Get a saved signature
Get a specific saved signature by ID.
Opens with: API key
Parameters
idstringpathRequiredSignature ID
Responses
200SavedSignatureResponseDtoSaved signature401Unauthorized404Signature not found
PUT/v1/signatures/{id}Update a saved signature
Update name or default status of a saved signature.
Opens with: API key
Parameters
idstringpathRequiredSignature ID
Request body: UpdateSavedSignatureDto
imageDatastringReplace the signature image (base64 data URI for DRAWN/UPLOADED, or generated image for TYPED). Optional — omit to leave the current image untouched while updating other fields.isDefaultbooleanSet as default signaturenamestringInternal label for this saved signaturesignerEmailstringDelivery email for self-sign use. Pass empty string to clear; omit to leave unchanged.signerNamestringLegal name to attribute to this signature on audit certificates / recipient rows when used for self-sign. Pass an empty string to clear.typeDRAWN | TYPED | UPLOADEDSignature type: only required when replacing imageData
Responses
200SavedSignatureResponseDtoSignature updated401Unauthorized404Signature not found
DELETE/v1/signatures/{id}Delete a saved signature
Remove a saved signature from the library.
Opens with: API key
Parameters
idstringpathRequiredSignature ID
Responses
204Signature deleted401Unauthorized404Signature not found
POST/v1/signatures/{id}/set-defaultSet as default signature
Set this signature as the default for its type (signature or initials).
Opens with: API key
Parameters
idstringpathRequiredSignature ID
Responses
200SavedSignatureResponseDtoSignature set as default401Unauthorized404Signature not found
GET/v1/signatures/defaultGet default signature
Get the user's default signature.
Opens with: API key
Responses
200SavedSignatureResponseDtoDefault signature or null if none set401Unauthorized
GET/v1/signatures/default/initialsGet default initials
Get the user's default initials signature.
Opens with: API key
Responses
200SavedSignatureResponseDtoDefault initials or null if none set401Unauthorized
Signing
GET/v1/signing/{token}Get signing session data
Public endpoint accessed via signing link. Returns all data needed to render the signing UI.
Opens with: No credential.
Parameters
tokenstringpathRequiredSigning session token (from email link)
Responses
200SigningSessionResponseDtoReturns signing session with documents and fields404Session not found409Session expired, revoked, or already completed429Too many requests
POST/v1/signing/{token}/authenticateAuthenticate signer
For SMS or access code authentication methods. Submit the code received.
Opens with: No credential.
Parameters
tokenstringpathRequiredSigning session token
Request body: AuthenticateDto
codestringRequiredAuthentication code (SMS or access code)
Responses
200AuthenticateResponseDtoAuthentication result404Session not found409Locked out due to too many failed attempts429Too many authentication attempts
POST/v1/signing/{token}/completeComplete signing session
Finalize signing after all required fields are completed. Requires consent acknowledgment.
Opens with: No credential.
Parameters
tokenstringpathRequiredSigning session token
Request body: CompleteSigningDto
consentbooleanRequiredConsent acknowledgment - signer agrees to sign electronicallyconsentVersionstringId of the consent statement the page displayed, e.g. "signing-ceremony/v1", echoed back from the session response. The wording itself is resolved server-side from that id and never taken from the request. Omit and the current statement is recorded.notestringOptional note from signer
Responses
200CompleteSigningResponseDtoSigning completed successfully400Not all required fields completed or missing consent404Session not found409Session already completed429Too many requests
GET/v1/signing/{token}/completed-documentDownload or view completed signed document
Returns the fully signed document after all signers have completed. Requires email verification. Defaults to a file download; pass disposition=inline to render it in the browser instead.
Opens with: No credential.
Parameters
tokenstringpathRequiredSigning session tokenemailstringqueryRequireddispositionstringqueryRequired
Responses
200Signed document file stream400Email parameter required403Email does not match recipient404Session not found409Envelope not yet completed
GET/v1/signing/{token}/completion-statusGet completion status
Check if the envelope is fully completed and get download URL for the signed document.
Opens with: No credential.
Parameters
tokenstringpathRequiredSigning session token
Responses
200CompletionStatusResponseDtoReturns completion status with optional download URL404Session not found429Too many requests
POST/v1/signing/{token}/declineDecline to sign
Decline signing the envelope. This will cancel the entire envelope for all recipients.
Opens with: No credential.
Parameters
tokenstringpathRequiredSigning session token
Request body: DeclineSigningDto
reasonstringRequiredReason for declining
Responses
200DeclineSigningResponseDtoSigning declined404Session not found409Session already completed or declined429Too many requests
GET/v1/signing/{token}/documents/{documentId}/contentGet document content for signing
Streams the document content through the API. This proxies DocVault to avoid exposing presigned URLs to the client.
Opens with: No credential.
Parameters
tokenstringpathRequiredSigning session tokendocumentIdstringpathRequiredDocument ID
Responses
200Document file stream404Session or document not found409Authentication required or session expired
POST/v1/signing/{token}/fields/{fieldId}Complete a field
Submit the value for a field (signature, text, checkbox, etc.)
Opens with: No credential.
Parameters
tokenstringpathRequiredSigning session tokenfieldIdstringpathRequiredField ID to complete
Request body: CompleteFieldDto
checkedbooleanCheckbox state (for checkbox fields)selectionstringDropdown selection (for dropdown fields)signatureCompleteSignatureFieldDtoSignature data (for signature/initials fields)textstringText value (for text fields)valueTypeTEXT | SIGNATURE | INITIALS | DATE | CHECKBOX | DROPDOWNRequiredField value type
Responses
200CompleteFieldResponseDtoField completed successfully400Invalid field value — wrong payload shape for the field type, or the value breaches the author's validation rules (returned on the session field as `validation`)404Session or field not found409Field already completed429Too many requests
DELETE/v1/signing/{token}/fields/{fieldId}Clear a field value
Remove a previously entered value so the signer can re-enter or leave it blank. Only allowed before the signer has finished signing.
Opens with: No credential.
Parameters
tokenstringpathRequiredSigning session tokenfieldIdstringpathRequiredField ID to clear
Responses
200ClearFieldResponseDtoField value cleared successfully404Session or field not found409Session already completed429Too many requests
POST/v1/signing/{token}/resend-sms-codeResend SMS verification code
Resend the SMS verification code. Rate limited to prevent abuse.
Opens with: No credential.
Parameters
tokenstringpathRequiredSigning session token
Responses
200SendSmsCodeResponseDtoSMS code resent (or rate limited)400Session does not use SMS authentication or no phone number404Session not found409Session expired or locked out429Too many SMS requests
POST/v1/signing/{token}/send-sms-codeSend SMS verification code
Send a new SMS verification code to the recipient's phone number. For SMS authentication only.
Opens with: No credential.
Parameters
tokenstringpathRequiredSigning session token
Responses
200SendSmsCodeResponseDtoSMS code sent (or rate limited)400Session does not use SMS authentication or no phone number404Session not found409Session expired or locked out429Too many SMS requests
GET/v1/signing/{token}/supporting/{supportingId}/contentRead a supporting document sent with this envelope
Material sent alongside the envelope to be READ, never signed. Streamed through the API, so no DocVault URL ever reaches the client. Defaults to rendering in the browser; pass disposition=attachment to download, which is recorded as a download rather than an open. Every read writes an activity row, and the first read by this recipient also writes a hash-chained audit event carrying the sha256 of the bytes served. A request whose User-Agent identifies a mail scanner is recorded as automated and never reaches the chain. A recipient who has already signed, and a sequential signer whose turn has not come, may both read; a voided, expired or declined envelope refuses.
Opens with: No credential.
Parameters
tokenstringpathRequiredSigning session tokensupportingIdstringpathRequiredSupporting document iddispositionstringqueryRequired
Responses
200Supporting document file stream400The item is an external link, not a document we hold404Session or supporting document not found409Authentication required, or the envelope is closed429Too many requests
Support
GET/v1/support/configWhether support is available on this environment
Returns false when HOME_GATEWAY_URL is unset (standalone dev). The web app hides the Support nav item and stops polling the unread count when false, so users are never shown a form that cannot deliver.
Opens with: API key
Responses
200SupportConfigResponseDto
GET/v1/support/ticketsList the current user's support tickets
Scoped to this user’s own tickets for CecurSign only. Pass ticketNumber to resolve a /support?ticket=TKT-000042 deep link in a single call.
Opens with: API key
Parameters
statusopen | in_progress | awaiting_response | resolved | closedqueryticketNumberstringqueryExact ticket reference, used to resolve /support?ticket=TKT-000042 deep linkspagenumberquerylimitnumberquery
Responses
200SupportTicketListResponseDto
POST/v1/support/ticketsRaise a support ticket
Creates a ticket on Home’s support desk. The submitter and tenant come from the session; the browser supplies only subject, description, category, priority and page context.
Opens with: API key
Request body: CreateSupportTicketDto
categoryquestion | bug | feature | billing | othercontextSupportContextDtodescriptionstringRequiredWhat the user needs help withformInstanceIduuidClient-generated UUID, stable for one form submission (including retries). Omit and the server generates one, which de-duplicates nothing.prioritylow | medium | high | urgentsubjectstringRequiredTicket subject
Responses
201CreateSupportTicketResponseDto400Validation failure403Support is not enabled for this account429Rate limited: retry shortly503Support is not configured or Home is unreachable
GET/v1/support/tickets/{id}Get a support ticket and its thread
Staff internal notes are stripped by Home — this only ever returns what the user is allowed to see. Does not clear the unread flag; call the read endpoint for that.
Opens with: API key
Parameters
idstringpathRequiredHome ticket id (UUID)
Responses
200SupportTicketThreadResponseDto404No such ticket for this app and tenant
POST/v1/support/tickets/{id}/commentsReply to a support ticket
Replying reopens an awaiting-response or resolved ticket. Closed tickets reject replies with 409.
Opens with: API key
Parameters
idstringpathRequiredHome ticket id (UUID)
Request body: ReplyToSupportTicketDto
contentstringRequiredReply body
Responses
201SupportReplyResponseDto409Ticket is closed: raise a new one
POST/v1/support/tickets/{id}/readClear the unread flag on a ticket
Called when the user opens the thread view. Reads are not implicit — fetching a thread never mutates state.
Opens with: API key
Parameters
idstringpathRequiredHome ticket id (UUID)
Responses
200SupportSuccessResponseDto
GET/v1/support/tickets/statsTicket counts by status for the current user
Feeds the KPI tiles above /support. Takes no status filter on purpose — the tiles narrow the set these counts describe, so counting a filtered set would zero every tile but the pressed one.
Opens with: API key
Responses
200SupportTicketStatsResponseDto
GET/v1/support/tickets/unread-countUnread reply count for the Support nav badge
Polled by the web app roughly every 60 seconds, only while the tab is visible. Cached briefly per user so multiple open tabs collapse onto one upstream call.
Opens with: API key
Responses
200SupportUnreadCountResponseDto
Supporting documents
GET/v1/envelopes/{envelopeId}/supportingList the supporting documents on an envelope
Material sent alongside the envelope for the recipient to read, never to sign. Nothing here is merged into the signed PDF, named on the certificate or hashed into the audit chain.
Opens with: API key
Parameters
envelopeIdstringpathRequiredEnvelope id
Responses
200SupportingDocumentListResponseDto404Envelope not found
POST/v1/envelopes/{envelopeId}/supportingAttach a supporting document to a draft envelope
Either a document from the firm's library or an https link to the firm's own site, never both. A library document is verified against the caller's tenant and department scope. An external link is never tracked, because the request goes to the firm's own server and never reaches us.
Opens with: API key
Parameters
envelopeIdstringpathRequiredEnvelope id
Request body: AttachSupportingDocumentDto
descriptionstringOne line of context, shown under the labeldocumentIdstringA document from this firm's library. Verified against the caller's tenant AND their department scope before it is accepted. Mutually exclusive with externalUrl.externalUrlstringAn https URL the firm already publishes on its own site. Mutually exclusive with documentId. An external link is NEVER tracked: the request goes to the firm's own server and never reaches us, so nothing about it appears in the evidence route.labelstringRequiredWhat the recipient sees. Not the filename: "Our privacy policy" reads better to a client than "privacy-policy-v4-FINAL.pdf".ordernumberPosition in the list. Omit and it is appended to the end.
Responses
201SupportingDocumentResponseDto400Both or neither source given, or a non-https link404Envelope or document not found409The envelope is no longer a draft
DELETE/v1/envelopes/{envelopeId}/supporting/{supportingId}Detach a supporting document from a draft envelope
Opens with: API key
Parameters
envelopeIdstringpathRequiredEnvelope idsupportingIdstringpathRequiredSupporting document id
Responses
204Detached404Envelope or supporting document not found409The envelope is no longer a draft
GET/v1/envelopes/{envelopeId}/supporting/evidenceWho read which supporting document, and how often
Per document, per recipient. `firstOpenedAt` is the read that produced the chained audit event and the one that reaches the certificate; the counts are the lightweight activity log. Reads attributed to a mail scanner are reported separately and are never part of the counts. External links report nothing and never will.
Opens with: API key
Parameters
envelopeIdstringpathRequiredEnvelope id
Responses
200SupportingDocumentEvidenceResponseDto404Envelope not found
PUT/v1/envelopes/{envelopeId}/supporting/orderReorder an envelope's supporting documents
Takes every id on the envelope, in the order they should appear. A partial list is refused rather than applied.
Opens with: API key
Parameters
envelopeIdstringpathRequiredEnvelope id
Request body: ReorderSupportingDocumentsDto
idsstring[]RequiredEvery supporting document id on this envelope or proposal, in the order they should appear. Ids that do not belong here are refused rather than ignored.
Responses
200SupportingDocumentListResponseDto400The list is incomplete, or names something else404Envelope not found409The envelope is no longer a draft
Team
GET/v1/team/invitationsList pending invitations
Opens with: API key
Responses
200InvitationListResponseDto
POST/v1/team/invitationsInvite a new team member
Opens with: API key
Request body: CreateInvitationDto
departmentIdsstring[]Departments to join on acceptance. Validated against the tenant now AND again at acceptance — a department can be retired in between.emailstringRequiredfirstNamestringgrantProposalsbooleanGrant proposals access on acceptance. Omit to write no grant row at all, which is not the same as false: false is an explicit, audited denial.lastNamestringroleOWNER | ADMIN | MEMBER | VIEWERRequiredvisibilityScopeOWN | DEPARTMENT | TENANTWhat they will be able to see, applied when they accept. Omit to leave the default (TENANT, the whole company). DEPARTMENT requires at least one departmentId.
Responses
201InvitationResponseDto
DELETE/v1/team/invitations/{id}Revoke an invitation
Opens with: API key
Parameters
idstringpathRequired
Responses
200
POST/v1/team/invitations/{id}/resendResend an invitation
Opens with: API key
Parameters
idstringpathRequired
Responses
200InvitationDto
POST/v1/team/invitations/acceptAccept an invitation and create account
Opens with: No credential.
Request body: AcceptInvitationDto
firstNamestringRequiredlastNamestringRequiredpasswordstringDeprecated. Passwords are set through Auth0, not here. Validated if supplied, ignored by the web client. Min 8 chars with uppercase, lowercase, number and special character.tokenstringRequired
Responses
201
GET/v1/team/invitations/accept/{token}Get invitation details by token
Opens with: No credential.
Parameters
tokenstringpathRequired
Responses
200404Invitation not found, already used, or expired
GET/v1/team/membersList team members
Requires an OWNER or ADMIN session.
Opens with: API key
Responses
200TeamMemberListResponseDto401Not authenticated, or insufficient role
DELETE/v1/team/members/{id}Remove team member
Opens with: API key
Parameters
idstringpathRequired
Responses
200
PUT/v1/team/members/{id}/roleUpdate team member role
Opens with: API key
Parameters
idstringpathRequired
Request body: UpdateMemberRoleDto
roleOWNER | ADMIN | MEMBER | VIEWERRequired
Responses
200TeamMemberDto
Templates
GET/v1/templatesList templates
Opens with: API key
Parameters
pagenumberqueryPage number (1-indexed)pageSizenumberqueryPage sizecategorystringqueryFilter by categoryisActivebooleanqueryFilter by active statussearchstringquerySearch by name or description
Responses
200TemplateListResponseDtoReturns list of templates
POST/v1/templatesCreate a new template
Opens with: API key
Request body: CreateTemplateDto
categorystringCategory for organizing templatesdefaultExpirynumberDefault expiry in days. Omit it to use the standard 30 days applied at send.defaultMessagestringDefault message pre-populated into the envelope "message" field when this template is useddescriptionstringTemplate descriptiondocumentIdsstring[]RequiredDocument IDs to bundle into this template, in signing orderdocumentPackIdobjectA document pack to send alongside every envelope made from this template. Its items are COPIED onto the envelope at creation, so editing the pack afterwards never changes anything already sent. Null clears it.isActivebooleanWhether template is activenamestringRequiredTemplate nameneverExpiresbooleanEnvelopes made from this template never expire. Give this or defaultExpiry, not both; setting either replaces the other.recipientRolesTemplateRecipientRoleDto[]RequiredRecipient roles for this templatesigningOrderPARALLEL | SEQUENTIALSigning order for envelopes created from this template. SEQUENTIAL = roles sign in the order listed (one at a time); PARALLEL = all roles can sign at any time.
Responses
201TemplateDetailResponseDtoTemplate created successfully400Invalid input404Document not found
GET/v1/templates/{id}Get template by ID
Opens with: API key
Parameters
idstringpathRequired
Responses
200TemplateDetailResponseDtoReturns template details404Template not found
PUT/v1/templates/{id}Update a template
Opens with: API key
Parameters
idstringpathRequired
Request body: UpdateTemplateDto
categorystringCategory for organizing templatesdefaultExpirynumberDefault expiry in days. Null clears it (sending then applies the standard 30 days); omit to leave it unchanged.defaultMessagestringDefault message pre-populated into the envelope "message" field when this template is useddescriptionstringTemplate descriptiondocumentIdsstring[]Document IDs (replaces all existing template documents when provided)documentPackIdobjectA document pack to send alongside every envelope made from this template. Its items are COPIED onto the envelope at creation, so editing the pack afterwards never changes anything already sent. Null clears it.isActivebooleanWhether template is activenamestringTemplate nameneverExpiresbooleanEnvelopes made from this template never expire. Give this or defaultExpiry, not both; setting either replaces the other.recipientRolesTemplateRecipientRoleDto[]Recipient roles for this templatesigningOrderPARALLEL | SEQUENTIALSigning order for envelopes created from this template
Responses
200TemplateDetailResponseDtoTemplate updated successfully400Invalid input404Template not found
DELETE/v1/templates/{id}Delete a template
Opens with: API key
Parameters
idstringpathRequired
Responses
204Template deleted successfully404Template not found
POST/v1/templates/{id}/duplicateDuplicate a template
Opens with: API key
Parameters
idstringpathRequired
Responses
201TemplateDetailResponseDtoTemplate duplicated successfully404Template not found
POST/v1/templates/{id}/envelopesCreate an envelope from a template
Opens with: API key
Parameters
idstringpathRequired
Responses
201Envelope created successfully400Invalid input or missing recipient mappings404Template not found or inactive
GET/v1/templates/categoriesGet list of template categories
Opens with: API key
Responses
200string[]Returns list of category names
GET/v1/templates/most-usedTemplates ranked by how often they have been sent
Backs Home's Templates panel. Active, non-deleted templates only, most-used first, ties broken by newest. `total` counts every active template so a capped list can say what it left out. The template LIST is firm-wide (a shared library); the usage counts that rank it are department-scoped, so "most used" means most used as far as this caller can see.
Opens with: API key
Parameters
limitstringqueryRequired
Responses
200MostUsedTemplatesResponseDtoTemplates in descending use
GET/v1/templates/role-namesRole names this company has used on its templates
Backs the suggestions on the template wizard's "Role name" box. Every role name across the tenant's templates (deleted templates excluded), de-duplicated case-insensitively, most-used first with ties alphabetical, each in its most common spelling. Suggestions only: a role name remains free text.
Opens with: API key
Responses
200TemplateRoleNamesResponseDtoRole names in descending use
Tenants
GET/v1/tenants/brandingGet current branding settings
Opens with: Signed-in session or API key
Responses
200BrandingResponseDtoReturns branding settings401Not authenticated404Tenant not found
PUT/v1/tenants/brandingUpdate branding settings (primary colour, company name)
Requires an OWNER or ADMIN session.
Opens with: Signed-in session or API key
Request body: UpdateBrandingDto
companyNamestringCompany name displayed in signing pagesprimaryColorstringPrimary brand colour in hex format (#RRGGBB)
Responses
200BrandingResponseDtoBranding updated400Invalid input401Not authenticated403Plan does not include custom branding404Tenant not found
POST/v1/tenants/branding/logoUpload branding logo
Requires an OWNER or ADMIN session.
Opens with: Signed-in session or API key
Request body
filebinaryLogo image file (PNG, JPEG or WebP, max 350KB)
Responses
201BrandingLogoResponseDtoLogo uploaded successfully400Invalid file401Not authenticated403Plan does not include custom branding
DELETE/v1/tenants/branding/logoRemove branding logo
Requires an OWNER or ADMIN session.
Opens with: Signed-in session or API key
Responses
200DeleteLogoResponseDtoLogo removed401Not authenticated404Tenant not found
GET/v1/tenants/currentGet current tenant
Opens with: Signed-in session or API key
Responses
200TenantResponseDtoReturns current tenant401Not authenticated404Tenant not found
PATCH/v1/tenants/currentUpdate the current tenant
Requires an OWNER or ADMIN session. Not available to API keys, because RolesGuard needs a person and these fields are the company's public identity. Partial update of `name`, `primaryColor` and `senderDisplayName`. The `settings` JSONB column is NOT writable through this route and a request carrying it is rejected with 400: it holds plan entitlements, quotas and suspension state, which Cecur Home owns and writes through the provisioning API.
Opens with: Signed-in session or API key
Request body: UpdateTenantDto
namestringTenant/Organization nameprimaryColorstringPrimary brand color (hex format)senderDisplayNameobjectDefault "from" name used on outbound envelope emails. Set to your company name to send under the organisation rather than the individual team member. Pass an empty string to clear.
Responses
200TenantResponseDtoTenant updated400Invalid input, or a non-writable property was sent401Not authenticated, or insufficient role403Sent `primaryColor` on a plan without custom branding404Tenant not found
GET/v1/tenants/current/company-profileGet the tenant company profile
Returns the company identity used by proposals and engagement letters: the source of the {{companyName}}, {{companyAddress}}, {{companyEmail}} and {{companyVatNumber}} merge tokens, plus the per-tenant default currency, VAT rate and proposal grace window. `isCompleteForProposals` is true only when firmName, companyAddress and companyEmail are all present and non-blank.
Opens with: Signed-in session or API key
Responses
200CompanyProfileResponseDtoReturns the company profile401Not authenticated404Tenant not found
PATCH/v1/tenants/current/company-profileUpdate the tenant company profile
Requires an OWNER or ADMIN session. Not available to API keys, because these fields are the company's legal identity on a binding engagement letter. Partial update. Omitted keys are left unchanged; an explicit null (or a blank string) clears a nullable field. `firmName` is not editable here - it mirrors Tenant.name, which is set via PATCH /v1/tenants/current or PUT /v1/tenants/branding. Region-derived defaults: when `region` is set to GB or IE AND the tenant has no default currency / VAT rate yet AND the request does not supply them, they are seeded (GB -> GBP / 20.00, IE -> EUR / 23.00). A value the tenant already chose is never overwritten, a value supplied in the same request always wins, and a region outside the map seeds nothing.
Opens with: Signed-in session or API key
Request body: UpdateCompanyProfileDto
companyAddressobjectRegistered company address, source of the {{companyAddress}} merge token. Send null (or an empty string) to clear it; omit the key to leave it unchanged.companyEmailobjectPublic company email address, source of the {{companyEmail}} merge token. Send null (or an empty string) to clear it; omit the key to leave it unchanged.companyRegistrationNumberobjectCompanies registration number (CRO / Companies House). Send null (or an empty string) to clear it; omit the key to leave it unchanged.defaultCurrencyobjectISO 4217 currency code used as the default for new proposals. Send null to clear it; omit the key to leave it unchanged.defaultVatRatestringDefault VAT rate (percent) for new proposals, 0-100 with at most 2 decimal places. Accepts a numeric string ("23.00", preferred) or a number. Send null to clear it; omit the key to leave it unchanged.financialYearStartDayobjectThe day of that month the financial year starts (1-31). Stored separately because a UK sole trader's year starts 6 April, not 1 April — month alone would mis-window a large part of the UK market by five days.financialYearStartMonthobjectThe month this company's financial year starts (1-12). Null clears it, which HIDES the "This financial year" analytics preset rather than falling back to January, because a confidently-labelled year that is not the company's own year is worse than no preset.proposalAutoRemindersEnabledbooleanWhether the nightly sweep chases clients who have not answered a proposal (day 3, 7 and 14 after sending, plus a final nudge two days before it expires). Defaults to true. Not nullable - omit the key to leave it unchanged. Turning it off does NOT disable the manual Remind button, which is an explicit act by someone looking at the proposal.proposalGraceWindowDaysnumberDefault grace window in days for abandoned proposal signing ceremonies. Not nullable - omit the key to leave it unchanged.regionobjectISO 3166-1 alpha-2 country code for the company. Drives currency/date formatting (en-GB / en-IE) and can seed the currency and VAT defaults - see the PATCH endpoint description. Send null to clear it; omit the key to leave it unchanged.vatNumberobjectVAT registration number, source of the {{companyVatNumber}} merge token. Send null (or an empty string) to clear it; omit the key to leave it unchanged.
Responses
200CompanyProfileResponseDtoCompany profile updated400Invalid input401Not authenticated, or insufficient role404Tenant not found
POST/v1/tenants/current/logoDeprecatedUpload tenant logo (deprecated)
Requires an OWNER or ADMIN session, and a plan that includes custom branding. DEPRECATED: identical to `POST /v1/tenants/branding/logo` — both write the same `logoData` columns. This one skipped the custom-branding plan check until 2026-08-07, which made it a route to a paid feature without the plan. It now carries the same gate. Use the branding route; this remains only for callers already integrated against it.
Opens with: Signed-in session or API key
Request body
filebinary
Responses
200LogoUploadResponseDtoLogo uploaded successfully400Invalid file401Not authenticated403Plan does not include custom branding
DELETE/v1/tenants/current/logoDeprecatedRemove tenant logo (deprecated)
Requires an OWNER or ADMIN session. DEPRECATED: identical to `DELETE /v1/tenants/branding/logo`. Removal is deliberately NOT plan-gated on either route — a tenant that drops off a branding plan must still be able to take its logo down.
Opens with: Signed-in session or API key
Responses
200DeleteLogoResponseDtoLogo removed401Not authenticated404Tenant not found
GET/v1/tenants/current/statsGet current tenant statistics
Opens with: Signed-in session or API key
Responses
200TenantStatsDtoReturns tenant statistics401Not authenticated
Users
GET/v1/users/meGet current user profile
Opens with: Signed-in session or API key
Responses
200UserProfileResponseDtoReturns current user401Not authenticated
PATCH/v1/users/meUpdate current user profile
Opens with: Signed-in session or API key
Request body: UpdateProfileDto
avatarUrlstringAvatar URLfirstNamestringFirst namelastNamestringLast namephonestringPhone numbersenderDisplayNameOverrideobjectPer-user override for the "from" name on outbound envelope emails. When set, takes precedence over the tenant's senderDisplayName. Pass an empty string to clear and revert to the tenant default.
Responses
200UserProfileResponseDtoProfile updated400Invalid input401Not authenticated
PATCH/v1/users/me/passwordChange password
Opens with: Signed-in session or API key
Request body: ChangePasswordDto
currentPasswordstringRequiredCurrent passwordnewPasswordstringRequiredNew password (min 8 chars, must include uppercase, number, and special character)
Responses
200MessageResponseDtoPassword changed400Invalid input or password requirements not met401Not authenticated or current password incorrect
Verification
GET/v1/verification/{envelopeId}Verify an envelope
Public endpoint to verify the authenticity of a signed envelope
Opens with: No credential.
Parameters
envelopeIduuidpathRequiredThe envelope ID to verify
Responses
200objectVerification result404Envelope not found
Version
GET/versionWhat code is running here
Identity, not health. Requires the X-Cecur-Version-Key header. Answers 503 if the key is not configured on this host, and 401 — never 404 — if the presented key is wrong or absent, so that "misconfigured" stays distinguishable from "never implemented".
Opens with: No credential.
Parameters
x-cecur-version-keystringheaderRequiredShared secret from CECUR_VERSION_KEY. Compared in constant time.
Responses
200VersionResponseDto401The version key was missing or wrong.503CECUR_VERSION_KEY is not configured on this host.
Webhooks
GET/v1/webhooksList webhooks
Get all webhook endpoints for your account
Opens with: API key
Responses
200WebhookListResponseDtoList of webhooks
POST/v1/webhooksCreate a webhook
Register a new webhook endpoint to receive event notifications
Opens with: API key
Request body: CreateWebhookDto
eventsENVELOPE_SENT | ENVELOPE_VIEWED | ENVELOPE_COMPLETED | ENVELOPE_DECLINED | ENVELOPE_VOIDED | ENVELOPE_EXPIRED | RECIPIENT_COMPLETED | PROPOSAL_SENT | PROPOSAL_VIEWED | PROPOSAL_ACCEPTED | PROPOSAL_DECLINED | PROPOSAL_EXPIRED | PROPOSAL_TERM_ENDING | BOOKING_CREATED | BOOKING_CANCELLED | BOOKING_RESCHEDULED[]RequiredArray of event types to subscribe tourlstringRequiredThe https URL to receive webhook events. Must not resolve to a private, loopback or link-local address.
Responses
201WebhookResponseDtoWebhook created successfully400Invalid request or duplicate URL
GET/v1/webhooks/{id}Get a webhook
Get details of a specific webhook endpoint
Opens with: API key
Parameters
idstringpathRequired
Responses
200WebhookResponseDtoWebhook details404Webhook not found
PUT/v1/webhooks/{id}Update a webhook
Update webhook URL, events, or active status
Opens with: API key
Parameters
idstringpathRequired
Request body: UpdateWebhookDto
eventsENVELOPE_SENT | ENVELOPE_VIEWED | ENVELOPE_COMPLETED | ENVELOPE_DECLINED | ENVELOPE_VOIDED | ENVELOPE_EXPIRED | RECIPIENT_COMPLETED | PROPOSAL_SENT | PROPOSAL_VIEWED | PROPOSAL_ACCEPTED | PROPOSAL_DECLINED | PROPOSAL_EXPIRED | PROPOSAL_TERM_ENDING | BOOKING_CREATED | BOOKING_CANCELLED | BOOKING_RESCHEDULED[]Array of event types to subscribe toisActivebooleanWhether the webhook is activeurlstringThe https URL to receive webhook events. Must not resolve to a private, loopback or link-local address.
Responses
200WebhookResponseDtoWebhook updated404Webhook not found
DELETE/v1/webhooks/{id}Delete a webhook
Remove a webhook endpoint
Opens with: API key
Parameters
idstringpathRequired
Responses
200Webhook deleted404Webhook not found
GET/v1/webhooks/{id}/deliveriesGet webhook deliveries
View delivery history for a webhook
Opens with: API key
Parameters
idstringpathRequiredlimitnumberqueryMaximum number of deliveries to return (default: 50)
Responses
200WebhookDeliveryListResponseDtoList of deliveries404Webhook not found
POST/v1/webhooks/{id}/deliveries/{deliveryId}/retryRetry a failed delivery
Send a failed or dead letter webhook delivery again. If the delivery is waiting between automatic attempts, its next attempt is brought forward to run now (broughtForward: true) on the same schedule; otherwise it is re-queued with up to three attempts.
Opens with: API key
Parameters
idstringpathRequireddeliveryIdstringpathRequired
Responses
200Delivery re-queued, or its next automatic attempt brought forward400Cannot retry this delivery404Webhook or delivery not found409An attempt of this delivery is running or about to run; wait for it to finish
POST/v1/webhooks/{id}/regenerate-secretRegenerate webhook secret
Generate a new signing secret for webhook verification
Opens with: API key
Parameters
idstringpathRequired
Responses
200WebhookResponseDtoSecret regenerated404Webhook not found
POST/v1/webhooks/{id}/testTest a webhook
Send a test event to verify webhook is working
Opens with: API key
Parameters
idstringpathRequired
Responses
200TestWebhookResponseDtoTest result404Webhook not found
GET/v1/webhooks/dead-letterGet dead letter deliveries
View all permanently failed webhook deliveries across all webhooks
Opens with: API key
Parameters
limitnumberqueryMaximum number of deliveries to return (default: 50)
Responses
200WebhookDeliveryListResponseDtoList of dead letter deliveries