---
title: "Generate and Send Document via SignNow"
url: "https://docs.signnow.com/docs/apex-generate-and-send-document"
type: "page"
section: "Integrations"
slug: "apex-generate-and-send-document"
---

# Generate and Send Document via SignNow

# Apex action: Generate and Send Document via SignNow

<!-- theme: info -->
> Before configuring this Apex action, review the [prerequisites](/docs/salesforce-apex-actions#prerequisites) for SignNow Apex actions and complete the required setup steps.

The **Generate and Send Document via SignNow** action dynamically generates a document by merging a .docx template with Salesforce record data, then sends it for signature based on a Salesforce event.

<!-- theme: info -->
> This action reads its .docx file from Salesforce Files and requires a File ID. It does not accept a document generation template stored in SignNow Admin Tools: to send one of those, use the [Send SignNow Template](/docs/apex-send-template) action. For an overview of all document generation paths in Salesforce, see [Document generation](/docs/salesforce-document-generation).

<!-- theme: info -->
> **Asynchronous action:** This action runs in the background after the flow completes. If you need to use the action's output in subsequent flow steps, use the synchronous version instead: **Generate and Send Document via SignNow (sync)**.

<!-- theme: info -->
> **Generate and Send Document via SignNow (sync)** works the same way but runs within the current flow transaction on a single record. Use it in screen flows when you need the action's output. It returns the **Document Group ID** and **Invite ID**, which you can use in subsequent flow steps.

For example, let's configure the action to generate and send a sales invoice whenever an Opportunity is moved to the **Closed Won** stage.

---

## Step 1. Create a .docx template with tags

Prepare a .docx file as the document template. Tags in the template are placeholders that are replaced with Salesforce field values when the document is generated.

For the full tag syntax reference, see [Step 1: Create a .docx template with tags](/docs/document-generations#step-1-create-a-docx-template-with-tags).

### Find Salesforce API field names

Tags in the template must use the **API field name**, not the field's display label. To find the API field name:

- In Salesforce, go to **Setup > Object Manager**.
- Select the object you are working with, or a related object (e.g., **Opportunity**).
- Click **Fields & Relationships**.
- Locate the field by its label. The **Field Name** column shows the API name.

![apex_docgen_1_2.png](/reference-assets/images/SignNow_Salesforce/apex_docgen/apex_docgen_1_2.png)

For standard fields (e.g., `Name`, `AccountNumber`), the API name matches the field label without spaces. For custom fields, the API name ends with `__c` (e.g., `OrderNumber__c`).

To access fields on related objects, use dot notation: `Account.Name`, `Owner.Name`.

<!-- theme: danger -->
> **Important:** Tags are **case-sensitive** and must match the field's API name exactly. For example, `{{Owner.Name}}` is filled with data, while `{{Owner.name}}` stays unfilled in the generated document.

<!-- theme: info -->
> **Compound address fields:** Some fields in the Object Manager appear as a single compound entry with the data type **Address** (e.g., `BillingAddress`). These compound fields cannot be used directly in tags. Instead, use the individual component field names following the pattern **[prefix] + [component]**. For example, `BillingAddress` breaks down into `BillingStreet`, `BillingCity`, `BillingState`, `BillingPostalCode`, and `BillingCountry`. These names are Salesforce conventions and do not appear as separate entries in the Object Manager UI. See the [Salesforce Address Compound Fields documentation](https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/object_reference/compound_fields_address.htm) for the full list.

### Tag syntax for Salesforce fields

| What to insert | Tag syntax | Example |
|---|---|---|
| Direct field on the queried object | `{{FieldApiName}}` | `{{OrderNumber__c}}` |
| Field on a parent object | `{{Relationship.FieldName}}` | `{{Account.Name}}`, `{{Owner.Name}}` |
| System date | `{{$today}}` | `{{$today}}` |
| Table (repeating rows) | `{{table(Relationship)}}` … `{{endtable}}` | `{{table(OpportunityLineItems)}}` |
| Field inside a table row | `{{FieldApiName}}` | `{{Quantity}}`, `{{UnitPrice}}` |
| Field on a specific child record | `{{Relationship[index].FieldName}}` | `{{OpportunityContactRoles[0].Contact.Name}}` |
| Sum of column above | `{{$sumabove}}` | `{{$sumabove}}` |
| Format a number (currency/decimals) | `{{numFormat(FieldApiName, ‘$#,###.00’)}}` | `{{numFormat(TotalPrice, ‘$#,###.00’)}}` |
| Conditional block | `{{if(condition)}}` … `{{else}}` … `{{endif}}` | `{{if($total > 5000.00)}}` |
| Signature field | `{{t:s;r:y;o:"RoleName";w:100;h:20;}}` | `{{t:s;r:y;o:"Manager";w:100;h:20;}}` |
| Text field | `{{t:t;r:y;o:"RoleName";w:100;h:20;}}` | `{{t:t;r:y;o:"Manager";w:100;h:20;}}` |

<!-- theme: danger -->
> **Number formatting with `numFormat`:** The format pattern must be wrapped in Word-style curly quotes (`‘` and `’`), for example `{{numFormat(TotalPrice, ‘$#,###.00’)}}`. Straight quotes (`'`) and backticks (`` ` ``) are not recognized and the tag will not render. Because Word converts straight quotes to curly quotes as you type, enter the tag directly in the .docx template instead of pasting it from a code editor.

<!-- theme: info -->
> **Fields on a specific child record:** When you know which child record you need, reference it by its position in the list instead of using a `{{table()}}` loop: `{{Relationship[index].FieldName}}`. The index is zero-based: `[0]` is the first child record, `[1]` is the second, and so on. For example, if the flow is triggered from an Opportunity, `{{OpportunityContactRoles[0].Contact.Name}}` is filled with the name of the first related contact. The child relationship must be included in the SOQL query as a subquery, the same as for table loops.

<!-- theme: warning -->
> **Role name in signature and text field tags:** The `o:"RoleName"` parameter specifies the signer role assigned to this field. For example, `o:"Manager"` assigns the field to the recipient with the **Manager** role. The role name must match a role defined in your SignNow document flow. The field will be filled by the recipient assigned to that role. Do not use spaces in role names: use `Recipient1` instead of `Recipient 1`, or `SalesManager` instead of `Sales Manager`.

<!-- theme: info -->
> **Note:** Both single-brace `{...}` and double-brace `{{...}}` syntax are supported for fillable field tags. For example, `{t:s;r:y;o:"Manager";w:100;h:20;}` and `{{t:s;r:y;o:"Manager";w:100;h:20;}}` are equivalent.

<!-- theme: info -->
> **💡 Tip: Inspect the data payload:** Insert `{{toJson($sysConstants)}}` anywhere in the document. When the document is generated, this tag outputs the entire JSON object with all available fields and values. This is useful for debugging: you can see exactly which fields are available, copy their exact names (including letter case) into your tags, and verify that the data looks as expected before finalizing your template.

### Invoice template example

Below is an example of a .docx template for a sales invoice that pulls data from a Salesforce Opportunity and its related line items.

<!--
focus: center
bg: none
-->
![docgen_docx_template_tags.png](/reference-assets/images/SignNow_Salesforce/salesforce_docgen/docgen_docx_template_tags.png)


Let's break down each section of the template in detail.

**Header fields** (from the Opportunity, its parent Account, and the Opportunity owner):

```
{{Account.Name}}
{{Account.BillingStreet}}
{{Account.BillingCity}}, {{Account.BillingState}} {{Account.BillingPostalCode}}
{{Account.BillingCountry}}

Invoice Number: {{OrderNumber__c}}
Date: {{$today}}

Bill To:
{{Owner.Name}}
{{Owner.Email}}
```

**Line items table** (repeating rows from `OpportunityLineItems`):

The table must include the `{{table(...)}}` and `{{endtable}}` tags as rows:

| Item | Quantity | Unit Price | Amount |
|---|---|---|---|
| `{{table(OpportunityLineItems)}}` | | | |
| `{{Name}}` | `{{Quantity}}` | `{{numFormat(UnitPrice, ‘$#,###.00’)}}` | `{{numFormat(TotalPrice, ‘$#,###.00’)}}` |
| `{{endtable}}` | | | |
| **Subtotal** | | | `{{numFormat($sumabove, ‘$#,###.00’)}}{{$total=$sumabove}}` |

**Conditional discount and [fillable fields](/docs/document-generations#fillable-field-tags):**

```
{{if($total > 5000.00)}}Your discount is 10%.
{{else}}Your discount is 5%.
{{endif}}

Sales manager signature  {{t:s;r:y;o:"Manager";w:100;h:20;}}
Sales manager name       {{t:t;r:y;o:"Manager";w:100;h:20;}}
```

---

## Step 2. Upload the .docx template to Salesforce Files

Once the template is ready, upload it to Salesforce Files so the Apex action can reference it by ID.

The file can be stored as an **Attachment** or a **ContentDocument**. It does not need to be attached to the triggering record. You can upload it anywhere in Salesforce Files.

To upload the file:

- Go to any Salesforce record or the **Files** tab.
- Click **Upload Files** and select your .docx file.

![apex_docgen_3.png](/reference-assets/images/SignNow_Salesforce/apex_docgen/apex_docgen_3.png)

### Get the file ID

The Apex action requires the **File ID** as an input. To retrieve it:

- Navigate to the record where the file is stored.
- Click the dropdown arrow next to the file. Select **View File Details**.

![apex_docgen_4.png](/reference-assets/images/SignNow_Salesforce/apex_docgen/apex_docgen_4.png)

- Copy the file ID from the browser URL.

![apex_docgen_5.png](/reference-assets/images/SignNow_Salesforce/apex_docgen/apex_docgen_5.png)

---

## Step 3. Configure the flow trigger

Go to **Flows > New Flow**.

Select **Start From Scratch** or **Use a Template**. Click **Next**.

Select **Record-Triggered Flow**. Click **Create**.

Configure the flow trigger:

- Select the object whose record triggers the flow. For our example, it is **Opportunity**.
- Select when the flow should be triggered. For our example, `A record is updated`.
- Set entry conditions. For our example, the condition field is `Stage`, the operator is `Equals`, and the value is `Closed Won`.
- Under **When to Run the Flow for Updated Records**, select the option that fits your use case.
- Under **Optimize Flow for**, select **Actions and Related Records**. This is required for external callouts.

![apex_docgen_6.png](/reference-assets/images/SignNow_Salesforce/apex_docgen/apex_docgen_6.png)

---

## Step 4. Configure the Generate and Send Document action

Click the **+** icon to add an action to the flow.

Start typing *signnow* in the input field and select **Generate and Send Document via SignNow**.

Configure the action inputs:

- **File ID:** the ID of the .docx template file uploaded in Step 2.
- **Record ID:** the Salesforce record ID used as the data source. For our example, select **Triggering Opportunity > Opportunity ID**.
- **SignNow Document Flow ID:** the ID of the pre-configured [SignNow document flow](/docs/document-flows) that defines recipients and the signing scenario.

<!-- theme: info -->
> Copy the SignNow document flow ID from **SignNow Admin Tools > Document Flows**.

<!-- theme: warning -->
> **Important:** The roles configured in the document flow must match the role names used in the .docx template's fillable field tags. For our example, the document flow must have a role named **Manager** to match the `o:"Manager"` parameter in the signature and text field tags.

- **SOQL Query:** a SOQL query that retrieves the data to merge into the template. Build it as described in Step 5, then return here to finish the action.

![apex_docgen_7.png](/reference-assets/images/SignNow_Salesforce/apex_docgen/apex_docgen_7.png)

---

## Step 5. Build the SOQL query

The SOQL query fetches Salesforce data and makes it available as tags in the .docx template. Every field referenced in the template must be included in the SOQL query.

**Rules for building the query:**

- The `FROM` clause must match the object of the triggering record (e.g., `FROM Opportunity`).
- The `WHERE` clause must be `WHERE Id = <record_id>`. The `<record_id>` placeholder is automatically replaced with the triggering record's ID at runtime.
- Direct fields are listed as-is: `Name`, `OrderNumber__c`.
- Fields on parent objects use dot notation: `Account.Name`, `Owner.Name`.
- Child records (used in `{{table()}}` loops or referenced by index, e.g., `{{OpportunityContactRoles[0].Contact.Name}}`) are included as subqueries: `(SELECT ... FROM ChildRelationshipName)`.
- In the SOQL subquery, use the **relationship name** (e.g., `OpportunityLineItems`). Use the same relationship name in the template's table tag: `{{table(OpportunityLineItems)}}`.

### Invoice example SOQL

```sql
SELECT
    Account.Name,
    Account.BillingStreet,
    Account.BillingCity,
    Account.BillingState,
    Account.BillingPostalCode,
    Account.BillingCountry,
    OrderNumber__c,
    Owner.Name,
    Owner.Email,
    (SELECT Name, Quantity, UnitPrice, TotalPrice FROM OpportunityLineItems)
FROM Opportunity
WHERE Id = <record_id>
```

**How the SOQL maps to template tags:**

| SOQL field | Template tag |
|---|---|
| `Account.Name` | `{{Account.Name}}` |
| `Account.BillingCity` | `{{Account.BillingCity}}` |
| `OrderNumber__c` | `{{OrderNumber__c}}` |
| `Owner.Name` | `{{Owner.Name}}` |
| `Owner.Email` | `{{Owner.Email}}` |
| `(SELECT ... FROM OpportunityLineItems)` | `{{table(OpportunityLineItems)}}` |
| `Name` *(inside subquery)* | `{{Name}}` *(inside table loop)* |
| `Quantity` *(inside subquery)* | `{{Quantity}}` *(inside table loop)* |

<!-- theme: info -->
> Inside a table loop you can also use a field from a record related to the child, as long as the subquery selects it. For example, add `PricebookEntry.Name` to the subquery and tag it as `{{PricebookEntry.Name}}` in the table row.

---

## Step 6. Save and activate the flow

Once the query is entered in the action, click **Save**, then click **Activate**.

---

## Result

![apex_docgen_result_upd.png](/reference-assets/images/SignNow_Salesforce/apex_docgen/apex_docgen_result_upd.png)


Each time an Opportunity is moved to the **Closed Won** stage, the action generates a PDF from the .docx template with the Opportunity's data merged in and sends it for signature to the recipients configured in the SignNow document flow.

---

View the full list of [SignNow Apex actions](/docs/salesforce-apex-actions).


---
*Full page: https://docs.signnow.com/docs/apex-generate-and-send-document*
