Publishing Resources

You can use the REST API to programmatically publish (upload) workbooks and data sources to the server. You can publish workbooks and data sources in two ways:

  • Using a single call, in which the complete file to publish is sent in the body of the request.

  • Using a multi-part upload, in which you send the file to the server in pieces. You should do this if the file you want to publish is very large, so that the upload process doesn't time out as you're publishing.

When you publish a workbook to the server, you must make sure that the server has all the information it needs to allow people to use the workbook. If the workbook uses a local data source, such as an Excel spreadsheet, you must publish a packaged workbook (a .twbx file), which includes the workbook and any external resources that it references, including data sources and background images. If you publish a workbook as a .twb file and the workbook uses a local data source, the publish process will fail. (Unlike the publish process that is used in Tableau Desktop, the REST API publish process cannot automatically include extracts or other resources that the workbook uses.)

Similarly, when you publish a data source, if the underlying data (such as an Excel spreadsheet) is available only on your local computer, you must publish a packaged data source (a .tdsx file). For more information, see Publishing Data Sources(Link opens in a new window).

Tip: The Python and Java samples include code that illustrates how to publish a workbook to Tableau Server. For more information, see REST API Samples.

Publishing a resource by using a single call

You can publish a file by using a single request, as long as the file is not too large. The maximum size of a file you can publish in one request is 64MB. However, if your file is that large, you should publish it in parts, as described under Publishing Flow for Multi-Part Upload later in this topic.

To publish using a single call, call Publish Workbook or Publish Data Source. These are POST requests that have the following format:

http://my-server/api/3.11/sites/site-id/workbooks

http://my-server/api/3.11/sites/site-id/datasources

In addition to the X-Tableau-auth header that you set for all requests, you must make sure that two other headers are set: Content-Length and Content-Type. The Content-Type header for the request must be set to multipart/mixed; boundary=boundary-string.

The boundary string is used to separate parts of the request body. The value of boundary-string can be any string up to 70 characters long that will not otherwise appear in the body of the request. One approach to managing boundary strings is to create a globally unique ID (GUID) and use that as the boundary string.

Note: Many programming frameworks automatically generate appropriate headers for Content-Length and Content-Type. For example, many frameworks generate a boundary string when you create a request whose content type is multipart/mixed.

The body of the request includes two sections, delimited with "--" (two hyphens) followed by the boundary string. The final boundary string consists of two hyphens, the boundary string itself, and two trailing hyphens.

Each section of the request body begins with a Content-Disposition header and a Content-Type header that describes the type of data in that section. The following example shows the request body for a Publish Workbook request. For this example, the boundary string has been set in the header to 6691a87289ac461bab2c945741f136e6.

--6691a87289ac461bab2c945741f136e6
Content-Disposition: name="request_payload"
Content-Type: text/xml

<tsRequest>
  <workbook name="Postal-rates">
    <project id="972bcf8c-d7ce-11e3-ac00-0fa50cfedda9"/>
  </workbook>
</tsRequest>
--6691a87289ac461bab2c945741f136e6
Content-Disposition: name="tableau_workbook"; filename="usps-rates.twbx"
Content-Type: application/octet-stream

<content here>
--6691a87289ac461bab2c945741f136e6--

In the first section, Content-Disposition is set to name="request_payload", and Content-Type is set to text/xml. These values are correct for data that consists of XML. Notice that some values, like request_payload and tableau_workbook, are literals.

In the second section, the two headers are set to values that are correct for data (including binary data) that represents the content of the file being published. Notice that the filename value for Content-Disposition indicates the name of the file being published.

Note: The content being published should not be encoded (for example, using Base-64 or UTF-8).

If the file is published successfully, the body of the response includes XML that provides information about the published document. For example, if you publish a workbook, the server responds with a workbook ID, name, and content URL, project information, and a list of the views that have been published. The following example shows a typical response for a workbook named Postal-rates that has been published to the default site.

<?xml version="1.0" encoding="UTF-8"?>
<tsResponse version-and-namespace-settings >
  <workbook id="1a1b1c1d2-e2f2-a2b3-c3d3-e3f4a4b4c4d" name="Postal-rates"
       contentUrl="Postal-rates">
    <project id="1f2f3e4e5-d6d7-c8c9-b0b1-a2a3f4f5e6e" name="default"/>
    <owner id="a2dd5d79-4a63-40a6-9934-f94090ccb653"/>
    <tags></tags>
    <views>
      <view id="1f1e1d1c2-b2a2-f2e3-d3c3-b3a4f4e4d4c"
          contentUrl="Postal-rates/sheets/PriceOverTime"/>
      <view id="c3321dee-a5e6-4734-bea3-6188b0b3f4d8"
          contentUrl="Postal-rates/sheets/CPIOverTime"/>
    </views>
  </workbook>
</tsResponse>

Publishing flow for multi-part upload

If the file you want to publish is large, you should publish it in multiple parts (or, for workbooks, publish asynchronously) . If the file is larger than 64 MB, you must publish it in multiple parts. Publishing in parts prevents a request from timing out. To perform a multi-part upload, send a succession of requests to the server, consisting of these methods:

The first two parts of the upload process (initiating and appending) are the same whether you're publishing a workbook or a data source. It's only when you're ready to commit your upload that you call a method that is specific to a workbook or a data source.

Initiating the file upload

You begin a multi-part upload by calling the Initiate File Upload method. This is a POST request that has the following format:

http://my-server/api/3.11/sites/site-id/fileUploads

The response for this request is HTTP code 201 (Created). The response body includes a <fileUpload> element that includes an upload session ID:

<?xml version="1.0" encoding="UTF-8"?>
<tsResponse version-and-namespace-settings >
  <fileUpload
     uploadSessionId="13253:6744F321974F4E8B8EC1424A3D56E0EA-0:0" fileSize="0"/>
</tsResponse>

Uploading the file in parts

To actually upload the file, you call the Append to File Upload method. Each call to Append to File Upload includes a part of the file. You call the method as many times as necessary to send the complete file. This method is a PUT request that has the following format:

http://my-server/api/3.11/sites/site-id/fileUploads/upload-session-id

For example, the URI for the upload file ID that was returned in the previous example might look like this:

http://my-server/api/3.11/sites/site-id/b4126fe9-d7ee-4083-88f9-a5eea1f40416/fileUploads/13253:73A6B41AAE1D462B82B6914B7CAD55BF-0:0

The format of the body for the PUT request is similar to the format of a POST request for publishing a file in one request, as described earlier. The request header must include a boundary string that is used to delimit sections of the request body. The following example shows an example of the request body for an Append to File Upload request. For this example, the boundary string has been set in the header to 12f71d3d4ae441caa0b38a5d4e0bde5e.

--12f71d3d4ae441caa0b38a5d4e0bde5e
Content-Disposition: name="request_payload"
Content-Type: text/xml


--12f71d3d4ae441caa0b38a5d4e0bde5e
Content-Disposition: name="tableau_file"; filename="FILE-NAME"
Content-Type: application/octet-stream

<content here, with line break before boundary delimiter>
--12f71d3d4ae441caa0b38a5d4e0bde5e--

The first part of the multi-part payload is effectively blank; it contains only headers.

Note: A blank payload requires two blank lines to conform to the RFC specification for multi-part payloads. If the request contains only one line for a blank payload, Tableau Server returns a 500-level error.

In the second section, the Content-Disposition line contains a filename header.

The section of the example that begins with "<content here" contains the portion of the file that you want to upload. This portion cannot be larger than 64 MB. Typically, however, the purpose of a multi-part upload is to be able to send smaller chunks at a time.

Note: The content being published should not be encoded (for example, using Base-64 or UTF-8).

If the PUT operation is successful, the server responds with HTTP 200 (OK). The body of the response includes the current file upload ID. It also includes a fileSize value that indicates the total size of the uploaded file so far, in megabytes.

<?xml version="1.0" encoding="UTF-8"?>
<tsResponse version-and-namespace-settings >
  <fileUpload uploadSessionId="13253:6744F321974F4E8B8EC1424A3D56E0EA-0:0"
    fileSize="1"/>
</tsResponse>

The minimum size that is reported in the fileSize attribute is 1, even if the size so far is less than 1 MB.

Finishing the upload

When you have finished making PUT requests to upload the contents of the file, you call Publish Workbook or Publish Data Source to commit the upload. As part of this call, you indicate what type of file you've uploaded and what project to store the file in.

For a multi-part upload, the Publish Workbook or Publish Data Source calls have different content than they do for publishing the file in one request. These methods are POST requests. For Publish Workbook, the format is this:

http://my-server/api/3.11/sites/site-id/workbooks?uploadSessionId=upload-session-id&workbookType=file-type&overwrite=bool

For Publish Data Source, the format is this:

http://my-server/api/3.11/sites/site-id/datasources?uploadSessionId=upload-session-id&datasourceType=file-type&overwrite=bool

The workbookType or datasourceType parameters in the URI must be set to one of the allowed file types for the file that you're uploading: twb or twbx for workbooks, and hyper, tds, tdsx, or tde for data sources.

For example, the URI to finish the multi-part workbook upload for the preceding examples might look like this:

http://my-server/api/3.11/sites/b4126fe9-d7ee-4083-88f9-a5eea1f40416/workbooks?uploadSessionId=13253:73A6B41AAE1D462B82B6914B7CAD55BF-0:0&workbookType=twbx

The body of the requests includes a single section delimited with boundary strings. The body has a <tsRequest> element that includes the name of the workbook or data source and the ID of the project to save the uploaded file with. The following example shows the body of a request for the Publish Workbook method that finishes a multi-part workbook update. For this example, the boundary string has been set in the header to f3dd83af28284d9fb28e20665ed49c86. The workbook will be saved under the name CPI-historical in the project whose ID is 1f2f3e4e5-d6d7-c8c9-b0b1-a2a3f4f5e6e.

--f3dd83af28284d9fb28e20665ed49c86
Content-Disposition: name="request_payload"
Content-Type: text/xml

<tsRequest>
  <workbook name="CPI-historical">
    <project id="1f2f3e4e5-d6d7-c8c9-b0b1-a2a3f4f5e6e"/>
  </workbook>
</tsRequest>
--f3dd83af28284d9fb28e20665ed49c86--

The server responds with a workbook ID, name, and content URL, project information, and a list of the views that have been published. The following example shows a typical response for a workbook named CPI-historical that has been published to the default site.

<?xml version="1.0" encoding="UTF-8"?>
<tsResponse version-and-namespace-settings >
  <workbook id="1a1b1c1d2-e2f2-a2b3-c3d3-e3f4a4b4c4d"
        name="CPI-historical" contentUrl="CPI-historical">
    <project id="1f2f3e4e5-d6d7-c8c9-b0b1-a2a3f4f5e6e" name="default"/>
    <owner id="9f9e9d9c8-b8a8-f8e7-d7c7-b7a6f6d6e6d"/>
    <tags></tags>
    <views>
      <view id="1f1e1d1c2-b2a2-f2e3-d3c3-b3a4f4e4d4c"
          contentUrl="CPI-historical/sheets/PriceOverTime"/>
      <view id="12ab34cd5-6ef7-8ab9-0cd1-2ef34ab56cd"
          contentUrl="CPI-historical/sheets/CPIOverTime"/>
    </views>
  </workbook>
</tsResponse>

Overwriting an existing file

You can add the overwrite parameter to the URI for the Publish Workbook or Publish Data Source methods. If you set the flag to true, the server saves the file that you've uploaded under the name you specify, even if the workbook or data source with that name already exists. If the resource doesn't already exist and you set overwrite to true, a new workbook or data source is created with the name you specify. If you set overwrite to false (or don't include the parameter, since the default is false), the server checks first to see whether a resource already exists with the name that you specify. In that case, if the resource already exists, the method fails.

The following example shows a request that calls the Publish Workbook method. The request specifies that the workbook that is being published should overwrite any existing workbook whose name is included in the request body.

http://my-server/api/3.11/sites/9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d/workbooks?uploadSessionId=13253:6744F321974F4E8B8EC1424A3D56E0EA-0:0&workbookType=twbx&overwrite=true

Appending data to an existing data source

If a data source already exists on the server, you can append data to it by calling the Publish Data Source method and specifying the append parameter. In effect, the append process adds rows to the data that's already in the data source.

You can append data to an existing data source under these conditions:

  • The data source on the server is an extract (.hyper or .tde file).

  • The data source that you're publishing is also an extract (.hyper or .tde file).

  • The data that you're appending has the same schema as the data that already exists in the data source.

Note: If an extract is stored using the multiple tables option, you can't append data to it.

Publishing workbooks and data sources asynchronously

The maximum size of a workbook or data source that can be published in a single request is 64 MB, but requests sometimes timeout with smaller file sizes, depending on the overall load on your Tableau Server installation. To avoid having the workbook or data source publishing process time out, you can use the asJob parameter to make publication asynchronous.

To make workbook publication asynchronous, add the asJob parameter to the Publish Workbook POST request, as shown in the following example:

POST /api/api-version/sites/site-id/workbooks&asJob=true

To make the data source publication asynchronous, add the asJob parameter to the Publish Data Source POST request, as shown in the following example:

POST /api/api-version/sites/site-id/datasources&asJob=true

When you set the asJob value to true, a job will start to perform the publishing process and return the <job> element inside of a <tsResponse> element, using the following format:

 <job id="job-id" mode="Asynchronous" type="PublishWorkbook" progress="0" createdAt="date-time" finishCode="1"/>

The <job> element contains the following attributes:

  • job-id is a unique identifier for the job. You can use the value of this attribute to check the status of the publishing job by passing it to Query Job.
  • job-mode has the value of Asynchronous when you set the asJob parameter to true in the POST request.
  • type has the value of PublishWorkbook or PublishDatasource.
  • progress is set to 0 when the asynchronous publishing job is created, and then changes to 100 when the job is complete.
  • createdAt provides a complete date and time that indicates when asynchronous publishing was initiated.
  • finishCode is set to 1 when the asynchronous publishing job is created, and then changes to 0 when the job is complete.

Troubleshooting

Because REST API requests that involve publishing can have more complex request body content than other requests, you might experience errors when you test them. Here are some troubleshooting tips:

  • Examine code in the Python and Java samples as a model for how to use the REST API for publishing. For more information, see REST API Samples.

  • Use a tool to examine the requests and responses as they go back and forth between your code and the server. (If you are using Windows, a useful tool for this purpose is Fiddler(Link opens in a new window).) Make sure that the format of the request is correct.

  • Be sure that the Content-Length header is set.

  • An HTTP response of 400 (Bad Request) can mean that the request body is not formatted correctly.

  • An HTTP response of 500 (Internal Server Error) can mean that a header is missing or incorrect (for example, Content-Length). It can also mean that the payload for Append to File Upload does not include the two required blank lines in the first part of the payload (per the RFC specification for multi-part payloads).


Thanks for your feedback!