Create an Apex integration service with an Invocable entry point that triggers a Queueable job to call an AWS Webhook with HMAC-SHA256 signature authentication, using Named Credentials and Custom Metadata for secure credential management.
flowchart TD
subgraph trigger [Entry Points]
Flow[Salesforce Flow]
Agentforce[Agentforce]
end
subgraph apex [Apex Layer]
Invocable[AWSWebhookService]
Queueable[AWSWebhookQueueable]
Crypto[HMAC-SHA256 Signing]
end
subgraph config [Configuration]
CMT[AWS_Integration_Setting__mdt]
NC[Named Credential]
EC[External Credential]
end
subgraph external [External]
AWS[AWS Webhook API]
end
Flow --> Invocable
Agentforce --> Invocable
Invocable -->|enqueue| Queueable
Queueable -->|query secret| CMT
Queueable --> Crypto
Crypto -->|callout| NC
NC -->|references| EC
NC --> AWS
Queueable -->|update| SFDC[(Case Record)]
Location: force-app/main/default/classes/AWSWebhookService.cls
This class contains:
- Inner class
WebhookRequestwith@InvocableVariableannotations:recordId(String, required) - The Case or record IDcontext(String, required) - Event context like "CaseCreate" or "CaseUpdate"configurationName(String, optional) - Custom Metadata DeveloperName, defaults to "AWS_DevOps_Agent"
- Inner class
WebhookResultfor Flow output:isSuccess(Boolean)message(String)jobId(String)
@InvocableMethodmethodtriggerWebhook(List<WebhookRequest> requests):- Validates inputs
- Instantiates and enqueues
AWSWebhookQueueable - Returns job ID for tracking
Location: force-app/main/default/classes/AWSWebhookQueueable.cls
Implements Queueable, Database.AllowsCallouts:
- Constructor accepts
recordId,context,configurationName execute(QueueableContext context)method:- Query
AWS_Integration_Setting__mdtto get secret and Named Credential name - Query Case record to build payload
- Generate ISO 8601 timestamp
- Build JSON payload with Case data
- Create HMAC-SHA256 signature:
sign(timestamp + ":" + payload, secret) - Make HTTP callout using
callout:+ Named Credential - Set custom headers:
x-amzn-event-timestamp,x-amzn-event-signature - Parse response and update Case with integration log
- Query
- Private helper methods:
generateSignature(String timestamp, String payload, String secret)- HMAC-SHA256 logicbuildPayload(Case caseRecord, String context)- JSON payload constructionupdateCaseWithResult(Id caseId, String requestId, Boolean isSuccess, String message)- Write-back logic
Location: force-app/main/default/objects/AWS_Integration_Setting__mdt/
Fields:
| Field API Name | Type | Description |
|---|---|---|
Endpoint_Named_Credential__c |
Text(100) | Named Credential API name (e.g., "AWS_Webhook") |
Secret_Key__c |
Text(255) | HMAC secret key for signing |
Active__c |
Checkbox | Whether this config is active |
Webhook_Path__c |
Text(255) | Path appended to Named Credential endpoint |
Timeout_Milliseconds__c |
Number | HTTP timeout (default 30000) |
Location: force-app/main/default/classes/AWSWebhookServiceTest.cls
- Mock HTTP callouts using
HttpCalloutMock - Test invocable method with valid/invalid inputs
- Test Queueable execution with mock responses
- Verify Case update after callout
The Bash script logic translates to Apex as follows:
// Bash: SIGNATURE=$(echo -n "${TIMESTAMP}:${PAYLOAD}" | openssl dgst -sha256 -hmac "$SECRET" -binary | base64)
private String generateSignature(String timestamp, String payload, String secret) {
String dataToSign = timestamp + ':' + payload;
Blob hmacData = Crypto.generateMac(
'HmacSHA256',
Blob.valueOf(dataToSign),
Blob.valueOf(secret)
);
return EncodingUtil.base64Encode(hmacData);
}Deploy directly from the source files:
# Deploy to your default org
sf project deploy start --source-dir force-app/main/default/classes/AWSWebhookService.cls \
--source-dir force-app/main/default/classes/AWSWebhookQueueable.cls \
--source-dir force-app/main/default/classes/AWSWebhookServiceTest.cls \
--source-dir force-app/main/default/objects/AWS_Integration_Setting__mdt
# Deploy to a specific org
sf project deploy start --source-dir force-app/main/default/classes/AWSWebhookService.cls \
--source-dir force-app/main/default/classes/AWSWebhookQueueable.cls \
--source-dir force-app/main/default/classes/AWSWebhookServiceTest.cls \
--source-dir force-app/main/default/objects/AWS_Integration_Setting__mdt \
--target-org YOUR_ORG_ALIAS# Deploy using the package.xml manifest
sf project deploy start --manifest manifest/package.xml
# Deploy to a specific target org
sf project deploy start --manifest manifest/package.xml --target-org YOUR_ORG_ALIAS# Validate without deploying (runs tests)
sf project deploy start --manifest manifest/package.xml --dry-run --test-level RunSpecifiedTests --tests AWSWebhookServiceTestsf apex run test --class-names AWSWebhookServiceTest --result-format human --wait 10The new Named Credentials framework (Spring '23+) separates authentication configuration from endpoint configuration using External Credentials and Named Credentials.
flowchart LR
subgraph salesforce [Salesforce Configuration]
NC[Named Credential]
EC[External Credential]
P[Principal]
end
NC -->|references| EC
EC -->|contains| P
NC -->|callout| AWS[AWS Webhook API]
The External Credential defines the authentication protocol. Since HMAC signing is handled in Apex, we use "Custom" authentication.
- Navigate to: Setup > Named Credentials > External Credentials tab > New
- Configure:
- Label: AWS Webhook Auth
- Name: AWS_Webhook_Auth
- Authentication Protocol: Custom
- Click Save
- In the Principals section, click New:
- Parameter Name: AWS_Webhook_Principal
- Sequence Number: 1
- Identity Type: Named Principal
- Click Save
- In the Permission Set Mappings section, assign the Principal to appropriate Permission Sets that need callout access
The Named Credential defines the endpoint URL and references the External Credential.
- Navigate to: Setup > Named Credentials > Named Credentials tab > New
- Configure:
- Label: AWS Webhook
- Name: AWS_Webhook (this is referenced in Custom Metadata)
- URL:
https://event-ai.us-east-1.api.aws(base URL only, no trailing slash) - Enabled for Callouts: Checked
- External Credential: AWS_Webhook_Auth (select from dropdown)
- Generate Authorization Header: Unchecked (HMAC signature handled in Apex)
- Allow Formulas in HTTP Header: Checked (optional, for dynamic headers)
- Allow Formulas in HTTP Body: Unchecked
- Click Save
Users and integrations that need to make callouts must have access to the External Credential Principal.
- Navigate to: Setup > Permission Sets
- Select or create a Permission Set for integration users
- Go to External Credential Principal Access
- Add AWS_Webhook_Auth - AWS_Webhook_Principal
- Assign the Permission Set to users/integration users who will trigger webhooks
- Navigate to: Setup > Custom Metadata Types > AWS Integration Setting > Manage Records
- Click New
- Configure:
- Label: Default
- DeveloperName: Default
- Endpoint Named Credential: AWS_Webhook
- Secret Key: (your HMAC secret from AWS)
- Webhook Path: /webhook/your-endpoint-path
- Active: Checked
- Timeout Milliseconds: 30000
- Click Save
If your AWS endpoint requires AWS Signature Version 4 instead of custom HMAC, you can configure the External Credential differently:
-
External Credential Settings:
- Authentication Protocol: AWS Signature Version 4
- Service: execute-api (for API Gateway) or your specific AWS service
- Region: us-east-1 (or your region)
- AWS Account ID: Your AWS account ID
-
Principal Settings:
- Identity Type: Named Principal
- Authentication Parameters:
- Access Key: Your AWS Access Key ID
- Access Secret: Your AWS Secret Access Key
Note: When using AWS Sig V4 via External Credentials, you would not need the HMAC signing logic in Apex as Salesforce handles the signing automatically.
The Invocable Action will appear in Flow Builder as:
- Category: Apex Action
- Label: "Trigger AWS Webhook"
Flow Configuration:
- Add an Apex Action element
- Select "Trigger AWS Webhook"
- Map inputs:
recordId-> Record Id from trigger or variablecontext-> Text literal like "CaseUpdate"configurationName-> (Optional) "AWS_DevOps_Agent"
- Capture outputs for logging/debugging
- No hardcoded secrets: HMAC secret retrieved from Custom Metadata at runtime
- No hardcoded URLs: Endpoint resolved via Named Credential
- External Credential isolation: Authentication configuration is separated from endpoint configuration
- Permission Set controlled access: Only users with the External Credential Principal assigned can make callouts
- Field-level security: Ensure Custom Metadata field
Secret_Key__cis only accessible to system administrators - HMAC validation: AWS can validate the signature to ensure request authenticity
- Audit trail: Named Credential callouts are logged in Salesforce event monitoring