RFC-143: Reduce Forge invocations from bulk operations

RFCs are a way for Atlassian to share what we’re working on with our valued developer community.

An RFC is a document for building shared understanding of a topic. It expresses a technical solution, but can also communicate how it should be built or document standards. A written specification facilitates feedback and drives consensus. It is not a tool for approving or committing to ideas, but a collaborative practice for shaping an idea and finding serious flaws early.

Please respect our community guidelines: keep it welcoming and safe by commenting on the idea, not the people, especially the author; keep it tidy by staying on topic; and keep comments constructive. Thanks!

Summary of project

  • Publish: 3 September 2026
  • Discuss: 4 September 2026 - 24 September 2026
  • Resolve: To be confirmed

A bulk operation affecting 1,000 issues can generate 1,000 Forge invocations. Some apps need every event, while others use the events only as a signal to refresh a cache, index, dashboard, or synchronized copy of the affected data.

Jira bulk-operation metadata can help an app identify each event after it has been delivered. The app must still receive and process every event, even when the intermediate states do not affect its result.

We are considering an explicit trigger setting that would let an app receive one notification when the bulk operation finishes and fetch the resulting state once. This could reduce invocation usage, queue backlogs, retries, and app-side code for debouncing or grouping related events. Apps that need every individual event would retain the current behaviour by default.

Jira bulk issue updates are used below as an example. The capability could later support other Atlassian products where a bulk operation and its completion can be identified reliably.

Problem

Forge currently treats each product event independently. When a customer bulk-updates 1,000 Jira issues, a matching avi:jira:updated:issue trigger can be invoked 1,000 times.

Webhooks generated by Jira bulk operations can include bulkOperationMetaData, which lets an app recognize that an individual event came from a bulk operation. For example:

{
  "bulkOperationMetaData": {
    "sendMail": true
  }
}

This improves classification after delivery, but it does not reduce the number of Forge invocations or indicate when the overall operation has finished.

Apps that reconcile by fetching the latest state may repeat the same work for many intermediate events. A burst can consume invocation capacity, create a backlog, and delay other useful processing. Each app must also implement its own debounce or grouping logic without a reliable completion signal.

Earlier Developer Community feedback has highlighted the value of reducing requests through event batching and the invocation pressure created by broad issue-update triggers.

Forge apps cannot currently express:

Skip the individual events from this bulk operation, notify me when it finishes, and let me fetch the final state once.

Proposed solution

We are considering a bulkEvents property on eligible Forge product-event triggers.

modules:
  trigger:
    - key: issue-updated-trigger
      function: handle-issue-update
      events:
        - avi:jira:updated:issue
      bulkEvents: exclude

The setting would support two initial behaviours:

  • include: Deliver every matching event created by the bulk operation.
  • exclude: Skip the individual events and deliver one notification when the bulk operation finishes.

If bulkEvents is omitted, Forge would use include. Existing triggers would continue to behave as they do today.

For an app that reconciles state after receiving events, exclude could replace hundreds or thousands of invocations with one completion notification. The app could then fetch the final state once instead of reacting to each intermediate change if needed.

A completion notification could contain the operation identifier, product and operation type, outcome, affected scope, and information the app can use to fetch the latest state. The exact payload is open for feedback.

At this stage, we are seeking feedback on whether these delivery choices would solve a meaningful problem for partners. If the feedback supports the approach, we will develop and share the detailed delivery contract before implementation. That contract will cover event types, payloads, ordering, retries, duplicate delivery, and failed or partially completed operations.

The setting would apply only to event types for which the product can provide a reliable bulk-operation completion signal. Jira bulk issue updates are the initial example. Broader product support would depend on those signals being available.

Asks

We would appreciate any feedback, particularly on the following:

  • Would you enable completion-only delivery for any triggers in an app you operate today?
  • Which app workflows need every individual event, and which only need the final state?
  • Would your app need individual events, a completion notification, or both?
  • Approximately how many invocations can one bulk operation create for your app today?
  • Would completion-only delivery reduce queue backlogs, rate-limit pressure, or app-side debounce and grouping logic for you?
  • Which information would you need in the completion notification to fetch or reconcile the final state safely?
  • How soon after the operation finishes would your app need the completion notification?
  • Would a manifest setting be sufficient, or would you need to configure the behaviour at runtime?
  • Which Jira or other Atlassian product operations create the most unnecessary event processing for your app?

Reducing the amount of invocations is definitely a step in the right direction.

I’m not sure I’m understanding what would be the shape of the bulk event that we’d receive at the end, if you have an example I think it could help us: if it doesn’t contain enough details we might end up having to run a bunch of subsequent API calls and it would reduce the amount of invocations but not the pressure on your APIs.

As you mentioned in your message, batching would also be extremely useful in some cases (raise your hand if your app listens to update events, writes them to a queue and processes them after a while) and it would be great to see some development on that front too.

Overall, I think we’d be open to this change, but we’d have to assess whether the additional complexity of having code that manages individual events and batched events provides enough advantages in the real world – it probably does, but I want to be sure before deploying it to prod :wink:

Thanks @RampreethEthiraj for this RFC, I do think that bulk operations can be beneficial to both the sender and the receiver.

Will the one notification when the bulk operation finishes be a single batched message of all events that would be send separately if the setting was include, or will it be a separate message that may require additional callbacks to your API so we get the full picture?

In the case of the later, then I’m not sure this effort will reduce the pressure on our combined systems.

Connect:

Or it’s Forge variant:

"metadata" : {
  "operationType" : "bulk",
  "sendMail" : false //Should be true/false, but always false - see https://ecosystem.atlassian.net/servicedesk/customer/portal/34/ECOHELP-160816
}

#1

There’s additional sendMail context. Would this proposal change based on this logic? Right now, the RFC only considers if event is bulk, but does not differentiate if the bulk event mail is sent, vs not sent.

#2

Would this be supported for events that supply endpoint to a Remote aswell, or just function trigger(s) defined?

#3

bulkEvents at a trigger level would imply that all bulk events are either sent/not sent. This prevents granularity between wanting to receive bulk events of a certain trigger event type (E.g. only wanting to react to avi:jira:deleted:issue bulk changes, but not avi:jira:updated:issue or comment bulk events).

Is there a specific reason why bulkEvents is per trigger, instead of having bulkEvents as a setting under each event?

E.g.

      events:
        - avi:jira:deleted:issue
          bulkEvents: include
        - avi:jira:updated:issue
          bulkEvents: exclude

For Forge, the Connect parity state between Bulk events, Bulk event metadata, Webhook Flow (Primary/Secondary) event detection, and Event retry causes differences that used to be detectable, but now rely on new behaviours in the manifest to drop webhooks (or not be sent them entirely).

Whilst we would appreciate the ability to filter out bulk events, or downstream events, there needs to be more granularity with the bulk metadata conditional filtering.

I’m responding to this from the perspective of ScriptRunner use-cases. Our customers often either care about state or change - state being “what is it now”, and change being that immutable record of "something happened, irrespective of what the state became after that event
exclude is useful for state-oriented apps (i.e. something that only cares about the final issue state), but could the completion contract also expose the actual successful mutations?
Otherwise apps that care about change semantics (e.g this issue transitioned to “done”, but it is now re-opened - that done transition gets lost).
Apps that care about change semantics must choose include and still suffer the invocation flood.

I do understand that event delivery is not guaranteed, but everything helps.

It would be ideal if the resulting event payload had something like:

{
"operationId": "123",
"change": {
"field": "status",
"to": "Done"
},
"results": {
"changedIssueIds": ["10042", "10043"],
"unchangedIssueIds": ["10045"],
"failedIssueIds":
}
}

Thanks

Interesting idea. I can see the benefit for one of our Jira apps. There we sync 3rd-party data to work-items, triggered by issue-events. Being able to do that in bulk would reduce overall costs, including invocation counts, calls to Jira, and calls to the 3rd party.
For that to work in our case, we need work-item data, like specific field values (status, assignee, reporter, a couple of others). So the completion-payload should contain either that data, or at least the work-item IDs, so that we can query Jira for it.

To your questions:

Would you enable completion-only delivery for any triggers in an app you operate today?

Probably. If the implementation complexity is worth the performance gain.

Which app workflows need every individual event, and which only need the final state?

We need to be informed about each updated work-item, but not necessary via individual events.

Would your app need individual events, a completion notification, or both?

For a bulk-update we could do with 1 completion-event. For individual product events (i.e. 1 work item is updated), we need an individual event for that, as is currently the case.

Approximately how many invocations can one bulk operation create for your app today?

Not sure I understand the question. Today, each work-item update is sent to us as an individual event. So, a bulk-operation with 1000 work-items, triggers our app 1000 times. Is that what you mean?

Would completion-only delivery reduce queue backlogs, rate-limit pressure, or app-side debounce and grouping logic for you?

Yes, definitely and significantly. At least for bulk-operations. Not sure how many work item events come from bulk-operations, though.

Which information would you need in the completion notification to fetch or reconcile the final state safely?

At least the ID/key of all affected work-items. Ideally also information about the change; I like @PeterBarnett’s proposal with "change": [{"field": ..., "to": ...}, ...]

How soon after the operation finishes would your app need the completion notification?

Asap. Because when the operation finishes the customer expects work-items to be up-to-date. Hopefully, customers do not expect work-item updates while the bulk-operation is running.

Would a manifest setting be sufficient, or would you need to configure the behaviour at runtime?

A runtime-setting gives more flexibility, but I think we could do with a manifest setting.

Which Jira or other Atlassian product operations create the most unnecessary event processing for your app?

Work-item updates for fields we are not interested in. The crux is, that “not interested in” is dynamically defined by each customer, so we cannot hardcode it as a filter-expression in the manifest.

Thank you @RampreethEthiraj for the RFC.
We wouldn’t benefit directly from completion-only delivery, as we need to process every individual event through a remote endpoint.
However, we would benefit from a consistent way to identify events originating from bulk operations, similar to the information provided by the X-Atlassian-Webhook-Flow header in Connect. This would allow us to prioritise event processing on our side and improve parity between Forge and Connect.