App Store Callbacks and Webhooks

Callbacks and Webhooks

The Duda App Store has two different types of notifications we send for specific events that happen in the Duda editor.

  • Callbacks notify your backend about events that are part of the core App lifecycle: installation, upgrade, downgrade, uninstallation. Duda needs to know that your platform has received this notification before Duda can continue to the next step for the user. Therefore, the response of your endpoint does affect the behavior of the Duda platform. Duda will progress the user flow only after getting a 200 HTTP response code from your endpoint. The timeout for callbacks is 60 seconds, but, realistically this should happen much faster. We have no retry policies for callbacks.

  • Webhooks notify the app about events that are not part of the App lifecycle. Therefore the behavior of the Duda platform is not affected by the response of the webhook endpoint. Your endpoint should return a 200 HTTP response for successful calls to allow Duda to monitor requests.

Authentication

Duda can (optionally) sign requests using the HMAC-SHA256 algorithm to allow your app to verify the authenticity of the sender (Duda) and of the payload.

📘

Getting a secret key

To get your secret key, please contact [email protected].

The signature value is calculated as follows:

  1. Signature = base64(hmac-sha256(secret-key, timestamp + "." + message))

  2. Where timestamp is the value of the x-duda-signature-timestamp request header.

  3. message is the actual request body.

  4. "." is a string separating the 2 values.

  5. secret-key your unique key you received from Duda, decoded using base64 and UTF-8.

This calculation is done by Duda and the value is added to the webhook in the x-duda-signature request header.

The same calculation should be made by the application and compared against the header to assert the message was signed by us using your secret key.

An example with data

The request body is {'key1':'world','key2':'world'} the timestamp (x-duda-signature-timestamp header) is 1570350275357 and our secret key is mysecretsecret.

Calculating base64(hmac-sha256(secret-key, timestamp + "." + message)) results in +DCfT1wIMUiaZnlZB4u59/d5wkXKA89lv67Ov66vnyc= which is the value we will find in x-duda-signature header of the signed request.

Code examples

const crypto = require('crypto');

// get data from request sent to webhook or callback endpoint
const timestamp = req.headers['x-duda-signature-timestamp'];
const msg = JSON.stringify(req.body);

const decodedKey = Buffer.from(secretKey, 'base64').toString('utf8');
const signature = crypto.createHmac('SHA256', decodedKey)
  .update(`${timestamp}.${msg}`)
  .digest('base64');

const signatureHeader = req.headers['x-duda-signature'];
assert(signature === signatureHeader);
String getHMAC(String timestamp,String message) {
        String secretKey = ...;
        SecretKeySpec signingKey = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256");

        String input = timestamp + "." + message;
        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(signingKey);
        byte[] rawHmac = mac.doFinal(input.getBytes(StandardCharsets.UTF_8));
        return new String(Base64.getEncoder().encode((rawHmac)));
}


String body = request.getBody();
String timestamp = request.getHeader("x-duda-signature-timestamp");

String hmac = getHMAC(timestamp, body);
String signautreHeader = request.getHeader("x-duda-signature");

assert hmac.equals(signautreHeader);

Callbacks

All apps are automatically registered to callbacks to the endpoints set in the manifest. All fields are required.

Install

Duda sends callbacks to the endpoint specified in the manifest under installation_endpoint with a payload of the following structure:

{
    "auth": {
        "type": "bearer",
        "authorization_code": "XXX-XXXXX-XXXXX",
        "refresh_token": "YYY-YYYYY-YYYYY",
        "expiration_date": 1554254560438
    },
    "api_endpoint": "https://api-sandbox.duda.co",
    "installer_account_uuid": "10",
    "account_owner_uuid": "12",
    "user_lang": "en",
    "app_plan_uuid": "332653a3-df51-45ce-a873-fbb0b1ccb49f",
    "recurrency": "MONTHLY",
    "site_name": "1501ccca016a4220861ef07fe2c8eb0d",
    "free": true,
    "configuration_data": "{object}"
}

The app_plan_uuid is the plan the user selected to install. The recurrency is the payment recurrency (ANNUAL, MONTHLY, or NULL for free plans). The site_name is the identifier for the site on which the app was installed. It should be the same value as shown in the browser address bar. The configuration_data is an optional field used only when the application is installed via an API. It contains a JSON object which is passed as a parameter by the installer of the application.

The properties installer_account_uuid, account_owner_uuid, and user_lang are explained on Hierarchy of users. The properties under auth are explained on App Store: Getting Started Guide, additional in-depth explanation is found on Authentication: Apps .

If the endpoint on installation_endpoint returned HTTP status code 200, Duda will finish the installation and direct the user to an iframe with the source specified on base_sso_url using the SSO link.

📘

The 'free' flag

Duda will send the free flag for installation which won't be paid. Your app should anticipate the free capabilities to avoid collecting payments on them. These would include:

  1. Installations on test environments.
  2. Installations generated by Duda employees for demo, sales, QA, and support cases.

Upgrade/Downgrade

Duda sends callbacks to the endpoint specified in the manifest under updowngrade_installation_endpoint with a payload of the following structure:

{
    "app_plan_uuid": "332653a3-df51-45ce-a873-fbb0b1ccb49f",
    "recurrency": "MONTHLY",
    "site_name": "1501ccca016a4220861ef07fe2c8eb0d"
}

The app_plan_uuid, recurrency, and site_name attributes are the same as for the installation callback. Duda would wait for a 200 HTTPS code response to finish the upgrade flow.

Uninstall

Duda sends callbacks to the endpoint specified in the manifest under uninstallation_endpoint with a payload of the following structure:

{
  "site_name":"1501ccca016a4220861ef07fe2c8eb0d",
  "free":false
}

Webhooks

The Duda App Store allows apps to register to webhooks. Webhooks allow the app to be informed about events happening outside the app integration context, such as publishing the site or updating the Duda reseller branding.
Part of the webhooks are available to Duda's users, see the public webhooks documentation. The other part is webhooks available only to apps, listed below.

Register to Webhooks

To register to webhooks, update your apps' manifest with the webhooks object:

"webhooks": {
    "endpoint": "https://example.com/endpoint",
    "events": [
      "PUBLISH"
    ]
  }

Where endpoint is the target URL and events is the list of events you want to register to.
Note that registering to webhooks requires sufficient scopes.

Webhooks reference

For the general structure of webhooks sent by Duda see the public webhooks documentation.
The following reference describes the webhooks available for apps.

Contact form sent

🚧

Version differences

Note that this webhook format is not the same as the current public webhook. Updating the version is under development.

{
   "data":[
      {
         "field_label":"Name",
         "field_value":"John",
         "field_type":"text",
         "field_key":"",
         "field_id":"dmform-00"
      },
      {
         "field_label":"Email",
         "field_value":"[email protected]",
         "field_type":"email",
         "field_key":"",
         "field_id":"dmform-01"
      },
      {
         "field_label":"Phone",
         "field_value":"5555555555",
         "field_type":"tel",
         "field_key":"",
         "field_id":"dmform-02"
      },
      {
         "field_label":"What services do you need?",
         "field_value":"Option 1",
         "field_type":"dropdown",
         "field_key":"",
         "field_id":"dmform-03"
      },
      {
         "field_label":"Title",
         "field_value":"Contact Us",
         "field_type":"form_title",
         "field_key":"",
         "field_id":null
      }
   ],
   "source":null,
   "resource_data":{
      "site_name":"1501ccca016a4220861ef07fe2c8eb0d"
   },
   "event_timestamp":1565789644948,
   "event_type":"CONTACT_FORM_SENT"
}

Site restored

{
   "data":null,
   "source":{
      "type":"EDITOR",
      "account_name":"[email protected]"
   },
   "resource_data":{
      "site_name":"1501ccca016a4220861ef07fe2c8eb0d"
   },
   "event_timestamp":1565789782454,
   "event_type":"SITE_RESTORED"
}

Content library updated

{
   "data":{
      "location_data":{
         "phones":[
            {
               "phoneNumber":"54564645",
               "label":""
            }
         ],
         "emails":[
            {
               "emailAddress":"[email protected]",
               "label":"John Smith"
            }
         ],
         "label":"HQ",
         "social_accounts":{

         },
         "address":{

         },
         "address_geolocation":"Piccadilly Circus, London, United Kingdom",
         "geo":{
            "longitude":"-0.13511",
            "latitude":"51.51008"
         },
         "logo_url":null,
         "business_hours":[

         ]
      },
      "additional_locations":[

      ],
      "site_texts":{
         "overview":"",
         "services":"",
         "custom":[

         ],
         "about_us":""
      },
      "business_data":{
         "name":null,
         "logo_url":null
      },
      "site_images":[

      ]
   },
   "source":{
      "type":"EDITOR",
      "account_name":"[email protected]"
   },
   "resource_data":{
      "site_name":"1501ccca016a4220861ef07fe2c8eb0d"
   },
   "event_timestamp":1565789980695,
   "event_type":"CONTENT_LIB_CHANGED"
}

Content library published

This event is the counterpart of the content library publish API.

{
   "data":{
      "location_data":{
         "phones":[
            {
               "phoneNumber":"54564645",
               "label":""
            }
         ],
         "emails":[
            {
               "emailAddress":"[email protected]",
               "label":"John Smith"
            }
         ],
         "label":"HQ",
         "social_accounts":{

         },
         "address":{

         },
         "address_geolocation":"Piccadilly Circus, London, United Kingdom",
         "geo":{
            "longitude":"-0.13511",
            "latitude":"51.51008"
         },
         "logo_url":null,
         "business_hours":[

         ]
      },
      "additional_locations":[

      ],
      "site_texts":{
         "overview":"",
         "services":"",
         "custom":[

         ],
         "about_us":""
      },
      "business_data":{
         "name":null,
         "logo_url":null
      },
      "site_images":[

      ]
   },
   "source":{
      "type":"API"
   },
   "resource_data":{
      "site_name":"1501ccca016a4220861ef07fe2c8eb0d"
   },
   "event_timestamp":1565790284129,
   "event_type":"CONTENT_LIB_PUBLISHED"
}

Did this page help you?