Handle Webhook Events

Learn how to register a webhook in the Adobe Developer Console and receive real-time job completion notifications.

Overview

When you submit a job, such as bulk-create-variation, the Express API runs the job asynchronously and returns a jobId. Without webhooks, you must call the /status endpoint repeatedly until the job finishes.

With webhook support, you register an HTTPS endpoint once in the Developer Console. When the job reaches a terminal state (succeeded, failed, partially succeeded, or cancelled), the Express API publishes an event to Adobe I/O Events, which then delivers a notification to your endpoint. Your application is notified immediately—no polling required.

data-variant=warning
data-slots=text
Webhook support is currently available only for Bulk Workflow APIs.

How it works

  1. Register a webhook endpoint in your Developer Console project under the Adobe Express API events card.
  2. Submit an async bulk request (for example, POST /bulk-create-variation). The API returns a jobId.
  3. The Express API processes the job asynchronously.
  4. When the job reaches a terminal state, the Express API publishes a CloudEvents-formatted event to Adobe I/O Events.
  5. Adobe I/O Events delivers the notification to your registered webhook URL. The notification contains at minimum the jobId and status; additional fields are optional depending on the event type.
  6. Your application handles the notification and—if needed—calls the /status endpoint once to retrieve full job details.

Prerequisites

Before you register a webhook:

data-variant=info
data-slots=text
To subscribe to events and receive notifications, Adobe Express API (which requires the appropriate Firefly/Express API entitlement for your organization) must be added to your project.

Register your webhook in the Adobe Developer Console

1. Open your project and add the Adobe Express API events

Developer Console "Add to Project" menu with "Event" selected

Developer Console Express API Events

2. Subscribe to bulk events

Select the event types you want to receive. For bulk jobs, choose from the four available events:

You can subscribe to any combination. Click Next.

Developer Console Event Subscription

3. Set up OAuth Server-to-Server credentials

When prompted for a credential type, select OAuth Server-to-Server (or choose your existing credential). This credential is used to authenticate your application's API calls; it is separate from the webhook delivery mechanism.

Developer Console Add to Server to Server

In the following screen (not illustrated here), you'll be able to provide a name and description for this event registration.

4. Configure the event registration

In the following screen, configure your webhook URL and delivery options:

  1. Enable Webhook.
  2. Enter your publicly accessible HTTPS webhook URL.
  3. Choose Batch delivery if you prefer to receive multiple events in a single HTTP request (recommended for high-volume workloads); otherwise leave it as Single.

Developer Console Configure Event Registration

Challenge request

When you save the registration, Adobe I/O Events sends a one-time HTTP GET request to your webhook URL with a challenge query parameter. Your endpoint must respond with the challenge value to prove it is reachable. For example:

GET https://your-endpoint.example.com/webhook?challenge=8ec8d794-e0ab-42df-9017-e3dada8e84f7

Your endpoint should respond with:

HTTP/1.1 200 OK
Content-Type: application/json

{"challenge":"8ec8d794-e0ab-42df-9017-e3dada8e84f7"}

For full details on synchronous and asynchronous challenge validation, see Introduction to Adobe I/O Events Webhooks.

5. Verify the registration

After saving, check that the Status shown in the Registration Details tab is Active. If it shows Disabled, your endpoint did not respond correctly to the challenge request. Re-check the URL and challenge handler, then edit the registration to re-trigger the challenge.

Developer Console Active Status

Event types

Adobe Express API exposes four events for bulk operations. All events follow the CloudEvents 1.0 specification.

Event type
Description
com.adobe.express-api.v1.succeeded
Emitted when the job completed successfully. All records were processed without errors.
com.adobe.express-api.v1.partially_succeeded
Emitted when the job completed with mixed results. Some records succeeded and some failed.
com.adobe.express-api.v1.failed
Emitted when the job failed to complete successfully.
com.adobe.express-api.v1.cancelled
Emitted when the job was cancelled before completion.

Event payload structure

Events are delivered as HTTP POST requests to your webhook URL. The body is a JSON object following the CloudEvents spec. See below for example payloads for a Succeeded and a Cancelled events.

data-slots=heading, code
data-repeat=2
data-languages=json, json

Succeeded

{
  "specversion": "1.0",
  "type": "com.adobe.express-api.v1.succeeded",
  "source": "urn:aio_provider_metadata:cc_ffs_exapi_job_status",
  "id": "5b5341fb-b4c1-422d-bc1a-b4ca248ec6da",
  "time": "2026-06-23T13:57:12.303Z",
  "datacontenttype": "application/json",
  "adobeinternal": {
    "imsOrg": "<YOUR_IMS_ORG_ID>@AdobeOrg"
  },
  "data": {
    "jobId": "57b96a1e-4896-4cad-bb82-b5674454b4b5",
    "status": "succeeded",
    "outputs": [
      {
        "destination": {
          "url": "https://example.com/output-manifest.json"
        },
        "mediaType": "application/json"
      }
    ],
    "summary": {
      "failedRecords": 0,
      "successfulRecords": 1000,
      "totalRecords": 1000
    }
  }
}

Cancelled

{
  "specversion": "1.0",
  "type": "com.adobe.express-api.v1.cancelled",
  "source": "urn:aio_provider_metadata:cc_ffs_exapi_job_status",
  "id": "8023f672-0de6-41cd-bbb9-e99b0a142560",
  "time": "2026-06-23T13:57:12.303Z",
  "datacontenttype": "application/json",
  "adobeinternal": {
    "imsOrg": "<YOUR_IMS_ORG_ID>@AdobeOrg"
  },
  "data": {
    "jobId": "57b96a1e-4896-4cad-bb82-b5674454b4b5",
    "status": "cancelled",
    "progress": {
      "injectedRecords": 1000,
      "processedRecords": 0,
      "totalRecords": 1000
    },
    "summary": {
      "failedRecords": 0,
      "successfulRecords": 0,
      "totalRecords": 1000
    }
  }
}

Field descriptions

Top-level fields

Field
Description
specversion
CloudEvents specification version. Always "1.0".
type
Event type string used for routing and filtering (see Event types).
source
Context in which the event occurred.
id
Unique UUID generated for each event delivery.
time
ISO 8601 timestamp of the job's terminal state.
datacontenttype
Content type of the data payload. Always "application/json".
adobeinternal
Adobe-specific extension containing IMS organization information.
data
Event data object containing job information (see below).

data object fields

Field
Required
Description
data.jobId
Yes
ID of the completed bulk job.
data.status
Yes
Terminal status of the job: "succeeded", "failed", "partially_succeeded", or "cancelled".
data.outputs
No
Presigned URL(s) to download the job's output manifest. Present on succeeded and partially_succeeded events.
data.summary
No
Final tally of records: failedRecords, successfulRecords, and totalRecords. Present on succeeded, partially_succeeded, and cancelled events.
data.progress
No
Record counts at the time of cancellation: injectedRecords, processedRecords, and totalRecords. Present on cancelled events.
data-variant=info
data-slots=text
data.jobId and data.status are the only guaranteed fields in every notification. Your application should treat all other data fields as optional and handle their absence gracefully.

Event filtering

Adobe I/O Events uses a fan-out model at the organization level: when an event is published, it is delivered to all registrations that exist within the same IMS organization, regardless of which project created the job. If you have multiple projects in your organization, all of their webhook registrations may receive an event.

To ensure your webhook only receives events relevant to your application, apply a client ID filter when configuring your event registration.

Developer Console Event Filtering

  1. In the event registration settings, locate the Event Filtering section.
  2. Name and describe your filter (for example, "Filter by Client ID").
  3. Add a filter on Client ID matching the client ID of your OAuth Server-to-Server credential.
{
  "recipientclientid": [
    {
      "equals-ignore-case": "<your_client_id_here>"
    }
  ]
}

This ensures that only events triggered by jobs submitted with your client ID are delivered to your webhook.

You can also filter by:

Click the Filter Definition > Select a Pattern dropdown for a series of demo patterns that you can copy/paste in the filter definition below. For details on advanced filtering options, see Subscriber Defined Filtering.

data-variant=info
data-slots=text
Events are isolated at the IMS organization level: registrations in one organization never receive events from a different organization. Cross-partner event sharing does not occur.

Security

Your webhook URL must be publicly accessible from the internet. Adobe strongly recommends securing it against forged requests. Adobe I/O Events provides two options:

For full details on both options, see the Security Considerations section of the Adobe I/O Events Webhooks guide.

Troubleshooting

Webhook registration shows Disabled or Unstable

To re-enable a disabled registration, edit the event registration in the Developer Console. This re-triggers the challenge request and, if successful, reactivates the registration.

For information about retry behavior, the journaling API, and automatic disable thresholds, see Troubleshooting Unstable/Disabled Registration Status in the Adobe I/O Events guide.

Events are not being delivered

Payload fields are missing

data.outputs, data.summary, and data.progress are optional and not present in every event. Design your handler to check for their presence before accessing them (see Field descriptions).

Next steps