PAYATHON 2026

为什么 docGenFormFields 值未生效,已发送的 envelope 仍显示占位符?

排障赵师傅

结论

应先创建草稿 envelope,读取草稿中的 DocGen 字段,再更新字段并生成文档,最后发送 envelope。

DocGen 有专用的字段读取和更新端点,需要在通过 POST /envelopes 创建草稿后调用。compositeTemplates 支持 DocGen,现有创建方式可以保留。DocuSign 官方的复合模板示例也采用这一流程。

当前请求使用 "status": "sent" 直接发送,并将 docGenFormFields 放在创建请求的根层级。HTTP 201 只表示 envelope 创建成功,不能说明占位符已完成替换。

为什么 textTabs 和 prefillTabs 没有效果

DocGen sender fields 用于在发送前生成文档正文,与签署界面的 tabs 是两套不同的机制。

  • textTabs 的 locked 控制对应签署字段是否可编辑,不会解析文档中的 DocGen 占位符。
  • prefillTabs 是文档级预填字段,放在 signer 下会出现题述的 PrefillTab not allowed for recipient 错误。即使位置正确,也不能代替 DocGen 的文档生成步骤。文档级用法可参见官方 SDK 文档中的 createDocumentTabs。

要根据字段值生成正文,需要调用 DocumentGeneration:updateEnvelopeDocGenFormFields。

正确的 API 调用步骤

所有请求均使用以下基础地址:

https://demo.docusign.net/restapi/v2.1

每次请求都携带 Authorization: Bearer <ACCESS_TOKEN>;有 JSON 请求体时,设置 Content-Type: application/json。将 {accountId}、{envelopeId} 等占位内容替换为实际值。

1. 使用 compositeTemplates 创建草稿

POST /accounts/{accountId}/envelopes

保留请求体中的模板和角色映射,将 status 改为 "created",并移除根层级的 docGenFormFields:

{
  "emailSubject": "Please sign this contract",
  "status": "created",
  "compositeTemplates": [
    {
      "compositeTemplateId": "1",
      "serverTemplates": [
        {
          "sequence": "1",
          "templateId": "3cfaca9c-ffd5-4c16-a416-992ca05317b0"
        }
      ],
      "inlineTemplates": [
        {
          "sequence": "2",
          "recipients": {
            "signers": [
              {
                "name": "John Doe",
                "email": "signer@example.com",
                "recipientId": "55136564",
                "roleName": "test_signer"
              }
            ]
          }
        }
      ]
    }
  ]
}

将示例邮箱替换为实际收件人邮箱。保存响应中的 envelopeId,后续请求会用到它。

2. 读取草稿中的 DocGen 字段

GET /accounts/{accountId}/envelopes/{envelopeId}/docGenFormFields

对应方法是 DocumentGeneration:getEnvelopeDocGenFormFields。

从响应中确认以下信息:

  • docGenFormFields[].documentId:字段所属的 DocGen 文档。
  • docGenFormFieldList[].name:更新字段时使用的名称。

更新请求必须使用当前草稿实际返回的 documentId。不要假定它是 "1",也不要用 compositeTemplateId 代替。

字段名称也以该响应为准。label、name 和 fullyQualifiedPath 含义不同。官方要求更新请求中的 name 与读取响应中的 name 一致,参见DocGen 字段读取与填充说明。

3. 更新字段并生成文档

PUT /accounts/{accountId}/envelopes/{envelopeId}/docGenFormFields?update_docgen_formfields_only=false

如果上一步确认两个字段属于同一文档,且返回的名称与题述一致,请求体可以写成:

{
  "docGenFormFields": [
    {
      "documentId": "<DOCUMENT_ID_FROM_GET_RESPONSE>",
      "docGenFormFieldList": [
        {
          "name": "AgreementParty_displayName_Z2bXgzv",
          "value": "John Doe"
        },
        {
          "name": "C_demo_field_value_1Xkjiv",
          "value": "Test"
        }
      ]
    }
  ]
}

将 <DOCUMENT_ID_FROM_GET_RESPONSE> 替换为上一步返回的实际值。如果两个字段属于不同文档,应分别放入对应 documentId 的对象中。

update_docgen_formfields_only 决定是否同时更新文档:

参数值行为
false 或省略更新字段并更新文档
true只保存字段值,文档保持不变

需要立即替换正文占位符时,使用 false 或省略该参数。DocuSign 官方 Postman 集合说明了这两种行为的区别。

4. 检查生成结果,再发送

确认生成文档中的 {{Agreement Party}} 和 {{demo_field}} 已替换为预期文本后,再调用:

PUT /accounts/{accountId}/envelopes/{envelopeId}
{
  "status": "sent"
}

这是官方支持的发送草稿 envelope方式。如果字段更新或文档检查失败,集成代码应停止发送,避免签署人收到仍带有占位符的文档。

模板和账户还需要检查什么

模板必须包含可供 DocGen 处理的 DOCX 文档及 sender field 定义。DocuSign 支持通过 Agreement Template Builder、Template Assistant for Word,以及规定的手动字段语法创建这些定义。正文中有 {{...}},并不表示字段元数据完整;isAceGenDocument: true 也不能证明文档已完成字段合并。

开发账户提供文档生成功能,生产账户则需要相应的 DocGen 权益,具体应向 DocuSign 确认。相关前提见官方文档生成说明。

如果按上述流程操作后,占位符仍未替换,可根据返回结果排查:

  • 草稿字段接口没有返回目标字段:检查模板版本、DOCX 字段定义,以及创建 envelope 时是否替换了原来的 DocGen 文档。
  • 字段存在但填值失败:检查 documentId 与 name 是否来自当前草稿,而非其他模板或 envelope。
  • 字段值已保存但正文未变化:确认没有使用 update_docgen_formfields_only=true,并检查生成响应和实际文档。

DocGen 成功生成的值会成为文档正文。签署人无法像编辑普通 text tab 那样修改这些值,因此无需再为它们设置 locked: true。

示例中的其他注意事项

原请求示例的第二个字段对象缺少结束的 },原样发送会产生 JSON 语法问题。不过,实际请求返回了 HTTP 201,排查应以实际发送的请求体和日志为准,不能将这处粘贴遗漏认定为占位符未解析的原因。

已经发送的 envelope 不适合直接套用上述草稿生成流程。排查时应新建草稿,完成字段填充和文档检查后再发送。

备注:内容仅供参考。