Skip to main content
Blog
Home/

Updating envelope custom fields using Go

Author Aaron Jackson-Wilde
Aaron Jackson-WildeProgrammer Writer
Summary4 min read

This code example shows you how to update an .envelope custom field using Go.

    • What is the difference between tabs and custom fields?
    • Retrieve your custom field ID
    • Additional resources

    Table of contents

    Let’s go on with Go! In this post, I’ll show you how to update custom envelope fields. Docusign code example history buffs will recognize that I am using the How to Set Tab values example mixed with using the EnvelopeCustomFields:update API method. However, to use this method, you need to provide the custom field tab ID so that the eSignature REST API can determine which tab value needs to be updated. 

    What is the difference between tabs and custom fields?

    Envelopes sent from Docusign tend to have documents attached, on which you're going to collect a signature and perhaps some data.  Those various text and input boxes you see on your document for collecting a signature are known as tabs; tabs are used at the document level. 

    Conversely, custom fields are input boxes for either text or arrays (also commonly referred to as lists [the key difference is that a list is an ORDERED array] in various programming language disciplines). In this example I’m not setting a custom field value for a field within a given document; I’m creating a custom field that lives on the envelope itself, in order to store transaction information—in this case, a field for a given customer type as well as for a tracking number. 

    To get started, I'll create an envelope definition with a small Base64 string of a text file, an email subject, an array of signers, an array of custom fields, and your envelope status. I've also added in a small bit of code to generate the current date when the envelope is sent:

    
    
    func makeCustomFieldsEnvelope(signerEmail string, signerName string, customerType string ) string {
     
        // You may perhaps like this style instead?
        // https://go.dev/play/p/Ik8-Mh3BBH4
    
    
        dateTimeString := time.Now().Format("01-02-2006")
     
        envelope := fmt.Sprintf(`{
        "emailSubject": "Please sign this document set",
        "documents": [{
            "documentBase64": "DQoNCg0KCQkJCXRleHQgZG9jDQoNCg0KDQoNCg0KUk0gIwlSTSAjCVJNICMNCg0KDQoNClxzMVwNCg0KLy9hbmNoMSANCgkvL2FuY2gyDQoJCS8vYW5jaDM=",
            "documentId": "1",
            "fileExtension": "txt",
            "name": "Order Summary %s"
        }],
    
    
                "customFields": {
                    "textCustomFields": [{
                        "name": "customerType",
                        "required": "true",
                        "show": "true",
                        "value": "%s"
                    },
                    {
                        "name": "trackingNumber",
                        "required": "false",
                        "show": "true",
                        "value": ""
                    },
                    {
                        "name": "shippingDate",
                        "required": "false",
                        "show": "true",
                        "value": ""
                    }]
        "recipients": {
            "signers": [
                {
                    "email": "%s",
                    "name": "%s",
                    "recipientId": "1",
                    "routingOrder": "1",
                    "tabs": {
                        "signHereTabs": [{
                            "documentId": "1",
                            "name": "SignHereTab",
                            "pageNumber": "1",
                            "recipientId": "1",
                            "tabLabel": "SignHereTab",
                            "xPosition": "75",
                            "yPosition": "572"
                        }]
                    },
                }
            ]
        },
        "status": "sent"
    }`, dateTimeString, signerEmail, signerName, customerType)
     
        return envelope
    }
    
    

    Retrieve your custom field ID

    To update the custom fields, you’ll need to retrieve their given ID. Retrieving these custom field IDs is as simple as a small GET call:

    // Send an envelope
    func getCustomFields(DSAccessToken string, DSAccountId string, envelopeId string) ([]string, error) {
        client := &http.Client{}
        // Use http.NewRequest in order to set custom headers
        req, err := http.NewRequest("GET", "https://demo.docusign.net/restapi/v2.1/accounts/"+DSAccountId+"/envelopes/"+envelopeId+"/customfields", nil)
        req.Header.Set("Authorization", "Bearer "+DSAccessToken)
        if err != nil {
            fmt.Printf("Request Failed: %s", err)
            return []string{""}, err
        }
        // Since http.NewRequest is being used, client.Do is needed to execute the request
        res, err := client.Do(req)
        if err != nil {
            fmt.Printf("Request Failed: %s", err)
            return []string{""}, err
        }
     
        body, err := io.ReadAll(res.Body)
        if err != nil {
            fmt.Printf("Request Failed: %s", err)
            return []string{""}, err
        }
        fmt.Printf("Body: %s\n", body)
        // Decode the response to JSON
        var customFields CustomFields
        jsonErr := json.Unmarshal(body, &customFields)
        if jsonErr != nil {
            fmt.Printf("Request Failed: %s", jsonErr)
            return []string{""}, jsonErr
        }
        responseArray := []string{customFields.TextCustomFields[0].FieldID, customFields.TextCustomFields[1].FieldID}
        return responseArray, nil
    }
    
    

    There is a bit of happy-path development happening here. I’m looking through the JSON response and returning the first two custom field IDs.  I'm aware that the first value is the tracking data and that the second value is the shipping date, something fairly straightforward if you're only working with one field; but when working with multiple fields, I suggest building a mechanism to search the field names for a given value and to retain the corresponding custom field ID accordingly. Speaking of which, make sure you include your CustomFields struct so that you can parse the resulting JSON. I stored mine at the very top, just before initializing variables:

    // Auto-generated using https://transform.tools/json-to-go
    type CustomFields struct {
        TextCustomFields []struct {
            FieldID  string `json:"fieldId"`
            Name     string `json:"name"`
            Show     string `json:"show"`
            Required string `json:"required"`
            Value    string `json:"value"`
        } `json:"textCustomFields"`
        ListCustomFields []interface{} `json:"listCustomFields"`
    }
    
    

    If you use this combination of setting custom fields on envelope generation and keeping those fields updated and maintained with an API interfacing script, then, so long as you keep a manageable system, you should be able to reliably set envelope metadata, such as payments, third-party logistics, or heuristics telemetry data, with the envelope itself. Furthermore, you can use the custom field as an index query: if you keep a format such as code designations within your messages, you can query for a given customer ID or type so that you can pull all results (or lack thereof). 

    For this example, you can set the shipping date and tracking number using arbitrary strings; I have set the date shipped to be three days from the current date. With these values, I'll update a small bit of JSON to populate those textCustomfields on that envelope and return the output response to the end user:

    // Sets custom field values for an envelope
    func setCustomfieldValues(DSAccessToken string, DSAccountId string, envelopeId string, trackingNumberFieldId string, shippingDateFieldId string) (string, error) {
        client := &http.Client{}
    
    
        // For the sake of the example these are arbitrary values,
        // on a real application these values should be retrieved
        // from an orders table on a database
        shippingDate := time.Now().Add(time.Hour * 24 * 3).Format("01-02-2006")
        trackingNumber := "1Z" + rand_str(16)
    
    
        requestBody := fmt.Sprintf(`{ textCustomFields: [
            { "fieldId" : "%s",
              "value"  : "%s" },
              { "fieldId" : "%s",
              "value"  : "%s" }
    
    
            ]}`, trackingNumberFieldId, trackingNumber, shippingDateFieldId, shippingDate)
    
    
        // Use http.NewRequest in order to set custom headers
        req, err := http.NewRequest("PUT", "https://demo.docusign.net/restapi/v2.1/accounts/"+DSAccountId+"/envelopes/"+envelopeId+"/custom_fields", strings.NewReader(requestBody))
        req.Header.Set("Authorization", "Bearer "+DSAccessToken)
        if err != nil {
            fmt.Printf("Request Failed: %s", err)
            return "", err
        }
        // Since http.NewRequest is being used, client.Do is needed to execute the request
        res, err := client.Do(req)
        if err != nil {
            fmt.Printf("Request Failed: %s", err)
            return "", err
        }
    
    
        body, err := io.ReadAll(res.Body)
        if err != nil {
            fmt.Printf("Request Failed: %s", err)
            return "", err
        }
        // fmt.Printf("Body: %s\n", body)
    
    
        return string(body), nil
    }
    
    

    That's it, folks! Use this humble snippet to complete your MVP. Possible enhancements to this code might include more functions to pull data sources into a temporary object or table and to then do some for-loop operations so that you can process them at scale. For example, you could use Envelopes:listStatusChanges to fetch a list of envelope IDs, and then proceed to update each one with the correlated tracking and shipping data. You can find the complete example in this GitHub. To call the code snippets made earlier, drop them into the main function for your Go app:

    func main() {
    
        //  ... OAuth still necessary for Access Tokens
    
        envelopeDefinitionCustomTabs := makeCustomFieldsEnvelope("sonic@example.com", "sonic the hedgefrog", "CUSTOMER-"+rand_str(14))
        // fmt.Printf(envelopeDefinitionCustomTabs)
    
        envelopeId2, err := sendEnvelope(dSAccessToken, dSAccountId, envelopeDefinitionCustomTabs)
        if err != nil {
            fmt.Printf("update custom fields request failed: %s", err)
        }
        // fmt.Print("envelope ID is: " + envelopeId2 + "\n")
    
        // Lets retrieve the Envelope custom Fields ids
        customFieldIds, err := getCustomFields(dSAccessToken, dSAccountId, envelopeId2)
    
        // Set the shipping date and tracking number on the envelope
        finalRes, err := setCustomfieldValues(dSAccessToken, dSAccountId, envelopeId2, customFieldIds[0], customFieldIds[1])
        fmt.Printf("Envelope Custom fields updated successfully: \n%s", finalRes)
    }
    
    

    Stay tuned for more cool stuff from us, and thanks for hanging out with me today!

    Your API brother-from-another-mother,

    Aaron Jackson-Wilde

    PS: Click one of the links 👇👇👇 for more tips and tricks to use the eSignature API!

    Additional resources

    Author Aaron Jackson-Wilde
    Aaron Jackson-WildeProgrammer Writer

    Aaron Jackson-Wilde has been with Docusign since 2019 and specializes in API-related developer content. Aaron contributes to the Quickstart launchers, How-To articles, and SDK documentation, and helps troubleshoot occasional issues on GitHub or StackOverflow.

    More posts from this author

    Related posts

    • Docusign 2024 Release 3: Capture the Critical Business Value Hidden in Your Agreements
      Intelligent Agreement Management

      Docusign 2024 Release 3: Capture the Critical Business Value Hidden in Your Agreements

    • How to improve your app’s UX while users wait for API calls to complete

      How to improve your app’s UX while users wait for API calls to complete

      Author Larry Kluger
      Larry Kluger
    • Trending Topics: Latest from our forums (November 2024)
      Author Paige Rossi
      Paige Rossi
    How to improve your app’s UX while users wait for API calls to complete

    How to improve your app’s UX while users wait for API calls to complete

    Author Larry Kluger
    Larry Kluger
    Trending Topics: Latest from our forums (November 2024)
    Author Paige Rossi
    Paige Rossi

    Discover what's new with Docusign IAM or start with eSignature for free

    Explore Docusign IAMTry eSignature for Free
    Person smiling while presenting