Bulk Webhooks on Flow with Kentaa

Kentaa is a digital fundraising platform for teams and individuals. This guide shows how to replicate Team, Actor and Donation records on Salesforce using Flow and Webhooks.

 

img

 

Scenario - Team Creation on Kentaa

Creating a Team fires the same "Team Created" event for all team members - this causes a race condition during the related Salesforce Account inserts.

This guide shows how to mitigate the issue by introducing a second "Platform Event-triggered" flow in addition to the standard webhook flow.

 

Key Concepts

 

Flow 1:

 

Flow 2:

 

img

 

 

Part 1 - Webhook Flow:

Kentaa Configuration:

Kentaa support provisions a customised environment with a suitable hierarchy and provides an API key to support callbacks. For this demo, we use the following configuration: Site ➝ Team Fund Raiser

.

 

Donation:

A Kentaa Donation corresponds to a Salesforce Task.

img

 

Fund Raising Actor ("Action")

Individual fundraising Actors in Kentaa are known as "Actions" and are represented as Contacts in Salesforce. Donations associated with Actors correspond to Salesforce Tasks with the the WhoId pointing to the corresponding Contact

img

 

Team:

Two or more fund raising Actors may form a Team. A Kentaa Team is modelled as a Salesforce Account with team Actors represented as related Contacts. Donations associated with a Team are modelled as Salesforce Tasks, with the WhatId pointing to the Account and the WhoId pointing to the Contact.

img

 

Create the necessary Kentaa External IDs to implement the model:

 

Salesforce External Ids:

img

 

Modify the Account object with an External Id representing the Kentaa Team Id and grant access to this field for all profiles.

 

Modify the Contact object with an External Id representing the Kentaa Actor Id and grant access to this field for all profiles.

 

Kentaa Team Creation:

When the Save button is clicked on the Kentaa platform, a Team event for every team member is sent to Salesforce. This causes a race condition - multiple webhooks triggered at the same time all assume the absence of the Team / Account record.

 

Salesforce Account Creation:

To avoid the race condition, we utilise Platform Events to convert parallel webhook processing of Kentaa's event notifications into event collections that are processed sequentially in a later context. A second flow retrieves each Kentaa notification from the event queue for processing.

In this second Flow, Streamscript processes the queue in batches as ordered platform event collections and the effects thereof are committed to the database

 

 

Create a Platform Event to enable deferred processing of Kentaa notifications:

 

Kentaa Event Message Structure:

{
    "object_type": "action",
    "object_id": 42,
    "event_type": "counters.update",
    "application": "Kentaa",
    "site_id": 12
} 

 

Salesforce Platform Event Definition:

The Platform Event has a single Text Area field, Data__c capped at 131,072 bytes. This size will always accommodate the full Kenta Notification.

img

  

 

Kentaa - Callback:

To retrieve the full event detail, the recipient of the webhook is expected to call back to Kentaa using a combination of the object_type and object_Id as the retrieval key. Notification detail retrieval is covered in the Subscriber Flow below.

 

Salesforce - Named Credential:

img

Create a Named Credential to hold your API key. At runtime, the named credential resolves the URL to the configured Kentaa physical endpoint. It also securely manages authentication for an external service callout on behalf of the authorized user performing the callout.

 

Salesforce - External Services:

Create three external services to support the Team, Actor and Donation callouts

Navigate to Setup > External Services

img

 

For each service, repeat the following steps:

1. Select the "From API Specification"

img

2. Complete the registration details for each service:

Team

Actor

Donation

 

3. Click Save & Next

img

 

4. Check the available Get operation

img

 

5. Click Finish

img

 

You should now have three external services available for use in the Subscriber flow:

img

 

With setup complete, proceed to creating the Webhook flow and Subscriber flow:

 

 

Part 1 - Webhook Flow:

Webhook notifications sent from Kentaa trigger the Kentaa Webhook flow. The Webhook action extracts the notification JSON and populates Data__c field of the Kentaa_Webhook__e platform event - parallel notifications are effectively batched and sequentialised - parallel notifications are effectively batched and sequentialised. This approach is the first of a two part strategy to address the issue of processing concurrent related requests.

img

 

Step 1 - Convert the parallel webhook to a batched sequential event:

# Streamscript
$Webhook.response  = '200 - OK'
$Kentaa_Webhook__e = New-Kentaa_Webhook__e
$Kentaa_Webhook__e.Body__c = $Webhook.request

return $Kentaa_Webhook__e 

The script returns a custom Platform Event that holds the event notification JSON.

 

Step 2 - Add a step to save the data, thereby publishing the platform event

 

Step 3 - Save the Flow:

Note (This flow property ensures the Site Guest User can insert the task record)

 

 

Part 2 - Subscriber Flow:

The Subscriber Flow makes use of the Bulk directive in an initial Streamscript step - this directive provides a comprehensive view of potentially related inbound events that would not be possible had these events been separately processed in multiple webhook flow instances.

# Streamscript Bulk

This Streamscript step categorises each Kentaa notification in the batch by qualifying type — new donations (Tasks), team members (Contacts), or teams (Accounts) — all other notifications are flagged. Duplicate team notifications are also flagged to prevent race conditions during record insertion or updates.

img

 

The Decision step routes the filtered notifications. Flagged messages not pertinent to the process are directed to the 'skipped' path while relevant de-duped Team, Actor, and Donation messages move forward for further processing. Each path concludes by executing an update or insert in Salesforce

 

Create a new Platform Triggered flow

 

Go to Setup > Flows > Platform Event - Triggered Flow. This flow will subscribes to platform event collections published by the webhook flow described above.

 

img

 

Select the platform event type Kentaa Webhook.

img

Click Save and ignore any warnings.

 

Step 1 - Add filter step

Add a Streamscript step as the first step in the flow

img

Modify the existing '#Streamscript' language instruction at the start of the script with the the Bulk directive

# Streamscript Bulk

 

Step 2 - Add filter logic

The script eliminates redundant 'Team created' eventsand excludes events that are not relevant to the process.

Paste in the following script:

# Streamscript Bulk
$ids         = []
$types       = []
$uniqueKeys  = []

$items = {!$Record}
foreach ($item in $items) {
    # read the webhook body
    $o = Json-Decode $item.Data__c
    
    # filter
    $id     = $o.object_id                     # eg: 12345
    $type   = `$o.object_type|$o.event_type`   # eg: team|sign_ups.create
    $key    = `$type|$id`                      # eg: team|sign_ups.create|12345
    
    # de-duplicate ( duplicates assigned 'null' )
    if ($uniqueKeys.contains($key)) {
        $id = null;  $type = null
    }
    $uniqueKeys.add($key)

    # add 'null - normalised' items
    $types.add($type)
    $ids.add($id)
}
    
# $ids and $types are correlated  - that is, $ids[1] relates to $types[1], etc
#   - $types: resolves to decision step path - team, participant, donation
#   - $ids: via external service callout retrieves fully described event for $id
return  -num $ids -text $types    

 

Click Done

 

img

 

Step 3 - Add a Decision element:

The Decision step routes notifications from the Filter Step — Team, Actor, and Donation messages move forward for further processing while all other messages are directed to the Skip path per the table below.:

object_type event_type   value   outcome
team sign_ups.create team|sign_ups.create Team
action sign_ups.create action|sign_ups.create Actor
donation sign_ups.create donation|sign_ups.create Donation
donation donations.update donation|donations.update Donation
null null <default> Skip

 

img

 

Outcome - Team

img

 

Outcome - Actor

img

 

Outcome - Donation

-OR-

img

 

Complete the Team Path:

Update the relevent Account record in the database or insert a new one if necessary.

img

 

Step 4 - Under the Team path, add an Action element. Then select TeamService to complete the Team callout.

 

img

 

Step 5 - Add a Get Records element that represents the Account

img

 

Step 6 - Add a New Assignment element. Assign the Kentaa Team Id and Kentaa Team Name using the response from the Kentaa Team callout

 

Step 7 - Add a New Decision element. Test for an empty Id on the selected Account record.

Outcome:

Default: (null value)

 

Step 8 - 'Upsert' the Account

Under the Account Exists path, add an Update Records step

Under the Not Exists path add a Create Records step

 

Complete the Actor path:

Link the Contact (Actor) with the corresponding Account (Team) - then upsert the Contact record.

img

 

Step 9 - Under the Actor path, add an Action element. Then select ActorService to complete the Actor callout.

img

 

Step 10 - Add a Get Records element that represents the Account.

 

Step 11 - Add a Get Records element that represents the Contact.

 

Step 12 - Add a New Assignment element that hydrates the selected Contact record.

Set Variables:

 

Step 13 - Add a New Decision element. Test for an empty Id on the selected Contact record.

Outcome:

Default: (null value)

 

Step 14 - 'Upsert' the Contact

Under the Contact Exists path, add an Update Records step

Under the Not Exists path add a Create Records step

 

Complete the Donation path:

Insert a Task representing the Donation. Before inserting, resolve the Donation's Team ID to its corresponding Salesforce Account ID and the Donation's Actor ID to its corresponding Salesforce Contact ID. Then, map the WhatId on the Task to the resolved Contact ID and the WhoId on the Task to the resolved Account ID.

img

 

Step 15 - Under the Donation path, add an Action element. Then select DonationService to complete the Donation callout.

img

 

Now complete the Donation path

 

  

Step 17 - Add a Get Records element that represents the Account.

 

Step 18 - Add a Get Records element that represents the Contact.

 

Step 19 - Insert the Task with a Create Records step

Set Field Values for the Task

 

The completed flow should look like this:

img

 

 

Part 3 - Test the Integration:

Navigate to your the site page provisioned by the Kentaa team, in our case "Test". Click Continue

img

 

Select Team, then click Continue

img

 

Add the team leader ("Jake") and additional team member ("Lilly") and the Team ("Jakes Rakes")

img

img

 

Finally, make a donation under Jakes name via the simulated payment gateway.

img

 

Click Submit. Kentaa sends a number of webhooks to Salesforce. Our integration correctly creates:

 

 

Wrap Up:

This integration employed Streamscript and platform events to resolve concurrency issues presented in the scenario at the start of this guide. These integration techniques can be adapted to other Salesforce webhook scenarios with similar dependencies..

 

 

Getting started with Streamscript
Install from the Salesforce AppExchange
Package install link: /packaging/installPackage.apexp?p0=04tGA000005dF3W