29 Jun 2018

Derivative Webhooks for .NET SDK

Default blog image

Just released the version 1.2 of the .NET SDK on Nuget with support for derivative webhooks! Special thanks to Fredrik Larsen for submitting the code for it!

The following code sample is based on the learn forge tutorial code, for WebAPI (inherits from ApiController). It first defines a workflow for the JobPayload. As the hook can be already defined, let's get the existing hooks, check for the workflow name and check if the callback URL is correct, if not, delete the hook. Note the callback URL on local environment is usually created with ngrok tool (or similar), so it may be outdated. With that, post the job. The next function listens to the webhook call, left empty for your custom implementation. 

public class ModelDerivativeController : ApiController
  /// <summary>
  /// Start the translation job for a give bucketKey/objectName
  /// </summary>
  /// <param name="objModel"></param>
  /// <returns></returns>
  public async Task<dynamic> TranslateObject([FromBody]TranslateObjectModel objModel)
    dynamic oauth = await OAuthController.GetInternalAsync();

    // prepare the payload
    List<JobPayloadItem> outputs = new List<JobPayloadItem>()
      new JobPayloadItem(
        new List<JobPayloadItem.ViewsEnum>()

    string workflow = "forgesampleworkflow";
    JobPayload job = new JobPayload(new JobPayloadInput(objModel.objectName), new JobPayloadOutput(outputs), new JobPayloadMisc(workflow));

    DerivativeWebhooksApi webhook = new DerivativeWebhooksApi();
    webhook.Configuration.AccessToken = oauth.access_token;
    dynamic existingHooks = await webhook.GetHooksAsync(DerivativeWebhookEvent.ExtractionFinished);

    // get the callback from your settings (e.g. web.config)
    string callbackUlr = WebConfigurationManager.AppSettings["FORGE_DERIVATIVE_CALLBACK_URL"];

    bool createHook = true; // need to create, we don't know if our hook is already there...
    foreach (KeyValuePair<string, dynamic> hook in new DynamicDictionaryItems(existingHooks.data))
      if (hook.Value.scope.workflow.Equals(workflow))
        // ok, found one hook with the same workflow, no need to create...
        createHook = false;
        if (!hook.Value.callbackUrl.Equals(callbackUlr))
          await webhook.DeleteHookAsync(DerivativeWebhookEvent.ExtractionFinished, new System.Guid(hook.Value.hookId));
          createHook = true; // ops, the callback URL is outdated, so delete and prepare to create again

    // need to (re)create the hook?
    if (createHook) await webhook.CreateHookAsync(DerivativeWebhookEvent.ExtractionFinished, callbackUlr, workflow);

    // start the translation
    DerivativesApi derivative = new DerivativesApi();
    derivative.Configuration.AccessToken = oauth.access_token;
    dynamic jobPosted = await derivative.TranslateAsync(job, true /* force retranslate? */);
    return jobPosted;

  public async Task Webhook([FromBody]JObject body)
    // ToDo


  /// <summary>
  /// Model for TranslateObject method
  /// </summary>
  public class TranslateObjectModel
    public string bucketKey { get; set; }
    public string objectName { get; set; }


Related Article