Docuseal API Structure for Creating and Managing Templates and Submissions: Complete REST Reference
Docuseal exposes a JSON-only REST API under the /api namespace that supports full CRUD operations for templates and submissions, secured via Bearer tokens or session cookies with CanCanCan authorization.
Docuseal is an open-source document signing platform hosted at docusealco/docuseal. The API structure for creating and managing templates and submissions follows RESTful conventions, exposing two primary resources—/api/templates and /api/submissions—that accept JSON payloads and return serialized responses via dedicated service objects.
Templates API Endpoints
The templates resource supports document template management, including schema definition, field configuration, and lifecycle operations.
List and Retrieve Operations
- List templates:
GET /api/templatessupports pagination, search via theqparameter, and filtering byarchived,folder,slug, andexternal_id. Implementation located atTemplatesController#index. - Retrieve single template:
GET /api/templates/:idreturns the template serialized byTemplates::SerializeForApi.call. SeeTemplatesController#show.
Update and Delete Operations
- Update template:
PATCH /api/templates/:idaccepts name, fields, submitters, folder assignment, and archived status. Strong parameters are defined inTemplatesController#update. - Delete template:
DELETE /api/templates/:idperforms a soft-delete (archive) by default. Append?permanently=trueto purge the record completely. SeeTemplatesController#destroy.
Clone and Nested Submissions
- Clone template:
POST /api/templates/:id/clonecreates a duplicate schema viaTemplatesCloneController#create. - Template submissions:
GET /api/templates/:id/submissionslists all submissions for a specific template, whilePOST /api/templates/:id/submissionscreates a new submission scoped to that template. These routes delegate toSubmissionsController.
Template Request Payload
Templates are accepted as a strong-parameter hash defined in template_params:
# app/controllers/api/templates_controller.rb (excerpt)
permitted_params = [
:name,
:external_id,
:shared_link,
{
submitters: [%i[
name uuid is_requester invite_by_uuid invite_via_field_uuid
optional_invite_by_uuid linked_to_uuid email order
]],
fields: [[:uuid, :submitter_uuid, :name, :type,
:required, :readonly, :default_value,
:title, :description, :prefillable,
{ preferences: {}, default_value: [], conditions: [%i[field_uuid value action operation]],
options: [%i[value uuid]],
validation: %i[message pattern min max step],
areas: [%i[uuid x y w h cell_w attachment_uuid option_uuid page]] }]]
}
]
Typical JSON body for template creation:
{
"template": {
"name": "Employment Contract",
"external_id": "emp-contract-v1",
"fields": [
{
"uuid": "f1",
"name": "Employee Name",
"type": "text",
"required": true
},
{
"uuid": "f2",
"name": "Start Date",
"type": "date",
"required": true
}
],
"submitters": [
{ "name": "Employee", "uuid": "s1", "email": "employee@example.com" },
{ "name": "HR Manager", "uuid": "s2", "email": "hr@example.com" }
]
}
}
Template Response Structure
Templates::SerializeForApi.call (located in [lib/templates/serialize_for_api.rb](https://github.com/docusealco/docuseal/blob/master/lib/templates/serialize_for_api.rb)) returns:
id,name,slug,fields,submitters,schema,authordocumentsarray containingid,uuid,url,preview_image_url,filenamefolder_name,application_key,expires_at
Submissions API Endpoints
The submissions resource handles document signing workflows, submitter invitations, and completion tracking.
List and Retrieve Operations
- List submissions:
GET /api/submissionssupports filtering bytemplate_id,slug,archived, andtemplate_folder. Implementation atSubmissionsController#index. - Retrieve submission:
GET /api/submissions/:idreturns submitters, documents, audit trail, and status viaSubmissionsController#show.
Create and Destroy Operations
- Create submission:
POST /api/submissionsis the primary endpoint for initiating signing workflows. Parameters are validated byParams::SubmissionCreateValidatorand normalized viaSubmissions::NormalizeParamUtils. SeeSubmissionsController#create. - Delete submission:
DELETE /api/submissions/:idarchives the record; use?permanently=truefor hard deletion. SeeSubmissionsController#destroy.
Collection Routes for Bulk Operations
The API provides specialized collection endpoints defined in [config/routes.rb](https://github.com/docusealco/docuseal/blob/master/config/routes.rb):
POST /api/submissions/init: Creates a submission without initial file uploads, useful for headless integrations.POST /api/submissions/emails: Bulk creates submissions from a list of email addresses.
Both routes utilize the same create action logic but handle different payload structures.
Submission Request Payload
The controller permits the following nested attributes via submissions_params:
# app/controllers/api/submissions_controller.rb (excerpt)
permitted_attrs = [
:send_email, :send_sms, :bcc_completed, :completed_redirect_url, :reply_to, :go_to_last,
:require_phone_2fa, :require_email_2fa, :expire_at, :name,
{
variables: {},
message: %i[subject body],
submitters: [[:send_email, :send_sms, :completed_redirect_url, :uuid, :name, :email,
:role, :completed, :phone, :application_key, :external_id,
:reply_to, :go_to_last, :require_phone_2fa, :require_email_2fa,
:order, :index, :invite_by,
{ metadata: {}, values: {}, roles: [], readonly_fields: [], message: %i[subject body],
fields: [:name, :uuid, :default_value, :value, :title, :description,
:readonly, :required, :validation_pattern, :invalid_message,
{ default_value: [], value: [], preferences: {}, validation: {} }] }]]
}
]
Example payload creating a submission from template ID 42:
{
"template_id": 42,
"send_email": true,
"expire_at": "2026-06-01T00:00:00Z",
"submitters": [
{
"email": "employee@example.com",
"name": "John Doe",
"role": "Employee",
"fields": [
{ "uuid": "f1", "value": "John Doe" },
{ "uuid": "f2", "value": "2023-05-01" }
]
},
{
"email": "hr@example.com",
"name": "HR Manager",
"role": "Manager"
}
],
"variables": { "company": "Acme Corp" },
"message": {
"subject": "Please sign the employment contract",
"body": "Hello, please review and sign the attached document."
}
}
Submission Response Structure
Submissions::SerializeForApi.call (located in [lib/submissions/serialize_for_api.rb](https://github.com/docusealco/docuseal/blob/master/lib/submissions/serialize_for_api.rb)) returns:
id,name,slug,expire_at,created_at,updated_at,archived_attemplate(includingid,name,external_id,folder_name)submittersarray withid,email,documents, andstatusvariables,documents(populated after completion),audit_log_url,combined_document_urlstatusenum:pending,completed,declined, orexpired
Authentication and Security
Both API resources are protected by CanCanCan authorization via load_and_authorize_resource. Requests require either:
- A valid Bearer token in the
Authorizationheader - An authenticated session cookie from a Docuseal user
Unauthorized requests return standard HTTP 401/403 responses.
Practical Code Examples
Create a Template
curl -X POST "https://your-docuseal-instance.com/api/templates" \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"template": {
"name": "NDA",
"external_id": "nda-2024",
"fields": [
{"uuid":"f1","name":"Company Name","type":"text","required":true},
{"uuid":"f2","name":"Effective Date","type":"date","required":true}
],
"submitters": [
{"name":"Counterparty","uuid":"s1","email":"partner@example.com"},
{"name":"Signer","uuid":"s2","email":"signer@example.com"}
]
}
}'
Response includes the template id, slug, and empty documents array (populated after PDF upload).
Create a Submission
curl -X POST "https://your-docuseal-instance.com/api/submissions" \
-H "Authorization: Bearer <TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"template_id": 123,
"expire_at": "2026-06-30T00:00:00Z",
"submitters": [
{
"email":"john.doe@example.com",
"name":"John Doe",
"role":"Employee",
"fields":[
{"uuid":"f1","value":"Acme Corp"},
{"uuid":"f2","value":"2023-05-01"}
]
},
{
"email":"hr@example.com",
"name":"HR Manager",
"role":"Manager"
}
],
"message": {"subject":"Please sign", "body":"Kindly sign the attached NDA."}
}'
Success returns submitter signing URLs:
{
"submitters": [
{
"id": 321,
"url": "https://your-docuseal-instance.com/s/321?token=abc123"
},
{
"id": 322,
"url": "https://your-docuseal-instance.com/s/322?token=def456"
}
],
"expire_at": "2026-06-30T00:00:00Z",
"created_at": "2026-05-05T12:34:56Z"
}
Retrieve Completed Submission
curl -X GET "https://your-docuseal-instance.com/api/submissions/789" \
-H "Authorization: Bearer <TOKEN>"
Returns status: "completed", documents array with download URLs, audit_log_url, and combined_document_url.
Summary
- The Docuseal API structure consists of two primary REST resources:
/api/templatesand/api/submissions, both requiring Bearer token or session authentication. - Templates support CRUD operations plus cloning (
POST /api/templates/:id/clone) and nested submission listing, with payloads validated viatemplate_paramsand serialized viaTemplates::CreateOrUpdateandSerializeForApi. - Submissions support creation with complex submitter configurations, bulk email endpoints (
/api/submissions/emails), and headless initialization (/api/submissions/init), usingSubmissions::NormalizeParamUtilsfor payload processing. - Both resources implement soft-delete by default (use
?permanently=truefor hard deletion) and return JSON structured by dedicated serialization classes inlib/templates/serialize_for_api.rbandlib/submissions/serialize_for_api.rb.
Frequently Asked Questions
What authentication method does the Docuseal API use?
The API uses Bearer token authentication via the Authorization header or session cookies from authenticated Docuseal users. All endpoints invoke load_and_authorize_resource from CanCanCan to enforce resource-level permissions.
How do I permanently delete a template or submission via the API?
By default, DELETE /api/templates/:id and DELETE /api/submissions/:id perform soft-deletion (archiving). Append ?permanently=true to the query string to trigger hard deletion, which removes the record from the database entirely.
What is the difference between POST /api/submissions and POST /api/submissions/init?
POST /api/submissions accepts full template and submitter data including initial file uploads. POST /api/submissions/init is a collection route shortcut that creates a submission without requiring file attachments, designed for headless integrations where documents are uploaded later.
How are webhook events triggered in the Docuseal API?
Webhook events (such as template.created, template.updated, submission.created) are enqueued via WebhookUrls.enqueue_events calls within the controller actions. These background jobs dispatch payloads to configured webhook URLs asynchronously after database transactions commit.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →