Notifications

Cloudinary provides a notification feature for informing your backend about certain actions that have been completed, either from an upload API method, or from a user action within the Media Library. When the action is completed an HTTP POST request is sent to a notification URL you provide (a webhook), and the data will contain all the results pertinent to that particular action. This means that you also need to set up a URL in your application that’s accessible from the public web.

By default, Cloudinary's upload API works synchronously. For example, uploaded assets are processed and eager transformations are generated synchronously during an upload API call, which means that the original asset and derived assets are available immediately for delivery and further management. However, in some cases you may need to be notified that an upload has completed, or you may want to process actions asynchronously in the background, especially actions that require a relatively long time to process and require your users to actively wait for the processing to finish.

Global notification URL

You can set a global Notification URL in the Upload settings page of the Cloudinary Management Console. Once configured, Cloudinary automatically sends responses to that URL address any time:

  • A user performs a certain action in the Media Library. For more details about the list of actions that trigger a notification, see the Media Library notifications topic below.
  • You run any Cloudinary upload API method that supports the notification_url parameter, but without specifically setting a value for the notification_url parameter. When the notification_url parameter is included in the method call, then the value set there overrides the global value. For more details, see the Upload API notifications topic below.

Notification Payload

When an action that triggers a notification is completed, an HTTP POST request will be sent to the notification URL you provided. The post data will contain all the result details for the action that triggered it. The body of this POST request is essentially the same information as you get in response to a standard synchronous request.

Here's an example POST request sent by Cloudinary after an upload completes:

Copy to clipboard
// in the header of the POST

POST /my_notification_endpoint HTTP/1.1
X-Cld-Timestamp: 1368881627
X-Cld-Signature: 29a383e289bc569310a8ca9899019a3167b4909e
Content-Type: application/json

// in the body of the POST

{
 "public_id": "djhoeaqcynvogt9xzbn9",
 "version": 1571215398,
 "width": 864,
 "height": 576,
 "format": "jpg",
 "resource_type": "image",
 "created_at": "2017-05-18T12:53:46Z",
 "bytes": 120253,
 "type": "upload",
 "url": "http://res.cloudinary.com/demo/image/upload/v1571215398/djhoeaqcynvogt9xzbn9.jpg",
 "secure_url": "https://res.cloudinary.com/demo/image/upload/v1571215398/djhoeaqcynvogt9xzbn9.jpg"
}

For additional examples of notification responses from various API calls, see notification response examples.

Note
If you would like to include the initiating client IP Address in the response sent to your notification, please contact us.

Upload API notifications

In specific situations, you may want to notify your backend as soon as a method call is complete, for example when dealing with user initiated uploads from a browser or mobile device. You can tell Cloudinary to notify your application as soon as the method is complete by adding the notification_url parameter to the method call and setting it to any valid HTTP or HTTPS URL.

The following upload API methods support the notification_url parameter: upload, explicit, explode, generate_archive, sprite, and multi.

For example, to add a notification_url to a specific upload call:

Ruby:
Copy to clipboard
Cloudinary::Uploader.upload("sample.jpg", 
  :notification_url => "https://mysite.example.com/my_notification_endpoint")
PHP:
Copy to clipboard
\Cloudinary\Uploader::upload("sample.jpg", 
  array("notification_url" => "https://mysite.example.com/my_notification_endpoint"));
Python:
Copy to clipboard
cloudinary.uploader.upload("sample.jpg", 
  notification_url = "https://mysite.example.com/my_notification_endpoint")
Node.js:
Copy to clipboard
cloudinary.v2.uploader.upload("sample.jpg", 
  { notification_url: "https://mysite.example.com/my_notification_endpoint" }, 
  function(error, result) {console.log(result, error); });
Java:
Copy to clipboard
cloudinary.uploader().upload("sample.jpg", 
  ObjectUtils.asMap("notification_url", "https://mysite.example.com/my_notification_endpoint"));
.Net:
Copy to clipboard
var uploadParams = new ImageUploadParams(){
  File = new FileDescription(@"sample.jpg"),
  NotificationUrl = "https://mysite.example.com/my_notification_endpoint"};
var uploadResult = cloudinary.Upload(uploadParams);

Media Library notifications

To enable Media Library notifications, you have to configure your account's Global notification URL.

When a user performs any of the following actions in the Media Library, it will trigger a notification and send it to your global notification URL:

  • Uploading a new asset.
  • Renaming an existing asset (including when the asset is moved to a different folder, which modifies the public_id).
  • Deleting an asset. Deleting a folder that contains assets will also trigger a delete notification for all individual assets in that folder. Note that deleting empty folders does not send a notification.
  • Adding, removing or updating the Context data for an asset.
  • Adding, removing or updating the Structured Metadata for an asset.
  • Adding or removing a Tag for an asset.
  • Creating a new Folder.

For some examples of notification responses from these types of operations, see the Notification response examples section.

Eager transformation notifications

You can instruct Cloudinary to send a separate notification whenever a requested Eager transformation completes by adding the eager_notification_url parameter to the method call and setting it to any valid HTTP or HTTPS webhook URL. Eager transformations should be set up to work asynchronously in the background after an upload completes by also including the eager_async parameter (for more details, see Eager asynchronous transformations). This eager notification is in addition to any notification sent via the optional notification_url parameter.

The following upload API methods support the eager_notification_url parameter: upload and explicit.

For example, the following method will upload the sample.jpg image and then eagerly generate two transformed images:

  1. Pad to a width and height of 300 pixels.
  2. Crop to a width of 160 pixels and a height of 100 pixels.

Furthermore, the transformations will be performed asynchronously after the image finishes uploading, with a callback URL to notify your application once the upload is complete, and a different callback URL to notify your application once the eager transformations are complete:

Ruby:
Copy to clipboard
Cloudinary::Uploader.upload("sample.jpg",
  :eager => [
    { :width => 300, :height => 300, :crop => :pad}, 
    { :width => 160, :height => 100, :crop => :crop}], 
  :eager_async => true, 
  :eager_notification_url => "https://mysite.example.com/eager_endpoint",
  :notification_url => "https://mysite.example.com/upload_endpoint")
PHP:
Copy to clipboard
\Cloudinary\Uploader::upload("sample.jpg", 
  array( 
    "eager" => array(
        array("width" => 300, "height" => 300, "crop" => "pad"),
        array("width" => 160, "height" => 100, "crop" => "crop")),
    "eager_async" => TRUE,
    "eager_notification_url" => "https://mysite.example.com/eager_endpoint",
    "notification_url" => "https://mysite.example.com/upload_endpoint"));
Python:
Copy to clipboard
cloudinary.uploader.upload("sample.jpg", 
  eager = [
    {"width": 300, "height": 300, "crop": "pad"},
    {"width": 160, "height": 100, "crop": "crop"}],
  eager_async = true,
  eager_notification_url = "https://mysite.example.com/eager_endpoint",
  notification_url = "https://mysite.example.com/upload_endpoint")
Node.js:
Copy to clipboard
cloudinary.v2.uploader.upload("sample.jpg", 
  { eager: [
      { width: 300, height: 300, crop: "pad" }, 
      { width: 160, height: 100, crop: "crop"} ],                                   
    eager_async: true,
    eager_notification_url: "https://mysite.example.com/eager_endpoint",
    notification_url: "https://mysite.example.com/upload_endpoint" }, 
  function(error, result) {console.log(result, error); });
Java:
Copy to clipboard
cloudinary.uploader().upload("sample.jpg", 
  ObjectUtils.asMap(
    "eager", Arrays.asList(
      new EagerTransformation().width(300).height(300).crop("pad"),
      new EagerTransformation().width(160).height(100).crop("crop")),
    "eager_async", true,
    "eager_notification_url", "https://mysite.example.com/eager_endpoint",
    "notification_url", "https://mysite.example.com/upload_endpoint"));
.Net:
Copy to clipboard
var uploadParams = new ImageUploadParams(){
  File = new FileDescription(@"sample.jpg"),
  EagerTransforms = new List<Transformation>(){
   new EagerTransformation().Width(300).Height(300).Crop("pad"),
   new EagerTransformation().Width(160).Height(100).Crop("crop")},
  EagerAsync = true,
  EagerNotificationUrl = "https://mysite.example.com/eager_endpoint",
  NotificationUrl = "https://mysite.example.com/upload_endpoint"};
var uploadResult = cloudinary.Upload(uploadParams);

Notification combinations with eager

The following table shows the results of adding different notification parameters when also requesting eager transformations with and without, the eager_async parameter.

Parameters Result
eager and
notification_url
The upload method response and the notification_url are sent after BOTH the upload completes and the eager transformations have finished.
eager and
eager_notification_url
The upload method response and the eager_notification_url are sent after BOTH the upload completes and the eager transformations have finished.
eager and
eager_notification_url and
eager_async
- The upload method response is sent after the upload completes and include a batch_id for tracking the eager job.
- eager_notification_url is sent after the eager transformations have finished with a batch_id for tracking.
eager and
notification_url and
eager_notification_url
The upload method response and the notification_url are sent after BOTH the upload completes and the eager transformations have finished. The eager notification is ignored.
eager and
notification_url and
eager_notification_url and
eager_async
- The upload method response and the notification_url are sent after the upload completes and include a batch_id for tracking the eager job.
- eager_notification_url is sent after the eager transformations have finished with a batch_id for tracking.

Eager notification response

When the eager transformations are completed, an HTTP POST request will be sent to the eager notification URL you provided with details about the requested eager transformations including the HTTP and HTTPS URLs for accessing the derived images. For example:

Copy to clipboard
{
  "notification_type":"eager", 
  "eager":[
  {
    "crop":"pad", 
    "width":300, 
    "height":300, 
    "bytes":1805410,
    "url":"http://res.cloudinary.com/demo/image/upload/c_pad,h_300,w_300/v1517972/qvd.jpg", 
    "secure_url":"https://res.cloudinary.com/demo/image/upload/h_100,w_190/v1517972/qvd.jpg" },
  {
    "crop":"crop", 
    "width":160, 
    "height":100, 
    "bytes":705220,
    "url":"http://res.cloudinary.com/demo/image/upload/c_crop,h_160,w_100,g_south/v1517972/qvd.jpg", 
    "secure_url":"https://res.cloudinary.com/demo/image/upload/c_crop,h_160,w_100/v1517972/qvd.jpg" }], 
  "public_id":"qvd", 
  "batch_id":"6ebf1479dd8b71eb4e632620520f7347978a37ee7e30595a119c0efb957fb8c3ff96"
}

Verifying notification signatures

Cloudinary signs all notification responses that it sends to your provided notification_url or eager_notification_url endpoint, allowing you to validate that they were not sent by a third-party. The notification includes both an X-Cld-Signature header and a X-Cld-Timestamp header for validating the notification response.

When you provide a notification_url or eager_notification_url endpoint in a method request, or if you set a global notification_url in the console settings, Cloudinary includes both an X-Cld-Signature header and a X-Cld-Timestamp header with the response sent to the specified notification endpoint.

Here's an example POST request header sent by Cloudinary:

Copy to clipboard
POST /my_notification_endpoint HTTP/1.1
X-Cld-Timestamp: 1368881627
X-Cld-Signature: 29a383e289bc569310a8ca9899019a3167b4909e
Content-Type: application/json

The signature is a hexadecimal message digest (hash value) created with the SHA-1 or SHA-256 (Secure Hash Algorithm) cryptographic function.

Note
By default, Cloudinary supports both SHA-1 and SHA-256 digests for validation, and you can use either. If you want to limit your account to only allowing the SHA-256 digest for all your validations, submit a request.

You can compare the returned signature value in the header with the value of a signature generated on your server-side as follows:

  1. Create a single string containing the entire response body
  2. Append the X-Cld-Timestamp value on the end of the string.
  3. Append your API secret to the end of the string.
  4. Create a hexadecimal message digest (hash value) of the string using an SHA function.

For example, if the response body is {public_id: 'sample'}, the X-Cld-Timestamp is 1315060510, and your API secret is abcd:

  • Parameters to sign:
    • {public_id: 'sample'}
    • 1315060510
  • In a single string:
    • {public_id: 'sample'}1315060510
  • String including the API secret that is used to create the SHA-1 signature:
    • {public_id: 'sample'}1315060510abcd
  • SHA-1 hexadecimal result:
    • b4ad47fb4e25c7bf5f92a20089f9db59bc302313

Tip
You should also compare the timestamp value with the current time to make sure that the signature was generated within a reasonable amount of time (e.g., within the last 2 hours).

PHP code example for validating the notification:

Copy to clipboard
//Read request body
$body = file_get_contents('php://input');
$api_secret = 'replace_with_real_api_secret';

if (!validateSignature(getallheaders(), $body, $api_secret)) {
    die("Validation failed");
}
//Use the response for your needs

function validateSignature($headers, $upload_response, $api_secret)
{
    $signed_payload = $upload_response . $headers["X-Cld-Timestamp"];
        // Compute signature with SHA-1 function and compare to header value   
    if (sha1($signed_payload . $api_secret) !== $headers['X-Cld-Signature']) {
        // Signatures DON'T match
        return false;
    }
        // Compare the current time to the received timestamp
    if ($headers["X-Cld-Timestamp"] <= strtotime('-2 hours')) {
        // Signatures match, but older than 2 hours
        return false;
    }
        // Signatures match, and timestamp is valid
    return true;
}

See also
Have a look at the Cloudinary Signatures quick reference for a summary of the payload string to sign for notification signatures as well as information on other use cases that may require signature generation.

Notification response examples

This section provides several examples of notifcation responses to a variety of different API calls or Media Library operations. Keep in mind that this list is not comprehensive, and that the exact content of the response will depend on the options sent with the relevant API call or the exact operation performed in the Media Library.

Tags

Copy to clipboard
{
  "notification_type": "resource_tags_changed",
  "source": "ui",
  "resources": [
    {
      "public_id": "testing",
      "resource_type": "image",
      "type": "upload",
      "added": [
        "random"
      ]
    }
  ]
}

Context

Copy to clipboard
{ 
   "notification_type":"resource_context_changed",
   "source":"api",
   "resources": {   
       "women_model2": {
          "added":[{"name":"key1","value":"mykey1"}],
          "removed":[],
          "updated":[]
       }
    }
}

Metadata

Copy to clipboard
{
  "notification_type": "resource_metadata_changed",
  "source": "api",
  "resources": {
    "eng/Image 2019-12-16 at 8.39.08 AM": {
      "previous_metadata": {},
      "new_metadata": {
        "onjmg1uxnmk69vssmmfp": "y0g4xju6awyrhumbyvbx"
      },
      "resource_type": "image",
      "type": "upload"
    }
  }
}

Rename

Copy to clipboard
{
  "notification_type": "rename",
  "from_public_id": "testing",
  "to_public_id": "testing-newname"
}

Create Folder

Copy to clipboard
{
  "notification_type": "create_folder",
  "source": "ui",
  "folder_path": "workflow",
  "folder_name": "workflow"
}

Delete

Copy to clipboard
{
  "notification_type": "delete",
  "resources": [
    {
      "resource_type": "image",
      "type": "fetch",
      "public_id": "https://res.cloudinary.com/lamedscloud/image/upload/faces/flatmates_test.jpg",
      "version": 1547517350
    }
  ]
}

Upload (simple)

Copy to clipboard
{
  "public_id": "illra05q2jkghemh5gkw",
  "version": 1549349780,
  "width": 1000,
  "height": 257,
  "format": "png",
  "resource_type": "image",
  "created_at": "2019-02-05T06:56:20Z",
  "tags": [],
  "pages": 1,
  "bytes": 20420,
  "type": "upload",
  "etag": "3defbddd14101621303dd7ff84370747",
  "placeholder": false,
  "url": "http://mendoza-res.cloudinary.com/image/upload/v1549349780/illra05q2jkghemh5gkw.png",
  "secure_url": "https://mendoza-res.cloudinary.com/image/upload/v1549349780/illra05q2jkghemh5gkw.png",
  "access_mode": "public",
  "original_filename": "cloudinary",
  "notification_type": "upload"
}

Upload (complex)

Copy to clipboard
{
  "public_id": "myfolder/squirrel",
  "version": 1548832642,
  "width": 1212,
  "height": 866,
  "format": "jpg",
  "resource_type": "image",
  "created_at": "2019-01-30T07:17:22Z",
  "tags": [
    "critter",
    "animal",
    "rodent",
    "organism",
    "mammal",
    "squirrel",
  ],
  "pages": 1,
  "bytes": 142139,
  "type": "upload",
  "etag": "f7d130b5ad921adb2c949c6e0d86e59d",
  "placeholder": false,
  "url": "http://res.cloudinary.com/demo/image/upload/v1548832642/myfolder/1.jpg",
  "secure_url": "https://res.cloudinary.com/demo/image/upload/v1548832642/myfolder/1.jpg",
  "access_mode": "public",
  "moderation": [
    {
      "status": "pending",
      "kind": "manual"
    }
  ],
  "info": {
    "categorization": {
      "imagga_tagging": {
        "status": "complete",
        "data": [
          {
            "tag": {
              "en": "critter"
            },
            "confidence": 1
          },
          {
            "tag": {
              "en": "animal"
            },
            "confidence": 1
          },
          {
            "tag": {
              "en": "rodent"
            },
            "confidence": 0.7187
          },
          {
            "tag": {
              "en": "organism"
            },
            "confidence": 0.6881
          },
          {
            "tag": {
              "en": "mammal"
            },
            "confidence": 0.5457
          },
          {
            "tag": {
              "en": "squirrel"
            },
            "confidence": 0.533
          },
        ]
      }
    },
    "detection": {
      "aws_rek_face": {
        "status": "complete",
        "data": {
          "celebrity_faces": [],
          "unrecognized_faces": [],
          "orientation_correction": "ROTATE_0"
        }
      }
    }
  },
  "faces": [],
  "phash": "4d32a4db14629beb",
  "coordinates": {
    "faces": []
  },
  "illustration_score": 0,
  "semi_transparent": false,
  "grayscale": false,
  "quality_analysis": {
    "focus": 1
  },
  "original_filename": "grey-squirrel",
  "notification_type": "upload"
}

✔️ Feedback sent!

Rate this page: