September 2, 2026

DocuSign API Creating Composite Templates With Server-Side Documents: A Working Example

Summary · 11 min read

DocuSign API creating composite template server side documents example: merge server templates and runtime docs in one envelope. JSON + curl included.

A DocuSign API creating composite template server side documents example boils down to one request: POST /restapi/v2.1/accounts/{accountId}/envelopes with a compositeTemplates array in the body. Each entry layers three building blocks: a serverTemplates element referencing a template stored in your DocuSign account, a document (or documents) object carrying a runtime-generated or uploaded file as Base64, and inlineTemplates that supply recipients, roles, and tabs. Because a composite template can replace the template's static document with a file your server just generated, it is the standard pattern for merging dynamic documents with preconfigured signing logic.

This guide walks through the JSON structure, gives you a complete curl request, shows the same call in Python, and ends with a decision table and a troubleshooting checklist for the errors that bite most teams.

What a DocuSign composite template is, and when you need one

A normal envelope request that uses a template passes a single templateId at the top level of the envelope definition, along with templateRoles to fill in the recipients. That works well when the document inside the template is fixed: the PDF you uploaded when you built the template is the PDF every signer receives.

The limitation appears when your application generates documents at runtime. Contracts pre-filled with CRM data, offer letters rendered per candidate, invoices assembled per customer — none of these exist when the template is designed, so none of them can live inside the template. A composite template solves this by letting one envelope definition combine:

  • Server-side templates you already configured in your DocuSign account (documents, recipient routing, tabs, merge fields).
  • Server-side documents supplied in the request itself, either as a Base64-encoded document object or a documents array.
  • Inline templates that define or override recipients and tabs directly in the JSON.

The key behavior is document replacement. When the document you supply in the request uses the same documentId as a document inside the referenced server template, DocuSign swaps your runtime file in place of the template's static file while keeping the template's recipients and tabs. If the documentId does not match anything in the template, the document is simply appended to the envelope. This replace-or-append mechanic is exactly what the "server side documents" part of the keyword refers to: your server supplies the bytes, the template supplies the signing logic.

If your team is still weighing whether to invest in this level of API integration at all, it is worth stepping back and reviewing the broader benefits of integrating electronic signature APIs before committing to a specific envelope pattern.

Anatomy of the compositeTemplates JSON structure

According to the DocuSign eSignature REST API reference (v2.1), compositeTemplates is a top-level array on the envelope definition. Each element looks like this:

```json

{

"compositeTemplates": [

{

"compositeTemplateId": "1",

"serverTemplates": [

{

"sequence": "1",

"templateId": "YOUR_TEMPLATE_ID"

}

],

"document": {

"documentId": "1",

"name": "agreement.pdf",

"fileExtension": "pdf",

"documentBase64": "JVBERi0xLjQK..."

},

"inlineTemplates": [

{

"sequence": "2",

"recipients": {

"signers": [

{

"recipientId": "1",

"roleName": "Client",

"name": "Ada Lovelace",

"email": "ada@example.com"

}

]

}

}

]

}

]

}

```

Field by field:

  • compositeTemplateId — an optional string identifier for this composite template, useful when you reference it elsewhere in the request.
  • serverTemplates — an array of templates already stored in your account. Each entry carries a sequence (a string like "1") and a templateId (the GUID shown on the template's details page). The template contributes its documents, recipients, tabs, and routing order.
  • document — a single document supplied in the request, with documentId, name, fileExtension, and documentBase64. To attach more than one runtime document to the same composite template, use the documents array instead — a single composite template should not carry both.
  • inlineTemplates — an array of inline definitions. Each has its own sequence and can carry documents, recipients, customFields, and tab definitions attached to recipients. Recipients here merge with the server template's recipients by matching roleName (and recipientId), which is how you fill in names and emails at request time without editing the template.
  • recipients and tabs — inside inlineTemplates, recipient objects (signers, carbon copies, and so on) accept the same tab arrays you would use anywhere else: signHereTabs, dateSignedTabs, textTabs, fullNameTabs, and the rest.

Two ordering rules matter. First, the sequence values of serverTemplates and inlineTemplates within one composite template must be unique, and together they define the layering order — later entries override earlier ones for the same role or field. Second, the order documents appear in the final envelope follows documentId, not the order of the arrays, which is why keeping your IDs deliberate avoids surprises when signers page through the packet.

Tabs are where the two worlds meet. Tabs defined in the server template carry over to the replaced document. Tabs that use anchorString positioning (search for text in the document, such as "anchorString": "Please sign here:") re-attach to your runtime file wherever that text appears; tabs with fixed xPos/yPos coordinates apply the same coordinates to the new file, which works only if your generated document keeps an identical layout. When your runtime documents vary in layout, prefer anchor strings.

Full example: replacing a template document with a server-side document

Here is a complete, runnable request against the DocuSign developer sandbox (demo.docusign.net). It takes an agreement template that contains a document with documentId "1", replaces that document with a PDF your server just generated, fills in the signer's details through an inline template, and sends the envelope.

First, the envelope definition, saved as envelope.json:

```json

{

"emailSubject": "Your agreement is ready to sign",

"status": "sent",

"compositeTemplates": [

{

"compositeTemplateId": "1",

"serverTemplates": [

{

"sequence": "1",

"templateId": "YOUR_TEMPLATE_ID"

}

],

"document": {

"documentId": "1",

"name": "agreement-generated.pdf",

"fileExtension": "pdf",

"documentBase64": "JVBERi0xLjQK..."

},

"inlineTemplates": [

{

"sequence": "2",

"recipients": {

"signers": [

{

"recipientId": "1",

"roleName": "Client",

"name": "Ada Lovelace",

"email": "ada@example.com"

}

]

}

}

]

}

]

}

```

Then the curl call:

```bash

curl --request POST \

"https://demo.docusign.net/restapi/v2.1/accounts/YOUR_ACCOUNT_ID/envelopes" \

--header "Authorization: Bearer " \

--header "Content-Type: application/json" \

--data @envelope.json

```

A successful response returns 201 Created with the new envelope's envelopeId, URI, status, and a recipients summary you can log for auditing.

Three implementation notes:

  1. The documentId match is what triggers replacement. The value "1" above must equal the documentId of the document inside the template you want to replace. Change it to an unused ID (for example "2") and your file is appended next to the template's document instead — sometimes desired, often a surprise.
  2. Use status: "created" to build a draft envelope you can inspect before sending. It is the cheapest way to visually confirm tab placement before you involve real signers.
  3. Base64 payloads get large. A multi-megabyte PDF inflates the JSON body by roughly a third; if request size becomes a problem, check the current official documentation for transfer options such as chunked uploads.

You can layer more onto this skeleton. Adding a second entry to serverTemplates (with sequence "3", for example) merges two stored templates into one envelope — handy when a packet combines an NDA template and a services agreement template. Adding tabs under the inline template's signer, as in "tabs": { "signHereTabs": [{ "anchorString": "Client signature:", "anchorXOffset": "0", "anchorYOffset": "0", "anchorUnits": "pixels" }] }, adds signature placement driven by your runtime document's text rather than fixed coordinates. And if your signing flow runs inside your own web app rather than over email, the embedded sending vs. remote sending API distinction determines whether you generate a recipient view URL or let DocuSign email signers directly.

Running the same request in Python

The identical call in Python, using requests, reads a local PDF, encodes it, and posts the envelope:

```python

import base64

import json

import requests

API_BASE = "https://demo.docusign.net/restapi/v2.1"

ACCOUNT_ID = "YOUR_ACCOUNT_ID"

ACCESS_TOKEN = "YOUR_ACCESS_TOKEN"

TEMPLATE_ID = "YOUR_TEMPLATE_ID"

with open("agreement.pdf", "rb") as f:

pdf_base64 = base64.b64encode(f.read()).decode("ascii")

envelope = {

"emailSubject": "Your agreement is ready to sign",

"status": "sent",

"compositeTemplates": [

{

"compositeTemplateId": "1",

"serverTemplates": [

{"sequence": "1", "templateId": TEMPLATE_ID}

],

"document": {

"documentId": "1",

"name": "agreement-generated.pdf",

"fileExtension": "pdf",

"documentBase64": pdf_base64,

},

"inlineTemplates": [

{

"sequence": "2",

"recipients": {

"signers": [

{

"recipientId": "1",

"roleName": "Client",

"name": "Ada Lovelace",

"email": "ada@example.com",

}

]

},

}

],

}

],

}

response = requests.post(

f"{API_BASE}/accounts/{ACCOUNT_ID}/envelopes",

headers={

"Authorization": f"Bearer {ACCESS_TOKEN}",

"Content-Type": "application/json",

},

data=json.dumps(envelope),

)

response.raise_for_status()

print(response.json()["envelopeId"])

```

Swap the demo host for https://www.docusign.net in production, obtain the access token through your usual OAuth flow, and keep credentials out of source control — the placeholders above exist for a reason.

Once the envelope completes, your integration usually has downstream work: retrieving what signers actually entered. The pattern for getting tab data and form fields from signed DocuSign documents via the recipients and envelope-form-data endpoints pairs naturally with this sending flow, as does downloading the certificate of completion as a combined PDF for your compliance archive.

Composite templates vs. a plain templateId: how to choose

Composite templates add JSON complexity, so before reaching for one, confirm your scenario actually needs it. This decision table covers the common cases:

Your scenarioRecommended approachWhy
Fixed document, changing recipientsTop-level templateId + templateRolesSimplest request; no Base64 payload
Runtime document, stable tabsComposite template with document replacementTemplate logic survives; only the file changes
Combining multiple templatesComposite template with several serverTemplates entriesTop-level templateId accepts only one template
Runtime tabs via anchorsComposite template with document + inlineTemplates tabsTabs travel with the request, not the template
Converting PDF form fieldsComposite template with transformPdfFieldsField conversion happens per uploaded document
Filling template merge fieldsTop-level templateId + customFieldsNo document replacement needed

A related cost consideration: envelope-heavy integrations are exactly the workload where API rate limits and account tiers start to matter, so read this DocuSign vs. Dropbox Sign API comparison on rate limits and pricing tiers alongside DocuSign's own limits documentation when you architect for scale.

Troubleshooting checklist for common composite template errors

When the request fails or produces a strange envelope, run through this checklist in order:

  • [ ] Duplicate documentId. Every document in the final envelope must have a unique documentId. If your runtime document collides with a template document you did not intend to replace, the API returns an error about the ID already being in use. Assign unused IDs deliberately.
  • [ ] Replacement did not happen — the envelope has both documents. Your uploaded document's documentId did not match any document inside the referenced template. Confirm the template's internal document IDs (the template's JSON or the DocuSign web UI shows them) and reuse the exact value.
  • [ ] Tabs landed in the wrong place after replacement. Fixed-position tabs carry their xPos/yPos onto the new file. If your generated document's layout differs from the template's original file, switch those tabs to anchorString-based positioning, or set anchorIgnoreIfNotPresent where a match may not exist.
  • [ ] Sequence errors. sequence values for serverTemplates and inlineTemplates within one composite template must be unique (DocuSign's docs describe them as defining processing order). A repeated value typically produces a validation error naming the offending entry.
  • [ ] Recipient merge failures. Inline recipients match template recipients by roleName. A typo in the role name leaves the template's placeholder recipient unresolved, which surfaces as an unassigned-recipient or unsent-envelope error at send time.
  • [ ] document and documents mixed. Use one or the other per composite template. If you need multiple runtime documents in one composite template, list them all in documents and leave document out.
  • [ ] Envelope stuck in created. status must be "sent" to email recipients immediately; "created" builds a draft deliberately. Check which one your code sets before assuming delivery failed.

One caveat: DocuSign's validation and error messages evolve between API versions and account configurations, so treat this checklist as a triage guide and confirm exact error wording against the official createEnvelope reference when a message does not match.

Dynamic Documents Without the JSON Assembly Line: Nota Sign

Composite templates solve a real problem, but they also show how much ceremony dynamic documents can demand: Base64 payloads, sequence arithmetic, role-name coupling, and documentId bookkeeping just to merge one generated PDF with one template. If your backlog is filling up with envelope-assembly glue code, evaluate Nota Sign — the global e-signature platform of FaDaDa — as the API foundation for document-heavy signing flows before that glue hardens into architecture.

The platform underneath the API is built for production at scale: FaDaDa has been ranked No.1 in China's e-signature software market by IDC for consecutive years, legal coverage spans 100+ countries and regions, and APAC deployments get iAM Smart and Singpass integrations, SES/AES/QES signature levels, and regional data centers for residency requirements. Pricing follows the same low-friction philosophy: no per-seat fees for the engineers and operators building the integration, and tailored plans for the volumes you actually project.

Describe your use case — generated documents, templates, cross-border signers — and ask how the API handles it: contact Nota Sign.

FAQ

Nota Sign helps businesses build compliant agreement workflows, and our content follows strict editorial guidelines.

Discover a better way to e-sign your documents

Start for Free
Contact Sales