# Move Artifactory and Nexus webhooks to CloudRepo

> Which CloudRepo webhook event and payload field replaces each Artifactory or Nexus one, and how to rewire a Jenkins job that runs on them.

If a Jenkins job, a chat message or a script runs when Artifactory or Nexus posts a webhook, this page maps each of their events and payload fields to CloudRepo’s, so you can rewire it one event at a time. A move copies your files and not your webhooks, so you create each webhook again in CloudRepo: [Webhooks](/docs/manage/webhooks.html) shows the form.

CloudRepo has three file events, upload, download and delete, and posts each one as JSON to an `https://` address (or as a message to Slack). This page covers the JSON webhook, the one a build job listens to.

## Artifactory events

Artifactory names an event by a domain and a type, and a webhook picks the ones it fires on. JFrog lists them on [Webhook Event Types](https://docs.jfrog.com/integrations/docs/webhook-event-types). Here is each artifact and Docker event beside the CloudRepo event that replaces it, and the domains CloudRepo has nothing for.

| Artifactory event                                                                  | CloudRepo event          | What to know                                                                                                                                                                    |
| ---------------------------------------------------------------------------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Artifact `deployed`                                                                | `upload-complete`        | Tick **Upload** on the CloudRepo webhook.                                                                                                                                       |
| Artifact `deleted`                                                                 | `delete-file-complete`   | Tick **Delete**. A Delete webhook also receives other `event-type` values from portal actions such as deleting a folder or restoring from the Trash, so filter on `event-type`. |
| Docker `pushed`                                                                    | `upload-complete`        | A tag push fires the upload webhooks. A pull, a layer upload and a push by digest fire none.                                                                                    |
| Docker `deleted`                                                                   | `delete-file-complete`   | Deleting a tag in the portal, or a tag or an image from a registry client, fires the delete webhooks.                                                                           |
| Artifact `moved` or `copied`                                                       | No match                 | CloudRepo has no event for either.                                                                                                                                              |
| Artifact `cached`                                                                  | No separate cached event | CloudRepo has no event named `cached`; its three events are upload, download and delete.                                                                                        |
| Artifact property `added` or `deleted`                                             | No match                 | CloudRepo has no events for artifact properties.                                                                                                                                |
| Docker `promoted`                                                                  | No match                 | CloudRepo has no event for a promotion.                                                                                                                                         |
| Build, Release Bundle, Distribution, Curation, Xray scan, AppTrust and user events | No match                 | CloudRepo’s webhooks are for file events in a repository.                                                                                                                       |
| None listed                                                                        | `download-complete`      | JFrog’s page lists no download event. Tick **Download** to receive one.                                                                                                         |

[Webhooks](/docs/manage/webhooks.html) says what a Docker repository fires and what it does not, and that a webhook on a Maven group’s member repository, not on the group, hears a download through the group.

## Nexus events

A Nexus repository webhook fires on asset events or on component events, and the payload’s `action` says what happened. Sonatype’s [Example Headers And Payloads](https://help.sonatype.com/en/example-headers-and-payloads.html) shows `CREATED`. The other actions, `UPDATED`, `DELETED` and `PURGED`, are in Sonatype’s [published source](https://github.com/sonatype/nexus-public/blob/main/public/common/components/nexus-repository-content/src/main/java/org/sonatype/nexus/repository/content/webhooks/RepositoryAssetWebhook.java).

| Nexus event                                   | CloudRepo event        | What to know                                                                                                        |
| --------------------------------------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------- |
| Asset `CREATED`                               | `upload-complete`      | One event for each file.                                                                                            |
| Component `CREATED`                           | `upload-complete`      | CloudRepo has no component event. The upload event is for the file.                                                 |
| Asset `DELETED`                               | `delete-file-complete` | A Delete webhook also receives other `event-type` values, so filter on `event-type`, as the Artifactory table says. |
| Component `DELETED`                           | `delete-file-complete` | CloudRepo has no component event.                                                                                   |
| Asset or component `UPDATED` or `PURGED`      | No match               | CloudRepo has no event for either.                                                                                  |
| Global webhooks (audit and repository events) | No match               | A CloudRepo webhook belongs to one repository.                                                                      |
| None listed                                   | `download-complete`    | Sonatype’s pages list no download event for repository webhooks. Tick **Download** to receive one.                  |

Sonatype shows two repository payloads, asset and component, whatever the format, and `asset.format` or `component.format` in the payload names the format. In CloudRepo a Docker tag push is `upload-complete` and deleting a tag or an image is `delete-file-complete`, whichever vendor you come from.

## Payload fields

Both vendors post a JSON body, and its field names differ from CloudRepo’s. JFrog’s come from [Webhook Event Types](https://docs.jfrog.com/integrations/docs/webhook-event-types) and Sonatype’s from [Example Headers And Payloads](https://help.sonatype.com/en/example-headers-and-payloads.html); a cell reads None listed where the vendor’s examples do not show the field. CloudRepo’s are the fields of a JSON webhook (a Slack webhook posts a text message instead).

| What you read                | Artifactory                                                                     | Nexus                                                                                                    | CloudRepo                                   |
| ---------------------------- | ------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------- |
| Which event                  | `domain` and `event_type`                                                       | The `X-Nexus-Webhook-Id` header and `action`                                                             | `event-type`                                |
| Repository                   | `data.repo_key`                                                                 | `repositoryName`                                                                                         | `repository-id`                             |
| File name and path           | `data.name` and `data.path`                                                     | `asset.name`                                                                                             | `file-name` and `file-path`                 |
| Size in bytes                | `data.size`                                                                     | None listed                                                                                              | `file-size-bytes`                           |
| Checksum                     | `data.sha256`                                                                   | None listed                                                                                              | No match                                    |
| Who did it                   | `userContext.id`                                                                | `initiator`, which Sonatype’s example shows as a user and an address                                     | `user-id`                                   |
| Where from                   | None listed                                                                     | `initiator`, in the same example                                                                         | `ip-address` and `user-agent`               |
| When                         | None listed                                                                     | `timestamp`                                                                                              | `event-timestamp`                           |
| Public or private repository | None listed                                                                     | None listed                                                                                              | `repository-visibility`                     |
| Links to the file            | None listed                                                                     | None listed                                                                                              | `private-url`, `public-url` and `admin-url` |
| Docker image and tag         | `data.image_name` and `data.tag`                                                | None listed                                                                                              | `file-name`, written as image:tag           |
| Component group and version  | None listed                                                                     | `component.group` and `component.version`                                                                | No match                                    |
| Where the event came from    | `subscription_key`, `jpd_origin` and `source`                                   | `nodeId` and the `X-Nexus-Webhook-Delivery` header                                                       | No match                                    |
| Other ids and details        | `userContext.isToken`, `userContext.realm`, `data.platforms`, `data.image_type` | `asset.id`, `asset.assetId`, `component.id`, `component.componentId`, `asset.format`, `component.format` | No match                                    |

JFrog’s `data.path` ends in the file name. CloudRepo’s `file-path` is the directory and `file-name` is the file, so JFrog’s path is `file-path`, a slash, then `file-name`, unless `file-path` is empty.

CloudRepo sends no checksum, so a job that keys on `data.sha256` has nothing to read.

## Fields by CloudRepo event

A CloudRepo event carries these fields and no others. **yes** means the event sends the field.

| CloudRepo field         | Upload                        | Download | Delete |
| ----------------------- | ----------------------------- | -------- | ------ |
| `event-type`            | yes                           | yes      | yes    |
| `user-id`               | yes                           | yes      | yes    |
| `repository-visibility` | yes                           | yes      | yes    |
| `repository-id`         | yes                           | yes      | yes    |
| `file-name`             | yes                           | yes      | yes    |
| `file-path`             | yes                           | yes      | yes    |
| `event-timestamp`       | yes                           | yes      | yes    |
| `file-size-bytes`       | yes                           | yes      | no     |
| `private-url`           | yes                           | no       | no     |
| `public-url`            | yes, public repositories only | no       | no     |
| `admin-url`             | yes                           | no       | no     |
| `ip-address`            | yes                           | yes      | yes    |
| `user-agent`            | yes                           | yes      | yes    |

An upload to a private repository posts this body:

**An upload event**

```json
{
  "event-type": "upload-complete",
  "user-id": "you@example.com",
  "repository-visibility": "private",
  "repository-id": "your-repo",
  "file-name": "app-1.0.jar",
  "file-path": "com/acme/app/1.0",
  "event-timestamp": "2026-10-23T12:00:00Z",
  "file-size-bytes": 48213,
  "private-url": "https://your-org.mycloudrepo.io/repositories/your-repo/com/acme/app/1.0/app-1.0.jar",
  "admin-url": "https://admin.cloudrepo.io/browse/your-repo/com/acme/app/1.0",
  "ip-address": "203.0.113.7",
  "user-agent": "Apache-Maven/3.9.9"
}
```

- `user-id` is the user’s email address, or `Anonymous` for an anonymous download of a public repository.
- `repository-visibility` is `private` or `public`.
- `event-timestamp` is an ISO 8601 time in UTC.
- In a Docker repository `file-name` is the image and the tag, such as `app:1.0` (a delete by digest names the image and the digest), and `file-path` is empty. An upload’s `private-url` is the registry address of the tag’s manifest. A delete carries no URL.

## What has no match

These are settings a vendor’s webhook has and CloudRepo’s does not. JFrog describes its in [Predefined Webhooks](https://docs.jfrog.com/integrations/docs/predefined-webhooks) and [Custom Webhooks](https://docs.jfrog.com/integrations/docs/custom-webhooks); Sonatype describes its in [Enabling A Repository Webhook Capability](https://help.sonatype.com/en/enabling-a-repository-webhook-capability.html) and [Working With HMAC Payloads](https://help.sonatype.com/en/working-with-hmac-payloads.html).

| Setting                          | Artifactory                                                                                   | Nexus                                                                                                                 | CloudRepo |
| -------------------------------- | --------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | --------- |
| Signing the request              | An optional secret token. JFrog names the `X-JFrog-Event-Auth` header and shows an HMAC check | An optional secret key. Sonatype describes an HMAC SHA-1 digest of the body in the `X-Nexus-Webhook-Signature` header | No match  |
| Your own headers, body or method | Custom webhooks set all three                                                                 | None listed                                                                                                           | No match  |
| A delivery log                   | A Troubleshooting tab                                                                         | None listed                                                                                                           | No match  |

CloudRepo does not sign a request or send a secret in a header, so there is nothing to verify on your side. It sends each event once, as a `POST` with the JSON body above. It does not retry a failed delivery and it shows you no log of deliveries, so a listener that is down misses the event.

CloudRepo posts to the address you register exactly as you wrote it, query string included. If your listener wants a token, put it in the query string, as the Jenkins plugin below reads its `token` from there, and treat the whole address as a secret, as you would a Slack webhook URL.

## Rewire a Jenkins job

A Jenkins job that runs on an Artifactory or Nexus webhook reads the repository and the file out of the body. This example uses the [Generic Webhook Trigger plugin](https://github.com/jenkinsci/generic-webhook-trigger-plugin), which pulls values out of a JSON body with JSONPath, so rewiring the job is two changes: the CloudRepo webhook that posts to it, and the paths its Jenkinsfile reads.

JFrog’s own Jenkins recipe, on its [Custom Webhooks](https://docs.jfrog.com/integrations/docs/custom-webhooks) page, shapes a form body for Jenkins’s `buildWithParameters` address. CloudRepo’s body is fixed, so that recipe has no twin: read the CloudRepo body with the plugin instead.

Before you start:

- CloudRepo posts to your Jenkins from outside your network, so the address of your trigger must be an `https://` URL that resolves to a public host. CloudRepo does not follow a redirect, so give the final address.
- Keep the trigger’s token in a Jenkins secret text credential. The Jenkinsfile names that credential with `tokenCredentialId` and holds no token.

### Create the webhook in CloudRepo

Create it as [Webhooks](/docs/manage/webhooks.html#creating-a-cloudrepo-webhook) shows: choose the repository, set **Webhook Target** to your Jenkins’s `generic-webhook-trigger/invoke` address followed by `?token=` and the trigger’s token, set the type to **JSON**, tick **Upload** and click **Create Webhook**.

### Change the paths the Jenkinsfile reads

An Artifactory job reads the repository and the path like this:

**Jenkinsfile, before (Artifactory)**

```groovy
pipeline {
  agent any
  triggers {
    GenericTrigger(
      genericVariables: [
        [key: 'REPO', value: '$.data.repo_key'],
        [key: 'FILE_PATH', value: '$.data.path']
      ],
      causeString: 'Artifact deployed to $REPO',
      tokenCredentialId: 'cloudrepo-webhook-token'
    )
  }
  stages {
    stage('Show') {
      steps {
        echo "New file: ${env.REPO}/${env.FILE_PATH}"
      }
    }
  }
}
```

After the move it reads the three CloudRepo fields instead:

**Jenkinsfile, after (CloudRepo)**

```groovy
pipeline {
  agent any
  triggers {
    GenericTrigger(
      genericVariables: [
        [key: 'REPO', value: '$[\'repository-id\']'],
        [key: 'DIR', value: '$[\'file-path\']'],
        [key: 'FILE', value: '$[\'file-name\']']
      ],
      causeString: 'File uploaded to $REPO',
      tokenCredentialId: 'cloudrepo-webhook-token'
    )
  }
  stages {
    stage('Show') {
      steps {
        echo "New file: ${env.REPO}/${env.DIR}/${env.FILE}"
      }
    }
  }
}
```

A Nexus job reads `$.repositoryName` and `$.asset.name` where the Artifactory job reads `$.data.repo_key` and `$.data.path`; both become the three paths above.

A file name and a path are whatever the uploader chose, so use them as values, as the example does, and make sure they are not pasted into a shell command.

### Run it

Run the pipeline once by hand. Jenkins applies a trigger written in a Jenkinsfile only after the pipeline has run. Then upload one file to the repository and watch the job start. When it does, delete the Artifactory or Nexus webhook.

---

The page: https://www.cloudrepo.io/docs/migrate/webhooks-from-artifactory-and-nexus.html
