Share

Webhooks

Overview

Webhooks allow for automated processing of results from the Autodesk Validation Tool (AVT) and the Autodesk Replication Tool (ART). Upon completion of a task, a set of data is sent by the application to a configurable server URL. This can be a web server run by the client, or a third-party service designed to process such data. Examples of third-party tools include:

Below is an overview diagram summarizing typical use of webhooks.

webhooks workflow diagram

Configuration

Webhooks are configured using the Settings tab. Access to this tab is only enabled upon request. If the tab is not visible, please contact support for access to be granted.

AVT ui

Follow the steps below to configure and enable a webhook. Note that screenshots from the Autodesk Replication Tools (ART) will be used, but the interface and configuration is identical in both Autodesk Validation Tool and Autodesk Replication Tool.

Create a webhook

After logging into AVT/ART, click Settings in the left-hand navigation bar to open the webhook configuration page. Click New Webhook to begin the creation process.

ART ui

Fill in the following fields to create the webhook:

  • Name: A descriptive name to identify the webhook
  • URL: The destination URL which will be called by the webhook
  • Secret: The Secret field can be revealed, copied or regenerated using the three icons on the right-hand side. See the Validation section for more information about how this property is used.
  • Enabled: Leave the Enabled checkbox selected unless the webhook should not be immediately activated.

ART ui

Once the webhook has been created, it will appear on the settings page:

ART ui

Test a webhook

To test that a webhook has been properly configured, click the Test button in the Actions section.

ART ui

If the webhook is successfully sent, the success property in the response will be true. The destination server should also have a record of the webhook being received.

ART ui

Get a Sample Webhook Body

To get a preview of what the webhook body will contain, click the Preview button.

ART ui

If the webhook is successfully sent, the success property in the response will be true. The destination server should also have a record of the webhook being received.

ART ui

Webhook Data

The webhook body is structured as follows. The data has been split into sections for clarity. The properties are explained below each section.

Root

{
    "version": "1.0",   
    "resource": "jobRunId",  
    "id": "af292bdf-136f-4577-a104-280cb3d09102",  
    "attempt": 1,  
    "creationTimeUtc": "2023-10-16T19:27:07.6701294Z",  
    "webhook": 
        {  
        
        },
    "payload": {
    
}
  • “version” Webhook schema version.
  • “Resource” The ID of the item that triggered the webhook. Will always be the job run ID.
  • “id” The ID of the webhook invocation. This ID is unique for every time.
  • “attempt” The attempt number of the current webhook event.
  • “creationTimeUtc” The time at which the current webhook body was created. This will be different between retry attempts.
  • “webhook” Information about the webhook itself. See below.
  • “payload” The result data of the task that triggered the webhook. See below.

webhook

"webhook": {
    "id": "af292bdf-136f-4577-a104-280cb3d09102",
    "webhookEventId": "dummyId",
    "webhookId": "38d91658-920f-4b6b-8b54-10e141895663",
    "scopeType": "User",
    "scopeId": "userId",
    "eventType": "None",
    "webhookUri": "https://webhook.site/41198c61-ce37-4f10-a2fd-03826e19d229",
    "userId": " userId",
    "customerId": "autodesk.com",
    "creationTimeUtc": "2023-10-16T19:27:07.6701294Z"
  },
  • “id” The ID of the webhook. This value is constant across all invocations of the webhook.
  • “webhookEventId” The ID of the webhook invocation. This ID is unique for every time a webhook is fired (e.g., task completed), but will be the same between attempts.
  • “scopeType” Will always be User.
  • “scopeId” Will always be the user ID.
  • “webhookUri” The configured webhook URL at the time that the webhook was sent.
  • “userId” The user ID, which is the same across all Autodesk products.
  • “customerId” The customer ID, internal to AVT/ART.
  • “creationTimeUtc” The time the webhook was created. Different from the value on the previous page, which is when the webhook was fired.

Payload

"payload": {
    "job": {
      
    },
    "jobRun": {
      
    },
    "jobRunItems": [
       {
        
      }
    ]
  }
}

Does not have any properties of its own. All data is shown below in the “job” (Task), “jobRun” (Task run), and “jobRunItems” (items from the Task run) sections.

Explanation

A job represents the configuration data for a Task. The following data is configured as part of a job object:

  • Source files & folders
  • Destination (ART only)
  • Report options
  • Run type & Schedule (Once now, once later, scheduled, on publish)

    A job run represents a single execution of the configured job. It consists of one or more job run items, each of which represents a single file that was part of the run.

    The job run coordinates the execution of each individual item, and its status represents the aggregate of the status of all items. If the run failed to execute, its status will be Error, and there will be no items. Otherwise, the run status will be Completed if all items ran successfully, Partially Completed if some, but not all, items ran successfully, and Error if no items ran successfully.

Job

"job": {
  "jobId": "jobId",
  "jobName": "jobName",
  "scheduleType": "OnceNow",
  "userId": "userId",
  "customerId": "customerId",
  "hubId": "hubId",
  "hubName": "hubName",
  "projectId": "projectId",
  "projectName": "projectName"
},
  • “jobId” The ID of the job (Task). This is the same value that will be seen in the URL of the Edit Job page.
  • “jobname” The job (Task) name.
  • “scheduleType” The type of schedule of the job (Task). This will be one of:
    • OnceNow
    • OnceLater
    • Recurring
    • OnPublish
  • “userId” The user ID to which the job (Task) belongs.
  • “customerId” The user’s customer ID.
  • “hubId” The hub ID where the source files are contained.
  • “hubName” The hub name.
  • “projectId” The project ID where the source files are contained.
  • “projectName” The project name.

Job Run

"jobRun": {
  "jobRunId": "jobRunId",
  "status": "Scheduled",
  "errorMessage": null,
  "errorCode": null,
  "startTime": "0001-01-01T00:00:00",
  "endTime": "0001-01-01T00:00:00",
  "totalFiles": 0,
  "successFiles": 0,
  "failedFiles": 0,
  "pendingFiles": 0,
  "exports": [
    {
      "format": "Offline",
      "signedUrl": "https://signed-avt-combined-file-url"
    },
    {
      "format": "Html",
      "signedUrl": "https://signed-html-combined-file-url"
    },
    {
      "format": "Excel",
      "signedUrl": "https://signed-excel-combined-file-url"
    }
  ]
},
  • “jobRunId” The ID of the job (Task) run.
  • “status” The status of the job (Task) run. Will be one of:
    • Error
    • PartiallyCompleted
    • Success PartiallyCompleted indicates some files completed successfully but others failed. Error indicates that all files failed.
  • “errorMessage” Contains an error message, if one occurred.
  • “errorCode” An error code to provide to the support team, if one occurred.
  • “startTime” The job (Task) run’s start time.
  • “endTime” The job (Task) run’s end time.
  • “totalFiles” The total number of files in the job (Task).
  • “successFiles” The number of files that succeeded.
  • “failedFiles” The number of files that failed.
  • “pendingFiles” The number of files that did not run. This can be non-zero if files remain queued longer than the maximum allowed execution time.
  • “exports”(AVT only) “format” is the type of the export. “signedUrl” is a signed URL containing the combined report (all models).
Note: There may not be any combined reports included with a given webhook. Only explicitly enabled export type will appear here.

Job Run items

"jobRunItems": [
  {
    "jobRunItemId": "561150af-2f59-48c5-be34-5859d787d860",
    "status": "Pending",
    "fileId": "fileId",
    "fileName": "fileName.rvt",
    "errorMessage": null,
    "errorCode": null,
    "exports": [
      {
        "format": "Offline",
        "signedUrl": "https://signed-avt-file-url"
      },
      {
        "format": "Html",
        "signedUrl": "https://signed-html-file-url"
      },
      {
        "format": "Excel",
        "signedUrl": "https://signed-excel-file-url"
      }
    ]
  }
]

One entry containing the above data will be present for every file that is processed as part of the job (Task) run.

  • “jobRunItemId” The ID of the job (Task) run item.
  • “status” The status of the job (Task) run item. Will be one of:
    • Pending
    • InProgress
    • Cancelled
    • FailedLimitProcessingTime
    • FailedDownload
    • FailedInstructions
    • FailedUpload
    • FailedUploadOptional
    • Success
    • Error
  • “fileId” The ID of the source file.
  • “fileName” The name of the source file.
  • “errorMessage” An error message, if one occurred.
  • “errorCode” An error code to provide to the support team, if one occurred.
  • “exports”
  • “format” is the type of the export
  • “signedUrl” is a signed URL containing the report (individual models).
Note: The offline (.AVT) export type will always be included, even if disabled for the job (Task). The other report types will appear if enabled in the job (Task) settings.

Summary

Here is a summarized list of the data fields available:

  • attempt
  • creationTimeUtc
  • customerId
  • endTime
  • errorCode
  • errorMessage
  • exports
  • failedFiles
  • fileId
  • fileName
  • hubId
  • hubName
  • id
  • jobId
  • jobname
  • jobRunId
  • jobRunItemId
  • payload
  • pendingFiles
  • projectId
  • projectName
  • Resource
  • scheduleType
  • scopeId
  • scopeType
  • startTime
  • status
  • successFiles
  • totalFiles
  • userId
  • version
  • webhook
  • webhookEventId
  • webhookUri

Exports

Only the data type and signed URL are included in the webhook body. A signed URL is a pre-authenticated URL that expires after 1 hour, which can be used to directly download the requested file without any additional authentication or other code required.

The primary export type to be used is the Offline export type, which is known on the UI as the .AVT export type. This file type is a .ZIP file containing a single JSON file (report.json). To access the report data, unzip the downloaded file, then load the report data from the JSON file. If necessary, the file can be renamed to a .ZIP file (e.g., “download.avt” would become “download.zip”). It is then possible to open the file in any compatible zip program.

Offline Export Extraction

The report.json file contained in the .AVT ZIP file can be parsed to extract a custom dataset as required.

Python example

The following example parses data from the Offline export into a structure that is organized in a similar way to the Files tab of the Excel export

import json
from datetime import datetime
import pandas as pd

file_path = r'report.json'

# Read the JSON data from the file
with open(file_path, 'r') as file:
    json_data = file.read()

# Parse the JSON string
data = json.loads(json_data)

reportDates = []
revitFiles = []
checksetPaths = []
checksetTitles = []
checksetAuthors = []
checksetDescriptions = []
overallResults = []
passPercents = []
passCounts = []
failCounts = []
reportCounts = []
errorCounts = []
notRunCounts = []

for f in data.get('fileResults'):
    reportDates.append(datetime.now())
    revitFiles.append(f.get('revitFile'))
    checksetPaths.append(data.get('checkSet').get('url'))
    checksetTitles.append(data.get('checkSet').get('name'))
    checksetAuthors.append(data.get('checkSet').get('author'))
    checksetDescriptions.append(data.get('checkSet').get('description'))
    overallResults.append(f.get('overallResult'))
    passPercents.append(f.get('passPercent'))
    passCounts.append(f.get('passChecks'))
    failCounts.append(f.get('failChecks'))
    reportCounts.append(f.get('countChecks'))
    errorCounts.append(f.get('errorChecks'))
    notRunCounts.append(f.get('skippedChecks'))

files  = pd.DataFrame({
    'Report Date': reportDates,
    'Revit File': revitFiles,
    'Checkset Path': checksetPaths,
    'Checkset Title': checksetTitles,
    'Checkset Author': checksetAuthors,
    'Checkset Description': checksetDescriptions,
    'Overall Result': overallResults,
    'Pass Percent': passPercents,
    'Pass Count': passCounts,
    'Fail Count': failCounts,
    'Report Count': reportCounts,
    'Error Count': errorCounts,
    'Not Run Count': notRunCounts
})

print(files)

Power BI example

When using PowerBI, it is possible to import the data using similar Python code to the previous example.

  1. Click Get Data on the Home toolbar and create a Blank query

  2. In the query editor, click Advanced Editor in the Query section of the Home toolbar

  3. Enter a query containing the relevant Python code, for example:

     let
         PythonScript = "
     import json
     from datetime import datetime
     import pandas as pd
    
     # Specify the correct path to the file being parsed
     file_path = r'C:\path\to\report.json'
    
     # Read the JSON data from the file
     with open(file_path, 'r') as file:
         json_data = file.read()
    
     # Parse the JSON string
     data = json.loads(json_data)
    
     reportDates = []
     revitFiles = []
     checksetPaths = []
     checksetTitles = []
     checksetAuthors = []
     checksetDescriptions = []
     overallResults = []
     passPercents = []
     passCounts = []
     failCounts = []
     reportCounts = []
     errorCounts = []
     notRunCounts = []
    
     for f in data.get('fileResults'):
         reportDates.append(datetime.now())
         revitFiles.append(f.get('revitFile'))
         checksetPaths.append(data.get('checkSet').get('url'))
         checksetTitles.append(data.get('checkSet').get('name'))
         checksetAuthors.append(data.get('checkSet').get('author'))
         checksetDescriptions.append(data.get('checkSet').get('description'))
         overallResults.append(f.get('overallResult'))
         passPercents.append(f.get('passPercent'))
         passCounts.append(f.get('passChecks'))
         failCounts.append(f.get('failChecks'))
         reportCounts.append(f.get('countChecks'))
         errorCounts.append(f.get('errorChecks'))
         notRunCounts.append(f.get('skippedChecks'))
    
     files  = pd.DataFrame({
         'Report Date': reportDates,
         'Revit File': revitFiles,
         'Checkset Path': checksetPaths,
         'Checkset Title': checksetTitles,
         'Checkset Author': checksetAuthors,
         'Checkset Description': checksetDescriptions,
         'Overall Result': overallResults,
         'Pass Percent': passPercents,
         'Pass Count': passCounts,
         'Fail Count': failCounts,
         'Report Count': reportCounts,
         'Error Count': errorCounts,
         'Not Run Count': notRunCounts
     })
    
     # Specify the data frame(s) to output
     files
    
     ",
         Output = Python.Execute(PythonScript)
     in
         Output

C# example

The following example parses data from the Offline export into a custom class, AvtReportSubset. The properties of this class should be named to match the JSON property names to be parsed.

public class FileResult
{
    public string RevitFile { get; set; }

    public string OverallResult { get; set; }

    public double PassPercent { get; set; }

    public int PassChecks { get; set; }

    public int FailChecks { get; set; }

    public int CountChecks { get; set; }

    public int ErrorChecks { get; set; }

    public int SkippedChecks { get; set; }
}

public class CheckSet
{
    public string Url { get; set; }

    public string Name { get; set; }

    public string Author { get; set; }

    public string Description { get; set; }
}

public class AvtReportSubset
{
    public CheckSet CheckSet { get; set; }

    public List<FileResult> FileResults { get; set; }
}

class Program
{
    static void Main()
    {
        string filePath = @"report.json";

        // Read the JSON data from the file
        string jsonData = System.IO.File.ReadAllText(filePath);

        // Deserialize the JSON string
        var data = System.Text.Json.JsonSerializer.Deserialize<AvtReportSubset>(
            jsonData, 
            new System.Text.Json.JsonSerializerOptions { PropertyNameCaseInsensitive = true });

        foreach (var file in data.FileResults)
        {
            Console.WriteLine($"Report Date: {DateTime.UtcNow}");
            Console.WriteLine($"Revit File: {file.RevitFile}");
            Console.WriteLine($"Checkset Path: {data.CheckSet.Url}");
            Console.WriteLine($"Checkset Title: {data.CheckSet.Name}");
            Console.WriteLine($"Checkset Author: {data.CheckSet.Author}");
            Console.WriteLine($"Checkset Description: {data.CheckSet.Description}");
            Console.WriteLine($"Overall Result: {file.OverallResult}");
            Console.WriteLine($"Pass Percent: {file.PassPercent}");
            Console.WriteLine($"Pass Count: {file.PassChecks}");
            Console.WriteLine($"Fail Count: {file.FailChecks}");
            Console.WriteLine($"Report Count: {file.CountChecks}");
            Console.WriteLine($"Error Count: {file.ErrorChecks}");
            Console.WriteLine($"Not Run Count: {file.SkippedChecks}");
        }
    }
}

Validating webhook

Since the webhook URL must be publicly accessible, it is in theory possible for anyone to call it. For this reason, every time a webhook is sent, a signature is calculated on the server and is sent as part of the header named bit-webhook-signature. This signature can be used to verify that the data has come from the intended source.

The signature is calculated in the same way as the Autodesk Webhooks API, and so the instructions at https://aps.autodesk.com/en/docs/webhooks/v1/tutorials/how-to-verify-payload-signature/ can be used to validate the signature. The only change that needs to be made is the header name.

In those instructions, WEBHOOKS_SECRET is the secret that can be revealed when creating or editing the webhook.

Failures and Retry Handling

If a webhook is not delivered successfully, several attempts to re-send it will be made. Not including the original send attempt, 8 retry attempts will be made before the webhook is disabled. This will be indicated on the UI, next to the Enabled icon.

The retry schedule is as follows, with the time being the amount of time elapsed since the initial attempt. The entries in bold indicate the points at which a warning email will be sent, notifying the user that the webhook is not being delivered successfully.

  1. 1 minute
  2. 5 minutes
  3. 15 minutes
  4. 40 minutes
  5. *140 minutes (2 hours, 20 minutes) *
  6. 9 hours
  7. 24 hours
  8. 48 hours

After the 8th and final retry attempt, the webhook will be disabled and an email will be sent notifying the user of such.

At any point before the final retry, the configured webhook URL can be updated, and the following retry attempt will use that new URL instead of the one initially configured.

Was this information helpful?