Outcome
Create a signature template: a reusable document, field schema, signer roles and default subject and message that envelopes can be seeded from. Only an active template can seed an envelope.
Before you start
- Verify the environment, authenticated identity and specifically approved organization context.
- The published application permission is workspace:write. Resolve and bind the exact canonical Sign workspace. A supplied id or organization membership alone grants no workspace access.
- Session callers require read_write or admin workspace access. API-key resource binding is verified by Auth; never treat a caller-supplied header as an independent grant.
- Approve the documented mutation and actual recipient/document targets before CLI --confirm or MCP confirm:true. A synthetic fixture authorizes no live agreement, notification or data deletion.
Discover the target
topolo whoami --json
topolo actions get app_topolo_sign.templates.create --detail --json
topolo resources topolo-sign --json
topolo workspaces list topolo-sign --json
- Set SIGN_WORKSPACE_ID to the exact granted workspace returned by discovery.
- Discover documentId and documentRef from upload or authorized retained metadata; they are distinct identifiers.
Request
POST /api/templates
Inspect and test this action. Save the reviewed input as payload.json. Supply resource context separately.
Input fields
| Field | Required | Meaning | Constraints |
|---|---|---|---|
name | Required | Nonempty template display name. | {"minLength":1} |
description | Optional | Template description, distinct from agreement content. | {} |
status | Optional | Template draft, active or archived state. Only active templates seed envelopes. | {"enum":["draft","active","archived"]} |
documentTitle | Optional | Human document title metadata. | {"minLength":1} |
documentRef | Optional | Exact immutable document.documentRef returned by documents.upload. Never invent an object key or file path. | {"minLength":1} |
defaultSubject | Optional | Subject copied into new envelopes from this template. | {"minLength":1} |
defaultMessage | Optional | Message copied into new envelopes from this template. | {} |
signerRoles | Optional | Template role names, supplied as a string list or supported comma-separated string. | {} |
fields | Optional | Structured version-2 field objects with type, label, assignee and page placement. Geometry uses top-left page fractions. Send freezes the source and fields. | {"maxItems":200} |
fieldSchema | Optional | Legacy field-name input upgraded by the current source; use structured fields for explicit assignee, capacity and geometry. | {} |
Read the complete input schema.
Effects
- Creates a reusable template holding a document, field schema, signer roles and default subject and message.
- Only a template in status "active" can seed an envelope.
Interpret the response
Read $.template.id for the returned template identifier. An empty collection does not discover a target.
Expect 201 and read $.template.id. Confirm the status is "active" before trying to seed an envelope from it.
Inspect the success schema. CLI/MCP add their transport envelope; --detail requests the full CLI result. HTTP 204 contains no JSON payload.
Worked examples
Captured from the exported Sign Worker with the existing SQLite-backed D1 harness applying all core, envelope and audit migrations to one local database, in-memory R2 and synthetic Auth/workspace/archive/Notify transports. These examples prove local validation, persistence and artifact production. They do not prove separate deployed D1 topology, real recipient consent, provider delivery or a live signed agreement. All identities, signing links and documents are synthetic; replace identifiers through authorized discovery and never execute a fixture against a live organization. Binary download examples use the released SDK binary projection; tests Base64-decode and compare original bytes, lengths and server SHA-256 headers.
Supported request · Create an active template bound to the stored PDF
Input · payload.json:
{
"name": "Fixture agreement",
"status": "active",
"documentRef": "sign-document:92c3741e-171c-439c-9419-597ffe485224",
"signerRoles": [
"Signer"
],
"fields": [
{
"id": "fixture_signature",
"type": "signature",
"label": "Signature",
"required": true,
"assignee": {
"role": "Signer",
"capacity": "personal"
},
"placement": {
"page": 1,
"x": 0.1,
"y": 0.7,
"width": 0.3,
"height": 0.08
}
}
],
"defaultSubject": "Review the fixture",
"defaultMessage": "Synthetic local example only."
}
CLI:
topolo actions validate app_topolo_sign.templates.create --data @payload.json --json
topolo actions plan app_topolo_sign.templates.create --resource-type workspace --resource-id "$SIGN_WORKSPACE_ID" --data @payload.json --json
topolo actions call app_topolo_sign.templates.create --resource-type workspace --resource-id "$SIGN_WORKSPACE_ID" --data @payload.json --confirm --detail --json
MCP tool and arguments:
{
"tool": "topolo_call_action",
"arguments": {
"actionId": "app_topolo_sign.templates.create",
"input": {
"name": "Fixture agreement",
"status": "active",
"documentRef": "sign-document:92c3741e-171c-439c-9419-597ffe485224",
"signerRoles": [
"Signer"
],
"fields": [
{
"id": "fixture_signature",
"type": "signature",
"label": "Signature",
"required": true,
"assignee": {
"role": "Signer",
"capacity": "personal"
},
"placement": {
"page": 1,
"x": 0.1,
"y": 0.7,
"width": 0.3,
"height": 0.08
}
}
],
"defaultSubject": "Review the fixture",
"defaultMessage": "Synthetic local example only."
},
"resourceType": "workspace",
"resourceId": "<exact granted id from discovery>",
"confirm": true,
"detail": true
}
}
Captured exported Worker response · HTTP 201:
{
"template": {
"id": "8a821f8b-b0d1-4808-a92e-8d3f16e4228f",
"name": "Fixture agreement",
"description": "",
"status": "active",
"documentTitle": "Fixture agreement",
"documentRef": "sign-document:92c3741e-171c-439c-9419-597ffe485224",
"defaultSubject": "Review the fixture",
"defaultMessage": "Synthetic local example only.",
"signerRoles": [
"Signer"
],
"fieldSchemaVersion": 2,
"fields": [
{
"id": "fixture_signature",
"type": "signature",
"label": "Signature",
"required": true,
"assignee": {
"role": "Signer",
"capacity": "personal"
},
"placement": {
"page": 1,
"x": 0.1,
"y": 0.7,
"width": 0.3,
"height": 0.08
}
}
],
"fieldSchema": [
"fixture_signature"
],
"createdByEmail": "operator@example.test",
"createdAt": "2026-10-05T18:07:40.450Z",
"updatedAt": "2026-10-05T18:07:40.450Z"
}
}
HTTP 201. Expect 201 and read $.template.id. Confirm the status is "active" before trying to seed an envelope from it.
Refused request · HTTP 401
Input · payload.json:
{
"name": "Fixture agreement",
"status": "active",
"documentRef": "sign-document:3314b7c7-66d1-4b67-83ca-b359421163a5",
"signerRoles": [
"Signer"
],
"fields": [
{
"id": "fixture_signature",
"type": "signature",
"label": "Signature",
"required": true,
"assignee": {
"role": "Signer",
"capacity": "personal"
},
"placement": {
"page": 1,
"x": 0.1,
"y": 0.7,
"width": 0.3,
"height": 0.08
}
}
],
"defaultSubject": "Review the fixture",
"defaultMessage": "Synthetic local example only."
}
CLI:
topolo actions validate app_topolo_sign.templates.create --data @payload.json --json
topolo actions plan app_topolo_sign.templates.create --resource-type workspace --resource-id "$SIGN_WORKSPACE_ID" --data @payload.json --json
Captured exported Worker response · HTTP 401:
{
"error": "Token validation failed"
}
HTTP 401. Resolve the exact refusal before retrying. This request is not a successful workflow.
Refused request · HTTP 403
Input · payload.json:
{
"name": "Fixture agreement",
"status": "active",
"documentRef": "sign-document:3314b7c7-66d1-4b67-83ca-b359421163a5",
"signerRoles": [
"Signer"
],
"fields": [
{
"id": "fixture_signature",
"type": "signature",
"label": "Signature",
"required": true,
"assignee": {
"role": "Signer",
"capacity": "personal"
},
"placement": {
"page": 1,
"x": 0.1,
"y": 0.7,
"width": 0.3,
"height": 0.08
}
}
],
"defaultSubject": "Review the fixture",
"defaultMessage": "Synthetic local example only."
}
CLI:
topolo actions validate app_topolo_sign.templates.create --data @payload.json --json
topolo actions plan app_topolo_sign.templates.create --resource-type workspace --resource-id "$SIGN_WORKSPACE_ID" --data @payload.json --json
Captured exported Worker response · HTTP 403:
{
"error": "This workspace requires read_write access",
"code": "workspace_access_insufficient"
}
HTTP 403. Resolve the exact refusal before retrying. This request is not a successful workflow.
Verify the result
- Expect 201 and read $.template.id.
- Confirm the status is "active" before trying to seed an envelope from it.
Errors and recovery
organization_required(HTTP 403): The token carries no organization scope. Re-authenticate against the organization that owns the workspace and retry.invalid_template(HTTP 400): The template failed validation. name is required and status must be a supported value; fields follow the same structured field rules as envelopes.create (the error code names the rule, detail the field).auth_error(HTTP 401): Reauthenticate in the same approved context before retrying.workspace_access_insufficient(HTTP 403): Discover the actual workspace grant and obtain the documented required access; do not retry with a guessed workspace or broader organization.
Some existing refusals return a human message as error rather than a stable code; preserve the actual response, status and request id. Platform validation, authentication and resource checks can refuse before the handler.
Recovery boundary
There is no delete for a template. Retire one created in error by setting a non-active status with templates.update; envelopes already created from it are unaffected.
Related actions
- Create envelope: Create a signature envelope as a DRAFT. Nothing is sent and no signer is contacted; the envelope stays editable until envelopes.send. Seed it from an active template, or supply the document, fields and recipients directly.
- List templates: List signature templates in this workspace with their status, signer roles and default subject and message.
- Update template: Update a signature template: its name, description, status, document, field schema, signer roles or defaults. Envelopes already created from it keep the terms they were created with.
Evidence
Captured from the exported Sign Worker with the existing SQLite-backed D1 harness applying all core, envelope and audit migrations to one local database, in-memory R2 and synthetic Auth/workspace/archive/Notify transports. These examples prove local validation, persistence and artifact production. They do not prove separate deployed D1 topology, real recipient consent, provider delivery or a live signed agreement. All identities, signing links and documents are synthetic; replace identifiers through authorized discovery and never execute a fixture against a live organization. Binary download examples use the released SDK binary projection; tests Base64-decode and compare original bytes, lengths and server SHA-256 headers.