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.
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:
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.
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
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.
Create the necessary Kentaa External IDs to implement the model:
Salesforce External Ids:
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.
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:
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.
X-Api-Key
<api-token>
( paste your api token from Kentaa )
Salesforce - External Services:
Create three external services to support the Team, Actor and Donation callouts
Navigate to Setup > External Services
For each service, repeat the following steps:
1. Select the "From API Specification"
2. Complete the registration details for each service:
Team
Actor
Donation
3. Click Save & Next
4. Check the available Get operation
5. Click Finish
You should now have three external services available for use in the Subscriber flow:
With setup complete, proceed to creating the Webhook flow and Subscriber 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.
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
Webhook > record
output
Step 3 - Save the Flow:
Note (This flow property ensures the Site Guest User can insert the task record)
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.
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.
Select the platform event type Kentaa Webhook.
Click Save and ignore any warnings.
Step 1 - Add filter step
Add a Streamscript step as the first step in the flow
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
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 |
Outcome - Team
Outcome - Actor
Outcome - Donation
-OR-
Complete the Team Path:
Update the relevent Account record in the database or insert a new one if necessary.
Step 4 - Under the Team path, add an Action element. Then select TeamService to complete the Team callout.
Step 5 - Add a Get Records element that represents the Account
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.
Step 9 - Under the Actor path, add an Action element. Then select ActorService to complete the Actor callout.
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.
Step 15 - Under the Donation path, add an Action element. Then select DonationService to complete the Donation callout.
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:
Navigate to your the site page provisioned by the Kentaa team, in our case "Test". Click Continue
Select Team, then click Continue
Add the team leader ("Jake") and additional team member ("Lilly") and the Team ("Jakes Rakes")
Finally, make a donation under Jakes name via the simulated payment gateway.
Click Submit. Kentaa sends a number of webhooks to Salesforce. Our integration correctly creates:
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=04tGA000005NEkX
Transforms and Pipelines |
How to call Apex from scripts |
Salesforce/Clockify Webhooks |