Migrating to API v2
API v2 is the current version of the DocSpring API. Our 4.x API clients call it, and the 3.x clients call API v1. API v1 keeps working, so you can move one integration at a time: an API token works with both.
This page lists everything that changes. The API v2 reference documents each endpoint, and the API v1 reference is still there for comparison.
The base URL
Section titled “The base URL”Send requests to /api/v2 on the same hosts:
https://sync.api.docspring.com/api/v2 (US)https://sync.api-eu.docspring.com/api/v2 (EU)The 4.x clients use these by default. The Go client’s module path is github.com/docspring/docspring-go/v4. If you
set the full base URL in your client’s configuration, change /api/v1 in it to /api/v2: the clients only pick the
version in the URLs they build from a region or host.
Operations
Section titled “Operations”Most operations keep their path. They’re named after the resource they act on, so several SDK methods have new names:
| API v1 (3.x clients) | API v2 (4.x clients) |
|---|---|
generatePdf | createSubmission |
batchGeneratePdfs | createSubmissionBatch |
generatePreview | generateSubmissionPreview |
combinePdfs (POST /combined_submissions) | createCombinedPdf (POST /combined_pdfs) |
getCombinedSubmission, listCombinedSubmissions | getCombinedPdf, listCombinedPdfs (/combined_pdfs) |
expireCombinedSubmission | expireCombinedPdf (DELETE /combined_pdfs/{combined_pdf_id}) |
createHtmlTemplate, createPdfTemplateFromUpload | createTemplate, with a template_type of html or pdf |
createPdfTemplate (a multipart PDF upload) | uploadTemplate (POST /templates/upload) |
getTemplate | getTemplate; lists return summaries |
getFullTemplate (?full=true) | getTemplate, without the template’s fields for now |
updateTemplateDocumentFromUpload | updateTemplateDocument (PUT /templates/{template_id}/document) |
updateTemplateDocument (a multipart PDF upload) | uploadTemplateDocument (PUT /templates/{template_id}/document/upload) |
moveTemplateToFolder | moveTemplate |
moveFolderToFolder | moveFolder |
getDataRequest, updateDataRequest | getSignatureRequest, updateSignatureRequest (/signature_requests) |
createDataRequestToken, createDataRequestEvent | createSignatureRequestToken, createSignatureRequestEvent |
API v2 also has getFolder, listCustomFiles, getCustomFile and deleteCustomFile.
Requests
Section titled “Requests”- Bodies are flat. v1 wrapped some bodies in a key (
{"template": {...}},{"folder": {...}}); v2 takes the properties at the top level. - Validation is strict. A property or query parameter the endpoint doesn’t have, a value of the wrong type or a
value outside an enum is a
422with avalidation_failederror. v1 ignored unknown properties. - The 4.x SDKs check each request before sending it, against the same request schemas the API uses. A request the
API would reject fails straight away with the SDK’s validation error, which has the details the
422would have. Each SDK’s README names the error and the setting that turns the check off. - Bodies are JSON. Send
Content-Type: application/json: any other content type is a415. (v1 read form-encoded and plain-text bodies as JSON.) Only the PDF uploads (uploadTemplateanduploadTemplateDocument) takemultipart/form-data. - Text is UTF-8. A JSON body or query string that isn’t valid UTF-8 is a
400with aninvalid_requesterror. v1 replaced the bytes it couldn’t read with�, so a name sent as Latin-1 printed asJos�instead ofJosé. - Bodies have a size limit: 50 MB to create a submission, 100 MB for a PDF upload, and 10 MB for everything else. A
larger body is a
413with arequest_too_largeerror. Send images as URLs instead of base64 data. - Query parameters:
version(the template version to use) andwaitare query parameters, not body properties. The submission lists taketest(trueorfalse) instead oftype.getSubmissionalways returns the data, andgetSubmissionBatchalways returns the submissions (include_dataadds their data), so they have noinclude_dataorinclude_submissions.createSignatureRequestTokentakes itstypein the body. - Lists use cursors. Send
limit(1 to 100, default 50), and pass the response’snext_cursorascursorto get the next page.pageandper_pageare gone. - Every name is snake_case, including the settings that v1 took in camelCase: template
defaults(font_size,date_format),html_engine_options(margin_top,print_background), a field dependency condition’srange_max, and the field settings in a submission’sfield_overrides. - Templates need a
template_type. v1 created a PDF template with no PDF when it was left out. - A test API token can’t create live submissions.
test: falsewith a test token is a422. - Authentication is an API token in the
Authorizationheader (HTTP Basic). API v2 doesn’t accept the unauthenticated requests that v1 allowed for templates with public submissions, or signature request tokens.
Submission data
Section titled “Submission data”v2 checks submission data more strictly:
- An empty
datais checked against the template’s fields like any other. - Date fields take
YYYY-MM-DDdates. When a PDF template’sparse_dates_using_template_date_formatis on, they also take NN/NN/YYYY dates in the order of the template’s date format. v1 accepted any string and read numeric dates day first. - Email fields take an email address, such as
[email protected], or an empty string. v1 accepted any string. - A value for a field shown as a barcode or QR code must be one it can show: the right number of digits for an EAN or UPC barcode, only ASCII characters for Code 128, not too long for a QR code. v1 accepted it and the submission failed when the PDF was generated.
- Each signature request needs
fieldsand anauth_type, and each field can be in only one signature request. A field thatdataalready fills in needsprefilled_values_editable. A field name the template doesn’t have is an error, unless the signature request setsignore_unknown_fields. - Only the signers can sign a submission that has signature requests. A value for a signature or initials field, in
dataor in a field override’sdefault, makes it aninvalid_datasubmission.editable: true, on the submission or infield_overrides, is a422, because signed PDFs are always static. v1 also makes the submissioninvalid_datafor signature values, and makes the signed PDF static whatevereditablesays. - A formula or condition can only use
your
dataand the fields of the signer who fills in its field. A formula field that no signature request asks for counts as the field of the one signer whose fields it uses, so leave a calculated total out offieldsto keep it calculated. A signature request whose field another signer’s formula or condition uses is a422naming the field, and so is each signer’s field in a formula that no signature request asks for and that uses more than one signer’s fields: otherwise one signer’s values could change what another signer saw and signed. v1 accepted it. - Each field in
field_overridestakes an object of field settings, checked against that field. These are a422: a setting that fields don’t have (such asfontSizeinstead offont_size), a value the field can’t take (such as a typeface the editor doesn’t offer, or a color that isn’t 6 hex digits), a setting for another kind of field (such ascheck_characteron a field displayed as text), and a setting that only the template sets (such astypeordescription). v1 ignored them, or failed the PDF.
Data that doesn’t match the template is still saved as an invalid_data submission, as in v1, so you can find it in
DocSpring. The response is a 422 with an invalid_data error that names the submission:
{ "error": { "code": "invalid_data", "message": "The data doesn't match the template's fields: /data/last_name is required", "details": [ { "code": "required", "message": "is required", "pointer": "/data/last_name" } ], "request_id": "req_1234567890abcdef01", "submission_id": "sub_1234567890abcdef01" }}Responses
Section titled “Responses”A response is the resource itself. API v1 wrapped it with a status:
{ "status": "success", "submission": { "id": "sub_1234567890abcdef01", "state": "processed" }}API v2 returns the submission:
{ "id": "sub_1234567890abcdef01", "state": "processed" }- Creates return
201, and other successful requests200.DELETEreturns the resource as it was deleted. - Lists return
{"data": [...], "next_cursor": "..."}.next_cursorisnullon the last page. - Submissions, batches, combined PDFs, signature requests, templates, folders, custom files and webhooks all have
created_atandupdated_at. - A resource in another account is a
404, like one that doesn’t exist. - Renamed: a submission’s
json_schema_errorsisdata_errors, and a template’slast_changed_by_type,last_changed_by_idandlast_changed_by_external_*are onelast_changed_byobject. - Gone: a template’s
demo,first_template,template_schema_versionandexternal_predefined_fields_template_*, a submission’sreferrer, a signature request’sorder(usesort_order), a combined PDF’ssubmission_ids(itssource_pdfslist them) andexpires_in.
Errors
Section titled “Errors”Every error, from every endpoint, has one shape:
{ "error": { "code": "validation_failed", "message": "Invalid request: /colour isn't a documented property", "details": [ { "code": "unknown_property", "message": "isn't a documented property", "pointer": "/colour" } ], "request_id": "req_1234567890abcdef01" }}code says what went wrong (validation_failed, invalid_data, not_found, insufficient_credits,
upgrade_required, rate_limited, polling_timeout and others). Each detail’s pointer is a JSON pointer to the
body value it’s about, and a detail about a query parameter has its name in parameter instead. A detail about the
request as a whole has neither. details is always a list. When the error is about a record that was saved (an
invalid_data submission, a submission waiting for credits, or a record still being processed after a 504), its ID
is in submission_id, submission_batch_id, combined_pdf_id or template_id. v1’s status, error string and
errors list are gone.
A submission’s or combined PDF’s files are in files:
"files": { "pdf": { "state": "processed", "error_message": null, "download_url": "https://downloads.docspring.com/store/...pdf?Expires=...&Signature=...&Key-Pair-Id=...&Hash-Algorithm=SHA256", "permanent_url": "https://app.docspring.com/submissions/sub_1234567890abcdef01/files/pdf", "filename": "sub_1234567890abcdef01.pdf", "content_type": "application/pdf", "size": 21843, "sha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08" }}files.pdf replaces download_url, permanent_download_url and pdf_hash (now sha256). A submission’s files also
has the audit_trail of its signature requests (v1’s audit_trail_download_url) and its preview PDF (v1’s
preview_download_url), with the same properties. Each file is left out until it exists, and files is empty once the
submission has expired. A file made after its submission was processed is listed while it’s being made, so check its
state (pending, processed or error, with error_message) before using its links. A template’s PDF has its
temporary link in document.download_url (v1’s document_url), and a custom file in download_url (v1’s url).
Templates
Section titled “Templates”readysays whether the template can generate PDFs: HTML templates always can, PDF templates once their PDF is processed. A PDF template’sdocumenthas the PDF’sfilename,state,page_count,page_dimensionsand links. They replacedocument_processed,document_state,document_parse_errorand the otherdocument_*andpage_*fields. AI field setup’s progress is indocumenttoo:page_processing_statusesandfield_logic_status(as in v1), andhas_fillable_fields(v1’shas_acroform_fields). Templates don’t returndetect_fields: send it when you create a template, withfield_source,field_namingandfield_logic(see Automatic field detection).- Lists return template summaries;
getTemplatereturns the template with itsdefaults,field_dependencies, HTML and versions, but not its fields yet (see Operations). - Templates have no
webhook_urlorwebhook_options. Create a webhook withtemplate_idsinstead. embed_domainsis a list (v1’s comma-separated string), and each entry is checked: a host such asapp.example.com, with an optional leading*., port andhttporhttpsscheme.copyTemplatewithparent_folder_id: nullputs the copy at the root, asmoveTemplatedoes. Leaveparent_folder_idout to put it in the template’s own folder (v1 did that fornulltoo).getTemplateSchemaandgetTemplateModelCodedescribe the version that submissions use by default (the latest published version, or the draft when none has been published), where v1 described the draft. Passversionfor another one.- A
field_dependenciescondition takes only what itsoperatoruses:presentandblanktake novalue,equalsandnot_equalstake a string, number or boolean,intakes a list of them,gt,gte,ltandltetake a number (or a number as a string) and only work on a number field, andrangetakesvalue,range_maxor both. API v1 saved any of them, and conditional logic skipped the conditions it couldn’t use; API v2 returns a422. Templates return their conditions in this shape.
Secrets
Section titled “Secrets”Secrets are write-only. A submission’s or combined PDF’s password, and a template’s encrypt_pdfs_password and
slack_webhook_url, aren’t returned. has_password, has_encrypt_pdfs_password and has_slack_webhook_url say
whether one is set.
Combined PDFs
Section titled “Combined PDFs”Combined submissions are called combined PDFs. Their sources are always source_pdfs, each with a type
(submission, combined_pdf, template, custom_file or url). A template source uses the template’s processed PDF
unless you set processed: false. Each source takes only its type’s properties: id for a submission, combined PDF,
template or custom file, url for a URL, and template_version and processed for a template. Any other is a 422.
Signature requests
Section titled “Signature requests”Data requests are called signature requests: the /signature_requests endpoints, a submission’s signature_requests
(also in the body that creates it), the SignatureRequest models in the 4.x clients and the signature_request.*
webhook events. A submission waiting for them is in the waiting_for_signature_requests state, and the template
setting add_data_request_submission_id_footers is add_signature_request_submission_id_footers. They’re the same
records in both versions, so an ID from API v1 works in API v2. New ones get IDs that start with srq_ instead of
drq_, whichever version creates them.
A signature request’s data holds only the values its own signer filled in.
A token’s link opens the signing form for 24 hours, or 7 days for an email token, and a form that opened in time
keeps working for 7 more days. Create a token each time you open the form or send a link.
To leave a signer out, give each signature request its role (Signer 1, Signer 2 and so on: Signer 1 fills the
template’s @@signature_requests/0 fields) and leave that signer’s out. v1’s skipped placeholders are gone, and so are
fields and auth_type’s exemption for them: every signature request needs both.
Waiting for PDFs
Section titled “Waiting for PDFs”Requests to sync.api.docspring.com that create a submission, batch, combined PDF or template PDF wait until it’s
processed, then return it in its final state. A failed PDF is a 201 with a failure state (error, syntax_error,
image_download_failed or image_processing_failed) and an error_message. GET requests don’t wait. If the PDF
isn’t ready in time, the response is a 504 with a polling_timeout error and the record’s ID. See
Synchronous Requests.
Webhooks
Section titled “Webhooks”Webhooks you create with API v2 send version 4 payloads: each event’s data is
the resource as API v2 returns it, and events use API v2’s names (signature_request.completed,
combined_pdf.processed).
Webhooks you created with API v1 or in the dashboard keep their version, so you can move your webhook endpoint
separately.