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
documentobject or adocumentsarray. - 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 asequence(a string like"1") and atemplateId(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, withdocumentId,name,fileExtension, anddocumentBase64. To attach more than one runtime document to the same composite template, use thedocumentsarray instead — a single composite template should not carry both.inlineTemplates— an array of inline definitions. Each has its ownsequenceand can carrydocuments,recipients,customFields, and tab definitions attached to recipients. Recipients here merge with the server template's recipients by matchingroleName(andrecipientId), which is how you fill in names and emails at request time without editing the template.recipientsandtabs— insideinlineTemplates, 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:
- The
documentIdmatch is what triggers replacement. The value"1"above must equal thedocumentIdof 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. - 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. - 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:
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 uniquedocumentId. 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
documentIddid 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/yPosonto the new file. If your generated document's layout differs from the template's original file, switch those tabs toanchorString-based positioning, or setanchorIgnoreIfNotPresentwhere a match may not exist. - [ ] Sequence errors.
sequencevalues forserverTemplatesandinlineTemplateswithin 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. - [ ]
documentanddocumentsmixed. Use one or the other per composite template. If you need multiple runtime documents in one composite template, list them all indocumentsand leavedocumentout. - [ ] Envelope stuck in
created.statusmust 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.








